@gethmy/mcp 2.3.1 → 2.3.2

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 (34) hide show
  1. package/dist/lib/active-learning.js +939 -787
  2. package/dist/lib/api-client.js +2527 -644
  3. package/dist/lib/auto-session.js +177 -196
  4. package/dist/lib/cli.js +34954 -128
  5. package/dist/lib/config.js +235 -201
  6. package/dist/lib/consolidation.js +374 -289
  7. package/dist/lib/context-assembly.js +1265 -838
  8. package/dist/lib/graph-expansion.js +139 -155
  9. package/dist/lib/http.js +1917 -130
  10. package/dist/lib/index.js +29525 -5
  11. package/dist/lib/lifecycle-maintenance.js +663 -79
  12. package/dist/lib/memory-cleanup.js +1315 -409
  13. package/dist/lib/onboard.js +2588 -32
  14. package/dist/lib/prompt-builder.js +438 -445
  15. package/dist/lib/remote.js +31733 -143
  16. package/dist/lib/server.js +29388 -3229
  17. package/dist/lib/skills.js +315 -132
  18. package/dist/lib/tui/agents.js +128 -107
  19. package/dist/lib/tui/docs.js +1590 -687
  20. package/dist/lib/tui/setup.js +5698 -804
  21. package/dist/lib/tui/theme.js +183 -86
  22. package/dist/lib/tui/writer.js +1149 -176
  23. package/package.json +2 -2
  24. package/src/memory-cleanup.ts +2 -4
  25. package/dist/lib/__tests__/active-learning.test.js +0 -386
  26. package/dist/lib/__tests__/agent-performance-profiles.test.js +0 -325
  27. package/dist/lib/__tests__/auto-session.test.js +0 -661
  28. package/dist/lib/__tests__/context-assembly.test.js +0 -362
  29. package/dist/lib/__tests__/graph-expansion.test.js +0 -150
  30. package/dist/lib/__tests__/integration-memory-crud.test.js +0 -797
  31. package/dist/lib/__tests__/integration-memory-system.test.js +0 -281
  32. package/dist/lib/__tests__/lifecycle-maintenance.test.js +0 -207
  33. package/dist/lib/__tests__/pattern-detection.test.js +0 -295
  34. package/dist/lib/__tests__/prompt-builder.test.js +0 -418
