Remove global hook registration from installWebSearchHook() - now only
copies hook file to ~/.ccs/hooks/. Hook registration handled by
ensureProfileHooks() which writes to per-profile settings.
Changes:
- hook-installer.ts: Remove ensureHookConfig() call
- ccs.ts: Remove redundant installWebSearchHook() call
- hook-config.ts: Add getClaudeSettingsPath() for test isolation
- postuninstall.js: add file logging for debugging on error
- profile-hook-injector.ts: use 'wx' flag for atomic marker creation
- profile-hook-injector.ts: include parse error message in debug log
- install-command.ts: use actual counts for consistent semantics
- Windows tests: align Section 7 with per-profile hook architecture
- ClaudeSymlinkManager.uninstall() now returns count for accurate reporting
- install-command.ts uses return value instead of always incrementing
- claude-dir-installer.ts uses getCcsHome() directly instead of string manipulation
- Add TODO comments to tests explaining --install is a no-op
Fix test isolation in symlink managers and checkers to prevent tests
from touching user's real ~/.ccs/ and ~/.claude/ directories.
Files fixed:
- src/api/services/profile-writer.ts - use getCcsDir()
- src/management/checks/symlink-check.ts - use functions instead of
module-level constants
- src/utils/claude-dir-installer.ts - use getCcsDir() for CCS paths
- src/utils/claude-symlink-manager.ts - use getCcsHome()/getCcsDir()
Replace os.homedir() with getCcsDir() in 11 source files to ensure
tests don't touch the user's real ~/.ccs/ directory. The getCcsDir()
function from config-manager.ts respects CCS_HOME env var for
test isolation.
Files fixed:
- src/auth/profile-detector.ts
- src/auth/profile-registry.ts
- src/delegation/headless-executor.ts
- src/delegation/session-manager.ts
- src/glmt/glmt-transformer.ts
- src/management/checks/config-check.ts
- src/management/checks/profile-check.ts
- src/management/checks/system-check.ts
- src/management/instance-manager.ts
- src/management/shared-manager.ts
- src/utils/update-checker.ts
Ensure all hook-related modules use getCcsDir() from environment.ts
for consistent test isolation. Prevents tests from touching the user's
real ~/.ccs/ directory during test runs.
Changes:
- hook-config.ts: Use getCcsDir() for hookConfigPath
- hook-installer.ts: Use getCcsDir() for HOOK_SOURCE_PATH
- profile-hook-injector.ts: Use getCcsDir() for hook sources
- CLAUDE.md: Add test isolation rules
- remove removeHookConfig() call from uninstallWebSearchHook()
- add removeMigrationMarker() cleanup
- update postuninstall.js to only clean CCS files
- global ~/.claude/settings.json is never touched
- ensureProfileHooks() injects hooks into profile settings
- migrateGlobalHook() one-time migration from global settings
- removeMigrationMarker() cleanup for uninstall
- export from barrel files for module access
* fix(doctor): use dynamic profile discovery for delegation check
Replace hardcoded ['glm', 'kimi'] list with DelegationValidator.getReadyProfiles()
to detect all configured *.settings.json profiles including mm, or1, g7, etc.
* fix(ci): exclude bot comments from triggering AI review
Bot progress comments were triggering new workflow runs,
which cancelled in-progress reviews due to concurrency group.
Added check for github.event.comment.user.type != 'Bot'.
* chore(release): 7.18.0-dev.1 [skip ci]
* fix(delegation): only check profiles defined in config.yaml
Previously getReadyProfiles() scanned all *.settings.json files,
including orphan files (ghcp, kiro) not in config.yaml.
Now reads from config.yaml:
- profiles section (excluding 'default')
- cliproxy.providers section
Fixes doctor showing 11 profiles instead of configured 9.
* chore(release): 7.18.0-dev.2 [skip ci]
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
- Clear stdinKeepalive interval on SIGINT/SIGTERM signal handlers
- Add cancelProjectSelection() to timeout/exit/error handlers
- Close server on error path in testLocalhostBinding to prevent fd leak
- Add TTL-based cleanup for stale auth sessions (10 min expiry)
- Use DEVICE_CODE_TIMEOUT_MS constant instead of hardcoded value (DRY)
- Extract common validation logic into validateProviderKey() function
- Convert validateGlmKey() and validateMiniMaxKey() to thin wrappers
- Remove 15+ unnecessary inline comments explaining obvious code
- Remove verbose JSDoc that duplicates function signatures
- Reduce file from 236 to 148 lines (37% reduction)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Restore src/commands/migrate-command.ts to dev branch state (was truncated)
- Remove src/config/rename-minimax-profile.ts (circular export causing build break)
- Fix mm.settings.json path typo in api-key-validator.ts
Build now passes: bun run typecheck && bun run lint:fix
* fix(cliproxy): pass variant port to executor for isolation
Variants configured with dedicated ports (8318-8417) were not using
their assigned port. The executor always defaulted to 8317.
Changes:
- Add port field to ProfileDetectionResult interface
- Pass variant.port from profile-detector to ccs.ts
- Forward port to execClaudeWithCLIProxy options
- Update executor priority: CLI flags > variant port > config.yaml > default
Closes#228
* fix(cliproxy): propagate port in unified config and UI preset handlers
Edge case fixes identified in codebase review:
- Unified config variant detection: add settingsPath and port fields
- Provider editor: use variant port in handleApplyPreset/handleCustomPresetApply
- preset-utils: add optional port parameter to applyDefaultPreset()
* chore(release): 7.11.1-dev.1 [skip ci]
* fix(cliproxy): use correct default port (8317) for remote HTTP connections
Root cause: Inconsistent default port logic across code paths.
- Test Connection used 8317 (correct)
- Actual API calls used 80 (wrong)
Changes:
- Add centralized getRemoteDefaultPort() helper in config-generator.ts
- Fix proxy-target-resolver.ts to use shared helper
- Fix rewriteLocalhostUrls() and getRemoteEnvVars() in config-generator.ts
- Update remote-proxy-client.ts to use shared helper (DRY)
Fixes all 3 reported issues:
1. Port empty → now correctly uses :8317 instead of :80
2. BASEURL construction → now includes correct port
3. CLIProxy Plus auth → now fetches from remote on correct port
* chore(release): 7.11.1-dev.2 [skip ci]
* feat(delegation): add Claude Code CLI flag passthrough
Add explicit passthrough support for key Claude Code CLI flags:
- --max-turns: Limit agentic turns (prevents infinite loops)
- --fallback-model: Auto-fallback when model overloaded
- --agents: Dynamic subagent JSON injection
- --betas: Enable experimental features
Maintain extraArgs catch-all for future Claude Code flags.
Update help command with new "Delegation Flags" section.
Closes#89
* test(delegation): add comprehensive CLI flag passthrough tests
Add 45 test cases covering all edge cases for CLI flag passthrough:
- DelegationHandler: timeout/max-turns/fallback-model/agents/betas validation
- HeadlessExecutor: duplicate flag filtering, undefined vs truthy checks
* chore(release): 7.11.1-dev.3 [skip ci]
* fix(ui): enable cancel button during OAuth authentication
Resolves#234 - Cancel button was disabled during authentication flow,
preventing users from canceling the OAuth process.
Changes:
- Add auth-session-manager.ts for tracking active OAuth sessions
- Add POST /cliproxy/auth/:provider/cancel endpoint to abort sessions
- Kill spawned CLIProxy auth process when cancel is triggered
- Enable Cancel button in AddAccountDialog during authentication
- Add cancel support to QuickSetupWizard auth step
- Update useCancelAuth hook to call backend cancel endpoint
* chore(release): 7.11.1-dev.4 [skip ci]
* fix(prompt): add stdin.pause() to prevent process hang after password input
Fixes#236. The password() method called resume() on stdin but never
paused it in cleanup, keeping the event loop alive indefinitely.
* chore(release): 7.11.1-dev.5 [skip ci]
* feat(cliproxy): add --allow-self-signed flag for HTTPS connections (#227)
Previously, allowSelfSigned was hardcoded to true for all HTTPS protocol
connections, forcing use of the native https module which has issues with
Cloudflare-proxied connections causing timeouts.
This change:
- Adds --allow-self-signed CLI flag (default: false)
- Adds CCS_ALLOW_SELF_SIGNED environment variable
- Uses standard fetch API by default for HTTPS (works with valid certs)
- Only uses native https module when --allow-self-signed is specified
Usage:
- For production HTTPS proxies with valid certs: no flag needed
- For dev proxies with self-signed certs: use --allow-self-signed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* chore(release): 7.11.1-dev.6 [skip ci]
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Shun Kakinoki <39187513+shunkakinoki@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Wrap JSON.parse in try-catch with clear error message for malformed JSON
- Add iflow, kiro, ghcp providers to CLIProxyVariantConfig type
- Make settings optional in CLIProxyVariantConfig (was required with empty string fallback)
- Remove unsafe type cast in loadConfigSafe()
Addresses additional edge cases from PR #215 code review.
Fixes#214 - Fresh install fails with 'Config not found' when running
ccs config before RecoveryManager runs.
Changes:
- Move RecoveryManager.recoverAll() before all early-exit commands in ccs.ts
- Fix loadConfigSafe() to return empty config instead of throwing in legacy mode
- Add getActiveConfigPath() for mode-aware config path resolution
- Rename cliproxy getConfigPath() to getCliproxyConfigPath() to avoid confusion
- Update help-command.ts to show correct config path based on mode
This ensures all commands benefit from auto-recovery on fresh installs,
and gracefully handles missing config files in all code paths.
The try/catch around loadConfig() in getSettingsPath() was ineffective
because process.exit() cannot be caught by try/catch - it terminates
the process immediately.
Replace with inline safe logic that:
1. Checks if config.json exists with fs.existsSync()
2. Reads and parses JSON manually
3. Uses isConfig() type guard for validation
4. Properly catches JSON parse errors
This ensures unified mode users don't crash when falling back to check
legacy config.json for profiles not found in config.yaml.
Add loadConfigSafe() function that handles both unified (config.yaml) and
legacy (config.json) config formats. Uses throwable errors instead of
process.exit() so try/catch blocks work properly in web server routes.
Updated files to use loadConfigSafe():
- overview-routes.ts: Dashboard overview API
- route-helpers.ts: readConfigSafe() helper
- profile-reader.ts: API profile reading
- profile-writer.ts: API profile writing
- variant-config-adapter.ts: CLIProxy variant config
Fixes#206 (Problem 2: config.json not found when user has config.yaml)
- Add SIGKILL escalation after 3s SIGTERM timeout in session-tracker
- Auto-stop running proxy before binary update in binary-manager
- Add waitForPortFree utility to port-utils
- Detect version mismatch on startup and auto-restart outdated proxy
- Store and expose version in session lock for detection
Fixes issue where old proxy version continues running after update,
causing UI to show different version than actually running.
Linux kernel truncates process names to 15 chars, causing
'cli-proxy-api-plus' to appear as 'cli-proxy-api-p' in lsof output.
Switch from exact whitelist to prefix matching for robust detection.
Two bugs caused the health check to incorrectly report "Proxy not running":
1. lsof command used mutually exclusive flags (-t and -F)
- Removed -t flag since -F already provides structured output
2. isCLIProxyProcess whitelist was missing common binary names
- Added: cliproxyapi, cli-proxy-api-plus (and .exe variants)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Check legacy config.json/profiles.json in isFirstTimeInstall()
- Use expandPath() for cross-platform path handling in profile-detector
- Add pre-flight API key validation for better error messages
- Enhance 401 error handling with Z.AI refresh guidance
Fixes#195
- dev versions (X.Y.Z-dev.N) now treated as newer than base release (X.Y.Z)
- reflects convention that dev work is AFTER the release, not before
- update GH Actions message to use 'ccs update --dev' instead of npm install
- update tests to match new semantic
- split 647-line ui.ts into 10 focused modules (<115 lines each)
- extract: colors, indicators, boxes, tables, text, spinner, tasks
- add init.ts for shared state and types.ts for interfaces
- ui.ts now re-exports from ui/index.ts for backward compat
- create utils/time.ts for centralized time formatting
- remove duplicate from auth/commands/types.ts
- re-export for backward compatibility
- export from utils/index.ts
- replace GeminiCliStatus, GrokCliStatus, OpenCodeCliStatus with type aliases
- use ComponentStatus from types/utils as single source of truth
- fix null vs undefined for optional fields
Fixes tag-agnostic cache issue causing 'ccs update --dev' to require
force. Implements tag-specific caching (dev_version vs latest_version)
to correctly detect updates across channels. Disables duplicate bot
comments on dev releases in .releaserc.cjs.
Dashboard Stop/Update buttons failed with contradictory errors:
- Stop: "No active CLIProxy session found"
- Update: "CLIProxy was already running"
Root cause: service-manager spawned proxy but never called registerSession(),
so stopProxy() couldn't find the lock file.
Changes:
- service-manager.ts: Add registerSession() after spawn
- session-tracker.ts: Make stopProxy() async with port-based fallback
- routes.ts: Update proxy-stop endpoint to async
- cliproxy-command.ts: Update handleStop() to await async stopProxy()
- port-utils.ts: Fix double-escaped regex for Windows port detection
- session-tracker.test.js: Update tests for async stopProxy()
- check cached update result synchronously on every command
- show warnBox notification if update available
- skip check for version/help/update commands
- refresh cache in background if stale (>24h)
- use proper UI components (warnBox) for notification