@stackmemoryai/stackmemory 0.5.56 → 0.5.58

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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/integrations/mcp/remote-server.ts"],
4
+ "sourcesContent": ["#!/usr/bin/env node\n/**\n * StackMemory Remote MCP Server\n * HTTP/SSE transport for Claude.ai web connector and other remote clients\n */\n\nimport express from 'express';\nimport cors from 'cors';\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';\nimport { z } from 'zod';\nimport Database from 'better-sqlite3';\nimport { validateInput, StartFrameSchema, AddAnchorSchema } from './schemas.js';\nimport { readFileSync, existsSync, mkdirSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { execSync } from 'child_process';\nimport { FrameManager, FrameType } from '../../core/context/index.js';\nimport { logger } from '../../core/monitoring/logger.js';\nimport { isFeatureEnabled } from '../../core/config/feature-flags.js';\n\n// Type imports for optional Linear integration\ntype LinearTaskManager =\n import('../../features/tasks/linear-task-manager.js').LinearTaskManager;\ntype LinearAuthManager = import('../linear/auth.js').LinearAuthManager;\ntype LinearSyncEngine = import('../linear/sync.js').LinearSyncEngine;\n\nconst DEFAULT_PORT = 3847;\n\nclass RemoteStackMemoryMCP {\n private server: Server;\n private db: Database.Database;\n private projectRoot: string;\n private frameManager: FrameManager;\n private taskStore: LinearTaskManager | null = null;\n private linearAuthManager: LinearAuthManager | null = null;\n private linearSync: LinearSyncEngine | null = null;\n private projectId: string;\n private contexts: Map<string, any> = new Map();\n private transports: Map<string, SSEServerTransport> = new Map();\n\n constructor(projectRoot?: string) {\n this.projectRoot = projectRoot || this.findProjectRoot();\n this.projectId = this.getProjectId();\n\n // Ensure .stackmemory directory exists\n const dbDir = join(this.projectRoot, '.stackmemory');\n if (!existsSync(dbDir)) {\n mkdirSync(dbDir, { recursive: true });\n }\n\n // Initialize database\n const dbPath = join(dbDir, 'context.db');\n this.db = new Database(dbPath);\n this.initDB();\n\n // Initialize frame manager\n this.frameManager = new FrameManager(this.db, this.projectId);\n\n // Initialize Linear integration\n this.initLinearIfEnabled();\n\n // Initialize MCP server\n this.server = new Server(\n {\n name: 'stackmemory-remote',\n version: '0.1.0',\n },\n {\n capabilities: {\n tools: {},\n },\n }\n );\n\n this.setupHandlers();\n this.loadInitialContext();\n\n logger.info('StackMemory Remote MCP Server initialized', {\n projectRoot: this.projectRoot,\n projectId: this.projectId,\n });\n }\n\n private findProjectRoot(): string {\n let dir = process.cwd();\n while (dir !== '/') {\n if (existsSync(join(dir, '.git'))) {\n return dir;\n }\n dir = dirname(dir);\n }\n return process.cwd();\n }\n\n private async initLinearIfEnabled(): Promise<void> {\n if (!isFeatureEnabled('linear')) {\n return;\n }\n\n try {\n const { LinearTaskManager } =\n await import('../../features/tasks/linear-task-manager.js');\n const { LinearAuthManager } = await import('../linear/auth.js');\n const { LinearSyncEngine, DEFAULT_SYNC_CONFIG } =\n await import('../linear/sync.js');\n\n this.taskStore = new LinearTaskManager(this.projectRoot, this.db);\n this.linearAuthManager = new LinearAuthManager(this.projectRoot);\n this.linearSync = new LinearSyncEngine(\n this.taskStore,\n this.linearAuthManager,\n DEFAULT_SYNC_CONFIG\n );\n } catch (error) {\n logger.warn('Failed to initialize Linear integration', { error });\n }\n }\n\n private initDB() {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS contexts (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n content TEXT NOT NULL,\n importance REAL DEFAULT 0.5,\n created_at INTEGER DEFAULT (unixepoch()),\n last_accessed INTEGER DEFAULT (unixepoch()),\n access_count INTEGER DEFAULT 1\n );\n\n CREATE TABLE IF NOT EXISTS attention_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n context_id TEXT,\n query TEXT,\n response TEXT,\n influence_score REAL,\n timestamp INTEGER DEFAULT (unixepoch())\n );\n `);\n }\n\n private loadInitialContext() {\n const projectInfo = this.getProjectInfo();\n this.addContext(\n 'project',\n `Project: ${projectInfo.name}\\nPath: ${projectInfo.path}`,\n 0.9\n );\n\n try {\n const recentCommits = execSync('git log --oneline -10', {\n cwd: this.projectRoot,\n }).toString();\n this.addContext('git_history', `Recent commits:\\n${recentCommits}`, 0.6);\n } catch {\n // Not a git repo\n }\n\n const readmePath = join(this.projectRoot, 'README.md');\n if (existsSync(readmePath)) {\n const readme = readFileSync(readmePath, 'utf-8');\n const summary = readme.substring(0, 500);\n this.addContext('readme', `Project README:\\n${summary}...`, 0.8);\n }\n\n this.loadStoredContexts();\n }\n\n private getProjectId(): string {\n let identifier: string;\n try {\n identifier = execSync('git config --get remote.origin.url', {\n cwd: this.projectRoot,\n stdio: 'pipe',\n timeout: 5000,\n })\n .toString()\n .trim();\n } catch {\n identifier = this.projectRoot;\n }\n\n const cleaned = identifier\n .replace(/\\.git$/, '')\n .replace(/[^a-zA-Z0-9-]/g, '-')\n .toLowerCase();\n\n return cleaned.substring(cleaned.length - 50) || 'unknown';\n }\n\n private getProjectInfo() {\n const packageJsonPath = join(this.projectRoot, 'package.json');\n if (existsSync(packageJsonPath)) {\n const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\n return {\n name: pkg.name || 'unknown',\n path: this.projectRoot,\n };\n }\n return {\n name: this.projectRoot.split('/').pop() || 'unknown',\n path: this.projectRoot,\n };\n }\n\n private addContext(type: string, content: string, importance: number = 0.5) {\n const id = `${type}_${Date.now()}`;\n\n this.db\n .prepare(\n `\n INSERT OR REPLACE INTO contexts (id, type, content, importance)\n VALUES (?, ?, ?, ?)\n `\n )\n .run(id, type, content, importance);\n\n this.contexts.set(id, { type, content, importance });\n return id;\n }\n\n private loadStoredContexts() {\n const stored = this.db\n .prepare(\n `\n SELECT * FROM contexts\n ORDER BY importance DESC, last_accessed DESC\n LIMIT 50\n `\n )\n .all() as any[];\n\n stored.forEach((ctx) => {\n this.contexts.set(ctx.id, ctx);\n });\n }\n\n private setupHandlers() {\n // Tool listing\n this.server.setRequestHandler(\n z.object({\n method: z.literal('tools/list'),\n }),\n async () => {\n return {\n tools: [\n {\n name: 'get_context',\n description: 'Get current project context',\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'What you want to know',\n },\n limit: {\n type: 'number',\n description: 'Max contexts to return',\n },\n },\n },\n },\n {\n name: 'add_decision',\n description: 'Record a decision or important information',\n inputSchema: {\n type: 'object',\n properties: {\n content: {\n type: 'string',\n description: 'The decision or information',\n },\n type: {\n type: 'string',\n enum: ['decision', 'constraint', 'learning'],\n },\n },\n required: ['content', 'type'],\n },\n },\n {\n name: 'start_frame',\n description: 'Start a new frame (task/subtask) on the call stack',\n inputSchema: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'Frame name/goal' },\n type: {\n type: 'string',\n enum: [\n 'task',\n 'subtask',\n 'tool_scope',\n 'review',\n 'write',\n 'debug',\n ],\n description: 'Frame type',\n },\n constraints: {\n type: 'array',\n items: { type: 'string' },\n description: 'Constraints for this frame',\n },\n },\n required: ['name', 'type'],\n },\n },\n {\n name: 'close_frame',\n description: 'Close current frame and generate digest',\n inputSchema: {\n type: 'object',\n properties: {\n result: {\n type: 'string',\n description: 'Frame completion result',\n },\n outputs: {\n type: 'object',\n description: 'Final outputs from frame',\n },\n },\n },\n },\n {\n name: 'add_anchor',\n description:\n 'Add anchored fact/decision/constraint to current frame',\n inputSchema: {\n type: 'object',\n properties: {\n type: {\n type: 'string',\n enum: [\n 'FACT',\n 'DECISION',\n 'CONSTRAINT',\n 'INTERFACE_CONTRACT',\n 'TODO',\n 'RISK',\n ],\n description: 'Anchor type',\n },\n text: { type: 'string', description: 'Anchor content' },\n priority: {\n type: 'number',\n description: 'Priority (0-10)',\n minimum: 0,\n maximum: 10,\n },\n },\n required: ['type', 'text'],\n },\n },\n {\n name: 'get_hot_stack',\n description: 'Get current active frames and context',\n inputSchema: {\n type: 'object',\n properties: {\n maxEvents: {\n type: 'number',\n description: 'Max recent events per frame',\n default: 20,\n },\n },\n },\n },\n {\n name: 'sm_search',\n description:\n 'Search across StackMemory - frames, events, decisions, tasks',\n inputSchema: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Search query' },\n scope: {\n type: 'string',\n enum: ['all', 'frames', 'events', 'decisions', 'tasks'],\n description: 'Scope of search',\n },\n limit: { type: 'number', description: 'Maximum results' },\n },\n required: ['query'],\n },\n },\n ],\n };\n }\n );\n\n // Tool execution\n this.server.setRequestHandler(\n z.object({\n method: z.literal('tools/call'),\n params: z.object({\n name: z.string(),\n arguments: z.record(z.unknown()),\n }),\n }),\n async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n switch (name) {\n case 'get_context':\n return this.handleGetContext(args);\n case 'add_decision':\n return this.handleAddDecision(args);\n case 'start_frame':\n return this.handleStartFrame(args);\n case 'close_frame':\n return this.handleCloseFrame(args);\n case 'add_anchor':\n return this.handleAddAnchor(args);\n case 'get_hot_stack':\n return this.handleGetHotStack(args);\n case 'sm_search':\n return this.handleSmSearch(args);\n default:\n throw new Error(`Unknown tool: ${name}`);\n }\n } catch (error: any) {\n return {\n content: [{ type: 'text', text: `Error: ${error.message}` }],\n };\n }\n }\n );\n }\n\n private async handleGetContext(args: any) {\n const { query = '', limit = 10 } = args;\n\n const contexts = Array.from(this.contexts.values())\n .sort((a, b) => b.importance - a.importance)\n .slice(0, limit);\n\n const response = contexts\n .map(\n (ctx) =>\n `[${ctx.type.toUpperCase()}] (importance: ${ctx.importance.toFixed(2)})\\n${ctx.content}`\n )\n .join('\\n\\n---\\n\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: response || 'No context available yet.',\n },\n ],\n };\n }\n\n private async handleAddDecision(args: any) {\n const { content, type = 'decision' } = args;\n const id = this.addContext(type, content, 0.8);\n\n return {\n content: [\n {\n type: 'text',\n text: `Added ${type}: ${content}\\nID: ${id}`,\n },\n ],\n };\n }\n\n private async handleStartFrame(args: unknown) {\n const { name, type, constraints } = validateInput(\n StartFrameSchema,\n args,\n 'start_frame'\n );\n\n const inputs: Record<string, any> = {};\n if (constraints) {\n inputs.constraints = constraints;\n }\n\n const frameId = this.frameManager.createFrame({\n type: type as FrameType,\n name,\n inputs,\n });\n\n this.addContext('active_frame', `Active frame: ${name} (${type})`, 0.9);\n\n return {\n content: [\n {\n type: 'text',\n text: `Started ${type}: ${name}\\nFrame ID: ${frameId}\\nStack depth: ${this.frameManager.getStackDepth()}`,\n },\n ],\n };\n }\n\n private async handleCloseFrame(args: any) {\n const { result, outputs } = args;\n const currentFrameId = this.frameManager.getCurrentFrameId();\n\n if (!currentFrameId) {\n return {\n content: [{ type: 'text', text: 'No active frame to close' }],\n };\n }\n\n this.frameManager.closeFrame(currentFrameId, outputs);\n\n return {\n content: [\n {\n type: 'text',\n text: `Closed frame: ${result || 'completed'}\\nStack depth: ${this.frameManager.getStackDepth()}`,\n },\n ],\n };\n }\n\n private async handleAddAnchor(args: unknown) {\n const { type, text, priority } = validateInput(\n AddAnchorSchema,\n args,\n 'add_anchor'\n );\n\n const anchorId = this.frameManager.addAnchor(type, text, priority);\n\n return {\n content: [\n {\n type: 'text',\n text: `Added ${type}: ${text}\\nAnchor ID: ${anchorId}`,\n },\n ],\n };\n }\n\n private async handleGetHotStack(args: any) {\n const { maxEvents = 20 } = args;\n\n const hotStack = this.frameManager.getHotStackContext(maxEvents);\n const activePath = this.frameManager.getActiveFramePath();\n\n if (hotStack.length === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No active frames. Start a frame with start_frame tool.',\n },\n ],\n };\n }\n\n let response = 'Active Call Stack:\\n\\n';\n\n activePath.forEach((frame, index) => {\n const indent = ' '.repeat(index);\n const context = hotStack[index];\n\n response += `${indent}${index + 1}. ${frame.name} (${frame.type})\\n`;\n\n if (context?.anchors?.length > 0) {\n response += `${indent} Anchors: ${context.anchors.length}\\n`;\n }\n\n if (context?.recentEvents?.length > 0) {\n response += `${indent} Events: ${context.recentEvents.length}\\n`;\n }\n\n response += '\\n';\n });\n\n response += `Total stack depth: ${hotStack.length}`;\n\n return {\n content: [{ type: 'text', text: response }],\n };\n }\n\n private async handleSmSearch(args: any) {\n const { query, scope = 'all', limit = 20 } = args;\n\n if (!query) {\n throw new Error('Query is required');\n }\n\n const results: any[] = [];\n\n if (scope === 'all' || scope === 'frames') {\n const frames = this.db\n .prepare(\n `\n SELECT frame_id, name, type, created_at\n FROM frames\n WHERE project_id = ? AND (name LIKE ? OR inputs LIKE ? OR outputs LIKE ?)\n ORDER BY created_at DESC\n LIMIT ?\n `\n )\n .all(\n this.projectId,\n `%${query}%`,\n `%${query}%`,\n `%${query}%`,\n limit\n ) as any[];\n\n frames.forEach((f) => {\n results.push({\n type: 'frame',\n id: f.frame_id,\n name: f.name,\n frameType: f.type,\n });\n });\n }\n\n if (scope === 'all' || scope === 'decisions') {\n const anchors = this.db\n .prepare(\n `\n SELECT a.anchor_id, a.type, a.text, f.name as frame_name\n FROM anchors a\n JOIN frames f ON a.frame_id = f.frame_id\n WHERE f.project_id = ? AND a.text LIKE ?\n ORDER BY a.created_at DESC\n LIMIT ?\n `\n )\n .all(this.projectId, `%${query}%`, limit) as any[];\n\n anchors.forEach((a) => {\n results.push({\n type: 'decision',\n id: a.anchor_id,\n decisionType: a.type,\n text: a.text,\n frame: a.frame_name,\n });\n });\n }\n\n let response = `Search Results for \"${query}\"\\n\\n`;\n response += `Found ${results.length} results\\n\\n`;\n\n results.slice(0, 10).forEach((r) => {\n if (r.type === 'frame') {\n response += `[Frame] ${r.name} (${r.frameType})\\n`;\n } else if (r.type === 'decision') {\n response += `[${r.decisionType}] ${r.text.slice(0, 60)}...\\n`;\n }\n });\n\n return {\n content: [{ type: 'text', text: response }],\n };\n }\n\n /**\n * Start the HTTP/SSE server\n */\n async startHttpServer(port: number = DEFAULT_PORT): Promise<void> {\n const app = express();\n\n // CORS configuration for Claude.ai\n app.use(\n cors({\n origin: [\n 'https://claude.ai',\n 'https://console.anthropic.com',\n /^http:\\/\\/localhost:\\d+$/,\n ],\n credentials: true,\n })\n );\n\n app.use(express.json());\n\n // Health check endpoint\n app.get('/health', (req, res) => {\n res.json({\n status: 'ok',\n server: 'stackmemory-remote',\n projectId: this.projectId,\n projectRoot: this.projectRoot,\n });\n });\n\n // SSE endpoint - establishes the connection\n app.get('/sse', async (req, res) => {\n logger.info('New SSE connection request');\n\n // Create SSE transport\n const transport = new SSEServerTransport('/message', res);\n const sessionId = transport.sessionId;\n\n // Store transport for message routing\n this.transports.set(sessionId, transport);\n\n // Clean up on close\n transport.onclose = () => {\n this.transports.delete(sessionId);\n logger.info('SSE connection closed', { sessionId });\n };\n\n transport.onerror = (error) => {\n logger.error('SSE transport error', { sessionId, error });\n this.transports.delete(sessionId);\n };\n\n // Connect the MCP server to this transport\n await this.server.connect(transport);\n\n // Start the SSE stream\n await transport.start();\n\n logger.info('SSE connection established', { sessionId });\n });\n\n // Message endpoint - receives client messages\n app.post('/message', async (req, res) => {\n const sessionId = req.query.sessionId as string;\n\n if (!sessionId) {\n res.status(400).json({ error: 'Missing sessionId' });\n return;\n }\n\n const transport = this.transports.get(sessionId);\n if (!transport) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n\n await transport.handlePostMessage(req, res);\n });\n\n // Server info endpoint\n app.get('/info', (req, res) => {\n res.json({\n name: 'stackmemory-remote',\n version: '0.1.0',\n protocol: 'mcp',\n transport: 'sse',\n endpoints: {\n sse: '/sse',\n message: '/message',\n health: '/health',\n },\n project: {\n id: this.projectId,\n root: this.projectRoot,\n name: this.getProjectInfo().name,\n },\n });\n });\n\n return new Promise((resolve) => {\n app.listen(port, () => {\n console.log(\n `StackMemory Remote MCP Server running on http://localhost:${port}`\n );\n console.log(`\\nEndpoints:`);\n console.log(` SSE: http://localhost:${port}/sse`);\n console.log(` Message: http://localhost:${port}/message`);\n console.log(` Health: http://localhost:${port}/health`);\n console.log(` Info: http://localhost:${port}/info`);\n console.log(\n `\\nProject: ${this.getProjectInfo().name} (${this.projectId})`\n );\n console.log(\n `\\nFor Claude.ai connector, use: http://localhost:${port}/sse`\n );\n resolve();\n });\n });\n }\n}\n\n// Export the class\nexport default RemoteStackMemoryMCP;\n\n// Export function to run the server\nexport async function runRemoteMCPServer(\n port: number = DEFAULT_PORT,\n projectRoot?: string\n): Promise<void> {\n const server = new RemoteStackMemoryMCP(projectRoot);\n await server.startHttpServer(port);\n}\n\n// CLI entry point\nif (import.meta.url === `file://${process.argv[1]}`) {\n const port = parseInt(\n process.env.PORT || process.argv[2] || String(DEFAULT_PORT),\n 10\n );\n const projectRoot = process.argv[3] || process.cwd();\n\n runRemoteMCPServer(port, projectRoot).catch((error) => {\n console.error('Failed to start remote MCP server:', error);\n process.exit(1);\n });\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackmemoryai/stackmemory",
3
- "version": "0.5.56",
3
+ "version": "0.5.58",
4
4
  "description": "Lossless memory runtime for AI coding tools - organizes context as a call stack instead of linear chat logs, with team collaboration and infinite retention",
5
5
  "engines": {
6
6
  "node": ">=20.0.0",
@@ -11,6 +11,7 @@
11
11
  "bin": {
12
12
  "stackmemory": "dist/cli/index.js",
13
13
  "codex-sm": "dist/cli/codex-sm.js",
14
+ "codex-smd": "bin/codex-smd",
14
15
  "claude-sm": "bin/claude-sm",
15
16
  "claude-smd": "bin/claude-smd",
16
17
  "opencode-sm": "bin/opencode-sm"
@@ -0,0 +1,302 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Create cleanup issues in Linear
4
+ */
5
+
6
+ import dotenv from 'dotenv';
7
+ import path from 'path';
8
+ import { fileURLToPath } from 'url';
9
+
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+ dotenv.config({ path: path.join(__dirname, '..', '.env') });
12
+
13
+ const TEAM_ID = 'STA'; // Stackmemoryai team
14
+
15
+ const cleanupTasks = [
16
+ {
17
+ title: 'Add tests for monitoring module (0% coverage)',
18
+ description: `## Problem
19
+ The \`/src/core/monitoring/\` module has 0% test coverage.
20
+
21
+ ## Files affected
22
+ - src/core/monitoring/logger.ts
23
+ - src/core/monitoring/health-check.ts
24
+ - src/core/monitoring/metrics-collector.ts
25
+ - src/core/monitoring/performance-tracker.ts
26
+ - src/core/monitoring/index.ts
27
+
28
+ ## Acceptance criteria
29
+ - [ ] Add unit tests for all public functions
30
+ - [ ] Test error handling paths
31
+ - [ ] Achieve >80% coverage`,
32
+ priority: 2, // High
33
+ labels: ['tech-debt', 'testing'],
34
+ },
35
+ {
36
+ title: 'Add tests for performance module (0% coverage)',
37
+ description: `## Problem
38
+ The \`/src/core/performance/\` module has 0% test coverage.
39
+
40
+ ## Files affected
41
+ - src/core/performance/profiler.ts
42
+ - src/core/performance/metrics.ts
43
+ - src/core/performance/index.ts
44
+
45
+ ## Acceptance criteria
46
+ - [ ] Add unit tests for profiler
47
+ - [ ] Add unit tests for metrics collection
48
+ - [ ] Achieve >80% coverage`,
49
+ priority: 2, // High
50
+ labels: ['tech-debt', 'testing'],
51
+ },
52
+ {
53
+ title: 'Add tests for session module (0% coverage)',
54
+ description: `## Problem
55
+ The \`/src/core/session/\` module has 0% test coverage.
56
+
57
+ ## Files affected
58
+ - src/core/session/session-manager.ts
59
+ - src/core/session/enhanced-handoff.ts
60
+ - src/core/session/index.ts
61
+
62
+ ## Acceptance criteria
63
+ - [ ] Add unit tests for session lifecycle
64
+ - [ ] Add unit tests for handoff
65
+ - [ ] Achieve >80% coverage`,
66
+ priority: 2, // High
67
+ labels: ['tech-debt', 'testing'],
68
+ },
69
+ {
70
+ title: 'Add tests for claude-code integration (7 files, 0% coverage)',
71
+ description: `## Problem
72
+ The \`/src/integrations/claude-code/\` module has no tests.
73
+
74
+ ## Files affected
75
+ - src/integrations/claude-code/agent-bridge.ts
76
+ - src/integrations/claude-code/context-injector.ts
77
+ - src/integrations/claude-code/hook-manager.ts
78
+ - src/integrations/claude-code/index.ts
79
+ - src/integrations/claude-code/mcp-client.ts
80
+ - src/integrations/claude-code/session-detector.ts
81
+ - src/integrations/claude-code/types.ts
82
+
83
+ ## Acceptance criteria
84
+ - [ ] Add unit tests for all modules
85
+ - [ ] Mock external dependencies
86
+ - [ ] Achieve >80% coverage`,
87
+ priority: 2, // High
88
+ labels: ['tech-debt', 'testing'],
89
+ },
90
+ {
91
+ title: 'Improve trace module test coverage (9%)',
92
+ description: `## Problem
93
+ The \`/src/core/trace/\` module has only ~9% test coverage.
94
+
95
+ ## Files affected (11 files)
96
+ - src/core/trace/trace-detector.ts
97
+ - src/core/trace/trace-bundler.ts
98
+ - src/core/trace/pattern-matcher.ts
99
+ - And 8 more...
100
+
101
+ ## Acceptance criteria
102
+ - [ ] Add tests for trace detection
103
+ - [ ] Add tests for pattern matching
104
+ - [ ] Add tests for trace bundling
105
+ - [ ] Achieve >80% coverage`,
106
+ priority: 3, // Medium
107
+ labels: ['tech-debt', 'testing'],
108
+ },
109
+ {
110
+ title: 'Reorganize scripts directory (112 files)',
111
+ description: `## Problem
112
+ The \`/scripts/\` directory has 112 files with no clear organization.
113
+
114
+ ## Proposed structure
115
+ \`\`\`
116
+ scripts/
117
+ setup/ # Installation & configuration
118
+ test/ # Test runners
119
+ cli/ # CLI wrappers
120
+ maintenance/ # Cleanup & sync tasks
121
+ demos/ # Demo & example scripts (DONE)
122
+ archive/ # Archived scripts
123
+ \`\`\`
124
+
125
+ ## Acceptance criteria
126
+ - [ ] Create subdirectories
127
+ - [ ] Move scripts to appropriate directories
128
+ - [ ] Update any references in package.json
129
+ - [ ] Update documentation`,
130
+ priority: 4, // Low
131
+ labels: ['tech-debt', 'organization'],
132
+ },
133
+ {
134
+ title: 'Remove ESLint blanket exclusion of /src/integrations/',
135
+ description: `## Problem
136
+ ESLint config (line 55) excludes entire \`/src/integrations/\` directory from linting.
137
+
138
+ ## Impact
139
+ - Potential code quality issues not caught
140
+ - Inconsistent code style
141
+ - 33 warnings not shown
142
+
143
+ ## Acceptance criteria
144
+ - [ ] Remove blanket exclusion from eslint.config.js
145
+ - [ ] Fix any lint errors that appear
146
+ - [ ] Keep specific exclusions only if truly needed`,
147
+ priority: 3, // Medium
148
+ labels: ['tech-debt', 'code-quality'],
149
+ },
150
+ {
151
+ title: 'Resolve service duplication (context-service.ts)',
152
+ description: `## Problem
153
+ \`context-service.ts\` exists in two locations with different implementations:
154
+ - /src/services/context-service.ts
155
+ - /src/daemon/services/context-service.ts
156
+
157
+ ## Acceptance criteria
158
+ - [ ] Analyze both implementations
159
+ - [ ] Rename daemon version to DaemonContextService or consolidate
160
+ - [ ] Update all imports
161
+ - [ ] Document the difference if both needed`,
162
+ priority: 3, // Medium
163
+ labels: ['tech-debt', 'organization'],
164
+ },
165
+ {
166
+ title: 'Rename temporary-named files (refactored-*, enhanced-*)',
167
+ description: `## Problem
168
+ Several files have temporary-sounding names:
169
+ - refactored-frame-manager.ts
170
+ - enhanced-hybrid-digest.ts
171
+ - enhanced-rehydration.ts
172
+ - enhanced-handoff.ts
173
+
174
+ ## Acceptance criteria
175
+ - [ ] Rename to final names (remove prefix)
176
+ - [ ] Update all imports
177
+ - [ ] Verify tests pass`,
178
+ priority: 4, // Low
179
+ labels: ['tech-debt', 'naming'],
180
+ },
181
+ {
182
+ title: 'Add documentation for monitoring, trace, and performance modules',
183
+ description: `## Problem
184
+ Critical modules lack documentation:
185
+ - /src/core/monitoring/ - no docs
186
+ - /src/core/trace/ - demos but no guide
187
+ - /src/core/performance/ - no docs
188
+
189
+ ## Acceptance criteria
190
+ - [ ] Add README.md or docs for each module
191
+ - [ ] Document public APIs
192
+ - [ ] Add usage examples`,
193
+ priority: 4, // Low
194
+ labels: ['documentation'],
195
+ },
196
+ ];
197
+
198
+ async function queryLinear(query, variables = {}) {
199
+ const apiKey =
200
+ process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY;
201
+ const response = await fetch('https://api.linear.app/graphql', {
202
+ method: 'POST',
203
+ headers: {
204
+ 'Content-Type': 'application/json',
205
+ Authorization: apiKey,
206
+ },
207
+ body: JSON.stringify({ query, variables }),
208
+ });
209
+
210
+ const data = await response.json();
211
+ if (data.errors) {
212
+ throw new Error(data.errors[0].message);
213
+ }
214
+ return data.data;
215
+ }
216
+
217
+ async function getTeamId() {
218
+ const teams = await queryLinear(`
219
+ query {
220
+ teams {
221
+ nodes {
222
+ id
223
+ key
224
+ name
225
+ }
226
+ }
227
+ }
228
+ `);
229
+
230
+ const team = teams.teams.nodes.find((t) => t.key === TEAM_ID);
231
+ if (!team) {
232
+ throw new Error(`Team ${TEAM_ID} not found`);
233
+ }
234
+ return team.id;
235
+ }
236
+
237
+ async function createIssue(teamId, task) {
238
+ const mutation = `
239
+ mutation CreateIssue($input: IssueCreateInput!) {
240
+ issueCreate(input: $input) {
241
+ success
242
+ issue {
243
+ id
244
+ identifier
245
+ title
246
+ url
247
+ }
248
+ }
249
+ }
250
+ `;
251
+
252
+ const result = await queryLinear(mutation, {
253
+ input: {
254
+ teamId,
255
+ title: task.title,
256
+ description: task.description,
257
+ priority: task.priority,
258
+ },
259
+ });
260
+
261
+ return result.issueCreate;
262
+ }
263
+
264
+ async function main() {
265
+ const apiKey =
266
+ process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY;
267
+ if (!apiKey) {
268
+ console.error('❌ LINEAR_API_KEY not set');
269
+ process.exit(1);
270
+ }
271
+
272
+ console.log('🔄 Connecting to Linear...');
273
+
274
+ // Verify connection
275
+ const viewer = await queryLinear('{ viewer { name email } }');
276
+ console.log(`✅ Connected as: ${viewer.viewer.name}`);
277
+
278
+ // Get team ID
279
+ const teamId = await getTeamId();
280
+ console.log(`📋 Team: ${TEAM_ID} (${teamId})\n`);
281
+
282
+ // Create issues
283
+ console.log(`Creating ${cleanupTasks.length} cleanup issues...\n`);
284
+
285
+ for (const task of cleanupTasks) {
286
+ try {
287
+ const result = await createIssue(teamId, task);
288
+ if (result.success) {
289
+ console.log(`✅ ${result.issue.identifier}: ${task.title}`);
290
+ console.log(` ${result.issue.url}\n`);
291
+ } else {
292
+ console.log(`❌ Failed: ${task.title}`);
293
+ }
294
+ } catch (error) {
295
+ console.log(`❌ Error creating "${task.title}": ${error.message}`);
296
+ }
297
+ }
298
+
299
+ console.log('✅ Done!');
300
+ }
301
+
302
+ main().catch(console.error);
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Test Browser MCP integration locally
4
+ */
5
+
6
+ import { BrowserMCPIntegration } from '../features/browser/browser-mcp.js';
7
+
8
+ async function testBrowserMCP() {
9
+ console.log('Testing Browser MCP Integration...\n');
10
+
11
+ const browser = new BrowserMCPIntegration({
12
+ headless: false, // Show browser for testing
13
+ defaultViewport: { width: 1280, height: 720 },
14
+ });
15
+
16
+ await browser.initialize();
17
+
18
+ console.log('Browser MCP initialized successfully!');
19
+ console.log('\nAvailable tools:');
20
+ console.log(' - browser_navigate');
21
+ console.log(' - browser_screenshot');
22
+ console.log(' - browser_click');
23
+ console.log(' - browser_type');
24
+ console.log(' - browser_evaluate');
25
+ console.log(' - browser_wait');
26
+ console.log(' - browser_get_content');
27
+ console.log(' - browser_close');
28
+
29
+ console.log('\nBrowser MCP is ready to use with StackMemory!');
30
+
31
+ // Clean up
32
+ await browser.cleanup();
33
+ process.exit(0);
34
+ }
35
+
36
+ testBrowserMCP().catch((error) => {
37
+ console.error('Browser MCP test failed:', error);
38
+ process.exit(1);
39
+ });