mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-10 02:24:35 +08:00
feat(codexlens): add CodexLens code indexing platform with incremental updates
- Add CodexLens Python package with SQLite FTS5 search and tree-sitter parsing - Implement workspace-local index storage (.codexlens/ directory) - Add incremental update CLI command for efficient file-level index refresh - Integrate CodexLens with CCW tools (codex_lens action: update) - Add CodexLens Auto-Sync hook template for automatic index updates on file changes - Add CodexLens status card in CCW Dashboard CLI Manager with install/init buttons - Add server APIs: /api/codexlens/status, /api/codexlens/bootstrap, /api/codexlens/init 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,12 +3,14 @@
|
||||
|
||||
// ========== CLI State ==========
|
||||
let cliToolStatus = { gemini: {}, qwen: {}, codex: {} };
|
||||
let codexLensStatus = { ready: false };
|
||||
let defaultCliTool = 'gemini';
|
||||
|
||||
// ========== Initialization ==========
|
||||
function initCliStatus() {
|
||||
// Load CLI status on init
|
||||
loadCliToolStatus();
|
||||
loadCodexLensStatus();
|
||||
}
|
||||
|
||||
// ========== Data Loading ==========
|
||||
@@ -29,6 +31,23 @@ async function loadCliToolStatus() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCodexLensStatus() {
|
||||
try {
|
||||
const response = await fetch('/api/codexlens/status');
|
||||
if (!response.ok) throw new Error('Failed to load CodexLens status');
|
||||
const data = await response.json();
|
||||
codexLensStatus = data;
|
||||
|
||||
// Update CodexLens badge
|
||||
updateCodexLensBadge();
|
||||
|
||||
return data;
|
||||
} catch (err) {
|
||||
console.error('Failed to load CodexLens status:', err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Badge Update ==========
|
||||
function updateCliBadge() {
|
||||
const badge = document.getElementById('badgeCliTools');
|
||||
@@ -42,6 +61,15 @@ function updateCliBadge() {
|
||||
}
|
||||
}
|
||||
|
||||
function updateCodexLensBadge() {
|
||||
const badge = document.getElementById('badgeCodexLens');
|
||||
if (badge) {
|
||||
badge.textContent = codexLensStatus.ready ? 'Ready' : 'Not Installed';
|
||||
badge.classList.toggle('text-success', codexLensStatus.ready);
|
||||
badge.classList.toggle('text-muted-foreground', !codexLensStatus.ready);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Rendering ==========
|
||||
function renderCliStatus() {
|
||||
const container = document.getElementById('cli-status-panel');
|
||||
@@ -75,15 +103,39 @@ function renderCliStatus() {
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
// CodexLens card
|
||||
const codexLensHtml = `
|
||||
<div class="cli-tool-card tool-codexlens ${codexLensStatus.ready ? 'available' : 'unavailable'}">
|
||||
<div class="cli-tool-header">
|
||||
<span class="cli-tool-status ${codexLensStatus.ready ? 'status-available' : 'status-unavailable'}"></span>
|
||||
<span class="cli-tool-name">CodexLens</span>
|
||||
<span class="badge px-1.5 py-0.5 text-xs rounded bg-muted text-muted-foreground">Index</span>
|
||||
</div>
|
||||
<div class="cli-tool-info">
|
||||
${codexLensStatus.ready
|
||||
? `<span class="text-success">v${codexLensStatus.version || 'installed'}</span>`
|
||||
: `<span class="text-muted-foreground">Not Installed</span>`
|
||||
}
|
||||
</div>
|
||||
<div class="cli-tool-actions flex gap-2 mt-2">
|
||||
${!codexLensStatus.ready
|
||||
? `<button class="btn-sm btn-primary" onclick="installCodexLens()">Install</button>`
|
||||
: `<button class="btn-sm btn-outline" onclick="initCodexLensIndex()">Init Index</button>`
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
container.innerHTML = `
|
||||
<div class="cli-status-header">
|
||||
<h3><i data-lucide="terminal" class="w-4 h-4"></i> CLI Tools</h3>
|
||||
<button class="btn-icon" onclick="loadCliToolStatus()" title="Refresh">
|
||||
<button class="btn-icon" onclick="refreshAllCliStatus()" title="Refresh">
|
||||
<i data-lucide="refresh-cw" class="w-4 h-4"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="cli-tools-grid">
|
||||
${toolsHtml}
|
||||
${codexLensHtml}
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -99,3 +151,55 @@ function setDefaultCliTool(tool) {
|
||||
renderCliStatus();
|
||||
showRefreshToast(`Default CLI tool set to ${tool}`, 'success');
|
||||
}
|
||||
|
||||
async function refreshAllCliStatus() {
|
||||
await Promise.all([loadCliToolStatus(), loadCodexLensStatus()]);
|
||||
renderCliStatus();
|
||||
}
|
||||
|
||||
async function installCodexLens() {
|
||||
showRefreshToast('Installing CodexLens...', 'info');
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/codexlens/bootstrap', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
showRefreshToast('CodexLens installed successfully!', 'success');
|
||||
await loadCodexLensStatus();
|
||||
renderCliStatus();
|
||||
} else {
|
||||
showRefreshToast(`Install failed: ${result.error}`, 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
showRefreshToast(`Install error: ${err.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function initCodexLensIndex() {
|
||||
showRefreshToast('Initializing CodexLens index...', 'info');
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/codexlens/init', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ path: projectPath })
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
const data = result.result?.result || result.result || result;
|
||||
const files = data.files_indexed || 0;
|
||||
const symbols = data.symbols_indexed || 0;
|
||||
showRefreshToast(`Index created: ${files} files, ${symbols} symbols`, 'success');
|
||||
} else {
|
||||
showRefreshToast(`Init failed: ${result.error}`, 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
showRefreshToast(`Init error: ${err.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,25 +13,95 @@ const HOOK_TEMPLATES = {
|
||||
event: 'PostToolUse',
|
||||
matcher: 'Write',
|
||||
command: 'curl',
|
||||
args: ['-s', '-X', 'POST', '-H', 'Content-Type: application/json', '-d', '{"type":"summary_written","filePath":"$CLAUDE_FILE_PATHS"}', 'http://localhost:3456/api/hook']
|
||||
args: ['-s', '-X', 'POST', '-H', 'Content-Type: application/json', '-d', '{"type":"summary_written","filePath":"$CLAUDE_FILE_PATHS"}', 'http://localhost:3456/api/hook'],
|
||||
description: 'Notify CCW dashboard when files are written',
|
||||
category: 'notification'
|
||||
},
|
||||
'log-tool': {
|
||||
event: 'PostToolUse',
|
||||
matcher: '',
|
||||
command: 'bash',
|
||||
args: ['-c', 'echo "[$(date)] Tool: $CLAUDE_TOOL_NAME, Files: $CLAUDE_FILE_PATHS" >> ~/.claude/tool-usage.log']
|
||||
args: ['-c', 'echo "[$(date)] Tool: $CLAUDE_TOOL_NAME, Files: $CLAUDE_FILE_PATHS" >> ~/.claude/tool-usage.log'],
|
||||
description: 'Log all tool executions to a file',
|
||||
category: 'logging'
|
||||
},
|
||||
'lint-check': {
|
||||
event: 'PostToolUse',
|
||||
matcher: 'Write',
|
||||
command: 'bash',
|
||||
args: ['-c', 'for f in $CLAUDE_FILE_PATHS; do if [[ "$f" =~ \\.(js|ts|jsx|tsx)$ ]]; then npx eslint "$f" --fix 2>/dev/null || true; fi; done']
|
||||
args: ['-c', 'for f in $CLAUDE_FILE_PATHS; do if [[ "$f" =~ \\.(js|ts|jsx|tsx)$ ]]; then npx eslint "$f" --fix 2>/dev/null || true; fi; done'],
|
||||
description: 'Run ESLint on JavaScript/TypeScript files after write',
|
||||
category: 'quality'
|
||||
},
|
||||
'git-add': {
|
||||
event: 'PostToolUse',
|
||||
matcher: 'Write',
|
||||
command: 'bash',
|
||||
args: ['-c', 'for f in $CLAUDE_FILE_PATHS; do git add "$f" 2>/dev/null || true; done']
|
||||
args: ['-c', 'for f in $CLAUDE_FILE_PATHS; do git add "$f" 2>/dev/null || true; done'],
|
||||
description: 'Automatically stage written files to git',
|
||||
category: 'git'
|
||||
},
|
||||
'codexlens-update': {
|
||||
event: 'PostToolUse',
|
||||
matcher: 'Write|Edit',
|
||||
command: 'bash',
|
||||
args: ['-c', 'if [ -d ".codexlens" ] && [ -n "$CLAUDE_FILE_PATHS" ]; then python -m codexlens update $CLAUDE_FILE_PATHS --json 2>/dev/null || ~/.codexlens/venv/bin/python -m codexlens update $CLAUDE_FILE_PATHS --json 2>/dev/null || true; fi'],
|
||||
description: 'Auto-update code index when files are written or edited',
|
||||
category: 'indexing'
|
||||
},
|
||||
'memory-update-related': {
|
||||
event: 'Stop',
|
||||
matcher: '',
|
||||
command: 'bash',
|
||||
args: ['-c', 'ccw tool exec update_module_claude \'{"strategy":"related","tool":"gemini"}\''],
|
||||
description: 'Update CLAUDE.md for changed modules when session ends',
|
||||
category: 'memory',
|
||||
configurable: true,
|
||||
config: {
|
||||
tool: { type: 'select', options: ['gemini', 'qwen', 'codex'], default: 'gemini', label: 'CLI Tool' },
|
||||
strategy: { type: 'select', options: ['related', 'single-layer'], default: 'related', label: 'Strategy' }
|
||||
}
|
||||
},
|
||||
'memory-update-periodic': {
|
||||
event: 'PostToolUse',
|
||||
matcher: 'Write|Edit',
|
||||
command: 'bash',
|
||||
args: ['-c', 'INTERVAL=300; LAST_FILE=~/.claude/.last_memory_update; NOW=$(date +%s); LAST=0; [ -f "$LAST_FILE" ] && LAST=$(cat "$LAST_FILE"); if [ $((NOW - LAST)) -ge $INTERVAL ]; then echo $NOW > "$LAST_FILE"; ccw tool exec update_module_claude \'{"strategy":"related","tool":"gemini"}\' & fi'],
|
||||
description: 'Periodically update CLAUDE.md (default: 5 min interval)',
|
||||
category: 'memory',
|
||||
configurable: true,
|
||||
config: {
|
||||
tool: { type: 'select', options: ['gemini', 'qwen', 'codex'], default: 'gemini', label: 'CLI Tool' },
|
||||
interval: { type: 'number', default: 300, min: 60, max: 3600, label: 'Interval (seconds)', step: 60 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ========== Wizard Templates (Special Category) ==========
|
||||
const WIZARD_TEMPLATES = {
|
||||
'memory-update': {
|
||||
name: 'Memory Update Hook',
|
||||
description: 'Automatically update CLAUDE.md documentation based on code changes',
|
||||
icon: 'brain',
|
||||
options: [
|
||||
{
|
||||
id: 'on-stop',
|
||||
name: 'On Session End',
|
||||
description: 'Update documentation when Claude session ends',
|
||||
templateId: 'memory-update-related'
|
||||
},
|
||||
{
|
||||
id: 'periodic',
|
||||
name: 'Periodic Update',
|
||||
description: 'Update documentation at regular intervals during session',
|
||||
templateId: 'memory-update-periodic'
|
||||
}
|
||||
],
|
||||
configFields: [
|
||||
{ key: 'tool', type: 'select', label: 'CLI Tool', options: ['gemini', 'qwen', 'codex'], default: 'gemini', description: 'Tool for documentation generation' },
|
||||
{ key: 'interval', type: 'number', label: 'Interval (seconds)', default: 300, min: 60, max: 3600, step: 60, showFor: ['periodic'], description: 'Time between updates' },
|
||||
{ key: 'strategy', type: 'select', label: 'Update Strategy', options: ['related', 'single-layer'], default: 'related', description: 'Related: changed modules, Single-layer: current directory' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ async function renderHookManager() {
|
||||
</div>
|
||||
|
||||
<div class="hook-templates-grid grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
${renderQuickInstallCard('codexlens-update', 'CodexLens Auto-Sync', 'Auto-update code index when files are written or edited', 'PostToolUse', 'Write|Edit')}
|
||||
${renderQuickInstallCard('ccw-notify', 'CCW Dashboard Notify', 'Notify CCW dashboard when files are written', 'PostToolUse', 'Write')}
|
||||
${renderQuickInstallCard('log-tool', 'Tool Usage Logger', 'Log all tool executions to a file', 'PostToolUse', 'All')}
|
||||
${renderQuickInstallCard('lint-check', 'Auto Lint Check', 'Run ESLint on JavaScript/TypeScript files after write', 'PostToolUse', 'Write')}
|
||||
|
||||
Reference in New Issue
Block a user