diff --git a/ccw/src/templates/dashboard-js/components/hook-manager.js b/ccw/src/templates/dashboard-js/components/hook-manager.js index 8c2b32b9..8ec28cd2 100644 --- a/ccw/src/templates/dashboard-js/components/hook-manager.js +++ b/ccw/src/templates/dashboard-js/components/hook-manager.js @@ -138,14 +138,14 @@ const HOOK_TEMPLATES = { category: 'memory', timeout: 5000 }, - // Session Context - Fires once per session at startup - // Uses state file to detect first prompt, only fires once + // Session Context - Progressive disclosure based on session state + // First prompt: returns cluster overview, subsequent: intent-matched sessions 'session-context': { event: 'UserPromptSubmit', matcher: '', - command: 'bash', - args: ['-c', 'STATE_FILE="/tmp/.ccw-session-$CLAUDE_SESSION_ID"; [ -f "$STATE_FILE" ] && exit 0; touch "$STATE_FILE"; curl -s -X POST -H "Content-Type: application/json" -d "{\\"sessionId\\":\\"$CLAUDE_SESSION_ID\\"}" http://localhost:3456/api/hook/session-context 2>/dev/null | jq -r ".content // empty"'], - description: 'Load session context once at startup (cluster overview)', + command: 'ccw', + args: ['hook', 'session-context', '--stdin'], + description: 'Progressive session context (cluster overview → intent matching)', category: 'context', timeout: 5000 } diff --git a/ccw/src/templates/dashboard-js/views/hook-manager.js b/ccw/src/templates/dashboard-js/views/hook-manager.js index 35125376..45da359e 100644 --- a/ccw/src/templates/dashboard-js/views/hook-manager.js +++ b/ccw/src/templates/dashboard-js/views/hook-manager.js @@ -449,8 +449,23 @@ function isHookTemplateInstalled(templateId) { const template = HOOK_TEMPLATES[templateId]; if (!template) return false; - // Build expected command string - const templateCmd = template.command + (template.args ? ' ' + template.args.join(' ') : ''); + // Define unique patterns for each template type (more specific than just command) + const uniquePatterns = { + 'session-context': 'hook session-context', + 'codexlens-update': 'codexlens update', + 'ccw-notify': 'api/hook', + 'log-tool': 'tool-usage.log', + 'lint-check': 'eslint', + 'git-add': 'git add', + 'memory-file-read': 'memory track --type file --action read', + 'memory-file-write': 'memory track --type file --action write', + 'memory-prompt-track': 'memory track --type topic', + 'skill-context-auto': 'skill-context-auto' + }; + + // Use unique pattern if defined, otherwise fall back to command + args + const searchPattern = uniquePatterns[templateId] || + (template.command + (template.args ? ' ' + template.args.join(' ') : '')); // Check project hooks const projectHooks = hookConfig.project?.hooks?.[template.event]; @@ -459,7 +474,7 @@ function isHookTemplateInstalled(templateId) { if (hookList.some(h => { // Check both old format (h.command) and new format (h.hooks[0].command) const cmd = h.hooks?.[0]?.command || h.command || ''; - return cmd.includes(template.command); + return cmd.includes(searchPattern); })) return true; } @@ -469,7 +484,7 @@ function isHookTemplateInstalled(templateId) { const hookList = Array.isArray(globalHooks) ? globalHooks : [globalHooks]; if (hookList.some(h => { const cmd = h.hooks?.[0]?.command || h.command || ''; - return cmd.includes(template.command); + return cmd.includes(searchPattern); })) return true; } @@ -512,7 +527,7 @@ async function uninstallHookTemplate(templateId) { // Define unique patterns for each template type const uniquePatterns = { - 'session-context': 'api/hook/session-context', + 'session-context': 'hook session-context', 'codexlens-update': 'codexlens update', 'ccw-notify': 'api/hook', 'log-tool': 'tool-usage.log', diff --git a/ccw/src/tools/smart-search.ts b/ccw/src/tools/smart-search.ts index 4841a9bb..931ee25e 100644 --- a/ccw/src/tools/smart-search.ts +++ b/ccw/src/tools/smart-search.ts @@ -244,6 +244,7 @@ interface SearchMetadata { warning?: string; note?: string; index_status?: 'indexed' | 'not_indexed' | 'partial'; + fallback?: string; // Fallback mode used (e.g., 'fuzzy') fallback_history?: string[]; suggested_weights?: Record; // Tokenization metadata (ripgrep mode)