@stackmemoryai/stackmemory 0.3.7 → 0.3.9

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 (202) hide show
  1. package/dist/agents/core/agent-task-manager.js +5 -5
  2. package/dist/agents/core/agent-task-manager.js.map +2 -2
  3. package/dist/agents/verifiers/base-verifier.js +2 -2
  4. package/dist/agents/verifiers/base-verifier.js.map +2 -2
  5. package/dist/cli/claude-sm.js +0 -11
  6. package/dist/cli/claude-sm.js.map +2 -2
  7. package/dist/cli/codex-sm.js +0 -11
  8. package/dist/cli/codex-sm.js.map +2 -2
  9. package/dist/cli/commands/chromadb.js +64 -34
  10. package/dist/cli/commands/chromadb.js.map +2 -2
  11. package/dist/cli/commands/clear.js +9 -13
  12. package/dist/cli/commands/clear.js.map +2 -2
  13. package/dist/cli/commands/config.js +43 -33
  14. package/dist/cli/commands/config.js.map +2 -2
  15. package/dist/cli/commands/context.js.map +2 -2
  16. package/dist/cli/commands/dashboard.js +41 -13
  17. package/dist/cli/commands/dashboard.js.map +2 -2
  18. package/dist/cli/commands/gc.js +69 -20
  19. package/dist/cli/commands/gc.js.map +2 -2
  20. package/dist/cli/commands/handoff.js.map +2 -2
  21. package/dist/cli/commands/infinite-storage.js +60 -19
  22. package/dist/cli/commands/infinite-storage.js.map +2 -2
  23. package/dist/cli/commands/linear-create.js +36 -8
  24. package/dist/cli/commands/linear-create.js.map +2 -2
  25. package/dist/cli/commands/linear-list.js +33 -10
  26. package/dist/cli/commands/linear-list.js.map +2 -2
  27. package/dist/cli/commands/linear-migrate.js +17 -4
  28. package/dist/cli/commands/linear-migrate.js.map +2 -2
  29. package/dist/cli/commands/linear-test.js +14 -6
  30. package/dist/cli/commands/linear-test.js.map +2 -2
  31. package/dist/cli/commands/linear-unified.js +123 -35
  32. package/dist/cli/commands/linear-unified.js.map +2 -2
  33. package/dist/cli/commands/linear.js.map +2 -2
  34. package/dist/cli/commands/monitor.js.map +2 -2
  35. package/dist/cli/commands/onboard.js +35 -8
  36. package/dist/cli/commands/onboard.js.map +2 -2
  37. package/dist/cli/commands/quality.js +2 -7
  38. package/dist/cli/commands/quality.js.map +2 -2
  39. package/dist/cli/commands/session.js +23 -6
  40. package/dist/cli/commands/session.js.map +2 -2
  41. package/dist/cli/commands/skills.js +72 -27
  42. package/dist/cli/commands/skills.js.map +2 -2
  43. package/dist/cli/commands/storage.js +108 -38
  44. package/dist/cli/commands/storage.js.map +2 -2
  45. package/dist/cli/commands/tui.js.map +2 -2
  46. package/dist/cli/commands/webhook.js +57 -18
  47. package/dist/cli/commands/webhook.js.map +2 -2
  48. package/dist/cli/commands/workflow.js +8 -15
  49. package/dist/cli/commands/workflow.js.map +2 -2
  50. package/dist/cli/commands/worktree.js +34 -13
  51. package/dist/cli/commands/worktree.js.map +2 -2
  52. package/dist/cli/index.js +0 -11
  53. package/dist/cli/index.js.map +2 -2
  54. package/dist/core/config/types.js.map +1 -1
  55. package/dist/core/context/auto-context.js +10 -6
  56. package/dist/core/context/auto-context.js.map +2 -2
  57. package/dist/core/context/context-bridge.js.map +2 -2
  58. package/dist/core/context/frame-database.js +13 -3
  59. package/dist/core/context/frame-database.js.map +2 -2
  60. package/dist/core/context/frame-digest.js +7 -5
  61. package/dist/core/context/frame-digest.js.map +2 -2
  62. package/dist/core/context/frame-manager.js.map +2 -2
  63. package/dist/core/context/frame-stack.js +16 -5
  64. package/dist/core/context/frame-stack.js.map +2 -2
  65. package/dist/core/context/incremental-gc.js +10 -3
  66. package/dist/core/context/incremental-gc.js.map +2 -2
  67. package/dist/core/context/index.js.map +1 -1
  68. package/dist/core/context/permission-manager.js.map +2 -2
  69. package/dist/core/context/recursive-context-manager.js +582 -0
  70. package/dist/core/context/recursive-context-manager.js.map +7 -0
  71. package/dist/core/context/refactored-frame-manager.js +12 -3
  72. package/dist/core/context/refactored-frame-manager.js.map +2 -2
  73. package/dist/core/context/shared-context-layer.js +4 -2
  74. package/dist/core/context/shared-context-layer.js.map +2 -2
  75. package/dist/core/database/batch-operations.js +112 -86
  76. package/dist/core/database/batch-operations.js.map +2 -2
  77. package/dist/core/database/query-cache.js +19 -9
  78. package/dist/core/database/query-cache.js.map +2 -2
  79. package/dist/core/database/sqlite-adapter.js +1 -1
  80. package/dist/core/database/sqlite-adapter.js.map +2 -2
  81. package/dist/core/digest/enhanced-hybrid-digest.js +8 -2
  82. package/dist/core/digest/enhanced-hybrid-digest.js.map +2 -2
  83. package/dist/core/errors/recovery.js +9 -2
  84. package/dist/core/errors/recovery.js.map +2 -2
  85. package/dist/core/execution/parallel-executor.js +254 -0
  86. package/dist/core/execution/parallel-executor.js.map +7 -0
  87. package/dist/core/frame/workflow-templates-stub.js.map +1 -1
  88. package/dist/core/frame/workflow-templates.js +40 -1
  89. package/dist/core/frame/workflow-templates.js.map +2 -2
  90. package/dist/core/monitoring/logger.js +6 -1
  91. package/dist/core/monitoring/logger.js.map +2 -2
  92. package/dist/core/monitoring/metrics.js.map +2 -2
  93. package/dist/core/monitoring/progress-tracker.js.map +2 -2
  94. package/dist/core/performance/context-cache.js.map +2 -2
  95. package/dist/core/performance/lazy-context-loader.js +24 -20
  96. package/dist/core/performance/lazy-context-loader.js.map +2 -2
  97. package/dist/core/performance/optimized-frame-context.js +27 -12
  98. package/dist/core/performance/optimized-frame-context.js.map +2 -2
  99. package/dist/core/performance/performance-benchmark.js +10 -6
  100. package/dist/core/performance/performance-benchmark.js.map +2 -2
  101. package/dist/core/performance/performance-profiler.js +51 -14
  102. package/dist/core/performance/performance-profiler.js.map +2 -2
  103. package/dist/core/performance/streaming-jsonl-parser.js +5 -1
  104. package/dist/core/performance/streaming-jsonl-parser.js.map +2 -2
  105. package/dist/core/projects/project-manager.js +14 -20
  106. package/dist/core/projects/project-manager.js.map +2 -2
  107. package/dist/core/retrieval/context-retriever.js.map +1 -1
  108. package/dist/core/retrieval/llm-context-retrieval.js.map +2 -2
  109. package/dist/core/session/clear-survival-stub.js +5 -1
  110. package/dist/core/session/clear-survival-stub.js.map +2 -2
  111. package/dist/core/session/clear-survival.js +35 -0
  112. package/dist/core/session/clear-survival.js.map +2 -2
  113. package/dist/core/session/index.js.map +1 -1
  114. package/dist/core/session/session-manager.js.map +2 -2
  115. package/dist/core/storage/chromadb-adapter.js +6 -2
  116. package/dist/core/storage/chromadb-adapter.js.map +2 -2
  117. package/dist/core/storage/chromadb-simple.js +17 -5
  118. package/dist/core/storage/chromadb-simple.js.map +2 -2
  119. package/dist/core/storage/infinite-storage.js +109 -46
  120. package/dist/core/storage/infinite-storage.js.map +2 -2
  121. package/dist/core/storage/railway-optimized-storage.js +48 -22
  122. package/dist/core/storage/railway-optimized-storage.js.map +2 -2
  123. package/dist/core/storage/remote-storage.js +41 -23
  124. package/dist/core/storage/remote-storage.js.map +2 -2
  125. package/dist/core/trace/cli-trace-wrapper.js +9 -2
  126. package/dist/core/trace/cli-trace-wrapper.js.map +2 -2
  127. package/dist/core/trace/db-trace-wrapper.js +96 -68
  128. package/dist/core/trace/db-trace-wrapper.js.map +2 -2
  129. package/dist/core/trace/debug-trace.js +25 -8
  130. package/dist/core/trace/debug-trace.js.map +2 -2
  131. package/dist/core/trace/index.js +6 -2
  132. package/dist/core/trace/index.js.map +2 -2
  133. package/dist/core/trace/linear-api-wrapper.js +10 -5
  134. package/dist/core/trace/linear-api-wrapper.js.map +2 -2
  135. package/dist/core/trace/trace-demo.js +14 -10
  136. package/dist/core/trace/trace-demo.js.map +2 -2
  137. package/dist/core/trace/trace-detector.js +9 -2
  138. package/dist/core/trace/trace-detector.js.map +2 -2
  139. package/dist/core/trace/types.js.map +1 -1
  140. package/dist/core/utils/compression.js.map +1 -1
  141. package/dist/core/utils/update-checker.js.map +1 -1
  142. package/dist/core/worktree/worktree-manager.js +18 -7
  143. package/dist/core/worktree/worktree-manager.js.map +2 -2
  144. package/dist/features/analytics/core/analytics-service.js.map +2 -2
  145. package/dist/features/analytics/queries/metrics-queries.js +1 -1
  146. package/dist/features/analytics/queries/metrics-queries.js.map +2 -2
  147. package/dist/features/tasks/pebbles-task-store.js.map +1 -1
  148. package/dist/features/tui/components/analytics-panel.js +36 -15
  149. package/dist/features/tui/components/analytics-panel.js.map +2 -2
  150. package/dist/features/tui/components/pr-tracker.js +19 -7
  151. package/dist/features/tui/components/pr-tracker.js.map +2 -2
  152. package/dist/features/tui/components/session-monitor.js +22 -9
  153. package/dist/features/tui/components/session-monitor.js.map +2 -2
  154. package/dist/features/tui/components/subagent-fleet.js +20 -13
  155. package/dist/features/tui/components/subagent-fleet.js.map +2 -2
  156. package/dist/features/tui/components/task-board.js +26 -10
  157. package/dist/features/tui/components/task-board.js.map +2 -2
  158. package/dist/features/tui/index.js.map +2 -2
  159. package/dist/features/tui/services/data-service.js +6 -2
  160. package/dist/features/tui/services/data-service.js.map +2 -2
  161. package/dist/features/tui/services/linear-task-reader.js +3 -1
  162. package/dist/features/tui/services/linear-task-reader.js.map +2 -2
  163. package/dist/features/tui/services/websocket-client.js +3 -1
  164. package/dist/features/tui/services/websocket-client.js.map +2 -2
  165. package/dist/features/tui/terminal-compat.js +6 -2
  166. package/dist/features/tui/terminal-compat.js.map +2 -2
  167. package/dist/features/web/client/stores/task-store.js.map +2 -2
  168. package/dist/features/web/server/index.js +18 -10
  169. package/dist/features/web/server/index.js.map +2 -2
  170. package/dist/integrations/anthropic/client.js +259 -0
  171. package/dist/integrations/anthropic/client.js.map +7 -0
  172. package/dist/integrations/claude-code/subagent-client.js +404 -0
  173. package/dist/integrations/claude-code/subagent-client.js.map +7 -0
  174. package/dist/integrations/linear/sync-service.js +12 -13
  175. package/dist/integrations/linear/sync-service.js.map +2 -2
  176. package/dist/integrations/linear/sync.js +174 -12
  177. package/dist/integrations/linear/sync.js.map +2 -2
  178. package/dist/integrations/linear/unified-sync.js +1 -1
  179. package/dist/integrations/linear/unified-sync.js.map +1 -1
  180. package/dist/integrations/linear/webhook-server.js +15 -16
  181. package/dist/integrations/linear/webhook-server.js.map +2 -2
  182. package/dist/mcp/stackmemory-mcp-server.js +0 -11
  183. package/dist/mcp/stackmemory-mcp-server.js.map +2 -2
  184. package/dist/servers/production/auth-middleware.js.map +2 -2
  185. package/dist/servers/railway/index.js.map +2 -2
  186. package/dist/services/config-service.js +6 -7
  187. package/dist/services/config-service.js.map +2 -2
  188. package/dist/services/context-service.js +11 -12
  189. package/dist/services/context-service.js.map +2 -2
  190. package/dist/skills/claude-skills.js +101 -2
  191. package/dist/skills/claude-skills.js.map +2 -2
  192. package/dist/skills/dashboard-launcher.js.map +2 -2
  193. package/dist/skills/recursive-agent-orchestrator.js +559 -0
  194. package/dist/skills/recursive-agent-orchestrator.js.map +7 -0
  195. package/dist/skills/repo-ingestion-skill.js.map +2 -2
  196. package/dist/skills/security-secrets-scanner.js +265 -0
  197. package/dist/skills/security-secrets-scanner.js.map +7 -0
  198. package/dist/utils/env.js +46 -0
  199. package/dist/utils/env.js.map +7 -0
  200. package/dist/utils/logger.js +0 -11
  201. package/dist/utils/logger.js.map +2 -2
  202. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/core/worktree/worktree-manager.ts"],
