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.
This commit is contained in:
Ed Zynda
2026-03-05 19:03:47 +03:00
parent 7e1686e572
commit 57250a3a3d
5 changed files with 33 additions and 34 deletions
+2 -2
View File
@@ -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)
+13 -11
View File
@@ -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 \
+16 -19
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -209,10 +209,10 @@ func spawnAgent(state *subState) {
}
args := []string{
"--prompt", prompt,
"--json",
"--no-session",
"--no-extensions",
prompt,
}
cmd := exec.Command(kitBinary, args...)