@stackmemoryai/stackmemory 0.5.23 → 0.5.25

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 (72) hide show
  1. package/bin/opencode-sm +6 -0
  2. package/dist/cli/claude-sm.js +2 -0
  3. package/dist/cli/claude-sm.js.map +2 -2
  4. package/dist/cli/commands/discovery.js +279 -0
  5. package/dist/cli/commands/discovery.js.map +7 -0
  6. package/dist/cli/index.js +2 -0
  7. package/dist/cli/index.js.map +2 -2
  8. package/dist/cli/opencode-sm.js +448 -0
  9. package/dist/cli/opencode-sm.js.map +7 -0
  10. package/dist/core/retrieval/llm-context-retrieval.js +1 -3
  11. package/dist/core/retrieval/llm-context-retrieval.js.map +3 -3
  12. package/dist/integrations/mcp/handlers/discovery-handlers.js +497 -0
  13. package/dist/integrations/mcp/handlers/discovery-handlers.js.map +7 -0
  14. package/dist/integrations/mcp/handlers/index.js +40 -12
  15. package/dist/integrations/mcp/handlers/index.js.map +2 -2
  16. package/dist/integrations/mcp/server.js +270 -0
  17. package/dist/integrations/mcp/server.js.map +2 -2
  18. package/dist/integrations/mcp/tool-definitions.js +141 -5
  19. package/dist/integrations/mcp/tool-definitions.js.map +2 -2
  20. package/package.json +3 -2
  21. package/dist/cli/commands/agent.js +0 -286
  22. package/dist/cli/commands/agent.js.map +0 -7
  23. package/dist/cli/commands/chromadb.js +0 -482
  24. package/dist/cli/commands/chromadb.js.map +0 -7
  25. package/dist/cli/commands/gc.js +0 -251
  26. package/dist/cli/commands/gc.js.map +0 -7
  27. package/dist/cli/commands/infinite-storage.js +0 -292
  28. package/dist/cli/commands/infinite-storage.js.map +0 -7
  29. package/dist/cli/commands/linear-create.js +0 -171
  30. package/dist/cli/commands/linear-create.js.map +0 -7
  31. package/dist/cli/commands/linear-list.js +0 -103
  32. package/dist/cli/commands/linear-list.js.map +0 -7
  33. package/dist/cli/commands/linear-migrate.js +0 -64
  34. package/dist/cli/commands/linear-migrate.js.map +0 -7
  35. package/dist/cli/commands/linear-test.js +0 -134
  36. package/dist/cli/commands/linear-test.js.map +0 -7
  37. package/dist/cli/commands/tui.js +0 -77
  38. package/dist/cli/commands/tui.js.map +0 -7
  39. package/dist/cli/commands/webhook.js +0 -181
  40. package/dist/cli/commands/webhook.js.map +0 -7
  41. package/dist/cli/streamlined-cli.js +0 -144
  42. package/dist/cli/streamlined-cli.js.map +0 -7
  43. package/dist/core/events/event-bus.js +0 -110
  44. package/dist/core/events/event-bus.js.map +0 -7
  45. package/dist/core/frame/workflow-templates-stub.js +0 -42
  46. package/dist/core/frame/workflow-templates-stub.js.map +0 -7
  47. package/dist/core/plugins/plugin-interface.js +0 -87
  48. package/dist/core/plugins/plugin-interface.js.map +0 -7
  49. package/dist/core/session/clear-survival-stub.js +0 -53
  50. package/dist/core/session/clear-survival-stub.js.map +0 -7
  51. package/dist/core/storage/chromadb-simple.js +0 -172
  52. package/dist/core/storage/chromadb-simple.js.map +0 -7
  53. package/dist/core/storage/simplified-storage.js +0 -328
  54. package/dist/core/storage/simplified-storage.js.map +0 -7
  55. package/dist/features/tasks/pebbles-task-store.js +0 -647
  56. package/dist/features/tasks/pebbles-task-store.js.map +0 -7
  57. package/dist/integrations/linear/sync-enhanced.js +0 -202
  58. package/dist/integrations/linear/sync-enhanced.js.map +0 -7
  59. package/dist/plugins/linear/index.js +0 -166
  60. package/dist/plugins/linear/index.js.map +0 -7
  61. package/dist/plugins/loader.js +0 -57
  62. package/dist/plugins/loader.js.map +0 -7
  63. package/dist/plugins/plugin-interface.js +0 -67
  64. package/dist/plugins/plugin-interface.js.map +0 -7
  65. package/dist/plugins/ralph/simple-ralph-plugin.js +0 -305
  66. package/dist/plugins/ralph/simple-ralph-plugin.js.map +0 -7
  67. package/dist/plugins/ralph/use-cases/code-generator.js +0 -151
  68. package/dist/plugins/ralph/use-cases/code-generator.js.map +0 -7
  69. package/dist/plugins/ralph/use-cases/test-generator.js +0 -201
  70. package/dist/plugins/ralph/use-cases/test-generator.js.map +0 -7
  71. package/dist/utils/logger.js +0 -52
  72. package/dist/utils/logger.js.map +0 -7
