From 1d07b55f98080d384a9e0f99b4d46c16c6293b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Flor=C3=AAncio?= Date: Fri, 4 Sep 2026 02:16:10 +0000 Subject: [PATCH] fix: quiet Claude passthrough by default (#3305) (#3317) --- .../internal/agent/agents/claude_acp.go | 2 +- .../agents/claude_acp_passthrough_test.go | 44 +++++++ apps/backend/internal/agent/agents/helpers.go | 57 ++++++--- .../internal/agent/agents/passthrough.go | 2 +- .../agent/settings/controller/agent_config.go | 8 +- .../controller/command_preview_test.go | 32 +++++ .../settings/controller/controller_test.go | 9 +- .../claude-passthrough-quiet-default/plan.md | 105 +++++++++++++++++ .../task-01-quiet-claude-passthrough.md | 110 ++++++++++++++++++ docs/public/agents-and-profiles.md | 2 + docs/specs/INDEX.md | 2 +- docs/specs/cli/README.md | 3 +- .../passthrough-launch-defaults.md | 37 ++++++ .../passthrough-launch-defaults.md | 80 +++++++++++++ 14 files changed, 463 insertions(+), 30 deletions(-) create mode 100644 apps/backend/internal/agent/agents/claude_acp_passthrough_test.go create mode 100644 apps/backend/internal/agent/settings/controller/command_preview_test.go create mode 100644 docs/plans/claude-passthrough-quiet-default/plan.md create mode 100644 docs/plans/claude-passthrough-quiet-default/task-01-quiet-claude-passthrough.md create mode 100644 docs/specs/cli/requirements/passthrough-launch-defaults.md create mode 100644 docs/specs/cli/system-design/passthrough-launch-defaults.md diff --git a/apps/backend/internal/agent/agents/claude_acp.go b/apps/backend/internal/agent/agents/claude_acp.go index b1e84c7cd0..3be178b811 100644 --- a/apps/backend/internal/agent/agents/claude_acp.go +++ b/apps/backend/internal/agent/agents/claude_acp.go @@ -55,7 +55,7 @@ func NewClaudeACP() *ClaudeACP { Supported: true, Label: "CLI Passthrough", Description: "Show terminal directly instead of chat interface", - PassthroughCmd: NewCommand("npx", "-y", "@anthropic-ai/claude-code", "--verbose"), + PassthroughCmd: NewCommand("npx", "-y", "@anthropic-ai/claude-code"), ModelFlag: NewParam("--model", "{model}"), IdleTimeout: 3 * time.Second, BufferMaxBytes: DefaultBufferMaxBytes, diff --git a/apps/backend/internal/agent/agents/claude_acp_passthrough_test.go b/apps/backend/internal/agent/agents/claude_acp_passthrough_test.go new file mode 100644 index 0000000000..574f1d46bb --- /dev/null +++ b/apps/backend/internal/agent/agents/claude_acp_passthrough_test.go @@ -0,0 +1,44 @@ +package agents + +import ( + "slices" + "testing" +) + +// @covers AC-CLI-PASSTHROUGH-LAUNCH-001.1 +func TestClaudeACP_PassthroughCmd_DefaultOmitsVerbose(t *testing.T) { + got := NewClaudeACP().PassthroughConfig().PassthroughCmd.Args() + want := []string{"npx", "-y", "@anthropic-ai/claude-code"} + if !slices.Equal(got, want) { + t.Fatalf("PassthroughCmd = %#v, want %#v", got, want) + } +} + +// @covers AC-CLI-PASSTHROUGH-LAUNCH-001.2 +func TestClaudeACP_PassthroughCmd_VerboseOptIn(t *testing.T) { + got := NewClaudeACP().BuildPassthroughCommand(PassthroughOptions{ + CLIFlagTokens: []string{"--verbose"}, + }).Args() + want := []string{"npx", "-y", "@anthropic-ai/claude-code", "--verbose"} + if !slices.Equal(got, want) { + t.Fatalf("argv = %#v, want %#v", got, want) + } +} + +func TestStandardPassthrough_DeduplicatesPermissionCLIFlags(t *testing.T) { + agent := StandardPassthrough{ + Cfg: PassthroughConfig{Supported: true, PassthroughCmd: NewCommand("auggie")}, + PermSettings: map[string]PermissionSetting{ + "allow_indexing": {Supported: true, ApplyMethod: PermissionApplyMethodCLIFlag, CLIFlag: "--allow-indexing"}, + }, + } + + got := agent.BuildPassthroughCommand(PassthroughOptions{ + PermissionValues: map[string]bool{"allow_indexing": true}, + CLIFlagTokens: []string{"--allow-indexing"}, + }).Args() + want := []string{"auggie", "--allow-indexing"} + if !slices.Equal(got, want) { + t.Fatalf("argv = %#v, want %#v", got, want) + } +} diff --git a/apps/backend/internal/agent/agents/helpers.go b/apps/backend/internal/agent/agents/helpers.go index 6ecb5a367c..d23b4bb470 100644 --- a/apps/backend/internal/agent/agents/helpers.go +++ b/apps/backend/internal/agent/agents/helpers.go @@ -1,6 +1,7 @@ package agents import ( + "slices" "strings" ) @@ -61,6 +62,44 @@ func mergeAgentctlAutoApprove(settings map[string]PermissionSetting) map[string] return merged } +func permissionCLIFlagArgs(settings map[string]PermissionSetting, values map[string]bool) [][]string { + if settings == nil || values == nil { + return nil + } + var args [][]string + for settingName, setting := range settings { + if !setting.Supported || setting.ApplyMethod != PermissionApplyMethodCLIFlag || setting.CLIFlag == "" { + continue + } + if value, exists := values[settingName]; !exists || !value { + continue + } + flagArgs := strings.Fields(setting.CLIFlag) + if setting.CLIFlagValue != "" { + flagArgs = append(flagArgs, setting.CLIFlagValue) + } + args = append(args, flagArgs) + } + return args +} + +func withoutPermissionCLIFlagDuplicates(tokens []string, settings map[string]PermissionSetting, values map[string]bool) []string { + remaining := slices.Clone(tokens) + for _, permissionArgs := range permissionCLIFlagArgs(settings, values) { + if len(permissionArgs) == 0 { + continue + } + for i := 0; i+len(permissionArgs) <= len(remaining); i++ { + if !slices.Equal(remaining[i:i+len(permissionArgs)], permissionArgs) { + continue + } + remaining = append(remaining[:i], remaining[i+len(permissionArgs):]...) + break + } + } + return remaining +} + // CmdBuilder constructs CLI command slices using a fluent API. type CmdBuilder struct { args []string @@ -119,22 +158,8 @@ func (b *CmdBuilder) Permissions(flag string, tools []string, opts CommandOption // Settings appends CLI flags for enabled permission settings. func (b *CmdBuilder) Settings(settings map[string]PermissionSetting, values map[string]bool) *CmdBuilder { - if settings == nil || values == nil { - return b - } - for settingName, setting := range settings { - if !setting.Supported || setting.ApplyMethod != PermissionApplyMethodCLIFlag || setting.CLIFlag == "" { - continue - } - value, exists := values[settingName] - if !exists || !value { - continue - } - if setting.CLIFlagValue != "" { - b.args = append(b.args, setting.CLIFlag, setting.CLIFlagValue) - } else { - b.args = append(b.args, strings.Fields(setting.CLIFlag)...) - } + for _, args := range permissionCLIFlagArgs(settings, values) { + b.args = append(b.args, args...) } return b } diff --git a/apps/backend/internal/agent/agents/passthrough.go b/apps/backend/internal/agent/agents/passthrough.go index ceb16bfc1d..c8038bb64e 100644 --- a/apps/backend/internal/agent/agents/passthrough.go +++ b/apps/backend/internal/agent/agents/passthrough.go @@ -17,7 +17,7 @@ func (p *StandardPassthrough) BuildPassthroughCommand(opts PassthroughOptions) C b := p.Cfg.PassthroughCmd.With(). Model(p.Cfg.ModelFlag, opts.Model). Settings(p.PermSettings, opts.PermissionValues). - Flag(opts.CLIFlagTokens...) + Flag(withoutPermissionCLIFlagDuplicates(opts.CLIFlagTokens, p.PermSettings, opts.PermissionValues)...) switch { case opts.SessionID != "" && !p.Cfg.SessionResumeFlag.IsEmpty(): diff --git a/apps/backend/internal/agent/settings/controller/agent_config.go b/apps/backend/internal/agent/settings/controller/agent_config.go index 6b7467b775..e8be45f2da 100644 --- a/apps/backend/internal/agent/settings/controller/agent_config.go +++ b/apps/backend/internal/agent/settings/controller/agent_config.go @@ -189,11 +189,8 @@ func (c *Controller) PreviewAgentCommand(ctx context.Context, agentName string, // Tolerate malformed entries silently — the preview is informational. cliFlagTokens, _ := cliflags.Resolve(cliFlagsFromDTO(req.CLIFlags)) - // Passthrough: BuildPassthroughCommand emits permission flags via Settings(); - // the launch path (manager_passthrough.go) does not append CLIFlagTokens for - // passthrough, so the preview must match — otherwise permission flags that - // the legacy allow_indexing backfill also pushes into CLIFlags get rendered - // twice (e.g. Auggie's --allow-indexing). + // Passthrough: BuildPassthroughCommand emits permission flags via Settings() + // and appends resolved profile CLI flags, matching manager_passthrough.go. // ACP: mirror lifecycle.CommandBuilder.BuildCommand by appending CLIFlagTokens // after the agent's BuildCommand. var cmd agents.Command @@ -201,6 +198,7 @@ func (c *Controller) PreviewAgentCommand(ctx context.Context, agentName string, cmd = ptAgent.BuildPassthroughCommand(agents.PassthroughOptions{ Model: req.Model, PermissionValues: req.PermissionSettings, + CLIFlagTokens: cliFlagTokens, }) } else { managedRuntimeVersion := "" diff --git a/apps/backend/internal/agent/settings/controller/command_preview_test.go b/apps/backend/internal/agent/settings/controller/command_preview_test.go new file mode 100644 index 0000000000..7dbbc27bec --- /dev/null +++ b/apps/backend/internal/agent/settings/controller/command_preview_test.go @@ -0,0 +1,32 @@ +package controller + +import ( + "context" + "slices" + "testing" + + "github.com/kandev/kandev/internal/agent/agents" + "github.com/kandev/kandev/internal/agent/settings/dto" +) + +// @covers AC-CLI-PASSTHROUGH-LAUNCH-001.5 +func TestController_PreviewAgentCommand_PassthroughIncludesCLIFlags(t *testing.T) { + controller := newTestController(map[string]agents.Agent{ + "claude-acp": agents.NewClaudeACP(), + }) + + result, err := controller.PreviewAgentCommand(context.Background(), "claude-acp", CommandPreviewRequest{ + CLIPassthrough: true, + CLIFlags: []dto.CLIFlagDTO{ + {Flag: "--verbose", Enabled: true}, + }, + }) + if err != nil { + t.Fatalf("PreviewAgentCommand() error = %v", err) + } + + want := []string{"npx", "-y", "@anthropic-ai/claude-code", "--verbose"} + if !slices.Equal(result.Command, want) { + t.Fatalf("preview command = %#v, want %#v", result.Command, want) + } +} diff --git a/apps/backend/internal/agent/settings/controller/controller_test.go b/apps/backend/internal/agent/settings/controller/controller_test.go index ae4d965d9a..74ae69a249 100644 --- a/apps/backend/internal/agent/settings/controller/controller_test.go +++ b/apps/backend/internal/agent/settings/controller/controller_test.go @@ -344,11 +344,10 @@ func TestController_PreviewAgentCommand_PassthroughDisabled(t *testing.T) { } } -// TestController_PreviewAgentCommand_PassthroughDoesNotDuplicateCLIFlag regresses -// the Auggie case where enabling CLI passthrough caused --allow-indexing to be -// emitted twice: once by BuildPassthroughCommand via Settings() and again by an -// unconditional CLIFlagTokens append in the preview. The launch path only adds -// it via Settings(), so the preview must match. +// TestController_PreviewAgentCommand_PassthroughDoesNotDuplicateCLIFlag keeps +// legacy permission CLI flags from being emitted twice when the same token is +// also present in the profile CLI-flags list. Preview and launch use the shared +// passthrough builder, so both paths must emit one token. func TestController_PreviewAgentCommand_PassthroughDoesNotDuplicateCLIFlag(t *testing.T) { permSettings := map[string]agents.PermissionSetting{ "allow_indexing": { diff --git a/docs/plans/claude-passthrough-quiet-default/plan.md b/docs/plans/claude-passthrough-quiet-default/plan.md new file mode 100644 index 0000000000..c9cfa22b50 --- /dev/null +++ b/docs/plans/claude-passthrough-quiet-default/plan.md @@ -0,0 +1,105 @@ +--- +created: 2026-09-03 +status: done +requirements: + - REQ-CLI-PASSTHROUGH-LAUNCH-001 +system_design: + - ../../specs/cli/system-design/passthrough-launch-defaults.md +legacy_specs: [] +--- + +# Implementation Plan: Claude Passthrough Quiet Default + +## Overview + +Remove the forced Claude verbose flag from the built-in passthrough command. +Keep verbose output available through the existing profile CLI-flags field. + +The confirmed root cause is the `--verbose` token in +`NewClaudeACP().PassthroughConfig().PassthroughCmd`. The standard passthrough +builder cannot disable a base-command token, so every Claude passthrough launch +receives the flag. + +## Scope + +### In scope + +- Make the built-in Claude passthrough command quiet by default. +- Preserve explicit `--verbose` opt-in through profile CLI flags. +- Add regression tests for both command forms. +- Document the quiet default and the verbose opt-in path. +- Keep the settings command preview aligned with the launched passthrough + command. + +### Out of scope + +- A new profile setting or user-interface control. +- Changes to ACP launches, other agents, or Claude output rendering. +- Migration of existing agent profiles. + +## Technical approach + +Add a focused test file beside `claude_acp.go`. First, prove that the current +base command violates the quiet-default contract. Then remove only the +hardcoded `--verbose` token. + +Use `BuildPassthroughCommand` in the opt-in test. This boundary proves that the +existing `CLIFlagTokens` path adds `--verbose` without a new setting. + +Update the CLI-flags reference in `docs/public/agents-and-profiles.md`. State +that Claude passthrough uses standard output by default and that `--verbose` is +an explicit profile flag. + +Pass resolved profile CLI flags through the passthrough branch of +`Controller.PreviewAgentCommand`, so the settings preview matches runtime. + +## Tests + +| Acceptance criterion | Evidence | +| --- | --- | +| `AC-CLI-PASSTHROUGH-LAUNCH-001.1` | `TestClaudeACP_PassthroughCmd_DefaultOmitsVerbose` compares the base argv with the quiet command. | +| `AC-CLI-PASSTHROUGH-LAUNCH-001.2` | `TestClaudeACP_PassthroughCmd_VerboseOptIn` builds a command with `CLIFlagTokens` and expects `--verbose`. | +| `AC-CLI-PASSTHROUGH-LAUNCH-001.3` | The focused agent package tests preserve existing command-composition coverage. | +| `AC-CLI-PASSTHROUGH-LAUNCH-001.4` | The opt-in test uses the current profile token input and requires no model change. | +| `AC-CLI-PASSTHROUGH-LAUNCH-001.5` | `TestController_PreviewAgentCommand_PassthroughIncludesCLIFlags` proves that preview output includes the enabled profile flag. | + +## E2E tests + +No browser E2E test is planned. The change has no user-interface path, and a +live Claude dependency would not give deterministic repository evidence. + +The agent-package test covers the complete Kandev-owned command argv. Anthropic +owns the output that each argv mode renders. + +## Work orders + +- [x] [Task 01: Make Claude passthrough quiet by default](task-01-quiet-claude-passthrough.md) + +## Verification results + +- RED: `go test ./internal/agent/agents -run + '^TestClaudeACP_PassthroughCmd_' -count=1` failed two tests before the + production change. The default argv contained the forced flag, and explicit + opt-in produced a duplicate flag. +- GREEN: The focused command passed two tests after the production change. +- Fixup RED: The controller preview regression failed before the passthrough + branch passed `CLIFlagTokens` to the builder. +- Fixup GREEN: The preview now forwards resolved profile CLI flags, and the + passthrough builder removes duplicate permission CLI tokens so preview and + runtime stay identical. Focused controller tests passed, including the + existing no-duplicate regression. +- Full agent package: `go test ./internal/agent/agents -count=1` passed 260 + tests; the controller package passed 237 tests. +- Specification lint passed all files, and the CLI catalog now reports 3 + requirements and 1 design. +- Public documentation: `node --test scripts/validate-public-docs.test.mjs` + passed 61 tests, and `node scripts/validate-public-docs.mjs` validated 41 + published pages. +- `git diff --check` and `gofmt -l` passed for changed Go files. + +## Risks + +- A user who depended on forced verbose output must add `--verbose` to that + profile. +- An overly broad command change can damage model, resume, permission, prompt, + or MCP arguments. The implementation changes one base token only. diff --git a/docs/plans/claude-passthrough-quiet-default/task-01-quiet-claude-passthrough.md b/docs/plans/claude-passthrough-quiet-default/task-01-quiet-claude-passthrough.md new file mode 100644 index 0000000000..c5b0a4513b --- /dev/null +++ b/docs/plans/claude-passthrough-quiet-default/task-01-quiet-claude-passthrough.md @@ -0,0 +1,110 @@ +--- +id: "01-quiet-claude-passthrough" +title: "Make Claude passthrough quiet by default" +status: done +wave: 1 +depends_on: [] +plan: "plan.md" +requirements: + - REQ-CLI-PASSTHROUGH-LAUNCH-001 +acceptance_criteria: + - AC-CLI-PASSTHROUGH-LAUNCH-001.1 + - AC-CLI-PASSTHROUGH-LAUNCH-001.2 + - AC-CLI-PASSTHROUGH-LAUNCH-001.3 + - AC-CLI-PASSTHROUGH-LAUNCH-001.4 + - AC-CLI-PASSTHROUGH-LAUNCH-001.5 +system_design: + - ../../specs/cli/system-design/passthrough-launch-defaults.md +--- + +# Task 01: Make Claude Passthrough Quiet by Default + +## Summary + +Remove the forced `--verbose` argument from the built-in Claude passthrough +command. Preserve verbose output as an explicit profile CLI flag. + +## In scope + +- Add focused command-construction regression tests for Claude passthrough. +- Remove the hardcoded default `--verbose` argument. +- Preserve all other Claude passthrough arguments and settings. +- Document the default and the profile CLI-flag opt-in. +- Keep the settings command preview aligned with runtime CLI flags. + +## Out of scope + +- A dedicated verbose-output setting. +- Changes to ACP launches or other agent definitions. +- Browser tests and live Claude integration tests. + +## Acceptance + +- The regression test fails before the production change because the base + command contains `--verbose`. +- The default Claude passthrough argv omits `--verbose` after the correction. +- An enabled profile CLI flag adds `--verbose` to the built command. +- The public profile reference explains the quiet default and verbose opt-in. +- The passthrough command preview includes enabled profile CLI flags. + +## Verification + +```bash +go test ./internal/agent/agents -run '^TestClaudeACP_PassthroughCmd_' -count=1 +go test ./internal/agent/agents -count=1 +go test ./internal/agent/settings/controller -run 'TestController_PreviewAgentCommand_PassthroughIncludesCLIFlags' -count=1 +go test ./internal/agent/settings/controller -count=1 +node --test scripts/validate-public-docs.test.mjs +node scripts/validate-public-docs.mjs +``` + +Run the Go commands from `apps/backend`. Run the Node commands from the +repository root. + +## Files likely touched + +- `apps/backend/internal/agent/agents/claude_acp.go` +- `apps/backend/internal/agent/agents/claude_acp_passthrough_test.go` +- `apps/backend/internal/agent/settings/controller/agent_config.go` +- `apps/backend/internal/agent/settings/controller/command_preview_test.go` +- `docs/public/agents-and-profiles.md` +- `docs/specs/INDEX.md` + +## Dependencies + +None. + +## Risks + +- The test must distinguish the built-in default from an explicit profile + flag. +- The production edit must not change other passthrough arguments. + +## Parallelism + +`sequential` + +## Inputs + +- `docs/specs/cli/requirements/passthrough-launch-defaults.md` +- `docs/specs/cli/system-design/passthrough-launch-defaults.md` +- GitHub issue #3305 and the diagnostic reproduction from this task. + +## Results + +- RED: The focused regression command failed both tests before the production + change for the expected hardcoded-flag reasons. +- GREEN: The focused regression command passed two tests after the production + change. +- Fixup RED: The new controller preview test failed before profile CLI tokens + were forwarded through the passthrough preview branch. +- Fixup GREEN: The preview and runtime now share the resolved CLI-token path, + with duplicate permission tokens removed. Focused and full controller tests + passed. +- Full agent package: `go test ./internal/agent/agents -count=1` passed 260 + tests; `go test ./internal/agent/settings/controller -count=1` passed 237 + tests. +- Specification lint and the CLI catalog checks passed after adding the + preview requirement. +- Public documentation checks passed 61 tests and validated 41 published pages. +- `git diff --check` and `gofmt -l` passed for changed Go files. diff --git a/docs/public/agents-and-profiles.md b/docs/public/agents-and-profiles.md index 1f05588b3d..751c49acb2 100644 --- a/docs/public/agents-and-profiles.md +++ b/docs/public/agents-and-profiles.md @@ -270,6 +270,8 @@ Each flag entry has a raw value, description, enabled state, and an agent-specif The field is not a shell script. Pipes, redirects, variable expansion, and command substitution do not run as shell syntax. Empty or malformed quoting is rejected. Keep separate profiles for materially different permission or workspace flags, and recheck customized flags after upgrading the CLI. +Claude CLI passthrough uses standard output by default. Add an enabled `--verbose` entry to the profile CLI flags when you need diagnostic output. + Some older profiles contain compatibility fields such as Auggie's `allow_indexing`; current launch behavior is represented by the active profile settings and flags. ### ACP command prefixes diff --git a/docs/specs/INDEX.md b/docs/specs/INDEX.md index d01c7c8e3d..3d901c552a 100644 --- a/docs/specs/INDEX.md +++ b/docs/specs/INDEX.md @@ -8,7 +8,7 @@ This catalog is the entry point for the system-oriented specification layout. Ea | --- | --- | --- | --- | | Agents | [README](agents/README.md) | complete | 27 requirements, 17 designs | | Auth | [README](auth/README.md) | complete | 7 requirements, 2 designs | -| CLI | [README](cli/README.md) | complete | 2 requirements, 0 designs | +| CLI | [README](cli/README.md) | complete | 3 requirements, 1 designs | | Costs | [README](costs/README.md) | complete | 2 requirements, 0 designs | | Desktop | [README](desktop/README.md) | complete | 1 requirements, 1 designs | | Executors | [README](executors/README.md) | complete | 3 requirements, 6 designs | diff --git a/docs/specs/cli/README.md b/docs/specs/cli/README.md index 867f52eb6b..3867176716 100644 --- a/docs/specs/cli/README.md +++ b/docs/specs/cli/README.md @@ -32,12 +32,13 @@ and CLI-specific compatibility behavior. - [CLI-Mode Task Parity (Kanban)](requirements/cli-mode-parity.md) - [Native Kandev CLI](requirements/native-kandev-cli.md) +- [Passthrough Launch Defaults](requirements/passthrough-launch-defaults.md) ### System design -- None. +- [Passthrough Launch Defaults](system-design/passthrough-launch-defaults.md) ## Migration record diff --git a/docs/specs/cli/requirements/passthrough-launch-defaults.md b/docs/specs/cli/requirements/passthrough-launch-defaults.md new file mode 100644 index 0000000000..381d63786a --- /dev/null +++ b/docs/specs/cli/requirements/passthrough-launch-defaults.md @@ -0,0 +1,37 @@ +--- +status: active +system: cli +created: 2026-09-03 +owners: + - kandev +--- + +# Passthrough Launch Defaults Requirements + +## Overview + +CLI passthrough gives users the native interactive output of an agent. Built-in +launch arguments must preserve that standard output unless a profile selects a +different mode. + +## Requirements + +### REQ-CLI-PASSTHROUGH-LAUNCH-001: Keep diagnostic output optional + +**Intent:** A user can operate Claude CLI passthrough without forced diagnostic +output. The user can enable diagnostic output for a selected profile. + +#### Acceptance criteria + +- **AC-CLI-PASSTHROUGH-LAUNCH-001.1:** When Kandev starts built-in Claude CLI passthrough without an enabled `--verbose` profile flag, the launched command shall omit `--verbose`. +- **AC-CLI-PASSTHROUGH-LAUNCH-001.2:** When the Claude profile contains an enabled `--verbose` CLI flag, the launched passthrough command shall include `--verbose`. +- **AC-CLI-PASSTHROUGH-LAUNCH-001.3:** The quiet default shall preserve model selection, permission flags, session resume, prompt delivery, and MCP configuration. +- **AC-CLI-PASSTHROUGH-LAUNCH-001.4:** Existing profiles shall require no data migration. A user can add `--verbose` through the existing CLI-flags field. +- **AC-CLI-PASSTHROUGH-LAUNCH-001.5:** When a profile contains enabled CLI flags, the passthrough command preview shall show the same flags that the launched command receives. + +## Out of scope + +- A dedicated verbose-output setting. +- Changes to Claude ACP execution. +- Changes to the launch defaults of other agents. +- Changes to Claude Code output rendering. diff --git a/docs/specs/cli/system-design/passthrough-launch-defaults.md b/docs/specs/cli/system-design/passthrough-launch-defaults.md new file mode 100644 index 0000000000..d99cdbadb8 --- /dev/null +++ b/docs/specs/cli/system-design/passthrough-launch-defaults.md @@ -0,0 +1,80 @@ +--- +status: current +system: cli +requirements: + - REQ-CLI-PASSTHROUGH-LAUNCH-001 +--- + +# Passthrough Launch Defaults System Design + +## Purpose and boundaries + +The CLI system owns the arguments that Kandev uses to launch agent CLIs in +passthrough mode. This design changes only the built-in Claude passthrough +default. + +## Requirement mapping + +| Requirement | Design section | +| --- | --- | +| `REQ-CLI-PASSTHROUGH-LAUNCH-001` | [Command composition](#command-composition), [Compatibility](#compatibility), [Verification](#verification) | + +## Components and responsibilities + +- `agents.NewClaudeACP` defines the required base command for Claude + passthrough. +- `agents.StandardPassthrough.BuildPassthroughCommand` adds model, permission, + resume, prompt, MCP, and profile CLI arguments. +- `lifecycle.Manager.profileCLIFlagTokens` resolves enabled profile CLI flags + before it builds the passthrough command. +- `agent/settings/controller.Controller.PreviewAgentCommand` builds the same + passthrough argv for the settings preview, including resolved profile CLI + flags. + +## Command composition + +The Claude base command contains `npx -y @anthropic-ai/claude-code`. It does not +contain `--verbose`. + +The standard passthrough builder appends enabled profile CLI arguments. Thus, a +profile with `--verbose` keeps access to the Claude verbose view mode. + +The change does not alter the ACP adapter command. It also does not alter model, +permission, resume, prompt, or MCP argument composition. + +The settings command preview passes resolved profile CLI flags to the same +passthrough builder. The preview therefore matches the command that Kandev +launches for the selected profile. + +## Compatibility + +The profile model and storage remain unchanged. Existing profiles receive the +quiet default unless their CLI-flags list already contains `--verbose`. + +No migration or new user-interface control is necessary. The current CLI-flags +field remains the explicit configuration path. + +## Failure behavior + +Malformed profile CLI flags keep the current warning and omission behavior. +This change adds no new error path. + +## Security and observability + +The change does not alter permission flags, environment handling, or command +redaction. Existing passthrough process logs continue to record the redacted +launch command. + +## Verification + +A focused agent-package test inspects the complete tokenized command. A +controller test compares passthrough preview output with an enabled profile +flag. The tests cover the quiet default and the explicit `--verbose` opt-in. + +A live Claude session is not necessary for this contract. The command argv is +the Kandev-owned boundary, while Claude owns its output rendering. + +## Related decisions + +None. The design uses the existing profile CLI-flags contract and adds no new +architecture boundary.