Squash merge PR #1240.\n\nValidated locally from the PR head because trusted-author GitHub CI was skipped for this fork PR:\n- bun run validate\n- bun run build:all\n- bun run test:all\n- CCS_E2E_SKIP_BUILD=1 bun run test:e2e
Closes#1218.
When the user invokes `ccs <profile> agents` (and other Claude subcommands
like `mcp`, `doctor`, `plugin`, ...), CCS was unconditionally injecting
session-only Claude flags (`--append-system-prompt`, `--disallowedTools`,
`--settings`, official-channels plugin specs) and forwarding the
`DISABLE_TELEMETRY=1` env var from profile settings. Each of those
either errored out the subcommand (`error: unknown option
'--append-system-prompt'`) or silently flipped Claude into
non-interactive list mode, so the new `claude agents` agent view never
opened under CCS.
Add a small `claude-subcommand-detector` that recognizes the documented
Claude subcommand set after skipping known value-taking flags, then have
the three steering-prompt injectors (websearch, image-analysis, browser),
the cliproxy and settings launchers, and the official-channels plan
short-circuit when a subcommand invocation is detected. Also strip
`DISABLE_TELEMETRY` from the spawned env only for subcommand
invocations — upstream Claude Code uses that var as a kill switch for
the subcommand TUIs, and the user's telemetry preference still applies
to every normal interactive session.
Verified end-to-end against `ccs glm agents` and `ccs ck agents`: the
agent view opens correctly, no `unknown option` error, and all 1938
existing tests pass.
Refactor getGeminiPlusOAuthCredentialError / getGeminiAuthUrlCredentialError
behind a provider-table-driven helper (PLUS_OAUTH_ENV_BY_PROVIDER) and add
getPlusOAuthCredentialError / getPlusAuthUrlCredentialError exports covering
both gemini and agy. Existing gemini-named exports preserved as aliases so
PR #1131 test surface remains unchanged. New AGY-parity test cases mirror
the original gemini diagnostics. CLI call sites in triggerOAuth /
handlePasteCallbackMode stay gemini-only; dashboard handler in the
follow-up commits will use the generalized exports for both providers.
Refs #1208
Resolve the dispatcher and dashboard conflicts from origin/dev while preserving the configured local CLIProxy port behavior. Also harden lifecycle port fallback and wait for the actual stopped port during binary installs.
All call sites that spawn or probe CLIProxyApiPlus now read
cliproxy_server.local.port from config via resolveLifecyclePort()
instead of using the hardcoded CLIPROXY_DEFAULT_PORT constant.
- Move resolveLifecyclePort helper to src/cliproxy/config/port-manager.ts
- Fix 7 call sites: ccs.ts, config-command.ts, copilot-executor.ts,
lifecycle.ts, binary-manager.ts, cliproxy-stats-routes.ts,
cliproxy-local-proxy.ts
- Remove duplicate resolveLocalCliproxyPort helper
- Cache port resolution in /proxy-status handler to avoid repeated I/O
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Issue #1161. Sweeps 127 files to import from
src/config/config-loader-facade.ts instead of unified-config-loader or
utils/config-manager directly.
WRITE callers (32 files): replaced raw saveUnifiedConfig /
mutateUnifiedConfig / updateUnifiedConfig calls with the facade's
cache-coherent wrappers saveConfig / mutateConfig / updateConfig. This
fixes a latent stale-cache window where direct writes through the
underlying loader bypassed the facade's memoization.
READ callers (95 files): mechanical import-path migration only —
function names unchanged because the facade re-exports them. No
behavior change.
Also updated:
- tests/unit/utils/browser/browser-setup.test.ts (DI interface rename)
- src/management/checks/image-analysis-check.ts (dynamic import rename)
- src/web-server/health-service.ts (dynamic require rename)
- src/ccs.ts (path prefix fix from sweep script)
After sweep: zero raw write callers remain outside src/config/. Direct
imports of config-manager remain only for symbols not in the facade
(getConfigPath, getCcsDirSource, etc). Behavior unchanged; full suite
passes 1824/1824.
Out of scope: switching loadOrCreateUnifiedConfig() callers to
getCachedConfig() — needs per-callsite cache-safety analysis. Tracked
as follow-up.
Refs #1161
Phase 07 of #1162. Splits tool-sanitization + codex-reasoning proxy spawn
out of the orchestrator:
- src/cliproxy/executor/proxy-chain-builder.ts (185 LOC):
buildProxyChain + ProxyChainContext / ProxyChainResult types.
DI escape hatch (_ToolSanitizationProxy / _CodexReasoningProxy) for
testability without refactoring the proxy classes (Bun module cache
blocks mock.module of already-loaded modules).
- 9 unit tests covering codex-only, tool-san only, both-together,
spawn failure swallowing, env propagation.
index.ts: 716 -> 665 LOC (-51). HTTPS tunnel kept inline because
tunnelPort is needed by image-analysis resolution before first-pass
buildClaudeEnvironment — folding it in would also require moving image
analysis. Two-pass buildClaudeEnvironment dance preserved exactly.
Behavior unchanged; full suite passes 1824/1824.
Refs #1162
Phase 06 of #1162. Splits the largest remaining concern out of the
orchestrator into a focused module:
- src/cliproxy/executor/auth-coordinator.ts (397 LOC):
handleLogout, handleImport, resolveSkipLocalAuth, runAntigravityGate,
ensureProviderAuthentication, runPreflightQuotaCheck,
runAccountSafetyGuards, ensureModelConfiguration,
ensureProviderSettingsFile. Preserves load-bearing ordering of
antigravity gate -> auth -> token refresh -> quota check.
- 36 unit tests covering --auth/--logout/--import early exit,
antigravity gate refusal/acceptance, OAuth trigger paths, composite
providers, remote-proxy skipLocalAuth.
index.ts: 895 -> 716 LOC (-179). Behavior unchanged; full suite passes
1824/1824. Module is 397 LOC (over the <200 ideal) — kept whole because
the auth ordering contract should not be split across files.
Refs #1162
Phases 04+05 of #1162. Splits two more concerns out of the orchestrator:
- src/cliproxy/executor/browser-launch-setup.ts (118 LOC):
resolveBrowserLaunchFlags + resolveBrowserRuntime. Encapsulates browser
flag resolution, attach config, blocked-override warning, and runtime
setup including MCP sync.
- src/cliproxy/executor/account-resolution.ts (197 LOC):
resolveRuntimeQuotaMonitorProviders, resolveAccounts (--accounts /
--use / --nickname / OAuth ban-risk warn / default touch),
applyAccountSafetyGuards, touchDefaultAccount.
- New tests: 184 + 430 LOC covering both modules.
index.ts: 1045 -> 895 LOC (-150). resolveRuntimeQuotaMonitorProviders
re-exported from index.ts for __testExports backwards compat. Behavior
unchanged; full suite passes 1824/1824.
Refs #1162
Phase 03 of #1162. Splits proxy + binary resolution out of the orchestrator
into a focused module:
- src/cliproxy/executor/proxy-resolver.ts (196 LOC):
ResolvedProxy + ResolveExecutorProxyContext interfaces, resolveExecutorProxy
function. Encapsulates proxy config resolution, port mutation, remote
reachability check, fallback prompt, local backend selection, and
binary acquisition.
- src/cliproxy/executor/__tests__/proxy-resolver.test.ts: 10 unit tests
covering local/remote/fallback paths.
index.ts: 1168 -> 1045 LOC (-123). Removes 9 now-unused imports. Browser
flag handling intentionally left in index.ts for Phase 04. Behavior
unchanged; full suite passes 1824/1824.
Refs #1162
Phases 01+02 of #1162. Splits executor flag parsing/validation out of the
1428-LOC orchestrator into a focused module:
New files:
- src/cliproxy/executor/arg-parser.ts (~500 LOC):
readOptionValue, hasGitLabTokenLoginFlag, getGitLabTokenLoginFlagName,
CCS_FLAGS + filterCcsFlags, ParsedExecutorFlags + parseExecutorFlags,
validateFlagCombinations (process.exit semantics preserved for parity).
- src/cliproxy/executor/__tests__/arg-parser.test.ts: 45 unit tests.
- src/cliproxy/executor/__tests__/index-characterization.test.ts:
TDD baseline (16 pass + 7 skipped scenarios at the spawn/dynamic-import
boundary; mock simplification deferred to a follow-up phase).
index.ts: 1428 -> 1175 LOC (-253). Re-exports preserved at module bottom
for backwards compatibility. Behavior unchanged; full test suite passes
(1824/1824).
Refs #1162
Emit auth-stage events around OAuth round-trips and profile lookups so auth
flows are traceable end-to-end. Doctor command emits dispatch stages around
each health-check phase for clearer diagnostic logs.
Refs #1141, #1138
- Stop cleanup interval when all auth sessions are removed (unregister,
cancel, cancelAll) so the Node event loop isn't held alive.
- getActiveSessionForProvider now returns the most recent session by
startedAt instead of the first map entry.
- HTTPS tunnel includes port in Host header when non-443.
forceValidChars() now ensures leading char is letter or underscore
(catches dot, hyphen, colon, slash — not just digits). registerTools()
always registers in mapping so collisions are detected regardless of
tool registration order.
sanitizeToolName() fallback now forces valid characters (replaces
unsupported chars with underscores, ensures leading letter/underscore)
instead of only truncating. registerTools() disambiguates colliding
sanitized names with numeric suffixes so restoreToolUse() always maps
back to the correct original.
- base-config-loader.ts moved from src/cliproxy/ to src/cliproxy/config/,
so __dirname relative path needs one more .. to reach config/ dir
- Update mock.module paths for proxy-detector and routing-strategy
in non-colocated test files
- Add src/**/__tests__/** to tsconfig.json exclude list
- Add ignores pattern for __tests__ in eslint.config.mjs
- Fix ui/src/lib/api-client.ts import path for provider-entitlement-types
- Remove stale build artifacts from ui/src/lib/ and tests/mocks/