claude-flow-novice 2.18.16 → 2.18.18

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 (71) hide show
  1. package/.claude/agents/cfn-dev-team/analysts/analyst.md +9 -0
  2. package/.claude/agents/cfn-dev-team/analysts/root-cause-analyst.md +9 -0
  3. package/.claude/agents/cfn-dev-team/architecture/api-designer-persona.md +10 -1
  4. package/.claude/agents/cfn-dev-team/architecture/base-template-generator.md +9 -0
  5. package/.claude/agents/cfn-dev-team/architecture/goal-planner.md +10 -1
  6. package/.claude/agents/cfn-dev-team/architecture/planner.md +9 -0
  7. package/.claude/agents/cfn-dev-team/architecture/system-architect.md +9 -0
  8. package/.claude/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +9 -0
  9. package/.claude/agents/cfn-dev-team/coordinators/consensus-builder.md +10 -1
  10. package/.claude/agents/cfn-dev-team/coordinators/handoff-coordinator.md +9 -0
  11. package/.claude/agents/cfn-dev-team/coordinators/multi-sprint-coordinator.md +9 -0
  12. package/.claude/agents/cfn-dev-team/dev-ops/devops-engineer.md +9 -0
  13. package/.claude/agents/cfn-dev-team/dev-ops/docker-specialist.md +9 -0
  14. package/.claude/agents/cfn-dev-team/dev-ops/github-commit-agent.md +9 -0
  15. package/.claude/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +9 -0
  16. package/.claude/agents/cfn-dev-team/developers/api-gateway-specialist.md +9 -0
  17. package/.claude/agents/cfn-dev-team/developers/data/data-engineer.md +9 -0
  18. package/.claude/agents/cfn-dev-team/developers/database/database-architect.md +9 -0
  19. package/.claude/agents/cfn-dev-team/developers/database/supabase-specialist.md +9 -0
  20. package/.claude/agents/cfn-dev-team/developers/frontend/mobile-dev.md +10 -1
  21. package/.claude/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +10 -1
  22. package/.claude/agents/cfn-dev-team/developers/frontend/ui-designer.md +10 -1
  23. package/.claude/agents/cfn-dev-team/developers/graphql-specialist.md +9 -0
  24. package/.claude/agents/cfn-dev-team/developers/rust-developer.md +10 -1
  25. package/.claude/agents/cfn-dev-team/documentation/agent-type-guidelines.md +9 -0
  26. package/.claude/agents/cfn-dev-team/documentation/api-documentation.md +10 -1
  27. package/.claude/agents/cfn-dev-team/documentation/pseudocode.md +10 -1
  28. package/.claude/agents/cfn-dev-team/documentation/specification-agent.md +10 -1
  29. package/.claude/agents/cfn-dev-team/product-owners/accessibility-advocate-persona.md +10 -1
  30. package/.claude/agents/cfn-dev-team/product-owners/cto-agent.md +9 -0
  31. package/.claude/agents/cfn-dev-team/product-owners/power-user-persona.md +10 -1
  32. package/.claude/agents/cfn-dev-team/product-owners/product-owner.md +9 -0
  33. package/.claude/agents/cfn-dev-team/reviewers/quality/code-quality-validator.md +9 -0
  34. package/.claude/agents/cfn-dev-team/reviewers/quality/cyclomatic-complexity-reducer.md +9 -0
  35. package/.claude/agents/cfn-dev-team/reviewers/quality/perf-analyzer.md +9 -0
  36. package/.claude/agents/cfn-dev-team/reviewers/quality/performance-benchmarker.md +10 -1
  37. package/.claude/agents/cfn-dev-team/reviewers/quality/quality-metrics.md +9 -0
  38. package/.claude/agents/cfn-dev-team/testers/api-testing-specialist.md +9 -0
  39. package/.claude/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +9 -0
  40. package/.claude/agents/cfn-dev-team/testers/contract-tester.md +9 -0
  41. package/.claude/agents/cfn-dev-team/testers/e2e/playwright-tester.md +9 -0
  42. package/.claude/agents/cfn-dev-team/testers/integration-tester.md +9 -0
  43. package/.claude/agents/cfn-dev-team/testers/interaction-tester.md +9 -0
  44. package/.claude/agents/cfn-dev-team/testers/load-testing-specialist.md +9 -0
  45. package/.claude/agents/cfn-dev-team/testers/mutation-testing-specialist.md +9 -0
  46. package/.claude/agents/cfn-dev-team/testers/playwright-tester.md +10 -1
  47. package/.claude/agents/cfn-dev-team/testers/unit/tdd-london-unit-swarm.md +10 -1
  48. package/.claude/agents/cfn-dev-team/testers/validation/validation-production-validator.md +10 -1
  49. package/.claude/agents/cfn-dev-team/testing/test-validation-agent.md +9 -0
  50. package/.claude/agents/cfn-dev-team/utility/agent-builder.md +10 -1
  51. package/.claude/agents/cfn-dev-team/utility/context-curator.md +9 -0
  52. package/.claude/agents/cfn-dev-team/utility/memory-leak-specialist.md +9 -0
  53. package/.claude/agents/cfn-dev-team/utility/researcher.md +9 -0
  54. package/.claude/agents/cfn-dev-team/utility/z-ai-specialist.md +9 -0
  55. package/.claude/hooks/SessionStart-cfn-build-ruvector.sh +12 -0
  56. package/.claude/hooks/SessionStart:cfn-build-ruvector.sh +28 -0
  57. package/.claude/skills/bulk-add-ruvector-instructions.sh +34 -46
  58. package/.claude/skills/cfn-local-ruvector-accelerator/.claude/hooks/SessionStart-cfn-build-ruvector.sh +12 -0
  59. package/.claude/skills/cfn-local-ruvector-accelerator/SKILL.md +112 -6
  60. package/.claude/skills/cfn-local-ruvector-accelerator/src/cli/index.rs +46 -11
  61. package/.claude/skills/cfn-local-ruvector-accelerator/src/cli/index_ast.rs +13 -1
  62. package/.claude/skills/cfn-local-ruvector-accelerator/src/embeddings.rs +4 -7
  63. package/.claude/skills/cfn-local-ruvector-accelerator/src/extractors/mod.rs +1 -1
  64. package/.claude/skills/cfn-local-ruvector-accelerator/src/migration_v2.rs +7 -7
  65. package/.claude/skills/cfn-local-ruvector-accelerator/src/query_api.rs +6 -3
  66. package/.claude/skills/cfn-local-ruvector-accelerator/src/schema_v2.rs +16 -1
  67. package/.claude/skills/cfn-local-ruvector-accelerator/src/store_v2.rs +7 -3
  68. package/.claude/skills/cfn-local-ruvector-accelerator/src/store_v2_tx.rs +5 -3
  69. package/.claude/skills/cfn-local-ruvector-accelerator/src/transaction_tests.rs +8 -2
  70. package/CLAUDE.md +2 -1
  71. package/package.json +1 -1
@@ -1,8 +1,114 @@
1
- # 1. Initialize local RuVector
2
- ./target/release/local-ruvector init
1
+ # RuVector Local Semantic Code Search
3
2
 
4
- # 2. Index your codebase
5
- ./target/release/local-ruvector index --path /path/to/project --types rs
3
+ ## WHEN TO USE THIS SKILL
6
4
 
7
- # 3. Query patterns instantly
8
- ./target/release/local-ruvector query --pattern "authentication rust" --limit 5
5
+ **USE RuVector V2 SQL for ALL indexed projects (400x FASTER than grep):**
6
+ ```bash
7
+ # Exact name lookup - 0.002s vs grep's 0.8s
8
+ sqlite3 ~/.local/share/ruvector/index_v2.db "SELECT file_path, line_number FROM entities WHERE name = 'MyFunction';"
9
+
10
+ # Fuzzy search - 0.004s
11
+ sqlite3 ~/.local/share/ruvector/index_v2.db "SELECT file_path, line_number FROM entities WHERE name LIKE '%Store%' LIMIT 10;"
12
+ ```
13
+
14
+ **USE grep/rg ONLY when:**
15
+ - Project is NOT indexed yet
16
+ - Searching for strings that aren't code entities (error messages, comments, config values)
17
+ - Quick one-off search in small directory
18
+
19
+ **USE RuVector semantic search when:**
20
+ - "Where is authentication implemented?" (conceptual search)
21
+ - Finding similar patterns you can't name exactly
22
+ - Discovering how a feature is built
23
+
24
+ ## Quick Commands
25
+
26
+ ### Semantic Search (V1 - Embeddings)
27
+ ```bash
28
+ # Natural language search
29
+ /codebase-search "authentication middleware pattern"
30
+ /cfn-ruvector-search "error handling in API routes"
31
+
32
+ # CLI direct (note: query text is positional, use --max-results not --limit)
33
+ ./.claude/skills/cfn-local-ruvector-accelerator/target/release/local-ruvector query "user login flow" --max-results 5
34
+ ```
35
+
36
+ ### Structural Search (V2 - SQL on AST)
37
+ ```bash
38
+ # Find all callers of a function
39
+ sqlite3 ~/.local/share/ruvector/index_v2.db \
40
+ "SELECT * FROM refs WHERE target_name = 'MyFunction';"
41
+
42
+ # Find all functions in a file
43
+ sqlite3 ~/.local/share/ruvector/index_v2.db \
44
+ "SELECT name, line_number FROM entities WHERE file_path LIKE '%myfile.rs' AND kind = 'function';"
45
+
46
+ # Find entities by project (multi-project isolation)
47
+ sqlite3 ~/.local/share/ruvector/index_v2.db \
48
+ "SELECT COUNT(*) FROM entities WHERE project_root = '/path/to/project';"
49
+ ```
50
+
51
+ ## Prerequisites
52
+
53
+ **OPENAI_API_KEY is REQUIRED for indexing.** Indexing will fail without a valid key.
54
+
55
+ ```bash
56
+ # Option 1: Export before running
57
+ export OPENAI_API_KEY="sk-..."
58
+
59
+ # Option 2: Add to shell profile (~/.bashrc or ~/.zshrc)
60
+ echo 'export OPENAI_API_KEY="sk-..."' >> ~/.bashrc
61
+ source ~/.bashrc
62
+
63
+ # Option 3: Inline with command
64
+ OPENAI_API_KEY="sk-..." ./local-ruvector index --path /project
65
+ ```
66
+
67
+ **Verify key is set:**
68
+ ```bash
69
+ echo $OPENAI_API_KEY # Should show your key (not empty)
70
+ ```
71
+
72
+ ## Index Management
73
+
74
+ ```bash
75
+ # Index a project (first time or full rebuild)
76
+ ./target/release/local-ruvector index --path /path/to/project --types rs,ts,py
77
+
78
+ # Incremental update (after code changes)
79
+ /codebase-reindex
80
+
81
+ # Check index stats
82
+ sqlite3 ~/.local/share/ruvector/index_v2.db "SELECT project_root, COUNT(*) FROM entities GROUP BY project_root;"
83
+ ```
84
+
85
+ ## Key Features
86
+
87
+ - **Multi-project isolation**: Index multiple projects in single database without data collision
88
+ - **Non-destructive**: Indexing one project never deletes data from other projects
89
+ - **Centralized storage**: `~/.local/share/ruvector/index_v2.db`
90
+ - **Dual search**: V1 semantic (embeddings) + V2 structural (SQL on AST)
91
+ - **Fast**: Rust binary with SQLite backend
92
+
93
+ ## Database Location
94
+ ```
95
+ ~/.local/share/ruvector/index_v2.db
96
+ ```
97
+
98
+ ## For Agents
99
+
100
+ Before implementing changes, ALWAYS query RuVector first:
101
+ ```bash
102
+ # Find similar patterns (slash command uses --top, CLI uses --max-results)
103
+ /codebase-search "relevant search terms" --top 5
104
+ # Or via CLI:
105
+ ./local-ruvector query "relevant search terms" --max-results 5
106
+
107
+ # Query past errors
108
+ ./.claude/skills/cfn-ruvector-codebase-index/query-error-patterns.sh --task-description "description"
109
+
110
+ # Query learnings
111
+ ./.claude/skills/cfn-ruvector-codebase-index/query-learnings.sh --task-description "description" --category PATTERN
112
+ ```
113
+
114
+ This prevents duplicated work and leverages existing solutions.
@@ -46,7 +46,7 @@ use anyhow::{Result, Context, anyhow};
46
46
  use std::fs;
