Skip to content

fix(agentsetup): preserve user settings across setup runs - #366

Merged
rodboev merged 20 commits into
mainfrom
pr/agent-setup-12
Nov 13, 2026
Merged

rodboev merged 20 commits into
mainfrom
pr/agent-setup-12

Conversation

@rodboev

@rodboev rodboev commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Apps can rerun setup without losing user settings on MCP entries or replacing hooks whose current executable path still resolves. Hook checks use the workspace the app names, and kit reports unknown for agents it can't inspect while continuing with the others. Missing items that setup can repair point to the app's repair command.

Codex and Muse hooks now run on Windows even when a path or argument contains spaces, $, or %, because kit writes them as literal PowerShell commands such as & 'C:\Program Files\hook.exe' 'run'. The next setup run rewrites hooks in the old form. An app that passes its own Windows command for these agents gets an error asking for a PowerShell one.

Used by kenn-io/kata#1105 and kenn-io/roborev#1778.

People often add credentials and other settings to an MCP server after
setup. Replacing the entry on the next setup run discards those settings
and can interrupt their connection.

Keep user-owned values while applying the application's launch settings.
Checks use the same ownership rule so those extra settings do not trigger
unnecessary repairs.
Codex rejects settings that belong to a different transport, so carrying
those settings across a switch prevents it from loading the config.
Preserve settings that remain valid for the selected transport.

Inline tables can also contain arrays of tables. Their elements must stay
in an array when setup updates the surrounding server entry.
Codex reports an empty tool list for a disabled server. A generic success
message makes that look like a running server with no tools. Name the
disabled state while keeping the setup result current.
A disabled server cannot serve tools even when its configuration matches.
Tell the user how to enable it so readiness reflects what the agent can use.

A transport switch can leave Codex unable to load the old settings. Stop
and name those settings so users can resolve the conflict without losing
their choices. Header names need case-insensitive matching, and Copilot's
default tool list must not replace a selection made by the user.
Codex prefers a retained bearer token over a supplied Authorization
header. Report the conflict so users can choose which credential to keep
without losing their existing configuration.

Nested tables inside arrays must stay with their array elements. Moving
them outside the array makes the entire Codex config unreadable.
Apps need to rerun setup as agent configs change. Replacing a whole MCP entry erased user settings, while checking only the app's old executable path marked a working hook as stale.
@roborev-ci

roborev-ci Bot commented Nov 12, 2026

Copy link
Copy Markdown

roborev: Combined Review (6e2e185)

Verdict: Changes require fixes for 7 findings.

High

  • agentmcp/config_document.go:49: Changing a Codex server command can leave its configuration unreadable when the entry uses inline TOML (braces) and contains lists of objects. The writer turns list elements into duplicate keys and flattens nested objects. This affects every update with that shape.

    Fix: Recursively preserve objects, lists, and nested tables in both inline replacement paths. Where: tomlInline, editTOML, agentmcp/config_document.go

  • agentmcp/config_document.go:83-86: Switching a Codex server between a local command and HTTP can prevent Codex from loading its configuration. The merge retains settings incompatible with the new connection type. This affects switches with those settings already present.

    Fix: Reject retained settings incompatible with the selected connection type with an actionable error before writing, while preserving compatible settings. Where: planDocument, env, env_vars, cwd, http_headers, env_http_headers, bearer_token_env_var, agentmcp/config_document.go

