-
Notifications
You must be signed in to change notification settings - Fork 605
feat(mcp): minimal mem_find_project tool #661
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1611,7 +1611,7 @@ func TestResolveToolsAgentProfile(t *testing.T) { | |
| } | ||
|
|
||
| expectedTools := []string{ | ||
| "mem_save", "mem_search", "mem_context", "mem_session_summary", | ||
| "mem_save", "mem_search", "mem_find_project", "mem_context", "mem_session_summary", | ||
| "mem_session_start", "mem_session_end", "mem_get_observation", | ||
| "mem_suggest_topic_key", "mem_capture_passive", "mem_save_prompt", | ||
| "mem_update", // skills explicitly say "use mem_update when you have an exact ID to correct" | ||
|
|
@@ -2254,7 +2254,7 @@ func TestNewServerWithToolsNilRegistersAll(t *testing.T) { | |
| tools := srv.ListTools() | ||
|
|
||
| allTools := []string{ | ||
| "mem_save", "mem_search", "mem_context", "mem_session_summary", | ||
| "mem_save", "mem_search", "mem_find_project", "mem_context", "mem_session_summary", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win Test The new assertions only prove the tool is listed. Add deterministic handler tests for a successful search result, missing query, invalid As per path instructions, 🤖 Prompt for AI AgentsSource: Path instructions |
||
| "mem_session_start", "mem_session_end", "mem_get_observation", | ||
| "mem_suggest_topic_key", "mem_capture_passive", "mem_save_prompt", | ||
| "mem_update", "mem_delete", "mem_stats", "mem_timeline", "mem_merge_projects", | ||
|
|
@@ -2364,14 +2364,14 @@ func TestNewServerBackwardsCompatible(t *testing.T) { | |
| srv := NewServer(s) | ||
| tools := srv.ListTools() | ||
|
|
||
| // 18 agent + 4 admin = 22 total. | ||
| if len(tools) != 22 { | ||
| t.Errorf("NewServer should register all 22 tools, got %d", len(tools)) | ||
| // 19 agent + 4 admin = 23 total. | ||
| if len(tools) != 23 { | ||
| t.Errorf("NewServer should register all 23 tools, got %d", len(tools)) | ||
| } | ||
| } | ||
|
|
||
| func TestProfileConsistency(t *testing.T) { | ||
| // Verify that agent + admin = all 22 tools | ||
| // Verify that agent + admin = all 23 tools | ||
| combined := make(map[string]bool) | ||
| for tool := range ProfileAgent { | ||
| combined[tool] = true | ||
|
|
@@ -2380,9 +2380,9 @@ func TestProfileConsistency(t *testing.T) { | |
| combined[tool] = true | ||
| } | ||
|
|
||
| // 18 agent + 4 admin = 22 total. | ||
| if len(combined) != 22 { | ||
| t.Errorf("agent + admin should cover all 22 tools, got %d", len(combined)) | ||
| // 19 agent + 4 admin = 23 total. | ||
| if len(combined) != 23 { | ||
| t.Errorf("agent + admin should cover all 23 tools, got %d", len(combined)) | ||
| } | ||
|
|
||
| // Verify no overlap between profiles | ||
|
|
@@ -2710,9 +2710,9 @@ func TestNewServerWithConfig(t *testing.T) { | |
| t.Fatal("expected MCP server instance") | ||
| } | ||
| tools := srv.ListTools() | ||
| // Should have all 22 tools (18 agent + 4 admin). | ||
| if len(tools) != 22 { | ||
| t.Errorf("NewServerWithConfig should register all 22 tools, got %d", len(tools)) | ||
| // Should have all 23 tools (19 agent + 4 admin). | ||
| if len(tools) != 23 { | ||
| t.Errorf("NewServerWithConfig should register all 23 tools, got %d", len(tools)) | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8830,3 +8830,77 @@ func TestSanitizeFTS(t *testing.T) { | |
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestSearchProjects(t *testing.T) { | ||
| st := newTestStore(t) | ||
|
|
||
| st.CreateSession("s1", "project-a", "/tmp/a") | ||
| st.CreateSession("s2", "project-b", "/tmp/b") | ||
| st.CreateSession("s3", "project-c", "/tmp/c") | ||
|
|
||
| // Seed 3 observations for project A | ||
| _, err := st.AddObservation(AddObservationParams{ | ||
| SessionID: "s1", Type: "bugfix", Title: "Fix auth token expiration", | ||
| Content: "The auth middleware was dropping tokens", Project: "project-a", | ||
| }) | ||
| if err != nil { t.Fatal(err) } | ||
| _, err = st.AddObservation(AddObservationParams{ | ||
| SessionID: "s1", Type: "bugfix", Title: "Auth token validation", | ||
| Content: "Middleware should validate auth tokens", Project: "project-a", | ||
| }) | ||
| if err != nil { t.Fatal(err) } | ||
| _, err = st.AddObservation(AddObservationParams{ | ||
| SessionID: "s1", Type: "bugfix", Title: "Minor fix", | ||
| Content: "Just a minor auth fix in the middleware", Project: "project-a", | ||
| }) | ||
| if err != nil { t.Fatal(err) } | ||
|
|
||
| // Seed 1 highly relevant observation for project B | ||
| _, err = st.AddObservation(AddObservationParams{ | ||
| SessionID: "s2", Type: "bugfix", Title: "Auth middleware completely rewritten", | ||
| Content: "Auth middleware auth middleware auth middleware tokens", Project: "project-b", | ||
| }) | ||
| if err != nil { t.Fatal(err) } | ||
|
|
||
| // Seed an irrelevant observation for project C | ||
| _, err = st.AddObservation(AddObservationParams{ | ||
| SessionID: "s3", Type: "feature", Title: "Database migration", | ||
| Content: "Added new tables", Project: "project-c", | ||
| }) | ||
| if err != nil { t.Fatal(err) } | ||
|
|
||
| // Force FTS sync if async (test setup normally does this, but just in case) | ||
| // We'll just search directly. | ||
|
|
||
| matches, err := st.SearchProjects("auth middleware", "all", 10) | ||
| if err != nil { | ||
| t.Fatalf("SearchProjects failed: %v", err) | ||
| } | ||
|
|
||
| if len(matches) != 2 { | ||
| t.Fatalf("Expected 2 projects, got %d: %+v", len(matches), matches) | ||
| } | ||
|
|
||
| // project-a should have 3 matches. project-b should have 1 match. | ||
| // project-b has more occurrences of the terms, so its top_rank might be better (more negative). | ||
| // Let's assert on the project names and counts. | ||
| hasA := false | ||
| hasB := false | ||
| for _, m := range matches { | ||
| if m.Project == "project-a" { | ||
| hasA = true | ||
| if m.MatchCount != 3 { | ||
| t.Errorf("project-a: expected 3 matches, got %d", m.MatchCount) | ||
| } | ||
| } | ||
| if m.Project == "project-b" { | ||
| hasB = true | ||
| if m.MatchCount != 1 { | ||
| t.Errorf("project-b: expected 1 match, got %d", m.MatchCount) | ||
| } | ||
| } | ||
| } | ||
| if !hasA || !hasB { | ||
| t.Errorf("Missing expected projects in results: %+v", matches) | ||
| } | ||
| } | ||
|
Comment on lines
+8834
to
+8906
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win Cover This only tests normal As per path instructions, 🤖 Prompt for AI AgentsSource: Path instructions |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: Gentleman-Programming/engram
Length of output: 296
🏁 Script executed:
Repository: Gentleman-Programming/engram
Length of output: 22281
🏁 Script executed:
Repository: Gentleman-Programming/engram
Length of output: 648
🏁 Script executed:
Repository: Gentleman-Programming/engram
Length of output: 5421
Preserve usable
TopRankprecision.%.2fcan collapse nearby computed ranks to the same displayed value, somem_find_projectdoes not return the actualTopRank. Use a precision-preserving format or structured numeric metadata.Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents