fix: Align semantic status check with CodexLens checkSemanticStatus

- Use checkSemanticStatus() from codex-lens.ts instead of isEmbedderAvailable()
- Returns accurate detection of fastembed installation status
- Fix install guide link to navigate to CLI Manager and open CodexLens config
- Update i18n: "Install Guide" -> "Go to Settings" (EN/ZH)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
catlog22
2025-12-20 13:51:50 +08:00
parent bfbab44756
commit 4a3ff82200
5 changed files with 70 additions and 16 deletions

View File

@@ -110,7 +110,7 @@ ccw install -m Path -p ./my-project
ccw view
# Start dashboard server on custom port
ccw serve -p 8080
ccw serve --port 8080
# Upgrade all installations
ccw upgrade -a

View File

@@ -102,7 +102,7 @@ ccw install -m Path -p ./my-project
ccw view
# 在自定义端口启动仪表板服务器
ccw serve -p 8080
ccw serve --port 8080
# 升级所有安装
ccw upgrade -a

View File

@@ -2,7 +2,8 @@ import * as http from 'http';
import { URL } from 'url';
import { getCoreMemoryStore } from '../core-memory-store.js';
import type { CoreMemory, SessionCluster, ClusterMember, ClusterRelation } from '../core-memory-store.js';
import { getEmbeddingStatus, generateEmbeddings, isEmbedderAvailable } from '../memory-embedder-bridge.js';
import { getEmbeddingStatus, generateEmbeddings } from '../memory-embedder-bridge.js';
import { checkSemanticStatus } from '../../tools/codex-lens.js';
import { StoragePaths } from '../../config/storage-paths.js';
import { join } from 'path';
@@ -309,9 +310,19 @@ export async function handleCoreMemoryRoutes(ctx: RouteContext): Promise<boolean
const projectPath = url.searchParams.get('path') || initialPath;
try {
if (!isEmbedderAvailable()) {
// Check semantic status using CodexLens's check (same as status page)
const semanticStatus = await checkSemanticStatus();
if (!semanticStatus.available) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(null));
res.end(JSON.stringify({
available: false,
total_chunks: 0,
embedded_chunks: 0,
pending_chunks: 0,
by_type: {},
error: semanticStatus.error
}));
return true;
}
@@ -320,10 +331,18 @@ export async function handleCoreMemoryRoutes(ctx: RouteContext): Promise<boolean
const status = await getEmbeddingStatus(dbPath);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(status));
res.end(JSON.stringify({ ...status, available: true }));
} catch (error: unknown) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: (error as Error).message }));
// Return status with available=true even on error (embedder exists but query failed)
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
available: true,
total_chunks: 0,
embedded_chunks: 0,
pending_chunks: 0,
by_type: {},
error: (error as Error).message
}));
}
return true;
}
@@ -335,8 +354,10 @@ export async function handleCoreMemoryRoutes(ctx: RouteContext): Promise<boolean
const basePath = projectPath || initialPath;
try {
if (!isEmbedderAvailable()) {
return { error: 'Embedder not available. Install CodexLens first.', status: 503 };
// Check semantic status using CodexLens's check
const semanticStatus = await checkSemanticStatus();
if (!semanticStatus.available) {
return { error: semanticStatus.error || 'Semantic search not available. Install it from CLI > CodexLens > Semantic page.', status: 503 };
}
const paths = StoragePaths.project(basePath);

View File

@@ -1281,8 +1281,8 @@ const i18n = {
'coreMemory.clusteringInProgress': 'Clustering in progress...',
'coreMemory.clusteringComplete': 'Created {created} clusters with {sessions} sessions',
'coreMemory.clusteringError': 'Auto-clustering failed',
'coreMemory.embeddingNotAvailable': 'Vector model not installed. Install to improve clustering accuracy.',
'coreMemory.installGuide': 'Install Guide',
'coreMemory.embeddingNotAvailable': 'Semantic search not installed. Install to improve clustering accuracy.',
'coreMemory.installGuide': 'Go to Settings',
'coreMemory.embeddingProgress': 'Embeddings: {pct}% ({pending} pending)',
'coreMemory.generateEmbeddings': 'Generate',
'coreMemory.noChunksYet': 'No memories chunked yet. Run "ccw memory embed" to enable semantic clustering.',
@@ -2602,8 +2602,8 @@ const i18n = {
'coreMemory.clusteringInProgress': '聚类进行中...',
'coreMemory.clusteringComplete': '创建了 {created} 个聚类,包含 {sessions} 个 session',
'coreMemory.clusteringError': '自动聚类失败',
'coreMemory.embeddingNotAvailable': '向量模型未安装。安装后可提升聚类准确度。',
'coreMemory.installGuide': '安装指南',
'coreMemory.embeddingNotAvailable': '语义搜索未安装。安装后可提升聚类准确度。',
'coreMemory.installGuide': '前往设置',
'coreMemory.embeddingProgress': '嵌入进度: {pct}% (待处理: {pending})',
'coreMemory.generateEmbeddings': '生成',
'coreMemory.noChunksYet': '暂无记忆分块。运行 "ccw memory embed" 启用语义聚类。',

View File

@@ -45,20 +45,29 @@ async function loadClusters() {
* Render embedding status hint
*/
function renderEmbeddingHint() {
// No status data - API call failed
if (!embeddingStatus) {
return '';
}
// Embedder not available - show install hint linking to status page
if (embeddingStatus.available === false) {
return `
<div class="embedding-hint warning">
<i data-lucide="alert-triangle"></i>
<span>${t('coreMemory.embeddingNotAvailable')}</span>
<a href="#" onclick="window.open('https://github.com/anthropics/claude-code', '_blank'); return false;" class="hint-link">
<a href="#" onclick="switchToSemanticStatus(); return false;" class="hint-link">
${t('coreMemory.installGuide')}
</a>
</div>
`;
}
// Has pending chunks - show progress
if (embeddingStatus.pending_chunks > 0) {
const pct = Math.round((embeddingStatus.embedded_chunks / embeddingStatus.total_chunks) * 100);
const pct = embeddingStatus.total_chunks > 0
? Math.round((embeddingStatus.embedded_chunks / embeddingStatus.total_chunks) * 100)
: 0;
return `
<div class="embedding-hint info">
<i data-lucide="cpu"></i>
@@ -70,18 +79,42 @@ function renderEmbeddingHint() {
`;
}
// No chunks yet - show hint to run embed
if (embeddingStatus.total_chunks === 0) {
return `
<div class="embedding-hint info">
<i data-lucide="info"></i>
<span>${t('coreMemory.noChunksYet')}</span>
<button class="btn btn-xs" onclick="triggerEmbedding()">
${t('coreMemory.generateEmbeddings')}
</button>
</div>
`;
}
// All embedded - no hint needed
return '';
}
/**
* Switch to CLI Manager and show semantic install hint
*/
function switchToSemanticStatus() {
// Navigate to CLI Manager view (which contains the Semantic Search section)
const navItem = document.querySelector('[data-view="cli-manager"]');
if (navItem) {
navItem.click();
// Open CodexLens config modal after navigation
setTimeout(() => {
if (typeof openCodexLensConfig === 'function') {
openCodexLensConfig();
}
}, 300);
} else {
showNotification(t('coreMemory.goToSemanticStatus'), 'info');
}
}
/**
* Trigger embedding generation
*/