mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-12 02:37:45 +08:00
feat: 添加工作空间索引状态接口,增强 CodexLens 状态检查功能,支持前端显示索引信息
This commit is contained in:
@@ -2366,5 +2366,121 @@ except Exception as e:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// API: Get workspace index status (FTS and Vector coverage percentages)
|
||||||
|
if (pathname === '/api/codexlens/workspace-status') {
|
||||||
|
try {
|
||||||
|
const projectPath = url.searchParams.get('path') || initialPath;
|
||||||
|
|
||||||
|
// Check if CodexLens is installed first
|
||||||
|
const venvStatus = await checkVenvStatus();
|
||||||
|
if (!venvStatus.ready) {
|
||||||
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
hasIndex: false,
|
||||||
|
fts: { indexed: false, percent: 0 },
|
||||||
|
vector: { indexed: false, percent: 0 },
|
||||||
|
message: 'CodexLens not installed'
|
||||||
|
}));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ftsStatus = { indexed: false, percent: 0, totalFiles: 0, indexedFiles: 0 };
|
||||||
|
let vectorStatus = { indexed: false, percent: 0, totalFiles: 0, filesWithEmbeddings: 0, totalChunks: 0 };
|
||||||
|
let hasIndex = false;
|
||||||
|
let indexRoot = '';
|
||||||
|
|
||||||
|
// First, get project info to check if index exists
|
||||||
|
const projectsResult = await executeCodexLens(['projects', 'show', projectPath, '--json']);
|
||||||
|
|
||||||
|
if (projectsResult.success && projectsResult.output) {
|
||||||
|
try {
|
||||||
|
const projectData = extractJSON(projectsResult.output);
|
||||||
|
if (projectData.success && projectData.result) {
|
||||||
|
const project = projectData.result;
|
||||||
|
hasIndex = true;
|
||||||
|
indexRoot = project.index_root || '';
|
||||||
|
|
||||||
|
// FTS is always 100% when index exists
|
||||||
|
ftsStatus = {
|
||||||
|
indexed: true,
|
||||||
|
percent: 100,
|
||||||
|
totalFiles: project.total_files || 0,
|
||||||
|
indexedFiles: project.total_files || 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Now get embeddings status for this specific project
|
||||||
|
const statusResult = await executeCodexLens(['index', 'status', projectPath, '--json']);
|
||||||
|
if (statusResult.success && statusResult.output) {
|
||||||
|
try {
|
||||||
|
const status = extractJSON(statusResult.output);
|
||||||
|
if (status.success && status.result && status.result.embeddings) {
|
||||||
|
const embeddings = status.result.embeddings;
|
||||||
|
|
||||||
|
// Find the project-specific embedding info from indexes array
|
||||||
|
const indexes = embeddings.indexes || [];
|
||||||
|
let projectEmbedding = null;
|
||||||
|
|
||||||
|
// Look for matching project by path or name
|
||||||
|
const { basename, resolve } = await import('path');
|
||||||
|
const normalizedPath = resolve(projectPath).toLowerCase();
|
||||||
|
const projectName = basename(projectPath);
|
||||||
|
|
||||||
|
for (const idx of indexes) {
|
||||||
|
const idxPath = (idx.path || '').toLowerCase();
|
||||||
|
const idxProject = (idx.project || '').toLowerCase();
|
||||||
|
if (idxPath.includes(normalizedPath.replace(/\\/g, '/')) ||
|
||||||
|
idxPath.includes(normalizedPath) ||
|
||||||
|
idxProject === projectName.toLowerCase()) {
|
||||||
|
projectEmbedding = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projectEmbedding) {
|
||||||
|
vectorStatus = {
|
||||||
|
indexed: projectEmbedding.has_embeddings || false,
|
||||||
|
percent: projectEmbedding.coverage_percent || 0,
|
||||||
|
totalFiles: projectEmbedding.total_files || project.total_files || 0,
|
||||||
|
filesWithEmbeddings: Math.round((projectEmbedding.coverage_percent || 0) * (projectEmbedding.total_files || 0) / 100),
|
||||||
|
totalChunks: projectEmbedding.total_chunks || 0
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// No specific project found, use aggregated stats
|
||||||
|
vectorStatus = {
|
||||||
|
indexed: embeddings.indexes_with_embeddings > 0,
|
||||||
|
percent: 0,
|
||||||
|
totalFiles: project.total_files || 0,
|
||||||
|
filesWithEmbeddings: 0,
|
||||||
|
totalChunks: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[CodexLens] Failed to parse index status:', e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[CodexLens] Failed to parse project data:', e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
success: true,
|
||||||
|
hasIndex,
|
||||||
|
indexRoot,
|
||||||
|
path: projectPath,
|
||||||
|
fts: ftsStatus,
|
||||||
|
vector: vectorStatus
|
||||||
|
}));
|
||||||
|
} catch (err) {
|
||||||
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||||
|
res.end(JSON.stringify({ success: false, error: err.message }));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -375,6 +375,14 @@ const i18n = {
|
|||||||
'codexlens.cleanFailed': 'Failed to clean indexes',
|
'codexlens.cleanFailed': 'Failed to clean indexes',
|
||||||
'codexlens.loadingConfig': 'Loading configuration...',
|
'codexlens.loadingConfig': 'Loading configuration...',
|
||||||
|
|
||||||
|
// Workspace Index Status
|
||||||
|
'codexlens.workspaceStatus': 'Workspace Index Status',
|
||||||
|
'codexlens.noIndexFound': 'No index found for current workspace',
|
||||||
|
'codexlens.filesIndexed': 'files indexed',
|
||||||
|
'codexlens.filesWithEmbeddings': 'files with embeddings',
|
||||||
|
'codexlens.vectorSearchEnabled': 'Vector search enabled',
|
||||||
|
'codexlens.vectorSearchPartial': 'Vector search requires ≥50% coverage',
|
||||||
|
|
||||||
// Model Management
|
// Model Management
|
||||||
'codexlens.semanticDeps': 'Semantic Dependencies',
|
'codexlens.semanticDeps': 'Semantic Dependencies',
|
||||||
'codexlens.checkingDeps': 'Checking dependencies...',
|
'codexlens.checkingDeps': 'Checking dependencies...',
|
||||||
@@ -2384,6 +2392,14 @@ const i18n = {
|
|||||||
'codexlens.cleanFailed': '清理索引失败',
|
'codexlens.cleanFailed': '清理索引失败',
|
||||||
'codexlens.loadingConfig': '加载配置中...',
|
'codexlens.loadingConfig': '加载配置中...',
|
||||||
|
|
||||||
|
// 工作空间索引状态
|
||||||
|
'codexlens.workspaceStatus': '工作空间索引状态',
|
||||||
|
'codexlens.noIndexFound': '当前工作空间未找到索引',
|
||||||
|
'codexlens.filesIndexed': '个文件已索引',
|
||||||
|
'codexlens.filesWithEmbeddings': '个文件已嵌入',
|
||||||
|
'codexlens.vectorSearchEnabled': '向量搜索已启用',
|
||||||
|
'codexlens.vectorSearchPartial': '向量搜索需要≥50%覆盖率',
|
||||||
|
|
||||||
// 模型管理
|
// 模型管理
|
||||||
'codexlens.semanticDeps': '语义搜索依赖',
|
'codexlens.semanticDeps': '语义搜索依赖',
|
||||||
'codexlens.checkingDeps': '检查依赖中...',
|
'codexlens.checkingDeps': '检查依赖中...',
|
||||||
|
|||||||
@@ -18,6 +18,117 @@ function escapeHtml(str) {
|
|||||||
.replace(/'/g, ''');
|
.replace(/'/g, ''');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// WORKSPACE INDEX STATUS
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh workspace index status (FTS and Vector coverage)
|
||||||
|
*/
|
||||||
|
async function refreshWorkspaceIndexStatus() {
|
||||||
|
var container = document.getElementById('workspaceIndexStatusContent');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
// Show loading state
|
||||||
|
container.innerHTML = '<div class="text-xs text-muted-foreground text-center py-2">' +
|
||||||
|
'<i data-lucide="loader-2" class="w-4 h-4 animate-spin inline mr-1"></i> ' + (t('common.loading') || 'Loading...') +
|
||||||
|
'</div>';
|
||||||
|
if (window.lucide) lucide.createIcons();
|
||||||
|
|
||||||
|
try {
|
||||||
|
var response = await fetch('/api/codexlens/workspace-status');
|
||||||
|
var result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
var html = '';
|
||||||
|
|
||||||
|
if (!result.hasIndex) {
|
||||||
|
// No index for current workspace
|
||||||
|
html = '<div class="text-center py-3">' +
|
||||||
|
'<div class="text-sm text-muted-foreground mb-2">' +
|
||||||
|
'<i data-lucide="alert-circle" class="w-4 h-4 inline mr-1"></i> ' +
|
||||||
|
(t('codexlens.noIndexFound') || 'No index found for current workspace') +
|
||||||
|
'</div>' +
|
||||||
|
'<button onclick="runFtsFullIndex()" class="text-xs text-primary hover:underline">' +
|
||||||
|
(t('codexlens.createIndex') || 'Create Index') +
|
||||||
|
'</button>' +
|
||||||
|
'</div>';
|
||||||
|
} else {
|
||||||
|
// FTS Status
|
||||||
|
var ftsPercent = result.fts.percent || 0;
|
||||||
|
var ftsColor = ftsPercent >= 100 ? 'bg-success' : (ftsPercent > 0 ? 'bg-blue-500' : 'bg-muted-foreground');
|
||||||
|
var ftsTextColor = ftsPercent >= 100 ? 'text-success' : (ftsPercent > 0 ? 'text-blue-500' : 'text-muted-foreground');
|
||||||
|
|
||||||
|
html += '<div class="space-y-1">' +
|
||||||
|
'<div class="flex items-center justify-between text-xs">' +
|
||||||
|
'<span class="flex items-center gap-1.5">' +
|
||||||
|
'<i data-lucide="file-text" class="w-3.5 h-3.5 text-blue-500"></i> ' +
|
||||||
|
'<span class="font-medium">' + (t('codexlens.ftsIndex') || 'FTS Index') + '</span>' +
|
||||||
|
'</span>' +
|
||||||
|
'<span class="' + ftsTextColor + ' font-medium">' + ftsPercent + '%</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="h-1.5 bg-muted rounded-full overflow-hidden">' +
|
||||||
|
'<div class="h-full ' + ftsColor + ' transition-all duration-300" style="width: ' + ftsPercent + '%"></div>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="text-xs text-muted-foreground">' +
|
||||||
|
(result.fts.indexedFiles || 0) + ' / ' + (result.fts.totalFiles || 0) + ' ' + (t('codexlens.filesIndexed') || 'files indexed') +
|
||||||
|
'</div>' +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
// Vector Status
|
||||||
|
var vectorPercent = result.vector.percent || 0;
|
||||||
|
var vectorColor = vectorPercent >= 100 ? 'bg-success' : (vectorPercent >= 50 ? 'bg-purple-500' : (vectorPercent > 0 ? 'bg-purple-400' : 'bg-muted-foreground'));
|
||||||
|
var vectorTextColor = vectorPercent >= 100 ? 'text-success' : (vectorPercent >= 50 ? 'text-purple-500' : (vectorPercent > 0 ? 'text-purple-400' : 'text-muted-foreground'));
|
||||||
|
|
||||||
|
html += '<div class="space-y-1 mt-3">' +
|
||||||
|
'<div class="flex items-center justify-between text-xs">' +
|
||||||
|
'<span class="flex items-center gap-1.5">' +
|
||||||
|
'<i data-lucide="brain" class="w-3.5 h-3.5 text-purple-500"></i> ' +
|
||||||
|
'<span class="font-medium">' + (t('codexlens.vectorIndex') || 'Vector Index') + '</span>' +
|
||||||
|
'</span>' +
|
||||||
|
'<span class="' + vectorTextColor + ' font-medium">' + vectorPercent.toFixed(1) + '%</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="h-1.5 bg-muted rounded-full overflow-hidden">' +
|
||||||
|
'<div class="h-full ' + vectorColor + ' transition-all duration-300" style="width: ' + vectorPercent + '%"></div>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="text-xs text-muted-foreground">' +
|
||||||
|
(result.vector.filesWithEmbeddings || 0) + ' / ' + (result.vector.totalFiles || 0) + ' ' + (t('codexlens.filesWithEmbeddings') || 'files with embeddings') +
|
||||||
|
(result.vector.totalChunks > 0 ? ' (' + result.vector.totalChunks + ' chunks)' : '') +
|
||||||
|
'</div>' +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
// Vector search availability indicator
|
||||||
|
if (vectorPercent >= 50) {
|
||||||
|
html += '<div class="flex items-center gap-1.5 mt-2 pt-2 border-t border-border">' +
|
||||||
|
'<i data-lucide="check-circle-2" class="w-3.5 h-3.5 text-success"></i>' +
|
||||||
|
'<span class="text-xs text-success">' + (t('codexlens.vectorSearchEnabled') || 'Vector search enabled') + '</span>' +
|
||||||
|
'</div>';
|
||||||
|
} else if (vectorPercent > 0) {
|
||||||
|
html += '<div class="flex items-center gap-1.5 mt-2 pt-2 border-t border-border">' +
|
||||||
|
'<i data-lucide="alert-triangle" class="w-3.5 h-3.5 text-warning"></i>' +
|
||||||
|
'<span class="text-xs text-warning">' + (t('codexlens.vectorSearchPartial') || 'Vector search requires ≥50% coverage') + '</span>' +
|
||||||
|
'</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = html;
|
||||||
|
} else {
|
||||||
|
container.innerHTML = '<div class="text-xs text-destructive text-center py-2">' +
|
||||||
|
'<i data-lucide="alert-circle" class="w-4 h-4 inline mr-1"></i> ' +
|
||||||
|
(result.error || t('common.error') || 'Error loading status') +
|
||||||
|
'</div>';
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[CodexLens] Failed to load workspace status:', err);
|
||||||
|
container.innerHTML = '<div class="text-xs text-destructive text-center py-2">' +
|
||||||
|
'<i data-lucide="alert-circle" class="w-4 h-4 inline mr-1"></i> ' +
|
||||||
|
(t('common.error') || 'Error') + ': ' + err.message +
|
||||||
|
'</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.lucide) lucide.createIcons();
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// CODEXLENS CONFIGURATION MODAL
|
// CODEXLENS CONFIGURATION MODAL
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -29,9 +140,23 @@ async function showCodexLensConfigModal() {
|
|||||||
try {
|
try {
|
||||||
showRefreshToast(t('codexlens.loadingConfig'), 'info');
|
showRefreshToast(t('codexlens.loadingConfig'), 'info');
|
||||||
|
|
||||||
// Fetch current config
|
// Fetch current config and status in parallel
|
||||||
const response = await fetch('/api/codexlens/config');
|
const [configResponse, statusResponse] = await Promise.all([
|
||||||
const config = await response.json();
|
fetch('/api/codexlens/config'),
|
||||||
|
fetch('/api/codexlens/status')
|
||||||
|
]);
|
||||||
|
const config = await configResponse.json();
|
||||||
|
const status = await statusResponse.json();
|
||||||
|
|
||||||
|
// Update window.cliToolsStatus to ensure isInstalled is correct
|
||||||
|
if (!window.cliToolsStatus) {
|
||||||
|
window.cliToolsStatus = {};
|
||||||
|
}
|
||||||
|
window.cliToolsStatus.codexlens = {
|
||||||
|
...(window.cliToolsStatus.codexlens || {}),
|
||||||
|
installed: status.ready || false,
|
||||||
|
version: status.version || null
|
||||||
|
};
|
||||||
|
|
||||||
const modalHtml = buildCodexLensConfigContent(config);
|
const modalHtml = buildCodexLensConfigContent(config);
|
||||||
|
|
||||||
@@ -147,6 +272,25 @@ function buildCodexLensConfigContent(config) {
|
|||||||
'</div>' +
|
'</div>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
|
|
||||||
|
// Workspace Index Status (only if installed)
|
||||||
|
(isInstalled
|
||||||
|
? '<div class="rounded-lg border border-border p-4 mb-4 bg-card" id="workspaceIndexStatus">' +
|
||||||
|
'<div class="flex items-center justify-between mb-3">' +
|
||||||
|
'<h4 class="text-sm font-medium flex items-center gap-2">' +
|
||||||
|
'<i data-lucide="hard-drive" class="w-4 h-4"></i> ' + (t('codexlens.workspaceStatus') || 'Workspace Index Status') +
|
||||||
|
'</h4>' +
|
||||||
|
'<button onclick="refreshWorkspaceIndexStatus()" class="text-xs text-primary hover:underline flex items-center gap-1" title="Refresh status">' +
|
||||||
|
'<i data-lucide="refresh-cw" class="w-3 h-3"></i> ' + (t('common.refresh') || 'Refresh') +
|
||||||
|
'</button>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div id="workspaceIndexStatusContent" class="space-y-3">' +
|
||||||
|
'<div class="text-xs text-muted-foreground text-center py-2">' +
|
||||||
|
'<i data-lucide="loader-2" class="w-4 h-4 animate-spin inline mr-1"></i> Loading...' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>'
|
||||||
|
: '') +
|
||||||
|
|
||||||
// Index Operations - 4 buttons grid
|
// Index Operations - 4 buttons grid
|
||||||
'<div class="space-y-2">' +
|
'<div class="space-y-2">' +
|
||||||
'<h4 class="text-xs font-medium text-muted-foreground uppercase tracking-wide mb-2">' + (t('codexlens.indexOperations') || 'Index Operations') + '</h4>' +
|
'<h4 class="text-xs font-medium text-muted-foreground uppercase tracking-wide mb-2">' + (t('codexlens.indexOperations') || 'Index Operations') + '</h4>' +
|
||||||
@@ -547,6 +691,9 @@ function initCodexLensConfigEvents(currentConfig) {
|
|||||||
// Load model lists (embedding and reranker)
|
// Load model lists (embedding and reranker)
|
||||||
loadModelList();
|
loadModelList();
|
||||||
loadRerankerModelList();
|
loadRerankerModelList();
|
||||||
|
|
||||||
|
// Load workspace index status
|
||||||
|
refreshWorkspaceIndexStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ async function renderHookManager() {
|
|||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
${renderWizardCard('memory-update')}
|
${renderWizardCard('memory-update')}
|
||||||
${renderWizardCard('memory-setup')}
|
${renderWizardCard('danger-protection')}
|
||||||
${renderWizardCard('skill-context')}
|
${renderWizardCard('skill-context')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -216,10 +216,12 @@ function renderWizardCard(wizardId) {
|
|||||||
// Get translated wizard name and description
|
// Get translated wizard name and description
|
||||||
const wizardName = wizardId === 'memory-update' ? t('hook.wizard.memoryUpdate') :
|
const wizardName = wizardId === 'memory-update' ? t('hook.wizard.memoryUpdate') :
|
||||||
wizardId === 'memory-setup' ? t('hook.wizard.memorySetup') :
|
wizardId === 'memory-setup' ? t('hook.wizard.memorySetup') :
|
||||||
wizardId === 'skill-context' ? t('hook.wizard.skillContext') : wizard.name;
|
wizardId === 'skill-context' ? t('hook.wizard.skillContext') :
|
||||||
|
wizardId === 'danger-protection' ? t('hook.wizard.dangerProtection') : wizard.name;
|
||||||
const wizardDesc = wizardId === 'memory-update' ? t('hook.wizard.memoryUpdateDesc') :
|
const wizardDesc = wizardId === 'memory-update' ? t('hook.wizard.memoryUpdateDesc') :
|
||||||
wizardId === 'memory-setup' ? t('hook.wizard.memorySetupDesc') :
|
wizardId === 'memory-setup' ? t('hook.wizard.memorySetupDesc') :
|
||||||
wizardId === 'skill-context' ? t('hook.wizard.skillContextDesc') : wizard.description;
|
wizardId === 'skill-context' ? t('hook.wizard.skillContextDesc') :
|
||||||
|
wizardId === 'danger-protection' ? t('hook.wizard.dangerProtectionDesc') : wizard.description;
|
||||||
|
|
||||||
// Translate options
|
// Translate options
|
||||||
const getOptionName = (wizardId, optId) => {
|
const getOptionName = (wizardId, optId) => {
|
||||||
@@ -237,6 +239,12 @@ function renderWizardCard(wizardId) {
|
|||||||
if (optId === 'keyword') return t('hook.wizard.keywordMatching');
|
if (optId === 'keyword') return t('hook.wizard.keywordMatching');
|
||||||
if (optId === 'auto') return t('hook.wizard.autoDetection');
|
if (optId === 'auto') return t('hook.wizard.autoDetection');
|
||||||
}
|
}
|
||||||
|
if (wizardId === 'danger-protection') {
|
||||||
|
if (optId === 'bash-confirm') return t('hook.wizard.dangerBashConfirm');
|
||||||
|
if (optId === 'file-protection') return t('hook.wizard.dangerFileProtection');
|
||||||
|
if (optId === 'git-destructive') return t('hook.wizard.dangerGitDestructive');
|
||||||
|
if (optId === 'network-confirm') return t('hook.wizard.dangerNetworkConfirm');
|
||||||
|
}
|
||||||
return wizard.options.find(o => o.id === optId)?.name || '';
|
return wizard.options.find(o => o.id === optId)?.name || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -255,6 +263,12 @@ function renderWizardCard(wizardId) {
|
|||||||
if (optId === 'keyword') return t('hook.wizard.keywordMatchingDesc');
|
if (optId === 'keyword') return t('hook.wizard.keywordMatchingDesc');
|
||||||
if (optId === 'auto') return t('hook.wizard.autoDetectionDesc');
|
if (optId === 'auto') return t('hook.wizard.autoDetectionDesc');
|
||||||
}
|
}
|
||||||
|
if (wizardId === 'danger-protection') {
|
||||||
|
if (optId === 'bash-confirm') return t('hook.wizard.dangerBashConfirmDesc');
|
||||||
|
if (optId === 'file-protection') return t('hook.wizard.dangerFileProtectionDesc');
|
||||||
|
if (optId === 'git-destructive') return t('hook.wizard.dangerGitDestructiveDesc');
|
||||||
|
if (optId === 'network-confirm') return t('hook.wizard.dangerNetworkConfirmDesc');
|
||||||
|
}
|
||||||
return wizard.options.find(o => o.id === optId)?.description || '';
|
return wizard.options.find(o => o.id === optId)?.description || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -903,7 +903,7 @@ def status(
|
|||||||
schema_version = store._get_schema_version(conn)
|
schema_version = store._get_schema_version(conn)
|
||||||
# Check if dual FTS tables exist
|
# Check if dual FTS tables exist
|
||||||
cursor = conn.execute(
|
cursor = conn.execute(
|
||||||
"SELECT name FROM sqlite_master WHERE type='table' AND name IN ('search_fts_exact', 'search_fts_fuzzy')"
|
"SELECT name FROM sqlite_master WHERE type='table' AND name IN ('files_fts_exact', 'files_fts_fuzzy')"
|
||||||
)
|
)
|
||||||
fts_tables = [row[0] for row in cursor.fetchall()]
|
fts_tables = [row[0] for row in cursor.fetchall()]
|
||||||
has_dual_fts = len(fts_tables) == 2
|
has_dual_fts = len(fts_tables) == 2
|
||||||
|
|||||||
Reference in New Issue
Block a user