From 1b6ace044786ec7d408b8356eed8855667cec9ef Mon Sep 17 00:00:00 2001 From: catlog22 Date: Tue, 27 Jan 2026 23:16:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=A7=84=E5=88=92?= =?UTF-8?q?=E7=AC=94=E8=AE=B0=E5=8A=9F=E8=83=BD=E4=BB=A5=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E7=94=9F=E6=88=90=E5=92=8C=E7=BA=A6=E6=9D=9F?= =?UTF-8?q?=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/agents/action-planning-agent.md | 12 ++ .../commands/workflow/brainstorm-with-file.md | 20 +-- .claude/commands/workflow/plan.md | 130 ++++++++++++++++-- .../workflow/tools/task-generate-agent.md | 21 ++- ccw/src/core/routes/cli-routes.ts | 2 +- .../dashboard-js/views/cli-manager.js | 101 ++++++++++++++ ccw/src/tools/claude-cli-tools.ts | 16 ++- 7 files changed, 274 insertions(+), 28 deletions(-) diff --git a/.claude/agents/action-planning-agent.md b/.claude/agents/action-planning-agent.md index 274a6075..25f91784 100644 --- a/.claude/agents/action-planning-agent.md +++ b/.claude/agents/action-planning-agent.md @@ -55,6 +55,17 @@ color: yellow **Step-by-step execution**: ``` +0. Load planning notes → Extract phase-level constraints (NEW) + Commands: Read('.workflow/active/{session-id}/planning-notes.md') + Output: Consolidated constraints from all workflow phases + Structure: + - User Intent: Original GOAL, KEY_CONSTRAINTS + - Context Findings: Critical files, architecture notes, constraints + - Conflict Decisions: Resolved conflicts, modified artifacts + - Consolidated Constraints: Numbered list of ALL constraints (Phase 1-3) + + USAGE: This is the PRIMARY source of constraints. All task generation MUST respect these constraints. + 1. Load session metadata → Extract user input - User description: Original task/feature requirements - Project scope: User-specified boundaries and goals @@ -826,6 +837,7 @@ Use `analysis_results.complexity` or task count to determine structure: ### 3.3 Guidelines Checklist **ALWAYS:** +- **Load planning-notes.md FIRST**: Read planning-notes.md before context-package.json. Use its Consolidated Constraints as primary constraint source for all task generation - **Search Tool Priority**: ACE (`mcp__ace-tool__search_context`) → CCW (`mcp__ccw-tools__smart_search`) / Built-in (`Grep`, `Glob`, `Read`) - Apply Quantification Requirements to all requirements, acceptance criteria, and modification points - Load IMPL_PLAN template: `Read(~/.claude/workflows/cli-templates/prompts/workflow/impl-plan-template.txt)` before generating IMPL_PLAN.md diff --git a/.claude/commands/workflow/brainstorm-with-file.md b/.claude/commands/workflow/brainstorm-with-file.md index 9fc801c1..281e0be1 100644 --- a/.claude/commands/workflow/brainstorm-with-file.md +++ b/.claude/commands/workflow/brainstorm-with-file.md @@ -19,7 +19,7 @@ Interactive brainstorming workflow with **multi-CLI collaboration** and **docume **Key features**: - **brainstorm.md**: Complete thought evolution timeline -- **Multi-CLI collaboration**: Gemini (creative), Codex (pragmatic), Qwen (systematic) perspectives +- **Multi-CLI collaboration**: Gemini (creative), Codex (pragmatic), Claude (systematic) perspectives - **Idea expansion**: Progressive questioning and exploration - **Diverge-Converge cycles**: Generate options then focus on best paths - **Synthesis**: Merge multiple perspectives into coherent solutions @@ -61,8 +61,8 @@ Phase 1: Seed Understanding Phase 2: Divergent Exploration (Multi-CLI Parallel) ├─ Gemini CLI: Creative/innovative perspectives - ├─ Codex CLI: Pragmatic/implementation perspectives - ├─ Qwen CLI: Systematic/architectural perspectives + ├─ Codex CLI: Pragmatic/implementation perspectives + ├─ Claude CLI: Systematic/architectural perspectives └─ Aggregate diverse viewpoints Phase 3: Interactive Refinement (Multi-Round) @@ -375,7 +375,7 @@ CONSTRAINTS: Focus on what can actually be built with current tech stack }) ) -// 3. Qwen: Systematic/Architectural Perspective +// 3. Claude: Systematic/Architectural Perspective cliPromises.push( Bash({ command: `ccw cli -p " @@ -404,7 +404,7 @@ EXPECTED: - Risk matrix CONSTRAINTS: Consider existing system architecture -" --tool qwen --mode analysis`, +" --tool claude --mode analysis`, run_in_background: true }) ) @@ -436,7 +436,7 @@ const perspectives = { }, systematic: { - source: 'qwen', + source: 'claude', decomposition: [...], patterns: [...], tradeoffs: [...] @@ -488,7 +488,7 @@ ${blockers.map(b => `- ⚠️ ${b}`).join('\n')} --- -#### Systematic Perspective (Qwen) +#### Systematic Perspective (Claude) **Problem Decomposition**: ${decomposition} @@ -956,7 +956,7 @@ ${decisionFramework} - **Total Rounds**: ${totalRounds} - **Ideas Generated**: ${totalIdeas} - **Ideas Survived**: ${survivedIdeas} -- **Perspectives Used**: Gemini (creative), Codex (pragmatic), Qwen (systematic) +- **Perspectives Used**: Gemini (creative), Codex (pragmatic), Claude (systematic) - **Duration**: ${duration} - **Artifacts**: brainstorm.md, perspectives.json, synthesis.json, ${ideaFiles.length} idea deep-dives ``` @@ -1056,7 +1056,7 @@ if (selection.includes("导出分享")) { #### Pragmatic Perspective (Codex) ... -#### Systematic Perspective (Qwen) +#### Systematic Perspective (Claude) ... #### Perspective Synthesis @@ -1105,7 +1105,7 @@ if (selection.includes("导出分享")) { |-----|------|-------|----------| | Gemini | Creative | Innovation, cross-domain | Generating novel ideas | | Codex | Pragmatic | Implementation, feasibility | Reality-checking ideas | -| Qwen | Systematic | Architecture, structure | Organizing solutions | +| Claude | Systematic | Architecture, structure | Organizing solutions | ### Collaboration Patterns diff --git a/.claude/commands/workflow/plan.md b/.claude/commands/workflow/plan.md index ff111296..f507eba6 100644 --- a/.claude/commands/workflow/plan.md +++ b/.claude/commands/workflow/plan.md @@ -115,7 +115,38 @@ CONTEXT: Existing user database schema, REST API endpoints **TodoWrite**: Mark phase 1 completed, phase 2 in_progress -**After Phase 1**: Return to user showing Phase 1 results, then auto-continue to Phase 2 +**After Phase 1**: Initialize planning-notes.md with user intent + +```javascript +// Create minimal planning notes document +const planningNotesPath = `.workflow/active/${sessionId}/planning-notes.md` +const userGoal = structuredDescription.goal +const userConstraints = structuredDescription.context || "None specified" + +Write(planningNotesPath, `# Planning Notes + +**Session**: ${sessionId} +**Created**: ${new Date().toISOString()} + +## User Intent (Phase 1) + +- **GOAL**: ${userGoal} +- **KEY_CONSTRAINTS**: ${userConstraints} + +--- + +## Context Findings (Phase 2) +(To be filled by context-gather) + +## Conflict Decisions (Phase 3) +(To be filled if conflicts detected) + +## Consolidated Constraints (Phase 4 Input) +1. ${userConstraints} +`) +``` + +Return to user showing Phase 1 results, then auto-continue to Phase 2 --- @@ -168,7 +199,37 @@ SlashCommand(command="/workflow:tools:context-gather --session [sessionId] \"[st **Note**: Phase 2 tasks completed and collapsed to summary. -**After Phase 2**: Return to user showing Phase 2 results, then auto-continue to Phase 3/4 (depending on conflict_risk) +**After Phase 2**: Update planning-notes.md with context findings, then auto-continue + +```javascript +// Read context-package to extract key findings +const contextPackage = JSON.parse(Read(contextPath)) +const conflictRisk = contextPackage.conflict_detection?.risk_level || 'low' +const criticalFiles = (contextPackage.exploration_results?.aggregated_insights?.critical_files || []) + .slice(0, 5).map(f => f.path) +const archPatterns = contextPackage.project_context?.architecture_patterns || [] +const constraints = contextPackage.exploration_results?.aggregated_insights?.constraints || [] + +// Append Phase 2 findings to planning-notes.md +Edit(planningNotesPath, { + old: '## Context Findings (Phase 2)\n(To be filled by context-gather)', + new: `## Context Findings (Phase 2) + +- **CRITICAL_FILES**: ${criticalFiles.join(', ') || 'None identified'} +- **ARCHITECTURE**: ${archPatterns.join(', ') || 'Not detected'} +- **CONFLICT_RISK**: ${conflictRisk} +- **CONSTRAINTS**: ${constraints.length > 0 ? constraints.join('; ') : 'None'}` +}) + +// Append Phase 2 constraints to consolidated list +Edit(planningNotesPath, { + old: '## Consolidated Constraints (Phase 4 Input)', + new: `## Consolidated Constraints (Phase 4 Input) +${constraints.map((c, i) => `${i + 2}. [Context] ${c}`).join('\n')}` +}) +``` + +Return to user showing Phase 2 results, then auto-continue to Phase 3/4 (depending on conflict_risk) --- @@ -229,7 +290,45 @@ SlashCommand(command="/workflow:tools:conflict-resolution --session [sessionId] **Note**: Phase 3 tasks completed and collapsed to summary. -**After Phase 3**: Return to user showing conflict resolution results (if executed) and selected strategies, then auto-continue to Phase 3.5 +**After Phase 3**: Update planning-notes.md with conflict decisions (if executed), then auto-continue + +```javascript +// If Phase 3 was executed, update planning-notes.md +if (conflictRisk >= 'medium') { + const conflictResPath = `.workflow/active/${sessionId}/.process/conflict-resolution.json` + + if (fs.existsSync(conflictResPath)) { + const conflictRes = JSON.parse(Read(conflictResPath)) + const resolved = conflictRes.resolved_conflicts || [] + const modifiedArtifacts = conflictRes.modified_artifacts || [] + const planningConstraints = conflictRes.planning_constraints || [] + + // Update Phase 3 section + Edit(planningNotesPath, { + old: '## Conflict Decisions (Phase 3)\n(To be filled if conflicts detected)', + new: `## Conflict Decisions (Phase 3) + +- **RESOLVED**: ${resolved.map(r => `${r.type} → ${r.strategy}`).join('; ') || 'None'} +- **MODIFIED_ARTIFACTS**: ${modifiedArtifacts.join(', ') || 'None'} +- **CONSTRAINTS**: ${planningConstraints.join('; ') || 'None'}` + }) + + // Append Phase 3 constraints to consolidated list + if (planningConstraints.length > 0) { + const currentNotes = Read(planningNotesPath) + const constraintCount = (currentNotes.match(/^\d+\./gm) || []).length + + Edit(planningNotesPath, { + old: '## Consolidated Constraints (Phase 4 Input)', + new: `## Consolidated Constraints (Phase 4 Input) +${planningConstraints.map((c, i) => `${constraintCount + i + 1}. [Conflict] ${c}`).join('\n')}` + }) + } + } +} +``` + +Return to user showing conflict resolution results (if executed) and selected strategies, then auto-continue to Phase 3.5 **Memory State Check**: - Evaluate current context window usage and memory state @@ -282,7 +381,12 @@ SlashCommand(command="/workflow:tools:task-generate-agent --session [sessionId]" **CLI Execution Note**: CLI tool usage is now determined semantically by action-planning-agent based on user's task description. If user specifies "use Codex/Gemini/Qwen for X", the agent embeds `command` fields in relevant `implementation_approach` steps. -**Input**: `sessionId` from Phase 1 +**Input**: +- `sessionId` from Phase 1 +- **planning-notes.md**: Consolidated constraints from all phases (Phase 1-3) + - Path: `.workflow/active/[sessionId]/planning-notes.md` + - Contains: User intent, context findings, conflict decisions, consolidated constraints + - **Purpose**: Provides structured, minimal context summary to action-planning-agent **Validation**: - `.workflow/active/[sessionId]/IMPL_PLAN.md` exists @@ -404,26 +508,22 @@ User Input (task description) ↓ Phase 1: session:start --auto "structured-description" ↓ Output: sessionId - ↓ Session Memory: Previous tasks, context, artifacts + ↓ Write: planning-notes.md (User Intent section) ↓ Phase 2: context-gather --session sessionId "structured-description" - ↓ Input: sessionId + session memory + structured description + ↓ Input: sessionId + structured description ↓ Output: contextPath (context-package.json) + conflict_risk + ↓ Update: planning-notes.md (Context Findings + Consolidated Constraints) ↓ Phase 3: conflict-resolution [AUTO-TRIGGERED if conflict_risk ≥ medium] ↓ Input: sessionId + contextPath + conflict_risk - ↓ CLI-powered conflict detection (JSON output) - ↓ AskUserQuestion: Present conflicts + resolution strategies - ↓ User selects strategies (or skip) - ↓ Apply modifications via Edit tool: - ↓ - Update guidance-specification.md - ↓ - Update role analyses (*.md) - ↓ - Mark context-package.json as "resolved" - ↓ Output: Modified brainstorm artifacts (NO report file) + ↓ Output: Modified brainstorm artifacts + ↓ Update: planning-notes.md (Conflict Decisions + Consolidated Constraints) ↓ Skip if conflict_risk is none/low → proceed directly to Phase 4 ↓ Phase 4: task-generate-agent --session sessionId - ↓ Input: sessionId + resolved brainstorm artifacts + session memory + ↓ Input: sessionId + planning-notes.md + context-package.json + brainstorm artifacts + ↓ planning-notes.md provides: User Intent, Context Findings, Constraints ↓ Output: IMPL_PLAN.md, task JSONs, TODO_LIST.md ↓ Return summary to user diff --git a/.claude/commands/workflow/tools/task-generate-agent.md b/.claude/commands/workflow/tools/task-generate-agent.md index f35c62c4..675bd8d1 100644 --- a/.claude/commands/workflow/tools/task-generate-agent.md +++ b/.claude/commands/workflow/tools/task-generate-agent.md @@ -161,12 +161,13 @@ const userConfig = { ### Phase 1: Context Preparation & Module Detection (Command Responsibility) -**Command prepares session paths, metadata, and detects module structure.** +**Command prepares session paths, metadata, detects module structure, and loads planning-notes.md.** **Session Path Structure**: ``` .workflow/active/WFS-{session-id}/ ├── workflow-session.json # Session metadata +├── planning-notes.md # Consolidated planning notes (NEW) ├── .process/ │ └── context-package.json # Context package with artifact catalog ├── .task/ # Output: Task JSON files @@ -248,9 +249,21 @@ IMPORTANT: This is PLANNING ONLY - you are generating planning documents, NOT im CRITICAL: Follow the progressive loading strategy defined in agent specification (load analysis.md files incrementally due to file size) +## PLANNING NOTES (PHASE 1-3 CONTEXT) +Load: .workflow/active/{session-id}/planning-notes.md + +This document contains: +- User Intent: Original GOAL and KEY_CONSTRAINTS from Phase 1 +- Context Findings: Critical files, architecture, and constraints from Phase 2 +- Conflict Decisions: Resolved conflicts and planning constraints from Phase 3 +- Consolidated Constraints: All constraints from all phases + +**USAGE**: Read planning-notes.md FIRST. Use Consolidated Constraints list to guide task sequencing and dependencies. + ## SESSION PATHS Input: - Session Metadata: .workflow/active/{session-id}/workflow-session.json + - Planning Notes: .workflow/active/{session-id}/planning-notes.md (NEW) - Context Package: .workflow/active/{session-id}/.process/context-package.json Output: @@ -376,6 +389,11 @@ IMPORTANT: Generate Task JSONs ONLY. IMPL_PLAN.md and TODO_LIST.md by Phase 3 Co CRITICAL: Follow the progressive loading strategy defined in agent specification (load analysis.md files incrementally due to file size) +## PLANNING NOTES (PHASE 1-3 CONTEXT) +Load: .workflow/active/{session-id}/planning-notes.md + +This document contains consolidated constraints and user intent to guide module-scoped task generation. + ## MODULE SCOPE - Module: ${module.name} (${module.type}) - Focus Paths: ${module.paths.join(', ')} @@ -386,6 +404,7 @@ CRITICAL: Follow the progressive loading strategy defined in agent specification ## SESSION PATHS Input: - Session Metadata: .workflow/active/{session-id}/workflow-session.json + - Planning Notes: .workflow/active/{session-id}/planning-notes.md (NEW) - Context Package: .workflow/active/{session-id}/.process/context-package.json Output: diff --git a/ccw/src/core/routes/cli-routes.ts b/ccw/src/core/routes/cli-routes.ts index 52e3e40e..ef29f0d1 100644 --- a/ccw/src/core/routes/cli-routes.ts +++ b/ccw/src/core/routes/cli-routes.ts @@ -302,7 +302,7 @@ export async function handleCliRoutes(ctx: RouteContext): Promise { if (req.method === 'PUT') { handlePostRequest(req, res, async (body: unknown) => { try { - const updates = body as { enabled?: boolean; primaryModel?: string; secondaryModel?: string; tags?: string[]; envFile?: string | null }; + const updates = body as { enabled?: boolean; primaryModel?: string; secondaryModel?: string; availableModels?: string[]; tags?: string[]; envFile?: string | null }; const updated = updateToolConfig(initialPath, tool, updates); // Broadcast config updated event diff --git a/ccw/src/templates/dashboard-js/views/cli-manager.js b/ccw/src/templates/dashboard-js/views/cli-manager.js index b564ee50..53753b34 100644 --- a/ccw/src/templates/dashboard-js/views/cli-manager.js +++ b/ccw/src/templates/dashboard-js/views/cli-manager.js @@ -482,6 +482,18 @@ function buildToolConfigModalContent(tool, config, models, status) { '' + '' + + // Available Models Section - Unified input with inline models + '
' + + '

Available Models (shown in dropdowns below)

' + + '
' + + (config.availableModels || models).map(function(model) { + return '' + escapeHtml(model) + ''; + }).join('') + + '' + + '
' + + '

Click × to remove, type to add new models

' + + '
' + + // Primary Model Section '
' + '

Primary Model (CLI endpoint calls)

' + @@ -855,6 +867,8 @@ function closeFileBrowserModal(selectedPath) { function initToolConfigModalEvents(tool, currentConfig, models) { // Local tags state (copy from config) var currentTags = (currentConfig.tags || []).slice(); + // Local available models state (copy from config or use defaults) + var currentModels = (currentConfig.availableModels || models).slice(); // Helper to render tags inline with input function renderTags() { @@ -896,6 +910,58 @@ function initToolConfigModalEvents(tool, currentConfig, models) { }); } + // Helper to render available models inline with input + function renderModels() { + var container = document.getElementById('modelsUnifiedInput'); + var input = document.getElementById('modelInput'); + if (!container) return; + + // Remove existing model items but keep the input + container.querySelectorAll('.tag-item').forEach(function(el) { el.remove(); }); + + // Insert models before the input + currentModels.forEach(function(model) { + var modelEl = document.createElement('span'); + modelEl.className = 'tag-item tag-model'; + modelEl.innerHTML = escapeHtml(model) + ''; + container.insertBefore(modelEl, input); + }); + + // Re-attach remove handlers + container.querySelectorAll('.tag-remove').forEach(function(btn) { + btn.onclick = function(e) { + e.stopPropagation(); + var modelToRemove = this.getAttribute('data-model'); + currentModels = currentModels.filter(function(m) { return m !== modelToRemove; }); + renderModels(); + updateModelSelects(); + }; + }); + } + + // Helper to update model select dropdowns with current model list + function updateModelSelects() { + var primarySelect = document.getElementById('primaryModelSelect'); + var secondarySelect = document.getElementById('secondaryModelSelect'); + if (!primarySelect || !secondarySelect) return; + + var primaryValue = primarySelect.value; + var secondaryValue = secondarySelect.value; + + // Rebuild options + var buildOptions = function(selectedValue) { + var html = ''; + currentModels.forEach(function(m) { + html += ''; + }); + html += ''; + return html; + }; + + primarySelect.innerHTML = buildOptions(primaryValue); + secondarySelect.innerHTML = buildOptions(secondaryValue); + } + // Click on unified input container focuses the input var unifiedInput = document.getElementById('tagsUnifiedInput'); if (unifiedInput) { @@ -906,6 +972,16 @@ function initToolConfigModalEvents(tool, currentConfig, models) { }; } + // Click on models unified input container focuses the input + var modelsUnifiedInput = document.getElementById('modelsUnifiedInput'); + if (modelsUnifiedInput) { + modelsUnifiedInput.onclick = function(e) { + if (e.target === this) { + document.getElementById('modelInput').focus(); + } + }; + } + // Tag input handler var tagInput = document.getElementById('tagInput'); if (tagInput) { @@ -933,8 +1009,27 @@ function initToolConfigModalEvents(tool, currentConfig, models) { }; }); + // Model input handler + var modelInput = document.getElementById('modelInput'); + if (modelInput) { + modelInput.onkeydown = function(e) { + if (e.key === 'Enter') { + e.preventDefault(); + var newModel = this.value.trim(); + if (newModel && currentModels.indexOf(newModel) === -1) { + currentModels.push(newModel); + renderModels(); + updateModelSelects(); + } + this.value = ''; + } + }; + } + // Initialize tags display renderTags(); + // Initialize models display + renderModels(); // Initialize lucide icons for predefined buttons if (window.lucide) lucide.createIcons(); @@ -1020,6 +1115,11 @@ function initToolConfigModalEvents(tool, currentConfig, models) { return; } + if (currentModels.length === 0) { + showRefreshToast('At least one available model is required', 'error'); + return; + } + // Get envFile value (only for gemini/qwen) var envFileInput = document.getElementById('envFileInput'); var envFile = envFileInput ? envFileInput.value.trim() : ''; @@ -1028,6 +1128,7 @@ function initToolConfigModalEvents(tool, currentConfig, models) { var updateData = { primaryModel: primaryModel, secondaryModel: secondaryModel, + availableModels: currentModels, tags: currentTags }; diff --git a/ccw/src/tools/claude-cli-tools.ts b/ccw/src/tools/claude-cli-tools.ts index 24a27358..d5d8e446 100644 --- a/ccw/src/tools/claude-cli-tools.ts +++ b/ccw/src/tools/claude-cli-tools.ts @@ -33,6 +33,11 @@ export interface ClaudeCliTool { enabled: boolean; primaryModel?: string; secondaryModel?: string; + /** + * Available models for this tool (shown in UI dropdown) + * If not provided, defaults will be used based on tool type + */ + availableModels?: string[]; tags: string[]; /** * Tool type determines routing: @@ -122,12 +127,13 @@ export interface ClaudeCliCombinedConfig extends ClaudeCliToolsConfig { // ========== Default Config ========== const DEFAULT_TOOLS_CONFIG: ClaudeCliToolsConfig = { - version: '3.3.0', + version: '3.4.0', tools: { gemini: { enabled: true, primaryModel: 'gemini-2.5-pro', secondaryModel: 'gemini-2.5-flash', + availableModels: ['gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.0-flash', 'gemini-2.0-flash-thinking', 'gemini-1.5-pro'], tags: [], type: 'builtin' }, @@ -135,6 +141,7 @@ const DEFAULT_TOOLS_CONFIG: ClaudeCliToolsConfig = { enabled: true, primaryModel: 'coder-model', secondaryModel: 'coder-model', + availableModels: ['coder-model', 'vision-model', 'qwen-2.5-coder', 'qwen-2.5-72b'], tags: [], type: 'builtin' }, @@ -142,6 +149,7 @@ const DEFAULT_TOOLS_CONFIG: ClaudeCliToolsConfig = { enabled: true, primaryModel: 'gpt-5.2', secondaryModel: 'gpt-5.2', + availableModels: ['gpt-5.2', 'gpt-5', 'gpt5-codex', 'o3', 'o1'], tags: [], type: 'builtin' }, @@ -149,6 +157,7 @@ const DEFAULT_TOOLS_CONFIG: ClaudeCliToolsConfig = { enabled: true, primaryModel: 'sonnet', secondaryModel: 'haiku', + availableModels: ['opus', 'sonnet', 'haiku'], tags: [], type: 'builtin' }, @@ -156,6 +165,7 @@ const DEFAULT_TOOLS_CONFIG: ClaudeCliToolsConfig = { enabled: true, primaryModel: 'opencode/glm-4.7-free', secondaryModel: 'opencode/glm-4.7-free', + availableModels: ['opencode/glm-4.7-free', 'opencode/deepseek-v3-free'], tags: [], type: 'builtin' } @@ -1098,6 +1108,7 @@ export function updateToolConfig( enabled: boolean; primaryModel: string; secondaryModel: string; + availableModels: string[]; tags: string[]; envFile: string | null; }> @@ -1114,6 +1125,9 @@ export function updateToolConfig( if (updates.secondaryModel !== undefined) { config.tools[tool].secondaryModel = updates.secondaryModel; } + if (updates.availableModels !== undefined) { + config.tools[tool].availableModels = updates.availableModels; + } if (updates.tags !== undefined) { config.tools[tool].tags = updates.tags; }