4
- "sourcesContent": ["/**\n * Git Worktree Manager for StackMemory\n * Manages multiple instances across git worktrees with context isolation\n */\n\nimport { execSync } from 'child_process';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\nimport { join, basename, dirname, resolve } from 'path';\nimport { homedir } from 'os';\nimport Database from 'better-sqlite3';\nimport { logger } from '../monitoring/logger.js';\nimport { ProjectManager } from '../projects/project-manager.js';\nimport {\n DatabaseError,\n SystemError,\n ErrorCode,\n createErrorHandler,\n} from '../errors/index.js';\nimport { retry } from '../errors/recovery.js';\n\nexport interface WorktreeInfo {\n path: string;\n branch: string;\n commit: string;\n isMainWorktree: boolean;\n isBare: boolean;\n isDetached: boolean;\n linkedPath?: string; // Path to main worktree\n contextId: string; // Unique context identifier\n}\n\nexport interface WorktreeConfig {\n enabled: boolean;\n autoDetect: boolean;\n isolateContexts: boolean;\n shareGlobalContext: boolean;\n syncInterval?: number; // Minutes between syncs\n maxWorktrees?: number;\n}\n\nexport interface WorktreeContext {\n worktreeId: string;\n projectId: string;\n branch: string;\n contextPath: string;\n dbPath: string;\n lastSynced: Date;\n metadata: Record<string, any>;\n}\n\nexport class WorktreeManager {\n private static instance: WorktreeManager;\n private config: WorktreeConfig;\n private configPath: string;\n private worktreeCache: Map<string, WorktreeInfo> = new Map();\n private contextMap: Map<string, WorktreeContext> = new Map();\n private db?: Database.Database;\n\n private constructor() {\n this.configPath = join(homedir(), '.stackmemory', 'worktree-config.json');\n this.config = this.loadConfig();\n \n try {\n if (this.config.enabled) {\n this.initialize();\n }\n } catch (error: unknown) {\n logger.error(\n 'Failed to initialize WorktreeManager',\n error instanceof Error ? error : new Error(String(error)),\n {\n configPath: this.configPath,\n }\n );\n // Don't throw here - allow the manager to be created without worktree support\n this.config.enabled = false;\n }\n }\n\n static getInstance(): WorktreeManager {\n if (!WorktreeManager.instance) {\n WorktreeManager.instance = new WorktreeManager();\n }\n return WorktreeManager.instance;\n }\n\n /**\n * Initialize worktree management\n */\n private initialize(): void {\n const dbPath = join(homedir(), '.stackmemory', 'worktrees.db');\n const errorHandler = createErrorHandler({\n operation: 'initialize',\n dbPath,\n });\n\n try {\n this.db = new Database(dbPath);\n\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS worktrees (\n id TEXT PRIMARY KEY,\n path TEXT NOT NULL UNIQUE,\n branch TEXT NOT NULL,\n commit TEXT,\n is_main BOOLEAN,\n is_bare BOOLEAN,\n is_detached BOOLEAN,\n linked_path TEXT,\n context_id TEXT UNIQUE,\n project_id TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n );\n\n CREATE TABLE IF NOT EXISTS worktree_contexts (\n context_id TEXT PRIMARY KEY,\n worktree_id TEXT NOT NULL,\n project_id TEXT,\n branch TEXT,\n context_path TEXT,\n db_path TEXT,\n last_synced DATETIME,\n metadata JSON,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (worktree_id) REFERENCES worktrees(id)\n );\n\n CREATE TABLE IF NOT EXISTS context_sync (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n source_context TEXT,\n target_context TEXT,\n sync_type TEXT, -- 'push', 'pull', 'merge'\n data JSON,\n synced_at DATETIME DEFAULT CURRENT_TIMESTAMP\n );\n\n CREATE INDEX IF NOT EXISTS idx_worktrees_project ON worktrees(project_id);\n CREATE INDEX IF NOT EXISTS idx_contexts_worktree ON worktree_contexts(worktree_id);\n `);\n } catch (error: unknown) {\n errorHandler(error);\n }\n }\n\n /**\n * Load configuration\n */\n private loadConfig(): WorktreeConfig {\n if (existsSync(this.configPath)) {\n try {\n return JSON.parse(readFileSync(this.configPath, 'utf-8'));\n } catch (error: unknown) {\n logger.error('Failed to load worktree config', error as Error);\n }\n }\n\n // Default config\n return {\n enabled: false,\n autoDetect: true,\n isolateContexts: true,\n shareGlobalContext: false,\n syncInterval: 15,\n maxWorktrees: 10,\n };\n }\n\n /**\n * Save configuration\n */\n saveConfig(config: Partial<WorktreeConfig>): void {\n this.config = { ...this.config, ...config };\n \n const configDir = dirname(this.configPath);\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n \n writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));\n \n // Reinitialize if just enabled\n if (config.enabled && !this.db) {\n this.initialize();\n }\n \n logger.info('Worktree configuration updated', { config: this.config });\n }\n\n /**\n * Detect git worktrees in current repository\n */\n detectWorktrees(repoPath?: string): WorktreeInfo[] {\n const path = repoPath || process.cwd();\n \n try {\n // Get worktree list\n const output = execSync('git worktree list --porcelain', {\n cwd: path,\n encoding: 'utf-8',\n });\n\n const worktrees: WorktreeInfo[] = [];\n const lines = output.split('\\n');\n let currentWorktree: Partial<WorktreeInfo> = {};\n\n for (const line of lines) {\n if (line.startsWith('worktree ')) {\n if (currentWorktree.path) {\n worktrees.push(this.finalizeWorktreeInfo(currentWorktree));\n }\n currentWorktree = { path: line.substring(9) };\n } else if (line.startsWith('HEAD ')) {\n currentWorktree.commit = line.substring(5);\n } else if (line.startsWith('branch ')) {\n currentWorktree.branch = line.substring(7);\n } else if (line === 'bare') {\n currentWorktree.isBare = true;\n } else if (line === 'detached') {\n currentWorktree.isDetached = true;\n }\n }\n\n // Add last worktree\n if (currentWorktree.path) {\n worktrees.push(this.finalizeWorktreeInfo(currentWorktree));\n }\n\n // Determine main worktree\n if (worktrees.length > 0) {\n const mainPath = this.getMainWorktreePath(path);\n worktrees.forEach(wt => {\n wt.isMainWorktree = wt.path === mainPath;\n if (!wt.isMainWorktree) {\n wt.linkedPath = mainPath;\n }\n });\n }\n\n // Cache results\n worktrees.forEach(wt => {\n this.worktreeCache.set(wt.path, wt);\n if (this.config.enabled) {\n this.saveWorktree(wt);\n }\n });\n\n logger.info(`Detected ${worktrees.length} worktrees`, { \n count: worktrees.length,\n branches: worktrees.map((w: any) => w.branch).filter(Boolean)\n });\n\n return worktrees;\n } catch (error: unknown) {\n logger.debug('Not a git repository or git worktree not available');\n return [];\n }\n }\n\n /**\n * Get main worktree path\n */\n private getMainWorktreePath(path: string): string {\n try {\n const gitDir = execSync('git rev-parse --git-common-dir', {\n cwd: path,\n encoding: 'utf-8',\n }).trim();\n\n // If it's a worktree, find the main repo\n if (gitDir.includes('/.git/worktrees/')) {\n const mainGitDir = gitDir.replace(/\\/\\.git\\/worktrees\\/.*$/, '');\n return mainGitDir;\n }\n\n // It's the main worktree\n return dirname(gitDir);\n } catch {\n return path;\n }\n }\n\n /**\n * Finalize worktree info with defaults\n */\n private finalizeWorktreeInfo(partial: Partial<WorktreeInfo>): WorktreeInfo {\n return {\n path: partial.path || '',\n branch: partial.branch || 'detached',\n commit: partial.commit || '',\n isMainWorktree: partial.isMainWorktree || false,\n isBare: partial.isBare || false,\n isDetached: partial.isDetached || false,\n linkedPath: partial.linkedPath,\n contextId: this.generateContextId(partial.path || '', partial.branch || ''),\n };\n }\n\n /**\n * Generate unique context ID for worktree\n */\n private generateContextId(path: string, branch: string): string {\n const repoName = basename(dirname(path));\n const sanitizedBranch = branch.replace(/[^a-zA-Z0-9-]/g, '_');\n return `${repoName}-${sanitizedBranch}-${Buffer.from(path).toString('base64').substring(0, 8)}`;\n }\n\n /**\n * Save worktree to database\n */\n private async saveWorktree(worktree: WorktreeInfo): Promise<void> {\n if (!this.db) return;\n\n const projectManager = ProjectManager.getInstance();\n const project = await projectManager.detectProject(worktree.path);\n\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO worktrees \n (id, path, branch, commit, is_main, is_bare, is_detached, linked_path, context_id, project_id, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)\n `);\n\n stmt.run(\n worktree.contextId,\n worktree.path,\n worktree.branch,\n worktree.commit,\n worktree.isMainWorktree ? 1 : 0,\n worktree.isBare ? 1 : 0,\n worktree.isDetached ? 1 : 0,\n worktree.linkedPath,\n worktree.contextId,\n project.id\n );\n }\n\n /**\n * Get or create context for worktree\n */\n getWorktreeContext(worktreePath: string): WorktreeContext {\n // Check cache\n const cached = this.contextMap.get(worktreePath);\n if (cached) {\n return cached;\n }\n\n const worktree = this.worktreeCache.get(worktreePath) || \n this.detectWorktrees(worktreePath).find((w: any) => w.path === worktreePath);\n\n if (!worktree) {\n throw new Error(`No worktree found at path: ${worktreePath}`);\n }\n\n // Create isolated context path\n const contextBasePath = this.config.isolateContexts\n ? join(homedir(), '.stackmemory', 'worktrees', worktree.contextId)\n : join(worktreePath, '.stackmemory');\n\n // Ensure directory exists\n if (!existsSync(contextBasePath)) {\n mkdirSync(contextBasePath, { recursive: true });\n }\n\n const context: WorktreeContext = {\n worktreeId: worktree.contextId,\n projectId: '', // Will be filled by project manager\n branch: worktree.branch,\n contextPath: contextBasePath,\n dbPath: join(contextBasePath, 'context.db'),\n lastSynced: new Date(),\n metadata: {\n isMainWorktree: worktree.isMainWorktree,\n linkedPath: worktree.linkedPath,\n },\n };\n\n // Save to database\n if (this.db && this.config.enabled) {\n this.saveContext(context);\n }\n\n // Cache it\n this.contextMap.set(worktreePath, context);\n\n logger.info('Created worktree context', {\n worktree: worktree.branch,\n path: contextBasePath,\n });\n\n return context;\n }\n\n /**\n * Save context to database\n */\n private saveContext(context: WorktreeContext): void {\n if (!this.db) return;\n\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO worktree_contexts\n (context_id, worktree_id, project_id, branch, context_path, db_path, last_synced, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n stmt.run(\n context.worktreeId,\n context.worktreeId,\n context.projectId,\n context.branch,\n context.contextPath,\n context.dbPath,\n context.lastSynced.toISOString(),\n JSON.stringify(context.metadata)\n );\n }\n\n /**\n * Sync contexts between worktrees\n */\n async syncContexts(sourceWorktree: string, targetWorktree: string, syncType: 'push' | 'pull' | 'merge' = 'merge'): Promise<void> {\n const source = this.getWorktreeContext(sourceWorktree);\n const target = this.getWorktreeContext(targetWorktree);\n\n logger.info('Syncing contexts between worktrees', {\n source: source.branch,\n target: target.branch,\n type: syncType,\n });\n\n // Open both databases\n const sourceDb = new Database(source.dbPath);\n const targetDb = new Database(target.dbPath);\n\n try {\n // Get contexts from source\n const contexts = sourceDb.prepare(`\n SELECT * FROM contexts \n WHERE created_at > datetime('now', '-7 days')\n ORDER BY created_at DESC\n `).all();\n\n // Sync based on type\n if (syncType === 'push' || syncType === 'merge') {\n this.mergeContexts(contexts, targetDb, syncType === 'merge');\n }\n\n if (syncType === 'pull') {\n const targetContexts = targetDb.prepare(`\n SELECT * FROM contexts \n WHERE created_at > datetime('now', '-7 days')\n ORDER BY created_at DESC\n `).all();\n \n this.mergeContexts(targetContexts, sourceDb, false);\n }\n\n // Log sync operation\n if (this.db) {\n const stmt = this.db.prepare(`\n INSERT INTO context_sync (source_context, target_context, sync_type, data)\n VALUES (?, ?, ?, ?)\n `);\n\n stmt.run(\n source.worktreeId,\n target.worktreeId,\n syncType,\n JSON.stringify({ count: contexts.length })\n );\n }\n\n logger.info('Context sync completed', {\n synced: contexts.length,\n type: syncType,\n });\n } finally {\n sourceDb.close();\n targetDb.close();\n }\n }\n\n /**\n * Merge contexts into target database\n */\n private mergeContexts(contexts: any[], targetDb: Database.Database, bidirectional: boolean): void {\n const stmt = targetDb.prepare(`\n INSERT OR REPLACE INTO contexts (id, type, content, metadata, created_at)\n VALUES (?, ?, ?, ?, ?)\n `);\n\n for (const ctx of contexts) {\n try {\n stmt.run(ctx.id, ctx.type, ctx.content, ctx.metadata, ctx.created_at);\n } catch (error: unknown) {\n logger.warn('Failed to merge context', { id: ctx.id, error });\n }\n }\n }\n\n /**\n * List all active worktrees\n */\n listActiveWorktrees(): WorktreeInfo[] {\n if (!this.db) {\n return Array.from(this.worktreeCache.values());\n }\n\n const stmt = this.db.prepare(`\n SELECT * FROM worktrees\n ORDER BY is_main DESC, branch ASC\n `);\n\n const rows = stmt.all() as any[];\n \n return rows.map((row: any) => ({\n path: row.path,\n branch: row.branch,\n commit: row.commit,\n isMainWorktree: row.is_main === 1,\n isBare: row.is_bare === 1,\n isDetached: row.is_detached === 1,\n linkedPath: row.linked_path,\n contextId: row.context_id,\n }));\n }\n\n /**\n * Clean up stale worktree contexts\n */\n cleanupStaleContexts(): void {\n if (!this.db) return;\n\n const activeWorktrees = this.detectWorktrees();\n const activePaths = new Set(activeWorktrees.map((w: any) => w.path));\n\n // Get all stored worktrees\n const stmt = this.db.prepare('SELECT * FROM worktrees');\n const stored = stmt.all() as any[];\n\n // Remove stale entries\n const deleteStmt = this.db.prepare('DELETE FROM worktrees WHERE id = ?');\n const deleteContextStmt = this.db.prepare('DELETE FROM worktree_contexts WHERE worktree_id = ?');\n\n for (const worktree of stored) {\n if (!activePaths.has(worktree.path)) {\n deleteStmt.run(worktree.id);\n deleteContextStmt.run(worktree.id);\n \n logger.info('Cleaned up stale worktree context', {\n path: worktree.path,\n branch: worktree.branch,\n });\n }\n }\n }\n\n /**\n * Get configuration\n */\n getConfig(): WorktreeConfig {\n return { ...this.config };\n }\n\n /**\n * Check if worktree support is enabled\n */\n isEnabled(): boolean {\n return this.config.enabled;\n }\n\n /**\n * Enable or disable worktree support\n */\n setEnabled(enabled: boolean): void {\n this.saveConfig({ enabled });\n }\n}"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Git Worktree Manager for StackMemory\n * Manages multiple instances across git worktrees with context isolation\n */\n\nimport { execSync } from 'child_process';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\nimport { join, basename, dirname, resolve } from 'path';\nimport { homedir } from 'os';\nimport Database from 'better-sqlite3';\nimport { logger } from '../monitoring/logger.js';\nimport { ProjectManager } from '../projects/project-manager.js';\nimport {\n DatabaseError,\n SystemError,\n ErrorCode,\n createErrorHandler,\n} from '../errors/index.js';\nimport { retry } from '../errors/recovery.js';\n\nexport interface WorktreeInfo {\n path: string;\n branch: string;\n commit: string;\n isMainWorktree: boolean;\n isBare: boolean;\n isDetached: boolean;\n linkedPath?: string; // Path to main worktree\n contextId: string; // Unique context identifier\n}\n\nexport interface WorktreeConfig {\n enabled: boolean;\n autoDetect: boolean;\n isolateContexts: boolean;\n shareGlobalContext: boolean;\n syncInterval?: number; // Minutes between syncs\n maxWorktrees?: number;\n}\n\nexport interface WorktreeContext {\n worktreeId: string;\n projectId: string;\n branch: string;\n contextPath: string;\n dbPath: string;\n lastSynced: Date;\n metadata: Record<string, any>;\n}\n\nexport class WorktreeManager {\n private static instance: WorktreeManager;\n private config: WorktreeConfig;\n private configPath: string;\n private worktreeCache: Map<string, WorktreeInfo> = new Map();\n private contextMap: Map<string, WorktreeContext> = new Map();\n private db?: Database.Database;\n\n private constructor() {\n this.configPath = join(homedir(), '.stackmemory', 'worktree-config.json');\n this.config = this.loadConfig();\n\n try {\n if (this.config.enabled) {\n this.initialize();\n }\n } catch (error: unknown) {\n logger.error(\n 'Failed to initialize WorktreeManager',\n error instanceof Error ? error : new Error(String(error)),\n {\n configPath: this.configPath,\n }\n );\n // Don't throw here - allow the manager to be created without worktree support\n this.config.enabled = false;\n }\n }\n\n static getInstance(): WorktreeManager {\n if (!WorktreeManager.instance) {\n WorktreeManager.instance = new WorktreeManager();\n }\n return WorktreeManager.instance;\n }\n\n /**\n * Initialize worktree management\n */\n private initialize(): void {\n const dbPath = join(homedir(), '.stackmemory', 'worktrees.db');\n const errorHandler = createErrorHandler({\n operation: 'initialize',\n dbPath,\n });\n\n try {\n this.db = new Database(dbPath);\n\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS worktrees (\n id TEXT PRIMARY KEY,\n path TEXT NOT NULL UNIQUE,\n branch TEXT NOT NULL,\n commit TEXT,\n is_main BOOLEAN,\n is_bare BOOLEAN,\n is_detached BOOLEAN,\n linked_path TEXT,\n context_id TEXT UNIQUE,\n project_id TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n );\n\n CREATE TABLE IF NOT EXISTS worktree_contexts (\n context_id TEXT PRIMARY KEY,\n worktree_id TEXT NOT NULL,\n project_id TEXT,\n branch TEXT,\n context_path TEXT,\n db_path TEXT,\n last_synced DATETIME,\n metadata JSON,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (worktree_id) REFERENCES worktrees(id)\n );\n\n CREATE TABLE IF NOT EXISTS context_sync (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n source_context TEXT,\n target_context TEXT,\n sync_type TEXT, -- 'push', 'pull', 'merge'\n data JSON,\n synced_at DATETIME DEFAULT CURRENT_TIMESTAMP\n );\n\n CREATE INDEX IF NOT EXISTS idx_worktrees_project ON worktrees(project_id);\n CREATE INDEX IF NOT EXISTS idx_contexts_worktree ON worktree_contexts(worktree_id);\n `);\n } catch (error: unknown) {\n errorHandler(error);\n }\n }\n\n /**\n * Load configuration\n */\n private loadConfig(): WorktreeConfig {\n if (existsSync(this.configPath)) {\n try {\n return JSON.parse(readFileSync(this.configPath, 'utf-8'));\n } catch (error: unknown) {\n logger.error('Failed to load worktree config', error as Error);\n }\n }\n\n // Default config\n return {\n enabled: false,\n autoDetect: true,\n isolateContexts: true,\n shareGlobalContext: false,\n syncInterval: 15,\n maxWorktrees: 10,\n };\n }\n\n /**\n * Save configuration\n */\n saveConfig(config: Partial<WorktreeConfig>): void {\n this.config = { ...this.config, ...config };\n\n const configDir = dirname(this.configPath);\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n\n writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));\n\n // Reinitialize if just enabled\n if (config.enabled && !this.db) {\n this.initialize();\n }\n\n logger.info('Worktree configuration updated', { config: this.config });\n }\n\n /**\n * Detect git worktrees in current repository\n */\n detectWorktrees(repoPath?: string): WorktreeInfo[] {\n const path = repoPath || process.cwd();\n\n try {\n // Get worktree list\n const output = execSync('git worktree list --porcelain', {\n cwd: path,\n encoding: 'utf-8',\n });\n\n const worktrees: WorktreeInfo[] = [];\n const lines = output.split('\\n');\n let currentWorktree: Partial<WorktreeInfo> = {};\n\n for (const line of lines) {\n if (line.startsWith('worktree ')) {\n if (currentWorktree.path) {\n worktrees.push(this.finalizeWorktreeInfo(currentWorktree));\n }\n currentWorktree = { path: line.substring(9) };\n } else if (line.startsWith('HEAD ')) {\n currentWorktree.commit = line.substring(5);\n } else if (line.startsWith('branch ')) {\n currentWorktree.branch = line.substring(7);\n } else if (line === 'bare') {\n currentWorktree.isBare = true;\n } else if (line === 'detached') {\n currentWorktree.isDetached = true;\n }\n }\n\n // Add last worktree\n if (currentWorktree.path) {\n worktrees.push(this.finalizeWorktreeInfo(currentWorktree));\n }\n\n // Determine main worktree\n if (worktrees.length > 0) {\n const mainPath = this.getMainWorktreePath(path);\n worktrees.forEach((wt) => {\n wt.isMainWorktree = wt.path === mainPath;\n if (!wt.isMainWorktree) {\n wt.linkedPath = mainPath;\n }\n });\n }\n\n // Cache results\n worktrees.forEach((wt) => {\n this.worktreeCache.set(wt.path, wt);\n if (this.config.enabled) {\n this.saveWorktree(wt);\n }\n });\n\n logger.info(`Detected ${worktrees.length} worktrees`, {\n count: worktrees.length,\n branches: worktrees.map((w: any) => w.branch).filter(Boolean),\n });\n\n return worktrees;\n } catch (error: unknown) {\n logger.debug('Not a git repository or git worktree not available');\n return [];\n }\n }\n\n /**\n * Get main worktree path\n */\n private getMainWorktreePath(path: string): string {\n try {\n const gitDir = execSync('git rev-parse --git-common-dir', {\n cwd: path,\n encoding: 'utf-8',\n }).trim();\n\n // If it's a worktree, find the main repo\n if (gitDir.includes('/.git/worktrees/')) {\n const mainGitDir = gitDir.replace(/\\/\\.git\\/worktrees\\/.*$/, '');\n return mainGitDir;\n }\n\n // It's the main worktree\n return dirname(gitDir);\n } catch {\n return path;\n }\n }\n\n /**\n * Finalize worktree info with defaults\n */\n private finalizeWorktreeInfo(partial: Partial<WorktreeInfo>): WorktreeInfo {\n return {\n path: partial.path || '',\n branch: partial.branch || 'detached',\n commit: partial.commit || '',\n isMainWorktree: partial.isMainWorktree || false,\n isBare: partial.isBare || false,\n isDetached: partial.isDetached || false,\n linkedPath: partial.linkedPath,\n contextId: this.generateContextId(\n partial.path || '',\n partial.branch || ''\n ),\n };\n }\n\n /**\n * Generate unique context ID for worktree\n */\n private generateContextId(path: string, branch: string): string {\n const repoName = basename(dirname(path));\n const sanitizedBranch = branch.replace(/[^a-zA-Z0-9-]/g, '_');\n return `${repoName}-${sanitizedBranch}-${Buffer.from(path).toString('base64').substring(0, 8)}`;\n }\n\n /**\n * Save worktree to database\n */\n private async saveWorktree(worktree: WorktreeInfo): Promise<void> {\n if (!this.db) return;\n\n const projectManager = ProjectManager.getInstance();\n const project = await projectManager.detectProject(worktree.path);\n\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO worktrees \n (id, path, branch, commit, is_main, is_bare, is_detached, linked_path, context_id, project_id, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)\n `);\n\n stmt.run(\n worktree.contextId,\n worktree.path,\n worktree.branch,\n worktree.commit,\n worktree.isMainWorktree ? 1 : 0,\n worktree.isBare ? 1 : 0,\n worktree.isDetached ? 1 : 0,\n worktree.linkedPath,\n worktree.contextId,\n project.id\n );\n }\n\n /**\n * Get or create context for worktree\n */\n getWorktreeContext(worktreePath: string): WorktreeContext {\n // Check cache\n const cached = this.contextMap.get(worktreePath);\n if (cached) {\n return cached;\n }\n\n const worktree =\n this.worktreeCache.get(worktreePath) ||\n this.detectWorktrees(worktreePath).find(\n (w: any) => w.path === worktreePath\n );\n\n if (!worktree) {\n throw new Error(`No worktree found at path: ${worktreePath}`);\n }\n\n // Create isolated context path\n const contextBasePath = this.config.isolateContexts\n ? join(homedir(), '.stackmemory', 'worktrees', worktree.contextId)\n : join(worktreePath, '.stackmemory');\n\n // Ensure directory exists\n if (!existsSync(contextBasePath)) {\n mkdirSync(contextBasePath, { recursive: true });\n }\n\n const context: WorktreeContext = {\n worktreeId: worktree.contextId,\n projectId: '', // Will be filled by project manager\n branch: worktree.branch,\n contextPath: contextBasePath,\n dbPath: join(contextBasePath, 'context.db'),\n lastSynced: new Date(),\n metadata: {\n isMainWorktree: worktree.isMainWorktree,\n linkedPath: worktree.linkedPath,\n },\n };\n\n // Save to database\n if (this.db && this.config.enabled) {\n this.saveContext(context);\n }\n\n // Cache it\n this.contextMap.set(worktreePath, context);\n\n logger.info('Created worktree context', {\n worktree: worktree.branch,\n path: contextBasePath,\n });\n\n return context;\n }\n\n /**\n * Save context to database\n */\n private saveContext(context: WorktreeContext): void {\n if (!this.db) return;\n\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO worktree_contexts\n (context_id, worktree_id, project_id, branch, context_path, db_path, last_synced, metadata)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n stmt.run(\n context.worktreeId,\n context.worktreeId,\n context.projectId,\n context.branch,\n context.contextPath,\n context.dbPath,\n context.lastSynced.toISOString(),\n JSON.stringify(context.metadata)\n );\n }\n\n /**\n * Sync contexts between worktrees\n */\n async syncContexts(\n sourceWorktree: string,\n targetWorktree: string,\n syncType: 'push' | 'pull' | 'merge' = 'merge'\n ): Promise<void> {\n const source = this.getWorktreeContext(sourceWorktree);\n const target = this.getWorktreeContext(targetWorktree);\n\n logger.info('Syncing contexts between worktrees', {\n source: source.branch,\n target: target.branch,\n type: syncType,\n });\n\n // Open both databases\n const sourceDb = new Database(source.dbPath);\n const targetDb = new Database(target.dbPath);\n\n try {\n // Get contexts from source\n const contexts = sourceDb\n .prepare(\n `\n SELECT * FROM contexts \n WHERE created_at > datetime('now', '-7 days')\n ORDER BY created_at DESC\n `\n )\n .all();\n\n // Sync based on type\n if (syncType === 'push' || syncType === 'merge') {\n this.mergeContexts(contexts, targetDb, syncType === 'merge');\n }\n\n if (syncType === 'pull') {\n const targetContexts = targetDb\n .prepare(\n `\n SELECT * FROM contexts \n WHERE created_at > datetime('now', '-7 days')\n ORDER BY created_at DESC\n `\n )\n .all();\n\n this.mergeContexts(targetContexts, sourceDb, false);\n }\n\n // Log sync operation\n if (this.db) {\n const stmt = this.db.prepare(`\n INSERT INTO context_sync (source_context, target_context, sync_type, data)\n VALUES (?, ?, ?, ?)\n `);\n\n stmt.run(\n source.worktreeId,\n target.worktreeId,\n syncType,\n JSON.stringify({ count: contexts.length })\n );\n }\n\n logger.info('Context sync completed', {\n synced: contexts.length,\n type: syncType,\n });\n } finally {\n sourceDb.close();\n targetDb.close();\n }\n }\n\n /**\n * Merge contexts into target database\n */\n private mergeContexts(\n contexts: any[],\n targetDb: Database.Database,\n bidirectional: boolean\n ): void {\n const stmt = targetDb.prepare(`\n INSERT OR REPLACE INTO contexts (id, type, content, metadata, created_at)\n VALUES (?, ?, ?, ?, ?)\n `);\n\n for (const ctx of contexts) {\n try {\n stmt.run(ctx.id, ctx.type, ctx.content, ctx.metadata, ctx.created_at);\n } catch (error: unknown) {\n logger.warn('Failed to merge context', { id: ctx.id, error });\n }\n }\n }\n\n /**\n * List all active worktrees\n */\n listActiveWorktrees(): WorktreeInfo[] {\n if (!this.db) {\n return Array.from(this.worktreeCache.values());\n }\n\n const stmt = this.db.prepare(`\n SELECT * FROM worktrees\n ORDER BY is_main DESC, branch ASC\n `);\n\n const rows = stmt.all() as any[];\n\n return rows.map((row: any) => ({\n path: row.path,\n branch: row.branch,\n commit: row.commit,\n isMainWorktree: row.is_main === 1,\n isBare: row.is_bare === 1,\n isDetached: row.is_detached === 1,\n linkedPath: row.linked_path,\n contextId: row.context_id,\n }));\n }\n\n /**\n * Clean up stale worktree contexts\n */\n cleanupStaleContexts(): void {\n if (!this.db) return;\n\n const activeWorktrees = this.detectWorktrees();\n const activePaths = new Set(activeWorktrees.map((w: any) => w.path));\n\n // Get all stored worktrees\n const stmt = this.db.prepare('SELECT * FROM worktrees');\n const stored = stmt.all() as any[];\n\n // Remove stale entries\n const deleteStmt = this.db.prepare('DELETE FROM worktrees WHERE id = ?');\n const deleteContextStmt = this.db.prepare(\n 'DELETE FROM worktree_contexts WHERE worktree_id = ?'\n );\n\n for (const worktree of stored) {\n if (!activePaths.has(worktree.path)) {\n deleteStmt.run(worktree.id);\n deleteContextStmt.run(worktree.id);\n\n logger.info('Cleaned up stale worktree context', {\n path: worktree.path,\n branch: worktree.branch,\n });\n }\n }\n }\n\n /**\n * Get configuration\n */\n getConfig(): WorktreeConfig {\n return { ...this.config };\n }\n\n /**\n * Check if worktree support is enabled\n */\n isEnabled(): boolean {\n return this.config.enabled;\n }\n\n /**\n * Enable or disable worktree support\n */\n setEnabled(enabled: boolean): void {\n this.saveConfig({ enabled });\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/features/analytics/core/analytics-service.ts"],
4
- "sourcesContent": ["import { MetricsQueries } from '../queries/metrics-queries.js';\nimport { LinearClient } from '../../../integrations/linear/client.js';\nimport { PebblesTaskStore } from '../../tasks/pebbles-task-store.js';\nimport Database from 'better-sqlite3';\nimport {\n TaskMetrics,\n TeamMetrics,\n TaskAnalytics,\n DashboardState,\n TimeRange,\n AnalyticsQuery,\n} from '../types/metrics.js';\nimport path from 'path';\nimport fs from 'fs';\nimport os from 'os';\n// Type-safe environment variable access\nfunction getEnv(key: string, defaultValue?: string): string {\n const value = process.env[key];\n if (value === undefined) {\n if (defaultValue !== undefined) return defaultValue;\n throw new Error(`Environment variable ${key} is required`);\n }\n return value;\n}\n\nfunction getOptionalEnv(key: string): string | undefined {\n return process.env[key];\n}\n\n\nexport class AnalyticsService {\n private metricsQueries: MetricsQueries;\n private linearClient?: LinearClient;\n private taskStore?: PebblesTaskStore;\n private dbPath: string;\n private projectPath: string;\n private updateCallbacks: Set<(state: DashboardState) => void> = new Set();\n\n constructor(projectPath?: string) {\n this.projectPath = projectPath || process.cwd();\n this.dbPath = path.join(this.projectPath, '.stackmemory', 'analytics.db');\n\n this.ensureDirectoryExists();\n this.metricsQueries = new MetricsQueries(this.dbPath);\n\n // Initialize task store for syncing\n this.initializeTaskStore();\n\n if (process.env['LINEAR_API_KEY']) {\n this.initializeLinearIntegration();\n }\n }\n\n private initializeTaskStore(): void {\n try {\n const contextDbPath = path.join(\n this.projectPath,\n '.stackmemory',\n 'context.db'\n );\n if (fs.existsSync(contextDbPath)) {\n const db = new Database(contextDbPath);\n this.taskStore = new PebblesTaskStore(this.projectPath, db);\n }\n } catch (error: unknown) {\n console.error('Failed to initialize task store:', error);\n }\n }\n\n private ensureDirectoryExists(): void {\n const dir = path.dirname(this.dbPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n }\n\n private async initializeLinearIntegration(): Promise<void> {\n try {\n const configPath = path.join(\n os.homedir(),\n '.stackmemory',\n 'linear-config.json'\n );\n if (fs.existsSync(configPath)) {\n const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));\n this.linearClient = new LinearClient(config);\n await this.syncLinearTasks();\n }\n } catch (error: unknown) {\n console.error('Failed to initialize Linear integration:', error);\n }\n }\n\n async syncLinearTasks(): Promise<void> {\n // First sync from task store (which includes Linear-synced tasks)\n await this.syncFromTaskStore();\n\n // Then try direct Linear sync if client available\n if (this.linearClient) {\n try {\n const issues = await this.linearClient.getIssues({ limit: 100 });\n for (const issue of issues) {\n const task: TaskAnalytics = {\n id: issue.id,\n title: issue.title,\n state: this.mapLinearState(issue.state.type),\n createdAt: new Date(issue.createdAt),\n completedAt:\n issue.state.type === 'completed'\n ? new Date(issue.updatedAt)\n : undefined,\n estimatedEffort: issue.estimate ? issue.estimate * 60 : undefined,\n assigneeId: issue.assignee?.id,\n priority: this.mapLinearPriority(issue.priority),\n labels: Array.isArray(issue.labels)\n ? issue.labels.map((l: any) => l.name)\n : (issue.labels as any)?.nodes?.map((l: any) => l.name) || [],\n blockingIssues: [],\n };\n this.metricsQueries.upsertTask(task);\n }\n } catch (error: unknown) {\n console.error('Failed to sync from Linear API:', error);\n }\n }\n\n await this.notifyUpdate();\n }\n\n async syncFromTaskStore(): Promise<number> {\n if (!this.taskStore) return 0;\n\n try {\n // Get all tasks including completed ones\n const allTasks = this.getAllTasksFromStore();\n let synced = 0;\n\n for (const task of allTasks) {\n const analyticsTask: TaskAnalytics = {\n id: task.id,\n title: task.title,\n state: this.mapTaskStatus(task.status),\n createdAt: new Date(task.created_at * 1000),\n completedAt: task.completed_at\n ? new Date(task.completed_at * 1000)\n : undefined,\n estimatedEffort: task.estimated_effort,\n actualEffort: task.actual_effort,\n assigneeId: task.assignee,\n priority: task.priority as TaskAnalytics['priority'],\n labels: task.tags || [],\n blockingIssues: task.depends_on || [],\n };\n this.metricsQueries.upsertTask(analyticsTask);\n synced++;\n }\n\n return synced;\n } catch (error: unknown) {\n console.error('Failed to sync from task store:', error);\n return 0;\n }\n }\n\n private getAllTasksFromStore(): any[] {\n if (!this.taskStore) return [];\n\n try {\n // Access the db directly to get ALL tasks including completed\n const contextDbPath = path.join(\n this.projectPath,\n '.stackmemory',\n 'context.db'\n );\n const db = new Database(contextDbPath);\n\n const rows = db\n .prepare(\n `\n SELECT * FROM task_cache \n ORDER BY created_at DESC\n `\n )\n .all() as any[];\n\n db.close();\n\n // Hydrate the rows\n return rows.map((row) => ({\n id: row.id,\n title: row.title,\n description: row.description,\n status: row.status,\n priority: row.priority,\n created_at: row.created_at,\n completed_at: row.completed_at,\n estimated_effort: row.estimated_effort,\n actual_effort: row.actual_effort,\n assignee: row.assignee,\n tags: JSON.parse(row.tags || '[]'),\n depends_on: JSON.parse(row.depends_on || '[]'),\n }));\n } catch (error: unknown) {\n console.error('Failed to get all tasks:', error);\n return [];\n }\n }\n\n private mapTaskStatus(status: string): TaskAnalytics['state'] {\n const statusMap: Record<string, TaskAnalytics['state']> = {\n pending: 'todo',\n in_progress: 'in_progress',\n completed: 'completed',\n blocked: 'blocked',\n cancelled: 'blocked',\n };\n return statusMap[status] || 'todo';\n }\n\n private mapLinearState(linearState: string): TaskAnalytics['state'] {\n const stateMap: Record<string, TaskAnalytics['state']> = {\n backlog: 'todo',\n unstarted: 'todo',\n started: 'in_progress',\n completed: 'completed',\n done: 'completed',\n canceled: 'blocked',\n };\n return stateMap[linearState.toLowerCase()] || 'todo';\n }\n\n private mapLinearPriority(priority: number): TaskAnalytics['priority'] {\n if (priority === 1) return 'urgent';\n if (priority === 2) return 'high';\n if (priority === 3) return 'medium';\n return 'low';\n }\n\n async getDashboardState(query: AnalyticsQuery = {}): Promise<DashboardState> {\n const timeRange = query.timeRange || this.getDefaultTimeRange();\n\n const metrics = this.metricsQueries.getTaskMetrics({\n ...query,\n timeRange,\n });\n\n const recentTasks = this.metricsQueries.getRecentTasks({\n ...query,\n limit: 20,\n });\n\n const teamMetrics = await this.getTeamMetrics(query);\n\n return {\n metrics,\n teamMetrics,\n recentTasks,\n timeRange,\n teamFilter: query.userIds || [],\n isLive: this.updateCallbacks.size > 0,\n lastUpdated: new Date(),\n };\n }\n\n private async getTeamMetrics(query: AnalyticsQuery): Promise<TeamMetrics[]> {\n const uniqueUserIds = new Set<string>();\n const tasks = this.metricsQueries.getRecentTasks({ limit: 1000 });\n\n tasks.forEach((task) => {\n if (task.assigneeId) {\n uniqueUserIds.add(task.assigneeId);\n }\n });\n\n const teamMetrics: TeamMetrics[] = [];\n const totalCompleted = tasks.filter((t) => t.state === 'completed').length;\n\n for (const userId of uniqueUserIds) {\n const userQuery = { ...query, userIds: [userId] };\n const individualMetrics = this.metricsQueries.getTaskMetrics(userQuery);\n\n teamMetrics.push({\n userId,\n userName: await this.getUserName(userId),\n individualMetrics,\n contributionPercentage:\n totalCompleted > 0\n ? (individualMetrics.completedTasks / totalCompleted) * 100\n : 0,\n lastActive: new Date(),\n });\n }\n\n return teamMetrics.sort(\n (a, b) => b.contributionPercentage - a.contributionPercentage\n );\n }\n\n private async getUserName(userId: string): Promise<string> {\n // Stub for now - would need LinearClient to expose user query method\n return userId;\n }\n\n private getDefaultTimeRange(): TimeRange {\n const end = new Date();\n const start = new Date();\n start.setDate(start.getDate() - 7);\n\n return {\n start,\n end,\n preset: '7d',\n };\n }\n\n subscribeToUpdates(callback: (state: DashboardState) => void): () => void {\n this.updateCallbacks.add(callback);\n\n return () => {\n this.updateCallbacks.delete(callback);\n };\n }\n\n private async notifyUpdate(): Promise<void> {\n const state = await this.getDashboardState();\n this.updateCallbacks.forEach((callback) => callback(state));\n }\n\n async addTask(task: TaskAnalytics): Promise<void> {\n this.metricsQueries.upsertTask(task);\n await this.notifyUpdate();\n }\n\n async updateTask(\n taskId: string,\n updates: Partial<TaskAnalytics>\n ): Promise<void> {\n const tasks = this.metricsQueries.getRecentTasks({ limit: 1 });\n const existingTask = tasks.find((t) => t.id === taskId);\n\n if (existingTask) {\n const updatedTask = { ...existingTask, ...updates };\n this.metricsQueries.upsertTask(updatedTask);\n await this.notifyUpdate();\n }\n }\n\n close(): void {\n this.metricsQueries.close();\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { MetricsQueries } from '../queries/metrics-queries.js';\nimport { LinearClient } from '../../../integrations/linear/client.js';\nimport { PebblesTaskStore } from '../../tasks/pebbles-task-store.js';\nimport Database from 'better-sqlite3';\nimport {\n TaskMetrics,\n TeamMetrics,\n TaskAnalytics,\n DashboardState,\n TimeRange,\n AnalyticsQuery,\n} from '../types/metrics.js';\nimport path from 'path';\nimport fs from 'fs';\nimport os from 'os';\n// Type-safe environment variable access\nfunction getEnv(key: string, defaultValue?: string): string {\n const value = process.env[key];\n if (value === undefined) {\n if (defaultValue !== undefined) return defaultValue;\n throw new Error(`Environment variable ${key} is required`);\n }\n return value;\n}\n\nfunction getOptionalEnv(key: string): string | undefined {\n return process.env[key];\n}\n\nexport class AnalyticsService {\n private metricsQueries: MetricsQueries;\n private linearClient?: LinearClient;\n private taskStore?: PebblesTaskStore;\n private dbPath: string;\n private projectPath: string;\n private updateCallbacks: Set<(state: DashboardState) => void> = new Set();\n\n constructor(projectPath?: string) {\n this.projectPath = projectPath || process.cwd();\n this.dbPath = path.join(this.projectPath, '.stackmemory', 'analytics.db');\n\n this.ensureDirectoryExists();\n this.metricsQueries = new MetricsQueries(this.dbPath);\n\n // Initialize task store for syncing\n this.initializeTaskStore();\n\n if (process.env['LINEAR_API_KEY']) {\n this.initializeLinearIntegration();\n }\n }\n\n private initializeTaskStore(): void {\n try {\n const contextDbPath = path.join(\n this.projectPath,\n '.stackmemory',\n 'context.db'\n );\n if (fs.existsSync(contextDbPath)) {\n const db = new Database(contextDbPath);\n this.taskStore = new PebblesTaskStore(this.projectPath, db);\n }\n } catch (error: unknown) {\n console.error('Failed to initialize task store:', error);\n }\n }\n\n private ensureDirectoryExists(): void {\n const dir = path.dirname(this.dbPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n }\n\n private async initializeLinearIntegration(): Promise<void> {\n try {\n const configPath = path.join(\n os.homedir(),\n '.stackmemory',\n 'linear-config.json'\n );\n if (fs.existsSync(configPath)) {\n const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));\n this.linearClient = new LinearClient(config);\n await this.syncLinearTasks();\n }\n } catch (error: unknown) {\n console.error('Failed to initialize Linear integration:', error);\n }\n }\n\n async syncLinearTasks(): Promise<void> {\n // First sync from task store (which includes Linear-synced tasks)\n await this.syncFromTaskStore();\n\n // Then try direct Linear sync if client available\n if (this.linearClient) {\n try {\n const issues = await this.linearClient.getIssues({ limit: 100 });\n for (const issue of issues) {\n const task: TaskAnalytics = {\n id: issue.id,\n title: issue.title,\n state: this.mapLinearState(issue.state.type),\n createdAt: new Date(issue.createdAt),\n completedAt:\n issue.state.type === 'completed'\n ? new Date(issue.updatedAt)\n : undefined,\n estimatedEffort: issue.estimate ? issue.estimate * 60 : undefined,\n assigneeId: issue.assignee?.id,\n priority: this.mapLinearPriority(issue.priority),\n labels: Array.isArray(issue.labels)\n ? issue.labels.map((l: any) => l.name)\n : (issue.labels as any)?.nodes?.map((l: any) => l.name) || [],\n blockingIssues: [],\n };\n this.metricsQueries.upsertTask(task);\n }\n } catch (error: unknown) {\n console.error('Failed to sync from Linear API:', error);\n }\n }\n\n await this.notifyUpdate();\n }\n\n async syncFromTaskStore(): Promise<number> {\n if (!this.taskStore) return 0;\n\n try {\n // Get all tasks including completed ones\n const allTasks = this.getAllTasksFromStore();\n let synced = 0;\n\n for (const task of allTasks) {\n const analyticsTask: TaskAnalytics = {\n id: task.id,\n title: task.title,\n state: this.mapTaskStatus(task.status),\n createdAt: new Date(task.created_at * 1000),\n completedAt: task.completed_at\n ? new Date(task.completed_at * 1000)\n : undefined,\n estimatedEffort: task.estimated_effort,\n actualEffort: task.actual_effort,\n assigneeId: task.assignee,\n priority: task.priority as TaskAnalytics['priority'],\n labels: task.tags || [],\n blockingIssues: task.depends_on || [],\n };\n this.metricsQueries.upsertTask(analyticsTask);\n synced++;\n }\n\n return synced;\n } catch (error: unknown) {\n console.error('Failed to sync from task store:', error);\n return 0;\n }\n }\n\n private getAllTasksFromStore(): any[] {\n if (!this.taskStore) return [];\n\n try {\n // Access the db directly to get ALL tasks including completed\n const contextDbPath = path.join(\n this.projectPath,\n '.stackmemory',\n 'context.db'\n );\n const db = new Database(contextDbPath);\n\n const rows = db\n .prepare(\n `\n SELECT * FROM task_cache \n ORDER BY created_at DESC\n `\n )\n .all() as any[];\n\n db.close();\n\n // Hydrate the rows\n return rows.map((row) => ({\n id: row.id,\n title: row.title,\n description: row.description,\n status: row.status,\n priority: row.priority,\n created_at: row.created_at,\n completed_at: row.completed_at,\n estimated_effort: row.estimated_effort,\n actual_effort: row.actual_effort,\n assignee: row.assignee,\n tags: JSON.parse(row.tags || '[]'),\n depends_on: JSON.parse(row.depends_on || '[]'),\n }));\n } catch (error: unknown) {\n console.error('Failed to get all tasks:', error);\n return [];\n }\n }\n\n private mapTaskStatus(status: string): TaskAnalytics['state'] {\n const statusMap: Record<string, TaskAnalytics['state']> = {\n pending: 'todo',\n in_progress: 'in_progress',\n completed: 'completed',\n blocked: 'blocked',\n cancelled: 'blocked',\n };\n return statusMap[status] || 'todo';\n }\n\n private mapLinearState(linearState: string): TaskAnalytics['state'] {\n const stateMap: Record<string, TaskAnalytics['state']> = {\n backlog: 'todo',\n unstarted: 'todo',\n started: 'in_progress',\n completed: 'completed',\n done: 'completed',\n canceled: 'blocked',\n };\n return stateMap[linearState.toLowerCase()] || 'todo';\n }\n\n private mapLinearPriority(priority: number): TaskAnalytics['priority'] {\n if (priority === 1) return 'urgent';\n if (priority === 2) return 'high';\n if (priority === 3) return 'medium';\n return 'low';\n }\n\n async getDashboardState(query: AnalyticsQuery = {}): Promise<DashboardState> {\n const timeRange = query.timeRange || this.getDefaultTimeRange();\n\n const metrics = this.metricsQueries.getTaskMetrics({\n ...query,\n timeRange,\n });\n\n const recentTasks = this.metricsQueries.getRecentTasks({\n ...query,\n limit: 20,\n });\n\n const teamMetrics = await this.getTeamMetrics(query);\n\n return {\n metrics,\n teamMetrics,\n recentTasks,\n timeRange,\n teamFilter: query.userIds || [],\n isLive: this.updateCallbacks.size > 0,\n lastUpdated: new Date(),\n };\n }\n\n private async getTeamMetrics(query: AnalyticsQuery): Promise<TeamMetrics[]> {\n const uniqueUserIds = new Set<string>();\n const tasks = this.metricsQueries.getRecentTasks({ limit: 1000 });\n\n tasks.forEach((task) => {\n if (task.assigneeId) {\n uniqueUserIds.add(task.assigneeId);\n }\n });\n\n const teamMetrics: TeamMetrics[] = [];\n const totalCompleted = tasks.filter((t) => t.state === 'completed').length;\n\n for (const userId of uniqueUserIds) {\n const userQuery = { ...query, userIds: [userId] };\n const individualMetrics = this.metricsQueries.getTaskMetrics(userQuery);\n\n teamMetrics.push({\n userId,\n userName: await this.getUserName(userId),\n individualMetrics,\n contributionPercentage:\n totalCompleted > 0\n ? (individualMetrics.completedTasks / totalCompleted) * 100\n : 0,\n lastActive: new Date(),\n });\n }\n\n return teamMetrics.sort(\n (a, b) => b.contributionPercentage - a.contributionPercentage\n );\n }\n\n private async getUserName(userId: string): Promise<string> {\n // Stub for now - would need LinearClient to expose user query method\n return userId;\n }\n\n private getDefaultTimeRange(): TimeRange {\n const end = new Date();\n const start = new Date();\n start.setDate(start.getDate() - 7);\n\n return {\n start,\n end,\n preset: '7d',\n };\n }\n\n subscribeToUpdates(callback: (state: DashboardState) => void): () => void {\n this.updateCallbacks.add(callback);\n\n return () => {\n this.updateCallbacks.delete(callback);\n };\n }\n\n private async notifyUpdate(): Promise<void> {\n const state = await this.getDashboardState();\n this.updateCallbacks.forEach((callback) => callback(state));\n }\n\n async addTask(task: TaskAnalytics): Promise<void> {\n this.metricsQueries.upsertTask(task);\n await this.notifyUpdate();\n }\n\n async updateTask(\n taskId: string,\n updates: Partial<TaskAnalytics>\n ): Promise<void> {\n const tasks = this.metricsQueries.getRecentTasks({ limit: 1 });\n const existingTask = tasks.find((t) => t.id === taskId);\n\n if (existingTask) {\n const updatedTask = { ...existingTask, ...updates };\n this.metricsQueries.upsertTask(updatedTask);\n await this.notifyUpdate();\n }\n }\n\n close(): void {\n this.metricsQueries.close();\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -66,7 +66,7 @@ class MetricsQueries {
66
66
  getTaskMetrics(query = {}) {
67
67
  try {
68
68
  const { timeRange, userIds, states, priorities } = query;
69
- let whereConditions = ["1=1"];
69
+ const whereConditions = ["1=1"];
70
70
  const params = {};
71
71
  if (timeRange) {
72
72
  whereConditions.push(
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/features/analytics/queries/metrics-queries.ts"],
4
- "sourcesContent": ["import Database from 'better-sqlite3';\nimport {\n TaskAnalytics,\n TaskMetrics,\n TimeRange,\n AnalyticsQuery,\n} from '../types/metrics.js';\nimport {\n DatabaseError,\n SystemError,\n ErrorCode,\n createErrorHandler,\n} from '../../../core/errors/index.js';\nimport { retry } from '../../../core/errors/recovery.js';\n\nexport class MetricsQueries {\n private db: Database.Database;\n\n constructor(dbPath: string) {\n try {\n this.db = new Database(dbPath, { readonly: false });\n this.initializeTables();\n } catch (error: unknown) {\n throw new DatabaseError(\n 'Failed to initialize metrics database',\n ErrorCode.DB_CONNECTION_FAILED,\n {\n dbPath,\n operation: 'constructor',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n private initializeTables(): void {\n const errorHandler = createErrorHandler({\n operation: 'initializeTables',\n });\n\n try {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS task_analytics (\n id TEXT PRIMARY KEY,\n title TEXT NOT NULL,\n state TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n completed_at INTEGER,\n estimated_effort INTEGER,\n actual_effort INTEGER,\n assignee_id TEXT,\n priority TEXT DEFAULT 'medium',\n labels TEXT DEFAULT '[]',\n blocking_issues TEXT DEFAULT '[]',\n updated_at INTEGER DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE INDEX IF NOT EXISTS idx_task_state ON task_analytics(state);\n CREATE INDEX IF NOT EXISTS idx_task_created ON task_analytics(created_at);\n CREATE INDEX IF NOT EXISTS idx_task_assignee ON task_analytics(assignee_id);\n `);\n } catch (error: unknown) {\n const dbError = errorHandler(error, {\n operation: 'initializeTables',\n schema: 'task_analytics',\n });\n \n throw new DatabaseError(\n 'Failed to initialize analytics tables',\n ErrorCode.DB_MIGRATION_FAILED,\n {\n operation: 'initializeTables',\n schema: 'task_analytics',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n getTaskMetrics(query: AnalyticsQuery = {}): TaskMetrics {\n try {\n const { timeRange, userIds, states, priorities } = query;\n\n let whereConditions: string[] = ['1=1'];\n const params: any = {};\n\n if (timeRange) {\n whereConditions.push(\n 'created_at >= @startTime AND created_at <= @endTime'\n );\n params.startTime = Math.floor(timeRange.start.getTime() / 1000);\n params.endTime = Math.floor(timeRange.end.getTime() / 1000);\n }\n\n if (userIds && userIds.length > 0) {\n whereConditions.push(\n `assignee_id IN (${userIds.map((_, i) => `@user${i}`).join(',')})`\n );\n userIds.forEach((id, i) => (params[`user${i}`] = id));\n }\n\n if (states && states.length > 0) {\n whereConditions.push(\n `state IN (${states.map((_, i) => `@state${i}`).join(',')})`\n );\n states.forEach((s, i) => (params[`state${i}`] = s));\n }\n\n if (priorities && priorities.length > 0) {\n whereConditions.push(\n `priority IN (${priorities.map((_, i) => `@priority${i}`).join(',')})`\n );\n priorities.forEach((p, i) => (params[`priority${i}`] = p));\n }\n\n const whereClause = whereConditions.join(' AND ');\n\n const metricsQuery = this.db.prepare(`\n SELECT \n COUNT(*) as total_tasks,\n SUM(CASE WHEN state = 'completed' THEN 1 ELSE 0 END) as completed_tasks,\n SUM(CASE WHEN state = 'in_progress' THEN 1 ELSE 0 END) as in_progress_tasks,\n SUM(CASE WHEN state = 'blocked' THEN 1 ELSE 0 END) as blocked_tasks,\n AVG(CASE \n WHEN state = 'completed' AND completed_at IS NOT NULL \n THEN (completed_at - created_at) * 1000\n ELSE NULL \n END) as avg_time_to_complete,\n AVG(CASE \n WHEN actual_effort IS NOT NULL AND estimated_effort IS NOT NULL AND estimated_effort > 0\n THEN (CAST(actual_effort AS REAL) / estimated_effort) * 100\n ELSE NULL\n END) as effort_accuracy,\n SUM(CASE \n WHEN json_array_length(blocking_issues) > 0 \n THEN json_array_length(blocking_issues)\n ELSE 0\n END) as blocking_issues_count\n FROM task_analytics\n WHERE ${whereClause}\n `);\n\n const result = metricsQuery.get(params) as any;\n\n const velocityQuery = this.db.prepare(`\n SELECT \n DATE(created_at, 'unixepoch') as day,\n COUNT(*) as completed_count\n FROM task_analytics\n WHERE state = 'completed' \n AND ${whereClause}\n GROUP BY day\n ORDER BY day DESC\n LIMIT 30\n `);\n\n const velocityData = velocityQuery.all(params) as any[];\n const velocityTrend = velocityData.map((v) => v.completed_count).reverse();\n\n return {\n totalTasks: result.total_tasks || 0,\n completedTasks: result.completed_tasks || 0,\n inProgressTasks: result.in_progress_tasks || 0,\n blockedTasks: result.blocked_tasks || 0,\n completionRate:\n result.total_tasks > 0\n ? (result.completed_tasks / result.total_tasks) * 100\n : 0,\n averageTimeToComplete: result.avg_time_to_complete || 0,\n effortAccuracy: result.effort_accuracy || 100,\n blockingIssuesCount: result.blocking_issues_count || 0,\n velocityTrend,\n };\n } catch (error: unknown) {\n throw new DatabaseError(\n 'Failed to get task metrics',\n ErrorCode.DB_QUERY_FAILED,\n {\n query,\n operation: 'getTaskMetrics',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n getRecentTasks(query: AnalyticsQuery = {}): TaskAnalytics[] {\n try {\n const { limit = 100, offset = 0 } = query;\n\n const tasksQuery = this.db.prepare(`\n SELECT \n id,\n title,\n state,\n created_at,\n completed_at,\n estimated_effort,\n actual_effort,\n assignee_id,\n priority,\n labels,\n blocking_issues\n FROM task_analytics\n ORDER BY updated_at DESC\n LIMIT ? OFFSET ?\n `);\n\n const rows = tasksQuery.all(limit, offset) as any[];\n\n return rows.map((row) => ({\n id: row.id,\n title: row.title,\n state: row.state as TaskAnalytics['state'],\n createdAt: new Date(row.created_at * 1000),\n completedAt: row.completed_at\n ? new Date(row.completed_at * 1000)\n : undefined,\n estimatedEffort: row.estimated_effort,\n actualEffort: row.actual_effort,\n assigneeId: row.assignee_id,\n priority: row.priority as TaskAnalytics['priority'],\n labels: JSON.parse(row.labels),\n blockingIssues: JSON.parse(row.blocking_issues),\n }));\n } catch (error: unknown) {\n throw new DatabaseError(\n 'Failed to get recent tasks',\n ErrorCode.DB_QUERY_FAILED,\n {\n limit: query.limit,\n offset: query.offset,\n operation: 'getRecentTasks',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n upsertTask(task: TaskAnalytics): void {\n try {\n const stmt = this.db.prepare(`\n INSERT INTO task_analytics (\n id, title, state, created_at, completed_at,\n estimated_effort, actual_effort, assignee_id,\n priority, labels, blocking_issues\n ) VALUES (\n @id, @title, @state, @created_at, @completed_at,\n @estimated_effort, @actual_effort, @assignee_id,\n @priority, @labels, @blocking_issues\n )\n ON CONFLICT(id) DO UPDATE SET\n title = @title,\n state = @state,\n completed_at = @completed_at,\n estimated_effort = @estimated_effort,\n actual_effort = @actual_effort,\n assignee_id = @assignee_id,\n priority = @priority,\n labels = @labels,\n blocking_issues = @blocking_issues,\n updated_at = strftime('%s', 'now')\n `);\n\n stmt.run({\n id: task.id,\n title: task.title,\n state: task.state,\n created_at: Math.floor(task.createdAt.getTime() / 1000),\n completed_at: task.completedAt\n ? Math.floor(task.completedAt.getTime() / 1000)\n : null,\n estimated_effort: task.estimatedEffort || null,\n actual_effort: task.actualEffort || null,\n assignee_id: task.assigneeId || null,\n priority: task.priority,\n labels: JSON.stringify(task.labels),\n blocking_issues: JSON.stringify(task.blockingIssues),\n });\n } catch (error: unknown) {\n throw new DatabaseError(\n `Failed to upsert task analytics: ${task.id}`,\n ErrorCode.DB_QUERY_FAILED,\n {\n taskId: task.id,\n taskState: task.state,\n operation: 'upsertTask',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n close(): void {\n try {\n this.db.close();\n } catch (error: unknown) {\n throw new DatabaseError(\n 'Failed to close analytics database',\n ErrorCode.DB_CONNECTION_FAILED,\n {\n operation: 'close',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import Database from 'better-sqlite3';\nimport {\n TaskAnalytics,\n TaskMetrics,\n TimeRange,\n AnalyticsQuery,\n} from '../types/metrics.js';\nimport {\n DatabaseError,\n SystemError,\n ErrorCode,\n createErrorHandler,\n} from '../../../core/errors/index.js';\nimport { retry } from '../../../core/errors/recovery.js';\n\nexport class MetricsQueries {\n private db: Database.Database;\n\n constructor(dbPath: string) {\n try {\n this.db = new Database(dbPath, { readonly: false });\n this.initializeTables();\n } catch (error: unknown) {\n throw new DatabaseError(\n 'Failed to initialize metrics database',\n ErrorCode.DB_CONNECTION_FAILED,\n {\n dbPath,\n operation: 'constructor',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n private initializeTables(): void {\n const errorHandler = createErrorHandler({\n operation: 'initializeTables',\n });\n\n try {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS task_analytics (\n id TEXT PRIMARY KEY,\n title TEXT NOT NULL,\n state TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n completed_at INTEGER,\n estimated_effort INTEGER,\n actual_effort INTEGER,\n assignee_id TEXT,\n priority TEXT DEFAULT 'medium',\n labels TEXT DEFAULT '[]',\n blocking_issues TEXT DEFAULT '[]',\n updated_at INTEGER DEFAULT (strftime('%s', 'now'))\n );\n\n CREATE INDEX IF NOT EXISTS idx_task_state ON task_analytics(state);\n CREATE INDEX IF NOT EXISTS idx_task_created ON task_analytics(created_at);\n CREATE INDEX IF NOT EXISTS idx_task_assignee ON task_analytics(assignee_id);\n `);\n } catch (error: unknown) {\n const dbError = errorHandler(error, {\n operation: 'initializeTables',\n schema: 'task_analytics',\n });\n\n throw new DatabaseError(\n 'Failed to initialize analytics tables',\n ErrorCode.DB_MIGRATION_FAILED,\n {\n operation: 'initializeTables',\n schema: 'task_analytics',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n getTaskMetrics(query: AnalyticsQuery = {}): TaskMetrics {\n try {\n const { timeRange, userIds, states, priorities } = query;\n\n const whereConditions: string[] = ['1=1'];\n const params: any = {};\n\n if (timeRange) {\n whereConditions.push(\n 'created_at >= @startTime AND created_at <= @endTime'\n );\n params.startTime = Math.floor(timeRange.start.getTime() / 1000);\n params.endTime = Math.floor(timeRange.end.getTime() / 1000);\n }\n\n if (userIds && userIds.length > 0) {\n whereConditions.push(\n `assignee_id IN (${userIds.map((_, i) => `@user${i}`).join(',')})`\n );\n userIds.forEach((id, i) => (params[`user${i}`] = id));\n }\n\n if (states && states.length > 0) {\n whereConditions.push(\n `state IN (${states.map((_, i) => `@state${i}`).join(',')})`\n );\n states.forEach((s, i) => (params[`state${i}`] = s));\n }\n\n if (priorities && priorities.length > 0) {\n whereConditions.push(\n `priority IN (${priorities.map((_, i) => `@priority${i}`).join(',')})`\n );\n priorities.forEach((p, i) => (params[`priority${i}`] = p));\n }\n\n const whereClause = whereConditions.join(' AND ');\n\n const metricsQuery = this.db.prepare(`\n SELECT \n COUNT(*) as total_tasks,\n SUM(CASE WHEN state = 'completed' THEN 1 ELSE 0 END) as completed_tasks,\n SUM(CASE WHEN state = 'in_progress' THEN 1 ELSE 0 END) as in_progress_tasks,\n SUM(CASE WHEN state = 'blocked' THEN 1 ELSE 0 END) as blocked_tasks,\n AVG(CASE \n WHEN state = 'completed' AND completed_at IS NOT NULL \n THEN (completed_at - created_at) * 1000\n ELSE NULL \n END) as avg_time_to_complete,\n AVG(CASE \n WHEN actual_effort IS NOT NULL AND estimated_effort IS NOT NULL AND estimated_effort > 0\n THEN (CAST(actual_effort AS REAL) / estimated_effort) * 100\n ELSE NULL\n END) as effort_accuracy,\n SUM(CASE \n WHEN json_array_length(blocking_issues) > 0 \n THEN json_array_length(blocking_issues)\n ELSE 0\n END) as blocking_issues_count\n FROM task_analytics\n WHERE ${whereClause}\n `);\n\n const result = metricsQuery.get(params) as any;\n\n const velocityQuery = this.db.prepare(`\n SELECT \n DATE(created_at, 'unixepoch') as day,\n COUNT(*) as completed_count\n FROM task_analytics\n WHERE state = 'completed' \n AND ${whereClause}\n GROUP BY day\n ORDER BY day DESC\n LIMIT 30\n `);\n\n const velocityData = velocityQuery.all(params) as any[];\n const velocityTrend = velocityData\n .map((v) => v.completed_count)\n .reverse();\n\n return {\n totalTasks: result.total_tasks || 0,\n completedTasks: result.completed_tasks || 0,\n inProgressTasks: result.in_progress_tasks || 0,\n blockedTasks: result.blocked_tasks || 0,\n completionRate:\n result.total_tasks > 0\n ? (result.completed_tasks / result.total_tasks) * 100\n : 0,\n averageTimeToComplete: result.avg_time_to_complete || 0,\n effortAccuracy: result.effort_accuracy || 100,\n blockingIssuesCount: result.blocking_issues_count || 0,\n velocityTrend,\n };\n } catch (error: unknown) {\n throw new DatabaseError(\n 'Failed to get task metrics',\n ErrorCode.DB_QUERY_FAILED,\n {\n query,\n operation: 'getTaskMetrics',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n getRecentTasks(query: AnalyticsQuery = {}): TaskAnalytics[] {\n try {\n const { limit = 100, offset = 0 } = query;\n\n const tasksQuery = this.db.prepare(`\n SELECT \n id,\n title,\n state,\n created_at,\n completed_at,\n estimated_effort,\n actual_effort,\n assignee_id,\n priority,\n labels,\n blocking_issues\n FROM task_analytics\n ORDER BY updated_at DESC\n LIMIT ? OFFSET ?\n `);\n\n const rows = tasksQuery.all(limit, offset) as any[];\n\n return rows.map((row) => ({\n id: row.id,\n title: row.title,\n state: row.state as TaskAnalytics['state'],\n createdAt: new Date(row.created_at * 1000),\n completedAt: row.completed_at\n ? new Date(row.completed_at * 1000)\n : undefined,\n estimatedEffort: row.estimated_effort,\n actualEffort: row.actual_effort,\n assigneeId: row.assignee_id,\n priority: row.priority as TaskAnalytics['priority'],\n labels: JSON.parse(row.labels),\n blockingIssues: JSON.parse(row.blocking_issues),\n }));\n } catch (error: unknown) {\n throw new DatabaseError(\n 'Failed to get recent tasks',\n ErrorCode.DB_QUERY_FAILED,\n {\n limit: query.limit,\n offset: query.offset,\n operation: 'getRecentTasks',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n upsertTask(task: TaskAnalytics): void {\n try {\n const stmt = this.db.prepare(`\n INSERT INTO task_analytics (\n id, title, state, created_at, completed_at,\n estimated_effort, actual_effort, assignee_id,\n priority, labels, blocking_issues\n ) VALUES (\n @id, @title, @state, @created_at, @completed_at,\n @estimated_effort, @actual_effort, @assignee_id,\n @priority, @labels, @blocking_issues\n )\n ON CONFLICT(id) DO UPDATE SET\n title = @title,\n state = @state,\n completed_at = @completed_at,\n estimated_effort = @estimated_effort,\n actual_effort = @actual_effort,\n assignee_id = @assignee_id,\n priority = @priority,\n labels = @labels,\n blocking_issues = @blocking_issues,\n updated_at = strftime('%s', 'now')\n `);\n\n stmt.run({\n id: task.id,\n title: task.title,\n state: task.state,\n created_at: Math.floor(task.createdAt.getTime() / 1000),\n completed_at: task.completedAt\n ? Math.floor(task.completedAt.getTime() / 1000)\n : null,\n estimated_effort: task.estimatedEffort || null,\n actual_effort: task.actualEffort || null,\n assignee_id: task.assigneeId || null,\n priority: task.priority,\n labels: JSON.stringify(task.labels),\n blocking_issues: JSON.stringify(task.blockingIssues),\n });\n } catch (error: unknown) {\n throw new DatabaseError(\n `Failed to upsert task analytics: ${task.id}`,\n ErrorCode.DB_QUERY_FAILED,\n {\n taskId: task.id,\n taskState: task.state,\n operation: 'upsertTask',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n close(): void {\n try {\n this.db.close();\n } catch (error: unknown) {\n throw new DatabaseError(\n 'Failed to close analytics database',\n ErrorCode.DB_CONNECTION_FAILED,\n {\n operation: 'close',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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"],
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
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -16,7 +16,12 @@ class AnalyticsPanel extends EventEmitter {
16
16
  this.showTokenUsage();
17
17
  }
18
18
  cycleMetric() {
19
- const metrics = ["tokens", "velocity", "quality", "performance"];
19
+ const metrics = [
20
+ "tokens",
21
+ "velocity",
22
+ "quality",
23
+ "performance"
24
+ ];
20
25
  const currentIndex = metrics.indexOf(this.currentMetric);
21
26
  this.currentMetric = metrics[(currentIndex + 1) % metrics.length];
22
27
  switch (this.currentMetric) {
@@ -36,12 +41,14 @@ class AnalyticsPanel extends EventEmitter {
36
41
  }
37
42
  showTokenUsage() {
38
43
  if (!this.data) return;
39
- const data = [{
40
- title: "Token Usage",
41
- x: this.data.tokens.labels.map((_, i) => i.toString()),
42
- y: this.data.tokens.values,
43
- style: { line: "yellow" }
44
- }];
44
+ const data = [
45
+ {
46
+ title: "Token Usage",
47
+ x: this.data.tokens.labels.map((_, i) => i.toString()),
48
+ y: this.data.tokens.values,
49
+ style: { line: "yellow" }
50
+ }
51
+ ];
45
52
  this.line.setData(data);
46
53
  if (typeof this.line.setLabel === "function") {
47
54
  this.line.setLabel(" \u{1F4C8} Analytics - Token Usage [m] cycle ");
@@ -50,12 +57,14 @@ class AnalyticsPanel extends EventEmitter {
50
57
  }
51
58
  showVelocity() {
52
59
  if (!this.data) return;
53
- const data = [{
54
- title: "Task Velocity",
55
- x: this.data.tasks.velocity.map((_, i) => `Sprint ${i + 1}`),
56
- y: this.data.tasks.velocity,
57
- style: { line: "green" }
58
- }];
60
+ const data = [
61
+ {
62
+ title: "Task Velocity",
63
+ x: this.data.tasks.velocity.map((_, i) => `Sprint ${i + 1}`),
64
+ y: this.data.tasks.velocity,
65
+ style: { line: "green" }
66
+ }
67
+ ];
59
68
  this.line.setData(data);
60
69
  if (typeof this.line.setLabel === "function") {
61
70
  this.line.setLabel(" \u{1F4C8} Analytics - Task Velocity [m] cycle ");
@@ -68,13 +77,25 @@ class AnalyticsPanel extends EventEmitter {
68
77
  {
69
78
  title: "Tests Passed",
70
79
  x: ["1", "2", "3", "4", "5"],
71
- y: [this.data.quality.testsPassed, this.data.quality.testsPassed, this.data.quality.testsPassed, this.data.quality.testsPassed, this.data.quality.testsPassed],
80
+ y: [
81
+ this.data.quality.testsPassed,
82
+ this.data.quality.testsPassed,
83
+ this.data.quality.testsPassed,
84
+ this.data.quality.testsPassed,
85
+ this.data.quality.testsPassed
86
+ ],
72
87
  style: { line: "green" }
73
88
  },
74
89
  {
75
90
  title: "Coverage %",
76
91
  x: ["1", "2", "3", "4", "5"],
77
- y: [this.data.quality.coverage, this.data.quality.coverage, this.data.quality.coverage, this.data.quality.coverage, this.data.quality.coverage],
92
+ y: [
93
+ this.data.quality.coverage,
94
+ this.data.quality.coverage,
95
+ this.data.quality.coverage,
96
+ this.data.quality.coverage,
97
+ this.data.quality.coverage
98
+ ],
78
99
  style: { line: "blue" }
79
100
  }
80
101
  ];
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/features/tui/components/analytics-panel.ts"],
4
- "sourcesContent": ["/**\n * Analytics Panel Component\n * Real-time charts and metrics visualization\n */\n\nimport contrib from 'blessed-contrib';\nimport { EventEmitter } from 'events';\nimport type { AnalyticsData } from '../types.js';\n\nexport class AnalyticsPanel extends EventEmitter {\n private line: any; // contrib.line type\n private currentMetric: 'tokens' | 'velocity' | 'quality' | 'performance' = 'tokens';\n private data: AnalyticsData | null = null;\n\n constructor(line: any) {\n super();\n this.line = line;\n this.initializeUI();\n }\n\n private initializeUI(): void {\n // Cycle through metrics\n this.line.screen.key(['m'], () => {\n this.cycleMetric();\n });\n\n // Set initial display\n this.showTokenUsage();\n }\n\n private cycleMetric(): void {\n const metrics: Array<typeof this.currentMetric> = ['tokens', 'velocity', 'quality', 'performance'];\n const currentIndex = metrics.indexOf(this.currentMetric);\n this.currentMetric = metrics[(currentIndex + 1) % metrics.length];\n \n switch (this.currentMetric) {\n case 'tokens':\n this.showTokenUsage();\n break;\n case 'velocity':\n this.showVelocity();\n break;\n case 'quality':\n this.showQuality();\n break;\n case 'performance':\n this.showPerformance();\n break;\n }\n }\n\n private showTokenUsage(): void {\n if (!this.data) return;\n \n const data = [{\n title: 'Token Usage',\n x: this.data.tokens.labels.map((_, i) => i.toString()),\n y: this.data.tokens.values,\n style: { line: 'yellow' }\n }];\n \n this.line.setData(data);\n if (typeof this.line.setLabel === 'function') {\n this.line.setLabel(' \uD83D\uDCC8 Analytics - Token Usage [m] cycle ');\n }\n this.line.screen.render();\n }\n\n private showVelocity(): void {\n if (!this.data) return;\n \n const data = [{\n title: 'Task Velocity',\n x: this.data.tasks.velocity.map((_, i) => `Sprint ${i + 1}`),\n y: this.data.tasks.velocity,\n style: { line: 'green' }\n }];\n \n this.line.setData(data);\n if (typeof this.line.setLabel === 'function') {\n this.line.setLabel(' \uD83D\uDCC8 Analytics - Task Velocity [m] cycle ');\n }\n this.line.screen.render();\n }\n\n private showQuality(): void {\n if (!this.data) return;\n \n const data = [\n {\n title: 'Tests Passed',\n x: ['1', '2', '3', '4', '5'],\n y: [this.data.quality.testsPassed, this.data.quality.testsPassed, this.data.quality.testsPassed, this.data.quality.testsPassed, this.data.quality.testsPassed],\n style: { line: 'green' }\n },\n {\n title: 'Coverage %',\n x: ['1', '2', '3', '4', '5'],\n y: [this.data.quality.coverage, this.data.quality.coverage, this.data.quality.coverage, this.data.quality.coverage, this.data.quality.coverage],\n style: { line: 'blue' }\n }\n ];\n \n this.line.setData(data);\n if (typeof this.line.setLabel === 'function') {\n this.line.setLabel(' \uD83D\uDCC8 Analytics - Code Quality [m] cycle ');\n }\n this.line.screen.render();\n }\n\n private showPerformance(): void {\n if (!this.data) return;\n \n const data = [\n {\n title: 'Response Time (ms)',\n x: this.data.performance.avgResponseTime.map((_, i) => i.toString()),\n y: this.data.performance.avgResponseTime,\n style: { line: 'cyan' }\n },\n {\n title: 'Error Rate (%)',\n x: this.data.performance.errorRate.map((_, i) => i.toString()),\n y: this.data.performance.errorRate.map((r: any) => r * 100),\n style: { line: 'red' }\n }\n ];\n \n this.line.setData(data);\n if (typeof this.line.setLabel === 'function') {\n this.line.setLabel(' \uD83D\uDCC8 Analytics - Performance [m] cycle ');\n }\n this.line.screen.render();\n }\n\n public update(data: AnalyticsData): void {\n this.data = data;\n \n // Refresh current view\n switch (this.currentMetric) {\n case 'tokens':\n this.showTokenUsage();\n break;\n case 'velocity':\n this.showVelocity();\n break;\n case 'quality':\n this.showQuality();\n break;\n case 'performance':\n this.showPerformance();\n break;\n }\n }\n\n public focus(): void {\n // Line chart doesn't have traditional focus\n this.emit('focused');\n }\n\n public hasFocus(): boolean {\n return false; // Line charts don't take focus\n }\n}"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Analytics Panel Component\n * Real-time charts and metrics visualization\n */\n\nimport contrib from 'blessed-contrib';\nimport { EventEmitter } from 'events';\nimport type { AnalyticsData } from '../types.js';\n\nexport class AnalyticsPanel extends EventEmitter {\n private line: any; // contrib.line type\n private currentMetric: 'tokens' | 'velocity' | 'quality' | 'performance' =\n 'tokens';\n private data: AnalyticsData | null = null;\n\n constructor(line: any) {\n super();\n this.line = line;\n this.initializeUI();\n }\n\n private initializeUI(): void {\n // Cycle through metrics\n this.line.screen.key(['m'], () => {\n this.cycleMetric();\n });\n\n // Set initial display\n this.showTokenUsage();\n }\n\n private cycleMetric(): void {\n const metrics: Array<typeof this.currentMetric> = [\n 'tokens',\n 'velocity',\n 'quality',\n 'performance',\n ];\n const currentIndex = metrics.indexOf(this.currentMetric);\n this.currentMetric = metrics[(currentIndex + 1) % metrics.length];\n\n switch (this.currentMetric) {\n case 'tokens':\n this.showTokenUsage();\n break;\n case 'velocity':\n this.showVelocity();\n break;\n case 'quality':\n this.showQuality();\n break;\n case 'performance':\n this.showPerformance();\n break;\n }\n }\n\n private showTokenUsage(): void {\n if (!this.data) return;\n\n const data = [\n {\n title: 'Token Usage',\n x: this.data.tokens.labels.map((_, i) => i.toString()),\n y: this.data.tokens.values,\n style: { line: 'yellow' },\n },\n ];\n\n this.line.setData(data);\n if (typeof this.line.setLabel === 'function') {\n this.line.setLabel(' \uD83D\uDCC8 Analytics - Token Usage [m] cycle ');\n }\n this.line.screen.render();\n }\n\n private showVelocity(): void {\n if (!this.data) return;\n\n const data = [\n {\n title: 'Task Velocity',\n x: this.data.tasks.velocity.map((_, i) => `Sprint ${i + 1}`),\n y: this.data.tasks.velocity,\n style: { line: 'green' },\n },\n ];\n\n this.line.setData(data);\n if (typeof this.line.setLabel === 'function') {\n this.line.setLabel(' \uD83D\uDCC8 Analytics - Task Velocity [m] cycle ');\n }\n this.line.screen.render();\n }\n\n private showQuality(): void {\n if (!this.data) return;\n\n const data = [\n {\n title: 'Tests Passed',\n x: ['1', '2', '3', '4', '5'],\n y: [\n this.data.quality.testsPassed,\n this.data.quality.testsPassed,\n this.data.quality.testsPassed,\n this.data.quality.testsPassed,\n this.data.quality.testsPassed,\n ],\n style: { line: 'green' },\n },\n {\n title: 'Coverage %',\n x: ['1', '2', '3', '4', '5'],\n y: [\n this.data.quality.coverage,\n this.data.quality.coverage,\n this.data.quality.coverage,\n this.data.quality.coverage,\n this.data.quality.coverage,\n ],\n style: { line: 'blue' },\n },\n ];\n\n this.line.setData(data);\n if (typeof this.line.setLabel === 'function') {\n this.line.setLabel(' \uD83D\uDCC8 Analytics - Code Quality [m] cycle ');\n }\n this.line.screen.render();\n }\n\n private showPerformance(): void {\n if (!this.data) return;\n\n const data = [\n {\n title: 'Response Time (ms)',\n x: this.data.performance.avgResponseTime.map((_, i) => i.toString()),\n y: this.data.performance.avgResponseTime,\n style: { line: 'cyan' },\n },\n {\n title: 'Error Rate (%)',\n x: this.data.performance.errorRate.map((_, i) => i.toString()),\n y: this.data.performance.errorRate.map((r: any) => r * 100),\n style: { line: 'red' },\n },\n ];\n\n this.line.setData(data);\n if (typeof this.line.setLabel === 'function') {\n this.line.setLabel(' \uD83D\uDCC8 Analytics - Performance [m] cycle ');\n }\n this.line.screen.render();\n }\n\n public update(data: AnalyticsData): void {\n this.data = data;\n\n // Refresh current view\n switch (this.currentMetric) {\n case 'tokens':\n this.showTokenUsage();\n break;\n case 'velocity':\n this.showVelocity();\n break;\n case 'quality':\n this.showQuality();\n break;\n case 'performance':\n this.showPerformance();\n break;\n }\n }\n\n public focus(): void {\n // Line chart doesn't have traditional focus\n this.emit('focused');\n }\n\n public hasFocus(): boolean {\n return false; // Line charts don't take focus\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }