@stackmemoryai/stackmemory 1.2.0 → 1.2.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 (47) hide show
  1. package/dist/src/cli/claude-sm.js +65 -0
  2. package/dist/src/cli/commands/skills.js +123 -1
  3. package/dist/src/hooks/graphiti-hooks.js +149 -0
  4. package/dist/src/hooks/session-summary.js +30 -0
  5. package/dist/src/integrations/graphiti/linear-graphiti-bridge.js +115 -0
  6. package/dist/src/integrations/greptile/client.js +101 -0
  7. package/dist/src/integrations/greptile/config.js +14 -0
  8. package/dist/src/integrations/greptile/index.js +11 -0
  9. package/dist/src/integrations/greptile/types.js +4 -0
  10. package/dist/src/integrations/linear/webhook.js +16 -0
  11. package/dist/src/integrations/mcp/handlers/greptile-handlers.js +456 -0
  12. package/dist/src/integrations/mcp/server.js +136 -0
  13. package/dist/src/integrations/mcp/tool-definitions.js +53 -1
  14. package/dist/src/skills/claude-skills.js +46 -1
  15. package/dist/src/skills/parallel-agent-skill.js +514 -0
  16. package/dist/src/utils/hook-installer.js +155 -0
  17. package/package.json +2 -2
  18. package/scripts/gepa/.before-optimize.md +140 -0
  19. package/scripts/gepa/config.json +7 -1
  20. package/scripts/gepa/evals/fixtures/api-endpoint.ts +31 -0
  21. package/scripts/gepa/evals/fixtures/brittle-integration.ts +38 -0
  22. package/scripts/gepa/evals/fixtures/fts5-triggers.sql +23 -0
  23. package/scripts/gepa/evals/fixtures/leaky-service.ts +70 -0
  24. package/scripts/gepa/evals/fixtures/mcp-dispatch-stub.ts +39 -0
  25. package/scripts/gepa/evals/fixtures/pr-diff.txt +24 -0
  26. package/scripts/gepa/evals/fixtures/unsafe-webhook.ts +34 -0
  27. package/scripts/gepa/evals/fixtures/unwrapped-db-op.ts +42 -0
  28. package/scripts/gepa/evals/stackmemory-tasks.jsonl +8 -0
  29. package/scripts/gepa/generations/gen-000/baseline.md +48 -0
  30. package/scripts/gepa/generations/gen-001/baseline.md +172 -0
  31. package/scripts/gepa/generations/gen-001/variant-a.md +176 -0
  32. package/scripts/gepa/generations/gen-001/variant-b.md +266 -0
  33. package/scripts/gepa/generations/gen-001/variant-c.md +142 -0
  34. package/scripts/gepa/generations/gen-001/variant-d.md +172 -0
  35. package/scripts/gepa/hooks/reflect.js +44 -5
  36. package/scripts/gepa/optimize.js +281 -39
  37. package/scripts/gepa/results/eval-1-baseline.json +218 -0
  38. package/scripts/gepa/results/eval-1-variant-a.json +218 -0
  39. package/scripts/gepa/results/eval-1-variant-b.json +218 -0
  40. package/scripts/gepa/results/eval-1-variant-c.json +198 -0
  41. package/scripts/gepa/results/eval-1-variant-d.json +198 -0
  42. package/scripts/gepa/state.json +44 -5
  43. package/scripts/install-claude-hooks-auto.js +176 -44
  44. package/templates/claude-hooks/auto-checkpoint.js +174 -0
  45. package/templates/claude-hooks/chime-on-stop.sh +22 -0
  46. package/templates/claude-hooks/session-rescue.sh +15 -0
  47. package/templates/claude-hooks/stop-checkpoint.js +120 -0
