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.
- package/.claude/agents/cfn-dev-team/analysts/analyst.md +9 -0
- package/.claude/agents/cfn-dev-team/analysts/root-cause-analyst.md +9 -0
- package/.claude/agents/cfn-dev-team/architecture/api-designer-persona.md +10 -1
- package/.claude/agents/cfn-dev-team/architecture/base-template-generator.md +9 -0
- package/.claude/agents/cfn-dev-team/architecture/goal-planner.md +10 -1
- package/.claude/agents/cfn-dev-team/architecture/planner.md +9 -0
- package/.claude/agents/cfn-dev-team/architecture/system-architect.md +9 -0
- package/.claude/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +9 -0
- package/.claude/agents/cfn-dev-team/coordinators/consensus-builder.md +10 -1
- package/.claude/agents/cfn-dev-team/coordinators/handoff-coordinator.md +9 -0
- package/.claude/agents/cfn-dev-team/coordinators/multi-sprint-coordinator.md +9 -0
- package/.claude/agents/cfn-dev-team/dev-ops/devops-engineer.md +9 -0
- package/.claude/agents/cfn-dev-team/dev-ops/docker-specialist.md +9 -0
- package/.claude/agents/cfn-dev-team/dev-ops/github-commit-agent.md +9 -0
- package/.claude/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +9 -0
- package/.claude/agents/cfn-dev-team/developers/api-gateway-specialist.md +9 -0
- package/.claude/agents/cfn-dev-team/developers/data/data-engineer.md +9 -0
- package/.claude/agents/cfn-dev-team/developers/database/database-architect.md +9 -0
- package/.claude/agents/cfn-dev-team/developers/database/supabase-specialist.md +9 -0
- package/.claude/agents/cfn-dev-team/developers/frontend/mobile-dev.md +10 -1
- package/.claude/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +10 -1
- package/.claude/agents/cfn-dev-team/developers/frontend/ui-designer.md +10 -1
- package/.claude/agents/cfn-dev-team/developers/graphql-specialist.md +9 -0
- package/.claude/agents/cfn-dev-team/developers/rust-developer.md +10 -1
- package/.claude/agents/cfn-dev-team/documentation/agent-type-guidelines.md +9 -0
- package/.claude/agents/cfn-dev-team/documentation/api-documentation.md +10 -1
- package/.claude/agents/cfn-dev-team/documentation/pseudocode.md +10 -1
- package/.claude/agents/cfn-dev-team/documentation/specification-agent.md +10 -1
- package/.claude/agents/cfn-dev-team/product-owners/accessibility-advocate-persona.md +10 -1
- package/.claude/agents/cfn-dev-team/product-owners/cto-agent.md +9 -0
- package/.claude/agents/cfn-dev-team/product-owners/power-user-persona.md +10 -1
- package/.claude/agents/cfn-dev-team/product-owners/product-owner.md +9 -0
- package/.claude/agents/cfn-dev-team/reviewers/quality/code-quality-validator.md +9 -0
- package/.claude/agents/cfn-dev-team/reviewers/quality/cyclomatic-complexity-reducer.md +9 -0
- package/.claude/agents/cfn-dev-team/reviewers/quality/perf-analyzer.md +9 -0
- package/.claude/agents/cfn-dev-team/reviewers/quality/performance-benchmarker.md +10 -1
- package/.claude/agents/cfn-dev-team/reviewers/quality/quality-metrics.md +9 -0
- package/.claude/agents/cfn-dev-team/testers/api-testing-specialist.md +9 -0
- package/.claude/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +9 -0
- package/.claude/agents/cfn-dev-team/testers/contract-tester.md +9 -0
- package/.claude/agents/cfn-dev-team/testers/e2e/playwright-tester.md +9 -0
- package/.claude/agents/cfn-dev-team/testers/integration-tester.md +9 -0
- package/.claude/agents/cfn-dev-team/testers/interaction-tester.md +9 -0
- package/.claude/agents/cfn-dev-team/testers/load-testing-specialist.md +9 -0
- package/.claude/agents/cfn-dev-team/testers/mutation-testing-specialist.md +9 -0
- package/.claude/agents/cfn-dev-team/testers/playwright-tester.md +10 -1
- package/.claude/agents/cfn-dev-team/testers/unit/tdd-london-unit-swarm.md +10 -1
- package/.claude/agents/cfn-dev-team/testers/validation/validation-production-validator.md +10 -1
- package/.claude/agents/cfn-dev-team/testing/test-validation-agent.md +9 -0
- package/.claude/agents/cfn-dev-team/utility/agent-builder.md +10 -1
- package/.claude/agents/cfn-dev-team/utility/context-curator.md +9 -0
- package/.claude/agents/cfn-dev-team/utility/memory-leak-specialist.md +9 -0
- package/.claude/agents/cfn-dev-team/utility/researcher.md +9 -0
- package/.claude/agents/cfn-dev-team/utility/z-ai-specialist.md +9 -0
- package/.claude/hooks/SessionStart-cfn-build-ruvector.sh +12 -0
- package/.claude/hooks/SessionStart:cfn-build-ruvector.sh +28 -0
- package/.claude/skills/bulk-add-ruvector-instructions.sh +34 -46
- package/.claude/skills/cfn-local-ruvector-accelerator/.claude/hooks/SessionStart-cfn-build-ruvector.sh +12 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/SKILL.md +112 -6
- package/.claude/skills/cfn-local-ruvector-accelerator/src/cli/index.rs +46 -11
- package/.claude/skills/cfn-local-ruvector-accelerator/src/cli/index_ast.rs +13 -1
- package/.claude/skills/cfn-local-ruvector-accelerator/src/embeddings.rs +4 -7
- package/.claude/skills/cfn-local-ruvector-accelerator/src/extractors/mod.rs +1 -1
- package/.claude/skills/cfn-local-ruvector-accelerator/src/migration_v2.rs +7 -7
- package/.claude/skills/cfn-local-ruvector-accelerator/src/query_api.rs +6 -3
- package/.claude/skills/cfn-local-ruvector-accelerator/src/schema_v2.rs +16 -1
- package/.claude/skills/cfn-local-ruvector-accelerator/src/store_v2.rs +7 -3
- package/.claude/skills/cfn-local-ruvector-accelerator/src/store_v2_tx.rs +5 -3
- package/.claude/skills/cfn-local-ruvector-accelerator/src/transaction_tests.rs +8 -2
- package/CLAUDE.md +2 -1
- package/package.json +1 -1
|
@@ -1,8 +1,114 @@
|
|
|
1
|
-
#
|
|
2
|
-
./target/release/local-ruvector init
|
|
1
|
+
# RuVector Local Semantic Code Search
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
./target/release/local-ruvector index --path /path/to/project --types rs
|
|
3
|
+
## WHEN TO USE THIS SKILL
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
|
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
|
|
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::<_,
|
|
314
|
-
row.get::<_, i64>(13)?, //
|
|
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
|
|
562
|
-
let
|
|
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
|
-
-
|
|
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.
|
|
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",
|