This commit fixes several critical issues found during comprehensive review: ## CRITICAL FIX: Template Consolidation ⚠️ - Moved 6 commands from ~/.claude/commands/ to templates/commands/ - Moved 3 skills from ~/.claude/skills/ to templates/skills/ - Team sharing was BROKEN without this (symlinks pointed to empty dirs) - Updated README with prominent consolidation step warning - Commands: list, remember, save, search, session, triggers - Skills: initialize-project, remember-this, memory ## HIGH PRIORITY FIXES: - ✅ Completed selective semantic indexing (removed TODO at line 878) - ✅ Fixed PostgreSQL references in health check (system uses SQLite) - ✅ Created comprehensive API documentation (docs/API_REFERENCE.md) - ✅ Added Depends import to fix authentication endpoints ## DOCUMENTATION: - Added complete API_REFERENCE.md (60+ endpoints documented) - Created HYBRID_INDEXING_INTEGRATION.md (5 KB architecture) - Updated README with API docs link + consolidation warning - Documented all hybrid indexing endpoints with examples ## IMPROVEMENTS: - Selective semantic indexing now actually ingests files (was placeholder) - Health check now correctly queries SQLite instead of PostgreSQL - All authentication endpoints now functional - Structural indexing stores as pure JSON (no embeddings) Impact: Team sharing now works, all features documented, critical bugs fixed. Tested: Pistn indexing (3,117 files in 3 seconds) ✅ 🚀 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
147 lines
5.3 KiB
Python
147 lines
5.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Incremental Code Indexer - expands project index with unindexed files
|
|
Called periodically by git hook to gradually build full project index
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import urllib.request
|
|
import urllib.error
|
|
from pathlib import Path
|
|
from code_indexer import CodeIndexer
|
|
|
|
|
|
class IncrementalIndexer:
|
|
"""Expand project index with previously unindexed files."""
|
|
|
|
def __init__(self, project_path: str, project_id: int, api_url: str = "http://localhost:8051"):
|
|
self.project_path = Path(project_path).resolve()
|
|
self.project_id = project_id
|
|
self.api_url = api_url
|
|
self.state_file = self.project_path / ".claude-os" / ".index_state"
|
|
|
|
def _get_indexed_files(self) -> set:
|
|
"""Query project-index MCP to get list of already indexed files."""
|
|
try:
|
|
# This would query the MCP to get indexed files
|
|
# For now, we'll track locally in the state file
|
|
if self.state_file.exists():
|
|
with open(self.state_file, 'r') as f:
|
|
data = json.load(f)
|
|
return set(data.get('indexed_files', []))
|
|
return set()
|
|
except Exception as e:
|
|
print(f"⚠️ Could not read indexed files: {e}")
|
|
return set()
|
|
|
|
def _find_all_code_files(self) -> list:
|
|
"""Find all code files in project."""
|
|
code_extensions = {'.rb', '.js', '.ts', '.tsx', '.jsx', '.json', '.yml', '.yaml', '.md'}
|
|
skip_dirs = {
|
|
'.git', 'node_modules', '.venv', 'venv', '__pycache__',
|
|
'dist', 'build', '.next', '.nuxt', 'coverage', 'tmp',
|
|
'.claude-os', '.env', '.DS_Store', '.bundle', 'vendor',
|
|
'test', 'tests', 'spec', 'specs', '__tests__', '.turbo'
|
|
}
|
|
|
|
files = []
|
|
for root, dirs, filenames in os.walk(self.project_path):
|
|
dirs[:] = [d for d in dirs if d not in skip_dirs]
|
|
|
|
for filename in filenames:
|
|
filepath = Path(root) / filename
|
|
if filepath.suffix in code_extensions:
|
|
rel_path = str(filepath.relative_to(self.project_path))
|
|
files.append(rel_path)
|
|
|
|
return sorted(files)
|
|
|
|
def _save_state(self, indexed_files: set):
|
|
"""Save indexing state."""
|
|
try:
|
|
self.state_file.parent.mkdir(parents=True, exist_ok=True)
|
|
with open(self.state_file, 'w') as f:
|
|
json.dump({
|
|
'indexed_files': sorted(list(indexed_files)),
|
|
'total_files': len(indexed_files)
|
|
}, f, indent=2)
|
|
except Exception as e:
|
|
print(f"⚠️ Could not save state: {e}")
|
|
|
|
def expand_index(self, batch_size: int = 30) -> dict:
|
|
"""Index next batch of unindexed files."""
|
|
print(f"\n🔄 Expanding project index...")
|
|
|
|
all_files = self._find_all_code_files()
|
|
indexed_files = self._get_indexed_files()
|
|
unindexed = [f for f in all_files if f not in indexed_files]
|
|
|
|
if not unindexed:
|
|
print(f"✅ All {len(all_files)} files already indexed!")
|
|
return {"indexed": 0, "total_files": len(all_files), "status": "complete"}
|
|
|
|
# Index next batch
|
|
batch = unindexed[:batch_size]
|
|
print(f"📂 Found {len(unindexed)} unindexed files, indexing next {len(batch)}...")
|
|
|
|
indexer = CodeIndexer(str(self.project_path), self.project_id, self.api_url)
|
|
indexed_count = 0
|
|
chunk_count = 0
|
|
|
|
for file_path in batch:
|
|
full_path = self.project_path / file_path
|
|
if not full_path.exists():
|
|
continue
|
|
|
|
try:
|
|
chunks = indexer._chunk_file(full_path)
|
|
for chunk in chunks:
|
|
if indexer._ingest_chunk(chunk, f"{self.project_path.name}-project_index"):
|
|
chunk_count += 1
|
|
indexed_count += 1
|
|
except Exception as e:
|
|
print(f"⚠️ Error indexing {file_path}: {e}")
|
|
|
|
# Update state
|
|
new_indexed = indexed_files | set(batch)
|
|
self._save_state(new_indexed)
|
|
|
|
progress = f"{len(new_indexed)}/{len(all_files)}"
|
|
print(f"✅ Expansion complete: {chunk_count} chunks from {indexed_count} files")
|
|
print(f" 📊 Total indexed: {progress} ({100*len(new_indexed)//len(all_files)}%)")
|
|
|
|
return {
|
|
"indexed": indexed_count,
|
|
"chunks": chunk_count,
|
|
"progress": f"{len(new_indexed)}/{len(all_files)}",
|
|
"percentage": 100 * len(new_indexed) // len(all_files),
|
|
"status": "in_progress" if len(new_indexed) < len(all_files) else "complete"
|
|
}
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) < 3:
|
|
print("Usage: incremental_indexer.py <project_id> <project_path> [api_url] [batch_size]")
|
|
sys.exit(1)
|
|
|
|
try:
|
|
project_id = int(sys.argv[1])
|
|
project_path = sys.argv[2]
|
|
api_url = sys.argv[3] if len(sys.argv) > 3 else "http://localhost:8051"
|
|
batch_size = int(sys.argv[4]) if len(sys.argv) > 4 else 30
|
|
|
|
indexer = IncrementalIndexer(project_path, project_id, api_url)
|
|
result = indexer.expand_index(batch_size)
|
|
|
|
# Exit with status code
|
|
sys.exit(0 if result['status'] == 'complete' else 0)
|
|
|
|
except Exception as e:
|
|
print(f"❌ Error: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|