47
47
  use std::path::{Path, PathBuf};
48
48
  use walkdir::{WalkDir, DirEntry};
49
- use tracing::{info, debug, warn};
49
+ use tracing::{info, debug, warn, error};
50
50
  use regex::Regex;
51
51
  use std::sync::Arc;
52
52
  use std::sync::RwLock;
@@ -286,6 +286,15 @@ impl IndexCommand {
286
286
  }
287
287
 
288
288
  pub fn execute(&mut self) -> Result<IndexStats> {
289
+ // Fail early if OPENAI_API_KEY is not set
290
+ if std::env::var("OPENAI_API_KEY").is_err() {
291
+ error!("OPENAI_API_KEY environment variable is required for indexing");
292
+ return Err(anyhow!(
293
+ "OPENAI_API_KEY not found. Set it with: export OPENAI_API_KEY=\"sk-...\"\n\
294
+ Indexing requires a valid OpenAI API key for generating embeddings."
295
+ ));
296
+ }
297
+
289
298
  info!("Starting index process");
290
299
  info!("File types: {:?}", self.file_types);
291
300
  if let Some(ref patterns) = self.patterns {
@@ -432,14 +441,21 @@ impl IndexCommand {
432
441
  ) -> Result<()> {
433
442
  let file_hash = self.calculate_file_hash(file_path)?;
434
443
 
444
+ // Check if file is already indexed with same hash (incremental indexing)
435
445
  if !self.force && self.is_file_indexed(file_path, &file_hash)? {
436
- debug!("Skipping already indexed file: {}", file_path.display());
446
+ debug!("Skipping already indexed file (unchanged): {}", file_path.display());
437
447
  return Ok(());
438
448
  }
439
449
 
440
- // Clean up old entries before reindexing to prevent duplicate entities
450
+ // Non-destructive update: Only delete entities for THIS specific file
451
+ // The delete_file_entities already scopes to project_root for multi-project safety
441
452
  let file_path_str = file_path.to_string_lossy();
442
- self.store_v2.delete_file_entities(&file_path_str, &self.project_dir)?;
453
+
454
+ // Only clean up if the file was previously indexed (avoid unnecessary DB operations)
455
+ if self.is_file_in_index(file_path)? {
456
+ debug!("Updating existing file entries: {}", file_path.display());
457
+ self.store_v2.delete_file_entities(&file_path_str, &self.project_dir)?;
458
+ }
443
459
 
444
460
  let content = fs::read_to_string(file_path)
445
461
  .with_context(|| format!("Failed to read file: {}", file_path.display()))?;
@@ -470,7 +486,7 @@ impl IndexCommand {
470
486
  s.embeddings_generated += embeddings.len();
471
487
  }
472
488
 
473
- self.mark_file_indexed(file_path, &file_hash)?;
489
+ self.mark_file_indexed(file_path, &file_hash, extraction_result.entities.len())?;
474
490
 
475
491
  Ok(())
476
492
  }
@@ -539,6 +555,7 @@ impl IndexCommand {
539
555
  doc_comment: None,
540
556
  attributes: None,
541
557
  metadata: Some(serde_json::to_string(&entity.metadata)?),
558
+ project_root: project_root_str.to_string(),
542
559
  created_at: chrono::Utc::now(),
543
560
  updated_at: chrono::Utc::now(),
544
561
  };
@@ -654,15 +671,33 @@ impl IndexCommand {
654
671
  Ok(count > 0)
655
672
  }
656
673
 
657
- fn mark_file_indexed(&self, file_path: &Path, file_hash: &str) -> Result<()> {
674
+ /// Check if file exists in the index (regardless of hash)
675
+ fn is_file_in_index(&self, file_path: &Path) -> Result<bool> {
676
+ let query = "SELECT COUNT(*) FROM file_hashes WHERE file_path = ?";
677
+ let mut stmt = self.store_v2.conn.prepare(query)?;
678
+ let count: i64 = stmt.query_row(
679
+ params![file_path.to_string_lossy()],
680
+ |row| row.get(0)
681
+ )?;
682
+ Ok(count > 0)
683
+ }
684
+
685
+ fn mark_file_indexed(&self, file_path: &Path, file_hash: &str, patterns_count: usize) -> Result<()> {
686
+ let timestamp = chrono::Utc::now().timestamp();
687
+ let file_path_str = file_path.to_string_lossy().to_string();
688
+
689
+ // Update file_hashes table (for incremental indexing)
658
690
  self.store_v2.conn.execute(
659
691
  "INSERT OR REPLACE INTO file_hashes (file_path, file_hash, indexed_at) VALUES (?1, ?2, ?3)",
660
- params![
661
- file_path.to_string_lossy(),
662
- file_hash,
663
- chrono::Utc::now().timestamp()
664
- ]
692
+ params![&file_path_str, file_hash, timestamp]
665
693
  )?;
694
+
695
+ // Also update the files table (for legacy compatibility and stats)
696
+ self.store_v2.conn.execute(
697
+ "INSERT OR REPLACE INTO files (path, hash, last_indexed, patterns_count) VALUES (?1, ?2, ?3, ?4)",
698
+ params![&file_path_str, file_hash, timestamp, patterns_count as i64]
699
+ )?;
700
+
666
701
  Ok(())
667
702
  }
668
703
 
@@ -99,6 +99,15 @@ impl AstIndexCommand {
99
99
  }
100
100
 
101
101
  pub fn execute(&mut self) -> Result<IndexStats> {
102
+ // Fail early if OPENAI_API_KEY is not set
103
+ if std::env::var("OPENAI_API_KEY").is_err() {
104
+ error!("OPENAI_API_KEY environment variable is required for indexing");
105
+ return Err(anyhow!(
106
+ "OPENAI_API_KEY not found. Set it with: export OPENAI_API_KEY=\"sk-...\"\n\
107
+ Indexing requires a valid OpenAI API key for generating embeddings."
108
+ ));
109
+ }
110
+
102
111
  let start_time = std::time::Instant::now();
103
112
 
104
113
  info!("Starting AST-based index process");
@@ -299,6 +308,7 @@ impl AstIndexCommand {
299
308
  let mut entity_map = HashMap::new();
300
309
  let mut type_usages = Vec::new();
301
310
 
311
+ let project_root_str = self.project_dir.to_string_lossy().to_string();
302
312
  for (idx, entity) in extraction_result.entities.iter().enumerate() {
303
313
  let store_entity = StoreEntity {
304
314
  id: 0,
@@ -313,6 +323,7 @@ impl AstIndexCommand {
313
323
  doc_comment: None, // TODO: Extract doc comments
314
324
  attributes: None, // TODO: Extract attributes
315
325
  metadata: Some(serde_json::to_string(&entity.metadata)?),
326
+ project_root: project_root_str.clone(),
316
327
  created_at: chrono::Utc::now(),
317
328
  updated_at: chrono::Utc::now(),
318
329
  };
@@ -613,6 +624,7 @@ impl AstIndexCommand {
613
624
  Ok(entity.id)
614
625
  } else {
615
626
  // Create a placeholder entity for unknown references
627
+ let project_root_str = self.project_dir.to_string_lossy().to_string();
616
628
  let placeholder = StoreEntity {
617
629
  id: 0,
618
630
  kind: EntityKind::Function,
@@ -626,11 +638,11 @@ impl AstIndexCommand {
626
638
  doc_comment: None,
627
639
  attributes: None,
628
640
  metadata: None,
641
+ project_root: project_root_str.clone(),
629
642
  created_at: chrono::Utc::now(),
630
643
  updated_at: chrono::Utc::now(),
631
644
  };
632
645
 
633
- let project_root_str = self.project_dir.to_string_lossy();
634
646
  Ok(self.store_v2.insert_entity(&placeholder, &project_root_str)?)
635
647
  }
636
648
  }
@@ -101,13 +101,10 @@ impl EmbeddingsManager {
101
101
  debug!("Generating embeddings for {} texts", texts.len());
102
102
 
103
103
  if self.config.api_key.is_none() {
104
- warn!("OpenAI API key not found, falling back to dummy embeddings");
105
- let mut embeddings = Vec::with_capacity(texts.len());
106
- for text in texts {
107
- embeddings.push(self.generate_dummy_embedding(text));
108
- }
109
- info!("Generated {} dummy embeddings", embeddings.len());
110
- return Ok(embeddings);
104
+ error!("OPENAI_API_KEY environment variable not set");
105
+ return Err(anyhow!(
106
+ "OPENAI_API_KEY not found. Set it with: export OPENAI_API_KEY=\"sk-...\""
107
+ ));
111
108
  }
112
109
 
113
110
  let mut all_embeddings = Vec::with_capacity(texts.len());
@@ -28,7 +28,7 @@ pub fn create_text_fallback_extractor() -> Result<text_fallback::TextFallbackExt
28
28
  }
29
29
 
30
30
  /// Common entity kinds across languages
31
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
31
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
32
32
  pub enum EntityKind {
33
33
  // Functions
34
34
  Function,
@@ -359,21 +359,21 @@ mod tests {
359
359
 
360
360
  SchemaV2::initialize(&conn)?;
361
361
 
362
- // Insert test entities with different path patterns
362
+ // Insert test entities with project_root set (schema now requires it)
363
363
  conn.execute(
364
- "INSERT INTO entities (kind, name, file_path, line_number) VALUES (?, ?, ?, ?)",
365
- params!["struct", "Test1", "/home/user/project/src/main.rs", 10]
364
+ "INSERT INTO entities (kind, name, file_path, line_number, project_root) VALUES (?, ?, ?, ?, ?)",
365
+ params!["struct", "Test1", "/home/user/project/src/main.rs", 10, "/home/user/project"]
366
366
  )?;
367
367
 
368
368
  conn.execute(
369
- "INSERT INTO entities (kind, name, file_path, line_number) VALUES (?, ?, ?, ?)",
370
- params!["function", "Test2", "/var/app/lib/utils.rs", 20]
369
+ "INSERT INTO entities (kind, name, file_path, line_number, project_root) VALUES (?, ?, ?, ?, ?)",
370
+ params!["function", "Test2", "/var/app/lib/utils.rs", 20, "/var/app"]
371
371
  )?;
372
372
 
373
- // Run migration
373
+ // Run migration (will be skipped since schema already has project_root)
374
374
  MigrationV2::run_v2_migration(&mut conn)?;
375
375
 
376
- // Verify project_root extraction
376
+ // Verify project_root values
377
377
  let project1: String = conn.query_row(
378
378
  "SELECT project_root FROM entities WHERE name = ?",
379
379
  params!["Test1"],
@@ -310,14 +310,15 @@ impl QueryApi {
310
310
  row.get::<_, Option<String>>(9)?, // doc_comment
311
311
  row.get::<_, Option<String>>(10)?, // attributes
312
312
  row.get::<_, Option<String>>(11)?, // metadata
313
- row.get::<_, i64>(12)?, // created_at
314
- row.get::<_, i64>(13)?, // updated_at
313
+ row.get::<_, String>(12)?, // project_root
314
+ row.get::<_, i64>(13)?, // created_at
315
+ row.get::<_, i64>(14)?, // updated_at
315
316
  ))
316
317
  })?;
317
318
 
318
319
  for row in rows {
319
320
  let row = row?;
320
- let (id, kind_str, name, signature, visibility_str, parent_id, file_path, line_number, column_number, doc_comment, attributes, metadata, created_at, updated_at) = row;
321
+ let (id, kind_str, name, signature, visibility_str, parent_id, file_path, line_number, column_number, doc_comment, attributes, metadata, project_root, created_at, updated_at) = row;
321
322
  // For now, just create a simple entity - the full parsing can be done later
322
323
  // This is just to get the IDs for reference finding
323
324
  matching_entities.push(crate::store_v2::Entity {
@@ -333,6 +334,7 @@ impl QueryApi {
333
334
  doc_comment,
334
335
  attributes,
335
336
  metadata,
337
+ project_root,
336
338
  created_at: chrono::DateTime::from_timestamp(created_at, 0).unwrap_or_default(),
337
339
  updated_at: chrono::DateTime::from_timestamp(updated_at, 0).unwrap_or_default(),
338
340
  });
@@ -366,6 +368,7 @@ impl QueryApi {
366
368
  doc_comment: None,
367
369
  attributes: None,
368
370
  metadata: None,
371
+ project_root: "".to_string(),
369
372
  created_at: chrono::DateTime::from_timestamp(0, 0).unwrap_or_default(),
370
373
  updated_at: chrono::DateTime::from_timestamp(0, 0).unwrap_or_default(),
371
374
  };
@@ -226,9 +226,10 @@ impl SchemaV2 {
226
226
  doc_comment TEXT,
227
227
  attributes TEXT,
228
228
  metadata TEXT,
229
+ project_root TEXT NOT NULL DEFAULT '',
229
230
  created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
230
231
  updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
231
-
232
+
232
233
  FOREIGN KEY (parent_id) REFERENCES entities(id) ON DELETE RESTRICT
233
234
  );
234
235
 
@@ -282,6 +283,14 @@ impl SchemaV2 {
282
283
 
283
284
  FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE RESTRICT
284
285
  );
286
+
287
+ -- Create files table for tracking indexed files (stats and legacy compatibility)
288
+ CREATE TABLE IF NOT EXISTS files (
289
+ path TEXT PRIMARY KEY,
290
+ hash TEXT NOT NULL,
291
+ last_indexed INTEGER NOT NULL,
292
+ patterns_count INTEGER NOT NULL DEFAULT 0
293
+ );
285
294
  "#
286
295
  )?;
287
296
 
@@ -311,6 +320,8 @@ impl SchemaV2 {
311
320
  CREATE INDEX IF NOT EXISTS idx_entities_kind_name ON entities(kind, name);
312
321
  CREATE INDEX IF NOT EXISTS idx_entities_file_kind ON entities(file_path, kind);
313
322
  CREATE INDEX IF NOT EXISTS idx_entities_parent_kind ON entities(parent_id, kind);
323
+ CREATE INDEX IF NOT EXISTS idx_entities_project_root ON entities(project_root);
324
+ CREATE INDEX IF NOT EXISTS idx_entities_project_file ON entities(project_root, file_path);
314
325
 
315
326
  -- Reference indexes
316
327
  CREATE INDEX IF NOT EXISTS idx_refs_source ON refs(source_entity_id);
@@ -338,6 +349,10 @@ impl SchemaV2 {
338
349
 
339
350
  -- Entity-module relationship index (via file path)
340
351
  CREATE INDEX IF NOT EXISTS idx_entities_module_lookup ON entities(file_path);
352
+
353
+ -- Files table indexes
354
+ CREATE INDEX IF NOT EXISTS idx_files_hash ON files(hash);
355
+ CREATE INDEX IF NOT EXISTS idx_files_last_indexed ON files(last_indexed);
341
356
  "#
342
357
  )?;
343
358
 
@@ -23,6 +23,7 @@ pub struct Entity {
23
23
  pub doc_comment: Option<String>,
24
24
  pub attributes: Option<String>,
25
25
  pub metadata: Option<String>,
26
+ pub project_root: String,
26
27
  pub created_at: DateTime<Utc>,
27
28
  pub updated_at: DateTime<Utc>,
28
29
  }
@@ -558,8 +559,9 @@ impl StoreV2 {
558
559
 
559
560
  // Helper methods to convert rows to structs
560
561
  pub(crate) fn row_to_entity(&self, row: &Row) -> rusqlite::Result<Entity> {
561
- let created_timestamp: i64 = row.get(12)?;
562
- let updated_timestamp: i64 = row.get(13)?;
562
+ let project_root: String = row.get(12)?;
563
+ let created_timestamp: i64 = row.get(13)?;
564
+ let updated_timestamp: i64 = row.get(14)?;
563
565
 
564
566
  let kind_str = row.get::<_, String>(1)?;
565
567
  let kind = EntityKind::from_str(&kind_str)
@@ -582,6 +584,7 @@ impl StoreV2 {
582
584
  doc_comment: row.get(9)?,
583
585
  attributes: row.get(10)?,
584
586
  metadata: row.get(11)?,
587
+ project_root,
585
588
  created_at: DateTime::from_timestamp(created_timestamp, 0).unwrap_or_default(),
586
589
  updated_at: DateTime::from_timestamp(updated_timestamp, 0).unwrap_or_default(),
587
590
  })
@@ -664,11 +667,12 @@ mod tests {
664
667
  doc_comment: Some("Test function".to_string()),
665
668
  attributes: None,
666
669
  metadata: None,
670
+ project_root: "/test/project".to_string(),
667
671
  created_at: Utc::now(),
668
672
  updated_at: Utc::now(),
669
673
  };
670
674
 
671
- let entity_id = store.insert_entity(&entity)?;
675
+ let entity_id = store.insert_entity(&entity, "/test/project")?;
672
676
  assert!(entity_id > 0);
673
677
 
674
678
  // Retrieve entity
@@ -225,10 +225,11 @@ impl StoreV2WithTx {
225
225
  // Execute the callback
226
226
  f(&tx)?;
227
227
 
228
- // Record file hash
228
+ // Record file hash with current timestamp
229
+ let current_time = chrono::Utc::now().timestamp();
229
230
  tx.execute(
230
- "INSERT INTO file_hashes (file_path, file_hash) VALUES (?, ?)",
231
- params![file_path, file_hash],
231
+ "INSERT INTO file_hashes (file_path, file_hash, indexed_at) VALUES (?, ?, ?)",
232
+ params![file_path, file_hash, current_time],
232
233
  )?;
233
234
 
234
235
  tx.commit()
@@ -308,6 +309,7 @@ mod tests {
308
309
  doc_comment: None,
309
310
  attributes: None,
310
311
  metadata: None,
312
+ project_root: "/test/project".to_string(),
311
313
  created_at: Utc::now(),
312
314
  updated_at: Utc::now(),
313
315
  }).collect();
@@ -44,6 +44,7 @@ fn test_atomic_file_indexing_with_rollback() -> Result<()> {
44
44
  doc_comment: None,
45
45
  attributes: None,
46
46
  metadata: None,
47
+ project_root: "/test/project".to_string(),
47
48
  created_at: chrono::Utc::now(),
48
49
  updated_at: chrono::Utc::now(),
49
50
  };
@@ -67,6 +68,7 @@ fn test_atomic_file_indexing_with_rollback() -> Result<()> {
67
68
  doc_comment: None,
68
69
  attributes: None,
69
70
  metadata: None,
71
+ project_root: "/test/project".to_string(),
70
72
  created_at: chrono::Utc::now(),
71
73
  updated_at: chrono::Utc::now(),
72
74
  };
@@ -84,6 +86,7 @@ fn test_atomic_file_indexing_with_rollback() -> Result<()> {
84
86
  doc_comment: None,
85
87
  attributes: None,
86
88
  metadata: None,
89
+ project_root: "/test/project".to_string(),
87
90
  created_at: chrono::Utc::now(),
88
91
  updated_at: chrono::Utc::now(),
89
92
  };
@@ -92,8 +95,8 @@ fn test_atomic_file_indexing_with_rollback() -> Result<()> {
92
95
  r#"
93
96
  INSERT INTO entities (
94
97
  kind, name, signature, visibility, parent_id, file_path,
95
- line_number, column_number, doc_comment, attributes, metadata
96
- ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)
98
+ line_number, column_number, doc_comment, attributes, metadata, project_root
99
+ ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)
97
100
  "#
98
101
  )?;
99
102
 
@@ -109,6 +112,7 @@ fn test_atomic_file_indexing_with_rollback() -> Result<()> {
109
112
  entity1.doc_comment,
110
113
  entity1.attributes,
111
114
  entity1.metadata,
115
+ entity1.project_root,
112
116
  ])?;
113
117
 
114
118
  stmt.execute(params![
@@ -123,6 +127,7 @@ fn test_atomic_file_indexing_with_rollback() -> Result<()> {
123
127
  entity2.doc_comment,
124
128
  entity2.attributes,
125
129
  entity2.metadata,
130
+ entity2.project_root,
126
131
  ])?;
127
132
 
128
133
  Ok(())
@@ -220,6 +225,7 @@ fn test_batch_insert_rollback() -> Result<()> {
220
225
  doc_comment: None,
221
226
  attributes: None,
222
227
  metadata: None,
228
+ project_root: "/test/project".to_string(),
223
229
  created_at: chrono::Utc::now(),
224
230
  updated_at: chrono::Utc::now(),
225
231
  });
package/CLAUDE.md CHANGED
@@ -24,7 +24,8 @@ Purpose: concise reference for CFN agents. Focus on persona, mandatory rules, ed
24
24
  - Do not run tests inside agents; run once via coordinator/main chat, agents read results.
25
25
  - Never save to project root; use appropriate subdirectories.
26
26
  - Never hardcode secrets; always redact as `[REDACTED]`.
27
- - Prefer `rg`/`grep` over `find`; when monitoring, sleep-check-sleep loops.
27
+ - Use RuVector SQL for indexed projects (400x faster than grep); use grep only for non-indexed projects or non-code strings.
28
+ - When monitoring, sleep-check-sleep loops.
28
29
  - All agent communication must use coordination protocols; no ad-hoc file coordination.
29
30
 
30
31
  ## 3) Cerebras MCP & Context Discovery Protocols
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-flow-novice",
3
- "version": "2.18.16",
3
+ "version": "2.18.18",
4
4
  "description": "Claude Flow Novice - Advanced orchestration platform for multi-agent AI workflows with CFN Loop architecture\n\nIncludes Local RuVector Accelerator and all CFN skills for complete functionality.",
5
5
  "main": "index.js",
6
6
  "type": "module",