From f88ad8e78198302f68ee0b420075d704ab01d8ff Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Fri, 9 Jan 2026 00:18:35 -0500 Subject: [PATCH 01/11] 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. --- src/management/checks/profile-check.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/management/checks/profile-check.ts b/src/management/checks/profile-check.ts index 7d80a4df..e29a0143 100644 --- a/src/management/checks/profile-check.ts +++ b/src/management/checks/profile-check.ts @@ -193,16 +193,9 @@ export class DelegationChecker implements IHealthChecker { return; } - // Check profile validity using DelegationValidator + // Check profile validity using DelegationValidator (dynamic discovery) const { DelegationValidator } = require('../../utils/delegation-validator'); - const readyProfiles: string[] = []; - - for (const profile of ['glm', 'kimi']) { - const validation = DelegationValidator.validate(profile); - if (validation.valid) { - readyProfiles.push(profile); - } - } + const readyProfiles = DelegationValidator.getReadyProfiles(); if (readyProfiles.length === 0) { spinner.warn(); From ce70617ee94645399ba05af581240a696ca9cfed Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Fri, 9 Jan 2026 01:05:37 -0500 Subject: [PATCH 02/11] 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'. --- .github/workflows/ai-review.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ai-review.yml b/.github/workflows/ai-review.yml index 62a088e6..7ba104ef 100644 --- a/.github/workflows/ai-review.yml +++ b/.github/workflows/ai-review.yml @@ -38,13 +38,14 @@ jobs: # Conditions: # - PR event: only on opened (not synchronize to avoid cancel-on-push) - # - Comment event: only if it's a PR and contains /review + # - Comment event: only if it's a PR, contains /review, and NOT from a bot if: > github.event_name == 'pull_request_target' || github.event_name == 'workflow_dispatch' || (github.event_name == 'issue_comment' && github.event.issue.pull_request && - contains(github.event.comment.body, '/review')) + contains(github.event.comment.body, '/review') && + github.event.comment.user.type != 'Bot') # CLIProxy environment for model routing env: From 150b08fa6e80491a73c8a915b9ee302788ede14a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 9 Jan 2026 06:08:16 +0000 Subject: [PATCH 03/11] chore(release): 7.18.0-dev.1 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f1a8c1cd..1338638a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "7.18.0", + "version": "7.18.0-dev.1", "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6", "keywords": [ "cli", From 0075248273e2d4912c4e277deebd6e668c5b3466 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Fri, 9 Jan 2026 01:13:51 -0500 Subject: [PATCH 04/11] 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. --- src/utils/delegation-validator.ts | 38 ++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/utils/delegation-validator.ts b/src/utils/delegation-validator.ts index cec57e3f..76be898b 100644 --- a/src/utils/delegation-validator.ts +++ b/src/utils/delegation-validator.ts @@ -139,27 +139,49 @@ export class DelegationValidator { } /** - * Get all delegation-ready profiles + * Get all delegation-ready profiles from config.yaml + * Only returns profiles explicitly defined in config, not orphan settings files * @returns List of profile names ready for delegation */ static getReadyProfiles(): string[] { const homeDir = os.homedir(); const ccsDir = path.join(homeDir, '.ccs'); + const configPath = path.join(ccsDir, 'config.yaml'); if (!fs.existsSync(ccsDir)) { return []; } const profiles: string[] = []; - const entries = fs.readdirSync(ccsDir, { withFileTypes: true }); - // Look for *.settings.json files - for (const entry of entries) { - if (entry.isFile() && entry.name.endsWith('.settings.json')) { - const profileName = entry.name.replace('.settings.json', ''); - if (this.isReady(profileName)) { - profiles.push(profileName); + // Get profiles from config.yaml (excludes 'default' which uses ~/.claude/settings.json) + if (fs.existsSync(configPath)) { + try { + const yaml = require('js-yaml'); + const content = fs.readFileSync(configPath, 'utf8'); + const config = yaml.load(content) as Record; + + if (config.profiles && typeof config.profiles === 'object') { + for (const profileName of Object.keys(config.profiles as object)) { + if (profileName !== 'default' && this.isReady(profileName)) { + profiles.push(profileName); + } + } } + + // Also check CLIProxy providers (gemini, codex, agy, etc.) + if (config.cliproxy && typeof config.cliproxy === 'object') { + const cliproxy = config.cliproxy as Record; + if (Array.isArray(cliproxy.providers)) { + for (const provider of cliproxy.providers) { + if (typeof provider === 'string' && this.isReady(provider)) { + profiles.push(provider); + } + } + } + } + } catch { + // Config parse error, fall back to empty } } From 14f005a7425b7dd77950202231aacad8da0eba4f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 9 Jan 2026 06:20:17 +0000 Subject: [PATCH 05/11] chore(release): 7.18.0-dev.3 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 816b1221..b7fee593 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "7.18.0-dev.2", + "version": "7.18.0-dev.3", "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6", "keywords": [ "cli", From 12b68f9f136c3529ac976eaec9e8903b43185e89 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Sun, 11 Jan 2026 14:49:41 -0500 Subject: [PATCH 06/11] fix(ui): improve sidebar navigation for collapsible menu items - CLIProxy Plus click now navigates to Overview AND opens submenu - Parent menu item highlights when any child route is active - Provides consistent visual hierarchy across all collapsible menus --- ui/src/components/layout/app-sidebar.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ui/src/components/layout/app-sidebar.tsx b/ui/src/components/layout/app-sidebar.tsx index bd5b489a..dc5da3b4 100644 --- a/ui/src/components/layout/app-sidebar.tsx +++ b/ui/src/components/layout/app-sidebar.tsx @@ -1,4 +1,4 @@ -import { Link, useLocation } from 'react-router-dom'; +import { Link, useLocation, useNavigate } from 'react-router-dom'; import { Home, Key, @@ -85,6 +85,7 @@ const navGroups = [ export function AppSidebar() { const location = useLocation(); + const navigate = useNavigate(); const { state } = useSidebar(); // Helper to check if a route is active (exact match) @@ -118,8 +119,13 @@ export function AppSidebar() { className="group/collapsible" > + {/* Click navigates to overview AND opens submenu */} - + navigate(item.path)} + > {item.icon && } {item.label} From 8d634fece65f4a2d1479bd452b33ef3c85abdf6a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 Jan 2026 19:50:56 +0000 Subject: [PATCH 07/11] chore(release): 7.18.0-dev.4 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b7fee593..666ee15c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "7.18.0-dev.3", + "version": "7.18.0-dev.4", "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6", "keywords": [ "cli", From 85f6bc07d44f54673163ad4fed6045a37ccabad0 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Sun, 11 Jan 2026 15:20:46 -0500 Subject: [PATCH 08/11] fix(ci): add explicit instruction to post review as PR comment The AI reviewer was completing the review but not posting it because the prompt didn't explicitly instruct it to use gh pr comment. --- .github/workflows/ai-review.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ai-review.yml b/.github/workflows/ai-review.yml index 7ba104ef..9e0a7f53 100644 --- a/.github/workflows/ai-review.yml +++ b/.github/workflows/ai-review.yml @@ -127,6 +127,13 @@ jobs: End your review with: > 🤖 Reviewed by `${{ env.REVIEW_MODEL }}` + ## IMPORTANT: Posting the Review + After completing your analysis, you MUST post the review as a PR comment using: + ``` + gh pr comment ${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }} --body "" + ``` + Use a heredoc for the body to handle multi-line content properly. + claude_args: | --model ${{ env.REVIEW_MODEL }} --allowedTools "Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Read,Glob,Grep" From dcc5c5d4ed925caf505eee93884f5adfd4f2131f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 Jan 2026 20:21:51 +0000 Subject: [PATCH 09/11] chore(release): 7.18.0-dev.5 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 666ee15c..2a5f67a2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "7.18.0-dev.4", + "version": "7.18.0-dev.5", "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6", "keywords": [ "cli", From 120aca466d646ee1c770b2712a0d2742d5dd62d6 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Tue, 13 Jan 2026 13:41:30 -0500 Subject: [PATCH 10/11] fix(ci): prevent self-cancelling AI review workflow Root cause: Bot progress comments triggered issue_comment events, which created new workflow runs with the same concurrency group, causing cancel-in-progress to kill the original review. Fixes: 1. Add event_name to concurrency group (prevents cross-event cancellation) 2. Explicit filter for ccs-agy-reviewer[bot] comments (belt-and-suspenders) Closes race condition causing 25+ cancelled AI review runs. --- .github/workflows/ai-review.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ai-review.yml b/.github/workflows/ai-review.yml index 9e0a7f53..cb017413 100644 --- a/.github/workflows/ai-review.yml +++ b/.github/workflows/ai-review.yml @@ -21,9 +21,10 @@ on: required: true type: string -# Cancel in-progress runs for same PR (safe now since auto-trigger is only on opened) +# Cancel in-progress runs for same PR + event type (prevents self-cancellation from bot comments) +# Each event type gets its own concurrency group to prevent cross-event cancellation concurrency: - group: ai-review-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }} + group: ai-review-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }} cancel-in-progress: true jobs: @@ -38,14 +39,16 @@ jobs: # Conditions: # - PR event: only on opened (not synchronize to avoid cancel-on-push) - # - Comment event: only if it's a PR, contains /review, and NOT from a bot + # - Comment event: only if it's a PR, contains /review, NOT from a bot, and NOT from our review bot + # The explicit bot name check prevents self-triggering from progress comments if: > github.event_name == 'pull_request_target' || github.event_name == 'workflow_dispatch' || (github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '/review') && - github.event.comment.user.type != 'Bot') + github.event.comment.user.type != 'Bot' && + github.event.comment.user.login != 'ccs-agy-reviewer[bot]') # CLIProxy environment for model routing env: From 28f3206a201d9267a12ddf58e61f0662e570eb02 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 13 Jan 2026 18:42:48 +0000 Subject: [PATCH 11/11] chore(release): 7.18.0-dev.6 [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2a5f67a2..904fd62e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "7.18.0-dev.5", + "version": "7.18.0-dev.6", "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6", "keywords": [ "cli",