mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-15 02:16:27 +00:00
feat(websearch): add model config + improve hook UX
- Add model field to Gemini/OpenCode WebSearch providers - Config.yaml now explicitly stores model (gemini-2.5-flash, opencode/grok-code) - UI: blur-to-save for model inputs (no save on every keystroke) - UI: collapsible install hints inside provider cards - UI: left accent border for enabled providers (better light theme contrast) - Hook: use systemMessage for info-styled confirmation - Hook: cleaner result format without defensive "NOT an error" language
This commit is contained in:
1 parent
6d69379ead
commit
14c53d575f
6 files changed
+342
-184
No files matched your search
@@ -11,6 +11,7 @@
|
||||
* CCS_WEBSEARCH_ENABLED=1 - Enable WebSearch (default: 1)
|
||||
* CCS_WEBSEARCH_TIMEOUT=55 - Timeout in seconds (default: 55)
|
||||
* CCS_WEBSEARCH_GEMINI=1 - Enable Gemini CLI provider
|
||||
* CCS_WEBSEARCH_GEMINI_MODEL - Gemini model (default: gemini-2.5-flash)
|
||||
* CCS_WEBSEARCH_OPENCODE=1 - Enable OpenCode provider
|
||||
* CCS_WEBSEARCH_GROK=1 - Enable Grok CLI provider
|
||||
* CCS_WEBSEARCH_OPENCODE_MODEL - OpenCode model (default: opencode/grok-code)
|
||||
@@ -258,13 +259,16 @@ function tryGeminiSearch(query, timeoutSec = DEFAULT_TIMEOUT_SEC) {
|
||||
const config = PROVIDER_CONFIG.gemini;
|
||||
const prompt = buildPrompt('gemini', query);
|
||||
|
||||
// Allow model override via env var
|
||||
const model = process.env.CCS_WEBSEARCH_GEMINI_MODEL || config.model;
|
||||
|
||||
if (process.env.CCS_DEBUG) {
|
||||
console.error(`[CCS Hook] Executing: gemini --model ${config.model} --yolo -p "..."`);
|
||||
console.error(`[CCS Hook] Executing: gemini --model ${model} --yolo -p "..."`);
|
||||
}
|
||||
|
||||
const spawnResult = spawnSync(
|
||||
'gemini',
|
||||
['--model', config.model, '--yolo', '-p', prompt],
|
||||
['--model', model, '--yolo', '-p', prompt],
|
||||
{
|
||||
encoding: 'utf8',
|
||||
timeout: timeoutMs,
|
||||
@@ -441,29 +445,40 @@ function tryGrokSearch(query, timeoutSec = DEFAULT_TIMEOUT_SEC) {
|
||||
*/
|
||||
function formatSearchResults(query, content, providerName) {
|
||||
return [
|
||||
'=== WEBSEARCH COMPLETED SUCCESSFULLY ===',
|
||||
`(via ${providerName} - this is NOT an error)`,
|
||||
`[WebSearch Result via ${providerName}]`,
|
||||
'',
|
||||
`Query: "${query}"`,
|
||||
'',
|
||||
content,
|
||||
'',
|
||||
'=========================================',
|
||||
'Use this information to answer the user. Search again if needed.',
|
||||
'---',
|
||||
'Use this information to answer the user.',
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Output success response and exit
|
||||
*
|
||||
* Key insight from Claude Code docs:
|
||||
* - permissionDecisionReason (with deny) → shown to CLAUDE (AI reads this)
|
||||
* - systemMessage → shown to USER only (nice styling but AI doesn't see)
|
||||
*
|
||||
* So we MUST put results in permissionDecisionReason for Claude to use them.
|
||||
* systemMessage provides the nice UI for the user.
|
||||
*/
|
||||
function outputSuccess(query, content, providerName) {
|
||||
const formattedResults = formatSearchResults(query, content, providerName);
|
||||
|
||||
const output = {
|
||||
decision: 'block',
|
||||
reason: `WebSearch completed via ${providerName}`,
|
||||
// Nice message for user (shows as "says:" - info style)
|
||||
systemMessage: `[WebSearch via ${providerName}] Results retrieved successfully. See below.`,
|
||||
hookSpecificOutput: {
|
||||
hookEventName: 'PreToolUse',
|
||||
permissionDecision: 'deny',
|
||||
permissionDecisionReason: formatSearchResults(query, content, providerName),
|
||||
// Full results here - Claude reads this
|
||||
permissionDecisionReason: formattedResults,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user