claude-flow-novice 2.18.39 → 2.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/SHARED_PROTOCOL.md +25 -1
- package/.claude/agents/cfn-dev-team/dev-ops/fly-io-specialist.md +418 -0
- package/.claude/agents/cfn-dev-team/developers/database/mem0-specialist.md +579 -0
- package/.claude/agents/cfn-dev-team/developers/database/memgraph-specialist.md +722 -0
- package/.claude/agents/cfn-dev-team/documentation/pseudocode.md +1 -1
- package/.claude/agents/cfn-dev-team/documentation/specification-agent.md +1 -1
- package/.claude/agents/cfn-dev-team/testers/tester.md +35 -0
- package/.claude/commands/{cfn-ruvector → cfn-codesearch}/cfn-codebase-reindex.md +4 -4
- package/.claude/commands/{cfn-ruvector → cfn-codesearch}/cfn-codebase-search.md +7 -7
- package/.claude/commands/{cfn-ruvector → cfn-codesearch}/cfn-detect-stale-docs.md +3 -3
- package/.claude/commands/{cfn-ruvector-search.md → cfn-codesearch-search.md} +7 -7
- package/.claude/commands/cfn-fix-errors.md +1 -1
- package/.claude/commands/cfn-loop-task.md +291 -291
- package/.claude/skills/{bulk-add-ruvector-instructions.sh → bulk-add-codesearch-instructions.sh} +12 -12
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/AGENT_INTEGRATION_PATTERNS.md +20 -20
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/AGENT_LIFECYCLE_INTEGRATION.md +11 -11
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/Cargo.toml +9 -4
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/EXTRACTION_EXAMPLES.md +6 -6
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/IMMEDIATE_ACTION_REQUIRED.md +9 -9
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/README.md +7 -7
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/SCHEMA_V2_IMPLEMENTATION.md +1 -1
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/SECURITY_IMPLEMENTATION.md +3 -3
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/SECURITY_TESTING_COMPLETION.md +4 -4
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/SKILL.md +31 -31
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/analyze-agent-failures.sh +11 -11
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/cfn-integration.sh +27 -27
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/index-code.sh +11 -11
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/index_all.sh +2 -2
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/ingest-agent-transcript.sh +10 -10
- package/.claude/skills/{cfn-local-ruvector-accelerator/init-local-ruvector.sh → cfn-codesearch/init-local-codesearch.sh} +16 -16
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/project-structure.md +10 -10
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/query-local.sh +6 -6
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/index.rs +233 -4
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/init.rs +18 -18
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/query.rs +64 -3
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/embeddings.rs +19 -5
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/lib.rs +6 -4
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/main.rs +35 -25
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/paths.rs +19 -13
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/query_v2.rs +3 -4
- package/.claude/skills/cfn-codesearch/src/store_pgvector.rs +301 -0
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/store_v2.rs +12 -1
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/test-agent-lifecycle-integration.sh +6 -6
- package/.claude/skills/{cfn-local-ruvector-accelerator/test-local-ruvector.sh → cfn-codesearch/test-local-codesearch.sh} +7 -7
- package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/test_query_api.sh +2 -2
- package/.claude/skills/cfn-epic-creator/SKILL.md +110 -2
- package/.claude/skills/cfn-epic-creator/invoke.sh +120 -1
- package/.claude/skills/cfn-mdap-context-injection/SKILL.md +2 -2
- package/.claude/skills/cfn-mdap-context-injection/inject.sh +21 -21
- package/package.json +11 -11
- package/readme/feature-status.md +405 -0
- package/.claude/agents/docker-ts-fixer.md +0 -65
- /package/.claude/agents/cfn-dev-team/{testing → testers}/test-validation-agent.md +0 -0
- /package/.claude/{agents/cfn-dev-team/utility → cfn-extras/agents}/context-curator.md +0 -0
- /package/.claude/{agents/custom → cfn-extras/agents/custom-agents}/cfn-docker-expert.md +0 -0
- /package/.claude/{agents/custom → cfn-extras/agents/custom-agents}/cfn-loops-cli-expert.md +0 -0
- /package/.claude/{agents/custom → cfn-extras/agents/custom-agents}/cfn-redis-operations.md +0 -0
- /package/.claude/{agents/custom → cfn-extras/agents/custom-agents}/cfn-system-expert.md +0 -0
- /package/.claude/{agents/csuite → cfn-extras/agents/custom-agents}/cto-agent.md +0 -0
- /package/.claude/{agents/custom → cfn-extras/agents/custom-agents}/trigger-dev-expert.md +0 -0
- /package/.claude/{agents/cfn-dev-team/coordinators → cfn-extras/agents/deprecated-coordinators}/consensus-builder.md +0 -0
- /package/.claude/{agents/cfn-dev-team/coordinators → cfn-extras/agents/deprecated-coordinators}/multi-sprint-coordinator.md +0 -0
- /package/.claude/{agents → cfn-extras/agents/docker-team}/docker-coordinators/cfn-docker-v3-coordinator.md +0 -0
- /package/.claude/{agents/cfn-dev-team/dev-ops → cfn-extras/agents}/kubernetes-specialist.md +0 -0
- /package/.claude/{agents/cfn-dev-team/architecture → cfn-extras/agents}/planner.md +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/.claude/hooks/SessionStart-cfn-build-ruvector.sh +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/Cargo.toml.backup +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/SECURITY_FINDINGS_SUMMARY.txt +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/docs/EXECUTIVE_SUMMARY.txt +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/docs/PHASE_4_QUERY_API.md +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/docs/RUST_AST_EXTRACTOR_IMPLEMENTATION.md +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/docs/TRANSACTION_MANAGEMENT.md +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/docs/VALIDATION_FINDINGS.txt +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/embeddings_manager.py.backup +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/epic-ast-indexer.json +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/index/index.bin +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/index/metadata.json +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/index-code.sh.backup +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/query-agent-patterns.sh +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/requirements.txt +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/search_engine_v2.py.backup +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/sqlite_store.py.backup +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/cleanup.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/export.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/find.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/index_ast.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/index_modified.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/migration.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/mod.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/refs.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/reset.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/stats.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/extractors/mod.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/extractors/rust.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/extractors/rust_placeholder.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/extractors/text_fallback.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/extractors/typescript.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/extractors/typescript_full.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/main.rs.backup +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/migration.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/migration_backup.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/migration_tx.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/migration_v2.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/path_validator.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/query_api.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/schema_v2.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/search_engine.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/security_tests.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/sqlite_store.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/store_v2_backup.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/store_v2_fixed.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/store_v2_tx.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/test_schema.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/transaction_tests.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/test_ast_indexing.rs +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/test_schema.sql +0 -0
- /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/test_schema_v2.sql +0 -0
|
@@ -63,7 +63,8 @@ use crate::extractors::text_fallback::TextFallbackExtractor;
|
|
|
63
63
|
use crate::store_v2::{StoreV2, Entity as StoreEntity, Reference as StoreReference, TypeUsage};
|
|
64
64
|
use crate::schema_v2::{EntityKind, RefKind, Visibility};
|
|
65
65
|
use crate::path_validator;
|
|
66
|
-
use
|
|
66
|
+
use codesearch::store_pgvector::PgvectorStore;
|
|
67
|
+
use codesearch::paths::{get_codesearch_dir, get_database_path};
|
|
67
68
|
// V1 index is deprecated - all operations use V2 (index_v2.db)
|
|
68
69
|
|
|
69
70
|
/// Directories to exclude from indexing.
|
|
@@ -246,11 +247,82 @@ pub struct IndexCommand {
|
|
|
246
247
|
search_engine: SearchEngine,
|
|
247
248
|
store: SqliteStore,
|
|
248
249
|
store_v2: StoreV2,
|
|
250
|
+
pgvector_store: Option<PgvectorStore>,
|
|
251
|
+
tokio_runtime: Option<tokio::runtime::Runtime>,
|
|
249
252
|
rust_extractor: RustExtractor,
|
|
250
253
|
typescript_extractor: TypeScriptExtractor,
|
|
251
254
|
text_fallback_extractor: TextFallbackExtractor,
|
|
252
255
|
}
|
|
253
256
|
|
|
257
|
+
/// Extract doc comments from a signature string.
|
|
258
|
+
/// Supports Rust-style (///, //!) and JSDoc-style (/** */) comments.
|
|
259
|
+
fn extract_doc_comment(signature: &str) -> Option<String> {
|
|
260
|
+
let lines: Vec<&str> = signature.lines().collect();
|
|
261
|
+
let mut doc_lines: Vec<String> = Vec::new();
|
|
262
|
+
let mut in_jsdoc = false;
|
|
263
|
+
|
|
264
|
+
for line in &lines {
|
|
265
|
+
let trimmed = line.trim();
|
|
266
|
+
|
|
267
|
+
// Handle JSDoc-style comments (/** ... */)
|
|
268
|
+
if trimmed.starts_with("/**") {
|
|
269
|
+
let after_open = trimmed.strip_prefix("/**").unwrap_or("").trim();
|
|
270
|
+
// Check if it's a single-line JSDoc (/** content */)
|
|
271
|
+
if after_open.ends_with("*/") {
|
|
272
|
+
let content = after_open.strip_suffix("*/").unwrap_or(after_open).trim();
|
|
273
|
+
if !content.is_empty() {
|
|
274
|
+
doc_lines.push(content.to_string());
|
|
275
|
+
}
|
|
276
|
+
// Single-line, don't enter jsdoc mode
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
// Multi-line JSDoc
|
|
280
|
+
in_jsdoc = true;
|
|
281
|
+
if !after_open.is_empty() {
|
|
282
|
+
doc_lines.push(after_open.to_string());
|
|
283
|
+
}
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if in_jsdoc {
|
|
288
|
+
if trimmed.contains("*/") {
|
|
289
|
+
in_jsdoc = false;
|
|
290
|
+
// Extract content before */
|
|
291
|
+
let content = trimmed.strip_suffix("*/").unwrap_or(trimmed)
|
|
292
|
+
.trim_start_matches('*').trim();
|
|
293
|
+
if !content.is_empty() {
|
|
294
|
+
doc_lines.push(content.to_string());
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
// Middle lines of JSDoc (usually start with *)
|
|
298
|
+
let content = trimmed.trim_start_matches('*').trim();
|
|
299
|
+
if !content.is_empty() {
|
|
300
|
+
doc_lines.push(content.to_string());
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Handle Rust-style doc comments (/// or //!)
|
|
307
|
+
if trimmed.starts_with("///") {
|
|
308
|
+
let content = trimmed.strip_prefix("///").unwrap_or("").trim();
|
|
309
|
+
doc_lines.push(content.to_string());
|
|
310
|
+
} else if trimmed.starts_with("//!") {
|
|
311
|
+
let content = trimmed.strip_prefix("//!").unwrap_or("").trim();
|
|
312
|
+
doc_lines.push(content.to_string());
|
|
313
|
+
} else if !doc_lines.is_empty() {
|
|
314
|
+
// Stop when we hit a non-doc-comment line after collecting some
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if doc_lines.is_empty() {
|
|
320
|
+
None
|
|
321
|
+
} else {
|
|
322
|
+
Some(doc_lines.join("\n"))
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
254
326
|
impl IndexCommand {
|
|
255
327
|
pub fn new(
|
|
256
328
|
project_dir: &Path,
|
|
@@ -258,13 +330,33 @@ impl IndexCommand {
|
|
|
258
330
|
file_types: Vec<String>,
|
|
259
331
|
patterns: Option<Vec<String>>,
|
|
260
332
|
force: bool,
|
|
333
|
+
pg_url: Option<&str>,
|
|
261
334
|
) -> Result<Self> {
|
|
262
|
-
let index_path =
|
|
335
|
+
let index_path = get_codesearch_dir()?;
|
|
263
336
|
let embeddings_manager = EmbeddingsManager::new(&index_path)?;
|
|
264
337
|
let search_engine = SearchEngine::new(&index_path)?;
|
|
265
338
|
let store = SqliteStore::new(&index_path.join("index.db"))?;
|
|
266
339
|
let store_v2 = StoreV2::new(&get_database_path()?)?;
|
|
267
340
|
|
|
341
|
+
// Initialize pgvector store and runtime if connection URL provided
|
|
342
|
+
let (pgvector_store, tokio_runtime) = if let Some(url) = pg_url {
|
|
343
|
+
info!("Initializing pgvector store with URL: {}", url);
|
|
344
|
+
let rt = tokio::runtime::Runtime::new()?;
|
|
345
|
+
match rt.block_on(PgvectorStore::new(url)) {
|
|
346
|
+
Ok(store) => {
|
|
347
|
+
info!("Successfully connected to pgvector");
|
|
348
|
+
(Some(store), Some(rt))
|
|
349
|
+
}
|
|
350
|
+
Err(e) => {
|
|
351
|
+
warn!("Failed to connect to pgvector: {}. Using SQLite only.", e);
|
|
352
|
+
(None, None)
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
} else {
|
|
356
|
+
info!("No pg_url provided, using SQLite only");
|
|
357
|
+
(None, None)
|
|
358
|
+
};
|
|
359
|
+
|
|
268
360
|
let source_path = if path.as_os_str().is_empty() || path == Path::new(".") {
|
|
269
361
|
project_dir.to_path_buf()
|
|
270
362
|
} else {
|
|
@@ -282,6 +374,8 @@ impl IndexCommand {
|
|
|
282
374
|
search_engine,
|
|
283
375
|
store,
|
|
284
376
|
store_v2,
|
|
377
|
+
pgvector_store,
|
|
378
|
+
tokio_runtime,
|
|
285
379
|
rust_extractor: RustExtractor::new()?,
|
|
286
380
|
typescript_extractor: TypeScriptExtractor::new()?,
|
|
287
381
|
text_fallback_extractor: TextFallbackExtractor::new()?,
|
|
@@ -476,11 +570,75 @@ impl IndexCommand {
|
|
|
476
570
|
|
|
477
571
|
let embeddings = self.generate_entity_embeddings(&extraction_result.entities)?;
|
|
478
572
|
|
|
479
|
-
// Store embeddings in entity_embeddings table
|
|
573
|
+
// Store embeddings in SQLite entity_embeddings table (metadata)
|
|
480
574
|
for (entity_id, embedding) in entity_ids.iter().zip(embeddings.iter()) {
|
|
481
575
|
self.store_v2.store_embedding(*entity_id, embedding, "text-embedding-3-small")?;
|
|
482
576
|
}
|
|
483
577
|
|
|
578
|
+
// Store embeddings in pgvector if available
|
|
579
|
+
if let Some(ref pgvector_store) = self.pgvector_store {
|
|
580
|
+
if let Some(ref rt) = self.tokio_runtime {
|
|
581
|
+
let project_root_str = self.project_dir.to_string_lossy().to_string();
|
|
582
|
+
|
|
583
|
+
// Build entities the same way store_entities does
|
|
584
|
+
for (i, entity) in extraction_result.entities.iter().enumerate() {
|
|
585
|
+
let store_entity = codesearch::store_v2::Entity {
|
|
586
|
+
id: entity_ids[i],
|
|
587
|
+
kind: match entity.kind {
|
|
588
|
+
crate::extractors::EntityKind::Function => codesearch::schema_v2::EntityKind::Function,
|
|
589
|
+
crate::extractors::EntityKind::Method => codesearch::schema_v2::EntityKind::Method,
|
|
590
|
+
crate::extractors::EntityKind::Constructor => codesearch::schema_v2::EntityKind::Constructor,
|
|
591
|
+
crate::extractors::EntityKind::Getter => codesearch::schema_v2::EntityKind::Getter,
|
|
592
|
+
crate::extractors::EntityKind::Setter => codesearch::schema_v2::EntityKind::Setter,
|
|
593
|
+
crate::extractors::EntityKind::Class => codesearch::schema_v2::EntityKind::Class,
|
|
594
|
+
crate::extractors::EntityKind::Interface => codesearch::schema_v2::EntityKind::Interface,
|
|
595
|
+
crate::extractors::EntityKind::Struct => codesearch::schema_v2::EntityKind::Struct,
|
|
596
|
+
crate::extractors::EntityKind::Enum => codesearch::schema_v2::EntityKind::Enum,
|
|
597
|
+
crate::extractors::EntityKind::Trait => codesearch::schema_v2::EntityKind::Trait,
|
|
598
|
+
crate::extractors::EntityKind::TypeAlias => codesearch::schema_v2::EntityKind::TypeAlias,
|
|
599
|
+
crate::extractors::EntityKind::Module => codesearch::schema_v2::EntityKind::Module,
|
|
600
|
+
crate::extractors::EntityKind::Namespace => codesearch::schema_v2::EntityKind::Namespace,
|
|
601
|
+
crate::extractors::EntityKind::Variable => codesearch::schema_v2::EntityKind::Variable,
|
|
602
|
+
crate::extractors::EntityKind::Constant => codesearch::schema_v2::EntityKind::Constant,
|
|
603
|
+
crate::extractors::EntityKind::Parameter => codesearch::schema_v2::EntityKind::Parameter,
|
|
604
|
+
crate::extractors::EntityKind::Import => codesearch::schema_v2::EntityKind::Import,
|
|
605
|
+
},
|
|
606
|
+
name: entity.name.clone(),
|
|
607
|
+
signature: Some(entity.signature.clone()),
|
|
608
|
+
visibility: match entity.visibility {
|
|
609
|
+
crate::extractors::Visibility::Public => codesearch::schema_v2::Visibility::Public,
|
|
610
|
+
crate::extractors::Visibility::Private => codesearch::schema_v2::Visibility::Private,
|
|
611
|
+
crate::extractors::Visibility::Protected => codesearch::schema_v2::Visibility::Protected,
|
|
612
|
+
crate::extractors::Visibility::Internal => codesearch::schema_v2::Visibility::Internal,
|
|
613
|
+
crate::extractors::Visibility::FilePrivate => codesearch::schema_v2::Visibility::FilePrivate,
|
|
614
|
+
},
|
|
615
|
+
parent_id: None,
|
|
616
|
+
file_path: file_path.to_string_lossy().to_string(),
|
|
617
|
+
line_number: entity.line as i64,
|
|
618
|
+
column_number: Some(entity.column as i64),
|
|
619
|
+
doc_comment: extract_doc_comment(&entity.signature),
|
|
620
|
+
attributes: None,
|
|
621
|
+
metadata: Some(serde_json::to_string(&entity.metadata)?),
|
|
622
|
+
project_root: project_root_str.clone(),
|
|
623
|
+
created_at: chrono::Utc::now(),
|
|
624
|
+
updated_at: chrono::Utc::now(),
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
// Use the stored runtime (created once in new())
|
|
628
|
+
match rt.block_on(pgvector_store.store_entity_with_embedding(
|
|
629
|
+
&store_entity,
|
|
630
|
+
&embeddings[i],
|
|
631
|
+
&project_root_str,
|
|
632
|
+
)) {
|
|
633
|
+
Ok(_) => debug!("Stored entity in pgvector: {}", store_entity.name),
|
|
634
|
+
Err(e) => warn!("Failed to store entity in pgvector: {:?}", e),
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
} else {
|
|
638
|
+
warn!("pgvector_store available but tokio_runtime is None - this is a bug");
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
484
642
|
{
|
|
485
643
|
let mut s = stats.write().unwrap();
|
|
486
644
|
s.files_processed += 1;
|
|
@@ -555,7 +713,7 @@ impl IndexCommand {
|
|
|
555
713
|
file_path: file_path.to_string_lossy().to_string(),
|
|
556
714
|
line_number: entity.line as i64,
|
|
557
715
|
column_number: Some(entity.column as i64),
|
|
558
|
-
doc_comment:
|
|
716
|
+
doc_comment: extract_doc_comment(&entity.signature),
|
|
559
717
|
attributes: None,
|
|
560
718
|
metadata: Some(serde_json::to_string(&entity.metadata)?),
|
|
561
719
|
project_root: project_root_str.to_string(),
|
|
@@ -721,4 +879,75 @@ impl IndexCommand {
|
|
|
721
879
|
|
|
722
880
|
Ok(format!("{:x}", hasher.finalize()))
|
|
723
881
|
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
#[cfg(test)]
|
|
885
|
+
mod tests {
|
|
886
|
+
use super::*;
|
|
887
|
+
|
|
888
|
+
#[test]
|
|
889
|
+
fn test_extract_rust_doc_comment() {
|
|
890
|
+
let signature = r#"/// Create a new Rust extractor
|
|
891
|
+
pub fn new() -> Self"#;
|
|
892
|
+
let doc = extract_doc_comment(signature);
|
|
893
|
+
assert_eq!(doc, Some("Create a new Rust extractor".to_string()));
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
#[test]
|
|
897
|
+
fn test_extract_rust_multiline_doc_comment() {
|
|
898
|
+
let signature = r#"/// Create a new store
|
|
899
|
+
/// This is the main entry point
|
|
900
|
+
pub fn new() -> Self"#;
|
|
901
|
+
let doc = extract_doc_comment(signature);
|
|
902
|
+
assert_eq!(doc, Some("Create a new store\nThis is the main entry point".to_string()));
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
#[test]
|
|
906
|
+
fn test_extract_rust_inner_doc_comment() {
|
|
907
|
+
let signature = r#"//! Module documentation
|
|
908
|
+
//! This module handles storage
|
|
909
|
+
pub mod store"#;
|
|
910
|
+
let doc = extract_doc_comment(signature);
|
|
911
|
+
assert_eq!(doc, Some("Module documentation\nThis module handles storage".to_string()));
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
#[test]
|
|
915
|
+
fn test_extract_jsdoc_comment() {
|
|
916
|
+
let signature = r#"/**
|
|
917
|
+
* Create a new instance
|
|
918
|
+
* @param name The name
|
|
919
|
+
*/
|
|
920
|
+
function create(name: string)"#;
|
|
921
|
+
let doc = extract_doc_comment(signature);
|
|
922
|
+
assert_eq!(doc, Some("Create a new instance\n@param name The name".to_string()));
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
#[test]
|
|
926
|
+
fn test_extract_jsdoc_single_line() {
|
|
927
|
+
let signature = r#"/** Quick summary */
|
|
928
|
+
function test()"#;
|
|
929
|
+
let doc = extract_doc_comment(signature);
|
|
930
|
+
assert_eq!(doc, Some("Quick summary".to_string()));
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
#[test]
|
|
934
|
+
fn test_no_doc_comment() {
|
|
935
|
+
let signature = r#"pub fn new() -> Self"#;
|
|
936
|
+
let doc = extract_doc_comment(signature);
|
|
937
|
+
assert_eq!(doc, None);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
#[test]
|
|
941
|
+
fn test_empty_signature() {
|
|
942
|
+
let doc = extract_doc_comment("");
|
|
943
|
+
assert_eq!(doc, None);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
#[test]
|
|
947
|
+
fn test_regular_comment_not_extracted() {
|
|
948
|
+
let signature = r#"// This is a regular comment
|
|
949
|
+
pub fn new() -> Self"#;
|
|
950
|
+
let doc = extract_doc_comment(signature);
|
|
951
|
+
assert_eq!(doc, None);
|
|
952
|
+
}
|
|
724
953
|
}
|
|
@@ -8,7 +8,7 @@ use crate::embeddings::EmbeddingsManager;
|
|
|
8
8
|
use crate::search_engine::SearchEngine;
|
|
9
9
|
use crate::sqlite_store::SqliteStore;
|
|
10
10
|
use crate::migration_v2::MigrationV2;
|
|
11
|
-
use
|
|
11
|
+
use codesearch::paths::{get_codesearch_dir, get_database_path};
|
|
12
12
|
// V1 index is deprecated - all operations use V2 (index_v2.db)
|
|
13
13
|
|
|
14
14
|
pub struct InitCommand {
|
|
@@ -42,9 +42,9 @@ impl InitCommand {
|
|
|
42
42
|
let _ = rusqlite::Connection::open_in_memory()
|
|
43
43
|
.context("SQLite is not available")?;
|
|
44
44
|
|
|
45
|
-
let
|
|
46
|
-
if
|
|
47
|
-
info!("RuVector already initialized at: {}",
|
|
45
|
+
let codesearch_dir = get_codesearch_dir()?;
|
|
46
|
+
if codesearch_dir.exists() {
|
|
47
|
+
info!("RuVector already initialized at: {}", codesearch_dir.display());
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
info!("Environment check passed");
|
|
@@ -52,24 +52,24 @@ impl InitCommand {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
pub fn execute(&self) -> Result<()> {
|
|
55
|
-
let
|
|
55
|
+
let codesearch_dir = get_codesearch_dir()?;
|
|
56
56
|
|
|
57
57
|
// Create directory if doesn't exist (idempotent)
|
|
58
|
-
if !
|
|
59
|
-
info!("Creating centralized RuVector directory: {}",
|
|
60
|
-
fs::create_dir_all(&
|
|
58
|
+
if !codesearch_dir.exists() {
|
|
59
|
+
info!("Creating centralized RuVector directory: {}", codesearch_dir.display());
|
|
60
|
+
fs::create_dir_all(&codesearch_dir)
|
|
61
61
|
.context("Failed to create RuVector directory")?;
|
|
62
|
-
fs::create_dir_all(
|
|
62
|
+
fs::create_dir_all(codesearch_dir.join("embeddings"))
|
|
63
63
|
.context("Failed to create embeddings directory")?;
|
|
64
64
|
// V1 index directory no longer created - using centralized V2 database
|
|
65
|
-
fs::create_dir_all(
|
|
65
|
+
fs::create_dir_all(codesearch_dir.join("cache"))
|
|
66
66
|
.context("Failed to create cache directory")?;
|
|
67
67
|
} else {
|
|
68
|
-
info!("Using existing centralized RuVector directory: {}",
|
|
68
|
+
info!("Using existing centralized RuVector directory: {}", codesearch_dir.display());
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// Create configuration file if missing
|
|
72
|
-
if !
|
|
72
|
+
if !codesearch_dir.join("config.json").exists() {
|
|
73
73
|
self.create_config()?;
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -83,7 +83,7 @@ impl InitCommand {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// Initialize embeddings manager
|
|
86
|
-
if !
|
|
86
|
+
if !codesearch_dir.join("embeddings").exists() {
|
|
87
87
|
self.initialize_embeddings()?;
|
|
88
88
|
}
|
|
89
89
|
|
|
@@ -95,8 +95,8 @@ impl InitCommand {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
fn create_config(&self) -> Result<()> {
|
|
98
|
-
let
|
|
99
|
-
let config_path =
|
|
98
|
+
let codesearch_dir = get_codesearch_dir()?;
|
|
99
|
+
let config_path = codesearch_dir.join("config.json");
|
|
100
100
|
|
|
101
101
|
let config = json!({
|
|
102
102
|
"version": "1.0.0",
|
|
@@ -129,7 +129,7 @@ impl InitCommand {
|
|
|
129
129
|
|
|
130
130
|
// Initialize Schema V2 using CREATE TABLE IF NOT EXISTS (always safe)
|
|
131
131
|
let mut conn = rusqlite::Connection::open(&db_path)?;
|
|
132
|
-
|
|
132
|
+
codesearch::schema_v2::SchemaV2::initialize(&conn)?;
|
|
133
133
|
|
|
134
134
|
// Run v2 migration to add multi-project isolation
|
|
135
135
|
info!("Running database migrations...");
|
|
@@ -163,14 +163,14 @@ impl InitCommand {
|
|
|
163
163
|
let conn = rusqlite::Connection::open(&db_path)?;
|
|
164
164
|
|
|
165
165
|
// Re-run schema initialization (uses IF NOT EXISTS)
|
|
166
|
-
|
|
166
|
+
codesearch::schema_v2::SchemaV2::initialize(&conn)?;
|
|
167
167
|
|
|
168
168
|
debug!("Schema tables recreated (non-destructive)");
|
|
169
169
|
Ok(())
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
fn initialize_embeddings(&self) -> Result<()> {
|
|
173
|
-
let embeddings_dir =
|
|
173
|
+
let embeddings_dir = get_codesearch_dir()?;
|
|
174
174
|
let _ = EmbeddingsManager::new(&embeddings_dir)?;
|
|
175
175
|
|
|
176
176
|
debug!("Embeddings manager initialized");
|
|
@@ -9,6 +9,8 @@ use serde::Serialize;
|
|
|
9
9
|
use crate::query_v2::{QueryV2, SearchResult};
|
|
10
10
|
use crate::paths::get_database_path;
|
|
11
11
|
use crate::path_validator;
|
|
12
|
+
use codesearch::store_pgvector::PgvectorStore;
|
|
13
|
+
use codesearch::embeddings::EmbeddingsManager;
|
|
12
14
|
|
|
13
15
|
#[derive(Debug, Clone)]
|
|
14
16
|
pub enum OutputFormat {
|
|
@@ -27,11 +29,12 @@ pub struct QueryConfig {
|
|
|
27
29
|
pub file_filter: Option<String>,
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
#[derive(Debug)]
|
|
31
32
|
pub struct QueryCommand {
|
|
32
33
|
project_dir: PathBuf,
|
|
33
34
|
config: QueryConfig,
|
|
34
35
|
query_v2: QueryV2,
|
|
36
|
+
pgvector_store: Option<PgvectorStore>,
|
|
37
|
+
embeddings_manager: Option<EmbeddingsManager>,
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
impl QueryCommand {
|
|
@@ -43,6 +46,7 @@ impl QueryCommand {
|
|
|
43
46
|
output_format: OutputFormat,
|
|
44
47
|
context_lines: usize,
|
|
45
48
|
file_filter: Option<String>,
|
|
49
|
+
pg_url: Option<&str>,
|
|
46
50
|
) -> Result<Self> {
|
|
47
51
|
// Canonicalize project_dir at the start for security
|
|
48
52
|
let canonical_project_dir = path_validator::canonicalize(project_dir)?;
|
|
@@ -51,6 +55,28 @@ impl QueryCommand {
|
|
|
51
55
|
let db_path = get_database_path()?;
|
|
52
56
|
let query_v2 = QueryV2::new(&db_path)?;
|
|
53
57
|
|
|
58
|
+
// Initialize pgvector store if connection URL provided
|
|
59
|
+
let (pgvector_store, embeddings_manager) = if let Some(url) = pg_url {
|
|
60
|
+
info!("Initializing pgvector store for queries");
|
|
61
|
+
let rt = tokio::runtime::Runtime::new()?;
|
|
62
|
+
match rt.block_on(PgvectorStore::new(url)) {
|
|
63
|
+
Ok(store) => {
|
|
64
|
+
info!("Successfully connected to pgvector");
|
|
65
|
+
// Need embeddings manager to generate query embeddings
|
|
66
|
+
let index_path = crate::paths::get_codesearch_dir()?;
|
|
67
|
+
let embeddings_mgr = EmbeddingsManager::new(&index_path)?;
|
|
68
|
+
(Some(store), Some(embeddings_mgr))
|
|
69
|
+
}
|
|
70
|
+
Err(e) => {
|
|
71
|
+
warn!("Failed to connect to pgvector: {}. Using SQLite only.", e);
|
|
72
|
+
(None, None)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
info!("No pg_url provided, using SQLite only");
|
|
77
|
+
(None, None)
|
|
78
|
+
};
|
|
79
|
+
|
|
54
80
|
Ok(Self {
|
|
55
81
|
project_dir: canonical_project_dir,
|
|
56
82
|
config: QueryConfig {
|
|
@@ -62,6 +88,8 @@ impl QueryCommand {
|
|
|
62
88
|
file_filter,
|
|
63
89
|
},
|
|
64
90
|
query_v2,
|
|
91
|
+
pgvector_store,
|
|
92
|
+
embeddings_manager,
|
|
65
93
|
})
|
|
66
94
|
}
|
|
67
95
|
|
|
@@ -72,8 +100,41 @@ impl QueryCommand {
|
|
|
72
100
|
let max_results = self.config.max_results.unwrap_or(10);
|
|
73
101
|
let threshold = self.config.threshold.unwrap_or(0.3);
|
|
74
102
|
|
|
75
|
-
// Perform search
|
|
76
|
-
let results =
|
|
103
|
+
// Perform search - use pgvector if available, otherwise SQLite
|
|
104
|
+
let results: Vec<SearchResult> = if let (Some(ref pgvector_store), Some(ref embeddings_mgr)) =
|
|
105
|
+
(&self.pgvector_store, &self.embeddings_manager) {
|
|
106
|
+
info!("Using pgvector for semantic search");
|
|
107
|
+
|
|
108
|
+
// Generate embedding for query
|
|
109
|
+
let query_texts: Vec<String> = vec![self.config.query.clone()];
|
|
110
|
+
let query_embeddings: Vec<Vec<f32>> = embeddings_mgr.generate_embeddings(&query_texts)?;
|
|
111
|
+
let query_embedding: &[f32] = &query_embeddings[0];
|
|
112
|
+
|
|
113
|
+
// Search pgvector
|
|
114
|
+
let rt = tokio::runtime::Runtime::new()?;
|
|
115
|
+
let project_root_str = self.project_dir.to_string_lossy().to_string();
|
|
116
|
+
let pg_results = rt.block_on(
|
|
117
|
+
pgvector_store.search_similar(
|
|
118
|
+
query_embedding,
|
|
119
|
+
max_results,
|
|
120
|
+
threshold,
|
|
121
|
+
Some(&project_root_str),
|
|
122
|
+
)
|
|
123
|
+
)?;
|
|
124
|
+
|
|
125
|
+
// Convert pgvector SearchResult to query_v2 SearchResult
|
|
126
|
+
pg_results.into_iter().map(|r| SearchResult {
|
|
127
|
+
entity_kind: r.entity_kind,
|
|
128
|
+
entity_name: r.entity_name,
|
|
129
|
+
file_path: r.file_path,
|
|
130
|
+
similarity: r.similarity,
|
|
131
|
+
line_start: Some(r.line_number),
|
|
132
|
+
line_end: None,
|
|
133
|
+
}).collect()
|
|
134
|
+
} else {
|
|
135
|
+
info!("Using SQLite for search");
|
|
136
|
+
self.query_v2.search(&self.config.query, max_results, threshold, &self.project_dir)?
|
|
137
|
+
};
|
|
77
138
|
|
|
78
139
|
// Filter by file if specified
|
|
79
140
|
let final_results: Vec<SearchResult> = if let Some(ref file_filter) = self.config.file_filter {
|
|
@@ -108,12 +108,26 @@ impl EmbeddingsManager {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
let mut all_embeddings = Vec::with_capacity(texts.len());
|
|
111
|
-
let rt = tokio::runtime::Runtime::new()?;
|
|
112
111
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
// Check if we're already in a tokio runtime
|
|
113
|
+
match tokio::runtime::Handle::try_current() {
|
|
114
|
+
Ok(handle) => {
|
|
115
|
+
// We're in a runtime, use it
|
|
116
|
+
for chunk in texts.chunks(self.config.batch_size) {
|
|
117
|
+
let embeddings = handle.block_on(self.call_openai_api(chunk))
|
|
118
|
+
.context("Failed to generate embeddings from OpenAI API")?;
|
|
119
|
+
all_embeddings.extend(embeddings);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
Err(_) => {
|
|
123
|
+
// Not in a runtime, create one
|
|
124
|
+
let rt = tokio::runtime::Runtime::new()?;
|
|
125
|
+
for chunk in texts.chunks(self.config.batch_size) {
|
|
126
|
+
let embeddings = rt.block_on(self.call_openai_api(chunk))
|
|
127
|
+
.context("Failed to generate embeddings from OpenAI API")?;
|
|
128
|
+
all_embeddings.extend(embeddings);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
117
131
|
}
|
|
118
132
|
|
|
119
133
|
info!("Generated {} embeddings", all_embeddings.len());
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
//!
|
|
1
|
+
//! CodeSearch - Fast semantic code search with AST indexing
|
|
2
2
|
//!
|
|
3
|
-
//! This crate provides
|
|
4
|
-
//! using vector embeddings and
|
|
3
|
+
//! This crate provides storage and search capabilities for code patterns
|
|
4
|
+
//! using vector embeddings. Supports SQLite (default) and pgvector backends.
|
|
5
5
|
|
|
6
6
|
pub mod embeddings;
|
|
7
7
|
pub mod sqlite_store;
|
|
@@ -15,6 +15,7 @@ pub mod schema_v2;
|
|
|
15
15
|
pub mod query_v2;
|
|
16
16
|
pub mod migration_v2;
|
|
17
17
|
pub mod migration_tx;
|
|
18
|
+
pub mod store_pgvector;
|
|
18
19
|
|
|
19
20
|
#[cfg(test)]
|
|
20
21
|
mod transaction_tests;
|
|
@@ -23,10 +24,11 @@ mod transaction_tests;
|
|
|
23
24
|
pub use embeddings::EmbeddingsManager;
|
|
24
25
|
pub use sqlite_store::SqliteStore;
|
|
25
26
|
pub use search_engine::SearchEngine;
|
|
26
|
-
pub use paths::{
|
|
27
|
+
pub use paths::{get_codesearch_dir, get_database_path};
|
|
27
28
|
// V1 index functions are deprecated - use V2 via get_database_path()
|
|
28
29
|
pub use store_v2::StoreV2;
|
|
29
30
|
pub use store_v2_tx::StoreV2WithTx;
|
|
30
31
|
pub use schema_v2::SchemaV2;
|
|
31
32
|
pub use query_v2::QueryV2;
|
|
32
33
|
pub use migration_v2::MigrationV2;
|
|
34
|
+
pub use store_pgvector::PgvectorStore;
|