feat: Add CLAUDE.md freshness tracking and update reminders

- Add SQLite table and CRUD methods for tracking update history
- Create freshness calculation service based on git file changes
- Add API endpoints for freshness data, marking updates, and history
- Display freshness badges in file tree (green/yellow/red indicators)
- Show freshness gauge and details in metadata panel
- Auto-mark files as updated after CLI sync
- Add English and Chinese i18n translations

Freshness algorithm: 100 - min((changedFilesCount / 20) * 100, 100)

🤖 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 16:14:46 +08:00
parent 4a3ff82200
commit b27d8a9570
18 changed files with 2260 additions and 18 deletions

View File

@@ -51,6 +51,7 @@ const ParamsSchema = z.object({
format: z.enum(['json', 'text', 'pretty']).default('json'),
languages: z.array(z.string()).optional(),
limit: z.number().default(20),
enrich: z.boolean().default(false),
// Additional fields for internal functions
file: z.string().optional(),
key: z.string().optional(),
@@ -516,7 +517,7 @@ async function initIndex(params: Params): Promise<ExecuteResult> {
* @returns Execution result
*/
async function searchCode(params: Params): Promise<ExecuteResult> {
const { query, path = '.', limit = 20, mode = 'auto' } = params;
const { query, path = '.', limit = 20, mode = 'auto', enrich = false } = params;
if (!query) {
return { success: false, error: 'Query is required for search action' };
@@ -537,6 +538,10 @@ async function searchCode(params: Params): Promise<ExecuteResult> {
const cliMode = modeMap[mode] || 'auto';
const args = ['search', query, '--limit', limit.toString(), '--mode', cliMode, '--json'];
if (enrich) {
args.push('--enrich');
}
const result = await executeCodexLens(args, { cwd: path });
if (result.success && result.output) {
@@ -557,7 +562,7 @@ async function searchCode(params: Params): Promise<ExecuteResult> {
* @returns Execution result
*/
async function searchFiles(params: Params): Promise<ExecuteResult> {
const { query, path = '.', limit = 20, mode = 'auto' } = params;
const { query, path = '.', limit = 20, mode = 'auto', enrich = false } = params;
if (!query) {
return { success: false, error: 'Query is required for search_files action' };
@@ -578,6 +583,10 @@ async function searchFiles(params: Params): Promise<ExecuteResult> {
const cliMode = modeMap[mode] || 'auto';
const args = ['search', query, '--files-only', '--limit', limit.toString(), '--mode', cliMode, '--json'];
if (enrich) {
args.push('--enrich');
}
const result = await executeCodexLens(args, { cwd: path });
if (result.success && result.output) {
@@ -764,6 +773,9 @@ Usage:
codex_lens(action="search", query="func", mode="hybrid") # Force hybrid search
codex_lens(action="search_files", query="x") # Search, return paths only
Graph Enrichment:
codex_lens(action="search", query="func", enrich=true) # Enrich results with code relationships
Search Modes:
- auto: Auto-detect (hybrid if embeddings exist, exact otherwise) [default]
- exact/text: Exact FTS for code identifiers
@@ -820,6 +832,11 @@ Note: For advanced operations (config, status, clean), use CLI directly: codexle
description: 'Maximum number of search results (for search and search_files actions)',
default: 20,
},
enrich: {
type: 'boolean',
description: 'Enrich search results with code graph relationships (calls, imports)',
default: false,
},
},
required: ['action'],
},

View File

@@ -36,6 +36,7 @@ const ParamsSchema = z.object({
includeHidden: z.boolean().default(false),
languages: z.array(z.string()).optional(),
limit: z.number().default(10),
enrich: z.boolean().default(false),
});
type Params = z.infer<typeof ParamsSchema>;
@@ -59,11 +60,21 @@ interface ExactMatch {
content: string;
}
interface RelationshipInfo {
type: string; // 'calls', 'imports', 'called_by', 'imported_by'
direction: 'outgoing' | 'incoming';
target?: string; // Target symbol name (for outgoing)
source?: string; // Source symbol name (for incoming)
file: string; // File path
line?: number; // Line number
}
interface SemanticMatch {
file: string;
score: number;
content: string;
symbol: string | null;
relationships?: RelationshipInfo[];
}
interface GraphMatch {
@@ -635,7 +646,7 @@ async function executeRipgrepMode(params: Params): Promise<SearchResult> {
* Requires index
*/
async function executeCodexLensExactMode(params: Params): Promise<SearchResult> {
const { query, path = '.', maxResults = 10 } = params;
const { query, path = '.', maxResults = 10, enrich = false } = params;
if (!query) {
return {
@@ -657,6 +668,9 @@ async function executeCodexLensExactMode(params: Params): Promise<SearchResult>
const indexStatus = await checkIndexStatus(path);
const args = ['search', query, '--limit', maxResults.toString(), '--mode', 'exact', '--json'];
if (enrich) {
args.push('--enrich');
}
const result = await executeCodexLens(args, { cwd: path });
if (!result.success) {
@@ -707,7 +721,7 @@ async function executeCodexLensExactMode(params: Params): Promise<SearchResult>
* Requires index with embeddings
*/
async function executeHybridMode(params: Params): Promise<SearchResult> {
const { query, path = '.', maxResults = 10 } = params;
const { query, path = '.', maxResults = 10, enrich = false } = params;
if (!query) {
return {
@@ -729,6 +743,9 @@ async function executeHybridMode(params: Params): Promise<SearchResult> {
const indexStatus = await checkIndexStatus(path);
const args = ['search', query, '--limit', maxResults.toString(), '--mode', 'hybrid', '--json'];
if (enrich) {
args.push('--enrich');
}
const result = await executeCodexLens(args, { cwd: path });
if (!result.success) {
@@ -958,6 +975,9 @@ export const schema: ToolSchema = {
smart_search(action="init") # Create FTS index for current directory
smart_search(action="status") # Check index and embedding status
**Graph Enrichment:**
smart_search(query="func", enrich=true) # Enrich results with code relationships (calls, imports, called_by, imported_by)
**Modes:** auto (intelligent routing), hybrid (semantic, needs index), exact (FTS), ripgrep (fast, no index), priority (fallback: hybrid→exact→ripgrep)`,
inputSchema: {
type: 'object',
@@ -1021,6 +1041,11 @@ export const schema: ToolSchema = {
items: { type: 'string' },
description: 'Languages to index (for init action). Example: ["javascript", "typescript"]',
},
enrich: {
type: 'boolean',
description: 'Enrich search results with code graph relationships (calls, imports, called_by, imported_by).',
default: false,
},
},
required: [],
},