@@ -22,6 +22,7 @@ function getOptionalEnv(key) {
22
22
  class LinearWebhookHandler {
23
23
  syncEngine;
24
24
  taskStore;
25
+ graphitiBridge;
25
26
  webhookSecret;
26
27
  constructor(webhookSecret) {
27
28
  this.webhookSecret = webhookSecret || process.env["LINEAR_WEBHOOK_SECRET"];
@@ -38,6 +39,12 @@ class LinearWebhookHandler {
38
39
  setTaskStore(taskStore) {
39
40
  this.taskStore = taskStore;
40
41
  }
42
+ /**
43
+ * Set the Graphiti bridge for knowledge graph sync
44
+ */
45
+ setGraphitiBridge(bridge) {
46
+ this.graphitiBridge = bridge;
47
+ }
41
48
  /**
42
49
  * Verify webhook signature
43
50
  */
@@ -104,6 +111,15 @@ class LinearWebhookHandler {
104
111
  default:
105
112
  logger.warn(`Unknown webhook action: ${payload.action}`);
106
113
  }
114
+ if (this.graphitiBridge) {
115
+ this.graphitiBridge.processWebhook(payload).catch((err) => {
116
+ logger.debug("Linear-Graphiti bridge error", {
117
+ action: payload.action,
118
+ identifier: payload.data.identifier,
119
+ error: err instanceof Error ? err.message : String(err)
120
+ });
121
+ });
122
+ }
107
123
  }
108
124
  /**
109
125
  * Handle issue created in Linear
@@ -0,0 +1,456 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ import { DEFAULT_GREPTILE_CONFIG } from "../../greptile/config.js";
6
+ import { GreptileClient, GreptileClientError } from "../../greptile/client.js";
7
+ import { logger } from "../../../core/monitoring/logger.js";
8
+ class GreptileHandlers {
9
+ config;
10
+ client = null;
11
+ constructor(deps) {
12
+ this.config = { ...DEFAULT_GREPTILE_CONFIG, ...deps?.config };
13
+ if (this.config.enabled && this.config.apiKey) {
14
+ try {
15
+ this.client = new GreptileClient(this.config);
16
+ } catch {
17
+ this.client = null;
18
+ }
19
+ }
20
+ }
21
+ getToolDefinitions() {
22
+ return [
23
+ {
24
+ name: "greptile_pr_comments",
25
+ description: "Get PR review comments from Greptile. Returns unaddressed comments with suggestedCode for auto-fix workflows.",
26
+ inputSchema: {
27
+ type: "object",
28
+ properties: {
29
+ name: {
30
+ type: "string",
31
+ description: 'Repository full name (e.g., "owner/repo")'
32
+ },
33
+ remote: {
34
+ type: "string",
35
+ enum: ["github", "gitlab", "azure", "bitbucket"],
36
+ description: "Remote provider"
37
+ },
38
+ defaultBranch: {
39
+ type: "string",
40
+ description: 'Default branch (e.g., "main")'
41
+ },
42
+ prNumber: {
43
+ type: "number",
44
+ description: "Pull request number"
45
+ },
46
+ greptileGenerated: {
47
+ type: "boolean",
48
+ description: "Filter for only Greptile review comments"
49
+ },
50
+ addressed: {
51
+ type: "boolean",
52
+ description: "Filter by comment addressed status"
53
+ }
54
+ },
55
+ required: ["name", "remote", "defaultBranch", "prNumber"]
56
+ }
57
+ },
58
+ {
59
+ name: "greptile_pr_details",
60
+ description: "Get detailed PR information including metadata, statistics, and review analysis from Greptile.",
61
+ inputSchema: {
62
+ type: "object",
63
+ properties: {
64
+ name: {
65
+ type: "string",
66
+ description: 'Repository full name (e.g., "owner/repo")'
67
+ },
68
+ remote: {
69
+ type: "string",
70
+ enum: ["github", "gitlab", "azure", "bitbucket"],
71
+ description: "Remote provider"
72
+ },
73
+ defaultBranch: {
74
+ type: "string",
75
+ description: "Default branch"
76
+ },
77
+ prNumber: {
78
+ type: "number",
79
+ description: "Pull request number"
80
+ }
81
+ },
82
+ required: ["name", "remote", "defaultBranch", "prNumber"]
83
+ }
84
+ },
85
+ {
86
+ name: "greptile_list_prs",
87
+ description: "List pull requests. Filter by repository, branch, author, or state.",
88
+ inputSchema: {
89
+ type: "object",
90
+ properties: {
91
+ name: {
92
+ type: "string",
93
+ description: 'Repository full name (e.g., "owner/repo")'
94
+ },
95
+ remote: {
96
+ type: "string",
97
+ enum: ["github", "gitlab", "azure", "bitbucket"],
98
+ description: "Remote provider"
99
+ },
100
+ defaultBranch: {
101
+ type: "string",
102
+ description: "Default branch"
103
+ },
104
+ sourceBranch: {
105
+ type: "string",
106
+ description: "Filter by source branch name"
107
+ },
108
+ authorLogin: {
109
+ type: "string",
110
+ description: "Filter by PR author username"
111
+ },
112
+ state: {
113
+ type: "string",
114
+ enum: ["open", "closed", "merged"],
115
+ description: "Filter by PR state"
116
+ },
117
+ limit: {
118
+ type: "number",
119
+ description: "Max results (default 20)"
120
+ }
121
+ }
122
+ }
123
+ },
124
+ {
125
+ name: "greptile_trigger_review",
126
+ description: "Trigger a Greptile code review for a pull request.",
127
+ inputSchema: {
128
+ type: "object",
129
+ properties: {
130
+ name: {
131
+ type: "string",
132
+ description: 'Repository full name (e.g., "owner/repo")'
133
+ },
134
+ remote: {
135
+ type: "string",
136
+ enum: ["github", "gitlab", "azure", "bitbucket"],
137
+ description: "Remote provider"
138
+ },
139
+ defaultBranch: {
140
+ type: "string",
141
+ description: "Default branch"
142
+ },
143
+ prNumber: {
144
+ type: "number",
145
+ description: "Pull request number"
146
+ },
147
+ branch: {
148
+ type: "string",
149
+ description: "Current working branch"
150
+ }
151
+ },
152
+ required: ["name", "remote", "prNumber"]
153
+ }
154
+ },
155
+ {
156
+ name: "greptile_search_patterns",
157
+ description: "Search custom coding patterns and instructions in Greptile.",
158
+ inputSchema: {
159
+ type: "object",
160
+ properties: {
161
+ query: {
162
+ type: "string",
163
+ description: "Search query for pattern content"
164
+ },
165
+ limit: {
166
+ type: "number",
167
+ description: "Max results (default 10)"
168
+ }
169
+ },
170
+ required: ["query"]
171
+ }
172
+ },
173
+ {
174
+ name: "greptile_create_pattern",
175
+ description: "Create a new custom coding pattern or instruction in Greptile.",
176
+ inputSchema: {
177
+ type: "object",
178
+ properties: {
179
+ body: {
180
+ type: "string",
181
+ description: "Pattern content"
182
+ },
183
+ type: {
184
+ type: "string",
185
+ enum: ["CUSTOM_INSTRUCTION", "PATTERN"],
186
+ description: "Context type (default: CUSTOM_INSTRUCTION)"
187
+ },
188
+ scopes: {
189
+ type: "object",
190
+ description: "Boolean expression defining where this pattern applies"
191
+ }
192
+ },
193
+ required: ["body"]
194
+ }
195
+ },
196
+ {
197
+ name: "greptile_status",
198
+ description: "Check Greptile integration connection status.",
199
+ inputSchema: {
200
+ type: "object",
201
+ properties: {}
202
+ }
203
+ }
204
+ ];
205
+ }
206
+ async handleListPRComments(args) {
207
+ if (!this.client) return this.disabledResponse();
208
+ try {
209
+ const toolArgs = {
210
+ name: args.name,
211
+ remote: args.remote,
212
+ defaultBranch: args.defaultBranch,
213
+ prNumber: args.prNumber
214
+ };
215
+ if (args.greptileGenerated !== void 0)
216
+ toolArgs.greptileGenerated = args.greptileGenerated;
217
+ if (args.addressed !== void 0) toolArgs.addressed = args.addressed;
218
+ const result = await this.client.callTool(
219
+ "list_merge_request_comments",
220
+ toolArgs
221
+ );
222
+ let actionableCount = 0;
223
+ if (Array.isArray(result)) {
224
+ actionableCount = result.filter(
225
+ (c) => !c.addressed && (c.suggestedCode || typeof c.body === "string" && c.body.includes("```suggestion"))
226
+ ).length;
227
+ }
228
+ return {
229
+ content: [
230
+ {
231
+ type: "text",
232
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
233
+ }
234
+ ],
235
+ metadata: { actionableCount, tool: "list_merge_request_comments" }
236
+ };
237
+ } catch (error) {
238
+ return this.handleError("listPRComments", error);
239
+ }
240
+ }
241
+ async handleGetMergeRequest(args) {
242
+ if (!this.client) return this.disabledResponse();
243
+ try {
244
+ const result = await this.client.callTool("get_merge_request", {
245
+ name: args.name,
246
+ remote: args.remote,
247
+ defaultBranch: args.defaultBranch,
248
+ prNumber: args.prNumber
249
+ });
250
+ return {
251
+ content: [
252
+ {
253
+ type: "text",
254
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
255
+ }
256
+ ],
257
+ metadata: { tool: "get_merge_request" }
258
+ };
259
+ } catch (error) {
260
+ return this.handleError("getMergeRequest", error);
261
+ }
262
+ }
263
+ async handleListPullRequests(args) {
264
+ if (!this.client) return this.disabledResponse();
265
+ try {
266
+ const toolArgs = {};
267
+ if (args.name) toolArgs.name = args.name;
268
+ if (args.remote) toolArgs.remote = args.remote;
269
+ if (args.defaultBranch) toolArgs.defaultBranch = args.defaultBranch;
270
+ if (args.sourceBranch) toolArgs.sourceBranch = args.sourceBranch;
271
+ if (args.authorLogin) toolArgs.authorLogin = args.authorLogin;
272
+ if (args.state) toolArgs.state = args.state;
273
+ if (args.limit) toolArgs.limit = args.limit;
274
+ const result = await this.client.callTool("list_pull_requests", toolArgs);
275
+ return {
276
+ content: [
277
+ {
278
+ type: "text",
279
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
280
+ }
281
+ ],
282
+ metadata: { tool: "list_pull_requests" }
283
+ };
284
+ } catch (error) {
285
+ return this.handleError("listPullRequests", error);
286
+ }
287
+ }
288
+ async handleTriggerCodeReview(args) {
289
+ if (!this.client) return this.disabledResponse();
290
+ try {
291
+ const toolArgs = {
292
+ name: args.name,
293
+ remote: args.remote,
294
+ prNumber: args.prNumber
295
+ };
296
+ if (args.defaultBranch) toolArgs.defaultBranch = args.defaultBranch;
297
+ if (args.branch) toolArgs.branch = args.branch;
298
+ const result = await this.client.callTool(
299
+ "trigger_code_review",
300
+ toolArgs
301
+ );
302
+ return {
303
+ content: [
304
+ {
305
+ type: "text",
306
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
307
+ }
308
+ ],
309
+ metadata: { tool: "trigger_code_review" }
310
+ };
311
+ } catch (error) {
312
+ return this.handleError("triggerCodeReview", error);
313
+ }
314
+ }
315
+ async handleSearchPatterns(args) {
316
+ if (!this.client) return this.disabledResponse();
317
+ try {
318
+ const toolArgs = { query: args.query };
319
+ if (args.limit) toolArgs.limit = args.limit;
320
+ const result = await this.client.callTool(
321
+ "search_custom_context",
322
+ toolArgs
323
+ );
324
+ return {
325
+ content: [
326
+ {
327
+ type: "text",
328
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
329
+ }
330
+ ],
331
+ metadata: { tool: "search_custom_context" }
332
+ };
333
+ } catch (error) {
334
+ return this.handleError("searchPatterns", error);
335
+ }
336
+ }
337
+ async handleCreatePattern(args) {
338
+ if (!this.client) return this.disabledResponse();
339
+ try {
340
+ const toolArgs = { body: args.body };
341
+ if (args.type) toolArgs.type = args.type;
342
+ if (args.scopes) toolArgs.scopes = args.scopes;
343
+ const result = await this.client.callTool(
344
+ "create_custom_context",
345
+ toolArgs
346
+ );
347
+ return {
348
+ content: [
349
+ {
350
+ type: "text",
351
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
352
+ }
353
+ ],
354
+ metadata: { tool: "create_custom_context" }
355
+ };
356
+ } catch (error) {
357
+ return this.handleError("createPattern", error);
358
+ }
359
+ }
360
+ async handleStatus() {
361
+ if (!this.client) {
362
+ return {
363
+ content: [
364
+ {
365
+ type: "text",
366
+ text: JSON.stringify(
367
+ {
368
+ connected: false,
369
+ message: "Greptile integration disabled (GREPTILE_API_KEY not set)"
370
+ },
371
+ null,
372
+ 2
373
+ )
374
+ }
375
+ ],
376
+ metadata: { connected: false }
377
+ };
378
+ }
379
+ try {
380
+ await this.client.callTool("list_pull_requests", { limit: 1 });
381
+ return {
382
+ content: [
383
+ {
384
+ type: "text",
385
+ text: JSON.stringify(
386
+ {
387
+ connected: true,
388
+ endpoint: this.config.mcpEndpoint
389
+ },
390
+ null,
391
+ 2
392
+ )
393
+ }
394
+ ],
395
+ metadata: { connected: true }
396
+ };
397
+ } catch (error) {
398
+ const msg = error instanceof Error ? error.message : String(error);
399
+ return {
400
+ content: [
401
+ {
402
+ type: "text",
403
+ text: JSON.stringify(
404
+ {
405
+ connected: false,
406
+ error: msg,
407
+ endpoint: this.config.mcpEndpoint
408
+ },
409
+ null,
410
+ 2
411
+ )
412
+ }
413
+ ],
414
+ metadata: { connected: false, error: msg }
415
+ };
416
+ }
417
+ }
418
+ disabledResponse() {
419
+ return {
420
+ content: [
421
+ {
422
+ type: "text",
423
+ text: "Greptile integration disabled (GREPTILE_API_KEY not set)"
424
+ }
425
+ ]
426
+ };
427
+ }
428
+ handleError(operation, error) {
429
+ const msg = error instanceof Error ? error.message : String(error);
430
+ const isConnectionError = msg.includes("ECONNREFUSED") || msg.includes("fetch failed") || msg.includes("network") || error instanceof GreptileClientError;
431
+ logger.debug(`Greptile ${operation} failed`, { error: msg });
432
+ if (isConnectionError) {
433
+ return {
434
+ content: [
435
+ {
436
+ type: "text",
437
+ text: `Greptile unavailable (${operation}). The service may be temporarily unreachable.`
438
+ }
439
+ ],
440
+ metadata: { error: true, unavailable: true, operation }
441
+ };
442
+ }
443
+ return {
444
+ content: [
445
+ {
446
+ type: "text",
447
+ text: `Greptile ${operation} error: ${msg}`
448
+ }
449
+ ],
450
+ metadata: { error: true, operation, message: msg }
451
+ };
452
+ }
453
+ }
454
+ export {
455
+ GreptileHandlers
456
+ };
@@ -30,6 +30,8 @@ import { TraceDetector } from "../../core/trace/trace-detector.js";
30
30
  import { LLMContextRetrieval } from "../../core/retrieval/index.js";
31
31
  import { DiscoveryHandlers } from "./handlers/discovery-handlers.js";
32
32
  import { DiffMemHandlers } from "./handlers/diffmem-handlers.js";
33
+ import { GreptileHandlers } from "./handlers/greptile-handlers.js";
34
+ import { GraphitiClient } from "../graphiti/client.js";
33
35
  import { fuzzyEdit } from "../../utils/fuzzy-edit.js";
34
36
  import { v4 as uuidv4 } from "uuid";
35
37
  import {
@@ -63,7 +65,9 @@ class LocalStackMemoryMCP {
63
65
  contextRetrieval;
64
66
  discoveryHandlers;
65
67
  diffMemHandlers;
68
+ greptileHandlers;
66
69
  providerHandlers = null;
70
+ graphitiClient = null;
67
71
  pendingPlans = /* @__PURE__ */ new Map();
68
72
  constructor() {
69
73
  this.projectRoot = this.findProjectRoot();
@@ -136,7 +140,17 @@ class LocalStackMemoryMCP {
136
140
  projectRoot: this.projectRoot
137
141
  });
138
142
  this.diffMemHandlers = new DiffMemHandlers();
143
+ this.greptileHandlers = new GreptileHandlers();
139
144
  this.initProviderHandlers();
145
+ if (process.env.GRAPHITI_ENDPOINT) {
146
+ this.graphitiClient = new GraphitiClient({
147
+ endpoint: process.env.GRAPHITI_ENDPOINT,
148
+ projectNamespace: process.env.STACKMEMORY_PROJECT_ID || this.projectId
149
+ });
150
+ logger.info("Graphiti client initialized", {
151
+ endpoint: process.env.GRAPHITI_ENDPOINT
152
+ });
153
+ }
140
154
  this.setupHandlers();
141
155
  this.loadInitialContext();
142
156
  this.loadPendingPlans();
@@ -1073,6 +1087,53 @@ ${summary}...`, 0.8);
1073
1087
  properties: {}
1074
1088
  }
1075
1089
  },
1090
+ // Graphiti tools (only active when GRAPHITI_ENDPOINT is set)
1091
+ ...this.graphitiClient ? [
1092
+ {
1093
+ name: "graphiti_status",
1094
+ description: "Check Graphiti temporal knowledge graph connection status",
1095
+ inputSchema: {
1096
+ type: "object",
1097
+ properties: {}
1098
+ }
1099
+ },
1100
+ {
1101
+ name: "graphiti_query",
1102
+ description: "Query the Graphiti temporal knowledge graph for entities, relations, and episodes",
1103
+ inputSchema: {
1104
+ type: "object",
1105
+ properties: {
1106
+ query: {
1107
+ type: "string",
1108
+ description: "Semantic text query"
1109
+ },
1110
+ entityTypes: {
1111
+ type: "array",
1112
+ items: { type: "string" },
1113
+ description: 'Entity types to filter (e.g., ["Person", "File", "Issue"])'
1114
+ },
1115
+ validFrom: {
1116
+ type: "number",
1117
+ description: "Start of time window (epoch ms)"
1118
+ },
1119
+ validTo: {
1120
+ type: "number",
1121
+ description: "End of time window (epoch ms)"
1122
+ },
1123
+ maxHops: {
1124
+ type: "number",
1125
+ description: "Graph traversal depth (default 2)"
1126
+ },
1127
+ k: {
1128
+ type: "number",
1129
+ description: "Top-k results (default 20)"
1130
+ }
1131
+ }
1132
+ }
1133
+ }
1134
+ ] : [],
1135
+ // Greptile tools (only active when GREPTILE_API_KEY is set)
1136
+ ...process.env.GREPTILE_API_KEY ? this.greptileHandlers.getToolDefinitions() : [],
1076
1137
  // Provider tools (only active when STACKMEMORY_MULTI_PROVIDER=true)
1077
1138
  ...isFeatureEnabled("multiProvider") ? [
1078
1139
  {
@@ -1321,6 +1382,28 @@ ${summary}...`, 0.8);
1321
1382
  case "diffmem_status":
1322
1383
  result = await this.diffMemHandlers.handleStatus();
1323
1384
  break;
1385
+ // Greptile handlers
1386
+ case "greptile_pr_comments":
1387
+ result = await this.greptileHandlers.handleListPRComments(args);
1388
+ break;
1389
+ case "greptile_pr_details":
1390
+ result = await this.greptileHandlers.handleGetMergeRequest(args);
1391
+ break;
1392
+ case "greptile_list_prs":
1393
+ result = await this.greptileHandlers.handleListPullRequests(args);
1394
+ break;
1395
+ case "greptile_trigger_review":
1396
+ result = await this.greptileHandlers.handleTriggerCodeReview(args);
1397
+ break;
1398
+ case "greptile_search_patterns":
1399
+ result = await this.greptileHandlers.handleSearchPatterns(args);
1400
+ break;
1401
+ case "greptile_create_pattern":
1402
+ result = await this.greptileHandlers.handleCreatePattern(args);
1403
+ break;
1404
+ case "greptile_status":
1405
+ result = await this.greptileHandlers.handleStatus();
1406
+ break;
1324
1407
  case "sm_edit":
1325
1408
  result = await this.handleSmEdit(args);
1326
1409
  break;
@@ -1373,6 +1456,59 @@ ${summary}...`, 0.8);
1373
1456
  );
1374
1457
  }
1375
1458
  break;
1459
+ // Graphiti tools
1460
+ case "graphiti_status":
1461
+ if (!this.graphitiClient) {
1462
+ result = {
1463
+ content: [
1464
+ {
1465
+ type: "text",
1466
+ text: JSON.stringify({
1467
+ connected: false,
1468
+ message: "Graphiti integration disabled (GRAPHITI_ENDPOINT not set)"
1469
+ })
1470
+ }
1471
+ ]
1472
+ };
1473
+ } else {
1474
+ const status = await this.graphitiClient.getStatus();
1475
+ result = {
1476
+ content: [
1477
+ { type: "text", text: JSON.stringify(status, null, 2) }
1478
+ ]
1479
+ };
1480
+ }
1481
+ break;
1482
+ case "graphiti_query":
1483
+ if (!this.graphitiClient) {
1484
+ result = {
1485
+ content: [
1486
+ {
1487
+ type: "text",
1488
+ text: "Graphiti integration disabled (GRAPHITI_ENDPOINT not set)"
1489
+ }
1490
+ ]
1491
+ };
1492
+ } else {
1493
+ const gCtx = await this.graphitiClient.queryTemporal({
1494
+ query: args.query,
1495
+ entityTypes: args.entityTypes,
1496
+ validFrom: args.validFrom,
1497
+ validTo: args.validTo,
1498
+ maxHops: args.maxHops,
1499
+ k: args.k
1500
+ });
1501
+ const text = gCtx.chunks.map((c) => c.text).join("\n\n");
1502
+ result = {
1503
+ content: [
1504
+ {
1505
+ type: "text",
1506
+ text: text || `No results found (${gCtx.totalTokens} tokens searched)`
1507
+ }
1508
+ ]
1509
+ };
1510
+ }
1511
+ break;
1376
1512
  default:
1377
1513
  throw new Error(`Unknown tool: ${name}`);
1378
1514
  }