Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions internal/semantic/commands/collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ func TestDeriveCollectionFromPath(t *testing.T) {
}{
{
name: "simple path with code suffix",
path: ".llm-index/code",
path: ".index/code",
expected: "code",
},
{
name: "simple path with docs suffix",
path: ".llm-index/docs",
path: ".index/docs",
expected: "docs",
},
{
Expand All @@ -28,17 +28,17 @@ func TestDeriveCollectionFromPath(t *testing.T) {
},
{
name: "path with trailing slash",
path: ".llm-index/code/",
path: ".index/code/",
expected: "code",
},
{
name: "default .llm-index should return empty",
path: ".llm-index",
name: "default .index should return empty",
path: ".index",
expected: "",
},
{
name: "llm-index without dot should return empty",
path: "llm-index",
name: "index without dot should return empty",
path: "index",
expected: "",
},
{
Expand All @@ -48,27 +48,27 @@ func TestDeriveCollectionFromPath(t *testing.T) {
},
{
name: "path with hyphen converted to underscore",
path: ".llm-index/my-collection",
path: ".index/my-collection",
expected: "my_collection",
},
{
name: "path with dot converted to underscore",
path: ".llm-index/v1.0",
path: ".index/v1.0",
expected: "v1_0",
},
{
name: "path starting with number gets prefix",
path: ".llm-index/123test",
path: ".index/123test",
expected: "idx_123test",
},
{
name: "alphanumeric path unchanged",
path: ".llm-index/MyProject2024",
path: ".index/MyProject2024",
expected: "MyProject2024",
},
{
name: "path with underscores preserved",
path: ".llm-index/my_project_code",
path: ".index/my_project_code",
expected: "my_project_code",
},
{
Expand All @@ -78,7 +78,7 @@ func TestDeriveCollectionFromPath(t *testing.T) {
},
{
name: "special characters removed",
path: ".llm-index/test@project#1",
path: ".index/test@project#1",
expected: "testproject1",
},
}
Expand Down Expand Up @@ -108,7 +108,7 @@ func TestResolveCollectionName(t *testing.T) {

t.Run("priority 1: explicit collection flag", func(t *testing.T) {
collectionName = "explicit_collection"
indexDir = ".llm-index/code"
indexDir = ".index/code"
os.Setenv("QDRANT_COLLECTION", "env_collection")

result := resolveCollectionName()
Expand All @@ -119,7 +119,7 @@ func TestResolveCollectionName(t *testing.T) {

t.Run("priority 2: derive from index-dir", func(t *testing.T) {
collectionName = ""
indexDir = ".llm-index/myproject"
indexDir = ".index/myproject"
os.Setenv("QDRANT_COLLECTION", "env_collection")

result := resolveCollectionName()
Expand All @@ -130,7 +130,7 @@ func TestResolveCollectionName(t *testing.T) {

t.Run("priority 3: environment variable", func(t *testing.T) {
collectionName = ""
indexDir = ".llm-index" // default, won't derive
indexDir = ".index" // default, won't derive
os.Setenv("QDRANT_COLLECTION", "env_collection")

result := resolveCollectionName()
Expand All @@ -141,7 +141,7 @@ func TestResolveCollectionName(t *testing.T) {

t.Run("priority 4: default llm_semantic", func(t *testing.T) {
collectionName = ""
indexDir = ".llm-index"
indexDir = ".index"
os.Unsetenv("QDRANT_COLLECTION")

result := resolveCollectionName()
Expand All @@ -163,7 +163,7 @@ func TestResolveCollectionName(t *testing.T) {

t.Run("default index-dir with no env uses default", func(t *testing.T) {
collectionName = ""
indexDir = ".llm-index"
indexDir = ".index"
os.Unsetenv("QDRANT_COLLECTION")

result := resolveCollectionName()
Expand All @@ -188,7 +188,7 @@ func TestResolveCollectionName_EdgeCases(t *testing.T) {

t.Run("whitespace-only collection name treated as empty", func(t *testing.T) {
collectionName = " "
indexDir = ".llm-index/code"
indexDir = ".index/code"
os.Unsetenv("QDRANT_COLLECTION")

result := resolveCollectionName()
Expand Down Expand Up @@ -250,7 +250,7 @@ func TestIndexDirFlag_InRootCmd(t *testing.T) {
t.Fatal("--index-dir flag not found in root command")
}

if flag.DefValue != ".llm-index" {
t.Errorf("--index-dir default should be '.llm-index', got %q", flag.DefValue)
if flag.DefValue != ".index" {
t.Errorf("--index-dir default should be '.index', got %q", flag.DefValue)
}
}
6 changes: 3 additions & 3 deletions internal/semantic/commands/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,18 +311,18 @@ func runCalibration(ctx context.Context, storage semantic.Storage, embedder sema

func resolveIndexPath(rootPath string) string {
// If custom index dir specified
if indexDir != "" && indexDir != ".llm-index" {
if indexDir != "" && indexDir != ".index" {
return filepath.Join(indexDir, "semantic.db")
}

// Try git root first
gitRoot, err := findGitRootFrom(rootPath)
if err == nil {
return filepath.Join(gitRoot, ".llm-index", "semantic.db")
return filepath.Join(gitRoot, ".index", "semantic.db")
}

// Fall back to the indexed directory
return filepath.Join(rootPath, ".llm-index", "semantic.db")
return filepath.Join(rootPath, ".index", "semantic.db")
}

func findGitRootFrom(startPath string) (string, error) {
Expand Down
23 changes: 21 additions & 2 deletions internal/semantic/commands/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func runMemoryStore(ctx context.Context, opts memoryStoreOpts) error {
indexPath = findIndexPath()
if indexPath == "" {
// Create default index path
indexPath = ".llm-index/semantic.db"
indexPath = ".index/semantic.db"
}
}

Expand Down Expand Up @@ -277,6 +277,25 @@ func runMemorySearch(ctx context.Context, opts memorySearchOpts) error {
return fmt.Errorf("search failed: %w", err)
}

// Track retrieval stats (automatic for memory profile)
if len(results) > 0 {
if tracker, ok := storage.(semantic.MemoryStatsTracker); ok {
// Build retrieval batch
retrievals := make([]semantic.MemoryRetrieval, len(results))
for i, r := range results {
retrievals[i] = semantic.MemoryRetrieval{
MemoryID: r.Entry.ID,
Score: r.Score,
}
}
// Track in background - don't fail search if tracking fails
if err := tracker.TrackMemoryRetrievalBatch(ctx, retrievals, opts.query); err != nil {
// Log error but don't fail the search
fmt.Fprintf(os.Stderr, "Warning: failed to track retrieval stats: %v\n", err)
}
}
}

// Output results
if opts.jsonOutput || opts.minOutput {
return outputMemoryJSON(results, opts.minOutput)
Expand Down Expand Up @@ -586,7 +605,7 @@ func runMemoryImport(ctx context.Context, opts memoryImportOpts) error {
indexPath = findIndexPath()
if indexPath == "" {
// Create default index path
indexPath = ".llm-index/semantic.db"
indexPath = ".index/semantic.db"
}
}

Expand Down
14 changes: 7 additions & 7 deletions internal/semantic/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ Supports any OpenAI-compatible embedding API (Ollama, vLLM, OpenAI, Azure, etc.)
rootCmd.PersistentFlags().StringVar(&apiURL, "api-url", getDefaultAPIURL(), "Embedding API URL (OpenAI-compatible)")
rootCmd.PersistentFlags().StringVar(&model, "model", getDefaultModel(), "Embedding model name (or set LLM_SEMANTIC_MODEL env var)")
rootCmd.PersistentFlags().StringVar(&apiKey, "api-key", "", "API key (or set LLM_SEMANTIC_API_KEY env var)")
rootCmd.PersistentFlags().StringVar(&indexDir, "index-dir", ".llm-index", "Directory for semantic index")
rootCmd.PersistentFlags().StringVar(&indexDir, "index-dir", ".index", "Directory for semantic index")
rootCmd.PersistentFlags().StringVar(&storageType, "storage", "sqlite", "Storage backend: sqlite (default) or qdrant")
rootCmd.PersistentFlags().StringVar(&collectionName, "collection", "", "Qdrant collection name (default: QDRANT_COLLECTION env or 'llm_semantic')")
rootCmd.PersistentFlags().StringVar(&embedderType, "embedder", "openai", "Embedding provider: openai (default), cohere, huggingface, openrouter")
Expand Down Expand Up @@ -128,7 +128,7 @@ func getAPIKey() string {
// resolveCollectionName returns the Qdrant collection name using this priority:
// 1. --collection flag if specified
// 2. Profile-specific config value (e.g., code_collection)
// 3. Derived from --index-dir (e.g., ".llm-index/code" → "code", ".llm-index/docs" → "docs")
// 3. Derived from --index-dir (e.g., ".index/code" → "code", ".index/docs" → "docs")
// 4. QDRANT_COLLECTION environment variable
// 5. Default: "llm_semantic"
func resolveCollectionName() string {
Expand All @@ -146,9 +146,9 @@ func resolveCollectionName() string {
}

// Priority 3: derive from index-dir if non-default
if indexDir != "" && indexDir != ".llm-index" {
if indexDir != "" && indexDir != ".index" {
// Extract the last path component as collection name
// e.g., ".llm-index/code" → "code", "indexes/docs" → "docs"
// e.g., ".index/code" → "code", "indexes/docs" → "docs"
derived := deriveCollectionFromPath(indexDir)
if derived != "" {
return derived
Expand Down Expand Up @@ -204,8 +204,8 @@ func deriveCollectionFromPath(path string) string {
name = path
}

// Skip if it's just ".llm-index" or similar default
if name == ".llm-index" || name == "llm-index" || name == "" {
// Skip if it's just ".index" or similar default
if name == ".index" || name == "index" || name == "" {
return ""
}

Expand Down Expand Up @@ -345,7 +345,7 @@ func ResetGlobalsForTesting() {
apiURL = getDefaultAPIURL()
model = getDefaultModel()
apiKey = ""
indexDir = ".llm-index"
indexDir = ".index"
storageType = "sqlite"
collectionName = ""
embedderType = "openai"
Expand Down
6 changes: 3 additions & 3 deletions internal/semantic/commands/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,15 +237,15 @@ func findIndexPath() string {
}
}

// Try .llm-index in current directory
path := filepath.Join(".llm-index", "semantic.db")
// Try .index in current directory
path := filepath.Join(".index", "semantic.db")
if _, err := os.Stat(path); err == nil {
return path
}

// Try to find git root and check there
if gitRoot, err := findGitRoot(); err == nil {
path := filepath.Join(gitRoot, ".llm-index", "semantic.db")
path := filepath.Join(gitRoot, ".index", "semantic.db")
if _, err := os.Stat(path); err == nil {
return path
}
Expand Down
2 changes: 1 addition & 1 deletion internal/semantic/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func ErrStorageFailure(operation string, cause error) *SemanticError {
Type: ErrTypeUnknown,
Message: fmt.Sprintf("storage %s failed", operation),
Cause: cause,
Hint: "The index may be corrupted. Try: rm -rf .llm-index && llm-semantic index .",
Hint: "The index may be corrupted. Try: rm -rf .index && llm-semantic index .",
}
}

Expand Down
Loading
Loading