@@ -1,325 +0,0 @@
1
- /**
2
- * Unit + integration tests for Agent Performance Profiles.
3
- *
4
- * Unit tests verify that runEndSessionPipeline correctly refreshes
5
- * the materialized view and upserts an "agent" knowledge entity.
6
- *
7
- * Integration tests (skipped when not configured) call the real API.
8
- *
9
- * Run with: bun test packages/mcp-server/src/__tests__/agent-performance-profiles.test.ts
10
- */
11
- import { describe, expect, mock, test } from "bun:test";
12
- // ============================================================
13
- // Module mocks (must be before imports)
14
- // ============================================================
15
- const mockWorkspaceId = "ws-test-123";
16
- const mockProjectId = "proj-test-456";
17
- mock.module("../config.js", () => ({
18
- getActiveWorkspaceId: () => mockWorkspaceId,
19
- getActiveProjectId: () => mockProjectId,
20
- isConfigured: () => true,
21
- loadConfig: () => { },
22
- getApiUrl: () => "http://localhost:54321",
23
- getApiKey: () => "test-key",
24
- getMemoryDir: () => null,
25
- getUserEmail: () => null,
26
- saveConfig: () => { },
27
- setActiveProject: () => { },
28
- setActiveWorkspace: () => { },
29
- resetClient: () => { },
30
- }));
31
- mock.module("../active-learning.js", () => ({
32
- extractLearnings: mock(async () => ({ count: 0, entityIds: [] })),
33
- extractMidSessionLearnings: mock(async () => ({ count: 0, entityIds: [] })),
34
- detectContradictions: mock(async () => ({ contradictions: [] })),
35
- }));
36
- mock.module("../context-assembly.js", () => ({
37
- recordContextFeedback: mock(async () => ({ adjusted: 0 })),
38
- assembleContext: mock(async () => ({ entities: [], markdown: "" })),
39
- cacheManifest: mock(() => { }),
40
- computeRelevanceScore: mock(() => 0),
41
- getCachedManifest: mock(() => null),
42
- mapToContextEntity: mock(() => ({})),
43
- trackSessionAssembly: mock(async () => ({})),
44
- }));
45
- mock.module("../lifecycle-maintenance.js", () => ({
46
- runLifecycleMaintenance: mock(async () => ({
47
- archived: 0,
48
- pruned: 0,
49
- promoted: 0,
50
- reviewed: 0,
51
- })),
52
- }));
53
- mock.module("../consolidation.js", () => ({
54
- consolidateMemories: mock(async () => ({ consolidated: 0 })),
55
- }));
56
- mock.module("../graph-expansion.js", () => ({
57
- autoExpandGraph: mock(async () => { }),
58
- }));
59
- // Import after mocking
60
- const { runEndSessionPipeline } = await import("../server.js");
61
- // ============================================================
62
- // Helpers
63
- // ============================================================
64
- const flush = () => new Promise((r) => setTimeout(r, 100));
65
- function makeMockClient() {
66
- const createdEntities = [];
67
- let nextId = 1;
68
- return {
69
- createdEntities,
70
- getCard: mock(async () => ({
71
- card: {
72
- title: "Test card",
73
- description: "desc",
74
- labels: [],
75
- subtasks: [],
76
- },
77
- })),
78
- removeLabelFromCard: mock(async () => ({ success: true })),
79
- createMemoryEntity: mock(async (data) => {
80
- const entity = { id: `entity-${nextId++}`, ...data };
81
- createdEntities.push(entity);
82
- return { entity };
83
- }),
84
- updateMemoryEntity: mock(async (entityId, updates) => ({
85
- entity: { id: entityId, ...updates },
86
- })),
87
- listMemoryEntities: mock(async () => ({
88
- entities: [],
89
- count: 0,
90
- })),
91
- searchMemoryEntities: mock(async () => ({
92
- entities: [],
93
- count: 0,
94
- })),
95
- getMemoryEntity: mock(async (id) => ({
96
- entity: { id, type: "agent" },
97
- })),
98
- deleteMemoryEntity: mock(async () => ({ success: true })),
99
- createMemoryRelation: mock(async () => ({ relation: {} })),
100
- refreshAgentProfiles: mock(async () => ({ refreshed: true })),
101
- getAgentProfile: mock(async () => ({
102
- profile: {
103
- agent_identifier: "claude-code",
104
- total_sessions: 10,
105
- completed_sessions: 8,
106
- paused_sessions: 1,
107
- blocked_sessions: 1,
108
- completion_rate_pct: 80,
109
- avg_active_duration_ms: 120000,
110
- avg_completion_progress: 95,
111
- first_session_at: "2026-01-01T00:00:00Z",
112
- last_session_at: "2026-03-05T00:00:00Z",
113
- },
114
- })),
115
- };
116
- }
117
- function makeDeps(overrides = {}) {
118
- return {
119
- getClient: () => null,
120
- isConfigured: () => true,
121
- getActiveProjectId: () => mockProjectId,
122
- getActiveWorkspaceId: () => overrides.workspaceId !== undefined
123
- ? overrides.workspaceId
124
- : mockWorkspaceId,
125
- setActiveProject: () => { },
126
- setActiveWorkspace: () => { },
127
- getApiUrl: () => "http://localhost",
128
- getMemoryDir: () => null,
129
- getUserEmail: () => null,
130
- saveConfig: () => { },
131
- resetClient: () => { },
132
- };
133
- }
134
- // ============================================================
135
- // Unit Tests
136
- // ============================================================
137
- describe("Agent profile refresh in runEndSessionPipeline", () => {
138
- test("creates new entity when none exists", async () => {
139
- const client = makeMockClient();
140
- const deps = makeDeps();
141
- await runEndSessionPipeline(client, deps, "card-1", "completed", 100, {
142
- agent_identifier: "claude-code",
143
- agent_name: "Claude Code",
144
- });
145
- await flush();
146
- expect(client.refreshAgentProfiles).toHaveBeenCalledWith(mockWorkspaceId);
147
- expect(client.getAgentProfile).toHaveBeenCalled();
148
- expect(client.createMemoryEntity).toHaveBeenCalled();
149
- const agentEntity = client.createdEntities.find((e) => e.type === "agent");
150
- expect(agentEntity).toBeTruthy();
151
- expect(agentEntity.memory_tier).toBe("reference");
152
- expect(agentEntity.confidence).toBe(1.0);
153
- expect(agentEntity.title).toBe("Agent Profile: claude-code");
154
- expect(agentEntity.tags).toContain("agent-profile");
155
- expect(agentEntity.tags).toContain("claude-code");
156
- expect(agentEntity.content).toContain("## claude-code Performance Profile");
157
- });
158
- test("updates existing entity by title match", async () => {
159
- const client = makeMockClient();
160
- client.listMemoryEntities = mock(async () => ({
161
- entities: [
162
- {
163
- id: "existing-1",
164
- title: "Agent Profile: claude-code",
165
- agent_identifier: "claude-code",
166
- },
167
- ],
168
- count: 1,
169
- }));
170
- const deps = makeDeps();
171
- await runEndSessionPipeline(client, deps, "card-1", "completed", 100, {
172
- agent_identifier: "claude-code",
173
- agent_name: "Claude Code",
174
- });
175
- await flush();
176
- expect(client.updateMemoryEntity).toHaveBeenCalledWith("existing-1", expect.objectContaining({
177
- content: expect.stringContaining("## claude-code Performance Profile"),
178
- confidence: 1.0,
179
- }));
180
- // Should NOT create a new entity
181
- const agentEntities = client.createdEntities.filter((e) => e.type === "agent");
182
- expect(agentEntities).toHaveLength(0);
183
- });
184
- test("updates existing entity by agent_identifier match", async () => {
185
- const client = makeMockClient();
186
- client.listMemoryEntities = mock(async () => ({
187
- entities: [
188
- {
189
- id: "existing-2",
190
- title: "Old Title",
191
- agent_identifier: "claude-code",
192
- },
193
- ],
194
- count: 1,
195
- }));
196
- const deps = makeDeps();
197
- await runEndSessionPipeline(client, deps, "card-1", "completed", 100, {
198
- agent_identifier: "claude-code",
199
- agent_name: "Claude Code",
200
- });
201
- await flush();
202
- expect(client.updateMemoryEntity).toHaveBeenCalledWith("existing-2", expect.objectContaining({
203
- confidence: 1.0,
204
- }));
205
- });
206
- test("skips when workspace is null", async () => {
207
- const client = makeMockClient();
208
- const deps = makeDeps({ workspaceId: null });
209
- await runEndSessionPipeline(client, deps, "card-1", "completed", 100, {
210
- agent_identifier: "claude-code",
211
- agent_name: "Claude Code",
212
- });
213
- await flush();
214
- expect(client.refreshAgentProfiles).not.toHaveBeenCalled();
215
- });
216
- test('skips when agent is "unknown"', async () => {
217
- const client = makeMockClient();
218
- const deps = makeDeps();
219
- await runEndSessionPipeline(client, deps, "card-1", "completed", 100, {
220
- agent_identifier: "unknown",
221
- agent_name: "Unknown",
222
- });
223
- await flush();
224
- expect(client.refreshAgentProfiles).not.toHaveBeenCalled();
225
- });
226
- test("skips when sessionData is undefined", async () => {
227
- const client = makeMockClient();
228
- const deps = makeDeps();
229
- await runEndSessionPipeline(client, deps, "card-1", "completed", 100);
230
- await flush();
231
- // agent_identifier defaults to "unknown" when sessionData is undefined
232
- expect(client.refreshAgentProfiles).not.toHaveBeenCalled();
233
- });
234
- test("non-fatal on refresh failure", async () => {
235
- const client = makeMockClient();
236
- client.refreshAgentProfiles = mock(async () => {
237
- throw new Error("Refresh service down");
238
- });
239
- const deps = makeDeps();
240
- // Should not throw
241
- const result = await runEndSessionPipeline(client, deps, "card-1", "completed", 100, {
242
- agent_identifier: "claude-code",
243
- agent_name: "Claude Code",
244
- });
245
- await flush();
246
- // Pipeline still returns normally
247
- expect(result).toBeTruthy();
248
- expect(typeof result.learningsExtracted).toBe("number");
249
- });
250
- test("null profile skips entity upsert", async () => {
251
- const client = makeMockClient();
252
- client.getAgentProfile = mock(async () => ({ profile: null }));
253
- const deps = makeDeps();
254
- await runEndSessionPipeline(client, deps, "card-1", "completed", 100, {
255
- agent_identifier: "claude-code",
256
- agent_name: "Claude Code",
257
- });
258
- await flush();
259
- expect(client.refreshAgentProfiles).toHaveBeenCalled();
260
- // No agent entity should be created since profile is null
261
- const agentEntities = client.createdEntities.filter((e) => e.type === "agent");
262
- expect(agentEntities).toHaveLength(0);
263
- });
264
- test("content has correct markdown", async () => {
265
- const client = makeMockClient();
266
- const deps = makeDeps();
267
- await runEndSessionPipeline(client, deps, "card-1", "completed", 100, {
268
- agent_identifier: "claude-code",
269
- agent_name: "Claude Code",
270
- });
271
- await flush();
272
- const agentEntity = client.createdEntities.find((e) => e.type === "agent");
273
- expect(agentEntity).toBeTruthy();
274
- const content = agentEntity.content;
275
- expect(content).toContain("## claude-code Performance Profile");
276
- expect(content).toContain("**Total sessions:**");
277
- expect(content).toContain("**Completed:**");
278
- expect(content).toContain("**Paused:**");
279
- expect(content).toContain("**Blocked:**");
280
- expect(content).toContain("**Avg duration:**");
281
- expect(content).toContain("**First session:**");
282
- expect(content).toContain("**Last session:**");
283
- });
284
- test("works with paused sessions", async () => {
285
- const client = makeMockClient();
286
- const deps = makeDeps();
287
- await runEndSessionPipeline(client, deps, "card-1", "paused", 50, {
288
- agent_identifier: "claude-code",
289
- agent_name: "Claude Code",
290
- });
291
- await flush();
292
- expect(client.refreshAgentProfiles).toHaveBeenCalled();
293
- expect(client.getAgentProfile).toHaveBeenCalled();
294
- });
295
- test("metadata contains profile data", async () => {
296
- const client = makeMockClient();
297
- const deps = makeDeps();
298
- await runEndSessionPipeline(client, deps, "card-1", "completed", 100, {
299
- agent_identifier: "claude-code",
300
- agent_name: "Claude Code",
301
- });
302
- await flush();
303
- const agentEntity = client.createdEntities.find((e) => e.type === "agent");
304
- expect(agentEntity).toBeTruthy();
305
- const metadata = agentEntity.metadata;
306
- expect(metadata.total_sessions).toBe(10);
307
- expect(metadata.completion_rate_pct).toBe(80);
308
- expect(metadata.completed_sessions).toBe(8);
309
- expect(metadata.avg_active_duration_ms).toBe(120000);
310
- });
311
- });
312
- // ============================================================
313
- // Integration Tests (skipped when not configured)
314
- // ============================================================
315
- // Integration tests are skipped in this file because mock.module() overrides
316
- // the real config. Run integration-memory-crud.test.ts for live API tests,
317
- // or use MCP tools directly for agent profile integration verification.
318
- describe.skip("Agent Performance Profiles - Integration (run via MCP tools)", () => {
319
- test("refreshAgentProfiles returns { refreshed: true }", () => { });
320
- test('getAgentProfile("claude-code") returns profile or null', () => { });
321
- test('getAgentProfile("non-existent-xyz") returns null profile', () => { });
322
- test("listAgentProfiles returns array", () => { });
323
- test("profile shape has expected fields", () => { });
324
- test("entity round-trip: create agent entity → recall by type → cleanup", () => { });
325
- });