@stackmemoryai/stackmemory 0.2.4 → 0.2.6

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 (179) hide show
  1. package/README.md +108 -0
  2. package/dist/index.js +382 -0
  3. package/dist/src/analytics/api/analytics-api.d.ts +24 -0
  4. package/dist/src/analytics/api/analytics-api.d.ts.map +1 -0
  5. package/dist/src/analytics/api/analytics-api.js +279 -0
  6. package/dist/src/analytics/api/analytics-api.js.map +1 -0
  7. package/dist/src/analytics/core/analytics-service.d.ts +23 -0
  8. package/dist/src/analytics/core/analytics-service.d.ts.map +1 -0
  9. package/dist/src/analytics/core/analytics-service.js +160 -0
  10. package/dist/src/analytics/core/analytics-service.js.map +1 -0
  11. package/dist/src/analytics/index.d.ts +12 -0
  12. package/dist/src/analytics/index.d.ts.map +1 -0
  13. package/dist/src/analytics/index.js +11 -0
  14. package/dist/src/analytics/index.js.map +1 -0
  15. package/dist/src/analytics/queries/metrics-queries.d.ts +11 -0
  16. package/dist/src/analytics/queries/metrics-queries.d.ts.map +1 -0
  17. package/dist/src/analytics/queries/metrics-queries.js +179 -0
  18. package/dist/src/analytics/queries/metrics-queries.js.map +1 -0
  19. package/dist/src/analytics/types/metrics.d.ts +60 -0
  20. package/dist/src/analytics/types/metrics.d.ts.map +1 -0
  21. package/dist/src/analytics/types/metrics.js +2 -0
  22. package/dist/src/analytics/types/metrics.js.map +1 -0
  23. package/dist/src/cli/analytics-viewer.d.ts +3 -0
  24. package/dist/src/cli/analytics-viewer.d.ts.map +1 -0
  25. package/dist/src/cli/analytics-viewer.js +89 -0
  26. package/dist/src/cli/analytics-viewer.js.map +1 -0
  27. package/dist/src/cli/browser-test.d.ts +6 -0
  28. package/dist/src/cli/browser-test.d.ts.map +1 -0
  29. package/dist/src/cli/browser-test.js +32 -0
  30. package/dist/src/cli/browser-test.js.map +1 -0
  31. package/dist/src/cli/cli.js +157 -0
  32. package/dist/src/cli/cli.js.map +1 -1
  33. package/dist/src/cli/commands/projects.d.ts +8 -0
  34. package/dist/src/cli/commands/projects.d.ts.map +1 -0
  35. package/dist/src/cli/commands/projects.js +220 -0
  36. package/dist/src/cli/commands/projects.js.map +1 -0
  37. package/dist/src/cli/index.d.ts +7 -0
  38. package/dist/src/cli/index.d.ts.map +1 -0
  39. package/dist/src/cli/index.js +704 -0
  40. package/dist/src/cli/index.js.map +1 -0
  41. package/dist/src/cli/project-commands.d.ts +8 -0
  42. package/dist/src/cli/project-commands.d.ts.map +1 -0
  43. package/dist/src/cli/project-commands.js +212 -0
  44. package/dist/src/cli/project-commands.js.map +1 -0
  45. package/dist/src/cli/utils/viewer.d.ts +3 -0
  46. package/dist/src/cli/utils/viewer.d.ts.map +1 -0
  47. package/dist/src/cli/utils/viewer.js +89 -0
  48. package/dist/src/cli/utils/viewer.js.map +1 -0
  49. package/dist/src/core/context/frame-manager.d.ts +106 -0
  50. package/dist/src/core/context/frame-manager.d.ts.map +1 -0
  51. package/dist/src/core/context/frame-manager.js +387 -0
  52. package/dist/src/core/context/frame-manager.js.map +1 -0
  53. package/dist/src/core/logger.test.js +1 -1
  54. package/dist/src/core/logger.test.js.map +1 -1
  55. package/dist/src/core/monitoring/error-handler.d.ts +46 -0
  56. package/dist/src/core/monitoring/error-handler.d.ts.map +1 -0
  57. package/dist/src/core/monitoring/error-handler.js +212 -0
  58. package/dist/src/core/monitoring/error-handler.js.map +1 -0
  59. package/dist/src/core/monitoring/logger.d.ts +24 -0
  60. package/dist/src/core/monitoring/logger.d.ts.map +1 -0
  61. package/dist/src/core/monitoring/logger.js +121 -0
  62. package/dist/src/core/monitoring/logger.js.map +1 -0
  63. package/dist/src/core/monitoring/metrics.d.ts +7 -0
  64. package/dist/src/core/monitoring/metrics.d.ts.map +1 -0
  65. package/dist/src/core/monitoring/metrics.js +13 -0
  66. package/dist/src/core/monitoring/metrics.js.map +1 -0
  67. package/dist/src/core/monitoring/progress-tracker.d.ts +95 -0
  68. package/dist/src/core/monitoring/progress-tracker.d.ts.map +1 -0
  69. package/dist/src/core/monitoring/progress-tracker.js +178 -0
  70. package/dist/src/core/monitoring/progress-tracker.js.map +1 -0
  71. package/dist/src/core/project-manager.d.ts +130 -0
  72. package/dist/src/core/project-manager.d.ts.map +1 -0
  73. package/dist/src/core/project-manager.js +582 -0
  74. package/dist/src/core/project-manager.js.map +1 -0
  75. package/dist/src/core/projects/project-manager.d.ts +130 -0
  76. package/dist/src/core/projects/project-manager.d.ts.map +1 -0
  77. package/dist/src/core/projects/project-manager.js +591 -0
  78. package/dist/src/core/projects/project-manager.js.map +1 -0
  79. package/dist/src/core/utils/update-checker.d.ts +38 -0
  80. package/dist/src/core/utils/update-checker.d.ts.map +1 -0
  81. package/dist/src/core/utils/update-checker.js +156 -0
  82. package/dist/src/core/utils/update-checker.js.map +1 -0
  83. package/dist/src/features/analytics/api/analytics-api.d.ts +24 -0
  84. package/dist/src/features/analytics/api/analytics-api.d.ts.map +1 -0
  85. package/dist/src/features/analytics/api/analytics-api.js +289 -0
  86. package/dist/src/features/analytics/api/analytics-api.js.map +1 -0
  87. package/dist/src/features/analytics/core/analytics-service.d.ts +23 -0
  88. package/dist/src/features/analytics/core/analytics-service.d.ts.map +1 -0
  89. package/dist/src/features/analytics/core/analytics-service.js +160 -0
  90. package/dist/src/features/analytics/core/analytics-service.js.map +1 -0
  91. package/dist/src/features/analytics/index.d.ts +12 -0
  92. package/dist/src/features/analytics/index.d.ts.map +1 -0
  93. package/dist/src/features/analytics/index.js +11 -0
  94. package/dist/src/features/analytics/index.js.map +1 -0
  95. package/dist/src/features/analytics/queries/metrics-queries.d.ts +11 -0
  96. package/dist/src/features/analytics/queries/metrics-queries.d.ts.map +1 -0
  97. package/dist/src/features/analytics/queries/metrics-queries.js +183 -0
  98. package/dist/src/features/analytics/queries/metrics-queries.js.map +1 -0
  99. package/dist/src/features/analytics/types/metrics.d.ts +60 -0
  100. package/dist/src/features/analytics/types/metrics.d.ts.map +1 -0
  101. package/dist/src/features/analytics/types/metrics.js +2 -0
  102. package/dist/src/features/analytics/types/metrics.js.map +1 -0
  103. package/dist/src/features/browser/browser-mcp.d.ts +94 -0
  104. package/dist/src/features/browser/browser-mcp.d.ts.map +1 -0
  105. package/dist/src/features/browser/browser-mcp.js +456 -0
  106. package/dist/src/features/browser/browser-mcp.js.map +1 -0
  107. package/dist/src/features/tasks/pebbles-task-store.d.ts +117 -0
  108. package/dist/src/features/tasks/pebbles-task-store.d.ts.map +1 -0
  109. package/dist/src/features/tasks/pebbles-task-store.js +335 -0
  110. package/dist/src/features/tasks/pebbles-task-store.js.map +1 -0
  111. package/dist/src/features/tasks/task-aware-context.d.ts +103 -0
  112. package/dist/src/features/tasks/task-aware-context.d.ts.map +1 -0
  113. package/dist/src/features/tasks/task-aware-context.js +412 -0
  114. package/dist/src/features/tasks/task-aware-context.js.map +1 -0
  115. package/dist/src/index.d.ts +4 -4
  116. package/dist/src/index.d.ts.map +1 -1
  117. package/dist/src/index.js +4 -4
  118. package/dist/src/index.js.map +1 -1
  119. package/dist/src/integrations/browser-mcp.d.ts +94 -0
  120. package/dist/src/integrations/browser-mcp.d.ts.map +1 -0
  121. package/dist/src/integrations/browser-mcp.js +431 -0
  122. package/dist/src/integrations/browser-mcp.js.map +1 -0
  123. package/dist/src/integrations/linear/auth.d.ts +99 -0
  124. package/dist/src/integrations/linear/auth.d.ts.map +1 -0
  125. package/dist/src/integrations/linear/auth.js +319 -0
  126. package/dist/src/integrations/linear/auth.js.map +1 -0
  127. package/dist/src/integrations/linear/auto-sync.d.ts +77 -0
  128. package/dist/src/integrations/linear/auto-sync.d.ts.map +1 -0
  129. package/dist/src/integrations/linear/auto-sync.js +268 -0
  130. package/dist/src/integrations/linear/auto-sync.js.map +1 -0
  131. package/dist/src/integrations/linear/client.d.ts +86 -0
  132. package/dist/src/integrations/linear/client.d.ts.map +1 -0
  133. package/dist/src/integrations/linear/client.js +277 -0
  134. package/dist/src/integrations/linear/client.js.map +1 -0
  135. package/dist/src/integrations/linear/config.d.ts +51 -0
  136. package/dist/src/integrations/linear/config.d.ts.map +1 -0
  137. package/dist/src/integrations/linear/config.js +103 -0
  138. package/dist/src/integrations/linear/config.js.map +1 -0
  139. package/dist/src/integrations/linear/sync.d.ts +97 -0
  140. package/dist/src/integrations/linear/sync.d.ts.map +1 -0
  141. package/dist/src/integrations/linear/sync.js +391 -0
  142. package/dist/src/integrations/linear/sync.js.map +1 -0
  143. package/dist/src/integrations/mcp/server.d.ts +40 -0
  144. package/dist/src/integrations/mcp/server.d.ts.map +1 -0
  145. package/dist/src/integrations/mcp/server.js +828 -0
  146. package/dist/src/integrations/mcp/server.js.map +1 -0
  147. package/dist/src/mcp/mcp-server.d.ts +1 -0
  148. package/dist/src/mcp/mcp-server.d.ts.map +1 -1
  149. package/dist/src/mcp/mcp-server.js +11 -0
  150. package/dist/src/mcp/mcp-server.js.map +1 -1
  151. package/dist/src/railway/index.d.ts +7 -0
  152. package/dist/src/railway/index.d.ts.map +1 -0
  153. package/dist/src/railway/index.js +401 -0
  154. package/dist/src/railway/index.js.map +1 -0
  155. package/dist/src/runway/auth/auth-middleware.d.ts +66 -0
  156. package/dist/src/runway/auth/auth-middleware.d.ts.map +1 -0
  157. package/dist/src/runway/auth/auth-middleware.js +337 -0
  158. package/dist/src/runway/auth/auth-middleware.js.map +1 -0
  159. package/dist/src/runway/server/runway-mcp-server.d.ts +46 -0
  160. package/dist/src/runway/server/runway-mcp-server.d.ts.map +1 -0
  161. package/dist/src/runway/server/runway-mcp-server.js +601 -0
  162. package/dist/src/runway/server/runway-mcp-server.js.map +1 -0
  163. package/dist/src/runway.bak/auth/auth-middleware.d.ts +66 -0
  164. package/dist/src/runway.bak/auth/auth-middleware.d.ts.map +1 -0
  165. package/dist/src/runway.bak/auth/auth-middleware.js +337 -0
  166. package/dist/src/runway.bak/auth/auth-middleware.js.map +1 -0
  167. package/dist/src/runway.bak/server/runway-mcp-server.d.ts +46 -0
  168. package/dist/src/runway.bak/server/runway-mcp-server.d.ts.map +1 -0
  169. package/dist/src/runway.bak/server/runway-mcp-server.js +601 -0
  170. package/dist/src/runway.bak/server/runway-mcp-server.js.map +1 -0
  171. package/dist/src/servers/production/auth-middleware.d.ts +66 -0
  172. package/dist/src/servers/production/auth-middleware.d.ts.map +1 -0
  173. package/dist/src/servers/production/auth-middleware.js +346 -0
  174. package/dist/src/servers/production/auth-middleware.js.map +1 -0
  175. package/dist/src/servers/railway/index.d.ts +7 -0
  176. package/dist/src/servers/railway/index.d.ts.map +1 -0
  177. package/dist/src/servers/railway/index.js +401 -0
  178. package/dist/src/servers/railway/index.js.map +1 -0
  179. package/package.json +27 -5
