free-coding-models 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,8 +10,8 @@
10
10
  *
11
11
  * 📖 Key J opens the FCM Proxy V2 overlay directly (with daemon status refresh).
12
12
  * 📖 Key I opens the unified "Feedback, bugs & requests" overlay.
13
- * 📖 The proxy overlay handles tool cycling (ROW_PROXY_TOOL), sync toggle, cleanup
14
- * for any syncable tool via cleanupToolConfig(), and all daemon management actions.
13
+ * 📖 The proxy overlay handles sync toggle and cleanup for the current Z-selected tool,
14
+ * plus all daemon management actions.
15
15
  *
16
16
  * It also owns the "test key" model selection used by the Settings overlay.
17
17
  * Some providers expose models in `/v1/models` that are not actually callable
@@ -31,6 +31,7 @@
31
31
  */
32
32
 
33
33
  import { loadChangelog } from './changelog-loader.js'
34
+ import { resolveProxySyncToolMode } from './proxy-sync.js'
34
35
 
35
36
  // 📖 Some providers need an explicit probe model because the first catalog entry
36
37
  // 📖 is not guaranteed to be accepted by their chat endpoint.
@@ -1284,15 +1285,14 @@ export function createKeyHandler(ctx) {
1284
1285
  if (state.proxyDaemonOpen) {
1285
1286
  const proxySettings = getProxySettings(state.config)
1286
1287
  const ROW_PROXY_ENABLED = 0
1287
- const ROW_PROXY_TOOL = 1
1288
- const ROW_PROXY_SYNC = 2
1289
- const ROW_PROXY_PORT = 3
1290
- const ROW_PROXY_CLEANUP = 4
1291
- const ROW_DAEMON_INSTALL = 5
1292
- const ROW_DAEMON_RESTART = 6
1293
- const ROW_DAEMON_STOP = 7
1294
- const ROW_DAEMON_KILL = 8
1295
- const ROW_DAEMON_LOGS = 9
1288
+ const ROW_PROXY_SYNC = 1
1289
+ const ROW_PROXY_PORT = 2
1290
+ const ROW_PROXY_CLEANUP = 3
1291
+ const ROW_DAEMON_INSTALL = 4
1292
+ const ROW_DAEMON_RESTART = 5
1293
+ const ROW_DAEMON_STOP = 6
1294
+ const ROW_DAEMON_KILL = 7
1295
+ const ROW_DAEMON_LOGS = 8
1296
1296
 
1297
1297
  const daemonStatus = state.daemonStatus || 'not-installed'
1298
1298
  const daemonIsInstalled = daemonStatus === 'running' || daemonStatus === 'stopped' || daemonStatus === 'unhealthy' || daemonStatus === 'stale'
@@ -1340,8 +1340,9 @@ export function createKeyHandler(ctx) {
1340
1340
  if (key.name === 'pageup') { state.proxyDaemonCursor = Math.max(0, state.proxyDaemonCursor - 5); return }
1341
1341
  if (key.name === 'pagedown') { state.proxyDaemonCursor = Math.min(maxRow, state.proxyDaemonCursor + 5); return }
1342
1342
 
1343
- // 📖 Resolve active proxy tool (persisted or fallback to Z-mode)
1344
- const activeProxyTool = proxySettings.activeTool || state.mode || 'opencode'
1343
+ // 📖 Proxy sync now follows the current Z-selected tool automatically.
1344
+ const currentToolMode = state.mode || 'opencode'
1345
+ const currentProxyTool = resolveProxySyncToolMode(currentToolMode)
1345
1346
 
1346
1347
  // 📖 Space toggles on proxy rows
1347
1348
  if (key.name === 'space') {
@@ -1352,14 +1353,16 @@ export function createKeyHandler(ctx) {
1352
1353
  state.proxyDaemonMessage = { type: 'success', msg: `✅ Proxy mode ${state.config.settings.proxy.enabled ? 'enabled' : 'disabled'}`, ts: Date.now() }
1353
1354
  return
1354
1355
  }
1355
- if (state.proxyDaemonCursor === ROW_PROXY_TOOL) {
1356
- // 📖 Space also cycles tool (same as Enter on this row)
1357
- } else if (state.proxyDaemonCursor === ROW_PROXY_SYNC) {
1356
+ if (state.proxyDaemonCursor === ROW_PROXY_SYNC) {
1357
+ if (!currentProxyTool) {
1358
+ state.proxyDaemonMessage = { type: 'warning', msg: '⚠ Current tool does not support persisted proxy sync', ts: Date.now() }
1359
+ return
1360
+ }
1358
1361
  if (!state.config.settings) state.config.settings = {}
1359
1362
  state.config.settings.proxy = { ...proxySettings, syncToOpenCode: !proxySettings.syncToOpenCode }
1360
1363
  saveConfig(state.config)
1361
1364
  const { getToolMeta } = await import('./tool-metadata.js')
1362
- const toolLabel = getToolMeta(activeProxyTool).label
1365
+ const toolLabel = getToolMeta(currentProxyTool).label
1363
1366
  state.proxyDaemonMessage = { type: 'success', msg: `✅ Auto-sync to ${toolLabel} ${state.config.settings.proxy.syncToOpenCode ? 'enabled' : 'disabled'}`, ts: Date.now() }
1364
1367
  return
1365
1368
  } else {
@@ -1367,20 +1370,6 @@ export function createKeyHandler(ctx) {
1367
1370
  }
1368
1371
  }
1369
1372
 
1370
- // 📖 Enter or Space on ROW_PROXY_TOOL → cycle active tool
1371
- if ((key.name === 'return' || key.name === 'space') && state.proxyDaemonCursor === ROW_PROXY_TOOL) {
1372
- const { PROXY_SYNCABLE_TOOLS } = await import('./proxy-sync.js')
1373
- const currentIdx = PROXY_SYNCABLE_TOOLS.indexOf(activeProxyTool)
1374
- const nextIdx = (currentIdx + 1) % PROXY_SYNCABLE_TOOLS.length
1375
- const nextTool = PROXY_SYNCABLE_TOOLS[nextIdx]
1376
- if (!state.config.settings) state.config.settings = {}
1377
- state.config.settings.proxy = { ...proxySettings, activeTool: nextTool }
1378
- saveConfig(state.config)
1379
- const { getToolMeta } = await import('./tool-metadata.js')
1380
- state.proxyDaemonMessage = { type: 'success', msg: `✅ Active tool: ${getToolMeta(nextTool).emoji} ${getToolMeta(nextTool).label}`, ts: Date.now() }
1381
- return
1382
- }
1383
-
1384
1373
  // 📖 Enter on proxy rows
1385
1374
  if (key.name === 'return') {
1386
1375
  // 📖 Proxy enabled — toggle
@@ -1394,11 +1383,15 @@ export function createKeyHandler(ctx) {
1394
1383
 
1395
1384
  // 📖 Auto-sync toggle
1396
1385
  if (state.proxyDaemonCursor === ROW_PROXY_SYNC) {
1386
+ if (!currentProxyTool) {
1387
+ state.proxyDaemonMessage = { type: 'warning', msg: '⚠ Current tool does not support persisted proxy sync', ts: Date.now() }
1388
+ return
1389
+ }
1397
1390
  if (!state.config.settings) state.config.settings = {}
1398
1391
  state.config.settings.proxy = { ...proxySettings, syncToOpenCode: !proxySettings.syncToOpenCode }
1399
1392
  saveConfig(state.config)
1400
1393
  const { getToolMeta } = await import('./tool-metadata.js')
1401
- const toolLabel = getToolMeta(activeProxyTool).label
1394
+ const toolLabel = getToolMeta(currentProxyTool).label
1402
1395
  state.proxyDaemonMessage = { type: 'success', msg: `✅ Auto-sync to ${toolLabel} ${state.config.settings.proxy.syncToOpenCode ? 'enabled' : 'disabled'}`, ts: Date.now() }
1403
1396
  return
1404
1397
  }
@@ -1412,9 +1405,13 @@ export function createKeyHandler(ctx) {
1412
1405
 
1413
1406
  // 📖 Clean proxy config — generalized for active tool
1414
1407
  if (state.proxyDaemonCursor === ROW_PROXY_CLEANUP) {
1415
- const result = cleanupToolConfig(activeProxyTool)
1408
+ if (!currentProxyTool) {
1409
+ state.proxyDaemonMessage = { type: 'warning', msg: '⚠ Current tool has no persisted proxy config to clean', ts: Date.now() }
1410
+ return
1411
+ }
1412
+ const result = cleanupToolConfig(currentProxyTool)
1416
1413
  const { getToolMeta } = await import('./tool-metadata.js')
1417
- const toolLabel = getToolMeta(activeProxyTool).label
1414
+ const toolLabel = getToolMeta(currentProxyTool).label
1418
1415
  if (result.success) {
1419
1416
  state.proxyDaemonMessage = { type: 'success', msg: `✅ ${toolLabel} proxy config cleaned — all fcm-* entries removed`, ts: Date.now() }
1420
1417
  } else {
package/src/opencode.js CHANGED
@@ -8,7 +8,7 @@
8
8
  * - Launch OpenCode CLI or Desktop
9
9
  * - Manage ZAI proxy bridge for non-standard API paths
10
10
  * - Start/stop the multi-account proxy server (fcm-proxy)
11
- * - Auto-start proxy when OpenCode config is already synced
11
+ * - Auto-start proxy when the current tool is configured for proxy auto-sync
12
12
  *
13
13
  * 🎯 Key features:
14
14
  * - Provider-aware config setup for OpenCode (NIM, Groq, Cerebras, etc.)
@@ -21,7 +21,7 @@
21
21
  * - `startOpenCode` — Launch OpenCode CLI with selected model
22
22
  * - `startOpenCodeDesktop` — Set model and open Desktop app
23
23
  * - `startProxyAndLaunch` — Start fcm-proxy then launch OpenCode
24
- * - `autoStartProxyIfSynced` — Auto-start proxy if opencode.json has fcm-proxy
24
+ * - `autoStartProxyIfSynced` — Auto-start proxy and sync the current tool when enabled
25
25
  * - `ensureProxyRunning` — Ensure proxy is running (start or reuse)
26
26
  * - `isProxyEnabledForConfig` — Check whether proxy mode is opted in
27
27
  *
@@ -40,12 +40,14 @@ import { copyFileSync, existsSync } from 'fs'
40
40
  import { sources } from '../sources.js'
41
41
  import { PROVIDER_COLOR } from './render-table.js'
42
42
  import { ProxyServer } from './proxy-server.js'
43
- import { loadOpenCodeConfig, saveOpenCodeConfig, syncToOpenCode } from './opencode-sync.js'
43
+ import { loadOpenCodeConfig, saveOpenCodeConfig } from './opencode-sync.js'
44
44
  import { getApiKey, getProxySettings } from './config.js'
45
45
  import { ENV_VAR_NAMES, OPENCODE_MODEL_MAP, isWindows, isMac, isLinux } from './provider-metadata.js'
46
46
  import { setActiveProxy } from './render-table.js'
47
47
  import { buildProxyTopologyFromConfig as _buildTopology } from './proxy-topology.js'
48
48
  import { isDaemonRunning, getDaemonInfo } from './daemon-manager.js'
49
+ import { syncProxyToTool, resolveProxySyncToolMode } from './proxy-sync.js'
50
+ import { getToolMeta } from './tool-metadata.js'
49
51
 
50
52
  // 📖 OpenCode config location: ~/.config/opencode/opencode.json on ALL platforms.
51
53
  // 📖 OpenCode uses xdg-basedir which resolves to %USERPROFILE%\.config on Windows.
@@ -633,24 +635,27 @@ export async function autoStartProxyIfSynced(fcmConfig, state) {
633
635
  try {
634
636
  const proxySettings = getProxySettings(fcmConfig)
635
637
  if (!proxySettings.enabled || !proxySettings.syncToOpenCode) return
636
-
637
- const ocConfig = loadOpenCodeConfig()
638
- if (!ocConfig?.provider?.['fcm-proxy']) return
638
+ const currentToolMode = state?.mode || 'opencode'
639
+ const syncTarget = resolveProxySyncToolMode(currentToolMode)
640
+ if (!syncTarget) return
639
641
 
640
642
  state.proxyStartupStatus = { phase: 'starting' }
641
643
 
642
644
  const started = await ensureProxyRunning(fcmConfig)
643
-
644
- syncToOpenCode(fcmConfig, sources, mergedModelsRef, {
645
- proxyPort: started.port,
646
- proxyToken: started.proxyToken,
647
- availableModelSlugs: started.availableModelSlugs,
648
- })
645
+ const syncResult = syncProxyToTool(syncTarget, {
646
+ baseUrl: `http://127.0.0.1:${started.port}/v1`,
647
+ token: started.proxyToken,
648
+ }, mergedModelsRef)
649
+ if (!syncResult.success) {
650
+ throw new Error(syncResult.error || `Proxy sync failed for ${syncTarget}`)
651
+ }
649
652
 
650
653
  state.proxyStartupStatus = {
651
654
  phase: 'running',
652
655
  port: started.port,
653
656
  accountCount: started.accountCount,
657
+ tool: getToolMeta(syncTarget).label,
658
+ path: syncResult.path || null,
654
659
  }
655
660
  } catch (err) {
656
661
  state.proxyStartupStatus = {
package/src/overlays.js CHANGED
@@ -5,27 +5,27 @@
5
5
  * @details
6
6
  * This module centralizes all overlay rendering in one place:
7
7
  * - Settings, Install Endpoints, Help, Log, Smart Recommend, Feedback, Changelog
8
- * - FCM Proxy V2 overlay with tool selector, auto-sync toggle, and cleanup
8
+ * - FCM Proxy V2 overlay with current-tool auto-sync toggle and cleanup
9
9
  * - Settings diagnostics for provider key tests, including wrapped retry/error details
10
10
  * - Recommend analysis timer orchestration and progress updates
11
11
  *
12
12
  * The factory pattern keeps stateful UI logic isolated while still
13
13
  * allowing the main CLI to control shared state and dependencies.
14
14
  *
15
- * 📖 The proxy overlay rows are: Enable → Active tool → Auto-sync → Port → Cleanup → Install/Restart/Stop/Kill/Logs
16
- * 📖 Tool selector cycles through PROXY_SYNCABLE_TOOLS (12 tools from proxy-sync.js)
15
+ * 📖 The proxy overlay rows are: Enable → Auto-sync current tool → Port → Cleanup → Install/Restart/Stop/Kill/Logs
17
16
  * 📖 Feedback overlay (I key) combines feature requests + bug reports in one left-aligned input
18
17
  *
19
18
  * → Functions:
20
19
  * - `createOverlayRenderers` — returns renderer + analysis helpers
21
20
  *
22
21
  * @exports { createOverlayRenderers }
23
- * @see ./proxy-sync.js — PROXY_SYNCABLE_TOOLS used by the tool selector
22
+ * @see ./proxy-sync.js — resolveProxySyncToolMode powers current-tool proxy sync hints
24
23
  * @see ./key-handler.js — handles keypresses for all overlay interactions
25
24
  */
26
25
 
27
26
  import { loadChangelog } from './changelog-loader.js'
28
- import { PROXY_SYNCABLE_TOOLS } from './proxy-sync.js'
27
+ import { buildCliHelpLines } from './cli-help.js'
28
+ import { resolveProxySyncToolMode } from './proxy-sync.js'
29
29
 
30
30
  export function createOverlayRenderers(state, deps) {
31
31
  const {
@@ -627,7 +627,7 @@ export function createOverlayRenderers(state, deps) {
627
627
  lines.push(` ${chalk.yellow('X')} Toggle token log page ${chalk.dim('(shows recent request usage from request-log.jsonl)')}`)
628
628
  lines.push(` ${chalk.yellow('Z')} Cycle tool mode ${chalk.dim('(OpenCode → Desktop → OpenClaw → Crush → Goose → Pi → Aider → Claude Code → Codex → Gemini → Qwen → OpenHands → Amp)')}`)
629
629
  lines.push(` ${chalk.yellow('F')} Toggle favorite on selected row ${chalk.dim('(⭐ pinned at top, persisted)')}`)
630
- lines.push(` ${chalk.yellow('Y')} Install endpoints ${chalk.dim('(provider catalog → all tools, Direct or FCM Proxy V2)')}`)
630
+ lines.push(` ${chalk.yellow('Y')} Install endpoints ${chalk.dim('(provider catalog → compatible tools, Direct or FCM Proxy V2)')}`)
631
631
  lines.push(` ${chalk.yellow('Q')} Smart Recommend ${chalk.dim('(🎯 find the best model for your task — questionnaire + live analysis)')}`)
632
632
  lines.push(` ${chalk.rgb(255, 87, 51).bold('I')} Feedback, bugs & requests ${chalk.dim('(📝 send anonymous feedback, bug reports, or feature requests)')}`)
633
633
  lines.push(` ${chalk.yellow('J')} FCM Proxy V2 settings ${chalk.dim('(📡 open proxy configuration and background service management)')}`)
@@ -651,29 +651,7 @@ export function createOverlayRenderers(state, deps) {
651
651
  lines.push(` ${chalk.yellow('U')} Check updates manually`)
652
652
  lines.push(` ${chalk.yellow('Esc')} Close settings`)
653
653
  lines.push('')
654
- lines.push(` ${chalk.bold('CLI Flags')}`)
655
- lines.push(` ${chalk.dim('Usage: free-coding-models [options]')}`)
656
- lines.push(` ${chalk.cyan('free-coding-models --opencode')} ${chalk.dim('OpenCode CLI mode')}`)
657
- lines.push(` ${chalk.cyan('free-coding-models --opencode-desktop')} ${chalk.dim('OpenCode Desktop mode')}`)
658
- lines.push(` ${chalk.cyan('free-coding-models --openclaw')} ${chalk.dim('OpenClaw mode')}`)
659
- lines.push(` ${chalk.cyan('free-coding-models --crush')} ${chalk.dim('Crush mode')}`)
660
- lines.push(` ${chalk.cyan('free-coding-models --goose')} ${chalk.dim('Goose mode')}`)
661
- lines.push(` ${chalk.cyan('free-coding-models --pi')} ${chalk.dim('Pi mode')}`)
662
- lines.push(` ${chalk.cyan('free-coding-models --aider')} ${chalk.dim('Aider mode')}`)
663
- lines.push(` ${chalk.cyan('free-coding-models --claude-code')} ${chalk.dim('Claude Code mode')}`)
664
- lines.push(` ${chalk.cyan('free-coding-models --codex')} ${chalk.dim('Codex CLI mode')}`)
665
- lines.push(` ${chalk.cyan('free-coding-models --gemini')} ${chalk.dim('Gemini CLI mode')}`)
666
- lines.push(` ${chalk.cyan('free-coding-models --qwen')} ${chalk.dim('Qwen Code mode')}`)
667
- lines.push(` ${chalk.cyan('free-coding-models --openhands')} ${chalk.dim('OpenHands mode')}`)
668
- lines.push(` ${chalk.cyan('free-coding-models --amp')} ${chalk.dim('Amp mode')}`)
669
- lines.push(` ${chalk.cyan('free-coding-models --best')} ${chalk.dim('Only top tiers (A+, S, S+)')}`)
670
- lines.push(` ${chalk.cyan('free-coding-models --fiable')} ${chalk.dim('10s reliability analysis')}`)
671
- lines.push(` ${chalk.cyan('free-coding-models --tier S|A|B|C')} ${chalk.dim('Filter by tier letter')}`)
672
- lines.push(` ${chalk.cyan('free-coding-models --no-telemetry')} ${chalk.dim('Disable telemetry for this run')}`)
673
- lines.push(` ${chalk.cyan('free-coding-models --recommend')} ${chalk.dim('Auto-open Smart Recommend on start')}`)
674
- lines.push(` ${chalk.cyan('free-coding-models --profile <name>')} ${chalk.dim('Load a saved config profile')}`)
675
- lines.push(` ${chalk.cyan('free-coding-models --clean-proxy')} ${chalk.dim('Remove persisted fcm-proxy config from OpenCode')}`)
676
- lines.push(` ${chalk.dim('Flags can be combined: --openclaw --tier S')}`)
654
+ lines.push(...buildCliHelpLines({ chalk, indent: ' ', title: 'CLI Flags' }))
677
655
  lines.push('')
678
656
  // 📖 Help overlay can be longer than viewport, so keep a dedicated scroll offset.
679
657
  const { visible, offset } = sliceOverlayLines(lines, state.helpScrollOffset, state.terminalRows)
@@ -1273,15 +1251,14 @@ export function createOverlayRenderers(state, deps) {
1273
1251
 
1274
1252
  // 📖 Row indices — these control cursor navigation
1275
1253
  const ROW_PROXY_ENABLED = 0
1276
- const ROW_PROXY_TOOL = 1
1277
- const ROW_PROXY_SYNC = 2
1278
- const ROW_PROXY_PORT = 3
1279
- const ROW_PROXY_CLEANUP = 4
1280
- const ROW_DAEMON_INSTALL = 5
1281
- const ROW_DAEMON_RESTART = 6
1282
- const ROW_DAEMON_STOP = 7
1283
- const ROW_DAEMON_KILL = 8
1284
- const ROW_DAEMON_LOGS = 9
1254
+ const ROW_PROXY_SYNC = 1
1255
+ const ROW_PROXY_PORT = 2
1256
+ const ROW_PROXY_CLEANUP = 3
1257
+ const ROW_DAEMON_INSTALL = 4
1258
+ const ROW_DAEMON_RESTART = 5
1259
+ const ROW_DAEMON_STOP = 6
1260
+ const ROW_DAEMON_KILL = 7
1261
+ const ROW_DAEMON_LOGS = 8
1285
1262
 
1286
1263
  const daemonStatus = state.daemonStatus || 'not-installed'
1287
1264
  const daemonInfo = state.daemonInfo
@@ -1318,10 +1295,16 @@ export function createOverlayRenderers(state, deps) {
1318
1295
  lines.push(` ${chalk.dim(' to this proxy which handles key rotation, rate limiting, and failover.')}`)
1319
1296
  lines.push('')
1320
1297
 
1321
- // 📖 Resolve active tool for proxy sync (persisted or fallback to Z-mode)
1322
- const activeProxyTool = proxySettings.activeTool || state.mode || 'opencode'
1323
- const activeToolMeta = getToolMeta(activeProxyTool)
1324
- const activeToolLabel = `${activeToolMeta.emoji} ${activeToolMeta.label}`
1298
+ // 📖 Proxy sync now always follows the currently selected Z-mode when supported.
1299
+ const currentToolMode = state.mode || 'opencode'
1300
+ const currentToolMeta = getToolMeta(currentToolMode)
1301
+ const currentToolLabel = `${currentToolMeta.emoji} ${currentToolMeta.label}`
1302
+ const proxySyncTool = resolveProxySyncToolMode(currentToolMode)
1303
+ const proxySyncHint = proxySyncTool
1304
+ ? chalk.dim(` Current tool: ${currentToolLabel}`)
1305
+ : chalk.yellow(` Current tool: ${currentToolLabel} (launcher-only, no persisted proxy config)`)
1306
+ lines.push(proxySyncHint)
1307
+ lines.push('')
1325
1308
 
1326
1309
  // 📖 Row 0: Proxy enabled toggle
1327
1310
  const r0b = state.proxyDaemonCursor === ROW_PROXY_ENABLED ? chalk.bold.cyan(' ❯ ') : chalk.dim(' ')
@@ -1330,20 +1313,18 @@ export function createOverlayRenderers(state, deps) {
1330
1313
  cursorLineByRow[ROW_PROXY_ENABLED] = lines.length
1331
1314
  lines.push(state.proxyDaemonCursor === ROW_PROXY_ENABLED ? chalk.bgRgb(20, 45, 60)(r0) : r0)
1332
1315
 
1333
- // 📖 Row 1: Active tool selector cycles through proxy-syncable tools
1334
- const r1b = state.proxyDaemonCursor === ROW_PROXY_TOOL ? chalk.bold.cyan(' ❯ ') : chalk.dim(' ')
1335
- const r1 = `${r1b}${chalk.bold('Active tool').padEnd(44)} ${chalk.cyanBright(activeToolLabel)} ${chalk.dim('← Enter to cycle')}`
1336
- cursorLineByRow[ROW_PROXY_TOOL] = lines.length
1337
- lines.push(state.proxyDaemonCursor === ROW_PROXY_TOOL ? chalk.bgRgb(20, 45, 60)(r1) : r1)
1338
-
1339
- // 📖 Row 2: Auto-sync proxy config to active tool
1316
+ // 📖 Row 1: Auto-sync proxy config to the current tool when that tool supports persisted sync.
1340
1317
  const r2b = state.proxyDaemonCursor === ROW_PROXY_SYNC ? chalk.bold.cyan(' ❯ ') : chalk.dim(' ')
1341
1318
  const r2val = proxySettings.syncToOpenCode ? chalk.greenBright('Enabled') : chalk.dim('Disabled')
1342
- const r2 = `${r2b}${chalk.bold(`Auto-sync proxy to ${activeToolMeta.label}`).padEnd(44)} ${r2val}`
1319
+ const r2label = proxySyncTool
1320
+ ? `Auto-sync proxy to ${currentToolMeta.label}`
1321
+ : 'Auto-sync proxy to current tool'
1322
+ const r2note = proxySyncTool ? '' : ` ${chalk.dim('(unavailable for this mode)')}`
1323
+ const r2 = `${r2b}${chalk.bold(r2label).padEnd(44)} ${r2val}${r2note}`
1343
1324
  cursorLineByRow[ROW_PROXY_SYNC] = lines.length
1344
1325
  lines.push(state.proxyDaemonCursor === ROW_PROXY_SYNC ? chalk.bgRgb(20, 45, 60)(r2) : r2)
1345
1326
 
1346
- // 📖 Row 3: Preferred port
1327
+ // 📖 Row 2: Preferred port
1347
1328
  const r3b = state.proxyDaemonCursor === ROW_PROXY_PORT ? chalk.bold.cyan(' ❯ ') : chalk.dim(' ')
1348
1329
  const r3val = state.settingsProxyPortEditMode && state.proxyDaemonCursor === ROW_PROXY_PORT
1349
1330
  ? chalk.cyanBright(`${state.settingsProxyPortBuffer}▏`)
@@ -1352,9 +1333,15 @@ export function createOverlayRenderers(state, deps) {
1352
1333
  cursorLineByRow[ROW_PROXY_PORT] = lines.length
1353
1334
  lines.push(state.proxyDaemonCursor === ROW_PROXY_PORT ? chalk.bgRgb(20, 45, 60)(r3) : r3)
1354
1335
 
1355
- // 📖 Row 4: Clean tool proxy config
1336
+ // 📖 Row 3: Clean current tool proxy config
1356
1337
  const r4b = state.proxyDaemonCursor === ROW_PROXY_CLEANUP ? chalk.bold.cyan(' ❯ ') : chalk.dim(' ')
1357
- const r4 = `${r4b}${chalk.bold(`Clean ${activeToolMeta.label} proxy config`).padEnd(44)} ${chalk.dim('Enter → removes all fcm-* entries')}`
1338
+ const r4title = proxySyncTool
1339
+ ? `Clean ${currentToolMeta.label} proxy config`
1340
+ : `Clean ${currentToolMeta.label} proxy config`
1341
+ const r4hint = proxySyncTool
1342
+ ? chalk.dim('Enter → removes all fcm-* entries')
1343
+ : chalk.dim('Unavailable for this mode')
1344
+ const r4 = `${r4b}${chalk.bold(r4title).padEnd(44)} ${r4hint}`
1358
1345
  cursorLineByRow[ROW_PROXY_CLEANUP] = lines.length
1359
1346
  lines.push(state.proxyDaemonCursor === ROW_PROXY_CLEANUP ? chalk.bgRgb(45, 30, 30)(r4) : r4)
1360
1347