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.
Files changed (117) hide show
  1. package/.claude/agents/SHARED_PROTOCOL.md +25 -1
  2. package/.claude/agents/cfn-dev-team/dev-ops/fly-io-specialist.md +418 -0
  3. package/.claude/agents/cfn-dev-team/developers/database/mem0-specialist.md +579 -0
  4. package/.claude/agents/cfn-dev-team/developers/database/memgraph-specialist.md +722 -0
  5. package/.claude/agents/cfn-dev-team/documentation/pseudocode.md +1 -1
  6. package/.claude/agents/cfn-dev-team/documentation/specification-agent.md +1 -1
  7. package/.claude/agents/cfn-dev-team/testers/tester.md +35 -0
  8. package/.claude/commands/{cfn-ruvector → cfn-codesearch}/cfn-codebase-reindex.md +4 -4
  9. package/.claude/commands/{cfn-ruvector → cfn-codesearch}/cfn-codebase-search.md +7 -7
  10. package/.claude/commands/{cfn-ruvector → cfn-codesearch}/cfn-detect-stale-docs.md +3 -3
  11. package/.claude/commands/{cfn-ruvector-search.md → cfn-codesearch-search.md} +7 -7
  12. package/.claude/commands/cfn-fix-errors.md +1 -1
  13. package/.claude/commands/cfn-loop-task.md +291 -291
  14. package/.claude/skills/{bulk-add-ruvector-instructions.sh → bulk-add-codesearch-instructions.sh} +12 -12
  15. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/AGENT_INTEGRATION_PATTERNS.md +20 -20
  16. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/AGENT_LIFECYCLE_INTEGRATION.md +11 -11
  17. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/Cargo.toml +9 -4
  18. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/EXTRACTION_EXAMPLES.md +6 -6
  19. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/IMMEDIATE_ACTION_REQUIRED.md +9 -9
  20. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/README.md +7 -7
  21. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/SCHEMA_V2_IMPLEMENTATION.md +1 -1
  22. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/SECURITY_IMPLEMENTATION.md +3 -3
  23. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/SECURITY_TESTING_COMPLETION.md +4 -4
  24. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/SKILL.md +31 -31
  25. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/analyze-agent-failures.sh +11 -11
  26. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/cfn-integration.sh +27 -27
  27. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/index-code.sh +11 -11
  28. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/index_all.sh +2 -2
  29. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/ingest-agent-transcript.sh +10 -10
  30. package/.claude/skills/{cfn-local-ruvector-accelerator/init-local-ruvector.sh → cfn-codesearch/init-local-codesearch.sh} +16 -16
  31. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/project-structure.md +10 -10
  32. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/query-local.sh +6 -6
  33. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/index.rs +233 -4
  34. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/init.rs +18 -18
  35. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/query.rs +64 -3
  36. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/embeddings.rs +19 -5
  37. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/lib.rs +6 -4
  38. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/main.rs +35 -25
  39. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/paths.rs +19 -13
  40. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/query_v2.rs +3 -4
  41. package/.claude/skills/cfn-codesearch/src/store_pgvector.rs +301 -0
  42. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/store_v2.rs +12 -1
  43. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/test-agent-lifecycle-integration.sh +6 -6
  44. package/.claude/skills/{cfn-local-ruvector-accelerator/test-local-ruvector.sh → cfn-codesearch/test-local-codesearch.sh} +7 -7
  45. package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/test_query_api.sh +2 -2
  46. package/.claude/skills/cfn-epic-creator/SKILL.md +110 -2
  47. package/.claude/skills/cfn-epic-creator/invoke.sh +120 -1
  48. package/.claude/skills/cfn-mdap-context-injection/SKILL.md +2 -2
  49. package/.claude/skills/cfn-mdap-context-injection/inject.sh +21 -21
  50. package/package.json +11 -11
  51. package/readme/feature-status.md +405 -0
  52. package/.claude/agents/docker-ts-fixer.md +0 -65
  53. /package/.claude/agents/cfn-dev-team/{testing → testers}/test-validation-agent.md +0 -0
  54. /package/.claude/{agents/cfn-dev-team/utility → cfn-extras/agents}/context-curator.md +0 -0
  55. /package/.claude/{agents/custom → cfn-extras/agents/custom-agents}/cfn-docker-expert.md +0 -0
  56. /package/.claude/{agents/custom → cfn-extras/agents/custom-agents}/cfn-loops-cli-expert.md +0 -0
  57. /package/.claude/{agents/custom → cfn-extras/agents/custom-agents}/cfn-redis-operations.md +0 -0
  58. /package/.claude/{agents/custom → cfn-extras/agents/custom-agents}/cfn-system-expert.md +0 -0
  59. /package/.claude/{agents/csuite → cfn-extras/agents/custom-agents}/cto-agent.md +0 -0
  60. /package/.claude/{agents/custom → cfn-extras/agents/custom-agents}/trigger-dev-expert.md +0 -0
  61. /package/.claude/{agents/cfn-dev-team/coordinators → cfn-extras/agents/deprecated-coordinators}/consensus-builder.md +0 -0
  62. /package/.claude/{agents/cfn-dev-team/coordinators → cfn-extras/agents/deprecated-coordinators}/multi-sprint-coordinator.md +0 -0
  63. /package/.claude/{agents → cfn-extras/agents/docker-team}/docker-coordinators/cfn-docker-v3-coordinator.md +0 -0
  64. /package/.claude/{agents/cfn-dev-team/dev-ops → cfn-extras/agents}/kubernetes-specialist.md +0 -0
  65. /package/.claude/{agents/cfn-dev-team/architecture → cfn-extras/agents}/planner.md +0 -0
  66. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/.claude/hooks/SessionStart-cfn-build-ruvector.sh +0 -0
  67. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/Cargo.toml.backup +0 -0
  68. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/SECURITY_FINDINGS_SUMMARY.txt +0 -0
  69. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/docs/EXECUTIVE_SUMMARY.txt +0 -0
  70. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/docs/PHASE_4_QUERY_API.md +0 -0
  71. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/docs/RUST_AST_EXTRACTOR_IMPLEMENTATION.md +0 -0
  72. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/docs/TRANSACTION_MANAGEMENT.md +0 -0
  73. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/docs/VALIDATION_FINDINGS.txt +0 -0
  74. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/embeddings_manager.py.backup +0 -0
  75. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/epic-ast-indexer.json +0 -0
  76. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/index/index.bin +0 -0
  77. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/index/metadata.json +0 -0
  78. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/index-code.sh.backup +0 -0
  79. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/query-agent-patterns.sh +0 -0
  80. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/requirements.txt +0 -0
  81. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/search_engine_v2.py.backup +0 -0
  82. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/sqlite_store.py.backup +0 -0
  83. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/cleanup.rs +0 -0
  84. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/export.rs +0 -0
  85. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/find.rs +0 -0
  86. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/index_ast.rs +0 -0
  87. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/index_modified.rs +0 -0
  88. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/migration.rs +0 -0
  89. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/mod.rs +0 -0
  90. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/refs.rs +0 -0
  91. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/reset.rs +0 -0
  92. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/cli/stats.rs +0 -0
  93. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/extractors/mod.rs +0 -0
  94. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/extractors/rust.rs +0 -0
  95. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/extractors/rust_placeholder.rs +0 -0
  96. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/extractors/text_fallback.rs +0 -0
  97. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/extractors/typescript.rs +0 -0
  98. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/extractors/typescript_full.rs +0 -0
  99. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/main.rs.backup +0 -0
  100. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/migration.rs +0 -0
  101. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/migration_backup.rs +0 -0
  102. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/migration_tx.rs +0 -0
  103. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/migration_v2.rs +0 -0
  104. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/path_validator.rs +0 -0
  105. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/query_api.rs +0 -0
  106. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/schema_v2.rs +0 -0
  107. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/search_engine.rs +0 -0
  108. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/security_tests.rs +0 -0
  109. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/sqlite_store.rs +0 -0
  110. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/store_v2_backup.rs +0 -0
  111. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/store_v2_fixed.rs +0 -0
  112. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/store_v2_tx.rs +0 -0
  113. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/test_schema.rs +0 -0
  114. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/src/transaction_tests.rs +0 -0
  115. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/test_ast_indexing.rs +0 -0
  116. /package/.claude/skills/{cfn-local-ruvector-accelerator → cfn-codesearch}/test_schema.sql +0 -0
  117. /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 local_ruvector::paths::{get_ruvector_dir, get_database_path};
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 = get_ruvector_dir()?;
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: None,
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 local_ruvector::paths::{get_ruvector_dir, get_database_path};
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 ruvector_dir = get_ruvector_dir()?;
46
- if ruvector_dir.exists() {
47
- info!("RuVector already initialized at: {}", ruvector_dir.display());
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 ruvector_dir = get_ruvector_dir()?;
55
+ let codesearch_dir = get_codesearch_dir()?;
56
56
 
57
57
  // Create directory if doesn't exist (idempotent)
58
- if !ruvector_dir.exists() {
59
- info!("Creating centralized RuVector directory: {}", ruvector_dir.display());
60
- fs::create_dir_all(&ruvector_dir)
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(ruvector_dir.join("embeddings"))
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(ruvector_dir.join("cache"))
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: {}", ruvector_dir.display());
68
+ info!("Using existing centralized RuVector directory: {}", codesearch_dir.display());
69
69
  }
70
70
 
71
71
  // Create configuration file if missing
72
- if !ruvector_dir.join("config.json").exists() {
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 !ruvector_dir.join("embeddings").exists() {
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 ruvector_dir = get_ruvector_dir()?;
99
- let config_path = ruvector_dir.join("config.json");
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
- local_ruvector::schema_v2::SchemaV2::initialize(&conn)?;
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
- local_ruvector::schema_v2::SchemaV2::initialize(&conn)?;
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 = get_ruvector_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 with project root isolation
76
- let results = self.query_v2.search(&self.config.query, max_results, threshold, &self.project_dir)?;
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
- for chunk in texts.chunks(self.config.batch_size) {
114
- let embeddings = rt.block_on(self.call_openai_api(chunk))
115
- .context("Failed to generate embeddings from OpenAI API")?;
116
- all_embeddings.extend(embeddings);
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
- //! Local RuVector Accelerator - Fast pattern storage and search
1
+ //! CodeSearch - Fast semantic code search with AST indexing
2
2
  //!
3
- //! This crate provides local storage and search capabilities for code patterns
4
- //! using vector embeddings and SQLite.
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::{get_ruvector_dir, get_database_path};
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;