@@ -0,0 +1,828 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * StackMemory MCP Server - Local Instance
4
+ * This runs locally and provides context to Claude Code
5
+ */
6
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
7
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
8
+ import { z } from 'zod';
9
+ import Database from 'better-sqlite3';
10
+ import { readFileSync, existsSync, mkdirSync } from 'fs';
11
+ import { join, dirname } from 'path';
12
+ import { execSync } from 'child_process';
13
+ import { FrameManager } from '../../core/context/frame-manager.js';
14
+ import { PebblesTaskStore, } from '../../features/tasks/pebbles-task-store.js';
15
+ // TODO: Temporarily disabled due to TypeScript errors
16
+ // import { LinearAuthManager, LinearOAuthSetup } from '../integrations/linear/auth.js';
17
+ // import { LinearSyncEngine, DEFAULT_SYNC_CONFIG } from '../integrations/linear/sync.js';
18
+ import { logger } from '../../core/monitoring/logger.js';
19
+ import { BrowserMCPIntegration } from '../../features/browser/browser-mcp.js';
20
+ // ============================================
21
+ // Simple Local MCP Server
22
+ // ============================================
23
+ class LocalStackMemoryMCP {
24
+ server;
25
+ db;
26
+ projectRoot;
27
+ frameManager;
28
+ taskStore;
29
+ // TODO: Temporarily disabled
30
+ // private linearAuthManager: LinearAuthManager;
31
+ // private linearSync: LinearSyncEngine;
32
+ projectId;
33
+ contexts = new Map();
34
+ browserMCP;
35
+ constructor() {
36
+ // Find project root (where .git is)
37
+ this.projectRoot = this.findProjectRoot();
38
+ this.projectId = this.getProjectId();
39
+ // Ensure .stackmemory directory exists
40
+ const dbDir = join(this.projectRoot, '.stackmemory');
41
+ if (!existsSync(dbDir)) {
42
+ mkdirSync(dbDir, { recursive: true });
43
+ }
44
+ // Initialize database
45
+ const dbPath = join(dbDir, 'context.db');
46
+ this.db = new Database(dbPath);
47
+ this.initDB();
48
+ // Initialize frame manager
49
+ this.frameManager = new FrameManager(this.db, this.projectId);
50
+ // Initialize task store
51
+ this.taskStore = new PebblesTaskStore(this.projectRoot, this.db);
52
+ // TODO: Initialize Linear integration (temporarily disabled)
53
+ // this.linearAuthManager = new LinearAuthManager(this.projectRoot);
54
+ // this.linearSync = new LinearSyncEngine(
55
+ // this.taskStore,
56
+ // this.linearAuthManager,
57
+ // DEFAULT_SYNC_CONFIG
58
+ // );
59
+ // Initialize MCP server
60
+ this.server = new Server({
61
+ name: 'stackmemory-local',
62
+ version: '0.1.0',
63
+ }, {
64
+ capabilities: {
65
+ tools: {},
66
+ },
67
+ });
68
+ // Initialize Browser MCP integration
69
+ this.browserMCP = new BrowserMCPIntegration({
70
+ headless: process.env.BROWSER_HEADLESS !== 'false',
71
+ defaultViewport: { width: 1280, height: 720 },
72
+ });
73
+ this.setupHandlers();
74
+ this.loadInitialContext();
75
+ // Initialize Browser MCP with this server
76
+ this.browserMCP.initialize(this.server).catch((error) => {
77
+ logger.error('Failed to initialize Browser MCP', error);
78
+ });
79
+ logger.info('StackMemory MCP Server initialized', {
80
+ projectRoot: this.projectRoot,
81
+ projectId: this.projectId,
82
+ });
83
+ }
84
+ findProjectRoot() {
85
+ let dir = process.cwd();
86
+ while (dir !== '/') {
87
+ if (existsSync(join(dir, '.git'))) {
88
+ return dir;
89
+ }
90
+ dir = dirname(dir);
91
+ }
92
+ return process.cwd();
93
+ }
94
+ initDB() {
95
+ this.db.exec(`
96
+ CREATE TABLE IF NOT EXISTS contexts (
97
+ id TEXT PRIMARY KEY,
98
+ type TEXT NOT NULL,
99
+ content TEXT NOT NULL,
100
+ importance REAL DEFAULT 0.5,
101
+ created_at INTEGER DEFAULT (unixepoch()),
102
+ last_accessed INTEGER DEFAULT (unixepoch()),
103
+ access_count INTEGER DEFAULT 1
104
+ );
105
+
106
+ CREATE TABLE IF NOT EXISTS frames (
107
+ frame_id TEXT PRIMARY KEY,
108
+ task TEXT NOT NULL,
109
+ status TEXT DEFAULT 'active',
110
+ created_at INTEGER DEFAULT (unixepoch())
111
+ );
112
+
113
+ CREATE TABLE IF NOT EXISTS attention_log (
114
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
115
+ context_id TEXT,
116
+ query TEXT,
117
+ response TEXT,
118
+ influence_score REAL,
119
+ timestamp INTEGER DEFAULT (unixepoch())
120
+ );
121
+ `);
122
+ }
123
+ loadInitialContext() {
124
+ // Load project information
125
+ const projectInfo = this.getProjectInfo();
126
+ this.addContext('project', `Project: ${projectInfo.name}\nPath: ${projectInfo.path}`, 0.9);
127
+ // Load recent git commits
128
+ try {
129
+ const recentCommits = execSync('git log --oneline -10', {
130
+ cwd: this.projectRoot,
131
+ }).toString();
132
+ this.addContext('git_history', `Recent commits:\n${recentCommits}`, 0.6);
133
+ }
134
+ catch {
135
+ // Not a git repo or git not available
136
+ }
137
+ // Load README if exists
138
+ const readmePath = join(this.projectRoot, 'README.md');
139
+ if (existsSync(readmePath)) {
140
+ const readme = readFileSync(readmePath, 'utf-8');
141
+ const summary = readme.substring(0, 500);
142
+ this.addContext('readme', `Project README:\n${summary}...`, 0.8);
143
+ }
144
+ // Load any existing decisions from previous sessions
145
+ this.loadStoredContexts();
146
+ }
147
+ getProjectId() {
148
+ // Use git remote or directory name as project ID
149
+ try {
150
+ const remoteUrl = execSync('git config --get remote.origin.url', {
151
+ cwd: this.projectRoot,
152
+ stdio: 'pipe',
153
+ })
154
+ .toString()
155
+ .trim();
156
+ return remoteUrl || this.projectRoot.split('/').pop() || 'unknown';
157
+ }
158
+ catch {
159
+ return this.projectRoot.split('/').pop() || 'unknown';
160
+ }
161
+ }
162
+ getProjectInfo() {
163
+ const packageJsonPath = join(this.projectRoot, 'package.json');
164
+ if (existsSync(packageJsonPath)) {
165
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
166
+ return {
167
+ name: pkg.name || 'unknown',
168
+ path: this.projectRoot,
169
+ };
170
+ }
171
+ return {
172
+ name: this.projectRoot.split('/').pop() || 'unknown',
173
+ path: this.projectRoot,
174
+ };
175
+ }
176
+ addContext(type, content, importance = 0.5) {
177
+ const id = `${type}_${Date.now()}`;
178
+ this.db
179
+ .prepare(`
180
+ INSERT OR REPLACE INTO contexts (id, type, content, importance)
181
+ VALUES (?, ?, ?, ?)
182
+ `)
183
+ .run(id, type, content, importance);
184
+ this.contexts.set(id, { type, content, importance });
185
+ return id;
186
+ }
187
+ loadStoredContexts() {
188
+ const stored = this.db
189
+ .prepare(`
190
+ SELECT * FROM contexts
191
+ ORDER BY importance DESC, last_accessed DESC
192
+ LIMIT 50
193
+ `)
194
+ .all();
195
+ stored.forEach((ctx) => {
196
+ this.contexts.set(ctx.id, ctx);
197
+ });
198
+ }
199
+ setupHandlers() {
200
+ // Tool listing
201
+ this.server.setRequestHandler(z.object({
202
+ method: z.literal('tools/list'),
203
+ }), async () => {
204
+ return {
205
+ tools: [
206
+ {
207
+ name: 'get_context',
208
+ description: 'Get current project context',
209
+ inputSchema: {
210
+ type: 'object',
211
+ properties: {
212
+ query: {
213
+ type: 'string',
214
+ description: 'What you want to know',
215
+ },
216
+ limit: {
217
+ type: 'number',
218
+ description: 'Max contexts to return',
219
+ },
220
+ },
221
+ },
222
+ },
223
+ {
224
+ name: 'add_decision',
225
+ description: 'Record a decision or important information',
226
+ inputSchema: {
227
+ type: 'object',
228
+ properties: {
229
+ content: {
230
+ type: 'string',
231
+ description: 'The decision or information',
232
+ },
233
+ type: {
234
+ type: 'string',
235
+ enum: ['decision', 'constraint', 'learning'],
236
+ },
237
+ },
238
+ required: ['content', 'type'],
239
+ },
240
+ },
241
+ {
242
+ name: 'start_frame',
243
+ description: 'Start a new frame (task/subtask) on the call stack',
244
+ inputSchema: {
245
+ type: 'object',
246
+ properties: {
247
+ name: { type: 'string', description: 'Frame name/goal' },
248
+ type: {
249
+ type: 'string',
250
+ enum: [
251
+ 'task',
252
+ 'subtask',
253
+ 'tool_scope',
254
+ 'review',
255
+ 'write',
256
+ 'debug',
257
+ ],
258
+ description: 'Frame type',
259
+ },
260
+ constraints: {
261
+ type: 'array',
262
+ items: { type: 'string' },
263
+ description: 'Constraints for this frame',
264
+ },
265
+ },
266
+ required: ['name', 'type'],
267
+ },
268
+ },
269
+ {
270
+ name: 'close_frame',
271
+ description: 'Close current frame and generate digest',
272
+ inputSchema: {
273
+ type: 'object',
274
+ properties: {
275
+ result: {
276
+ type: 'string',
277
+ description: 'Frame completion result',
278
+ },
279
+ outputs: {
280
+ type: 'object',
281
+ description: 'Final outputs from frame',
282
+ },
283
+ },
284
+ },
285
+ },
286
+ {
287
+ name: 'add_anchor',
288
+ description: 'Add anchored fact/decision/constraint to current frame',
289
+ inputSchema: {
290
+ type: 'object',
291
+ properties: {
292
+ type: {
293
+ type: 'string',
294
+ enum: [
295
+ 'FACT',
296
+ 'DECISION',
297
+ 'CONSTRAINT',
298
+ 'INTERFACE_CONTRACT',
299
+ 'TODO',
300
+ 'RISK',
301
+ ],
302
+ description: 'Anchor type',
303
+ },
304
+ text: { type: 'string', description: 'Anchor content' },
305
+ priority: {
306
+ type: 'number',
307
+ description: 'Priority (0-10)',
308
+ minimum: 0,
309
+ maximum: 10,
310
+ },
311
+ },
312
+ required: ['type', 'text'],
313
+ },
314
+ },
315
+ {
316
+ name: 'get_hot_stack',
317
+ description: 'Get current active frames and context',
318
+ inputSchema: {
319
+ type: 'object',
320
+ properties: {
321
+ maxEvents: {
322
+ type: 'number',
323
+ description: 'Max recent events per frame',
324
+ default: 20,
325
+ },
326
+ },
327
+ },
328
+ },
329
+ {
330
+ name: 'create_task',
331
+ description: 'Create a new task in git-tracked JSONL storage',
332
+ inputSchema: {
333
+ type: 'object',
334
+ properties: {
335
+ title: { type: 'string', description: 'Task title' },
336
+ description: {
337
+ type: 'string',
338
+ description: 'Task description',
339
+ },
340
+ priority: {
341
+ type: 'string',
342
+ enum: ['low', 'medium', 'high', 'urgent'],
343
+ description: 'Task priority',
344
+ },
345
+ estimatedEffort: {
346
+ type: 'number',
347
+ description: 'Estimated effort in minutes',
348
+ },
349
+ dependsOn: {
350
+ type: 'array',
351
+ items: { type: 'string' },
352
+ description: 'Task IDs this depends on',
353
+ },
354
+ tags: {
355
+ type: 'array',
356
+ items: { type: 'string' },
357
+ description: 'Tags for categorization',
358
+ },
359
+ },
360
+ required: ['title'],
361
+ },
362
+ },
363
+ {
364
+ name: 'update_task_status',
365
+ description: 'Update task status with automatic time tracking',
366
+ inputSchema: {
367
+ type: 'object',
368
+ properties: {
369
+ taskId: { type: 'string', description: 'Task ID to update' },
370
+ status: {
371
+ type: 'string',
372
+ enum: [
373
+ 'pending',
374
+ 'in_progress',
375
+ 'completed',
376
+ 'blocked',
377
+ 'cancelled',
378
+ ],
379
+ description: 'New status',
380
+ },
381
+ reason: {
382
+ type: 'string',
383
+ description: 'Reason for status change (especially for blocked)',
384
+ },
385
+ },
386
+ required: ['taskId', 'status'],
387
+ },
388
+ },
389
+ {
390
+ name: 'get_active_tasks',
391
+ description: 'Get currently active tasks',
392
+ inputSchema: {
393
+ type: 'object',
394
+ properties: {
395
+ frameId: {
396
+ type: 'string',
397
+ description: 'Filter by specific frame ID',
398
+ },
399
+ },
400
+ },
401
+ },
402
+ {
403
+ name: 'get_task_metrics',
404
+ description: 'Get project task metrics and analytics',
405
+ inputSchema: {
406
+ type: 'object',
407
+ properties: {},
408
+ },
409
+ },
410
+ {
411
+ name: 'add_task_dependency',
412
+ description: 'Add dependency relationship between tasks',
413
+ inputSchema: {
414
+ type: 'object',
415
+ properties: {
416
+ taskId: {
417
+ type: 'string',
418
+ description: 'Task that depends on another',
419
+ },
420
+ dependsOnId: {
421
+ type: 'string',
422
+ description: 'Task ID that this depends on',
423
+ },
424
+ },
425
+ required: ['taskId', 'dependsOnId'],
426
+ },
427
+ },
428
+ ],
429
+ };
430
+ });
431
+ // Tool execution
432
+ this.server.setRequestHandler(z.object({
433
+ method: z.literal('tools/call'),
434
+ params: z.object({
435
+ name: z.string(),
436
+ arguments: z.record(z.unknown()),
437
+ }),
438
+ }), async (request) => {
439
+ const { name, arguments: args } = request.params;
440
+ switch (name) {
441
+ case 'get_context':
442
+ return this.handleGetContext(args);
443
+ case 'add_decision':
444
+ return this.handleAddDecision(args);
445
+ case 'start_frame':
446
+ return this.handleStartFrame(args);
447
+ case 'close_frame':
448
+ return this.handleCloseFrame(args);
449
+ case 'add_anchor':
450
+ return this.handleAddAnchor(args);
451
+ case 'get_hot_stack':
452
+ return this.handleGetHotStack(args);
453
+ case 'create_task':
454
+ return this.handleCreateTask(args);
455
+ case 'update_task_status':
456
+ return this.handleUpdateTaskStatus(args);
457
+ case 'get_active_tasks':
458
+ return this.handleGetActiveTasks(args);
459
+ case 'get_task_metrics':
460
+ return this.handleGetTaskMetrics(args);
461
+ case 'add_task_dependency':
462
+ return this.handleAddTaskDependency(args);
463
+ default:
464
+ throw new Error(`Unknown tool: ${name}`);
465
+ }
466
+ });
467
+ }
468
+ async handleGetContext(args) {
469
+ const { query = '', limit = 10 } = args;
470
+ // Get relevant contexts
471
+ const contexts = Array.from(this.contexts.values())
472
+ .sort((a, b) => b.importance - a.importance)
473
+ .slice(0, limit);
474
+ // Update access counts
475
+ contexts.forEach((ctx) => {
476
+ this.db
477
+ .prepare(`
478
+ UPDATE contexts
479
+ SET last_accessed = unixepoch(),
480
+ access_count = access_count + 1
481
+ WHERE id = ?
482
+ `)
483
+ .run(ctx.id);
484
+ });
485
+ // Format response
486
+ const response = contexts
487
+ .map((ctx) => `[${ctx.type.toUpperCase()}] (importance: ${ctx.importance.toFixed(2)})\n${ctx.content}`)
488
+ .join('\n\n---\n\n');
489
+ // Log for attention tracking
490
+ this.logAttention(query, response);
491
+ return {
492
+ content: [
493
+ {
494
+ type: 'text',
495
+ text: response ||
496
+ 'No context available yet. Start adding decisions and information!',
497
+ },
498
+ ],
499
+ };
500
+ }
501
+ async handleAddDecision(args) {
502
+ const { content, type = 'decision' } = args;
503
+ const id = this.addContext(type, content, 0.8);
504
+ return {
505
+ content: [
506
+ {
507
+ type: 'text',
508
+ text: `✓ Added ${type}: ${content}\nID: ${id}`,
509
+ },
510
+ ],
511
+ };
512
+ }
513
+ async handleStartFrame(args) {
514
+ const { name, type, constraints } = args;
515
+ const inputs = {};
516
+ if (constraints) {
517
+ inputs.constraints = constraints;
518
+ }
519
+ const frameId = this.frameManager.createFrame({
520
+ type: type,
521
+ name,
522
+ inputs,
523
+ });
524
+ // Log event
525
+ this.frameManager.addEvent('user_message', {
526
+ action: 'start_frame',
527
+ name,
528
+ type,
529
+ constraints,
530
+ });
531
+ // Add as context
532
+ this.addContext('active_frame', `Active frame: ${name} (${type})`, 0.9);
533
+ const stackDepth = this.frameManager.getStackDepth();
534
+ return {
535
+ content: [
536
+ {
537
+ type: 'text',
538
+ text: `🚀 Started ${type}: ${name}\nFrame ID: ${frameId}\nStack depth: ${stackDepth}`,
539
+ },
540
+ ],
541
+ };
542
+ }
543
+ async handleCloseFrame(args) {
544
+ const { result, outputs } = args;
545
+ const currentFrameId = this.frameManager.getCurrentFrameId();
546
+ if (!currentFrameId) {
547
+ return {
548
+ content: [
549
+ {
550
+ type: 'text',
551
+ text: '⚠️ No active frame to close',
552
+ },
553
+ ],
554
+ };
555
+ }
556
+ // Log completion event
557
+ this.frameManager.addEvent('assistant_message', {
558
+ action: 'close_frame',
559
+ result,
560
+ outputs,
561
+ });
562
+ this.frameManager.closeFrame(currentFrameId, outputs);
563
+ const newStackDepth = this.frameManager.getStackDepth();
564
+ return {
565
+ content: [
566
+ {
567
+ type: 'text',
568
+ text: `✅ Closed frame: ${result || 'completed'}\nStack depth: ${newStackDepth}`,
569
+ },
570
+ ],
571
+ };
572
+ }
573
+ async handleAddAnchor(args) {
574
+ const { type, text, priority = 5 } = args;
575
+ const anchorId = this.frameManager.addAnchor(type, text, priority);
576
+ // Log anchor creation
577
+ this.frameManager.addEvent('decision', {
578
+ anchor_type: type,
579
+ text,
580
+ priority,
581
+ anchor_id: anchorId,
582
+ });
583
+ return {
584
+ content: [
585
+ {
586
+ type: 'text',
587
+ text: `📌 Added ${type}: ${text}\nAnchor ID: ${anchorId}`,
588
+ },
589
+ ],
590
+ };
591
+ }
592
+ async handleGetHotStack(args) {
593
+ const { maxEvents = 20 } = args;
594
+ const hotStack = this.frameManager.getHotStackContext(maxEvents);
595
+ const activePath = this.frameManager.getActiveFramePath();
596
+ if (hotStack.length === 0) {
597
+ return {
598
+ content: [
599
+ {
600
+ type: 'text',
601
+ text: '📚 No active frames. Start a frame with start_frame tool.',
602
+ },
603
+ ],
604
+ };
605
+ }
606
+ let response = '📚 **Active Call Stack:**\n\n';
607
+ activePath.forEach((frame, index) => {
608
+ const indent = ' '.repeat(index);
609
+ const context = hotStack[index];
610
+ response += `${indent}${index + 1}. **${frame.name}** (${frame.type})\n`;
611
+ if (context && context.anchors && context.anchors.length > 0) {
612
+ response += `${indent} 📌 ${context.anchors.length} anchors\n`;
613
+ }
614
+ if (context && context.recentEvents && context.recentEvents.length > 0) {
615
+ response += `${indent} 📝 ${context.recentEvents.length} recent events\n`;
616
+ }
617
+ response += '\n';
618
+ });
619
+ response += `**Total stack depth:** ${hotStack.length}`;
620
+ // Log stack access
621
+ this.frameManager.addEvent('observation', {
622
+ action: 'get_hot_stack',
623
+ stack_depth: hotStack.length,
624
+ total_anchors: hotStack.reduce((sum, frame) => sum + frame.anchors.length, 0),
625
+ total_events: hotStack.reduce((sum, frame) => sum + frame.recentEvents.length, 0),
626
+ });
627
+ return {
628
+ content: [
629
+ {
630
+ type: 'text',
631
+ text: response,
632
+ },
633
+ ],
634
+ };
635
+ }
636
+ logAttention(query, response) {
637
+ // Simple attention logging for analysis
638
+ this.db
639
+ .prepare(`
640
+ INSERT INTO attention_log (query, response)
641
+ VALUES (?, ?)
642
+ `)
643
+ .run(query, response);
644
+ }
645
+ async handleCreateTask(args) {
646
+ const { title, description, priority, estimatedEffort, dependsOn, tags } = args;
647
+ const currentFrameId = this.frameManager.getCurrentFrameId();
648
+ if (!currentFrameId) {
649
+ return {
650
+ content: [
651
+ {
652
+ type: 'text',
653
+ text: '⚠️ No active frame. Start a frame first with start_frame tool.',
654
+ },
655
+ ],
656
+ };
657
+ }
658
+ const taskId = this.taskStore.createTask({
659
+ title,
660
+ description,
661
+ priority: priority,
662
+ frameId: currentFrameId,
663
+ dependsOn,
664
+ tags,
665
+ estimatedEffort,
666
+ });
667
+ // Log task creation event
668
+ this.frameManager.addEvent('decision', {
669
+ action: 'create_task',
670
+ task_id: taskId,
671
+ title,
672
+ priority: priority || 'medium',
673
+ });
674
+ return {
675
+ content: [
676
+ {
677
+ type: 'text',
678
+ text: `✅ Created task: ${title}\nID: ${taskId}\nFrame: ${currentFrameId}\nStored in: .stackmemory/tasks.jsonl`,
679
+ },
680
+ ],
681
+ };
682
+ }
683
+ async handleUpdateTaskStatus(args) {
684
+ const { taskId, status, reason } = args;
685
+ try {
686
+ this.taskStore.updateTaskStatus(taskId, status, reason);
687
+ // Log status change event
688
+ this.frameManager.addEvent('observation', {
689
+ action: 'update_task_status',
690
+ task_id: taskId,
691
+ new_status: status,
692
+ reason,
693
+ });
694
+ return {
695
+ content: [
696
+ {
697
+ type: 'text',
698
+ text: `✅ Updated task ${taskId} to ${status}${reason ? `\nReason: ${reason}` : ''}`,
699
+ },
700
+ ],
701
+ };
702
+ }
703
+ catch (error) {
704
+ return {
705
+ content: [
706
+ {
707
+ type: 'text',
708
+ text: `❌ Failed to update task: ${error}`,
709
+ },
710
+ ],
711
+ };
712
+ }
713
+ }
714
+ async handleGetActiveTasks(args) {
715
+ const { frameId } = args;
716
+ const activeTasks = this.taskStore.getActiveTasks(frameId);
717
+ if (activeTasks.length === 0) {
718
+ return {
719
+ content: [
720
+ {
721
+ type: 'text',
722
+ text: frameId
723
+ ? `📝 No active tasks in frame ${frameId}`
724
+ : '📝 No active tasks in project',
725
+ },
726
+ ],
727
+ };
728
+ }
729
+ let response = '📝 **Active Tasks**\n\n';
730
+ activeTasks.forEach((task) => {
731
+ const priority = task.priority.toUpperCase();
732
+ const status = task.status.replace('_', ' ').toUpperCase();
733
+ const effort = task.estimated_effort
734
+ ? ` (${task.estimated_effort}m)`
735
+ : '';
736
+ response += `- **[${status}]** ${task.title}${effort}\n`;
737
+ response += ` Priority: ${priority} | ID: ${task.id}\n`;
738
+ if (task.depends_on.length > 0) {
739
+ response += ` Depends on: ${task.depends_on.join(', ')}\n`;
740
+ }
741
+ response += '\n';
742
+ });
743
+ return {
744
+ content: [
745
+ {
746
+ type: 'text',
747
+ text: response,
748
+ },
749
+ ],
750
+ };
751
+ }
752
+ async handleGetTaskMetrics(_args) {
753
+ const metrics = this.taskStore.getMetrics();
754
+ let response = '📊 **Task Metrics**\n\n';
755
+ response += `**Total Tasks:** ${metrics.total_tasks}\n`;
756
+ response += `**Completion Rate:** ${(metrics.completion_rate * 100).toFixed(1)}%\n\n`;
757
+ response += '**By Status:**\n';
758
+ Object.entries(metrics.by_status).forEach(([status, count]) => {
759
+ response += `- ${status}: ${count}\n`;
760
+ });
761
+ response += '\n**By Priority:**\n';
762
+ Object.entries(metrics.by_priority).forEach(([priority, count]) => {
763
+ response += `- ${priority}: ${count}\n`;
764
+ });
765
+ if (metrics.blocked_tasks > 0) {
766
+ response += `\n⚠️ **${metrics.blocked_tasks} blocked tasks**`;
767
+ }
768
+ if (metrics.avg_effort_accuracy > 0) {
769
+ response += `\n🎯 **Effort Accuracy:** ${(metrics.avg_effort_accuracy * 100).toFixed(1)}%`;
770
+ }
771
+ return {
772
+ content: [
773
+ {
774
+ type: 'text',
775
+ text: response,
776
+ },
777
+ ],
778
+ };
779
+ }
780
+ async handleAddTaskDependency(args) {
781
+ const { taskId, dependsOnId } = args;
782
+ try {
783
+ this.taskStore.addDependency(taskId, dependsOnId);
784
+ // Log dependency creation
785
+ this.frameManager.addEvent('decision', {
786
+ action: 'add_task_dependency',
787
+ task_id: taskId,
788
+ depends_on_id: dependsOnId,
789
+ });
790
+ return {
791
+ content: [
792
+ {
793
+ type: 'text',
794
+ text: `🔗 Added dependency: ${taskId} depends on ${dependsOnId}`,
795
+ },
796
+ ],
797
+ };
798
+ }
799
+ catch (error) {
800
+ return {
801
+ content: [
802
+ {
803
+ type: 'text',
804
+ text: `❌ Failed to add dependency: ${error}`,
805
+ },
806
+ ],
807
+ };
808
+ }
809
+ }
810
+ async start() {
811
+ const transport = new StdioServerTransport();
812
+ await this.server.connect(transport);
813
+ console.error('StackMemory MCP Server started');
814
+ }
815
+ }
816
+ // Export the class
817
+ export default LocalStackMemoryMCP;
818
+ // Export function to run the server
819
+ export async function runMCPServer() {
820
+ const server = new LocalStackMemoryMCP();
821
+ await server.start();
822
+ }
823
+ // Start the server
824
+ if (import.meta.url === `file://${process.argv[1]}`) {
825
+ const server = new LocalStackMemoryMCP();
826
+ server.start().catch(console.error);
827
+ }
828
+ //# sourceMappingURL=server.js.map