Medium

  • agentmcp/config_document.go:105-106: Rerunning Copilot setup replaces a user's chosen server tools, such as ["search"], with ["*"], meaning all tools. The merge overwrites their selection even when launch settings match. This affects every rerun with a custom selection.

    Fix: Preserve existing tool selections and apply the wildcard default only when no selection is present. Where: Server.native, mergeEntry, tools, agentmcp/config_document.go

  • agentmcp/config_document.go:49-53: Checking a disabled Codex server can report readiness without enablement guidance despite unavailable tools. The comparison preserves the disabled value, and the probe accepts an empty tools object as success. This affects otherwise matching entries when status returns tools={}.

    Fix: Check whether the server is disabled before probing and return a non-ready result with enablement instructions, while preserving the disabled setting during installation. Where: checkCodex, enabled, agentmcp/config_document.go

  • agenthook/config.go:283-288: Rerunning setup with an unavailable executable can replace working hooks in Cursor, Copilot, and Hermes. These agents delete and recreate hooks without checking the installed executable. This affects reruns changing only executable paths.

    Fix: Pass the supplied workspace through hook planning and preserve installed commands whose executables resolve when arguments and other settings match. Where: WorkspaceDir, planConfig, planDirectJSONConfig, planHermesConfig, agenthook/config.go

  • agentmcp/config_document.go:49: Updating Codex HTTP authentication can silently configure both a token from an environment variable and an Authorization header. The merge retains the old credential source despite their mutual exclusion. This affects updates switching between these sources in either direction.

    Fix: Validate merged authentication settings regardless of header-name case and reject conflicting credential sources before writing. Where: planDocument, bearer_token_env_var, http_headers, env_http_headers, agentmcp/config_document.go

  • agenthook/check.go:108-111: Windows users checking Codex hooks get “unknown” for literal executable paths containing '%' or arguments containing '$'. The check applies raw command restrictions to generated commands. This affects structured options that previously bypassed those restrictions.

    Fix: Recognize literal commands generated from structured options while inspecting installed executables and continuing to reject raw commands that require shell interpretation. Where: Check, rawHookWords, agenthook/check.go


Reviewers: 3x codex, codex (security), codex (test-value) | Synthesis: codex, 8m9s | Total: 22m26s

Users need setup to keep their own settings and trusted hook registrations when an application changes its launch path. A disabled Codex server also needs an actionable status even when its command has drifted.

Preserving settings is limited to launch-only updates on the same transport. Applications that supply other fields replace the entry so kit does not need to infer each agent's credential and transport rules.
Setup must retain user settings without changing large numbers in Claude Code's configuration. Keep the original encoded values intact while comparing registrations.

The existing matching and reporting paths already cover the same cases, so the extra passes and duplicate tests add maintenance without extending the supported behavior.
A launch update must retain user settings without rounding large numbers. One registration exercises both requirements and keeps the regression close to the ownership rule it protects.
Setup spans config writes, native hook checks, and command output. Keeping each behavior at the layer that owns it makes failures easier to diagnose while retaining coverage of user settings and trusted moved hooks.

Manual commands use different shell quoting on Windows, so the print check must match the platform's output.
@roborev-ci

roborev-ci Bot commented Nov 12, 2026

Copy link
Copy Markdown

roborev: Combined Review (481821d)

Verdict: Changes require fixes for 1 finding.

