@stackmemoryai/stackmemory 0.5.23 → 0.5.24

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 (69) hide show
  1. package/dist/cli/claude-sm.js +2 -0
  2. package/dist/cli/claude-sm.js.map +2 -2
  3. package/dist/cli/commands/discovery.js +279 -0
  4. package/dist/cli/commands/discovery.js.map +7 -0
  5. package/dist/cli/index.js +2 -0
  6. package/dist/cli/index.js.map +2 -2
  7. package/dist/core/retrieval/llm-context-retrieval.js +1 -3
  8. package/dist/core/retrieval/llm-context-retrieval.js.map +3 -3
  9. package/dist/integrations/mcp/handlers/discovery-handlers.js +497 -0
  10. package/dist/integrations/mcp/handlers/discovery-handlers.js.map +7 -0
  11. package/dist/integrations/mcp/handlers/index.js +40 -12
  12. package/dist/integrations/mcp/handlers/index.js.map +2 -2
  13. package/dist/integrations/mcp/server.js +270 -0
  14. package/dist/integrations/mcp/server.js.map +2 -2
  15. package/dist/integrations/mcp/tool-definitions.js +141 -5
  16. package/dist/integrations/mcp/tool-definitions.js.map +2 -2
  17. package/package.json +1 -1
  18. package/dist/cli/commands/agent.js +0 -286
  19. package/dist/cli/commands/agent.js.map +0 -7
  20. package/dist/cli/commands/chromadb.js +0 -482
  21. package/dist/cli/commands/chromadb.js.map +0 -7
  22. package/dist/cli/commands/gc.js +0 -251
  23. package/dist/cli/commands/gc.js.map +0 -7
  24. package/dist/cli/commands/infinite-storage.js +0 -292
  25. package/dist/cli/commands/infinite-storage.js.map +0 -7
  26. package/dist/cli/commands/linear-create.js +0 -171
  27. package/dist/cli/commands/linear-create.js.map +0 -7
  28. package/dist/cli/commands/linear-list.js +0 -103
  29. package/dist/cli/commands/linear-list.js.map +0 -7
  30. package/dist/cli/commands/linear-migrate.js +0 -64
  31. package/dist/cli/commands/linear-migrate.js.map +0 -7
  32. package/dist/cli/commands/linear-test.js +0 -134
  33. package/dist/cli/commands/linear-test.js.map +0 -7
  34. package/dist/cli/commands/tui.js +0 -77
  35. package/dist/cli/commands/tui.js.map +0 -7
  36. package/dist/cli/commands/webhook.js +0 -181
  37. package/dist/cli/commands/webhook.js.map +0 -7
  38. package/dist/cli/streamlined-cli.js +0 -144
  39. package/dist/cli/streamlined-cli.js.map +0 -7
  40. package/dist/core/events/event-bus.js +0 -110
  41. package/dist/core/events/event-bus.js.map +0 -7
  42. package/dist/core/frame/workflow-templates-stub.js +0 -42
  43. package/dist/core/frame/workflow-templates-stub.js.map +0 -7
  44. package/dist/core/plugins/plugin-interface.js +0 -87
  45. package/dist/core/plugins/plugin-interface.js.map +0 -7
  46. package/dist/core/session/clear-survival-stub.js +0 -53
  47. package/dist/core/session/clear-survival-stub.js.map +0 -7
  48. package/dist/core/storage/chromadb-simple.js +0 -172
  49. package/dist/core/storage/chromadb-simple.js.map +0 -7
  50. package/dist/core/storage/simplified-storage.js +0 -328
  51. package/dist/core/storage/simplified-storage.js.map +0 -7
  52. package/dist/features/tasks/pebbles-task-store.js +0 -647
  53. package/dist/features/tasks/pebbles-task-store.js.map +0 -7
  54. package/dist/integrations/linear/sync-enhanced.js +0 -202
  55. package/dist/integrations/linear/sync-enhanced.js.map +0 -7
  56. package/dist/plugins/linear/index.js +0 -166
  57. package/dist/plugins/linear/index.js.map +0 -7
  58. package/dist/plugins/loader.js +0 -57
  59. package/dist/plugins/loader.js.map +0 -7
  60. package/dist/plugins/plugin-interface.js +0 -67
  61. package/dist/plugins/plugin-interface.js.map +0 -7
  62. package/dist/plugins/ralph/simple-ralph-plugin.js +0 -305
  63. package/dist/plugins/ralph/simple-ralph-plugin.js.map +0 -7
  64. package/dist/plugins/ralph/use-cases/code-generator.js +0 -151
  65. package/dist/plugins/ralph/use-cases/code-generator.js.map +0 -7
  66. package/dist/plugins/ralph/use-cases/test-generator.js +0 -201
  67. package/dist/plugins/ralph/use-cases/test-generator.js.map +0 -7
  68. package/dist/utils/logger.js +0 -52
  69. package/dist/utils/logger.js.map +0 -7
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/features/tasks/pebbles-task-store.ts"],
4
- "sourcesContent": ["/**\n * Pebbles Task Storage\n * Git-native JSONL storage with SQLite cache for tasks\n */\n\nimport Database from 'better-sqlite3';\nimport { EventEmitter } from 'events';\nimport { createHash } from 'crypto';\nimport { appendFile, existsSync, mkdirSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { logger } from '../../core/monitoring/logger.js';\nimport {\n DatabaseError,\n TaskError,\n SystemError,\n ErrorCode,\n wrapError,\n createErrorHandler,\n} from '../../core/errors/index.js';\nimport { retry, withTimeout } from '../../core/errors/recovery.js';\nimport { StreamingJSONLParser } from '../../core/performance/streaming-jsonl-parser.js';\nimport { ContextCache } from '../../core/performance/context-cache.js';\n\nexport type TaskStatus =\n | 'pending'\n | 'in_progress'\n | 'completed'\n | 'blocked'\n | 'cancelled';\nexport type TaskPriority = 'low' | 'medium' | 'high' | 'urgent';\n\nexport interface PebblesTask {\n id: string; // Content-hash based (merge-friendly)\n type: 'task_create' | 'task_update' | 'task_complete' | 'task_block';\n timestamp: number;\n parent_id?: string; // For subtasks\n frame_id: string; // Associated call stack frame\n\n // Task data\n title: string;\n description?: string;\n status: TaskStatus;\n priority: TaskPriority;\n assignee?: string;\n\n // Tracking\n created_at: number;\n started_at?: number;\n completed_at?: number;\n estimated_effort?: number; // Minutes\n actual_effort?: number;\n\n // Relationships\n depends_on: string[]; // Task IDs\n blocks: string[]; // Task IDs this blocks\n tags: string[]; // For filtering\n\n // Integration hooks (for Linear phase)\n external_refs?: {\n linear?: { id: string; url: string };\n github?: { issue: number; url: string };\n };\n\n // Context relevance\n context_score?: number; // For intelligent assembly\n last_accessed?: number;\n}\n\nexport interface TaskMetrics {\n total_tasks: number;\n by_status: Record<TaskStatus, number>;\n by_priority: Record<TaskPriority, number>;\n completion_rate: number;\n avg_effort_accuracy: number;\n blocked_tasks: number;\n overdue_tasks: number;\n}\n\nexport class PebblesTaskStore extends EventEmitter {\n private db: Database.Database;\n private projectRoot: string;\n private tasksFile: string;\n private cacheFile: string;\n private jsonlParser: StreamingJSONLParser;\n private taskCache: ContextCache<PebblesTask>;\n\n constructor(projectRoot: string, db: Database.Database) {\n super();\n this.projectRoot = projectRoot;\n this.db = db;\n\n // Ensure .stackmemory directory exists\n const stackmemoryDir = join(projectRoot, '.stackmemory');\n if (!existsSync(stackmemoryDir)) {\n mkdirSync(stackmemoryDir, { recursive: true });\n }\n\n this.tasksFile = join(stackmemoryDir, 'tasks.jsonl');\n this.cacheFile = join(stackmemoryDir, 'cache.db');\n\n // Initialize performance optimizations\n this.jsonlParser = new StreamingJSONLParser();\n this.taskCache = new ContextCache<PebblesTask>({\n maxSize: 10 * 1024 * 1024, // 10MB for tasks\n maxItems: 1000,\n defaultTTL: 3600000, // 1 hour\n });\n\n this.initializeCache();\n // Load existing tasks from JSONL synchronously\n this.loadFromJSONLSync();\n }\n\n private initializeCache() {\n const errorHandler = createErrorHandler({\n operation: 'initializeCache',\n projectRoot: this.projectRoot,\n });\n\n try {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS task_cache (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n timestamp INTEGER NOT NULL,\n parent_id TEXT,\n frame_id TEXT NOT NULL,\n title TEXT NOT NULL,\n description TEXT,\n status TEXT NOT NULL,\n priority TEXT NOT NULL,\n assignee TEXT,\n created_at INTEGER NOT NULL,\n started_at INTEGER,\n completed_at INTEGER,\n estimated_effort INTEGER,\n actual_effort INTEGER,\n depends_on TEXT DEFAULT '[]',\n blocks TEXT DEFAULT '[]',\n tags TEXT DEFAULT '[]',\n external_refs TEXT DEFAULT '{}',\n context_score REAL DEFAULT 0.5,\n last_accessed INTEGER\n );\n \n CREATE INDEX IF NOT EXISTS idx_task_status ON task_cache(status);\n CREATE INDEX IF NOT EXISTS idx_task_priority ON task_cache(priority);\n CREATE INDEX IF NOT EXISTS idx_task_frame ON task_cache(frame_id);\n CREATE INDEX IF NOT EXISTS idx_task_timestamp ON task_cache(timestamp);\n CREATE INDEX IF NOT EXISTS idx_task_parent ON task_cache(parent_id);\n `);\n } catch (error: unknown) {\n const dbError = errorHandler(error, {\n operation: 'initializeCache',\n schema: 'task_cache',\n });\n\n throw new DatabaseError(\n 'Failed to initialize task cache schema',\n ErrorCode.DB_MIGRATION_FAILED,\n {\n projectRoot: this.projectRoot,\n cacheFile: this.cacheFile,\n operation: 'initializeCache',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Load existing tasks from JSONL into SQLite cache (optimized)\n */\n private async loadFromJSONL() {\n if (!existsSync(this.tasksFile)) return;\n\n const errorHandler = createErrorHandler({\n operation: 'loadFromJSONL',\n tasksFile: this.tasksFile,\n });\n\n try {\n let loaded = 0;\n let errors = 0;\n\n // Use streaming parser for memory efficiency\n for await (const batch of this.jsonlParser.parseStream<PebblesTask>(\n this.tasksFile,\n {\n batchSize: 100,\n filter: (obj) => obj.type && obj.id && obj.title, // Basic validation\n onProgress: (count) => {\n if (count % 500 === 0) {\n logger.debug('Loading tasks progress', { loaded: count });\n }\n },\n }\n )) {\n for (const task of batch) {\n try {\n this.upsertToCache(task);\n // Add to in-memory cache for fast access\n this.taskCache.set(task.id, task, {\n ttl: 3600000, // 1 hour cache\n });\n loaded++;\n } catch (error: unknown) {\n errors++;\n logger.warn('Failed to cache task', {\n taskId: task.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n\n logger.info('Loaded tasks from JSONL', {\n loaded,\n errors,\n file: this.tasksFile,\n cacheStats: this.taskCache.getStats(),\n });\n } catch (error: unknown) {\n const systemError = errorHandler(error, {\n operation: 'loadFromJSONL',\n file: this.tasksFile,\n });\n\n throw new SystemError(\n 'Failed to load tasks from JSONL file',\n ErrorCode.INTERNAL_ERROR,\n {\n tasksFile: this.tasksFile,\n operation: 'loadFromJSONL',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Load existing tasks from JSONL synchronously (for constructor)\n */\n private loadFromJSONLSync() {\n if (!existsSync(this.tasksFile)) return;\n\n try {\n const content = readFileSync(this.tasksFile, 'utf-8');\n const lines = content.split('\\n').filter((line) => line.trim());\n\n let loaded = 0;\n let errors = 0;\n\n for (const line of lines) {\n try {\n const task = JSON.parse(line) as PebblesTask;\n\n // Basic validation\n if (task.type && task.id && task.title) {\n this.upsertToCache(task);\n // Add to in-memory cache for fast access\n this.taskCache.set(task.id, task, {\n ttl: 3600000, // 1 hour cache\n });\n loaded++;\n }\n } catch (error: unknown) {\n errors++;\n logger.warn('Failed to parse JSONL line', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n logger.info('Loaded tasks from JSONL', {\n loaded,\n errors,\n file: this.tasksFile,\n });\n } catch (error: unknown) {\n logger.error('Failed to load tasks from JSONL', error as Error);\n }\n }\n\n /**\n * Create a new task with content-hash ID\n */\n public createTask(options: {\n title: string;\n description?: string;\n priority?: TaskPriority;\n frameId: string;\n parentId?: string;\n dependsOn?: string[];\n tags?: string[];\n estimatedEffort?: number;\n assignee?: string;\n }): string {\n const now = Math.floor(Date.now() / 1000);\n\n // Create content for hash (ensures deterministic ID)\n const content = `${options.title}:${options.frameId}:${now}:${Math.random()}`;\n const id = this.generateTaskId(content);\n\n const task: PebblesTask = {\n id,\n type: 'task_create',\n timestamp: now,\n parent_id: options.parentId,\n frame_id: options.frameId,\n title: options.title,\n description: options.description,\n status: 'pending',\n priority: options.priority || 'medium',\n assignee: options.assignee,\n created_at: now,\n estimated_effort: options.estimatedEffort,\n depends_on: options.dependsOn || [],\n blocks: [],\n tags: options.tags || [],\n context_score: 0.5,\n };\n\n this.appendTask(task);\n this.emit('task:created', task);\n this.emit('sync:needed', 'task:created');\n return id;\n }\n\n /**\n * Update task status with new event\n */\n public updateTaskStatus(\n taskId: string,\n newStatus: TaskStatus,\n _reason?: string\n ): void {\n const existing = this.getTask(taskId);\n if (!existing) {\n throw new TaskError(\n `Task not found: ${taskId}`,\n ErrorCode.TASK_NOT_FOUND,\n {\n taskId,\n newStatus,\n operation: 'updateTaskStatus',\n }\n );\n }\n\n // Validate status transition\n if (existing.status === 'completed' && newStatus !== 'cancelled') {\n throw new TaskError(\n `Cannot change completed task status from ${existing.status} to ${newStatus}`,\n ErrorCode.TASK_INVALID_STATE,\n {\n taskId,\n currentStatus: existing.status,\n newStatus,\n operation: 'updateTaskStatus',\n }\n );\n }\n\n const now = Math.floor(Date.now() / 1000);\n const updates: Partial<PebblesTask> = {\n status: newStatus,\n timestamp: now,\n };\n\n // Automatic time tracking\n if (newStatus === 'in_progress' && existing.status === 'pending') {\n updates.started_at = now;\n updates.type = 'task_update';\n } else if (newStatus === 'completed' && existing.status === 'in_progress') {\n updates.completed_at = now;\n updates.type = 'task_complete';\n if (existing.started_at) {\n updates.actual_effort = Math.floor((now - existing.started_at) / 60); // Minutes\n }\n } else if (newStatus === 'blocked') {\n updates.type = 'task_block';\n }\n\n const updatedTask: PebblesTask = { ...existing, ...updates };\n this.appendTask(updatedTask);\n\n if (newStatus === 'completed') {\n this.emit('task:completed', updatedTask);\n }\n this.emit('sync:needed', 'task:updated');\n }\n\n /**\n * Add dependency relationship\n */\n public addDependency(taskId: string, dependsOnId: string): void {\n const task = this.getTask(taskId);\n const dependsOnTask = this.getTask(dependsOnId);\n\n if (!task) {\n throw new TaskError(\n `Task not found: ${taskId}`,\n ErrorCode.TASK_NOT_FOUND,\n {\n taskId,\n operation: 'addDependency',\n }\n );\n }\n\n if (!dependsOnTask) {\n throw new TaskError(\n `Dependency task not found: ${dependsOnId}`,\n ErrorCode.TASK_NOT_FOUND,\n {\n dependsOnId,\n taskId,\n operation: 'addDependency',\n }\n );\n }\n\n // Check for circular dependency\n if (this.wouldCreateCircularDependency(taskId, dependsOnId)) {\n throw new TaskError(\n `Adding dependency would create circular dependency: ${taskId} -> ${dependsOnId}`,\n ErrorCode.TASK_CIRCULAR_DEPENDENCY,\n {\n taskId,\n dependsOnId,\n operation: 'addDependency',\n }\n );\n }\n\n // Update task dependencies\n const updatedTask: PebblesTask = {\n ...task,\n depends_on: [...new Set([...task.depends_on, dependsOnId])],\n timestamp: Math.floor(Date.now() / 1000),\n type: 'task_update',\n };\n\n // Update blocking task\n const updatedBlockingTask: PebblesTask = {\n ...dependsOnTask,\n blocks: [...new Set([...dependsOnTask.blocks, taskId])],\n timestamp: Math.floor(Date.now() / 1000),\n type: 'task_update',\n };\n\n this.appendTask(updatedTask);\n this.appendTask(updatedBlockingTask);\n }\n\n /**\n * Get current active tasks\n */\n public getActiveTasks(frameId?: string): PebblesTask[] {\n try {\n let query = `\n SELECT * FROM task_cache \n WHERE status IN ('pending', 'in_progress')\n `;\n const params: any[] = [];\n\n if (frameId) {\n query += ` AND frame_id = ?`;\n params.push(frameId);\n }\n\n query += ` ORDER BY priority DESC, created_at ASC`;\n\n const rows = this.db.prepare(query).all(...params) as any[];\n return this.hydrateTasks(rows);\n } catch (error: unknown) {\n throw new DatabaseError(\n 'Failed to get active tasks',\n ErrorCode.DB_QUERY_FAILED,\n {\n frameId,\n operation: 'getActiveTasks',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Get task by ID (latest version)\n */\n public getTask(taskId: string): PebblesTask | undefined {\n try {\n const row = this.db\n .prepare(\n `\n SELECT * FROM task_cache WHERE id = ?\n `\n )\n .get(taskId) as any;\n\n return row ? this.hydrateTask(row) : undefined;\n } catch (error: unknown) {\n throw new DatabaseError(\n `Failed to get task: ${taskId}`,\n ErrorCode.DB_QUERY_FAILED,\n {\n taskId,\n operation: 'getTask',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Get tasks that are blocking other tasks\n */\n public getBlockingTasks(): PebblesTask[] {\n try {\n const rows = this.db\n .prepare(\n `\n SELECT * FROM task_cache \n WHERE JSON_ARRAY_LENGTH(blocks) > 0 \n AND status NOT IN ('completed', 'cancelled')\n ORDER BY priority DESC\n `\n )\n .all() as any[];\n\n return this.hydrateTasks(rows);\n } catch (error: unknown) {\n throw new DatabaseError(\n 'Failed to get blocking tasks',\n ErrorCode.DB_QUERY_FAILED,\n {\n operation: 'getBlockingTasks',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Get metrics for current project\n */\n public getMetrics(): TaskMetrics {\n try {\n const statusCounts = this.db\n .prepare(\n `\n SELECT status, COUNT(*) as count \n FROM task_cache \n GROUP BY status\n `\n )\n .all() as { status: TaskStatus; count: number }[];\n\n const priorityCounts = this.db\n .prepare(\n `\n SELECT priority, COUNT(*) as count \n FROM task_cache \n GROUP BY priority \n `\n )\n .all() as { priority: TaskPriority; count: number }[];\n\n const totalTasks = statusCounts.reduce((sum, s) => sum + s.count, 0);\n const completedTasks =\n statusCounts.find((s) => s.status === 'completed')?.count || 0;\n const blockedTasks =\n statusCounts.find((s) => s.status === 'blocked')?.count || 0;\n\n // Calculate effort accuracy\n const effortRows = this.db\n .prepare(\n `\n SELECT estimated_effort, actual_effort \n FROM task_cache \n WHERE estimated_effort IS NOT NULL \n AND actual_effort IS NOT NULL\n `\n )\n .all() as { estimated_effort: number; actual_effort: number }[];\n\n let avgEffortAccuracy = 0;\n if (effortRows.length > 0) {\n const accuracies = effortRows.map(\n (r) =>\n 1 -\n Math.abs(r.estimated_effort - r.actual_effort) /\n Math.max(r.estimated_effort, 1)\n );\n avgEffortAccuracy =\n accuracies.reduce((sum, acc) => sum + acc, 0) / accuracies.length;\n }\n\n return {\n total_tasks: totalTasks,\n by_status: Object.fromEntries(\n statusCounts.map((s) => [s.status, s.count])\n ) as any,\n by_priority: Object.fromEntries(\n priorityCounts.map((p) => [p.priority, p.count])\n ) as any,\n completion_rate: totalTasks > 0 ? completedTasks / totalTasks : 0,\n avg_effort_accuracy: avgEffortAccuracy,\n blocked_tasks: blockedTasks,\n overdue_tasks: 0, // TODO: implement due dates\n };\n } catch (error: unknown) {\n throw new DatabaseError(\n 'Failed to get task metrics',\n ErrorCode.DB_QUERY_FAILED,\n {\n operation: 'getMetrics',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Export tasks for Linear integration (Phase 2)\n */\n public exportForLinear(): Array<{\n title: string;\n description?: string;\n priority: number;\n status: string;\n estimate?: number;\n dependencies: string[];\n }> {\n const tasks = this.db\n .prepare(\n `\n SELECT * FROM task_cache \n WHERE external_refs IS NULL OR JSON_EXTRACT(external_refs, '$.linear') IS NULL\n ORDER BY created_at ASC\n `\n )\n .all() as any[];\n\n return tasks.map((task) => ({\n title: task.title,\n description: task.description,\n priority: this.mapPriorityToLinear(task.priority),\n status: this.mapStatusToLinear(task.status),\n estimate: task.estimated_effort,\n dependencies: JSON.parse(task.depends_on || '[]'),\n }));\n }\n\n // Private methods\n private appendTask(task: PebblesTask) {\n try {\n // Append to JSONL file (git-tracked source of truth)\n const jsonLine = JSON.stringify(task) + '\\n';\n appendFile(this.tasksFile, jsonLine, (err) => {\n if (err) {\n logger.error(\n `Failed to append task ${task.id} to JSONL: ${err.message}`,\n err,\n {\n taskId: task.id,\n tasksFile: this.tasksFile,\n }\n );\n }\n });\n\n // Update SQLite cache (for fast queries) with retry logic\n retry(() => Promise.resolve(this.upsertToCache(task)), {\n maxAttempts: 3,\n initialDelay: 100,\n onRetry: (attempt, error) => {\n logger.warn(`Retrying task cache upsert (attempt ${attempt})`, {\n taskId: task.id,\n errorMessage:\n error instanceof Error ? error.message : String(error),\n });\n },\n }).catch((error) => {\n logger.error(\n 'Failed to upsert task to cache after retries',\n error instanceof Error ? error : new Error(String(error)),\n {\n taskId: task.id,\n }\n );\n throw error;\n });\n\n logger.info('Appended task', {\n id: task.id,\n type: task.type,\n title: task.title,\n status: task.status,\n });\n } catch (error: unknown) {\n throw new SystemError(\n `Failed to append task: ${task.id}`,\n ErrorCode.INTERNAL_ERROR,\n {\n taskId: task.id,\n operation: 'appendTask',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n private upsertToCache(task: PebblesTask) {\n try {\n this.db\n .prepare(\n `\n INSERT OR REPLACE INTO task_cache (\n id, type, timestamp, parent_id, frame_id, title, description,\n status, priority, assignee, created_at, started_at, completed_at,\n estimated_effort, actual_effort, depends_on, blocks, tags,\n external_refs, context_score, last_accessed\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `\n )\n .run(\n task.id,\n task.type,\n task.timestamp,\n task.parent_id,\n task.frame_id,\n task.title,\n task.description,\n task.status,\n task.priority,\n task.assignee,\n task.created_at,\n task.started_at,\n task.completed_at,\n task.estimated_effort,\n task.actual_effort,\n JSON.stringify(task.depends_on),\n JSON.stringify(task.blocks),\n JSON.stringify(task.tags),\n JSON.stringify(task.external_refs || {}),\n task.context_score,\n task.last_accessed\n );\n } catch (error: unknown) {\n throw new DatabaseError(\n `Failed to upsert task to cache: ${task.id}`,\n ErrorCode.DB_QUERY_FAILED,\n {\n taskId: task.id,\n taskTitle: task.title,\n operation: 'upsertToCache',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n private generateTaskId(content: string): string {\n const hash = createHash('sha256').update(content).digest('hex');\n return `tsk-${hash.substring(0, 8)}`;\n }\n\n private hydrateTask = (row: any): PebblesTask => ({\n ...row,\n depends_on: JSON.parse(row.depends_on || '[]'),\n blocks: JSON.parse(row.blocks || '[]'),\n tags: JSON.parse(row.tags || '[]'),\n external_refs: JSON.parse(row.external_refs || '{}'),\n });\n\n private hydrateTasks(rows: any[]): PebblesTask[] {\n return rows.map(this.hydrateTask);\n }\n\n private mapPriorityToLinear(priority: TaskPriority): number {\n const map = { low: 1, medium: 2, high: 3, urgent: 4 };\n return map[priority] || 2;\n }\n\n private mapStatusToLinear(status: TaskStatus): string {\n const map = {\n pending: 'Backlog',\n in_progress: 'In Progress',\n completed: 'Done',\n blocked: 'Blocked',\n cancelled: 'Cancelled',\n };\n return map[status] || 'Backlog';\n }\n\n /**\n * Check if adding a dependency would create a circular dependency\n */\n private wouldCreateCircularDependency(\n taskId: string,\n dependsOnId: string\n ): boolean {\n const visited = new Set<string>();\n const stack = [dependsOnId];\n\n while (stack.length > 0) {\n const currentId = stack.pop()!;\n\n if (currentId === taskId) {\n return true; // Found circular dependency\n }\n\n if (visited.has(currentId)) {\n continue;\n }\n\n visited.add(currentId);\n\n // Get dependencies of current task\n const currentTask = this.getTask(currentId);\n if (currentTask) {\n stack.push(...currentTask.depends_on);\n }\n }\n\n return false;\n }\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,202 +0,0 @@
1
- import { logger } from "../../core/monitoring/logger.js";
2
- class LinearDuplicateDetector {
3
- linearClient;
4
- titleCache = /* @__PURE__ */ new Map();
5
- cacheExpiry = 5 * 60 * 1e3;
6
- // 5 minutes
7
- lastCacheRefresh = 0;
8
- constructor(linearClient) {
9
- this.linearClient = linearClient;
10
- }
11
- /**
12
- * Search for existing Linear issues with similar titles
13
- */
14
- async searchByTitle(title, teamId) {
15
- const normalizedTitle = this.normalizeTitle(title);
16
- if (this.isCacheValid()) {
17
- const cached = this.titleCache.get(normalizedTitle);
18
- if (cached) return cached;
19
- }
20
- try {
21
- const allIssues = await this.linearClient.getIssues({
22
- teamId,
23
- limit: 100
24
- // Use smaller limit to avoid API errors
25
- });
26
- const matchingIssues = allIssues.filter((issue) => {
27
- const issueNormalized = this.normalizeTitle(issue.title);
28
- if (issueNormalized === normalizedTitle) return true;
29
- const similarity = this.calculateSimilarity(normalizedTitle, issueNormalized);
30
- return similarity > 0.85;
31
- });
32
- this.titleCache.set(normalizedTitle, matchingIssues);
33
- this.lastCacheRefresh = Date.now();
34
- return matchingIssues;
35
- } catch (error) {
36
- logger.error("Failed to search Linear issues by title:", error);
37
- return [];
38
- }
39
- }
40
- /**
41
- * Check if a task title would create a duplicate in Linear
42
- */
43
- async checkForDuplicate(title, teamId) {
44
- const existingIssues = await this.searchByTitle(title, teamId);
45
- if (existingIssues.length === 0) {
46
- return { isDuplicate: false };
47
- }
48
- let bestMatch;
49
- let bestSimilarity = 0;
50
- for (const issue of existingIssues) {
51
- const similarity = this.calculateSimilarity(
52
- this.normalizeTitle(title),
53
- this.normalizeTitle(issue.title)
54
- );
55
- if (similarity > bestSimilarity) {
56
- bestSimilarity = similarity;
57
- bestMatch = issue;
58
- }
59
- }
60
- return {
61
- isDuplicate: true,
62
- existingIssue: bestMatch,
63
- similarity: bestSimilarity
64
- };
65
- }
66
- /**
67
- * Merge task content into existing Linear issue
68
- */
69
- async mergeIntoExisting(existingIssue, newTitle, newDescription, additionalContext) {
70
- try {
71
- let mergedDescription = existingIssue.description || "";
72
- if (newDescription && !mergedDescription.includes(newDescription)) {
73
- mergedDescription += `
74
-
75
- ## Additional Context (${(/* @__PURE__ */ new Date()).toISOString()})
76
- `;
77
- mergedDescription += newDescription;
78
- }
79
- if (additionalContext) {
80
- mergedDescription += `
81
-
82
- ---
83
- ${additionalContext}`;
84
- }
85
- const updateQuery = `
86
- mutation UpdateIssue($id: String!, $input: IssueUpdateInput!) {
87
- issueUpdate(id: $id, input: $input) {
88
- issue {
89
- id
90
- identifier
91
- title
92
- description
93
- updatedAt
94
- }
95
- }
96
- }
97
- `;
98
- const variables = {
99
- id: existingIssue.id,
100
- input: {
101
- description: mergedDescription
102
- }
103
- };
104
- const response = await this.linearClient.graphql(updateQuery, variables);
105
- const updatedIssue = response.issueUpdate?.issue;
106
- if (updatedIssue) {
107
- logger.info(
108
- `Merged content into existing Linear issue ${existingIssue.identifier}: ${existingIssue.title}`
109
- );
110
- return updatedIssue;
111
- }
112
- return existingIssue;
113
- } catch (error) {
114
- logger.error("Failed to merge into existing Linear issue:", error);
115
- return existingIssue;
116
- }
117
- }
118
- /**
119
- * Normalize title for comparison
120
- */
121
- normalizeTitle(title) {
122
- return title.toLowerCase().trim().replace(/\s+/g, " ").replace(/[^\w\s-]/g, "").replace(/^(sta|eng|bug|feat|task|tsk)[-\s]\d+[-\s:]*/, "").trim();
123
- }
124
- /**
125
- * Calculate similarity between two strings (Levenshtein distance based)
126
- */
127
- calculateSimilarity(str1, str2) {
128
- if (str1 === str2) return 1;
129
- if (str1.length === 0 || str2.length === 0) return 0;
130
- const distance = this.levenshteinDistance(str1, str2);
131
- const maxLength = Math.max(str1.length, str2.length);
132
- return 1 - distance / maxLength;
133
- }
134
- /**
135
- * Calculate Levenshtein distance between two strings
136
- */
137
- levenshteinDistance(str1, str2) {
138
- const m = str1.length;
139
- const n = str2.length;
140
- const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
141
- for (let i = 0; i <= m; i++) dp[i][0] = i;
142
- for (let j = 0; j <= n; j++) dp[0][j] = j;
143
- for (let i = 1; i <= m; i++) {
144
- for (let j = 1; j <= n; j++) {
145
- if (str1[i - 1] === str2[j - 1]) {
146
- dp[i][j] = dp[i - 1][j - 1];
147
- } else {
148
- dp[i][j] = 1 + Math.min(
149
- dp[i - 1][j],
150
- // deletion
151
- dp[i][j - 1],
152
- // insertion
153
- dp[i - 1][j - 1]
154
- // substitution
155
- );
156
- }
157
- }
158
- }
159
- return dp[m][n];
160
- }
161
- /**
162
- * Check if cache is still valid
163
- */
164
- isCacheValid() {
165
- return Date.now() - this.lastCacheRefresh < this.cacheExpiry;
166
- }
167
- /**
168
- * Clear the title cache
169
- */
170
- clearCache() {
171
- this.titleCache.clear();
172
- this.lastCacheRefresh = 0;
173
- }
174
- }
175
- async function syncToLinearWithDuplicateCheck(linearClient, task, teamId) {
176
- const detector = new LinearDuplicateDetector(linearClient);
177
- const duplicateCheck = await detector.checkForDuplicate(task.title, teamId);
178
- if (duplicateCheck.isDuplicate && duplicateCheck.existingIssue) {
179
- logger.info(
180
- `Found existing Linear issue for "${task.title}": ${duplicateCheck.existingIssue.identifier} (${Math.round((duplicateCheck.similarity || 0) * 100)}% match)`
181
- );
182
- const mergedIssue = await detector.mergeIntoExisting(
183
- duplicateCheck.existingIssue,
184
- task.title,
185
- task.description,
186
- `StackMemory Task ID: ${task.id}`
187
- );
188
- return { issue: mergedIssue, wasmerged: true };
189
- }
190
- const newIssue = await linearClient.createIssue({
191
- title: task.title,
192
- description: task.description,
193
- teamId
194
- });
195
- logger.info(`Created new Linear issue: ${newIssue.identifier}`);
196
- return { issue: newIssue, wasmerged: false };
197
- }
198
- export {
199
- LinearDuplicateDetector,
200
- syncToLinearWithDuplicateCheck
201
- };
202
- //# sourceMappingURL=sync-enhanced.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/integrations/linear/sync-enhanced.ts"],
4
- "sourcesContent": ["/**\n * Enhanced Linear Sync with Duplicate Detection\n * Prevents duplicate issues by checking titles before creation\n */\n\nimport { LinearClient, LinearIssue } from './client.js';\nimport { logger } from '../../core/monitoring/logger.js';\n\nexport interface DuplicateCheckResult {\n isDuplicate: boolean;\n existingIssue?: LinearIssue;\n similarity?: number;\n}\n\nexport class LinearDuplicateDetector {\n private linearClient: LinearClient;\n private titleCache: Map<string, LinearIssue[]> = new Map();\n private cacheExpiry: number = 5 * 60 * 1000; // 5 minutes\n private lastCacheRefresh: number = 0;\n\n constructor(linearClient: LinearClient) {\n this.linearClient = linearClient;\n }\n\n /**\n * Search for existing Linear issues with similar titles\n */\n async searchByTitle(title: string, teamId?: string): Promise<LinearIssue[]> {\n const normalizedTitle = this.normalizeTitle(title);\n \n // Check cache first\n if (this.isCacheValid()) {\n const cached = this.titleCache.get(normalizedTitle);\n if (cached) return cached;\n }\n\n try {\n // Get all issues from the team (Linear API limit is 250)\n const allIssues = await this.linearClient.getIssues({\n teamId,\n limit: 100, // Use smaller limit to avoid API errors\n });\n\n // Filter for matching titles (exact and fuzzy)\n const matchingIssues = allIssues.filter(issue => {\n const issueNormalized = this.normalizeTitle(issue.title);\n \n // Exact match\n if (issueNormalized === normalizedTitle) return true;\n \n // Fuzzy match - check if titles are very similar\n const similarity = this.calculateSimilarity(normalizedTitle, issueNormalized);\n return similarity > 0.85; // 85% similarity threshold\n });\n\n // Update cache\n this.titleCache.set(normalizedTitle, matchingIssues);\n this.lastCacheRefresh = Date.now();\n\n return matchingIssues;\n } catch (error) {\n logger.error('Failed to search Linear issues by title:', error as Error);\n return [];\n }\n }\n\n /**\n * Check if a task title would create a duplicate in Linear\n */\n async checkForDuplicate(\n title: string,\n teamId?: string\n ): Promise<DuplicateCheckResult> {\n const existingIssues = await this.searchByTitle(title, teamId);\n \n if (existingIssues.length === 0) {\n return { isDuplicate: false };\n }\n\n // Find the best match\n let bestMatch: LinearIssue | undefined;\n let bestSimilarity = 0;\n\n for (const issue of existingIssues) {\n const similarity = this.calculateSimilarity(\n this.normalizeTitle(title),\n this.normalizeTitle(issue.title)\n );\n \n if (similarity > bestSimilarity) {\n bestSimilarity = similarity;\n bestMatch = issue;\n }\n }\n\n return {\n isDuplicate: true,\n existingIssue: bestMatch,\n similarity: bestSimilarity,\n };\n }\n\n /**\n * Merge task content into existing Linear issue\n */\n async mergeIntoExisting(\n existingIssue: LinearIssue,\n newTitle: string,\n newDescription?: string,\n additionalContext?: string\n ): Promise<LinearIssue> {\n try {\n // Build merged description\n let mergedDescription = existingIssue.description || '';\n \n if (newDescription && !mergedDescription.includes(newDescription)) {\n mergedDescription += `\\n\\n## Additional Context (${new Date().toISOString()})\\n`;\n mergedDescription += newDescription;\n }\n\n if (additionalContext) {\n mergedDescription += `\\n\\n---\\n${additionalContext}`;\n }\n\n // Update the existing issue\n const updateQuery = `\n mutation UpdateIssue($id: String!, $input: IssueUpdateInput!) {\n issueUpdate(id: $id, input: $input) {\n issue {\n id\n identifier\n title\n description\n updatedAt\n }\n }\n }\n `;\n\n const variables = {\n id: existingIssue.id,\n input: {\n description: mergedDescription,\n },\n };\n\n const response = await this.linearClient.graphql(updateQuery, variables);\n const updatedIssue = response.issueUpdate?.issue;\n\n if (updatedIssue) {\n logger.info(\n `Merged content into existing Linear issue ${existingIssue.identifier}: ${existingIssue.title}`\n );\n return updatedIssue;\n }\n\n return existingIssue;\n } catch (error) {\n logger.error('Failed to merge into existing Linear issue:', error as Error);\n return existingIssue;\n }\n }\n\n /**\n * Normalize title for comparison\n */\n private normalizeTitle(title: string): string {\n return title\n .toLowerCase()\n .trim()\n .replace(/\\s+/g, ' ') // Normalize whitespace\n .replace(/[^\\w\\s-]/g, '') // Remove special characters except hyphens\n .replace(/^(sta|eng|bug|feat|task|tsk)[-\\s]\\d+[-\\s:]*/, '') // Remove issue prefixes\n .trim();\n }\n\n /**\n * Calculate similarity between two strings (Levenshtein distance based)\n */\n private calculateSimilarity(str1: string, str2: string): number {\n if (str1 === str2) return 1;\n if (str1.length === 0 || str2.length === 0) return 0;\n\n // Use Levenshtein distance for similarity calculation\n const distance = this.levenshteinDistance(str1, str2);\n const maxLength = Math.max(str1.length, str2.length);\n \n return 1 - (distance / maxLength);\n }\n\n /**\n * Calculate Levenshtein distance between two strings\n */\n private levenshteinDistance(str1: string, str2: string): number {\n const m = str1.length;\n const n = str2.length;\n const dp: number[][] = Array(m + 1)\n .fill(null)\n .map(() => Array(n + 1).fill(0));\n\n for (let i = 0; i <= m; i++) dp[i][0] = i;\n for (let j = 0; j <= n; j++) dp[0][j] = j;\n\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n if (str1[i - 1] === str2[j - 1]) {\n dp[i][j] = dp[i - 1][j - 1];\n } else {\n dp[i][j] = 1 + Math.min(\n dp[i - 1][j], // deletion\n dp[i][j - 1], // insertion\n dp[i - 1][j - 1] // substitution\n );\n }\n }\n }\n\n return dp[m][n];\n }\n\n /**\n * Check if cache is still valid\n */\n private isCacheValid(): boolean {\n return Date.now() - this.lastCacheRefresh < this.cacheExpiry;\n }\n\n /**\n * Clear the title cache\n */\n clearCache(): void {\n this.titleCache.clear();\n this.lastCacheRefresh = 0;\n }\n}\n\n/**\n * Enhanced sync function that prevents duplicates\n */\nexport async function syncToLinearWithDuplicateCheck(\n linearClient: LinearClient,\n task: any,\n teamId: string\n): Promise<{ issue: LinearIssue; wasmerged: boolean }> {\n const detector = new LinearDuplicateDetector(linearClient);\n \n // Check for duplicates\n const duplicateCheck = await detector.checkForDuplicate(task.title, teamId);\n \n if (duplicateCheck.isDuplicate && duplicateCheck.existingIssue) {\n // Merge into existing issue\n logger.info(\n `Found existing Linear issue for \"${task.title}\": ${duplicateCheck.existingIssue.identifier} (${Math.round((duplicateCheck.similarity || 0) * 100)}% match)`\n );\n \n const mergedIssue = await detector.mergeIntoExisting(\n duplicateCheck.existingIssue,\n task.title,\n task.description,\n `StackMemory Task ID: ${task.id}`\n );\n \n return { issue: mergedIssue, wasmerged: true };\n }\n \n // No duplicate found, create new issue\n const newIssue = await linearClient.createIssue({\n title: task.title,\n description: task.description,\n teamId,\n });\n \n logger.info(`Created new Linear issue: ${newIssue.identifier}`);\n return { issue: newIssue, wasmerged: false };\n}"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,166 +0,0 @@
1
- import { LinearClient } from "@linear/sdk";
2
- import { logger } from "../../core/monitoring/logger.js";
3
- class LinearPlugin {
4
- name = "linear-integration";
5
- version = "1.0.0";
6
- description = "Linear task synchronization for StackMemory";
7
- client = null;
8
- apiKey = null;
9
- async initialize() {
10
- this.apiKey = process.env["LINEAR_API_KEY"] || null;
11
- if (!this.apiKey) {
12
- logger.info("Linear plugin: No API key found, running in offline mode");
13
- return;
14
- }
15
- try {
16
- this.client = new LinearClient({ apiKey: this.apiKey });
17
- await this.client.me;
18
- logger.info("Linear plugin initialized successfully");
19
- } catch (error) {
20
- logger.warn("Linear plugin: Failed to connect", error);
21
- this.client = null;
22
- }
23
- }
24
- async shutdown() {
25
- this.client = null;
26
- logger.info("Linear plugin shut down");
27
- }
28
- registerCommands() {
29
- return [
30
- {
31
- name: "linear-sync",
32
- description: "Sync tasks with Linear",
33
- action: this.syncTasks.bind(this),
34
- options: [
35
- {
36
- flag: "--team <id>",
37
- description: "Linear team ID"
38
- },
39
- {
40
- flag: "--project <id>",
41
- description: "Linear project ID"
42
- }
43
- ]
44
- },
45
- {
46
- name: "linear-create",
47
- description: "Create a Linear issue from current context",
48
- action: this.createIssue.bind(this),
49
- options: [
50
- {
51
- flag: "--title <title>",
52
- description: "Issue title"
53
- },
54
- {
55
- flag: "--description <desc>",
56
- description: "Issue description"
57
- }
58
- ]
59
- }
60
- ];
61
- }
62
- registerTools() {
63
- return [
64
- {
65
- name: "linear_sync",
66
- description: "Sync current context with Linear tasks",
67
- inputSchema: {
68
- type: "object",
69
- properties: {
70
- teamId: { type: "string", description: "Linear team ID" },
71
- projectId: { type: "string", description: "Linear project ID" }
72
- }
73
- },
74
- handler: async (input) => {
75
- return await this.syncTasks(input);
76
- }
77
- },
78
- {
79
- name: "linear_create_issue",
80
- description: "Create a Linear issue",
81
- inputSchema: {
82
- type: "object",
83
- properties: {
84
- title: { type: "string" },
85
- description: { type: "string" },
86
- teamId: { type: "string" }
87
- },
88
- required: ["title"]
89
- },
90
- handler: async (input) => {
91
- return await this.createIssue(input);
92
- }
93
- }
94
- ];
95
- }
96
- onFrameCreated(frame) {
97
- if (frame.type === "task" && this.client) {
98
- this.syncFrameToLinear(frame).catch((error) => {
99
- logger.debug("Failed to sync frame to Linear", error);
100
- });
101
- }
102
- }
103
- async syncTasks(options) {
104
- if (!this.client) {
105
- return { success: false, message: "Linear not connected" };
106
- }
107
- try {
108
- const issues = await this.client.issues({
109
- filter: {
110
- team: { id: { eq: options.teamId } }
111
- }
112
- });
113
- return {
114
- success: true,
115
- synced: issues.nodes.length,
116
- message: `Synced ${issues.nodes.length} issues from Linear`
117
- };
118
- } catch (error) {
119
- logger.error("Linear sync failed", error);
120
- return { success: false, error: error.message };
121
- }
122
- }
123
- async createIssue(options) {
124
- if (!this.client) {
125
- return { success: false, message: "Linear not connected" };
126
- }
127
- try {
128
- const issue = await this.client.createIssue({
129
- title: options.title,
130
- description: options.description,
131
- teamId: options.teamId || await this.getDefaultTeamId()
132
- });
133
- return {
134
- success: true,
135
- issueId: issue.issue?.id,
136
- url: issue.issue?.url,
137
- message: `Created Linear issue: ${issue.issue?.identifier}`
138
- };
139
- } catch (error) {
140
- logger.error("Failed to create Linear issue", error);
141
- return { success: false, error: error.message };
142
- }
143
- }
144
- async syncFrameToLinear(frame) {
145
- if (frame.name && frame.content) {
146
- await this.createIssue({
147
- title: frame.name,
148
- description: frame.content
149
- });
150
- }
151
- }
152
- async getDefaultTeamId() {
153
- if (!this.client) throw new Error("Linear not connected");
154
- const teams = await this.client.teams();
155
- if (teams.nodes.length > 0) {
156
- return teams.nodes[0].id;
157
- }
158
- throw new Error("No Linear teams found");
159
- }
160
- }
161
- var linear_default = LinearPlugin;
162
- export {
163
- LinearPlugin,
164
- linear_default as default
165
- };
166
- //# sourceMappingURL=index.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/plugins/linear/index.ts"],
4
- "sourcesContent": ["/**\n * Linear Integration Plugin for StackMemory\n * Provides task synchronization with Linear as an optional feature\n */\n\nimport { StackMemoryPlugin, PluginCommand, PluginTool } from '../plugin-interface.js';\nimport { LinearClient } from '@linear/sdk';\nimport { logger } from '../../core/monitoring/logger.js';\n\nexport class LinearPlugin implements StackMemoryPlugin {\n name = 'linear-integration';\n version = '1.0.0';\n description = 'Linear task synchronization for StackMemory';\n \n private client: LinearClient | null = null;\n private apiKey: string | null = null;\n \n async initialize(): Promise<void> {\n this.apiKey = process.env['LINEAR_API_KEY'] || null;\n \n if (!this.apiKey) {\n logger.info('Linear plugin: No API key found, running in offline mode');\n return;\n }\n \n try {\n this.client = new LinearClient({ apiKey: this.apiKey });\n await this.client.me; // Test connection\n logger.info('Linear plugin initialized successfully');\n } catch (error) {\n logger.warn('Linear plugin: Failed to connect', error);\n this.client = null;\n }\n }\n \n async shutdown(): Promise<void> {\n this.client = null;\n logger.info('Linear plugin shut down');\n }\n \n registerCommands(): PluginCommand[] {\n return [\n {\n name: 'linear-sync',\n description: 'Sync tasks with Linear',\n action: this.syncTasks.bind(this),\n options: [\n {\n flag: '--team <id>',\n description: 'Linear team ID',\n },\n {\n flag: '--project <id>',\n description: 'Linear project ID',\n }\n ]\n },\n {\n name: 'linear-create',\n description: 'Create a Linear issue from current context',\n action: this.createIssue.bind(this),\n options: [\n {\n flag: '--title <title>',\n description: 'Issue title',\n },\n {\n flag: '--description <desc>',\n description: 'Issue description',\n }\n ]\n }\n ];\n }\n \n registerTools(): PluginTool[] {\n return [\n {\n name: 'linear_sync',\n description: 'Sync current context with Linear tasks',\n inputSchema: {\n type: 'object',\n properties: {\n teamId: { type: 'string', description: 'Linear team ID' },\n projectId: { type: 'string', description: 'Linear project ID' }\n }\n },\n handler: async (input) => {\n return await this.syncTasks(input);\n }\n },\n {\n name: 'linear_create_issue',\n description: 'Create a Linear issue',\n inputSchema: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n description: { type: 'string' },\n teamId: { type: 'string' }\n },\n required: ['title']\n },\n handler: async (input) => {\n return await this.createIssue(input);\n }\n }\n ];\n }\n \n onFrameCreated(frame: any): void {\n // Auto-sync with Linear when a task frame is created\n if (frame.type === 'task' && this.client) {\n this.syncFrameToLinear(frame).catch(error => {\n logger.debug('Failed to sync frame to Linear', error);\n });\n }\n }\n \n private async syncTasks(options: any): Promise<any> {\n if (!this.client) {\n return { success: false, message: 'Linear not connected' };\n }\n \n try {\n // Basic sync implementation\n const issues = await this.client.issues({\n filter: {\n team: { id: { eq: options.teamId } }\n }\n });\n \n return {\n success: true,\n synced: issues.nodes.length,\n message: `Synced ${issues.nodes.length} issues from Linear`\n };\n } catch (error) {\n logger.error('Linear sync failed', error);\n return { success: false, error: (error as Error).message };\n }\n }\n \n private async createIssue(options: any): Promise<any> {\n if (!this.client) {\n return { success: false, message: 'Linear not connected' };\n }\n \n try {\n const issue = await this.client.createIssue({\n title: options.title,\n description: options.description,\n teamId: options.teamId || (await this.getDefaultTeamId())\n });\n \n return {\n success: true,\n issueId: issue.issue?.id,\n url: issue.issue?.url,\n message: `Created Linear issue: ${issue.issue?.identifier}`\n };\n } catch (error) {\n logger.error('Failed to create Linear issue', error);\n return { success: false, error: (error as Error).message };\n }\n }\n \n private async syncFrameToLinear(frame: any): Promise<void> {\n // Simple frame to Linear sync\n if (frame.name && frame.content) {\n await this.createIssue({\n title: frame.name,\n description: frame.content\n });\n }\n }\n \n private async getDefaultTeamId(): Promise<string> {\n if (!this.client) throw new Error('Linear not connected');\n \n const teams = await this.client.teams();\n if (teams.nodes.length > 0) {\n return teams.nodes[0].id;\n }\n throw new Error('No Linear teams found');\n }\n}\n\n// Export for plugin registration\nexport default LinearPlugin;"],
5
- "mappings": "AAMA,SAAS,oBAAoB;AAC7B,SAAS,cAAc;AAEhB,MAAM,aAA0C;AAAA,EACrD,OAAO;AAAA,EACP,UAAU;AAAA,EACV,cAAc;AAAA,EAEN,SAA8B;AAAA,EAC9B,SAAwB;AAAA,EAEhC,MAAM,aAA4B;AAChC,SAAK,SAAS,QAAQ,IAAI,gBAAgB,KAAK;AAE/C,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,KAAK,0DAA0D;AACtE;AAAA,IACF;AAEA,QAAI;AACF,WAAK,SAAS,IAAI,aAAa,EAAE,QAAQ,KAAK,OAAO,CAAC;AACtD,YAAM,KAAK,OAAO;AAClB,aAAO,KAAK,wCAAwC;AAAA,IACtD,SAAS,OAAO;AACd,aAAO,KAAK,oCAAoC,KAAK;AACrD,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AAAA,EAEA,MAAM,WAA0B;AAC9B,SAAK,SAAS;AACd,WAAO,KAAK,yBAAyB;AAAA,EACvC;AAAA,EAEA,mBAAoC;AAClC,WAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,QAAQ,KAAK,UAAU,KAAK,IAAI;AAAA,QAChC,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,QAAQ,KAAK,YAAY,KAAK,IAAI;AAAA,QAClC,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,gBAA8B;AAC5B,WAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,aAAa;AAAA,UACX,MAAM;AAAA,UACN,YAAY;AAAA,YACV,QAAQ,EAAE,MAAM,UAAU,aAAa,iBAAiB;AAAA,YACxD,WAAW,EAAE,MAAM,UAAU,aAAa,oBAAoB;AAAA,UAChE;AAAA,QACF;AAAA,QACA,SAAS,OAAO,UAAU;AACxB,iBAAO,MAAM,KAAK,UAAU,KAAK;AAAA,QACnC;AAAA,MACF;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,aAAa;AAAA,UACX,MAAM;AAAA,UACN,YAAY;AAAA,YACV,OAAO,EAAE,MAAM,SAAS;AAAA,YACxB,aAAa,EAAE,MAAM,SAAS;AAAA,YAC9B,QAAQ,EAAE,MAAM,SAAS;AAAA,UAC3B;AAAA,UACA,UAAU,CAAC,OAAO;AAAA,QACpB;AAAA,QACA,SAAS,OAAO,UAAU;AACxB,iBAAO,MAAM,KAAK,YAAY,KAAK;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,eAAe,OAAkB;AAE/B,QAAI,MAAM,SAAS,UAAU,KAAK,QAAQ;AACxC,WAAK,kBAAkB,KAAK,EAAE,MAAM,WAAS;AAC3C,eAAO,MAAM,kCAAkC,KAAK;AAAA,MACtD,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAc,UAAU,SAA4B;AAClD,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,EAAE,SAAS,OAAO,SAAS,uBAAuB;AAAA,IAC3D;AAEA,QAAI;AAEF,YAAM,SAAS,MAAM,KAAK,OAAO,OAAO;AAAA,QACtC,QAAQ;AAAA,UACN,MAAM,EAAE,IAAI,EAAE,IAAI,QAAQ,OAAO,EAAE;AAAA,QACrC;AAAA,MACF,CAAC;AAED,aAAO;AAAA,QACL,SAAS;AAAA,QACT,QAAQ,OAAO,MAAM;AAAA,QACrB,SAAS,UAAU,OAAO,MAAM,MAAM;AAAA,MACxC;AAAA,IACF,SAAS,OAAO;AACd,aAAO,MAAM,sBAAsB,KAAK;AACxC,aAAO,EAAE,SAAS,OAAO,OAAQ,MAAgB,QAAQ;AAAA,IAC3D;AAAA,EACF;AAAA,EAEA,MAAc,YAAY,SAA4B;AACpD,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,EAAE,SAAS,OAAO,SAAS,uBAAuB;AAAA,IAC3D;AAEA,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,OAAO,YAAY;AAAA,QAC1C,OAAO,QAAQ;AAAA,QACf,aAAa,QAAQ;AAAA,QACrB,QAAQ,QAAQ,UAAW,MAAM,KAAK,iBAAiB;AAAA,MACzD,CAAC;AAED,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,MAAM,OAAO;AAAA,QACtB,KAAK,MAAM,OAAO;AAAA,QAClB,SAAS,yBAAyB,MAAM,OAAO,UAAU;AAAA,MAC3D;AAAA,IACF,SAAS,OAAO;AACd,aAAO,MAAM,iCAAiC,KAAK;AACnD,aAAO,EAAE,SAAS,OAAO,OAAQ,MAAgB,QAAQ;AAAA,IAC3D;AAAA,EACF;AAAA,EAEA,MAAc,kBAAkB,OAA2B;AAEzD,QAAI,MAAM,QAAQ,MAAM,SAAS;AAC/B,YAAM,KAAK,YAAY;AAAA,QACrB,OAAO,MAAM;AAAA,QACb,aAAa,MAAM;AAAA,MACrB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAc,mBAAoC;AAChD,QAAI,CAAC,KAAK,OAAQ,OAAM,IAAI,MAAM,sBAAsB;AAExD,UAAM,QAAQ,MAAM,KAAK,OAAO,MAAM;AACtC,QAAI,MAAM,MAAM,SAAS,GAAG;AAC1B,aAAO,MAAM,MAAM,CAAC,EAAE;AAAA,IACxB;AACA,UAAM,IAAI,MAAM,uBAAuB;AAAA,EACzC;AACF;AAGA,IAAO,iBAAQ;",
6
- "names": []
7
- }
@@ -1,57 +0,0 @@
1
- import { pluginManager } from "./plugin-interface.js";
2
- import { logger } from "../core/monitoring/logger.js";
3
- import { existsSync, readFileSync } from "fs";
4
- import { join } from "path";
5
- async function loadPlugins() {
6
- const configPath = join(process.cwd(), ".stackmemory", "plugins.json");
7
- let enabledPlugins = [];
8
- if (existsSync(configPath)) {
9
- try {
10
- const config = JSON.parse(readFileSync(configPath, "utf-8"));
11
- enabledPlugins = config.enabled || [];
12
- } catch (error) {
13
- logger.warn("Failed to load plugin configuration", error);
14
- }
15
- } else {
16
- enabledPlugins = [];
17
- if (process.env["ENABLE_LINEAR_PLUGIN"] === "true") {
18
- enabledPlugins.push("linear");
19
- }
20
- }
21
- for (const pluginName of enabledPlugins) {
22
- try {
23
- await loadPlugin(pluginName);
24
- } catch (error) {
25
- logger.warn(`Failed to load plugin ${pluginName}`, error);
26
- }
27
- }
28
- const loadedPlugins = pluginManager.getAllPlugins();
29
- if (loadedPlugins.length > 0) {
30
- logger.info(
31
- `Loaded ${loadedPlugins.length} plugins: ${loadedPlugins.map((p) => p.name).join(", ")}`
32
- );
33
- }
34
- }
35
- async function loadPlugin(name) {
36
- switch (name) {
37
- case "linear": {
38
- const { LinearPlugin } = await import("./linear/index.js");
39
- const plugin = new LinearPlugin();
40
- await pluginManager.register(plugin);
41
- break;
42
- }
43
- // Future plugins can be added here
44
- // case 'github': {
45
- // const { GitHubPlugin } = await import('./github/index.js');
46
- // await pluginManager.register(new GitHubPlugin());
47
- // break;
48
- // }
49
- default:
50
- logger.warn(`Unknown plugin: ${name}`);
51
- }
52
- }
53
- export {
54
- loadPlugins,
55
- pluginManager
56
- };
57
- //# sourceMappingURL=loader.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/plugins/loader.ts"],
4
- "sourcesContent": ["/**\n * Plugin Loader for StackMemory\n * Automatically loads plugins based on configuration\n */\n\nimport { pluginManager } from './plugin-interface.js';\nimport { logger } from '../core/monitoring/logger.js';\nimport { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\n\nexport async function loadPlugins(): Promise<void> {\n // Check for plugin configuration\n const configPath = join(process.cwd(), '.stackmemory', 'plugins.json');\n\n let enabledPlugins: string[] = [];\n\n if (existsSync(configPath)) {\n try {\n const config = JSON.parse(readFileSync(configPath, 'utf-8'));\n enabledPlugins = config.enabled || [];\n } catch (error) {\n logger.warn('Failed to load plugin configuration', error);\n }\n } else {\n // Default plugins (none by default, all optional)\n enabledPlugins = [];\n\n // Check environment variables for opt-in plugins\n if (process.env['ENABLE_LINEAR_PLUGIN'] === 'true') {\n enabledPlugins.push('linear');\n }\n }\n\n // Load enabled plugins\n for (const pluginName of enabledPlugins) {\n try {\n await loadPlugin(pluginName);\n } catch (error) {\n logger.warn(`Failed to load plugin ${pluginName}`, error);\n }\n }\n\n const loadedPlugins = pluginManager.getAllPlugins();\n if (loadedPlugins.length > 0) {\n logger.info(\n `Loaded ${loadedPlugins.length} plugins: ${loadedPlugins.map((p) => p.name).join(', ')}`\n );\n }\n}\n\nasync function loadPlugin(name: string): Promise<void> {\n switch (name) {\n case 'linear': {\n const { LinearPlugin } = await import('./linear/index.js');\n const plugin = new LinearPlugin();\n await pluginManager.register(plugin);\n break;\n }\n\n // Future plugins can be added here\n // case 'github': {\n // const { GitHubPlugin } = await import('./github/index.js');\n // await pluginManager.register(new GitHubPlugin());\n // break;\n // }\n\n default:\n logger.warn(`Unknown plugin: ${name}`);\n }\n}\n\nexport { pluginManager };\n"],
5
- "mappings": "AAKA,SAAS,qBAAqB;AAC9B,SAAS,cAAc;AACvB,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAY;AAErB,eAAsB,cAA6B;AAEjD,QAAM,aAAa,KAAK,QAAQ,IAAI,GAAG,gBAAgB,cAAc;AAErE,MAAI,iBAA2B,CAAC;AAEhC,MAAI,WAAW,UAAU,GAAG;AAC1B,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,aAAa,YAAY,OAAO,CAAC;AAC3D,uBAAiB,OAAO,WAAW,CAAC;AAAA,IACtC,SAAS,OAAO;AACd,aAAO,KAAK,uCAAuC,KAAK;AAAA,IAC1D;AAAA,EACF,OAAO;AAEL,qBAAiB,CAAC;AAGlB,QAAI,QAAQ,IAAI,sBAAsB,MAAM,QAAQ;AAClD,qBAAe,KAAK,QAAQ;AAAA,IAC9B;AAAA,EACF;AAGA,aAAW,cAAc,gBAAgB;AACvC,QAAI;AACF,YAAM,WAAW,UAAU;AAAA,IAC7B,SAAS,OAAO;AACd,aAAO,KAAK,yBAAyB,UAAU,IAAI,KAAK;AAAA,IAC1D;AAAA,EACF;AAEA,QAAM,gBAAgB,cAAc,cAAc;AAClD,MAAI,cAAc,SAAS,GAAG;AAC5B,WAAO;AAAA,MACL,UAAU,cAAc,MAAM,aAAa,cAAc,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA,IACxF;AAAA,EACF;AACF;AAEA,eAAe,WAAW,MAA6B;AACrD,UAAQ,MAAM;AAAA,IACZ,KAAK,UAAU;AACb,YAAM,EAAE,aAAa,IAAI,MAAM,OAAO,mBAAmB;AACzD,YAAM,SAAS,IAAI,aAAa;AAChC,YAAM,cAAc,SAAS,MAAM;AACnC;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA;AACE,aAAO,KAAK,mBAAmB,IAAI,EAAE;AAAA,EACzC;AACF;",
6
- "names": []
7
- }
@@ -1,67 +0,0 @@
1
- class PluginManager {
2
- plugins = /* @__PURE__ */ new Map();
3
- async register(plugin) {
4
- if (this.plugins.has(plugin.name)) {
5
- throw new Error(`Plugin ${plugin.name} already registered`);
6
- }
7
- this.plugins.set(plugin.name, plugin);
8
- if (plugin.initialize) {
9
- await plugin.initialize();
10
- }
11
- }
12
- async unregister(name) {
13
- const plugin = this.plugins.get(name);
14
- if (!plugin) return;
15
- if (plugin.shutdown) {
16
- await plugin.shutdown();
17
- }
18
- this.plugins.delete(name);
19
- }
20
- getPlugin(name) {
21
- return this.plugins.get(name);
22
- }
23
- getAllPlugins() {
24
- return Array.from(this.plugins.values());
25
- }
26
- // Get all commands from all plugins
27
- getAllCommands() {
28
- const commands = [];
29
- for (const plugin of this.plugins.values()) {
30
- if (plugin.registerCommands) {
31
- commands.push(...plugin.registerCommands());
32
- }
33
- }
34
- return commands;
35
- }
36
- // Get all tools from all plugins
37
- getAllTools() {
38
- const tools = [];
39
- for (const plugin of this.plugins.values()) {
40
- if (plugin.registerTools) {
41
- tools.push(...plugin.registerTools());
42
- }
43
- }
44
- return tools;
45
- }
46
- // Emit events to all plugins
47
- async emitFrameCreated(frame) {
48
- for (const plugin of this.plugins.values()) {
49
- if (plugin.onFrameCreated) {
50
- plugin.onFrameCreated(frame);
51
- }
52
- }
53
- }
54
- async emitContextSaved(context) {
55
- for (const plugin of this.plugins.values()) {
56
- if (plugin.onContextSaved) {
57
- plugin.onContextSaved(context);
58
- }
59
- }
60
- }
61
- }
62
- const pluginManager = new PluginManager();
63
- export {
64
- PluginManager,
65
- pluginManager
66
- };
67
- //# sourceMappingURL=plugin-interface.js.map