@stackmemoryai/stackmemory 1.2.1 → 1.2.4
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.
- package/dist/src/cli/codex-sm.js +6 -8
- package/dist/src/cli/commands/config.js +0 -81
- package/dist/src/cli/commands/context-rehydrate.js +133 -47
- package/dist/src/cli/commands/db.js +35 -8
- package/dist/src/cli/commands/handoff.js +1 -1
- package/dist/src/cli/commands/linear.js +9 -0
- package/dist/src/cli/commands/ralph.js +2 -2
- package/dist/src/cli/commands/setup.js +2 -2
- package/dist/src/cli/commands/signup.js +3 -1
- package/dist/src/cli/commands/skills.js +123 -1
- package/dist/src/cli/commands/storage-tier.js +26 -8
- package/dist/src/cli/index.js +1 -57
- package/dist/src/core/config/feature-flags.js +0 -4
- package/dist/src/core/context/dual-stack-manager.js +10 -3
- package/dist/src/core/context/frame-database.js +32 -0
- package/dist/src/core/context/frame-handoff-manager.js +2 -2
- package/dist/src/core/context/{refactored-frame-manager.js → frame-manager.js} +3 -3
- package/dist/src/core/context/index.js +2 -2
- package/dist/src/core/database/sqlite-adapter.js +161 -1
- package/dist/src/core/digest/frame-digest-integration.js +1 -1
- package/dist/src/core/digest/index.js +1 -1
- package/dist/src/core/execution/parallel-executor.js +5 -1
- package/dist/src/core/projects/project-isolation.js +18 -4
- package/dist/src/core/security/index.js +2 -0
- package/dist/src/core/security/input-sanitizer.js +23 -0
- package/dist/src/core/utils/update-checker.js +10 -6
- package/dist/src/daemon/daemon-config.js +2 -1
- package/dist/src/daemon/services/auto-save-service.js +121 -0
- package/dist/src/daemon/services/maintenance-service.js +76 -1
- package/dist/src/features/sweep/prompt-builder.js +2 -2
- package/dist/src/hooks/graphiti-hooks.js +149 -0
- package/dist/src/hooks/session-summary.js +30 -0
- package/dist/src/integrations/graphiti/linear-graphiti-bridge.js +115 -0
- package/dist/src/integrations/greptile/client.js +101 -0
- package/dist/src/integrations/greptile/config.js +14 -0
- package/dist/src/integrations/greptile/index.js +11 -0
- package/dist/src/integrations/greptile/types.js +4 -0
- package/dist/src/integrations/linear/config.js +3 -1
- package/dist/src/integrations/linear/sync.js +18 -5
- package/dist/src/integrations/linear/webhook.js +16 -0
- package/dist/src/integrations/mcp/handlers/code-execution-handlers.js +33 -7
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +397 -0
- package/dist/src/integrations/mcp/handlers/greptile-handlers.js +456 -0
- package/dist/src/integrations/mcp/handlers/index.js +55 -1
- package/dist/src/integrations/mcp/handlers/task-handlers.js +55 -12
- package/dist/src/integrations/mcp/handlers/team-handlers.js +211 -0
- package/dist/src/integrations/mcp/handlers/trace-handlers.js +25 -9
- package/dist/src/integrations/mcp/index.js +2 -2
- package/dist/src/integrations/mcp/refactored-server.js +31 -10
- package/dist/src/integrations/mcp/server.js +27 -0
- package/dist/src/integrations/mcp/tool-definitions.js +215 -1
- package/dist/src/integrations/ralph/context/context-budget-manager.js +10 -2
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +54 -22
- package/dist/src/integrations/ralph/learning/pattern-learner.js +59 -24
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +81 -35
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +12 -4
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +32 -9
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +25 -8
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +17 -5
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js +73 -22
- package/dist/src/skills/claude-skills.js +46 -103
- package/dist/src/skills/parallel-agent-skill.js +514 -0
- package/dist/src/utils/hook-installer.js +8 -0
- package/package.json +5 -5
- package/scripts/gepa/.before-optimize.md +140 -159
- package/scripts/gepa/config.json +7 -1
- package/scripts/gepa/evals/fixtures/api-endpoint.ts +31 -0
- package/scripts/gepa/evals/fixtures/brittle-integration.ts +38 -0
- package/scripts/gepa/evals/fixtures/fts5-triggers.sql +23 -0
- package/scripts/gepa/evals/fixtures/leaky-service.ts +70 -0
- package/scripts/gepa/evals/fixtures/mcp-dispatch-stub.ts +39 -0
- package/scripts/gepa/evals/fixtures/pr-diff.txt +24 -0
- package/scripts/gepa/evals/fixtures/unsafe-webhook.ts +34 -0
- package/scripts/gepa/evals/fixtures/unwrapped-db-op.ts +42 -0
- package/scripts/gepa/evals/stackmemory-tasks.jsonl +8 -0
- package/scripts/gepa/generations/gen-000/baseline.md +172 -159
- package/scripts/gepa/generations/gen-001/baseline.md +172 -159
- package/scripts/gepa/generations/gen-001/variant-a.md +156 -146
- package/scripts/gepa/generations/gen-001/variant-b.md +199 -170
- package/scripts/gepa/generations/gen-001/variant-c.md +127 -46
- package/scripts/gepa/generations/gen-001/variant-d.md +160 -107
- package/scripts/gepa/hooks/reflect.js +44 -5
- package/scripts/gepa/optimize.js +281 -39
- package/scripts/gepa/results/eval-1-baseline.json +187 -10
- package/scripts/gepa/results/eval-1-variant-a.json +188 -11
- package/scripts/gepa/results/eval-1-variant-b.json +188 -11
- package/scripts/gepa/results/eval-1-variant-c.json +168 -11
- package/scripts/gepa/results/eval-1-variant-d.json +169 -12
- package/scripts/gepa/state.json +18 -18
- package/scripts/install-claude-hooks-auto.js +8 -0
- package/templates/claude-hooks/cord-trace.js +225 -0
- package/dist/src/core/config/storage-config.js +0 -114
- package/dist/src/core/storage/chromadb-adapter.js +0 -379
- package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js +0 -458
- package/dist/src/integrations/ralph/coordination/enhanced-coordination.js +0 -409
- package/dist/src/skills/repo-ingestion-skill.js +0 -631
- package/templates/claude-hooks/chromadb-wrapper +0 -21
- /package/dist/src/core/context/{enhanced-rehydration.js → rehydration.js} +0 -0
- /package/dist/src/core/digest/{enhanced-hybrid-digest.js → hybrid-digest.js} +0 -0
- /package/dist/src/core/session/{enhanced-handoff.js → handoff.js} +0 -0
|
@@ -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
|
+
};
|
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
import {
|
|
19
19
|
ProviderHandlers
|
|
20
20
|
} from "./provider-handlers.js";
|
|
21
|
+
import { TeamHandlers } from "./team-handlers.js";
|
|
22
|
+
import { CordHandlers } from "./cord-handlers.js";
|
|
21
23
|
import {
|
|
22
24
|
ContextHandlers as ContextHandlers2
|
|
23
25
|
} from "./context-handlers.js";
|
|
@@ -27,12 +29,16 @@ import {
|
|
|
27
29
|
} from "./linear-handlers.js";
|
|
28
30
|
import { TraceHandlers as TraceHandlers2 } from "./trace-handlers.js";
|
|
29
31
|
import { ProviderHandlers as ProviderHandlers2 } from "./provider-handlers.js";
|
|
32
|
+
import { TeamHandlers as TeamHandlers2 } from "./team-handlers.js";
|
|
33
|
+
import { CordHandlers as CordHandlers2 } from "./cord-handlers.js";
|
|
30
34
|
class MCPHandlerFactory {
|
|
31
35
|
contextHandlers;
|
|
32
36
|
taskHandlers;
|
|
33
37
|
linearHandlers;
|
|
34
38
|
traceHandlers;
|
|
35
39
|
providerHandlers;
|
|
40
|
+
teamHandlers;
|
|
41
|
+
cordHandlers;
|
|
36
42
|
constructor(deps) {
|
|
37
43
|
this.contextHandlers = new ContextHandlers2({
|
|
38
44
|
frameManager: deps.frameManager
|
|
@@ -51,6 +57,16 @@ class MCPHandlerFactory {
|
|
|
51
57
|
browserMCP: deps.browserMCP
|
|
52
58
|
});
|
|
53
59
|
this.providerHandlers = new ProviderHandlers2();
|
|
60
|
+
if (deps.dbAdapter) {
|
|
61
|
+
this.teamHandlers = new TeamHandlers2({
|
|
62
|
+
frameManager: deps.frameManager,
|
|
63
|
+
dbAdapter: deps.dbAdapter
|
|
64
|
+
});
|
|
65
|
+
this.cordHandlers = new CordHandlers2({
|
|
66
|
+
frameManager: deps.frameManager,
|
|
67
|
+
dbAdapter: deps.dbAdapter
|
|
68
|
+
});
|
|
69
|
+
}
|
|
54
70
|
}
|
|
55
71
|
/**
|
|
56
72
|
* Get handler for a specific tool
|
|
@@ -130,6 +146,32 @@ class MCPHandlerFactory {
|
|
|
130
146
|
return this.providerHandlers.handleBatchCheck.bind(
|
|
131
147
|
this.providerHandlers
|
|
132
148
|
);
|
|
149
|
+
// Team handlers
|
|
150
|
+
case "team_context_get":
|
|
151
|
+
if (!this.teamHandlers) throw new Error("Team tools require dbAdapter");
|
|
152
|
+
return this.teamHandlers.handleTeamContextGet.bind(this.teamHandlers);
|
|
153
|
+
case "team_context_share":
|
|
154
|
+
if (!this.teamHandlers) throw new Error("Team tools require dbAdapter");
|
|
155
|
+
return this.teamHandlers.handleTeamContextShare.bind(this.teamHandlers);
|
|
156
|
+
case "team_search":
|
|
157
|
+
if (!this.teamHandlers) throw new Error("Team tools require dbAdapter");
|
|
158
|
+
return this.teamHandlers.handleTeamSearch.bind(this.teamHandlers);
|
|
159
|
+
// Cord orchestration handlers
|
|
160
|
+
case "cord_spawn":
|
|
161
|
+
if (!this.cordHandlers) throw new Error("Cord tools require dbAdapter");
|
|
162
|
+
return this.cordHandlers.handleCordSpawn.bind(this.cordHandlers);
|
|
163
|
+
case "cord_fork":
|
|
164
|
+
if (!this.cordHandlers) throw new Error("Cord tools require dbAdapter");
|
|
165
|
+
return this.cordHandlers.handleCordFork.bind(this.cordHandlers);
|
|
166
|
+
case "cord_complete":
|
|
167
|
+
if (!this.cordHandlers) throw new Error("Cord tools require dbAdapter");
|
|
168
|
+
return this.cordHandlers.handleCordComplete.bind(this.cordHandlers);
|
|
169
|
+
case "cord_ask":
|
|
170
|
+
if (!this.cordHandlers) throw new Error("Cord tools require dbAdapter");
|
|
171
|
+
return this.cordHandlers.handleCordAsk.bind(this.cordHandlers);
|
|
172
|
+
case "cord_tree":
|
|
173
|
+
if (!this.cordHandlers) throw new Error("Cord tools require dbAdapter");
|
|
174
|
+
return this.cordHandlers.handleCordTree.bind(this.cordHandlers);
|
|
133
175
|
default:
|
|
134
176
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
135
177
|
}
|
|
@@ -167,7 +209,17 @@ class MCPHandlerFactory {
|
|
|
167
209
|
// Provider tools (conditionally active)
|
|
168
210
|
"delegate_to_model",
|
|
169
211
|
"batch_submit",
|
|
170
|
-
"batch_check"
|
|
212
|
+
"batch_check",
|
|
213
|
+
// Team tools
|
|
214
|
+
"team_context_get",
|
|
215
|
+
"team_context_share",
|
|
216
|
+
"team_search",
|
|
217
|
+
// Cord orchestration tools
|
|
218
|
+
"cord_spawn",
|
|
219
|
+
"cord_fork",
|
|
220
|
+
"cord_complete",
|
|
221
|
+
"cord_ask",
|
|
222
|
+
"cord_tree"
|
|
171
223
|
];
|
|
172
224
|
}
|
|
173
225
|
/**
|
|
@@ -179,10 +231,12 @@ class MCPHandlerFactory {
|
|
|
179
231
|
}
|
|
180
232
|
export {
|
|
181
233
|
ContextHandlers,
|
|
234
|
+
CordHandlers,
|
|
182
235
|
DiscoveryHandlers,
|
|
183
236
|
LinearHandlers,
|
|
184
237
|
MCPHandlerFactory,
|
|
185
238
|
ProviderHandlers,
|
|
186
239
|
TaskHandlers,
|
|
240
|
+
TeamHandlers,
|
|
187
241
|
TraceHandlers
|
|
188
242
|
};
|
|
@@ -12,7 +12,13 @@ class TaskHandlers {
|
|
|
12
12
|
*/
|
|
13
13
|
async handleCreateTask(args) {
|
|
14
14
|
try {
|
|
15
|
-
const {
|
|
15
|
+
const {
|
|
16
|
+
title,
|
|
17
|
+
description,
|
|
18
|
+
priority = "medium",
|
|
19
|
+
tags = [],
|
|
20
|
+
parent_id
|
|
21
|
+
} = args;
|
|
16
22
|
if (!title) {
|
|
17
23
|
throw new Error("Task title is required");
|
|
18
24
|
}
|
|
@@ -40,7 +46,10 @@ class TaskHandlers {
|
|
|
40
46
|
}
|
|
41
47
|
};
|
|
42
48
|
} catch (error) {
|
|
43
|
-
logger.error(
|
|
49
|
+
logger.error(
|
|
50
|
+
"Error creating task",
|
|
51
|
+
error instanceof Error ? error : new Error(String(error))
|
|
52
|
+
);
|
|
44
53
|
throw error;
|
|
45
54
|
}
|
|
46
55
|
}
|
|
@@ -57,12 +66,20 @@ class TaskHandlers {
|
|
|
57
66
|
throw new Error("Status is required");
|
|
58
67
|
}
|
|
59
68
|
const validStatus = this.validateStatus(status);
|
|
60
|
-
await this.deps.taskStore.updateTaskStatus(
|
|
69
|
+
await this.deps.taskStore.updateTaskStatus(
|
|
70
|
+
task_id,
|
|
71
|
+
validStatus,
|
|
72
|
+
progress
|
|
73
|
+
);
|
|
61
74
|
const task = await this.deps.taskStore.getTask(task_id);
|
|
62
75
|
if (!task) {
|
|
63
76
|
throw new Error(`Task not found: ${task_id}`);
|
|
64
77
|
}
|
|
65
|
-
logger.info("Updated task status", {
|
|
78
|
+
logger.info("Updated task status", {
|
|
79
|
+
taskId: task_id,
|
|
80
|
+
status: validStatus,
|
|
81
|
+
progress
|
|
82
|
+
});
|
|
66
83
|
return {
|
|
67
84
|
content: [
|
|
68
85
|
{
|
|
@@ -77,7 +94,10 @@ class TaskHandlers {
|
|
|
77
94
|
}
|
|
78
95
|
};
|
|
79
96
|
} catch (error) {
|
|
80
|
-
logger.error(
|
|
97
|
+
logger.error(
|
|
98
|
+
"Error updating task status",
|
|
99
|
+
error instanceof Error ? error : new Error(String(error))
|
|
100
|
+
);
|
|
81
101
|
throw error;
|
|
82
102
|
}
|
|
83
103
|
}
|
|
@@ -138,7 +158,10 @@ ${summaryText}`
|
|
|
138
158
|
}
|
|
139
159
|
};
|
|
140
160
|
} catch (error) {
|
|
141
|
-
logger.error(
|
|
161
|
+
logger.error(
|
|
162
|
+
"Error getting active tasks",
|
|
163
|
+
error instanceof Error ? error : new Error(String(error))
|
|
164
|
+
);
|
|
142
165
|
throw error;
|
|
143
166
|
}
|
|
144
167
|
}
|
|
@@ -172,7 +195,10 @@ ${Object.entries(metrics.by_status || {}).map(([status, count]) => `- ${status}:
|
|
|
172
195
|
metadata: metrics
|
|
173
196
|
};
|
|
174
197
|
} catch (error) {
|
|
175
|
-
logger.error(
|
|
198
|
+
logger.error(
|
|
199
|
+
"Error getting task metrics",
|
|
200
|
+
error instanceof Error ? error : new Error(String(error))
|
|
201
|
+
);
|
|
176
202
|
throw error;
|
|
177
203
|
}
|
|
178
204
|
}
|
|
@@ -191,7 +217,11 @@ ${Object.entries(metrics.by_status || {}).map(([status, count]) => `- ${status}:
|
|
|
191
217
|
if (!task || !dependencyTask) {
|
|
192
218
|
throw new Error("One or both tasks not found");
|
|
193
219
|
}
|
|
194
|
-
logger.info("Added task dependency", {
|
|
220
|
+
logger.info("Added task dependency", {
|
|
221
|
+
taskId: task_id,
|
|
222
|
+
dependsOn: depends_on,
|
|
223
|
+
type: dependency_type
|
|
224
|
+
});
|
|
195
225
|
return {
|
|
196
226
|
content: [
|
|
197
227
|
{
|
|
@@ -206,7 +236,10 @@ ${Object.entries(metrics.by_status || {}).map(([status, count]) => `- ${status}:
|
|
|
206
236
|
}
|
|
207
237
|
};
|
|
208
238
|
} catch (error) {
|
|
209
|
-
logger.error(
|
|
239
|
+
logger.error(
|
|
240
|
+
"Error adding task dependency",
|
|
241
|
+
error instanceof Error ? error : new Error(String(error))
|
|
242
|
+
);
|
|
210
243
|
throw error;
|
|
211
244
|
}
|
|
212
245
|
}
|
|
@@ -217,7 +250,9 @@ ${Object.entries(metrics.by_status || {}).map(([status, count]) => `- ${status}:
|
|
|
217
250
|
const validPriorities = ["low", "medium", "high", "urgent"];
|
|
218
251
|
const normalizedPriority = priority.toLowerCase();
|
|
219
252
|
if (!validPriorities.includes(normalizedPriority)) {
|
|
220
|
-
throw new Error(
|
|
253
|
+
throw new Error(
|
|
254
|
+
`Invalid priority: ${priority}. Must be one of: ${validPriorities.join(", ")}`
|
|
255
|
+
);
|
|
221
256
|
}
|
|
222
257
|
return normalizedPriority;
|
|
223
258
|
}
|
|
@@ -225,10 +260,18 @@ ${Object.entries(metrics.by_status || {}).map(([status, count]) => `- ${status}:
|
|
|
225
260
|
* Validate task status
|
|
226
261
|
*/
|
|
227
262
|
validateStatus(status) {
|
|
228
|
-
const validStatuses = [
|
|
263
|
+
const validStatuses = [
|
|
264
|
+
"pending",
|
|
265
|
+
"in_progress",
|
|
266
|
+
"blocked",
|
|
267
|
+
"completed",
|
|
268
|
+
"cancelled"
|
|
269
|
+
];
|
|
229
270
|
const normalizedStatus = status.toLowerCase().replace("_", "-");
|
|
230
271
|
if (!validStatuses.includes(normalizedStatus)) {
|
|
231
|
-
throw new Error(
|
|
272
|
+
throw new Error(
|
|
273
|
+
`Invalid status: ${status}. Must be one of: ${validStatuses.join(", ")}`
|
|
274
|
+
);
|
|
232
275
|
}
|
|
233
276
|
return normalizedStatus;
|
|
234
277
|
}
|