From 57250a3a3d74770f2d9eab40a9b7644ac60e10af Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Thu, 5 Mar 2026 19:03:47 +0300 Subject: [PATCH] refactor: remove --prompt flag, positional args are the only way Drop the --prompt/-p flag entirely. Non-interactive mode is now triggered by passing positional arguments: kit "Explain this" kit @file.go "Review this" --json kit @a.go @b.go --quiet Updated extension examples (kit-kit.go, subagent-widget.go) to pass the prompt as a positional arg. Updated AGENTS.md and README.md. --- AGENTS.md | 4 +-- README.md | 24 ++++++++++-------- cmd/root.go | 35 ++++++++++++-------------- examples/extensions/kit-kit.go | 2 +- examples/extensions/subagent-widget.go | 2 +- 5 files changed, 33 insertions(+), 34 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 9b1f8911..f55c45de 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,9 +83,9 @@ tmux kill-session -t kittest # cleanup ### Non-Interactive Kit (Subprocess Spawning) Extensions can spawn Kit as a subprocess for sub-agent patterns: ```bash -kit --prompt "question" --quiet --no-session --no-extensions --system-prompt /path/to/prompt.txt --model provider/model +kit --quiet --no-session --no-extensions --system-prompt /path/to/prompt.txt --model provider/model "question" ``` -Key flags: `--quiet` (stdout only, no TUI), `--no-session` (ephemeral), `--no-extensions` (prevent recursive loading), `--system-prompt` (string or file path). +Positional args are the prompt. `@file` args attach file content. Key flags: `--quiet` (stdout only, no TUI), `--no-session` (ephemeral), `--no-extensions` (prevent recursive loading), `--system-prompt` (string or file path). ## External Repo Research - **ALWAYS use `btca`** to search external repos (e.g. iteratr, other reference codebases) diff --git a/README.md b/README.md index 20e661ca..2d225c0d 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ A powerful, extensible AI coding agent CLI with multi-provider support, built-in - **Extension System**: Write custom tools, commands, widgets, and UI modifications in Go - **Interactive TUI**: Rich terminal interface powered by Bubble Tea with streaming, syntax highlighting, and custom rendering - **Session Management**: Tree-based conversation history with branching support -- **Non-Interactive Mode**: Script-friendly `--prompt` mode with JSON output +- **Non-Interactive Mode**: Script-friendly positional args with JSON output - **Go SDK**: Embed Kit in your own applications ## Installation @@ -57,7 +57,10 @@ go build -o kit ./cmd/kit kit # Run a one-off prompt -kit --prompt "List files in src/" +kit "List files in src/" + +# Attach files as context +kit @main.go @test.go "Review these files" # Continue the most recent session kit --continue @@ -70,13 +73,13 @@ kit --model anthropic/claude-sonnet-4-5-20250929 ```bash # Get JSON output for scripting -kit --prompt "Explain main.go" --json +kit "Explain main.go" --json # Quiet mode (final response only) -kit --quiet --prompt "Run tests" +kit "Run tests" --quiet # Ephemeral mode (no session file) -kit --prompt "Quick question" --no-session +kit "Quick question" --no-session ``` ## Configuration @@ -142,11 +145,10 @@ mcpServers: --resume, -r Interactive session picker --no-session Ephemeral mode, no persistence -# Behavior ---prompt, -p Run in non-interactive mode with given prompt ---quiet Suppress all output (only with --prompt) ---json Output response as JSON (only with --prompt) ---no-exit Continue to interactive mode after --prompt +# Behavior (non-interactive: pass prompt as positional arg) +--quiet Suppress all output (non-interactive only) +--json Output response as JSON (non-interactive only) +--no-exit Enter interactive mode after prompt completes --max-steps Maximum agent steps (0 for unlimited) --stream Enable streaming output (default: true) --compact Enable compact output mode @@ -378,7 +380,7 @@ host.ClearSession() Spawn Kit as a subprocess for multi-agent orchestration: ```bash -kit --prompt "Analyze codebase" \ +kit "Analyze codebase" \ --json \ --no-session \ --no-extensions \ diff --git a/cmd/root.go b/cmd/root.go index f190d734..0422376a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -29,7 +29,7 @@ var ( providerURL string providerAPIKey string debugMode bool - promptFlag string + positionalPrompt string // set by processPositionalArgs from CLI positional args quietFlag bool jsonFlag bool noExitFlag bool @@ -208,9 +208,7 @@ func init() { "model to use (format: provider/model)") rootCmd.PersistentFlags(). BoolVar(&debugMode, "debug", false, "enable debug logging") - rootCmd.PersistentFlags(). - StringVarP(&promptFlag, "prompt", "p", "", "non-interactive prompt (prefer positional args instead)") - _ = rootCmd.PersistentFlags().MarkHidden("prompt") + rootCmd.PersistentFlags(). BoolVar(&quietFlag, "quiet", false, "suppress all output (non-interactive mode only)") rootCmd.PersistentFlags(). @@ -259,7 +257,6 @@ func init() { _ = viper.BindPFlag("system-prompt", rootCmd.PersistentFlags().Lookup("system-prompt")) _ = viper.BindPFlag("model", rootCmd.PersistentFlags().Lookup("model")) _ = viper.BindPFlag("debug", rootCmd.PersistentFlags().Lookup("debug")) - _ = viper.BindPFlag("prompt", rootCmd.PersistentFlags().Lookup("prompt")) _ = viper.BindPFlag("max-steps", rootCmd.PersistentFlags().Lookup("max-steps")) _ = viper.BindPFlag("stream", rootCmd.PersistentFlags().Lookup("stream")) _ = viper.BindPFlag("compact", rootCmd.PersistentFlags().Lookup("compact")) @@ -286,7 +283,7 @@ func init() { // processPositionalArgs separates positional CLI arguments into @file // attachments and prompt text. File content is read and prepended to -// promptFlag so the agent receives it. Positional args are the primary +// positionalPrompt so the agent receives it. Positional args are the primary // way to run non-interactive mode: // // kit "Explain this codebase" @@ -323,19 +320,19 @@ func processPositionalArgs(args []string) { // value (for backward compat with subprocess invocations). if len(promptParts) > 0 { extra := strings.Join(promptParts, " ") - if promptFlag != "" { - promptFlag = promptFlag + " " + extra + if positionalPrompt != "" { + positionalPrompt = positionalPrompt + " " + extra } else { - promptFlag = extra + positionalPrompt = extra } } // Prepend file content to the prompt. if fileContent.Len() > 0 { - if promptFlag == "" { - promptFlag = strings.TrimSpace(fileContent.String()) + if positionalPrompt == "" { + positionalPrompt = strings.TrimSpace(fileContent.String()) } else { - promptFlag = strings.TrimSpace(fileContent.String()) + "\n\n" + promptFlag + positionalPrompt = strings.TrimSpace(fileContent.String()) + "\n\n" + positionalPrompt } } } @@ -584,16 +581,16 @@ func globalShortcutsProviderForUI(k *kit.Kit) func() map[string]func() { func runNormalMode(ctx context.Context) error { // Validate flag combinations - if quietFlag && promptFlag == "" { + if quietFlag && positionalPrompt == "" { return fmt.Errorf("--quiet requires a prompt (e.g. kit \"your question\" --quiet)") } - if jsonFlag && promptFlag == "" { + if jsonFlag && positionalPrompt == "" { return fmt.Errorf("--json requires a prompt (e.g. kit \"your question\" --json)") } if jsonFlag && noExitFlag { return fmt.Errorf("--json and --no-exit flags cannot be used together") } - if noExitFlag && promptFlag == "" { + if noExitFlag && positionalPrompt == "" { return fmt.Errorf("--no-exit requires a prompt (e.g. kit \"your question\" --no-exit)") } @@ -661,7 +658,7 @@ func runNormalMode(ctx context.Context) error { // Create CLI for non-interactive mode only. var cli *ui.CLI - if promptFlag != "" { + if positionalPrompt != "" { cli, err = SetupCLIForNonInteractive(kitInstance) if err != nil { return fmt.Errorf("failed to setup CLI: %v", err) @@ -708,7 +705,7 @@ func runNormalMode(ctx context.Context) error { kitInstance.SetExtensionContext(extensions.Context{ CWD: cwd, Model: modelName, - Interactive: promptFlag == "", + Interactive: positionalPrompt == "", Print: func(text string) { appInstance.PrintFromExtension("", text) }, PrintInfo: func(text string) { appInstance.PrintFromExtension("info", text) }, PrintError: func(text string) { appInstance.PrintFromExtension("error", text) }, @@ -966,8 +963,8 @@ func runNormalMode(ctx context.Context) error { } // Check if running in non-interactive mode - if promptFlag != "" { - return runNonInteractiveModeApp(ctx, appInstance, cli, promptFlag, quietFlag, jsonFlag, noExitFlag, modelName, parsedProvider, kitInstance.GetLoadingMessage(), serverNames, toolNames, mcpToolCount, extensionToolCount, usageTracker, extCommands, contextPaths, skillItems, getWidgets, getHeader, getFooter, getToolRenderer, getEditorInterceptor, getUIVisibility, getStatusBarEntries, emitBeforeFork, emitBeforeSessionSwitch, getGlobalShortcuts, getExtensionCommands) + if positionalPrompt != "" { + return runNonInteractiveModeApp(ctx, appInstance, cli, positionalPrompt, quietFlag, jsonFlag, noExitFlag, modelName, parsedProvider, kitInstance.GetLoadingMessage(), serverNames, toolNames, mcpToolCount, extensionToolCount, usageTracker, extCommands, contextPaths, skillItems, getWidgets, getHeader, getFooter, getToolRenderer, getEditorInterceptor, getUIVisibility, getStatusBarEntries, emitBeforeFork, emitBeforeSessionSwitch, getGlobalShortcuts, getExtensionCommands) } // Quiet mode is not allowed in interactive mode diff --git a/examples/extensions/kit-kit.go b/examples/extensions/kit-kit.go index ea91ca14..95738b2c 100644 --- a/examples/extensions/kit-kit.go +++ b/examples/extensions/kit-kit.go @@ -488,11 +488,11 @@ func queryExpert(name, question string) (output string, exitCode int, elapsed ti // Build subprocess arguments. Use --json for structured output parsing. // Don't pass --model; the subprocess inherits the same config/env default. args := []string{ - "--prompt", question, "--json", "--no-session", "--no-extensions", "--system-prompt", tmpFile.Name(), + question, } var stdoutBuf, stderrBuf bytes.Buffer diff --git a/examples/extensions/subagent-widget.go b/examples/extensions/subagent-widget.go index 9666bd8b..0e66ab50 100644 --- a/examples/extensions/subagent-widget.go +++ b/examples/extensions/subagent-widget.go @@ -209,10 +209,10 @@ func spawnAgent(state *subState) { } args := []string{ - "--prompt", prompt, "--json", "--no-session", "--no-extensions", + prompt, } cmd := exec.Command(kitBinary, args...)