feat: enhance CLI discussion agent and multi-CLI planning with JSON string support; improve error handling and internationalization

This commit is contained in:
catlog22
2026-01-13 23:51:46 +08:00
parent 6922ca27de
commit 4fe7f6cde6
5 changed files with 75 additions and 14 deletions

View File

@@ -24,21 +24,25 @@ You are a multi-CLI collaborative discussion agent. You orchestrate multiple CLI
task_description: string, // User's task or requirement task_description: string, // User's task or requirement
round_number: number, // Current discussion round (1, 2, 3...) round_number: number, // Current discussion round (1, 2, 3...)
session: { id, folder }, // Session metadata session: { id, folder }, // Session metadata
ace_context: { // From ACE semantic search ace_context: { // From ACE semantic search (may be JSON string from orchestrator)
relevant_files: string[], relevant_files: string[],
detected_patterns: string[], detected_patterns: string[],
architecture_insights: string architecture_insights: string
}, },
// Optional // Optional
previous_rounds: RoundResult[], // Results from previous rounds previous_rounds: RoundResult[], // Results from previous rounds (may be JSON string from orchestrator)
user_feedback: string | null, // User's feedback/clarification from last round user_feedback: string | null, // User's feedback/clarification from last round
cli_config: { cli_config: { // CLI configuration (may be JSON string from orchestrator)
tools: string[], // CLI tools to use (default: ['gemini', 'codex']) tools: string[], // CLI tools to use (default: ['gemini', 'codex'])
timeout: number, // CLI timeout in ms timeout: number, // CLI timeout in ms
fallback_chain: string[] // Fallback order fallback_chain: string[] // Fallback order
} }
} }
// NOTE: When called from orchestrator, ace_context, previous_rounds, and cli_config
// may be passed as JSON strings (via JSON.stringify). The execute function parses
// these automatically - see "Input Parsing" section in Main Execution.
``` ```
## Output Schema ## Output Schema
@@ -475,7 +479,21 @@ function createDegradedAnalysis() {
```javascript ```javascript
async function execute(input) { async function execute(input) {
const startTime = Date.now() const startTime = Date.now()
const { task_description, round_number, session, ace_context, previous_rounds, user_feedback, cli_config } = input const { task_description, round_number, session, user_feedback, cli_config: cli_config_raw } = input
// === Input Parsing ===
// Parse stringified inputs from orchestrator (may be passed as JSON.stringify'd strings)
const ace_context = typeof input.ace_context === 'string'
? JSON.parse(input.ace_context)
: (input.ace_context || {})
const previous_rounds = typeof input.previous_rounds === 'string'
? JSON.parse(input.previous_rounds)
: (input.previous_rounds || [])
const cli_config = typeof cli_config_raw === 'string'
? JSON.parse(cli_config_raw)
: (cli_config_raw || { tools: ['gemini', 'codex'], timeout: 600000, fallback_chain: ['gemini', 'codex', 'qwen'] })
const roundFolder = `${session.folder}/rounds/${round_number}` const roundFolder = `${session.folder}/rounds/${round_number}`
Bash(`mkdir -p ${roundFolder}`) Bash(`mkdir -p ${roundFolder}`)

View File

@@ -206,16 +206,18 @@ ${JSON.stringify(contextPackage, null, 2)}
## Previous Rounds ## Previous Rounds
${analysisResults.length > 0 ${analysisResults.length > 0
? analysisResults.map(r => `Round ${r.round}: ${r.summary}`).join('\n') ? JSON.stringify(analysisResults, null, 2)
: 'None (first round)'} : 'None (first round)'}
## User Feedback ## User Feedback
${userFeedback || 'None'} ${userFeedback || 'None'}
## CLI Configuration ## CLI Configuration
- Tools: ${effectiveTools.join(', ')} ${JSON.stringify({
- Timeout: 600000ms tools: effectiveTools,
- Fallback Chain: gemini → codex → qwen timeout: 600000,
fallback_chain: ['gemini', 'codex', 'qwen']
}, null, 2)}
## Output Requirements ## Output Requirements
Write: ${sessionFolder}/rounds/${currentRound}/synthesis.json Write: ${sessionFolder}/rounds/${currentRound}/synthesis.json
@@ -461,7 +463,7 @@ const planningContext = {
task_description: taskDescription, task_description: taskDescription,
selected_solution: selectedSolution, selected_solution: selectedSolution,
analysis_rounds: analysisResults, analysis_rounds: analysisResults,
consensus_points: finalSynthesis.consensus_points, consensus_points: finalSynthesis._internal?.cross_verification?.agreements || [],
user_constraints: userDecision.constraints || null, user_constraints: userDecision.constraints || null,
ace_context: contextPackage, ace_context: contextPackage,
clarifications: sessionState.user_decisions clarifications: sessionState.user_decisions
@@ -514,7 +516,7 @@ ${selectedSolution.cons.map(c => `- ${c}`).join('\n')}
${selectedSolution.affected_files.map(f => `- ${f.file}:${f.line} - ${f.reason}`).join('\n')} ${selectedSolution.affected_files.map(f => `- ${f.file}:${f.line} - ${f.reason}`).join('\n')}
### Analysis Consensus ### Analysis Consensus
${finalSynthesis.consensus_points.map(p => `- ${p}`).join('\n')} ${(finalSynthesis._internal?.cross_verification?.agreements || []).map(p => `- ${p}`).join('\n')}
### User Constraints ### User Constraints
${userDecision.constraints ? JSON.stringify(userDecision.constraints) : 'None specified'} ${userDecision.constraints ? JSON.stringify(userDecision.constraints) : 'None specified'}

View File

@@ -746,8 +746,7 @@
} }
.file-browser-loading, .file-browser-loading,
.file-browser-empty, .file-browser-empty {
.file-browser-error {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -758,9 +757,28 @@
} }
.file-browser-error { .file-browser-error {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
min-height: 200px;
font-size: 0.875rem;
text-align: center;
padding: 1rem;
gap: 0.5rem;
}
.file-browser-error p {
margin: 0;
color: hsl(var(--destructive)); color: hsl(var(--destructive));
} }
.file-browser-hint {
color: hsl(var(--muted-foreground));
font-size: 0.8rem;
}
.file-browser-item { .file-browser-item {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -278,6 +278,8 @@ const i18n = {
'cli.fileBrowserUp': 'Parent Directory', 'cli.fileBrowserUp': 'Parent Directory',
'cli.fileBrowserHome': 'Home', 'cli.fileBrowserHome': 'Home',
'cli.fileBrowserShowHidden': 'Show hidden files', 'cli.fileBrowserShowHidden': 'Show hidden files',
'cli.fileBrowserApiError': 'Server restart required to enable file browser',
'cli.fileBrowserManualHint': 'Type the full path above and click Select (e.g., C:\\Users\\name\\.gemini)',
// CodexLens Configuration // CodexLens Configuration
'codexlens.config': 'CodexLens Configuration', 'codexlens.config': 'CodexLens Configuration',
@@ -2517,6 +2519,8 @@ const i18n = {
'cli.fileBrowserUp': '上级目录', 'cli.fileBrowserUp': '上级目录',
'cli.fileBrowserHome': '主目录', 'cli.fileBrowserHome': '主目录',
'cli.fileBrowserShowHidden': '显示隐藏文件', 'cli.fileBrowserShowHidden': '显示隐藏文件',
'cli.fileBrowserApiError': '需要重启服务器以启用文件浏览器',
'cli.fileBrowserManualHint': '请在上方输入完整路径后点击选择(如 C:\\Users\\用户名\\.gemini',
// CodexLens 配置 // CodexLens 配置
'codexlens.config': 'CodexLens 配置', 'codexlens.config': 'CodexLens 配置',

View File

@@ -659,7 +659,17 @@ async function loadFileBrowserDirectory(path) {
} catch (err) { } catch (err) {
console.error('Failed to load directory:', err); console.error('Failed to load directory:', err);
if (listContainer) { if (listContainer) {
listContainer.innerHTML = '<div class="file-browser-error">Failed to load directory</div>'; listContainer.innerHTML = '<div class="file-browser-error">' +
'<p>' + t('cli.fileBrowserApiError') + '</p>' +
'<p class="file-browser-hint">' + t('cli.fileBrowserManualHint') + '</p>' +
'</div>';
}
// Enable manual path entry mode - enable select button when path is typed
var selectBtn = document.getElementById('fileBrowserSelectBtn');
var pathInput = document.getElementById('fileBrowserPathInput');
if (selectBtn && pathInput) {
selectBtn.disabled = false;
pathInput.focus();
} }
} }
} }
@@ -744,8 +754,17 @@ function initFileBrowserEvents() {
var selectBtn = document.getElementById('fileBrowserSelectBtn'); var selectBtn = document.getElementById('fileBrowserSelectBtn');
if (selectBtn) { if (selectBtn) {
selectBtn.onclick = function() { selectBtn.onclick = function() {
// First try selected path from list, then fall back to path input
var path = selectBtn.getAttribute('data-selected-path'); var path = selectBtn.getAttribute('data-selected-path');
closeFileBrowserModal(path); if (!path) {
var pathInput = document.getElementById('fileBrowserPathInput');
if (pathInput && pathInput.value.trim()) {
path = pathInput.value.trim();
}
}
if (path) {
closeFileBrowserModal(path);
}
}; };
} }