From bd8e2e44e608d7ddcd9c23a2dd909fc3abe99e39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=92=E6=B1=9F=E5=AD=A4=E5=BD=B1?= Date: Sat, 25 Jul 2026 18:12:46 +0800 Subject: [PATCH 1/7] =?UTF-8?q?chore(deps):=20=E6=9B=B4=E6=96=B0=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E6=A8=A1=E5=9E=8B=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- env/platforms/claude.json | 2 +- env/platforms/codex.json | 2 +- env/platforms/gemini.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/env/platforms/claude.json b/env/platforms/claude.json index bcaef1d..c1e6799 100644 --- a/env/platforms/claude.json +++ b/env/platforms/claude.json @@ -8,7 +8,7 @@ "ANTHROPIC_BASE_URL": "${claude.url}", "CLAUDE_CODE_EFFORT_LEVEL": "medium", "CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS": "1", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-8", + "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-5", "ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-5", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5-20251001-thinking" }, diff --git a/env/platforms/codex.json b/env/platforms/codex.json index 379cb8f..2e1523d 100644 --- a/env/platforms/codex.json +++ b/env/platforms/codex.json @@ -3,7 +3,7 @@ "api": { "enabled": false }, - "model": "gpt-5.5", + "model": "gpt-5.6-sol", "sandbox_mode": "workspace-write", "approval_policy": "on-request", "allow_login_shell": true, diff --git a/env/platforms/gemini.json b/env/platforms/gemini.json index e666f16..9437070 100644 --- a/env/platforms/gemini.json +++ b/env/platforms/gemini.json @@ -4,7 +4,7 @@ "enabled": true }, "model": { - "name": "gemini-3.5-flash", + "name": "gemini-3.6-flash", "maxSessionTurns": -1, "compressionThreshold": 0.5, "skipNextSpeakerCheck": true From 5bf493aa94f6d450ab8e38752468cffba65db0f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=92=E6=B1=9F=E5=AD=A4=E5=BD=B1?= Date: Wed, 5 Aug 2026 10:54:09 +0800 Subject: [PATCH 2/7] =?UTF-8?q?chore(cline):=20=E6=9B=B4=E6=96=B0=E6=A8=A1?= =?UTF-8?q?=E5=9E=8BID=EF=BC=8C=E7=A7=BB=E9=99=A4=E5=89=8D=E7=BC=80"deepse?= =?UTF-8?q?ek-ai/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- env/platforms/cline.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/env/platforms/cline.json b/env/platforms/cline.json index 317414b..9358bc8 100644 --- a/env/platforms/cline.json +++ b/env/platforms/cline.json @@ -5,8 +5,8 @@ }, "globalState": { "openAiBaseUrl": "${cline.url}", - "planModeOpenAiModelId": "deepseek-ai/deepseek-v4-pro", - "actModeOpenAiModelId": "deepseek-ai/deepseek-v4-flash" + "planModeOpenAiModelId": "deepseek-v4-pro", + "actModeOpenAiModelId": "deepseek-v4-flash" }, "secrets": { "openAiApiKey": "${cline.key}" From 007aef12cb18f783c5a4b371027b6f469fd48300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=92=E6=B1=9F=E5=AD=A4=E5=BD=B1?= Date: Sat, 8 Aug 2026 18:10:03 +0800 Subject: [PATCH 3/7] =?UTF-8?q?chore(mcp):=20=E6=B7=BB=E5=8A=A0=20codebudd?= =?UTF-8?q?y=20=E5=B9=B3=E5=8F=B0=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- env/mcp/xcodebuild.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/env/mcp/xcodebuild.json b/env/mcp/xcodebuild.json index a29c0da..cfd72c5 100644 --- a/env/mcp/xcodebuild.json +++ b/env/mcp/xcodebuild.json @@ -12,6 +12,7 @@ }, "platforms": [ "claude", - "codex" + "codex", + "codebuddy" ] } From 523595a3577207138006eeb39bb01e478712e90a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=92=E6=B1=9F=E5=AD=A4=E5=BD=B1?= Date: Tue, 11 Aug 2026 17:31:34 +0800 Subject: [PATCH 4/7] =?UTF-8?q?feat(gemini):=20=E4=B8=BA=20Xcode=20?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E7=9B=AE=E5=BD=95=E6=B7=BB=E5=8A=A0=20.gemin?= =?UTF-8?q?i=20=E5=AD=90=E7=9B=AE=E5=BD=95=E5=8F=8A=20GEMINI.md=20?= =?UTF-8?q?=E7=AC=A6=E5=8F=B7=E9=93=BE=E6=8E=A5=E5=92=8C=20.env=20?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在同步 Gemini 配置时,自动在 Xcode 的 Gemini 目录下创建 .gemini 子目录,并将 ~/.gemini/GEMINI.md 符号链接到该目录中,同时从配置的 export_env_to_zshrc 块解析并写入 .env 文件,以支持 Xcode 插件加载环境变量和自定义指令。 --- sync/core/paths.py | 10 ++++++ sync/platforms/gemini.py | 75 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/sync/core/paths.py b/sync/core/paths.py index 904b833..3673fb3 100644 --- a/sync/core/paths.py +++ b/sync/core/paths.py @@ -126,6 +126,16 @@ def xcode_gemini_dir() -> Path: return xcode_coding_assistant_dir() / "gemini" +def xcode_gemini_dotgemini_dir() -> Path: + """Xcode Gemini nested .gemini directory (holds GEMINI.md symlink + .env).""" + return xcode_gemini_dir() / ".gemini" + + +def xcode_gemini_env_path() -> Path: + """Xcode Gemini .env file path inside the nested .gemini directory.""" + return xcode_gemini_dotgemini_dir() / ".env" + + # ── Standard tool config paths ─────────────────────────────────────────────── def codex_config_path() -> Path: diff --git a/sync/platforms/gemini.py b/sync/platforms/gemini.py index 6e4a204..6bef9a3 100644 --- a/sync/platforms/gemini.py +++ b/sync/platforms/gemini.py @@ -1,10 +1,14 @@ from pathlib import Path from typing import Any +import os + from core.common import ( api_enabled as _api_enabled, + load_secrets, prune_managed_keys_via_sidecar, read_json_object, + resolve_secrets, write_json, ) from core.paths import ( @@ -12,6 +16,8 @@ gemini_settings_path, xcode_coding_assistant_exists, xcode_gemini_dir, + xcode_gemini_dotgemini_dir, + xcode_gemini_env_path, ) # Internal/platform keys that should NOT appear in the managed settings.json. @@ -74,6 +80,69 @@ def _sync_settings( prune_managed_keys_via_sidecar(path, set(managed_settings.keys()), sidecar_path) +def _ensure_gemini_md_symlink(dotgemini: Path) -> None: + """Step 3: symlink ~/.gemini/GEMINI.md into the Xcode .gemini dir. + + A symlink (rather than a copy) keeps a single source of truth: edits to the + user's ~/.gemini/GEMINI.md propagate automatically. We gracefully handle an + existing symlink (refresh target), an existing real file (leave it untouched + unless it already points at the source), and a missing source (warn + skip). + """ + source = gemini_root_dir() / "GEMINI.md" + if not source.exists(): + print("[gemini] ~/.gemini/GEMINI.md not found — skipping GEMINI.md symlink for Xcode.") + return + link = dotgemini / "GEMINI.md" + # Already a correct symlink to the source: nothing to do. + if link.is_symlink() and os.path.realpath(link) == os.path.realpath(source): + return + # Replace any existing file/symlink at the target. + if link.exists() or link.is_symlink(): + link.unlink() + os.symlink(source, link) + print(f"[gemini] Symlinked GEMINI.md -> {link} (source: {source}).") + + +def _sync_xcode_env_file(dotgemini: Path, cfg: dict[str, Any]) -> None: + """Step 4: create .env in the Xcode .gemini dir from export_env_to_zshrc. + + The platform config's ``export_env_to_zshrc`` block holds {VAR: value} pairs + (values may contain ${secret} placeholders). We resolve placeholders against + env/secrets.json, then write/merge them into the .env file as KEY="value" + lines. Existing lines NOT managed by the sync (any other keys) are preserved. + """ + export = cfg.get("export_env_to_zshrc") + if not isinstance(export, dict) or not export: + print("[gemini] No export_env_to_zshrc block — skipping .env for Xcode.") + return + + secrets = load_secrets() + resolved = resolve_secrets(export, secrets) + unresolved = [v for v in resolved.values() if isinstance(v, str) and "${" in v] + if unresolved: + print("[gemini] ⚠ .env: unresolved placeholders remain — check env/secrets.json.") + + env_path = dotgemini / ".env" + existing: dict[str, str] = {} + if env_path.is_file(): + for raw in env_path.read_text(encoding="utf-8").splitlines(): + line = raw.strip() + if not line or line.startswith("#") or "=" not in line: + continue + key, _, val = line.partition("=") + existing[key.strip()] = val.strip().strip('"').strip("'") + + # Overlay managed vars on top of any developer-customized ones. + merged = dict(existing) + for k, v in resolved.items(): + if isinstance(v, str): + merged[k] = v + + lines = [f'{k}="{v}"' for k, v in merged.items()] + env_path.write_text("\n".join(lines) + "\n", encoding="utf-8") + print(f"[gemini] Synced .env for Xcode at {env_path} ({len(merged)} vars).") + + def sync(mcp_servers: dict[str, Any], cfg: dict[str, Any]) -> None: """Sync MCP servers and platform config to Gemini CLI (native + Xcode). @@ -114,3 +183,9 @@ def sync(mcp_servers: dict[str, Any], cfg: dict[str, Any]) -> None: mcp_servers, xc / ".managed_settings_keys.json", ) + + # ── Steps 2-4: nested .gemini dir, GEMINI.md symlink, .env ── + dotgemini = xcode_gemini_dotgemini_dir() + dotgemini.mkdir(parents=True, exist_ok=True) # step 2 + _ensure_gemini_md_symlink(dotgemini) # step 3 + _sync_xcode_env_file(dotgemini, cfg) # step 4 From 4abb2dce4371e43f522282e239513344c69cdae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=92=E6=B1=9F=E5=AD=A4=E5=BD=B1?= Date: Thu, 13 Aug 2026 14:33:18 +0800 Subject: [PATCH 5/7] =?UTF-8?q?feat(env):=20=E6=B7=BB=E5=8A=A0=20grok-4.6?= =?UTF-8?q?=20=E6=A8=A1=E5=9E=8B=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- env/platforms/codebuddy.json | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/env/platforms/codebuddy.json b/env/platforms/codebuddy.json index 2bd2980..5b90e2b 100644 --- a/env/platforms/codebuddy.json +++ b/env/platforms/codebuddy.json @@ -29,11 +29,22 @@ "maxOutputTokens": 8192, "supportsToolCall": true, "supportsImages": false - } + }, + { + "id": "grok-4.6", + "name": "grok-4.6", + "vendor": "dataeyes", + "url": "${codebuddy.url}", + "apiKey": "${codebuddy.key}", + "supportsToolCall": true, + "supportsImages": true, + "supportsReasoning": true + } ], "availableModels": [ "deepseek-v4-pro", - "deepseek-v4-flash" + "deepseek-v4-flash", + "grok-4.6" ], "preamble": { "target": "CODEBUDDY.md", From 2127ca08b2389206125af354aae3413d835d68fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=92=E6=B1=9F=E5=AD=A4=E5=BD=B1?= Date: Thu, 13 Aug 2026 14:50:23 +0800 Subject: [PATCH 6/7] =?UTF-8?q?feat(env):=20=E5=90=AF=E7=94=A8=20CodeBuddy?= =?UTF-8?q?=20API=20=E5=B9=B6=E6=B7=BB=E5=8A=A0=20gemini-3.5-flash=20?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- env/platforms/codebuddy.json | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/env/platforms/codebuddy.json b/env/platforms/codebuddy.json index 5b90e2b..f135b9f 100644 --- a/env/platforms/codebuddy.json +++ b/env/platforms/codebuddy.json @@ -1,27 +1,23 @@ { "_comment": "CodeBuddy model configuration synced when CodeBuddy is installed. API model fields sync by default; set api.enabled=false to disable API sync and clear the managed availableModels list.", "api": { - "enabled": false + "enabled": true }, "models": [ { - "id": "deepseek-v4-pro", - "name": "DeepSeek V4 Pro", + "id": "deepseek-v4-pro-of", + "name": "deepseek-v4-pro-of", "vendor": "dataeyes", "url": "${codebuddy.url}", "apiKey": "${codebuddy.key}", "maxInputTokens": 128000, "maxOutputTokens": 8192, "supportsToolCall": true, - "supportsImages": false, - "relatedModels": { - "lite": "deepseek-v4-flash", - "reasoning": "deepseek-v4-pro" - } + "supportsImages": false }, { - "id": "deepseek-v4-flash", - "name": "DeepSeek V4 Flash", + "id": "deepseek-v4-flash-0731", + "name": "deepseek-v4-flash-0731", "vendor": "dataeyes", "url": "${codebuddy.url}", "apiKey": "${codebuddy.key}", @@ -39,12 +35,23 @@ "supportsToolCall": true, "supportsImages": true, "supportsReasoning": true + }, + { + "id": "gemini-3.5-flash", + "name": "gemini-3.5-flash", + "vendor": "dataeyes", + "url": "${codebuddy.url}", + "apiKey": "${codebuddy.key}", + "supportsToolCall": true, + "supportsImages": true, + "supportsReasoning": true } ], "availableModels": [ - "deepseek-v4-pro", - "deepseek-v4-flash", - "grok-4.6" + "deepseek-v4-pro-of", + "deepseek-v4-flash-0731", + "grok-4.6", + "gemini-3.5-flash" ], "preamble": { "target": "CODEBUDDY.md", From bf049104d1e4a343dda0e6dcf6b90aa62a40901d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=92=E6=B1=9F=E5=AD=A4=E5=BD=B1?= Date: Fri, 14 Aug 2026 09:34:36 +0800 Subject: [PATCH 7/7] =?UTF-8?q?fix(codebuddy):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E5=88=97=E8=A1=A8=E9=85=8D=E7=BD=AE=E5=B9=B6?= =?UTF-8?q?=E5=90=AF=E7=94=A8=E6=95=B0=E6=8D=AE=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- env/platforms/codebuddy.json | 51 ++++++++++++++++++------------------ env/platforms/codex.json | 2 +- sync/platforms/codebuddy.py | 6 ++++- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/env/platforms/codebuddy.json b/env/platforms/codebuddy.json index f135b9f..0076990 100644 --- a/env/platforms/codebuddy.json +++ b/env/platforms/codebuddy.json @@ -26,33 +26,32 @@ "supportsToolCall": true, "supportsImages": false }, - { - "id": "grok-4.6", - "name": "grok-4.6", - "vendor": "dataeyes", - "url": "${codebuddy.url}", - "apiKey": "${codebuddy.key}", - "supportsToolCall": true, - "supportsImages": true, - "supportsReasoning": true - }, - { - "id": "gemini-3.5-flash", - "name": "gemini-3.5-flash", - "vendor": "dataeyes", - "url": "${codebuddy.url}", - "apiKey": "${codebuddy.key}", - "supportsToolCall": true, - "supportsImages": true, - "supportsReasoning": true - } - ], - "availableModels": [ - "deepseek-v4-pro-of", - "deepseek-v4-flash-0731", - "grok-4.6", - "gemini-3.5-flash" + { + "id": "grok-4.6", + "name": "grok-4.6", + "vendor": "dataeyes", + "url": "${codebuddy.url}", + "apiKey": "${codebuddy.key}", + "maxInputTokens": 128000, + "maxOutputTokens": 8192, + "supportsToolCall": true, + "supportsImages": true, + "supportsReasoning": true + }, + { + "id": "gemini-3.5-flash", + "name": "gemini-3.5-flash", + "vendor": "dataeyes", + "url": "${codebuddy.url}", + "apiKey": "${codebuddy.key}", + "maxInputTokens": 128000, + "maxOutputTokens": 8192, + "supportsToolCall": true, + "supportsImages": true, + "supportsReasoning": true + } ], + "availableModels": [], "preamble": { "target": "CODEBUDDY.md", "mode": "full", diff --git a/env/platforms/codex.json b/env/platforms/codex.json index 2e1523d..efe8b2d 100644 --- a/env/platforms/codex.json +++ b/env/platforms/codex.json @@ -1,7 +1,7 @@ { "_comment": "Codex platform config — lean team-shared set (core + security/sandbox). Third-party dataeyes API sync is enabled by default; set api.enabled=false to disable API sync and remove the managed model_provider / preferred_auth_method / model_providers block and the DATAEYES_API_KEY env export. MCP servers and the preamble are independent of API sync and always sync. Per-developer preference knobs (reasoning effort, verbosity, personality, features, history, tui, analytics, etc.) are intentionally NOT synced.", "api": { - "enabled": false + "enabled": true }, "model": "gpt-5.6-sol", "sandbox_mode": "workspace-write", diff --git a/sync/platforms/codebuddy.py b/sync/platforms/codebuddy.py index e07d233..b1af7e7 100644 --- a/sync/platforms/codebuddy.py +++ b/sync/platforms/codebuddy.py @@ -126,9 +126,13 @@ def _merge_available_models( - Config-managed IDs appear first and replace any existing duplicates. - User-added IDs not in the config are preserved after config entries. + - An explicitly empty list from config means "clear all managed models". """ if not config_available: - return existing_available + # Config explicitly set availableModels to [] — sync the empty list. + # (The caller already guards against the None case, so an empty list + # here means "set to empty", not "no config".) + return [] config_ids = set(config_available) # User-added IDs not managed by our config