Add comprehensive tests for ast-grep and tree-sitter relationship extraction

- Introduced test suite for AstGrepPythonProcessor covering pattern definitions, parsing, and relationship extraction.
- Added comparison tests between tree-sitter and ast-grep for consistency in relationship extraction.
- Implemented tests for ast-grep binding module to verify functionality and availability.
- Ensured tests cover various scenarios including inheritance, function calls, and imports.
This commit is contained in:
catlog22
2026-02-15 21:14:14 +08:00
parent 126a357aa2
commit 48a6a1f2aa
56 changed files with 10622 additions and 374 deletions

View File

@@ -35,6 +35,10 @@ interface CommandOptions {
delete?: boolean;
merge?: string;
dedup?: boolean;
unified?: boolean;
topK?: string;
minScore?: string;
category?: string;
}
/**
@@ -844,6 +848,114 @@ async function jobsAction(options: CommandOptions): Promise<void> {
}
}
/**
* Unified vector+FTS search across all memory stores
*/
async function unifiedSearchAction(keyword: string, options: CommandOptions): Promise<void> {
if (!keyword || keyword.trim() === '') {
console.error(chalk.red('Error: Query is required'));
console.error(chalk.gray('Usage: ccw core-memory search --unified <query> [--topK 20] [--minScore 0] [--category <cat>]'));
process.exit(1);
}
try {
const { UnifiedMemoryService } = await import('../core/unified-memory-service.js');
const service = new UnifiedMemoryService(getProjectPath());
const topK = parseInt(options.topK || '20', 10);
const minScore = parseFloat(options.minScore || '0');
const category = options.category || undefined;
console.log(chalk.cyan(`\n Unified search: "${keyword}" (topK=${topK}, minScore=${minScore})\n`));
const results = await service.search(keyword, {
limit: topK,
minScore,
category: category as any,
});
if (results.length === 0) {
console.log(chalk.yellow(' No results found.\n'));
return;
}
if (options.json) {
console.log(JSON.stringify({ query: keyword, total: results.length, results }, null, 2));
return;
}
console.log(chalk.gray(' -----------------------------------------------------------------------'));
for (const result of results) {
const sources: string[] = [];
if (result.rank_sources.vector_rank) sources.push(`vec:#${result.rank_sources.vector_rank}`);
if (result.rank_sources.fts_rank) sources.push(`fts:#${result.rank_sources.fts_rank}`);
if (result.rank_sources.heat_score) sources.push(`heat:${result.rank_sources.heat_score.toFixed(1)}`);
const snippet = result.content.substring(0, 120).replace(/\n/g, ' ');
console.log(
chalk.cyan(` ${result.source_id}`) +
chalk.gray(` [${result.source_type}/${result.category}]`) +
chalk.white(` score=${result.score.toFixed(4)}`)
);
console.log(chalk.gray(` Sources: ${sources.join(' | ')}`));
console.log(chalk.white(` ${snippet}${result.content.length > 120 ? '...' : ''}`));
console.log(chalk.gray(' -----------------------------------------------------------------------'));
}
console.log(chalk.gray(`\n Total: ${results.length}\n`));
} catch (error) {
console.error(chalk.red(`Error: ${(error as Error).message}`));
process.exit(1);
}
}
/**
* Rebuild the unified HNSW vector index from scratch
*/
async function reindexAction(options: CommandOptions): Promise<void> {
try {
const { UnifiedVectorIndex, isUnifiedEmbedderAvailable } = await import('../core/unified-vector-index.js');
if (!isUnifiedEmbedderAvailable()) {
console.error(chalk.red('Error: Unified embedder is not available.'));
console.error(chalk.gray('Ensure Python venv and embedder script are set up.'));
process.exit(1);
}
const index = new UnifiedVectorIndex(getProjectPath());
console.log(chalk.cyan('\n Rebuilding unified vector index...\n'));
const result = await index.reindexAll();
if (!result.success) {
console.error(chalk.red(` Reindex failed: ${result.error}\n`));
process.exit(1);
}
if (options.json) {
console.log(JSON.stringify(result, null, 2));
return;
}
console.log(chalk.green(' Reindex complete.'));
if (result.hnsw_count !== undefined) {
console.log(chalk.white(` HNSW vectors: ${result.hnsw_count}`));
}
if (result.elapsed_time !== undefined) {
console.log(chalk.white(` Elapsed: ${result.elapsed_time.toFixed(2)}s`));
}
console.log();
} catch (error) {
console.error(chalk.red(`Error: ${(error as Error).message}`));
process.exit(1);
}
}
/**
* Core Memory command entry point
*/
@@ -889,7 +1001,11 @@ export async function coreMemoryCommand(
break;
case 'search':
await searchAction(textArg, options);
if (options.unified) {
await unifiedSearchAction(textArg, options);
} else {
await searchAction(textArg, options);
}
break;
case 'projects':
@@ -921,6 +1037,10 @@ export async function coreMemoryCommand(
await jobsAction(options);
break;
case 'reindex':
await reindexAction(options);
break;
default:
console.log(chalk.bold.cyan('\n CCW Core Memory\n'));
console.log(' Manage core memory entries and session clusters.\n');
@@ -945,12 +1065,14 @@ export async function coreMemoryCommand(
console.log(chalk.white(' context ') + chalk.gray('Get progressive index'));
console.log(chalk.white(' load-cluster <id> ') + chalk.gray('Load cluster context'));
console.log(chalk.white(' search <keyword> ') + chalk.gray('Search sessions'));
console.log(chalk.white(' search --unified <query> ') + chalk.gray('Unified vector+FTS search'));
console.log();
console.log(chalk.bold(' Memory V2 Pipeline:'));
console.log(chalk.white(' extract ') + chalk.gray('Run batch memory extraction'));
console.log(chalk.white(' extract-status ') + chalk.gray('Show extraction pipeline status'));
console.log(chalk.white(' consolidate ') + chalk.gray('Run memory consolidation'));
console.log(chalk.white(' jobs ') + chalk.gray('List all pipeline jobs'));
console.log(chalk.white(' reindex ') + chalk.gray('Rebuild unified vector index'));
console.log();
console.log(chalk.bold(' Options:'));
console.log(chalk.gray(' --id <id> Memory ID (for export/summary)'));