@@ -0,0 +1,497 @@
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 { logger } from "../../../core/monitoring/logger.js";
6
+ import { execSync } from "child_process";
7
+ import { existsSync, readFileSync } from "fs";
8
+ import { join } from "path";
9
+ class DiscoveryHandlers {
10
+ constructor(deps) {
11
+ this.deps = deps;
12
+ }
13
+ /**
14
+ * Discover relevant files based on current context
15
+ */
16
+ async handleDiscover(args) {
17
+ try {
18
+ const {
19
+ query,
20
+ depth = "medium",
21
+ includePatterns = ["*.ts", "*.tsx", "*.js", "*.md", "*.json"],
22
+ excludePatterns = ["node_modules", "dist", ".git", "*.min.js"],
23
+ maxFiles = 20
24
+ } = args;
25
+ logger.info("Starting discovery", { query, depth });
26
+ const keywords = this.extractContextKeywords(query);
27
+ const mdContext = this.parseMdFiles();
28
+ const recentFiles = this.getRecentFilesFromContext();
29
+ const discoveredFiles = await this.searchCodebase(
30
+ keywords,
31
+ includePatterns,
32
+ excludePatterns,
33
+ depth,
34
+ maxFiles
35
+ );
36
+ const rankedFiles = this.rankFiles(
37
+ discoveredFiles,
38
+ recentFiles,
39
+ keywords
40
+ );
41
+ const contextSummary = this.generateContextSummary(keywords, rankedFiles);
42
+ const result = {
43
+ files: rankedFiles.slice(0, maxFiles),
44
+ keywords,
45
+ contextSummary,
46
+ mdContext
47
+ };
48
+ return {
49
+ content: [
50
+ {
51
+ type: "text",
52
+ text: this.formatDiscoveryResult(result)
53
+ }
54
+ ],
55
+ metadata: result
56
+ };
57
+ } catch (error) {
58
+ logger.error("Discovery failed", error);
59
+ throw error;
60
+ }
61
+ }
62
+ /**
63
+ * Get related files to a specific file or concept
64
+ */
65
+ async handleRelatedFiles(args) {
66
+ try {
67
+ const { file, concept, maxFiles = 10 } = args;
68
+ if (!file && !concept) {
69
+ throw new Error("Either file or concept is required");
70
+ }
71
+ let relatedFiles = [];
72
+ if (file) {
73
+ relatedFiles = this.findFileReferences(file, maxFiles);
74
+ }
75
+ if (concept) {
76
+ const conceptFiles = this.searchForConcept(concept, maxFiles);
77
+ relatedFiles = this.mergeAndDedupe(relatedFiles, conceptFiles);
78
+ }
79
+ return {
80
+ content: [
81
+ {
82
+ type: "text",
83
+ text: this.formatRelatedFiles(relatedFiles, file, concept)
84
+ }
85
+ ],
86
+ metadata: { relatedFiles }
87
+ };
88
+ } catch (error) {
89
+ logger.error("Related files search failed", error);
90
+ throw error;
91
+ }
92
+ }
93
+ /**
94
+ * Get session summary with actionable context
95
+ */
96
+ async handleSessionSummary(args) {
97
+ try {
98
+ const { includeFiles = true, includeDecisions = true } = args;
99
+ const hotStack = this.deps.frameManager.getHotStackContext(50);
100
+ const recentFiles = includeFiles ? this.getRecentFilesFromContext() : [];
101
+ const decisions = includeDecisions ? this.getRecentDecisions() : [];
102
+ const summary = {
103
+ activeFrames: hotStack.length,
104
+ currentGoal: hotStack[hotStack.length - 1]?.header?.goal || "No active task",
105
+ recentFiles: recentFiles.slice(0, 10),
106
+ decisions: decisions.slice(0, 5),
107
+ stackDepth: this.deps.frameManager.getStackDepth()
108
+ };
109
+ return {
110
+ content: [
111
+ {
112
+ type: "text",
113
+ text: this.formatSessionSummary(summary)
114
+ }
115
+ ],
116
+ metadata: summary
117
+ };
118
+ } catch (error) {
119
+ logger.error("Session summary failed", error);
120
+ throw error;
121
+ }
122
+ }
123
+ // ===============================
124
+ // Private helper methods
125
+ // ===============================
126
+ extractContextKeywords(query) {
127
+ const keywords = /* @__PURE__ */ new Set();
128
+ if (query) {
129
+ const queryWords = query.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
130
+ queryWords.forEach((w) => keywords.add(w));
131
+ }
132
+ const hotStack = this.deps.frameManager.getHotStackContext(20);
133
+ for (const frame of hotStack) {
134
+ if (frame.header?.goal) {
135
+ const goalWords = frame.header.goal.toLowerCase().split(/[\s\-_]+/).filter((w) => w.length > 2);
136
+ goalWords.forEach((w) => keywords.add(w));
137
+ }
138
+ frame.header?.constraints?.forEach((c) => {
139
+ const words = c.toLowerCase().split(/[\s\-_]+/).filter((w) => w.length > 2);
140
+ words.forEach((w) => keywords.add(w));
141
+ });
142
+ frame.recentEvents?.forEach((evt) => {
143
+ if (evt.data?.content) {
144
+ const words = String(evt.data.content).toLowerCase().split(/[\s\-_]+/).filter((w) => w.length > 3).slice(0, 5);
145
+ words.forEach((w) => keywords.add(w));
146
+ }
147
+ });
148
+ }
149
+ try {
150
+ const fileEvents = this.deps.db.prepare(
151
+ `
152
+ SELECT DISTINCT data FROM events e
153
+ JOIN frames f ON e.frame_id = f.frame_id
154
+ WHERE e.type IN ('file_read', 'file_write', 'file_edit')
155
+ ORDER BY e.timestamp DESC
156
+ LIMIT 20
157
+ `
158
+ ).all();
159
+ for (const evt of fileEvents) {
160
+ try {
161
+ const data = JSON.parse(evt.data || "{}");
162
+ if (data.path) {
163
+ const pathParts = data.path.split("/").slice(-2);
164
+ pathParts.forEach((part) => {
165
+ const words = part.replace(/\.[^.]+$/, "").split(/[\-_]+/).filter((w) => w.length > 2);
166
+ words.forEach((w) => keywords.add(w.toLowerCase()));
167
+ });
168
+ }
169
+ } catch {
170
+ }
171
+ }
172
+ } catch {
173
+ }
174
+ const stopwords = /* @__PURE__ */ new Set([
175
+ "the",
176
+ "and",
177
+ "for",
178
+ "with",
179
+ "this",
180
+ "that",
181
+ "from",
182
+ "have",
183
+ "has",
184
+ "been",
185
+ "will",
186
+ "can",
187
+ "should",
188
+ "would",
189
+ "could",
190
+ "function",
191
+ "const",
192
+ "let",
193
+ "var",
194
+ "import",
195
+ "export",
196
+ "return",
197
+ "async",
198
+ "await"
199
+ ]);
200
+ return Array.from(keywords).filter((k) => !stopwords.has(k));
201
+ }
202
+ parseMdFiles() {
203
+ const mdContext = {};
204
+ const mdFiles = ["CLAUDE.md", "README.md", ".stackmemory/context.md"];
205
+ for (const mdFile of mdFiles) {
206
+ const fullPath = join(this.deps.projectRoot, mdFile);
207
+ if (existsSync(fullPath)) {
208
+ try {
209
+ const content = readFileSync(fullPath, "utf8");
210
+ const sections = this.extractMdSections(content);
211
+ mdContext[mdFile] = sections;
212
+ } catch {
213
+ }
214
+ }
215
+ }
216
+ const homeClaude = join(process.env["HOME"] || "", ".claude", "CLAUDE.md");
217
+ if (existsSync(homeClaude)) {
218
+ try {
219
+ const content = readFileSync(homeClaude, "utf8");
220
+ mdContext["~/.claude/CLAUDE.md"] = this.extractMdSections(content);
221
+ } catch {
222
+ }
223
+ }
224
+ return mdContext;
225
+ }
226
+ extractMdSections(content) {
227
+ const lines = content.split("\n");
228
+ const sections = [];
229
+ let currentSection = "";
230
+ let inCodeBlock = false;
231
+ for (const line of lines) {
232
+ if (line.startsWith("```")) {
233
+ inCodeBlock = !inCodeBlock;
234
+ continue;
235
+ }
236
+ if (inCodeBlock) continue;
237
+ if (line.startsWith("#")) {
238
+ if (currentSection) sections.push(currentSection.trim());
239
+ currentSection = line + "\n";
240
+ } else if (currentSection && line.trim()) {
241
+ currentSection += line + "\n";
242
+ }
243
+ }
244
+ if (currentSection) sections.push(currentSection.trim());
245
+ return sections.map((s) => s.length > 500 ? s.slice(0, 500) + "..." : s).join("\n\n");
246
+ }
247
+ getRecentFilesFromContext() {
248
+ const files = /* @__PURE__ */ new Set();
249
+ try {
250
+ const fileEvents = this.deps.db.prepare(
251
+ `
252
+ SELECT DISTINCT data FROM events e
253
+ JOIN frames f ON e.frame_id = f.frame_id
254
+ WHERE e.type IN ('file_read', 'file_write', 'file_edit', 'tool_call')
255
+ AND e.timestamp > ?
256
+ ORDER BY e.timestamp DESC
257
+ LIMIT 50
258
+ `
259
+ ).all(Math.floor(Date.now() / 1e3) - 3600);
260
+ for (const evt of fileEvents) {
261
+ try {
262
+ const data = JSON.parse(evt.data || "{}");
263
+ if (data.path) files.add(data.path);
264
+ if (data.file) files.add(data.file);
265
+ if (data.file_path) files.add(data.file_path);
266
+ } catch {
267
+ }
268
+ }
269
+ } catch {
270
+ }
271
+ try {
272
+ const gitStatus = execSync("git status --porcelain", {
273
+ cwd: this.deps.projectRoot,
274
+ encoding: "utf8"
275
+ });
276
+ const modifiedFiles = gitStatus.split("\n").filter((l) => l.trim()).map((l) => l.slice(3).trim()).filter((f) => f);
277
+ modifiedFiles.forEach((f) => files.add(f));
278
+ } catch {
279
+ }
280
+ return Array.from(files);
281
+ }
282
+ async searchCodebase(keywords, includePatterns, excludePatterns, depth, maxFiles) {
283
+ const files = [];
284
+ const maxResults = depth === "shallow" ? 10 : depth === "medium" ? 25 : 50;
285
+ for (const keyword of keywords.slice(0, 10)) {
286
+ try {
287
+ const excludeArgs = excludePatterns.map((p) => `--exclude-dir=${p}`).join(" ");
288
+ const includeArgs = includePatterns.map((p) => `--include=${p}`).join(" ");
289
+ const cmd = `grep -ril ${excludeArgs} ${includeArgs} "${keyword}" . 2>/dev/null | head -${maxResults}`;
290
+ const result = execSync(cmd, {
291
+ cwd: this.deps.projectRoot,
292
+ encoding: "utf8",
293
+ timeout: 5e3
294
+ });
295
+ const matchedFiles = result.split("\n").filter((f) => f.trim());
296
+ for (const file of matchedFiles) {
297
+ const cleanPath = file.replace(/^\.\//, "");
298
+ const existing = files.find((f) => f.path === cleanPath);
299
+ if (existing) {
300
+ existing.matchedKeywords = existing.matchedKeywords || [];
301
+ if (!existing.matchedKeywords.includes(keyword)) {
302
+ existing.matchedKeywords.push(keyword);
303
+ }
304
+ } else {
305
+ files.push({
306
+ path: cleanPath,
307
+ relevance: "medium",
308
+ reason: `Contains keyword: ${keyword}`,
309
+ matchedKeywords: [keyword]
310
+ });
311
+ }
312
+ }
313
+ } catch {
314
+ }
315
+ }
316
+ for (const file of files) {
317
+ const matchCount = file.matchedKeywords?.length || 0;
318
+ if (matchCount >= 3) {
319
+ file.relevance = "high";
320
+ file.reason = `Matches ${matchCount} keywords: ${file.matchedKeywords?.slice(0, 3).join(", ")}`;
321
+ }
322
+ }
323
+ return files;
324
+ }
325
+ rankFiles(discovered, recent, keywords) {
326
+ const recentSet = new Set(recent);
327
+ for (const recentFile of recent) {
328
+ const existing = discovered.find((f) => f.path === recentFile);
329
+ if (existing) {
330
+ existing.relevance = "high";
331
+ existing.reason = "Recently accessed + " + existing.reason;
332
+ } else {
333
+ discovered.push({
334
+ path: recentFile,
335
+ relevance: "high",
336
+ reason: "Recently accessed in context"
337
+ });
338
+ }
339
+ }
340
+ return discovered.sort((a, b) => {
341
+ const relevanceOrder = { high: 3, medium: 2, low: 1 };
342
+ const relDiff = relevanceOrder[b.relevance] - relevanceOrder[a.relevance];
343
+ if (relDiff !== 0) return relDiff;
344
+ const aMatches = a.matchedKeywords?.length || 0;
345
+ const bMatches = b.matchedKeywords?.length || 0;
346
+ return bMatches - aMatches;
347
+ });
348
+ }
349
+ findFileReferences(file, maxFiles) {
350
+ const results = [];
351
+ try {
352
+ const basename = file.replace(/\.[^.]+$/, "");
353
+ const cmd = `grep -ril "from.*${basename}" . --include="*.ts" --include="*.tsx" --include="*.js" --exclude-dir=node_modules --exclude-dir=dist 2>/dev/null | head -${maxFiles}`;
354
+ const result = execSync(cmd, {
355
+ cwd: this.deps.projectRoot,
356
+ encoding: "utf8",
357
+ timeout: 5e3
358
+ });
359
+ const files = result.split("\n").filter((f) => f.trim());
360
+ for (const f of files) {
361
+ results.push({
362
+ path: f.replace(/^\.\//, ""),
363
+ relevance: "high",
364
+ reason: `Imports ${file}`
365
+ });
366
+ }
367
+ } catch {
368
+ }
369
+ return results;
370
+ }
371
+ searchForConcept(concept, maxFiles) {
372
+ const results = [];
373
+ try {
374
+ const cmd = `grep -ril "${concept}" . --include="*.ts" --include="*.tsx" --include="*.md" --exclude-dir=node_modules --exclude-dir=dist 2>/dev/null | head -${maxFiles}`;
375
+ const result = execSync(cmd, {
376
+ cwd: this.deps.projectRoot,
377
+ encoding: "utf8",
378
+ timeout: 5e3
379
+ });
380
+ const files = result.split("\n").filter((f) => f.trim());
381
+ for (const f of files) {
382
+ results.push({
383
+ path: f.replace(/^\.\//, ""),
384
+ relevance: "medium",
385
+ reason: `Contains "${concept}"`
386
+ });
387
+ }
388
+ } catch {
389
+ }
390
+ return results;
391
+ }
392
+ mergeAndDedupe(a, b) {
393
+ const pathSet = new Set(a.map((f) => f.path));
394
+ const merged = [...a];
395
+ for (const file of b) {
396
+ if (!pathSet.has(file.path)) {
397
+ merged.push(file);
398
+ pathSet.add(file.path);
399
+ }
400
+ }
401
+ return merged;
402
+ }
403
+ getRecentDecisions() {
404
+ try {
405
+ const decisions = this.deps.db.prepare(
406
+ `
407
+ SELECT a.text, a.type, a.priority, f.name as frame_name, a.created_at
408
+ FROM anchors a
409
+ JOIN frames f ON a.frame_id = f.frame_id
410
+ WHERE a.type IN ('DECISION', 'CONSTRAINT', 'FACT')
411
+ ORDER BY a.created_at DESC
412
+ LIMIT 10
413
+ `
414
+ ).all();
415
+ return decisions;
416
+ } catch {
417
+ return [];
418
+ }
419
+ }
420
+ generateContextSummary(keywords, files) {
421
+ const hotStack = this.deps.frameManager.getHotStackContext(5);
422
+ const currentGoal = hotStack[hotStack.length - 1]?.header?.goal;
423
+ let summary = "";
424
+ if (currentGoal) {
425
+ summary += `Current task: ${currentGoal}
426
+ `;
427
+ }
428
+ summary += `Context keywords: ${keywords.slice(0, 10).join(", ")}
429
+ `;
430
+ summary += `Relevant files found: ${files.length}
431
+ `;
432
+ summary += `High relevance: ${files.filter((f) => f.relevance === "high").length}`;
433
+ return summary;
434
+ }
435
+ formatDiscoveryResult(result) {
436
+ let output = "# Discovery Results\n\n";
437
+ output += "## Context Summary\n";
438
+ output += result.contextSummary + "\n\n";
439
+ output += "## Relevant Files\n\n";
440
+ for (const file of result.files.slice(0, 15)) {
441
+ const icon = file.relevance === "high" ? "[HIGH]" : file.relevance === "medium" ? "[MED]" : "[LOW]";
442
+ output += `${icon} ${file.path}
443
+ `;
444
+ output += ` ${file.reason}
445
+ `;
446
+ }
447
+ if (result.keywords.length > 0) {
448
+ output += "\n## Keywords Used\n";
449
+ output += result.keywords.slice(0, 15).join(", ") + "\n";
450
+ }
451
+ return output;
452
+ }
453
+ formatRelatedFiles(files, file, concept) {
454
+ let output = "# Related Files\n\n";
455
+ if (file) output += `Related to file: ${file}
456
+ `;
457
+ if (concept) output += `Related to concept: ${concept}
458
+ `;
459
+ output += "\n";
460
+ for (const f of files) {
461
+ output += `- ${f.path}
462
+ ${f.reason}
463
+ `;
464
+ }
465
+ return output;
466
+ }
467
+ formatSessionSummary(summary) {
468
+ let output = "# Session Summary\n\n";
469
+ output += `**Current Goal:** ${summary.currentGoal}
470
+ `;
471
+ output += `**Active Frames:** ${summary.activeFrames}
472
+ `;
473
+ output += `**Stack Depth:** ${summary.stackDepth}
474
+
475
+ `;
476
+ if (summary.recentFiles.length > 0) {
477
+ output += "## Recent Files\n";
478
+ for (const f of summary.recentFiles) {
479
+ output += `- ${f}
480
+ `;
481
+ }
482
+ output += "\n";
483
+ }
484
+ if (summary.decisions.length > 0) {
485
+ output += "## Recent Decisions\n";
486
+ for (const d of summary.decisions) {
487
+ output += `- [${d.type}] ${d.text}
488
+ `;
489
+ }
490
+ }
491
+ return output;
492
+ }
493
+ }
494
+ export {
495
+ DiscoveryHandlers
496
+ };
497
+ //# sourceMappingURL=discovery-handlers.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/integrations/mcp/handlers/discovery-handlers.ts"],
4
+ "sourcesContent": ["/**\n * Discovery MCP Tool Handlers\n * Intelligently discovers relevant files based on current context\n */\n\nimport { FrameManager } from '../../../core/context/frame-manager.js';\nimport { LLMContextRetrieval } from '../../../core/retrieval/index.js';\nimport { logger } from '../../../core/monitoring/logger.js';\nimport { execSync } from 'child_process';\nimport { existsSync, readFileSync, readdirSync, statSync } from 'fs';\nimport { join, relative, extname } from 'path';\nimport Database from 'better-sqlite3';\n\nexport interface DiscoveryDependencies {\n frameManager: FrameManager;\n contextRetrieval: LLMContextRetrieval;\n db: Database.Database;\n projectRoot: string;\n}\n\ninterface DiscoveredFile {\n path: string;\n relevance: 'high' | 'medium' | 'low';\n reason: string;\n matchedKeywords?: string[];\n excerpt?: string;\n}\n\ninterface DiscoveryResult {\n files: DiscoveredFile[];\n keywords: string[];\n contextSummary: string;\n mdContext: Record<string, string>;\n}\n\nexport class DiscoveryHandlers {\n constructor(private deps: DiscoveryDependencies) {}\n\n /**\n * Discover relevant files based on current context\n */\n async handleDiscover(args: {\n query?: string;\n depth?: 'shallow' | 'medium' | 'deep';\n includePatterns?: string[];\n excludePatterns?: string[];\n maxFiles?: number;\n }): Promise<any> {\n try {\n const {\n query,\n depth = 'medium',\n includePatterns = ['*.ts', '*.tsx', '*.js', '*.md', '*.json'],\n excludePatterns = ['node_modules', 'dist', '.git', '*.min.js'],\n maxFiles = 20,\n } = args;\n\n logger.info('Starting discovery', { query, depth });\n\n // Step 1: Extract keywords from current context\n const keywords = this.extractContextKeywords(query);\n\n // Step 2: Parse .md files for additional context\n const mdContext = this.parseMdFiles();\n\n // Step 3: Get recently touched files from frames\n const recentFiles = this.getRecentFilesFromContext();\n\n // Step 4: Search codebase for relevant files\n const discoveredFiles = await this.searchCodebase(\n keywords,\n includePatterns,\n excludePatterns,\n depth,\n maxFiles\n );\n\n // Step 5: Merge and rank results\n const rankedFiles = this.rankFiles(\n discoveredFiles,\n recentFiles,\n keywords\n );\n\n // Step 6: Generate context summary\n const contextSummary = this.generateContextSummary(keywords, rankedFiles);\n\n const result: DiscoveryResult = {\n files: rankedFiles.slice(0, maxFiles),\n keywords,\n contextSummary,\n mdContext,\n };\n\n return {\n content: [\n {\n type: 'text',\n text: this.formatDiscoveryResult(result),\n },\n ],\n metadata: result,\n };\n } catch (error) {\n logger.error('Discovery failed', error);\n throw error;\n }\n }\n\n /**\n * Get related files to a specific file or concept\n */\n async handleRelatedFiles(args: {\n file?: string;\n concept?: string;\n maxFiles?: number;\n }): Promise<any> {\n try {\n const { file, concept, maxFiles = 10 } = args;\n\n if (!file && !concept) {\n throw new Error('Either file or concept is required');\n }\n\n let relatedFiles: DiscoveredFile[] = [];\n\n if (file) {\n // Find files that import/reference this file\n relatedFiles = this.findFileReferences(file, maxFiles);\n }\n\n if (concept) {\n // Search for files mentioning this concept\n const conceptFiles = this.searchForConcept(concept, maxFiles);\n relatedFiles = this.mergeAndDedupe(relatedFiles, conceptFiles);\n }\n\n return {\n content: [\n {\n type: 'text',\n text: this.formatRelatedFiles(relatedFiles, file, concept),\n },\n ],\n metadata: { relatedFiles },\n };\n } catch (error) {\n logger.error('Related files search failed', error);\n throw error;\n }\n }\n\n /**\n * Get session summary with actionable context\n */\n async handleSessionSummary(args: {\n includeFiles?: boolean;\n includeDecisions?: boolean;\n }): Promise<any> {\n try {\n const { includeFiles = true, includeDecisions = true } = args;\n\n const hotStack = this.deps.frameManager.getHotStackContext(50);\n const recentFiles = includeFiles ? this.getRecentFilesFromContext() : [];\n const decisions = includeDecisions ? this.getRecentDecisions() : [];\n\n const summary = {\n activeFrames: hotStack.length,\n currentGoal:\n hotStack[hotStack.length - 1]?.header?.goal || 'No active task',\n recentFiles: recentFiles.slice(0, 10),\n decisions: decisions.slice(0, 5),\n stackDepth: this.deps.frameManager.getStackDepth(),\n };\n\n return {\n content: [\n {\n type: 'text',\n text: this.formatSessionSummary(summary),\n },\n ],\n metadata: summary,\n };\n } catch (error) {\n logger.error('Session summary failed', error);\n throw error;\n }\n }\n\n // ===============================\n // Private helper methods\n // ===============================\n\n private extractContextKeywords(query?: string): string[] {\n const keywords: Set<string> = new Set();\n\n // Add query terms\n if (query) {\n const queryWords = query\n .toLowerCase()\n .split(/\\s+/)\n .filter((w) => w.length > 2);\n queryWords.forEach((w) => keywords.add(w));\n }\n\n // Extract from current frames\n const hotStack = this.deps.frameManager.getHotStackContext(20);\n for (const frame of hotStack) {\n // Frame name/goal\n if (frame.header?.goal) {\n const goalWords = frame.header.goal\n .toLowerCase()\n .split(/[\\s\\-_]+/)\n .filter((w) => w.length > 2);\n goalWords.forEach((w) => keywords.add(w));\n }\n\n // Constraints\n frame.header?.constraints?.forEach((c: string) => {\n const words = c\n .toLowerCase()\n .split(/[\\s\\-_]+/)\n .filter((w) => w.length > 2);\n words.forEach((w) => keywords.add(w));\n });\n\n // Recent events\n frame.recentEvents?.forEach((evt: any) => {\n if (evt.data?.content) {\n const words = String(evt.data.content)\n .toLowerCase()\n .split(/[\\s\\-_]+/)\n .filter((w) => w.length > 3)\n .slice(0, 5);\n words.forEach((w) => keywords.add(w));\n }\n });\n }\n\n // Extract from recent files in events\n try {\n const fileEvents = this.deps.db\n .prepare(\n `\n SELECT DISTINCT data FROM events e\n JOIN frames f ON e.frame_id = f.frame_id\n WHERE e.type IN ('file_read', 'file_write', 'file_edit')\n ORDER BY e.timestamp DESC\n LIMIT 20\n `\n )\n .all() as any[];\n\n for (const evt of fileEvents) {\n try {\n const data = JSON.parse(evt.data || '{}');\n if (data.path) {\n // Extract meaningful parts from file path\n const pathParts = data.path.split('/').slice(-2);\n pathParts.forEach((part: string) => {\n const words = part\n .replace(/\\.[^.]+$/, '')\n .split(/[\\-_]+/)\n .filter((w) => w.length > 2);\n words.forEach((w) => keywords.add(w.toLowerCase()));\n });\n }\n } catch {}\n }\n } catch {}\n\n // Remove common stopwords\n const stopwords = new Set([\n 'the',\n 'and',\n 'for',\n 'with',\n 'this',\n 'that',\n 'from',\n 'have',\n 'has',\n 'been',\n 'will',\n 'can',\n 'should',\n 'would',\n 'could',\n 'function',\n 'const',\n 'let',\n 'var',\n 'import',\n 'export',\n 'return',\n 'async',\n 'await',\n ]);\n\n return Array.from(keywords).filter((k) => !stopwords.has(k));\n }\n\n private parseMdFiles(): Record<string, string> {\n const mdContext: Record<string, string> = {};\n const mdFiles = ['CLAUDE.md', 'README.md', '.stackmemory/context.md'];\n\n for (const mdFile of mdFiles) {\n const fullPath = join(this.deps.projectRoot, mdFile);\n if (existsSync(fullPath)) {\n try {\n const content = readFileSync(fullPath, 'utf8');\n // Extract key sections\n const sections = this.extractMdSections(content);\n mdContext[mdFile] = sections;\n } catch {}\n }\n }\n\n // Also check ~/.claude/CLAUDE.md\n const homeClaude = join(process.env['HOME'] || '', '.claude', 'CLAUDE.md');\n if (existsSync(homeClaude)) {\n try {\n const content = readFileSync(homeClaude, 'utf8');\n mdContext['~/.claude/CLAUDE.md'] = this.extractMdSections(content);\n } catch {}\n }\n\n return mdContext;\n }\n\n private extractMdSections(content: string): string {\n // Extract meaningful sections (headers and their content)\n const lines = content.split('\\n');\n const sections: string[] = [];\n let currentSection = '';\n let inCodeBlock = false;\n\n for (const line of lines) {\n if (line.startsWith('```')) {\n inCodeBlock = !inCodeBlock;\n continue;\n }\n if (inCodeBlock) continue;\n\n if (line.startsWith('#')) {\n if (currentSection) sections.push(currentSection.trim());\n currentSection = line + '\\n';\n } else if (currentSection && line.trim()) {\n currentSection += line + '\\n';\n }\n }\n if (currentSection) sections.push(currentSection.trim());\n\n // Return condensed version (first 500 chars of each section)\n return sections\n .map((s) => (s.length > 500 ? s.slice(0, 500) + '...' : s))\n .join('\\n\\n');\n }\n\n private getRecentFilesFromContext(): string[] {\n const files: Set<string> = new Set();\n\n try {\n const fileEvents = this.deps.db\n .prepare(\n `\n SELECT DISTINCT data FROM events e\n JOIN frames f ON e.frame_id = f.frame_id\n WHERE e.type IN ('file_read', 'file_write', 'file_edit', 'tool_call')\n AND e.timestamp > ?\n ORDER BY e.timestamp DESC\n LIMIT 50\n `\n )\n .all(Math.floor(Date.now() / 1000) - 3600) as any[]; // Last hour\n\n for (const evt of fileEvents) {\n try {\n const data = JSON.parse(evt.data || '{}');\n if (data.path) files.add(data.path);\n if (data.file) files.add(data.file);\n if (data.file_path) files.add(data.file_path);\n } catch {}\n }\n } catch {}\n\n // Also get from git status\n try {\n const gitStatus = execSync('git status --porcelain', {\n cwd: this.deps.projectRoot,\n encoding: 'utf8',\n });\n const modifiedFiles = gitStatus\n .split('\\n')\n .filter((l) => l.trim())\n .map((l) => l.slice(3).trim())\n .filter((f) => f);\n modifiedFiles.forEach((f) => files.add(f));\n } catch {}\n\n return Array.from(files);\n }\n\n private async searchCodebase(\n keywords: string[],\n includePatterns: string[],\n excludePatterns: string[],\n depth: 'shallow' | 'medium' | 'deep',\n maxFiles: number\n ): Promise<DiscoveredFile[]> {\n const files: DiscoveredFile[] = [];\n const maxResults = depth === 'shallow' ? 10 : depth === 'medium' ? 25 : 50;\n\n // Build grep command for each keyword\n for (const keyword of keywords.slice(0, 10)) {\n try {\n const excludeArgs = excludePatterns\n .map((p) => `--exclude-dir=${p}`)\n .join(' ');\n const includeArgs = includePatterns\n .map((p) => `--include=${p}`)\n .join(' ');\n\n const cmd = `grep -ril ${excludeArgs} ${includeArgs} \"${keyword}\" . 2>/dev/null | head -${maxResults}`;\n const result = execSync(cmd, {\n cwd: this.deps.projectRoot,\n encoding: 'utf8',\n timeout: 5000,\n });\n\n const matchedFiles = result.split('\\n').filter((f) => f.trim());\n for (const file of matchedFiles) {\n const cleanPath = file.replace(/^\\.\\//, '');\n const existing = files.find((f) => f.path === cleanPath);\n if (existing) {\n existing.matchedKeywords = existing.matchedKeywords || [];\n if (!existing.matchedKeywords.includes(keyword)) {\n existing.matchedKeywords.push(keyword);\n }\n } else {\n files.push({\n path: cleanPath,\n relevance: 'medium',\n reason: `Contains keyword: ${keyword}`,\n matchedKeywords: [keyword],\n });\n }\n }\n } catch {\n // Grep failed for this keyword, continue\n }\n }\n\n // Boost relevance for files with multiple keyword matches\n for (const file of files) {\n const matchCount = file.matchedKeywords?.length || 0;\n if (matchCount >= 3) {\n file.relevance = 'high';\n file.reason = `Matches ${matchCount} keywords: ${file.matchedKeywords?.slice(0, 3).join(', ')}`;\n }\n }\n\n return files;\n }\n\n private rankFiles(\n discovered: DiscoveredFile[],\n recent: string[],\n keywords: string[]\n ): DiscoveredFile[] {\n const recentSet = new Set(recent);\n\n // Add recent files with high relevance\n for (const recentFile of recent) {\n const existing = discovered.find((f) => f.path === recentFile);\n if (existing) {\n existing.relevance = 'high';\n existing.reason = 'Recently accessed + ' + existing.reason;\n } else {\n discovered.push({\n path: recentFile,\n relevance: 'high',\n reason: 'Recently accessed in context',\n });\n }\n }\n\n // Sort by relevance and match count\n return discovered.sort((a, b) => {\n const relevanceOrder = { high: 3, medium: 2, low: 1 };\n const relDiff = relevanceOrder[b.relevance] - relevanceOrder[a.relevance];\n if (relDiff !== 0) return relDiff;\n\n const aMatches = a.matchedKeywords?.length || 0;\n const bMatches = b.matchedKeywords?.length || 0;\n return bMatches - aMatches;\n });\n }\n\n private findFileReferences(file: string, maxFiles: number): DiscoveredFile[] {\n const results: DiscoveredFile[] = [];\n\n try {\n // Search for imports of this file\n const basename = file.replace(/\\.[^.]+$/, '');\n const cmd = `grep -ril \"from.*${basename}\" . --include=\"*.ts\" --include=\"*.tsx\" --include=\"*.js\" --exclude-dir=node_modules --exclude-dir=dist 2>/dev/null | head -${maxFiles}`;\n const result = execSync(cmd, {\n cwd: this.deps.projectRoot,\n encoding: 'utf8',\n timeout: 5000,\n });\n\n const files = result.split('\\n').filter((f) => f.trim());\n for (const f of files) {\n results.push({\n path: f.replace(/^\\.\\//, ''),\n relevance: 'high',\n reason: `Imports ${file}`,\n });\n }\n } catch {}\n\n return results;\n }\n\n private searchForConcept(\n concept: string,\n maxFiles: number\n ): DiscoveredFile[] {\n const results: DiscoveredFile[] = [];\n\n try {\n const cmd = `grep -ril \"${concept}\" . --include=\"*.ts\" --include=\"*.tsx\" --include=\"*.md\" --exclude-dir=node_modules --exclude-dir=dist 2>/dev/null | head -${maxFiles}`;\n const result = execSync(cmd, {\n cwd: this.deps.projectRoot,\n encoding: 'utf8',\n timeout: 5000,\n });\n\n const files = result.split('\\n').filter((f) => f.trim());\n for (const f of files) {\n results.push({\n path: f.replace(/^\\.\\//, ''),\n relevance: 'medium',\n reason: `Contains \"${concept}\"`,\n });\n }\n } catch {}\n\n return results;\n }\n\n private mergeAndDedupe(\n a: DiscoveredFile[],\n b: DiscoveredFile[]\n ): DiscoveredFile[] {\n const pathSet = new Set(a.map((f) => f.path));\n const merged = [...a];\n\n for (const file of b) {\n if (!pathSet.has(file.path)) {\n merged.push(file);\n pathSet.add(file.path);\n }\n }\n\n return merged;\n }\n\n private getRecentDecisions(): any[] {\n try {\n const decisions = this.deps.db\n .prepare(\n `\n SELECT a.text, a.type, a.priority, f.name as frame_name, a.created_at\n FROM anchors a\n JOIN frames f ON a.frame_id = f.frame_id\n WHERE a.type IN ('DECISION', 'CONSTRAINT', 'FACT')\n ORDER BY a.created_at DESC\n LIMIT 10\n `\n )\n .all();\n return decisions;\n } catch {\n return [];\n }\n }\n\n private generateContextSummary(\n keywords: string[],\n files: DiscoveredFile[]\n ): string {\n const hotStack = this.deps.frameManager.getHotStackContext(5);\n const currentGoal = hotStack[hotStack.length - 1]?.header?.goal;\n\n let summary = '';\n if (currentGoal) {\n summary += `Current task: ${currentGoal}\\n`;\n }\n summary += `Context keywords: ${keywords.slice(0, 10).join(', ')}\\n`;\n summary += `Relevant files found: ${files.length}\\n`;\n summary += `High relevance: ${files.filter((f) => f.relevance === 'high').length}`;\n\n return summary;\n }\n\n private formatDiscoveryResult(result: DiscoveryResult): string {\n let output = '# Discovery Results\\n\\n';\n\n output += '## Context Summary\\n';\n output += result.contextSummary + '\\n\\n';\n\n output += '## Relevant Files\\n\\n';\n for (const file of result.files.slice(0, 15)) {\n const icon =\n file.relevance === 'high'\n ? '[HIGH]'\n : file.relevance === 'medium'\n ? '[MED]'\n : '[LOW]';\n output += `${icon} ${file.path}\\n`;\n output += ` ${file.reason}\\n`;\n }\n\n if (result.keywords.length > 0) {\n output += '\\n## Keywords Used\\n';\n output += result.keywords.slice(0, 15).join(', ') + '\\n';\n }\n\n return output;\n }\n\n private formatRelatedFiles(\n files: DiscoveredFile[],\n file?: string,\n concept?: string\n ): string {\n let output = '# Related Files\\n\\n';\n\n if (file) output += `Related to file: ${file}\\n`;\n if (concept) output += `Related to concept: ${concept}\\n`;\n output += '\\n';\n\n for (const f of files) {\n output += `- ${f.path}\\n ${f.reason}\\n`;\n }\n\n return output;\n }\n\n private formatSessionSummary(summary: any): string {\n let output = '# Session Summary\\n\\n';\n\n output += `**Current Goal:** ${summary.currentGoal}\\n`;\n output += `**Active Frames:** ${summary.activeFrames}\\n`;\n output += `**Stack Depth:** ${summary.stackDepth}\\n\\n`;\n\n if (summary.recentFiles.length > 0) {\n output += '## Recent Files\\n';\n for (const f of summary.recentFiles) {\n output += `- ${f}\\n`;\n }\n output += '\\n';\n }\n\n if (summary.decisions.length > 0) {\n output += '## Recent Decisions\\n';\n for (const d of summary.decisions) {\n output += `- [${d.type}] ${d.text}\\n`;\n }\n }\n\n return output;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -2,13 +2,26 @@ import { fileURLToPath as __fileURLToPath } from 'url';
2
2
  import { dirname as __pathDirname } from 'path';
3
3
  const __filename = __fileURLToPath(import.meta.url);
4
4
  const __dirname = __pathDirname(__filename);
5
- import { ContextHandlers } from "./context-handlers.js";
5
+ import {
6
+ ContextHandlers
7
+ } from "./context-handlers.js";
6
8
  import { TaskHandlers } from "./task-handlers.js";
7
- import { LinearHandlers } from "./linear-handlers.js";
8
- import { TraceHandlers } from "./trace-handlers.js";
9
- import { ContextHandlers as ContextHandlers2 } from "./context-handlers.js";
9
+ import {
10
+ LinearHandlers
11
+ } from "./linear-handlers.js";
12
+ import {
13
+ TraceHandlers
14
+ } from "./trace-handlers.js";
15
+ import {
16
+ DiscoveryHandlers
17
+ } from "./discovery-handlers.js";
18
+ import {
19
+ ContextHandlers as ContextHandlers2
20
+ } from "./context-handlers.js";
10
21
  import { TaskHandlers as TaskHandlers2 } from "./task-handlers.js";
11
- import { LinearHandlers as LinearHandlers2 } from "./linear-handlers.js";
22
+ import {
23
+ LinearHandlers as LinearHandlers2
24
+ } from "./linear-handlers.js";
12
25
  import { TraceHandlers as TraceHandlers2 } from "./trace-handlers.js";
13
26
  class MCPHandlerFactory {
14
27
  contextHandlers;
@@ -43,7 +56,9 @@ class MCPHandlerFactory {
43
56
  case "get_context":
44
57
  return this.contextHandlers.handleGetContext.bind(this.contextHandlers);
45
58
  case "add_decision":
46
- return this.contextHandlers.handleAddDecision.bind(this.contextHandlers);
59
+ return this.contextHandlers.handleAddDecision.bind(
60
+ this.contextHandlers
61
+ );
47
62
  case "start_frame":
48
63
  return this.contextHandlers.handleStartFrame.bind(this.contextHandlers);
49
64
  case "close_frame":
@@ -51,7 +66,9 @@ class MCPHandlerFactory {
51
66
  case "add_anchor":
52
67
  return this.contextHandlers.handleAddAnchor.bind(this.contextHandlers);
53
68
  case "get_hot_stack":
54
- return this.contextHandlers.handleGetHotStack.bind(this.contextHandlers);
69
+ return this.contextHandlers.handleGetHotStack.bind(
70
+ this.contextHandlers
71
+ );
55
72
  // Task handlers
56
73
  case "create_task":
57
74
  return this.taskHandlers.handleCreateTask.bind(this.taskHandlers);
@@ -62,14 +79,20 @@ class MCPHandlerFactory {
62
79
  case "get_task_metrics":
63
80
  return this.taskHandlers.handleGetTaskMetrics.bind(this.taskHandlers);
64
81
  case "add_task_dependency":
65
- return this.taskHandlers.handleAddTaskDependency.bind(this.taskHandlers);
82
+ return this.taskHandlers.handleAddTaskDependency.bind(
83
+ this.taskHandlers
84
+ );
66
85
  // Linear handlers
67
86
  case "linear_sync":
68
87
  return this.linearHandlers.handleLinearSync.bind(this.linearHandlers);
69
88
  case "linear_update_task":
70
- return this.linearHandlers.handleLinearUpdateTask.bind(this.linearHandlers);
89
+ return this.linearHandlers.handleLinearUpdateTask.bind(
90
+ this.linearHandlers
91
+ );
71
92
  case "linear_get_tasks":
72
- return this.linearHandlers.handleLinearGetTasks.bind(this.linearHandlers);
93
+ return this.linearHandlers.handleLinearGetTasks.bind(
94
+ this.linearHandlers
95
+ );
73
96
  case "linear_status":
74
97
  return this.linearHandlers.handleLinearStatus.bind(this.linearHandlers);
75
98
  // Trace handlers
@@ -78,13 +101,17 @@ class MCPHandlerFactory {
78
101
  case "analyze_traces":
79
102
  return this.traceHandlers.handleAnalyzeTraces.bind(this.traceHandlers);
80
103
  case "start_browser_debug":
81
- return this.traceHandlers.handleStartBrowserDebug.bind(this.traceHandlers);
104
+ return this.traceHandlers.handleStartBrowserDebug.bind(
105
+ this.traceHandlers
106
+ );
82
107
  case "take_screenshot":
83
108
  return this.traceHandlers.handleTakeScreenshot.bind(this.traceHandlers);
84
109
  case "execute_script":
85
110
  return this.traceHandlers.handleExecuteScript.bind(this.traceHandlers);
86
111
  case "stop_browser_debug":
87
- return this.traceHandlers.handleStopBrowserDebug.bind(this.traceHandlers);
112
+ return this.traceHandlers.handleStopBrowserDebug.bind(
113
+ this.traceHandlers
114
+ );
88
115
  default:
89
116
  throw new Error(`Unknown tool: ${toolName}`);
90
117
  }
@@ -130,6 +157,7 @@ class MCPHandlerFactory {
130
157
  }
131
158
  export {
132
159
  ContextHandlers,
160
+ DiscoveryHandlers,
133
161
  LinearHandlers,
134
162
  MCPHandlerFactory,
135
163
  TaskHandlers,