@stackmemoryai/stackmemory 1.8.1 → 1.10.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.
- package/dist/src/cli/commands/orchestrate.js +8 -1
- package/dist/src/cli/commands/orchestrator.js +27 -6
- package/dist/src/cli/commands/ralph.js +43 -0
- package/dist/src/cli/commands/rules.js +250 -0
- package/dist/src/cli/commands/skill.js +406 -0
- package/dist/src/cli/index.js +43 -0
- package/dist/src/core/rules/built-in-rules.js +289 -0
- package/dist/src/core/rules/pr-review-rule.js +87 -0
- package/dist/src/core/rules/rule-engine.js +85 -0
- package/dist/src/core/rules/rule-store.js +99 -0
- package/dist/src/core/rules/types.js +4 -0
- package/dist/src/core/skills/index.js +21 -0
- package/dist/src/core/skills/skill-matcher.js +178 -0
- package/dist/src/core/skills/skill-registry.js +646 -0
- package/dist/src/core/skills/types.js +1 -47
- package/dist/src/integrations/linear/client.js +66 -0
- package/dist/src/integrations/mcp/handlers/index.js +43 -1
- package/dist/src/integrations/mcp/handlers/linear-handlers.js +102 -0
- package/dist/src/integrations/mcp/handlers/provenant-handlers.js +398 -0
- package/dist/src/integrations/mcp/handlers/skill-handlers.js +67 -167
- package/dist/src/integrations/mcp/server.js +117 -6
- package/dist/src/integrations/mcp/tool-definitions.js +152 -1
- package/dist/src/integrations/ralph/loopmax.js +488 -0
- package/package.json +2 -2
|
@@ -3,66 +3,31 @@ import { dirname as __pathDirname } from 'path';
|
|
|
3
3
|
const __filename = __fileURLToPath(import.meta.url);
|
|
4
4
|
const __dirname = __pathDirname(__filename);
|
|
5
5
|
import { logger } from "../../../core/monitoring/logger.js";
|
|
6
|
-
function _getEnv(key, defaultValue) {
|
|
7
|
-
const value = process.env[key];
|
|
8
|
-
if (value === void 0) {
|
|
9
|
-
if (defaultValue !== void 0) return defaultValue;
|
|
10
|
-
throw new Error(`Environment variable ${key} is required`);
|
|
11
|
-
}
|
|
12
|
-
return value;
|
|
13
|
-
}
|
|
14
|
-
function _getOptionalEnv(key) {
|
|
15
|
-
return process.env[key];
|
|
16
|
-
}
|
|
17
6
|
import {
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
getSkillRegistry,
|
|
8
|
+
matchPromptFromRegistry
|
|
20
9
|
} from "../../../core/skills/index.js";
|
|
21
10
|
class SkillHandlers {
|
|
22
|
-
constructor(
|
|
23
|
-
this.
|
|
24
|
-
this.userId = userId || getDefaultUserId();
|
|
11
|
+
constructor(dbPath) {
|
|
12
|
+
this.dbPath = dbPath;
|
|
25
13
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
*/
|
|
31
|
-
getStorage() {
|
|
32
|
-
if (!this.skillStorage) {
|
|
33
|
-
const url = this.redisUrl || process.env["REDIS_URL"];
|
|
34
|
-
if (!url) {
|
|
35
|
-
throw new Error("REDIS_URL not configured for skill storage");
|
|
36
|
-
}
|
|
37
|
-
this.skillStorage = getSkillStorage({
|
|
38
|
-
redisUrl: url,
|
|
39
|
-
userId: this.userId
|
|
40
|
-
});
|
|
14
|
+
registry = null;
|
|
15
|
+
getRegistry() {
|
|
16
|
+
if (!this.registry) {
|
|
17
|
+
this.registry = getSkillRegistry(this.dbPath);
|
|
41
18
|
}
|
|
42
|
-
return this.
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Get current user ID
|
|
46
|
-
*/
|
|
47
|
-
getUserId() {
|
|
48
|
-
return this.userId;
|
|
19
|
+
return this.registry;
|
|
49
20
|
}
|
|
50
|
-
/**
|
|
51
|
-
* Check if skill storage is available
|
|
52
|
-
*/
|
|
53
21
|
isAvailable() {
|
|
54
|
-
return !!(
|
|
22
|
+
return !!(process.env["STACKMEMORY_SKILLS"] || process.env["SM_SKILLS"]);
|
|
55
23
|
}
|
|
56
24
|
// ============================================================
|
|
57
25
|
// SKILL OPERATIONS
|
|
58
26
|
// ============================================================
|
|
59
|
-
/**
|
|
60
|
-
* Record a new skill/learning
|
|
61
|
-
*/
|
|
62
27
|
async recordSkill(args, context) {
|
|
63
28
|
try {
|
|
64
|
-
const
|
|
65
|
-
const skill =
|
|
29
|
+
const registry = this.getRegistry();
|
|
30
|
+
const skill = registry.createSkill({
|
|
66
31
|
content: args.content,
|
|
67
32
|
category: args.category,
|
|
68
33
|
priority: args.priority || "medium",
|
|
@@ -71,10 +36,6 @@ class SkillHandlers {
|
|
|
71
36
|
source: args.source || "observation",
|
|
72
37
|
sessionId: context.sessionId
|
|
73
38
|
});
|
|
74
|
-
logger.info("Recorded skill via MCP", {
|
|
75
|
-
skillId: skill.id,
|
|
76
|
-
category: skill.category
|
|
77
|
-
});
|
|
78
39
|
return { success: true, skill };
|
|
79
40
|
} catch (error) {
|
|
80
41
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -82,13 +43,10 @@ class SkillHandlers {
|
|
|
82
43
|
return { success: false, error: message };
|
|
83
44
|
}
|
|
84
45
|
}
|
|
85
|
-
/**
|
|
86
|
-
* Get relevant skills for current context
|
|
87
|
-
*/
|
|
88
46
|
async getRelevantSkills(args) {
|
|
89
47
|
try {
|
|
90
|
-
const
|
|
91
|
-
const skills =
|
|
48
|
+
const registry = this.getRegistry();
|
|
49
|
+
const skills = registry.getRelevantSkills({
|
|
92
50
|
tool: args.tool,
|
|
93
51
|
language: args.language,
|
|
94
52
|
framework: args.framework,
|
|
@@ -102,12 +60,9 @@ class SkillHandlers {
|
|
|
102
60
|
return { success: false, error: message };
|
|
103
61
|
}
|
|
104
62
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Query skills with filters
|
|
107
|
-
*/
|
|
108
63
|
async querySkills(args) {
|
|
109
64
|
try {
|
|
110
|
-
const
|
|
65
|
+
const registry = this.getRegistry();
|
|
111
66
|
const query = {
|
|
112
67
|
categories: args.categories,
|
|
113
68
|
priorities: args.priorities,
|
|
@@ -119,7 +74,7 @@ class SkillHandlers {
|
|
|
119
74
|
sortBy: args.sortBy || "priority",
|
|
120
75
|
sortOrder: "desc"
|
|
121
76
|
};
|
|
122
|
-
const skills =
|
|
77
|
+
const skills = registry.querySkills(query);
|
|
123
78
|
return { success: true, skills, total: skills.length };
|
|
124
79
|
} catch (error) {
|
|
125
80
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -127,16 +82,11 @@ class SkillHandlers {
|
|
|
127
82
|
return { success: false, error: message };
|
|
128
83
|
}
|
|
129
84
|
}
|
|
130
|
-
/**
|
|
131
|
-
* Validate/reinforce a skill
|
|
132
|
-
*/
|
|
133
85
|
async validateSkill(args) {
|
|
134
86
|
try {
|
|
135
|
-
const
|
|
136
|
-
const skill =
|
|
137
|
-
if (!skill) {
|
|
138
|
-
return { success: false, error: "Skill not found" };
|
|
139
|
-
}
|
|
87
|
+
const registry = this.getRegistry();
|
|
88
|
+
const skill = registry.validateSkill(args.skill_id);
|
|
89
|
+
if (!skill) return { success: false, error: "Skill not found" };
|
|
140
90
|
return { success: true, skill };
|
|
141
91
|
} catch (error) {
|
|
142
92
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -144,21 +94,16 @@ class SkillHandlers {
|
|
|
144
94
|
return { success: false, error: message };
|
|
145
95
|
}
|
|
146
96
|
}
|
|
147
|
-
/**
|
|
148
|
-
* Update a skill
|
|
149
|
-
*/
|
|
150
97
|
async updateSkill(args) {
|
|
151
98
|
try {
|
|
152
|
-
const
|
|
153
|
-
const skill =
|
|
99
|
+
const registry = this.getRegistry();
|
|
100
|
+
const skill = registry.updateSkill({
|
|
154
101
|
id: args.skill_id,
|
|
155
102
|
content: args.content,
|
|
156
103
|
priority: args.priority,
|
|
157
104
|
tags: args.tags
|
|
158
105
|
});
|
|
159
|
-
if (!skill) {
|
|
160
|
-
return { success: false, error: "Skill not found" };
|
|
161
|
-
}
|
|
106
|
+
if (!skill) return { success: false, error: "Skill not found" };
|
|
162
107
|
return { success: true, skill };
|
|
163
108
|
} catch (error) {
|
|
164
109
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -166,16 +111,11 @@ class SkillHandlers {
|
|
|
166
111
|
return { success: false, error: message };
|
|
167
112
|
}
|
|
168
113
|
}
|
|
169
|
-
/**
|
|
170
|
-
* Delete a skill
|
|
171
|
-
*/
|
|
172
114
|
async deleteSkill(args) {
|
|
173
115
|
try {
|
|
174
|
-
const
|
|
175
|
-
const deleted =
|
|
176
|
-
if (!deleted) {
|
|
177
|
-
return { success: false, error: "Skill not found" };
|
|
178
|
-
}
|
|
116
|
+
const registry = this.getRegistry();
|
|
117
|
+
const deleted = registry.deleteSkill(args.skill_id);
|
|
118
|
+
if (!deleted) return { success: false, error: "Skill not found" };
|
|
179
119
|
return { success: true };
|
|
180
120
|
} catch (error) {
|
|
181
121
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -184,16 +124,26 @@ class SkillHandlers {
|
|
|
184
124
|
}
|
|
185
125
|
}
|
|
186
126
|
// ============================================================
|
|
127
|
+
// MATCH PROMPT (NEW)
|
|
128
|
+
// ============================================================
|
|
129
|
+
async matchPrompt(args) {
|
|
130
|
+
try {
|
|
131
|
+
const result = matchPromptFromRegistry(args.prompt);
|
|
132
|
+
return { success: true, result };
|
|
133
|
+
} catch (error) {
|
|
134
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
135
|
+
logger.error("Failed to match prompt", { error: message });
|
|
136
|
+
return { success: false, error: message };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// ============================================================
|
|
187
140
|
// SESSION JOURNAL OPERATIONS
|
|
188
141
|
// ============================================================
|
|
189
|
-
/**
|
|
190
|
-
* Record a journal entry
|
|
191
|
-
*/
|
|
192
142
|
async recordJournalEntry(args, context) {
|
|
193
143
|
try {
|
|
194
|
-
const
|
|
144
|
+
const registry = this.getRegistry();
|
|
195
145
|
const sessionId = context.sessionId || "default";
|
|
196
|
-
const entry =
|
|
146
|
+
const entry = registry.createJournalEntry(
|
|
197
147
|
sessionId,
|
|
198
148
|
args.type,
|
|
199
149
|
args.title,
|
|
@@ -207,14 +157,11 @@ class SkillHandlers {
|
|
|
207
157
|
return { success: false, error: message };
|
|
208
158
|
}
|
|
209
159
|
}
|
|
210
|
-
/**
|
|
211
|
-
* Get session journal
|
|
212
|
-
*/
|
|
213
160
|
async getSessionJournal(args, context) {
|
|
214
161
|
try {
|
|
215
|
-
const
|
|
162
|
+
const registry = this.getRegistry();
|
|
216
163
|
const sessionId = args.session_id || context.sessionId || "default";
|
|
217
|
-
const entries =
|
|
164
|
+
const entries = registry.getSessionJournal(sessionId);
|
|
218
165
|
return { success: true, entries };
|
|
219
166
|
} catch (error) {
|
|
220
167
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -222,20 +169,15 @@ class SkillHandlers {
|
|
|
222
169
|
return { success: false, error: message };
|
|
223
170
|
}
|
|
224
171
|
}
|
|
225
|
-
/**
|
|
226
|
-
* Promote a journal entry to a skill
|
|
227
|
-
*/
|
|
228
172
|
async promoteToSkill(args) {
|
|
229
173
|
try {
|
|
230
|
-
const
|
|
231
|
-
const skill =
|
|
174
|
+
const registry = this.getRegistry();
|
|
175
|
+
const skill = registry.promoteToSkill(
|
|
232
176
|
args.entry_id,
|
|
233
177
|
args.category,
|
|
234
178
|
args.priority || "medium"
|
|
235
179
|
);
|
|
236
|
-
if (!skill) {
|
|
237
|
-
return { success: false, error: "Journal entry not found" };
|
|
238
|
-
}
|
|
180
|
+
if (!skill) return { success: false, error: "Journal entry not found" };
|
|
239
181
|
return { success: true, skill };
|
|
240
182
|
} catch (error) {
|
|
241
183
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -246,13 +188,10 @@ class SkillHandlers {
|
|
|
246
188
|
// ============================================================
|
|
247
189
|
// SESSION MANAGEMENT
|
|
248
190
|
// ============================================================
|
|
249
|
-
/**
|
|
250
|
-
* Start session tracking
|
|
251
|
-
*/
|
|
252
191
|
async startSession(args) {
|
|
253
192
|
try {
|
|
254
|
-
const
|
|
255
|
-
|
|
193
|
+
const registry = this.getRegistry();
|
|
194
|
+
registry.startSession(args.session_id);
|
|
256
195
|
return { success: true };
|
|
257
196
|
} catch (error) {
|
|
258
197
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -260,16 +199,11 @@ class SkillHandlers {
|
|
|
260
199
|
return { success: false, error: message };
|
|
261
200
|
}
|
|
262
201
|
}
|
|
263
|
-
/**
|
|
264
|
-
* End session and get summary
|
|
265
|
-
*/
|
|
266
202
|
async endSession(args) {
|
|
267
203
|
try {
|
|
268
|
-
const
|
|
269
|
-
const summary =
|
|
270
|
-
if (!summary) {
|
|
271
|
-
return { success: false, error: "Session not found" };
|
|
272
|
-
}
|
|
204
|
+
const registry = this.getRegistry();
|
|
205
|
+
const summary = registry.endSession(args.session_id);
|
|
206
|
+
if (!summary) return { success: false, error: "Session not found" };
|
|
273
207
|
return { success: true, summary };
|
|
274
208
|
} catch (error) {
|
|
275
209
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -278,60 +212,12 @@ class SkillHandlers {
|
|
|
278
212
|
}
|
|
279
213
|
}
|
|
280
214
|
// ============================================================
|
|
281
|
-
//
|
|
215
|
+
// METRICS
|
|
282
216
|
// ============================================================
|
|
283
|
-
/**
|
|
284
|
-
* Get promotion candidates
|
|
285
|
-
*/
|
|
286
|
-
async getPromotionCandidates() {
|
|
287
|
-
try {
|
|
288
|
-
const storage = this.getStorage();
|
|
289
|
-
const skills = await storage.getPromotionCandidates();
|
|
290
|
-
return { success: true, skills };
|
|
291
|
-
} catch (error) {
|
|
292
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
293
|
-
logger.error("Failed to get promotion candidates", { error: message });
|
|
294
|
-
return { success: false, error: message };
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* Promote skill priority
|
|
299
|
-
*/
|
|
300
|
-
async promoteSkillPriority(args) {
|
|
301
|
-
try {
|
|
302
|
-
const storage = this.getStorage();
|
|
303
|
-
const skill = await storage.promoteSkill(args.skill_id);
|
|
304
|
-
if (!skill) {
|
|
305
|
-
return { success: false, error: "Skill not found" };
|
|
306
|
-
}
|
|
307
|
-
return { success: true, skill };
|
|
308
|
-
} catch (error) {
|
|
309
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
310
|
-
logger.error("Failed to promote skill priority", { error: message });
|
|
311
|
-
return { success: false, error: message };
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* Archive stale skills
|
|
316
|
-
*/
|
|
317
|
-
async archiveStaleSkills(args) {
|
|
318
|
-
try {
|
|
319
|
-
const storage = this.getStorage();
|
|
320
|
-
const count = await storage.archiveStaleSkills(args.days_threshold || 90);
|
|
321
|
-
return { success: true, archivedCount: count };
|
|
322
|
-
} catch (error) {
|
|
323
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
324
|
-
logger.error("Failed to archive stale skills", { error: message });
|
|
325
|
-
return { success: false, error: message };
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* Get skill storage metrics
|
|
330
|
-
*/
|
|
331
217
|
async getSkillMetrics() {
|
|
332
218
|
try {
|
|
333
|
-
const
|
|
334
|
-
const metrics =
|
|
219
|
+
const registry = this.getRegistry();
|
|
220
|
+
const metrics = registry.getMetrics();
|
|
335
221
|
return { success: true, metrics };
|
|
336
222
|
} catch (error) {
|
|
337
223
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -520,6 +406,20 @@ const SKILL_TOOL_DEFINITIONS = [
|
|
|
520
406
|
type: "object",
|
|
521
407
|
properties: {}
|
|
522
408
|
}
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
name: "match_prompt",
|
|
412
|
+
description: "Match a prompt against skill rules and return scored skill suggestions",
|
|
413
|
+
inputSchema: {
|
|
414
|
+
type: "object",
|
|
415
|
+
properties: {
|
|
416
|
+
prompt: {
|
|
417
|
+
type: "string",
|
|
418
|
+
description: "The prompt text to match against skill rules"
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
required: ["prompt"]
|
|
422
|
+
}
|
|
523
423
|
}
|
|
524
424
|
];
|
|
525
425
|
export {
|
|
@@ -29,10 +29,6 @@ import { execSync } from "child_process";
|
|
|
29
29
|
import { FrameManager } from "../../core/context/index.js";
|
|
30
30
|
import { logger } from "../../core/monitoring/logger.js";
|
|
31
31
|
import { isFeatureEnabled } from "../../core/config/feature-flags.js";
|
|
32
|
-
import {
|
|
33
|
-
TaskPriority,
|
|
34
|
-
TaskStatus
|
|
35
|
-
} from "../../features/tasks/linear-task-manager.js";
|
|
36
32
|
import { BrowserMCPIntegration } from "../../features/browser/browser-mcp.js";
|
|
37
33
|
import { TraceDetector } from "../../core/trace/trace-detector.js";
|
|
38
34
|
import { LLMContextRetrieval } from "../../core/retrieval/index.js";
|
|
@@ -1353,6 +1349,15 @@ ${summary}...`, 0.8);
|
|
|
1353
1349
|
case "linear_status":
|
|
1354
1350
|
result = await this.handleLinearStatus(args);
|
|
1355
1351
|
break;
|
|
1352
|
+
case "linear_create_comment":
|
|
1353
|
+
result = await this.handleLinearCreateComment(args);
|
|
1354
|
+
break;
|
|
1355
|
+
case "linear_update_comment":
|
|
1356
|
+
result = await this.handleLinearUpdateComment(args);
|
|
1357
|
+
break;
|
|
1358
|
+
case "linear_list_comments":
|
|
1359
|
+
result = await this.handleLinearListComments(args);
|
|
1360
|
+
break;
|
|
1356
1361
|
case "get_traces":
|
|
1357
1362
|
result = await this.handleGetTraces(args);
|
|
1358
1363
|
break;
|
|
@@ -2665,6 +2670,114 @@ Tokens: Valid`
|
|
|
2665
2670
|
};
|
|
2666
2671
|
}
|
|
2667
2672
|
}
|
|
2673
|
+
async getLinearClient() {
|
|
2674
|
+
const { LinearClient } = await import("../linear/client.js");
|
|
2675
|
+
const tokens = this.linearAuthManager.loadTokens();
|
|
2676
|
+
if (!tokens) {
|
|
2677
|
+
throw new Error("Linear not configured. Run: stackmemory linear setup");
|
|
2678
|
+
}
|
|
2679
|
+
return new LinearClient({
|
|
2680
|
+
apiKey: tokens.accessToken,
|
|
2681
|
+
useBearer: true,
|
|
2682
|
+
onUnauthorized: async () => {
|
|
2683
|
+
const refreshed = await this.linearAuthManager.refreshAccessToken();
|
|
2684
|
+
return refreshed.accessToken;
|
|
2685
|
+
}
|
|
2686
|
+
});
|
|
2687
|
+
}
|
|
2688
|
+
async handleLinearCreateComment(args) {
|
|
2689
|
+
const { issue_id, body } = args;
|
|
2690
|
+
if (!issue_id || !body) {
|
|
2691
|
+
return {
|
|
2692
|
+
content: [
|
|
2693
|
+
{ type: "text", text: "Error: issue_id and body are required" }
|
|
2694
|
+
]
|
|
2695
|
+
};
|
|
2696
|
+
}
|
|
2697
|
+
try {
|
|
2698
|
+
const client = await this.getLinearClient();
|
|
2699
|
+
const comment = await client.createComment(issue_id, body);
|
|
2700
|
+
return {
|
|
2701
|
+
content: [
|
|
2702
|
+
{
|
|
2703
|
+
type: "text",
|
|
2704
|
+
text: `Comment created on ${issue_id}
|
|
2705
|
+
ID: ${comment.id}
|
|
2706
|
+
Preview: ${body.slice(0, 100)}${body.length > 100 ? "..." : ""}`
|
|
2707
|
+
}
|
|
2708
|
+
],
|
|
2709
|
+
metadata: { id: comment.id, issueId: issue_id }
|
|
2710
|
+
};
|
|
2711
|
+
} catch (error) {
|
|
2712
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
2713
|
+
return {
|
|
2714
|
+
content: [{ type: "text", text: `Error creating comment: ${msg}` }]
|
|
2715
|
+
};
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
async handleLinearUpdateComment(args) {
|
|
2719
|
+
const { comment_id, body } = args;
|
|
2720
|
+
if (!comment_id || !body) {
|
|
2721
|
+
return {
|
|
2722
|
+
content: [
|
|
2723
|
+
{ type: "text", text: "Error: comment_id and body are required" }
|
|
2724
|
+
]
|
|
2725
|
+
};
|
|
2726
|
+
}
|
|
2727
|
+
try {
|
|
2728
|
+
const client = await this.getLinearClient();
|
|
2729
|
+
const comment = await client.updateComment(comment_id, body);
|
|
2730
|
+
return {
|
|
2731
|
+
content: [
|
|
2732
|
+
{
|
|
2733
|
+
type: "text",
|
|
2734
|
+
text: `Comment ${comment_id} updated
|
|
2735
|
+
Preview: ${body.slice(0, 100)}${body.length > 100 ? "..." : ""}`
|
|
2736
|
+
}
|
|
2737
|
+
],
|
|
2738
|
+
metadata: { id: comment.id, updatedAt: comment.updatedAt }
|
|
2739
|
+
};
|
|
2740
|
+
} catch (error) {
|
|
2741
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
2742
|
+
return {
|
|
2743
|
+
content: [{ type: "text", text: `Error updating comment: ${msg}` }]
|
|
2744
|
+
};
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
async handleLinearListComments(args) {
|
|
2748
|
+
const { issue_id } = args;
|
|
2749
|
+
if (!issue_id) {
|
|
2750
|
+
return {
|
|
2751
|
+
content: [{ type: "text", text: "Error: issue_id is required" }]
|
|
2752
|
+
};
|
|
2753
|
+
}
|
|
2754
|
+
try {
|
|
2755
|
+
const client = await this.getLinearClient();
|
|
2756
|
+
const comments = await client.getComments(issue_id);
|
|
2757
|
+
const lines = comments.map(
|
|
2758
|
+
(c) => `${c.id.slice(0, 8)} | ${c.user?.name ?? "unknown"} | ${new Date(c.createdAt).toISOString().slice(0, 10)} | ${c.body.slice(0, 60).replace(/\n/g, " ")}${c.body.length > 60 ? "..." : ""}`
|
|
2759
|
+
);
|
|
2760
|
+
const text = comments.length > 0 ? `${comments.length} comments on ${issue_id}:
|
|
2761
|
+
${lines.join("\n")}` : `No comments on ${issue_id}`;
|
|
2762
|
+
return {
|
|
2763
|
+
content: [{ type: "text", text }],
|
|
2764
|
+
metadata: {
|
|
2765
|
+
count: comments.length,
|
|
2766
|
+
comments: comments.map((c) => ({
|
|
2767
|
+
id: c.id,
|
|
2768
|
+
author: c.user?.name,
|
|
2769
|
+
createdAt: c.createdAt,
|
|
2770
|
+
bodyPreview: c.body.slice(0, 200)
|
|
2771
|
+
}))
|
|
2772
|
+
}
|
|
2773
|
+
};
|
|
2774
|
+
} catch (error) {
|
|
2775
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
2776
|
+
return {
|
|
2777
|
+
content: [{ type: "text", text: `Error listing comments: ${msg}` }]
|
|
2778
|
+
};
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2668
2781
|
async handleGetTraces(args) {
|
|
2669
2782
|
const { type, minScore, limit = 20 } = args;
|
|
2670
2783
|
this.traceDetector.flush();
|
|
@@ -3238,8 +3351,6 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
3238
3351
|
server.start().catch(console.error);
|
|
3239
3352
|
}
|
|
3240
3353
|
export {
|
|
3241
|
-
TaskPriority,
|
|
3242
|
-
TaskStatus,
|
|
3243
3354
|
server_default as default,
|
|
3244
3355
|
runMCPServer
|
|
3245
3356
|
};
|