Medium

  • agenthook/json.go:349-351: On Windows, checking generated Codex hook commands containing $, `, or % always reports “unknown” despite Codex trusting them; setup also overwrites still-working hooks after executable moves. The parser rejects these literal characters.

    Fix: Preserve structured commands, decode generated Windows arguments literally during readiness checks and moved-hook matching, retain conservative restrictions for raw overrides, and cover both cases with literal dollar-sign arguments. Where: hookWords, Check, sameMovedHook, rawHookWords, agenthook/json.go, agenthook/check.go, check_test.go


Reviewers: 3x codex, codex (security), codex (test-value) | Synthesis: codex, 4m23s | Total: 14m4s

Users switching Claude Code between HTTP and SSE need settings from the old transport removed. Keeping them can leave a registration with settings intended for a different connection.

Manual setup should explain when a selected agent or server name has no CLI command and continue to the remaining agents. Existing JSON values also need to retain their number and URL text when setup updates a registration.
A hook can carry commands for more than one platform. Callers need to know which arguments determine whether a moved executable keeps its registration.
Windows short paths can contain a tilde, so rejecting that character makes an installed hook look unavailable and can replace it on the next setup run. Relative PATH entries also need to resolve in the workspace where the hook runs.

Keep JSON encoding errors visible so an unsupported value cannot be mistaken for a successful update.
@roborev-ci

roborev-ci Bot commented Nov 12, 2026

Copy link
Copy Markdown

roborev: Combined Review (ee26134)

Verdict: Changes require fixes for 1 finding.

Medium

  • agenthook/check_command_windows.go:20-21: On Windows, Codex hooks installed from executable and argument values report “unknown” whenever arguments contain literal $ or executable paths contain %; setup also replaces working hooks after executable moves. Parsing rejects these literals as shell syntax.

    Fix: Decode generated commands using Windows argument quoting to preserve literal characters during readiness checks and moved-executable matching, while retaining restrictions for raw shell overrides. Where: rawHookWords, agenthook/check_command_windows.go


Reviewers: 3x codex, codex (security), codex (test-value) | Synthesis: codex, 3m37s | Total: 12m25s

The manual setup command needs PowerShell's call operator on Windows. The check must accept that operator so it can verify the command remains available after an unsupported agent.
@roborev-ci

roborev-ci Bot commented Nov 12, 2026

Copy link
Copy Markdown

roborev: Combined Review (aca6387)

Verdict: Changes require fixes for 1 finding.

Medium

  • agenthook/json.go:372-374: Windows users checking matching, trusted Codex hooks see unknown; requesting another executable replaces a still-resolvable command. Parsing rejects literal $, backtick, or % even quoted. This affects every generated hook containing these characters in paths or arguments.

    Fix: Decode generated commands using literal Windows quoting during readiness checks and moved-hook matching, while keeping conservative shell restrictions for raw overrides. Where: hookWords, rawHookWords, Check, sameMovedHook, agenthook/json.go, agenthook/check_command_windows.go


Reviewers: 3x codex, codex (security), codex (test-value) | Synthesis: codex, 5m0s | Total: 14m6s

@rodboev
rodboev marked this pull request as ready for review November 13, 2026 00:15
Codex and Muse run Windows hooks in PowerShell. Commands with special
characters must remain literal so checks can find the executable and setup
can keep an existing hook after its executable moves.
@roborev-ci

roborev-ci Bot commented Nov 13, 2026

Copy link
Copy Markdown

roborev: Combined Review (f29171c)

Verdict: Changes require fixes for 2 findings.

Medium

  • agenthook/command.go:70-71: Codex/Muse setup succeeds but breaks Windows hooks whenever you supply a base command and Windows override without a PowerShell override. It writes only the Unix command; Muse reinstalls also remove existing overrides.

    Fix: Translate the Windows command override’s literal arguments into PowerShell syntax when no PowerShell override is supplied, preserving explicit overrides and generated quoting. Where: profileCommands, agenthook/command.go

  • agenthook/check_command_powershell.go:24-25: On Windows, rerunning setup preserves a broken hook when its executable exists but is quoted without &, which tells PowerShell to run it. The comparison treats it as equivalent to the generated command.

    Fix: Require the PowerShell call operator before quoted executables, continue accepting valid bare commands, and replace malformed registrations with generated invocations during setup. Where: powerShellHookWords, agenthook/check_command_powershell.go


Reviewers: 3x codex, codex (security), codex (test-value) | Synthesis: codex, 5m22s | Total: 17m26s

A caller that passes its own Windows command but no PowerShell one got a Codex or Muse hook with only the Unix command, which fails on Windows. Fall back to the caller's Windows command instead of dropping it.

PowerShell runs a quoted executable only after the & call operator, so a hook written as 'C:\hook.exe' arg never runs. Treat it as needing repair instead of matching the generated command.
@roborev-ci

roborev-ci Bot commented Nov 13, 2026

Copy link
Copy Markdown

roborev: Combined Review (36ffac6)

Verdict: Changes require fixes for 1 finding.

Medium

  • agenthook/command.go:74: Codex or Muse setup succeeds, but hooks fail on Windows. Windows command text is copied into PowerShell without its required & execution operator. This happens whenever quoted executable paths contain spaces and no PowerShell override is supplied.

    Fix: Preserve explicit PowerShell overrides, and otherwise convert the Windows executable and arguments into a properly quoted PowerShell invocation with the required execution operator or return an actionable error. Where: agenthook/command.go, profileCommands, CommandWindows, CommandPowerShell


Reviewers: 3x codex, codex (security), codex (test-value) | Synthesis: codex, 5m12s | Total: 17m36s

Codex and Muse run hooks in PowerShell, which treats a leading quoted path as a string instead of a program. A caller's Windows command such as "C:\Program Files\hook.exe" run therefore installed cleanly but never ran. Prefix the call operator so the same command runs.
@roborev-ci

roborev-ci Bot commented Nov 13, 2026

Copy link
Copy Markdown

roborev: Combined Review (db074ce)

Verdict: Changes require fixes for 2 findings.

Medium

  • agenthook/check_command_powershell.go:52-53: Windows users supplying quoted CommandWindows overrides without PowerShell overrides see “unknown” even unchanged; executable moves make setup replace working hooks, invalidating Codex trust. The parser rejects double quotes every time.

    Fix: Accept literal double-quoted words and preserve working hooks when executables move, while rejecting expansions, unsupported expressions, and quoted executables without the call operator. Where: powerShellHookWords, agenthook/check_command_powershell.go

  • agenthook/command.go:76-78: Windows users configuring Codex/Muse hooks through CommandWindows without a PowerShell override get different arguments whenever hooks run with values such as "$value". The code adds & to launch quoted executables but lets PowerShell expand arguments.

    Fix: Decode the Windows executable and arguments and quote them literally for PowerShell, while preserving explicit PowerShell overrides. Where: profileCommands, agenthook/command.go


Reviewers: 3x codex, codex (security), codex (test-value) | Synthesis: codex, 5m2s | Total: 15m13s

…itten

Codex and Muse run commandWindows in PowerShell and command in a POSIX shell. Converting a caller's Win32 command into PowerShell let PowerShell expand values such as $HOME, so a caller that sets CommandWindows for these agents now gets an error asking for CommandPowerShell instead.

Writing command with Windows quoting on Windows broke the moved-hook check, which reads it as POSIX, so these agents now always get a POSIX command there.

A moved hook is kept only when the other platform's command names the same executable with the same arguments. A stale or replaced override gets rewritten instead of silently surviving. Double-quoted literal words count as plain text, so a hand-written command quoted that way no longer reports unknown.
@roborev-ci

roborev-ci Bot commented Nov 13, 2026

Copy link
Copy Markdown

roborev: Combined Review (a33b742)

Verdict: Changes require fixes for 1 finding.

Medium

  • agentsetup/items.go:88: When you request manual setup commands for Cursor, its name appears twice in the unavailable-command message. The formatter always adds an agent prefix. This affects every request whose error already identifies the agent.

    Fix: Omit the agent prefix when the error already identifies the agent, and retain it otherwise. Where: Print, agentsetup/items.go, TestPrint, agentsetup/review_test.go


Reviewers: 3x codex, codex (security), codex (test-value) | Synthesis: codex, 4m8s | Total: 12m39s

…able

Every unavailable-command error already names its agent, so the extra prefix printed it twice, as in "# cursor: manual MCP command unavailable for agent "cursor"".
@roborev-ci

roborev-ci Bot commented Nov 13, 2026

Copy link
Copy Markdown

roborev: Combined Review (2c92372)

Verdict: No findings at or above medium severity.


Reviewers: 3x codex, codex (security), codex (test-value) | Synthesis: codex, 15s | Total: 9m12s

@rodboev
rodboev merged commit cecbdf0 into main Nov 13, 2026
15 checks passed
@rodboev
rodboev deleted the pr/agent-setup-12 branch November 13, 2026 04:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant