@stackmemoryai/stackmemory 0.3.6 → 0.3.8

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 (267) 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/agents/verifiers/formatter-verifier.js.map +2 -2
  6. package/dist/agents/verifiers/llm-judge.js.map +2 -2
  7. package/dist/cli/claude-sm.js +13 -13
  8. package/dist/cli/claude-sm.js.map +2 -2
  9. package/dist/cli/codex-sm.js +13 -13
  10. package/dist/cli/codex-sm.js.map +2 -2
  11. package/dist/cli/commands/agent.js.map +2 -2
  12. package/dist/cli/commands/chromadb.js +261 -46
  13. package/dist/cli/commands/chromadb.js.map +2 -2
  14. package/dist/cli/commands/clear.js +10 -3
  15. package/dist/cli/commands/clear.js.map +2 -2
  16. package/dist/cli/commands/config.js +43 -33
  17. package/dist/cli/commands/config.js.map +2 -2
  18. package/dist/cli/commands/context.js +13 -2
  19. package/dist/cli/commands/context.js.map +2 -2
  20. package/dist/cli/commands/dashboard.js +41 -13
  21. package/dist/cli/commands/dashboard.js.map +2 -2
  22. package/dist/cli/commands/gc.js +251 -0
  23. package/dist/cli/commands/gc.js.map +7 -0
  24. package/dist/cli/commands/handoff.js +12 -1
  25. package/dist/cli/commands/handoff.js.map +2 -2
  26. package/dist/cli/commands/infinite-storage.js +92 -40
  27. package/dist/cli/commands/infinite-storage.js.map +2 -2
  28. package/dist/cli/commands/linear-create.js +49 -10
  29. package/dist/cli/commands/linear-create.js.map +2 -2
  30. package/dist/cli/commands/linear-list.js +45 -11
  31. package/dist/cli/commands/linear-list.js.map +2 -2
  32. package/dist/cli/commands/linear-migrate.js +29 -5
  33. package/dist/cli/commands/linear-migrate.js.map +2 -2
  34. package/dist/cli/commands/linear-test.js +26 -7
  35. package/dist/cli/commands/linear-test.js.map +2 -2
  36. package/dist/cli/commands/linear-unified.js +350 -0
  37. package/dist/cli/commands/linear-unified.js.map +7 -0
  38. package/dist/cli/commands/linear.js +17 -6
  39. package/dist/cli/commands/linear.js.map +2 -2
  40. package/dist/cli/commands/monitor.js.map +2 -2
  41. package/dist/cli/commands/onboard.js +35 -8
  42. package/dist/cli/commands/onboard.js.map +2 -2
  43. package/dist/cli/commands/quality.js +2 -7
  44. package/dist/cli/commands/quality.js.map +2 -2
  45. package/dist/cli/commands/search.js.map +2 -2
  46. package/dist/cli/commands/session.js +23 -6
  47. package/dist/cli/commands/session.js.map +2 -2
  48. package/dist/cli/commands/skills.js +84 -28
  49. package/dist/cli/commands/skills.js.map +2 -2
  50. package/dist/cli/commands/storage.js +119 -38
  51. package/dist/cli/commands/storage.js.map +2 -2
  52. package/dist/cli/commands/tasks.js.map +2 -2
  53. package/dist/cli/commands/tui.js +13 -2
  54. package/dist/cli/commands/tui.js.map +2 -2
  55. package/dist/cli/commands/webhook.js +71 -21
  56. package/dist/cli/commands/webhook.js.map +2 -2
  57. package/dist/cli/commands/workflow.js +11 -7
  58. package/dist/cli/commands/workflow.js.map +2 -2
  59. package/dist/cli/commands/worktree.js +34 -13
  60. package/dist/cli/commands/worktree.js.map +2 -2
  61. package/dist/cli/index.js +7 -5
  62. package/dist/cli/index.js.map +2 -2
  63. package/dist/core/config/config-manager.js.map +2 -2
  64. package/dist/core/config/types.js.map +1 -1
  65. package/dist/core/context/auto-context.js +10 -6
  66. package/dist/core/context/auto-context.js.map +2 -2
  67. package/dist/core/context/compaction-handler.js.map +2 -2
  68. package/dist/core/context/context-bridge.js.map +2 -2
  69. package/dist/core/context/dual-stack-manager.js.map +2 -2
  70. package/dist/core/context/frame-database.js +13 -3
  71. package/dist/core/context/frame-database.js.map +2 -2
  72. package/dist/core/context/frame-digest.js +7 -5
  73. package/dist/core/context/frame-digest.js.map +2 -2
  74. package/dist/core/context/frame-handoff-manager.js.map +2 -2
  75. package/dist/core/context/frame-manager.js +12 -1
  76. package/dist/core/context/frame-manager.js.map +2 -2
  77. package/dist/core/context/frame-stack.js +16 -5
  78. package/dist/core/context/frame-stack.js.map +2 -2
  79. package/dist/core/context/incremental-gc.js +286 -0
  80. package/dist/core/context/incremental-gc.js.map +7 -0
  81. package/dist/core/context/index.js.map +1 -1
  82. package/dist/core/context/permission-manager.js +12 -1
  83. package/dist/core/context/permission-manager.js.map +2 -2
  84. package/dist/core/context/refactored-frame-manager.js +12 -3
  85. package/dist/core/context/refactored-frame-manager.js.map +2 -2
  86. package/dist/core/context/shared-context-layer.js +16 -3
  87. package/dist/core/context/shared-context-layer.js.map +2 -2
  88. package/dist/core/context/stack-merge-resolver.js.map +2 -2
  89. package/dist/core/context/validation.js.map +2 -2
  90. package/dist/core/database/batch-operations.js +112 -86
  91. package/dist/core/database/batch-operations.js.map +2 -2
  92. package/dist/core/database/connection-pool.js.map +2 -2
  93. package/dist/core/database/migration-manager.js.map +2 -2
  94. package/dist/core/database/paradedb-adapter.js.map +2 -2
  95. package/dist/core/database/query-cache.js +19 -9
  96. package/dist/core/database/query-cache.js.map +2 -2
  97. package/dist/core/database/query-router.js.map +2 -2
  98. package/dist/core/database/sqlite-adapter.js +1 -1
  99. package/dist/core/database/sqlite-adapter.js.map +2 -2
  100. package/dist/core/digest/enhanced-hybrid-digest.js +8 -2
  101. package/dist/core/digest/enhanced-hybrid-digest.js.map +2 -2
  102. package/dist/core/errors/recovery.js +9 -2
  103. package/dist/core/errors/recovery.js.map +2 -2
  104. package/dist/core/frame/workflow-templates-stub.js.map +1 -1
  105. package/dist/core/frame/workflow-templates.js +40 -1
  106. package/dist/core/frame/workflow-templates.js.map +2 -2
  107. package/dist/core/merge/resolution-engine.js.map +2 -2
  108. package/dist/core/monitoring/error-handler.js.map +2 -2
  109. package/dist/core/monitoring/logger.js +19 -3
  110. package/dist/core/monitoring/logger.js.map +2 -2
  111. package/dist/core/monitoring/metrics.js +13 -2
  112. package/dist/core/monitoring/metrics.js.map +2 -2
  113. package/dist/core/monitoring/progress-tracker.js +12 -1
  114. package/dist/core/monitoring/progress-tracker.js.map +2 -2
  115. package/dist/core/monitoring/session-monitor.js.map +2 -2
  116. package/dist/core/performance/context-cache.js.map +2 -2
  117. package/dist/core/performance/lazy-context-loader.js +24 -20
  118. package/dist/core/performance/lazy-context-loader.js.map +2 -2
  119. package/dist/core/performance/monitor.js.map +2 -2
  120. package/dist/core/performance/optimized-frame-context.js +27 -12
  121. package/dist/core/performance/optimized-frame-context.js.map +2 -2
  122. package/dist/core/performance/performance-benchmark.js +10 -6
  123. package/dist/core/performance/performance-benchmark.js.map +2 -2
  124. package/dist/core/performance/performance-profiler.js +63 -15
  125. package/dist/core/performance/performance-profiler.js.map +2 -2
  126. package/dist/core/performance/streaming-jsonl-parser.js +5 -1
  127. package/dist/core/performance/streaming-jsonl-parser.js.map +2 -2
  128. package/dist/core/persistence/postgres-adapter.js.map +2 -2
  129. package/dist/core/projects/project-manager.js +14 -20
  130. package/dist/core/projects/project-manager.js.map +2 -2
  131. package/dist/core/retrieval/context-retriever.js.map +2 -2
  132. package/dist/core/retrieval/graph-retrieval.js.map +2 -2
  133. package/dist/core/retrieval/llm-context-retrieval.js.map +2 -2
  134. package/dist/core/retrieval/retrieval-benchmarks.js.map +2 -2
  135. package/dist/core/retrieval/summary-generator.js.map +2 -2
  136. package/dist/core/session/clear-survival-stub.js +5 -1
  137. package/dist/core/session/clear-survival-stub.js.map +2 -2
  138. package/dist/core/session/clear-survival.js +35 -0
  139. package/dist/core/session/clear-survival.js.map +2 -2
  140. package/dist/core/session/handoff-generator.js.map +2 -2
  141. package/dist/core/session/index.js.map +1 -1
  142. package/dist/core/session/session-manager.js +16 -5
  143. package/dist/core/session/session-manager.js.map +2 -2
  144. package/dist/core/skills/skill-storage.js +13 -2
  145. package/dist/core/skills/skill-storage.js.map +2 -2
  146. package/dist/core/storage/chromadb-adapter.js +6 -2
  147. package/dist/core/storage/chromadb-adapter.js.map +2 -2
  148. package/dist/core/storage/chromadb-simple.js +17 -5
  149. package/dist/core/storage/chromadb-simple.js.map +2 -2
  150. package/dist/core/storage/infinite-storage.js +109 -46
  151. package/dist/core/storage/infinite-storage.js.map +2 -2
  152. package/dist/core/storage/railway-optimized-storage.js +67 -30
  153. package/dist/core/storage/railway-optimized-storage.js.map +2 -2
  154. package/dist/core/storage/remote-storage.js +53 -24
  155. package/dist/core/storage/remote-storage.js.map +2 -2
  156. package/dist/core/trace/cli-trace-wrapper.js +25 -7
  157. package/dist/core/trace/cli-trace-wrapper.js.map +2 -2
  158. package/dist/core/trace/db-trace-wrapper.js +96 -68
  159. package/dist/core/trace/db-trace-wrapper.js.map +2 -2
  160. package/dist/core/trace/debug-trace.js +44 -16
  161. package/dist/core/trace/debug-trace.js.map +2 -2
  162. package/dist/core/trace/index.js +50 -35
  163. package/dist/core/trace/index.js.map +2 -2
  164. package/dist/core/trace/linear-api-wrapper.js +10 -5
  165. package/dist/core/trace/linear-api-wrapper.js.map +2 -2
  166. package/dist/core/trace/trace-demo.js +26 -11
  167. package/dist/core/trace/trace-demo.js.map +2 -2
  168. package/dist/core/trace/trace-detector.js +9 -2
  169. package/dist/core/trace/trace-detector.js.map +2 -2
  170. package/dist/core/trace/trace-store.js.map +2 -2
  171. package/dist/core/trace/types.js.map +1 -1
  172. package/dist/core/utils/compression.js.map +1 -1
  173. package/dist/core/utils/update-checker.js.map +2 -2
  174. package/dist/core/worktree/worktree-manager.js +18 -7
  175. package/dist/core/worktree/worktree-manager.js.map +2 -2
  176. package/dist/features/analytics/api/analytics-api.js.map +2 -2
  177. package/dist/features/analytics/core/analytics-service.js +12 -1
  178. package/dist/features/analytics/core/analytics-service.js.map +2 -2
  179. package/dist/features/analytics/queries/metrics-queries.js +1 -1
  180. package/dist/features/analytics/queries/metrics-queries.js.map +2 -2
  181. package/dist/features/tasks/pebbles-task-store.js.map +2 -2
  182. package/dist/features/tui/components/analytics-panel.js +36 -15
  183. package/dist/features/tui/components/analytics-panel.js.map +2 -2
  184. package/dist/features/tui/components/pr-tracker.js +19 -7
  185. package/dist/features/tui/components/pr-tracker.js.map +2 -2
  186. package/dist/features/tui/components/session-monitor.js +22 -9
  187. package/dist/features/tui/components/session-monitor.js.map +2 -2
  188. package/dist/features/tui/components/subagent-fleet.js +20 -13
  189. package/dist/features/tui/components/subagent-fleet.js.map +2 -2
  190. package/dist/features/tui/components/task-board.js +666 -2
  191. package/dist/features/tui/components/task-board.js.map +2 -2
  192. package/dist/features/tui/index.js +16 -5
  193. package/dist/features/tui/index.js.map +2 -2
  194. package/dist/features/tui/services/data-service.js +30 -15
  195. package/dist/features/tui/services/data-service.js.map +2 -2
  196. package/dist/features/tui/services/linear-task-reader.js +3 -1
  197. package/dist/features/tui/services/linear-task-reader.js.map +2 -2
  198. package/dist/features/tui/services/websocket-client.js +16 -3
  199. package/dist/features/tui/services/websocket-client.js.map +2 -2
  200. package/dist/features/tui/terminal-compat.js +33 -18
  201. package/dist/features/tui/terminal-compat.js.map +2 -2
  202. package/dist/features/web/client/stores/task-store.js.map +2 -2
  203. package/dist/features/web/server/index.js +31 -12
  204. package/dist/features/web/server/index.js.map +2 -2
  205. package/dist/integrations/claude-code/enhanced-pre-clear-hooks.js.map +2 -2
  206. package/dist/integrations/claude-code/lifecycle-hooks.js.map +2 -2
  207. package/dist/integrations/claude-code/post-task-hooks.js.map +2 -2
  208. package/dist/integrations/linear/auth.js +17 -6
  209. package/dist/integrations/linear/auth.js.map +2 -2
  210. package/dist/integrations/linear/auto-sync.js.map +2 -2
  211. package/dist/integrations/linear/client.js.map +2 -2
  212. package/dist/integrations/linear/config.js.map +2 -2
  213. package/dist/integrations/linear/migration.js.map +2 -2
  214. package/dist/integrations/linear/oauth-server.js +13 -2
  215. package/dist/integrations/linear/oauth-server.js.map +2 -2
  216. package/dist/integrations/linear/rest-client.js.map +2 -2
  217. package/dist/integrations/linear/sync-enhanced.js +202 -0
  218. package/dist/integrations/linear/sync-enhanced.js.map +7 -0
  219. package/dist/integrations/linear/sync-manager.js.map +2 -2
  220. package/dist/integrations/linear/sync-service.js +24 -14
  221. package/dist/integrations/linear/sync-service.js.map +2 -2
  222. package/dist/integrations/linear/sync.js +196 -3
  223. package/dist/integrations/linear/sync.js.map +2 -2
  224. package/dist/integrations/linear/unified-sync.js +560 -0
  225. package/dist/integrations/linear/unified-sync.js.map +7 -0
  226. package/dist/integrations/linear/webhook-handler.js +12 -1
  227. package/dist/integrations/linear/webhook-handler.js.map +2 -2
  228. package/dist/integrations/linear/webhook-server.js +29 -19
  229. package/dist/integrations/linear/webhook-server.js.map +2 -2
  230. package/dist/integrations/linear/webhook.js +12 -1
  231. package/dist/integrations/linear/webhook.js.map +2 -2
  232. package/dist/integrations/mcp/handlers/context-handlers.js.map +2 -2
  233. package/dist/integrations/mcp/handlers/linear-handlers.js.map +2 -2
  234. package/dist/integrations/mcp/handlers/skill-handlers.js +13 -2
  235. package/dist/integrations/mcp/handlers/skill-handlers.js.map +2 -2
  236. package/dist/integrations/mcp/handlers/task-handlers.js.map +2 -2
  237. package/dist/integrations/mcp/handlers/trace-handlers.js.map +2 -2
  238. package/dist/integrations/mcp/middleware/tool-scoring.js.map +2 -2
  239. package/dist/integrations/mcp/refactored-server.js +15 -4
  240. package/dist/integrations/mcp/refactored-server.js.map +2 -2
  241. package/dist/integrations/mcp/server.js +12 -1
  242. package/dist/integrations/mcp/server.js.map +2 -2
  243. package/dist/integrations/mcp/tool-definitions.js.map +2 -2
  244. package/dist/integrations/pg-aiguide/embedding-provider.js +13 -2
  245. package/dist/integrations/pg-aiguide/embedding-provider.js.map +2 -2
  246. package/dist/integrations/pg-aiguide/semantic-search.js.map +2 -2
  247. package/dist/mcp/stackmemory-mcp-server.js +1 -1
  248. package/dist/mcp/stackmemory-mcp-server.js.map +2 -2
  249. package/dist/middleware/exponential-rate-limiter.js.map +2 -2
  250. package/dist/servers/production/auth-middleware.js +13 -2
  251. package/dist/servers/production/auth-middleware.js.map +2 -2
  252. package/dist/servers/railway/index.js +22 -11
  253. package/dist/servers/railway/index.js.map +2 -2
  254. package/dist/services/config-service.js +6 -7
  255. package/dist/services/config-service.js.map +2 -2
  256. package/dist/services/context-service.js +11 -12
  257. package/dist/services/context-service.js.map +2 -2
  258. package/dist/skills/claude-skills.js +108 -3
  259. package/dist/skills/claude-skills.js.map +2 -2
  260. package/dist/skills/dashboard-launcher.js.map +2 -2
  261. package/dist/skills/repo-ingestion-skill.js +561 -0
  262. package/dist/skills/repo-ingestion-skill.js.map +7 -0
  263. package/dist/utils/env.js +46 -0
  264. package/dist/utils/env.js.map +7 -0
  265. package/dist/utils/logger.js +1 -1
  266. package/dist/utils/logger.js.map +2 -2
  267. package/package.json +5 -1
@@ -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\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) {\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) {\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) {\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) {\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) {\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) {\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) {\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) {\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) {\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) {\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) {\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) {\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) {\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) {\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) {\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) {\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) {\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) {\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) {\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) {\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) {\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) {\n throw new DatabaseError(\n `Failed to upsert task to cache: ${task.id}`,\n ErrorCode.DB_QUERY_FAILED,\n {\n taskId: task.id,\n taskTitle: task.title,\n operation: 'upsertToCache',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n private generateTaskId(content: string): string {\n const hash = createHash('sha256').update(content).digest('hex');\n return `tsk-${hash.substring(0, 8)}`;\n }\n\n private hydrateTask = (row: any): PebblesTask => ({\n ...row,\n depends_on: JSON.parse(row.depends_on || '[]'),\n blocks: JSON.parse(row.blocks || '[]'),\n tags: JSON.parse(row.tags || '[]'),\n external_refs: JSON.parse(row.external_refs || '{}'),\n });\n\n private hydrateTasks(rows: any[]): PebblesTask[] {\n return rows.map(this.hydrateTask);\n }\n\n private mapPriorityToLinear(priority: TaskPriority): number {\n const map = { low: 1, medium: 2, high: 3, urgent: 4 };\n return map[priority] || 2;\n }\n\n private mapStatusToLinear(status: TaskStatus): string {\n const map = {\n pending: 'Backlog',\n in_progress: 'In Progress',\n completed: 'Done',\n blocked: 'Blocked',\n cancelled: 'Cancelled',\n };\n return map[status] || 'Backlog';\n }\n\n /**\n * Check if adding a dependency would create a circular dependency\n */\n private wouldCreateCircularDependency(\n taskId: string,\n dependsOnId: string\n ): boolean {\n const visited = new Set<string>();\n const stack = [dependsOnId];\n\n while (stack.length > 0) {\n const currentId = stack.pop()!;\n\n if (currentId === taskId) {\n return true; // Found circular dependency\n }\n\n if (visited.has(currentId)) {\n continue;\n }\n\n visited.add(currentId);\n\n // Get dependencies of current task\n const currentTask = this.getTask(currentId);\n if (currentTask) {\n stack.push(...currentTask.depends_on);\n }\n }\n\n return false;\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Pebbles Task Storage\n * Git-native JSONL storage with SQLite cache for tasks\n */\n\nimport Database from 'better-sqlite3';\nimport { EventEmitter } from 'events';\nimport { createHash } from 'crypto';\nimport { appendFile, existsSync, mkdirSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { logger } from '../../core/monitoring/logger.js';\nimport {\n DatabaseError,\n TaskError,\n SystemError,\n ErrorCode,\n wrapError,\n createErrorHandler,\n} from '../../core/errors/index.js';\nimport { retry, withTimeout } from '../../core/errors/recovery.js';\nimport { StreamingJSONLParser } from '../../core/performance/streaming-jsonl-parser.js';\nimport { ContextCache } from '../../core/performance/context-cache.js';\n\nexport type TaskStatus =\n | 'pending'\n | 'in_progress'\n | 'completed'\n | 'blocked'\n | 'cancelled';\nexport type TaskPriority = 'low' | 'medium' | 'high' | 'urgent';\n\nexport interface PebblesTask {\n id: string; // Content-hash based (merge-friendly)\n type: 'task_create' | 'task_update' | 'task_complete' | 'task_block';\n timestamp: number;\n parent_id?: string; // For subtasks\n frame_id: string; // Associated call stack frame\n\n // Task data\n title: string;\n description?: string;\n status: TaskStatus;\n priority: TaskPriority;\n assignee?: string;\n\n // Tracking\n created_at: number;\n started_at?: number;\n completed_at?: number;\n estimated_effort?: number; // Minutes\n actual_effort?: number;\n\n // Relationships\n depends_on: string[]; // Task IDs\n blocks: string[]; // Task IDs this blocks\n tags: string[]; // For filtering\n\n // Integration hooks (for Linear phase)\n external_refs?: {\n linear?: { id: string; url: string };\n github?: { issue: number; url: string };\n };\n\n // Context relevance\n context_score?: number; // For intelligent assembly\n last_accessed?: number;\n}\n\nexport interface TaskMetrics {\n total_tasks: number;\n by_status: Record<TaskStatus, number>;\n by_priority: Record<TaskPriority, number>;\n completion_rate: number;\n avg_effort_accuracy: number;\n blocked_tasks: number;\n overdue_tasks: number;\n}\n\nexport class PebblesTaskStore extends EventEmitter {\n private db: Database.Database;\n private projectRoot: string;\n private tasksFile: string;\n private cacheFile: string;\n private jsonlParser: StreamingJSONLParser;\n private taskCache: ContextCache<PebblesTask>;\n\n constructor(projectRoot: string, db: Database.Database) {\n super();\n this.projectRoot = projectRoot;\n this.db = db;\n\n // Ensure .stackmemory directory exists\n const stackmemoryDir = join(projectRoot, '.stackmemory');\n if (!existsSync(stackmemoryDir)) {\n mkdirSync(stackmemoryDir, { recursive: true });\n }\n\n this.tasksFile = join(stackmemoryDir, 'tasks.jsonl');\n this.cacheFile = join(stackmemoryDir, 'cache.db');\n\n // Initialize performance optimizations\n this.jsonlParser = new StreamingJSONLParser();\n this.taskCache = new ContextCache<PebblesTask>({\n maxSize: 10 * 1024 * 1024, // 10MB for tasks\n maxItems: 1000,\n defaultTTL: 3600000, // 1 hour\n });\n\n this.initializeCache();\n // Load existing tasks from JSONL synchronously\n this.loadFromJSONLSync();\n }\n\n private initializeCache() {\n const errorHandler = createErrorHandler({\n operation: 'initializeCache',\n projectRoot: this.projectRoot,\n });\n\n try {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS task_cache (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n timestamp INTEGER NOT NULL,\n parent_id TEXT,\n frame_id TEXT NOT NULL,\n title TEXT NOT NULL,\n description TEXT,\n status TEXT NOT NULL,\n priority TEXT NOT NULL,\n assignee TEXT,\n created_at INTEGER NOT NULL,\n started_at INTEGER,\n completed_at INTEGER,\n estimated_effort INTEGER,\n actual_effort INTEGER,\n depends_on TEXT DEFAULT '[]',\n blocks TEXT DEFAULT '[]',\n tags TEXT DEFAULT '[]',\n external_refs TEXT DEFAULT '{}',\n context_score REAL DEFAULT 0.5,\n last_accessed INTEGER\n );\n \n CREATE INDEX IF NOT EXISTS idx_task_status ON task_cache(status);\n CREATE INDEX IF NOT EXISTS idx_task_priority ON task_cache(priority);\n CREATE INDEX IF NOT EXISTS idx_task_frame ON task_cache(frame_id);\n CREATE INDEX IF NOT EXISTS idx_task_timestamp ON task_cache(timestamp);\n CREATE INDEX IF NOT EXISTS idx_task_parent ON task_cache(parent_id);\n `);\n } catch (error: unknown) {\n const dbError = errorHandler(error, {\n operation: 'initializeCache',\n schema: 'task_cache',\n });\n\n throw new DatabaseError(\n 'Failed to initialize task cache schema',\n ErrorCode.DB_MIGRATION_FAILED,\n {\n projectRoot: this.projectRoot,\n cacheFile: this.cacheFile,\n operation: 'initializeCache',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Load existing tasks from JSONL into SQLite cache (optimized)\n */\n private async loadFromJSONL() {\n if (!existsSync(this.tasksFile)) return;\n\n const errorHandler = createErrorHandler({\n operation: 'loadFromJSONL',\n tasksFile: this.tasksFile,\n });\n\n try {\n let loaded = 0;\n let errors = 0;\n\n // Use streaming parser for memory efficiency\n for await (const batch of this.jsonlParser.parseStream<PebblesTask>(\n this.tasksFile,\n {\n batchSize: 100,\n filter: (obj) => obj.type && obj.id && obj.title, // Basic validation\n onProgress: (count) => {\n if (count % 500 === 0) {\n logger.debug('Loading tasks progress', { loaded: count });\n }\n },\n }\n )) {\n for (const task of batch) {\n try {\n this.upsertToCache(task);\n // Add to in-memory cache for fast access\n this.taskCache.set(task.id, task, {\n ttl: 3600000, // 1 hour cache\n });\n loaded++;\n } catch (error: unknown) {\n errors++;\n logger.warn('Failed to cache task', {\n taskId: task.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n\n logger.info('Loaded tasks from JSONL', {\n loaded,\n errors,\n file: this.tasksFile,\n cacheStats: this.taskCache.getStats(),\n });\n } catch (error: unknown) {\n const systemError = errorHandler(error, {\n operation: 'loadFromJSONL',\n file: this.tasksFile,\n });\n\n throw new SystemError(\n 'Failed to load tasks from JSONL file',\n ErrorCode.INTERNAL_ERROR,\n {\n tasksFile: this.tasksFile,\n operation: 'loadFromJSONL',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Load existing tasks from JSONL synchronously (for constructor)\n */\n private loadFromJSONLSync() {\n if (!existsSync(this.tasksFile)) return;\n\n try {\n const content = readFileSync(this.tasksFile, 'utf-8');\n const lines = content.split('\\n').filter((line) => line.trim());\n\n let loaded = 0;\n let errors = 0;\n\n for (const line of lines) {\n try {\n const task = JSON.parse(line) as PebblesTask;\n\n // Basic validation\n if (task.type && task.id && task.title) {\n this.upsertToCache(task);\n // Add to in-memory cache for fast access\n this.taskCache.set(task.id, task, {\n ttl: 3600000, // 1 hour cache\n });\n loaded++;\n }\n } catch (error: unknown) {\n errors++;\n logger.warn('Failed to parse JSONL line', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n logger.info('Loaded tasks from JSONL', {\n loaded,\n errors,\n file: this.tasksFile,\n });\n } catch (error: unknown) {\n logger.error('Failed to load tasks from JSONL', error as Error);\n }\n }\n\n /**\n * Create a new task with content-hash ID\n */\n public createTask(options: {\n title: string;\n description?: string;\n priority?: TaskPriority;\n frameId: string;\n parentId?: string;\n dependsOn?: string[];\n tags?: string[];\n estimatedEffort?: number;\n assignee?: string;\n }): string {\n const now = Math.floor(Date.now() / 1000);\n\n // Create content for hash (ensures deterministic ID)\n const content = `${options.title}:${options.frameId}:${now}:${Math.random()}`;\n const id = this.generateTaskId(content);\n\n const task: PebblesTask = {\n id,\n type: 'task_create',\n timestamp: now,\n parent_id: options.parentId,\n frame_id: options.frameId,\n title: options.title,\n description: options.description,\n status: 'pending',\n priority: options.priority || 'medium',\n assignee: options.assignee,\n created_at: now,\n estimated_effort: options.estimatedEffort,\n depends_on: options.dependsOn || [],\n blocks: [],\n tags: options.tags || [],\n context_score: 0.5,\n };\n\n this.appendTask(task);\n this.emit('task:created', task);\n this.emit('sync:needed', 'task:created');\n return id;\n }\n\n /**\n * Update task status with new event\n */\n public updateTaskStatus(\n taskId: string,\n newStatus: TaskStatus,\n _reason?: string\n ): void {\n const existing = this.getTask(taskId);\n if (!existing) {\n throw new TaskError(\n `Task not found: ${taskId}`,\n ErrorCode.TASK_NOT_FOUND,\n {\n taskId,\n newStatus,\n operation: 'updateTaskStatus',\n }\n );\n }\n\n // Validate status transition\n if (existing.status === 'completed' && newStatus !== 'cancelled') {\n throw new TaskError(\n `Cannot change completed task status from ${existing.status} to ${newStatus}`,\n ErrorCode.TASK_INVALID_STATE,\n {\n taskId,\n currentStatus: existing.status,\n newStatus,\n operation: 'updateTaskStatus',\n }\n );\n }\n\n const now = Math.floor(Date.now() / 1000);\n const updates: Partial<PebblesTask> = {\n status: newStatus,\n timestamp: now,\n };\n\n // Automatic time tracking\n if (newStatus === 'in_progress' && existing.status === 'pending') {\n updates.started_at = now;\n updates.type = 'task_update';\n } else if (newStatus === 'completed' && existing.status === 'in_progress') {\n updates.completed_at = now;\n updates.type = 'task_complete';\n if (existing.started_at) {\n updates.actual_effort = Math.floor((now - existing.started_at) / 60); // Minutes\n }\n } else if (newStatus === 'blocked') {\n updates.type = 'task_block';\n }\n\n const updatedTask: PebblesTask = { ...existing, ...updates };\n this.appendTask(updatedTask);\n\n if (newStatus === 'completed') {\n this.emit('task:completed', updatedTask);\n }\n this.emit('sync:needed', 'task:updated');\n }\n\n /**\n * Add dependency relationship\n */\n public addDependency(taskId: string, dependsOnId: string): void {\n const task = this.getTask(taskId);\n const dependsOnTask = this.getTask(dependsOnId);\n\n if (!task) {\n throw new TaskError(\n `Task not found: ${taskId}`,\n ErrorCode.TASK_NOT_FOUND,\n {\n taskId,\n operation: 'addDependency',\n }\n );\n }\n\n if (!dependsOnTask) {\n throw new TaskError(\n `Dependency task not found: ${dependsOnId}`,\n ErrorCode.TASK_NOT_FOUND,\n {\n dependsOnId,\n taskId,\n operation: 'addDependency',\n }\n );\n }\n\n // Check for circular dependency\n if (this.wouldCreateCircularDependency(taskId, dependsOnId)) {\n throw new TaskError(\n `Adding dependency would create circular dependency: ${taskId} -> ${dependsOnId}`,\n ErrorCode.TASK_CIRCULAR_DEPENDENCY,\n {\n taskId,\n dependsOnId,\n operation: 'addDependency',\n }\n );\n }\n\n // Update task dependencies\n const updatedTask: PebblesTask = {\n ...task,\n depends_on: [...new Set([...task.depends_on, dependsOnId])],\n timestamp: Math.floor(Date.now() / 1000),\n type: 'task_update',\n };\n\n // Update blocking task\n const updatedBlockingTask: PebblesTask = {\n ...dependsOnTask,\n blocks: [...new Set([...dependsOnTask.blocks, taskId])],\n timestamp: Math.floor(Date.now() / 1000),\n type: 'task_update',\n };\n\n this.appendTask(updatedTask);\n this.appendTask(updatedBlockingTask);\n }\n\n /**\n * Get current active tasks\n */\n public getActiveTasks(frameId?: string): PebblesTask[] {\n try {\n let query = `\n SELECT * FROM task_cache \n WHERE status IN ('pending', 'in_progress')\n `;\n const params: any[] = [];\n\n if (frameId) {\n query += ` AND frame_id = ?`;\n params.push(frameId);\n }\n\n query += ` ORDER BY priority DESC, created_at ASC`;\n\n const rows = this.db.prepare(query).all(...params) as any[];\n return this.hydrateTasks(rows);\n } catch (error: unknown) {\n throw new DatabaseError(\n 'Failed to get active tasks',\n ErrorCode.DB_QUERY_FAILED,\n {\n frameId,\n operation: 'getActiveTasks',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Get task by ID (latest version)\n */\n public getTask(taskId: string): PebblesTask | undefined {\n try {\n const row = this.db\n .prepare(\n `\n SELECT * FROM task_cache WHERE id = ?\n `\n )\n .get(taskId) as any;\n\n return row ? this.hydrateTask(row) : undefined;\n } catch (error: unknown) {\n throw new DatabaseError(\n `Failed to get task: ${taskId}`,\n ErrorCode.DB_QUERY_FAILED,\n {\n taskId,\n operation: 'getTask',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Get tasks that are blocking other tasks\n */\n public getBlockingTasks(): PebblesTask[] {\n try {\n const rows = this.db\n .prepare(\n `\n SELECT * FROM task_cache \n WHERE JSON_ARRAY_LENGTH(blocks) > 0 \n AND status NOT IN ('completed', 'cancelled')\n ORDER BY priority DESC\n `\n )\n .all() as any[];\n\n return this.hydrateTasks(rows);\n } catch (error: unknown) {\n throw new DatabaseError(\n 'Failed to get blocking tasks',\n ErrorCode.DB_QUERY_FAILED,\n {\n operation: 'getBlockingTasks',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Get metrics for current project\n */\n public getMetrics(): TaskMetrics {\n try {\n const statusCounts = this.db\n .prepare(\n `\n SELECT status, COUNT(*) as count \n FROM task_cache \n GROUP BY status\n `\n )\n .all() as { status: TaskStatus; count: number }[];\n\n const priorityCounts = this.db\n .prepare(\n `\n SELECT priority, COUNT(*) as count \n FROM task_cache \n GROUP BY priority \n `\n )\n .all() as { priority: TaskPriority; count: number }[];\n\n const totalTasks = statusCounts.reduce((sum, s) => sum + s.count, 0);\n const completedTasks =\n statusCounts.find((s) => s.status === 'completed')?.count || 0;\n const blockedTasks =\n statusCounts.find((s) => s.status === 'blocked')?.count || 0;\n\n // Calculate effort accuracy\n const effortRows = this.db\n .prepare(\n `\n SELECT estimated_effort, actual_effort \n FROM task_cache \n WHERE estimated_effort IS NOT NULL \n AND actual_effort IS NOT NULL\n `\n )\n .all() as { estimated_effort: number; actual_effort: number }[];\n\n let avgEffortAccuracy = 0;\n if (effortRows.length > 0) {\n const accuracies = effortRows.map(\n (r) =>\n 1 -\n Math.abs(r.estimated_effort - r.actual_effort) /\n Math.max(r.estimated_effort, 1)\n );\n avgEffortAccuracy =\n accuracies.reduce((sum, acc) => sum + acc, 0) / accuracies.length;\n }\n\n return {\n total_tasks: totalTasks,\n by_status: Object.fromEntries(\n statusCounts.map((s) => [s.status, s.count])\n ) as any,\n by_priority: Object.fromEntries(\n priorityCounts.map((p) => [p.priority, p.count])\n ) as any,\n completion_rate: totalTasks > 0 ? completedTasks / totalTasks : 0,\n avg_effort_accuracy: avgEffortAccuracy,\n blocked_tasks: blockedTasks,\n overdue_tasks: 0, // TODO: implement due dates\n };\n } catch (error: unknown) {\n throw new DatabaseError(\n 'Failed to get task metrics',\n ErrorCode.DB_QUERY_FAILED,\n {\n operation: 'getMetrics',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Export tasks for Linear integration (Phase 2)\n */\n public exportForLinear(): Array<{\n title: string;\n description?: string;\n priority: number;\n status: string;\n estimate?: number;\n dependencies: string[];\n }> {\n const tasks = this.db\n .prepare(\n `\n SELECT * FROM task_cache \n WHERE external_refs IS NULL OR JSON_EXTRACT(external_refs, '$.linear') IS NULL\n ORDER BY created_at ASC\n `\n )\n .all() as any[];\n\n return tasks.map((task) => ({\n title: task.title,\n description: task.description,\n priority: this.mapPriorityToLinear(task.priority),\n status: this.mapStatusToLinear(task.status),\n estimate: task.estimated_effort,\n dependencies: JSON.parse(task.depends_on || '[]'),\n }));\n }\n\n // Private methods\n private appendTask(task: PebblesTask) {\n try {\n // Append to JSONL file (git-tracked source of truth)\n const jsonLine = JSON.stringify(task) + '\\n';\n appendFile(this.tasksFile, jsonLine, (err) => {\n if (err) {\n logger.error(\n `Failed to append task ${task.id} to JSONL: ${err.message}`,\n err,\n {\n taskId: task.id,\n tasksFile: this.tasksFile,\n }\n );\n }\n });\n\n // Update SQLite cache (for fast queries) with retry logic\n retry(() => Promise.resolve(this.upsertToCache(task)), {\n maxAttempts: 3,\n initialDelay: 100,\n onRetry: (attempt, error) => {\n logger.warn(`Retrying task cache upsert (attempt ${attempt})`, {\n taskId: task.id,\n errorMessage:\n error instanceof Error ? error.message : String(error),\n });\n },\n }).catch((error) => {\n logger.error(\n 'Failed to upsert task to cache after retries',\n error instanceof Error ? error : new Error(String(error)),\n {\n taskId: task.id,\n }\n );\n throw error;\n });\n\n logger.info('Appended task', {\n id: task.id,\n type: task.type,\n title: task.title,\n status: task.status,\n });\n } catch (error: unknown) {\n throw new SystemError(\n `Failed to append task: ${task.id}`,\n ErrorCode.INTERNAL_ERROR,\n {\n taskId: task.id,\n operation: 'appendTask',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n private upsertToCache(task: PebblesTask) {\n try {\n this.db\n .prepare(\n `\n INSERT OR REPLACE INTO task_cache (\n id, type, timestamp, parent_id, frame_id, title, description,\n status, priority, assignee, created_at, started_at, completed_at,\n estimated_effort, actual_effort, depends_on, blocks, tags,\n external_refs, context_score, last_accessed\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `\n )\n .run(\n task.id,\n task.type,\n task.timestamp,\n task.parent_id,\n task.frame_id,\n task.title,\n task.description,\n task.status,\n task.priority,\n task.assignee,\n task.created_at,\n task.started_at,\n task.completed_at,\n task.estimated_effort,\n task.actual_effort,\n JSON.stringify(task.depends_on),\n JSON.stringify(task.blocks),\n JSON.stringify(task.tags),\n JSON.stringify(task.external_refs || {}),\n task.context_score,\n task.last_accessed\n );\n } catch (error: unknown) {\n throw new DatabaseError(\n `Failed to upsert task to cache: ${task.id}`,\n ErrorCode.DB_QUERY_FAILED,\n {\n taskId: task.id,\n taskTitle: task.title,\n operation: 'upsertToCache',\n },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n private generateTaskId(content: string): string {\n const hash = createHash('sha256').update(content).digest('hex');\n return `tsk-${hash.substring(0, 8)}`;\n }\n\n private hydrateTask = (row: any): PebblesTask => ({\n ...row,\n depends_on: JSON.parse(row.depends_on || '[]'),\n blocks: JSON.parse(row.blocks || '[]'),\n tags: JSON.parse(row.tags || '[]'),\n external_refs: JSON.parse(row.external_refs || '{}'),\n });\n\n private hydrateTasks(rows: any[]): PebblesTask[] {\n return rows.map(this.hydrateTask);\n }\n\n private mapPriorityToLinear(priority: TaskPriority): number {\n const map = { low: 1, medium: 2, high: 3, urgent: 4 };\n return map[priority] || 2;\n }\n\n private mapStatusToLinear(status: TaskStatus): string {\n const map = {\n pending: 'Backlog',\n in_progress: 'In Progress',\n completed: 'Done',\n blocked: 'Blocked',\n cancelled: 'Cancelled',\n };\n return map[status] || 'Backlog';\n }\n\n /**\n * Check if adding a dependency would create a circular dependency\n */\n private wouldCreateCircularDependency(\n taskId: string,\n dependsOnId: string\n ): boolean {\n const visited = new Set<string>();\n const stack = [dependsOnId];\n\n while (stack.length > 0) {\n const currentId = stack.pop()!;\n\n if (currentId === taskId) {\n return true; // Found circular dependency\n }\n\n if (visited.has(currentId)) {\n continue;\n }\n\n visited.add(currentId);\n\n // Get dependencies of current task\n const currentTask = this.getTask(currentId);\n if (currentTask) {\n stack.push(...currentTask.depends_on);\n }\n }\n\n return false;\n }\n}\n"],
5
+ "mappings": "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",
6
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 => 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": "AAMA,SAAS,oBAAoB;AAGtB,MAAM,uBAAuB,aAAa;AAAA,EACvC;AAAA;AAAA,EACA,gBACN;AAAA,EACM,OAA6B;AAAA,EAErC,YAAY,MAAW;AACrB,UAAM;AACN,SAAK,OAAO;AACZ,SAAK,aAAa;AAAA,EACpB;AAAA,EAEQ,eAAqB;AAE3B,SAAK,KAAK,OAAO,IAAI,CAAC,GAAG,GAAG,MAAM;AAChC,WAAK,YAAY;AAAA,IACnB,CAAC;AAGD,SAAK,eAAe;AAAA,EACtB;AAAA,EAEQ,cAAoB;AAC1B,UAAM,UAA4C;AAAA,MAChD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,UAAM,eAAe,QAAQ,QAAQ,KAAK,aAAa;AACvD,SAAK,gBAAgB,SAAS,eAAe,KAAK,QAAQ,MAAM;AAEhE,YAAQ,KAAK,eAAe;AAAA,MAC1B,KAAK;AACH,aAAK,eAAe;AACpB;AAAA,MACF,KAAK;AACH,aAAK,aAAa;AAClB;AAAA,MACF,KAAK;AACH,aAAK,YAAY;AACjB;AAAA,MACF,KAAK;AACH,aAAK,gBAAgB;AACrB;AAAA,IACJ;AAAA,EACF;AAAA,EAEQ,iBAAuB;AAC7B,QAAI,CAAC,KAAK,KAAM;AAEhB,UAAM,OAAO;AAAA,MACX;AAAA,QACE,OAAO;AAAA,QACP,GAAG,KAAK,KAAK,OAAO,OAAO,IAAI,CAAC,GAAG,MAAM,EAAE,SAAS,CAAC;AAAA,QACrD,GAAG,KAAK,KAAK,OAAO;AAAA,QACpB,OAAO,EAAE,MAAM,SAAS;AAAA,MAC1B;AAAA,IACF;AAEA,SAAK,KAAK,QAAQ,IAAI;AACtB,QAAI,OAAO,KAAK,KAAK,aAAa,YAAY;AAC5C,WAAK,KAAK,SAAS,+CAAwC;AAAA,IAC7D;AACA,SAAK,KAAK,OAAO,OAAO;AAAA,EAC1B;AAAA,EAEQ,eAAqB;AAC3B,QAAI,CAAC,KAAK,KAAM;AAEhB,UAAM,OAAO;AAAA,MACX;AAAA,QACE,OAAO;AAAA,QACP,GAAG,KAAK,KAAK,MAAM,SAAS,IAAI,CAAC,GAAG,MAAM,UAAU,IAAI,CAAC,EAAE;AAAA,QAC3D,GAAG,KAAK,KAAK,MAAM;AAAA,QACnB,OAAO,EAAE,MAAM,QAAQ;AAAA,MACzB;AAAA,IACF;AAEA,SAAK,KAAK,QAAQ,IAAI;AACtB,QAAI,OAAO,KAAK,KAAK,aAAa,YAAY;AAC5C,WAAK,KAAK,SAAS,iDAA0C;AAAA,IAC/D;AACA,SAAK,KAAK,OAAO,OAAO;AAAA,EAC1B;AAAA,EAEQ,cAAoB;AAC1B,QAAI,CAAC,KAAK,KAAM;AAEhB,UAAM,OAAO;AAAA,MACX;AAAA,QACE,OAAO;AAAA,QACP,GAAG,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG;AAAA,QAC3B,GAAG;AAAA,UACD,KAAK,KAAK,QAAQ;AAAA,UAClB,KAAK,KAAK,QAAQ;AAAA,UAClB,KAAK,KAAK,QAAQ;AAAA,UAClB,KAAK,KAAK,QAAQ;AAAA,UAClB,KAAK,KAAK,QAAQ;AAAA,QACpB;AAAA,QACA,OAAO,EAAE,MAAM,QAAQ;AAAA,MACzB;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,GAAG,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG;AAAA,QAC3B,GAAG;AAAA,UACD,KAAK,KAAK,QAAQ;AAAA,UAClB,KAAK,KAAK,QAAQ;AAAA,UAClB,KAAK,KAAK,QAAQ;AAAA,UAClB,KAAK,KAAK,QAAQ;AAAA,UAClB,KAAK,KAAK,QAAQ;AAAA,QACpB;AAAA,QACA,OAAO,EAAE,MAAM,OAAO;AAAA,MACxB;AAAA,IACF;AAEA,SAAK,KAAK,QAAQ,IAAI;AACtB,QAAI,OAAO,KAAK,KAAK,aAAa,YAAY;AAC5C,WAAK,KAAK,SAAS,gDAAyC;AAAA,IAC9D;AACA,SAAK,KAAK,OAAO,OAAO;AAAA,EAC1B;AAAA,EAEQ,kBAAwB;AAC9B,QAAI,CAAC,KAAK,KAAM;AAEhB,UAAM,OAAO;AAAA,MACX;AAAA,QACE,OAAO;AAAA,QACP,GAAG,KAAK,KAAK,YAAY,gBAAgB,IAAI,CAAC,GAAG,MAAM,EAAE,SAAS,CAAC;AAAA,QACnE,GAAG,KAAK,KAAK,YAAY;AAAA,QACzB,OAAO,EAAE,MAAM,OAAO;AAAA,MACxB;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,GAAG,KAAK,KAAK,YAAY,UAAU,IAAI,CAAC,GAAG,MAAM,EAAE,SAAS,CAAC;AAAA,QAC7D,GAAG,KAAK,KAAK,YAAY,UAAU,IAAI,CAAC,MAAW,IAAI,GAAG;AAAA,QAC1D,OAAO,EAAE,MAAM,MAAM;AAAA,MACvB;AAAA,IACF;AAEA,SAAK,KAAK,QAAQ,IAAI;AACtB,QAAI,OAAO,KAAK,KAAK,aAAa,YAAY;AAC5C,WAAK,KAAK,SAAS,+CAAwC;AAAA,IAC7D;AACA,SAAK,KAAK,OAAO,OAAO;AAAA,EAC1B;AAAA,EAEO,OAAO,MAA2B;AACvC,SAAK,OAAO;AAGZ,YAAQ,KAAK,eAAe;AAAA,MAC1B,KAAK;AACH,aAAK,eAAe;AACpB;AAAA,MACF,KAAK;AACH,aAAK,aAAa;AAClB;AAAA,MACF,KAAK;AACH,aAAK,YAAY;AACjB;AAAA,MACF,KAAK;AACH,aAAK,gBAAgB;AACrB;AAAA,IACJ;AAAA,EACF;AAAA,EAEO,QAAc;AAEnB,SAAK,KAAK,SAAS;AAAA,EACrB;AAAA,EAEO,WAAoB;AACzB,WAAO;AAAA,EACT;AACF;",
6
6
  "names": []
7
7
  }
@@ -54,13 +54,17 @@ class PRTracker extends EventEmitter {
54
54
  }
55
55
  getChecksStatus(checks) {
56
56
  if (!checks) return "{gray-fg}no checks{/}";
57
- if (checks.failed > 0) return `{red-fg}\u2717 ${checks.failed}/${checks.total}{/}`;
58
- if (checks.pending > 0) return `{yellow-fg}\u23F3 ${checks.pending}/${checks.total}{/}`;
57
+ if (checks.failed > 0)
58
+ return `{red-fg}\u2717 ${checks.failed}/${checks.total}{/}`;
59
+ if (checks.pending > 0)
60
+ return `{yellow-fg}\u23F3 ${checks.pending}/${checks.total}{/}`;
59
61
  return `{green-fg}\u2713 ${checks.passed}/${checks.total}{/}`;
60
62
  }
61
63
  getReviewStatus(reviews) {
62
64
  const approved = reviews.filter((r) => r.state === "approved").length;
63
- const changes = reviews.filter((r) => r.state === "changes_requested").length;
65
+ const changes = reviews.filter(
66
+ (r) => r.state === "changes_requested"
67
+ ).length;
64
68
  if (changes > 0) return `{red-fg}\u{1F44E}${changes}{/}`;
65
69
  if (approved > 0) return `{green-fg}\u{1F44D}${approved}{/}`;
66
70
  return "{gray-fg}no reviews{/}";
@@ -80,12 +84,20 @@ class PRTracker extends EventEmitter {
80
84
  let items = [];
81
85
  let label = "";
82
86
  if (this.viewMode === "prs") {
83
- items = Array.from(this.prs.values()).map((pr) => this.formatPRItem(pr));
84
- const open = Array.from(this.prs.values()).filter((pr) => pr.state === "open").length;
87
+ items = Array.from(this.prs.values()).map(
88
+ (pr) => this.formatPRItem(pr)
89
+ );
90
+ const open = Array.from(this.prs.values()).filter(
91
+ (pr) => pr.state === "open"
92
+ ).length;
85
93
  label = ` \u{1F500} Pull Requests (${open}/${this.prs.size}) [Tab] Issues `;
86
94
  } else {
87
- items = Array.from(this.issues.values()).map((issue) => this.formatIssueItem(issue));
88
- const open = Array.from(this.issues.values()).filter((i) => i.state === "open").length;
95
+ items = Array.from(this.issues.values()).map(
96
+ (issue) => this.formatIssueItem(issue)
97
+ );
98
+ const open = Array.from(this.issues.values()).filter(
99
+ (i) => i.state === "open"
100
+ ).length;
89
101
  label = ` \u{1F41B} Issues (${open}/${this.issues.size}) [Tab] PRs `;
90
102
  }
91
103
  this.list.setItems(items);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/features/tui/components/pr-tracker.ts"],
4
- "sourcesContent": ["/**\n * PR/Issue Tracker Component\n * Displays GitHub PRs and issues with status indicators\n */\n\nimport blessed from 'blessed';\nimport { EventEmitter } from 'events';\nimport type { PRData, IssueData } from '../types.js';\n\nexport class PRTracker extends EventEmitter {\n private list: blessed.Widgets.ListElement;\n private prs: Map<string, PRData>;\n private issues: Map<string, IssueData>;\n private selectedItem: string | null = null;\n private viewMode: 'prs' | 'issues' = 'prs';\n\n constructor(list: blessed.Widgets.ListElement) {\n super();\n this.list = list;\n this.prs = new Map();\n this.issues = new Map();\n this.initializeUI();\n }\n\n private initializeUI(): void {\n this.list.on('select', (item, index) => {\n if (this.viewMode === 'prs') {\n const prId = Array.from(this.prs.keys())[index];\n this.selectPR(prId);\n } else {\n const issueId = Array.from(this.issues.keys())[index];\n this.selectIssue(issueId);\n }\n });\n\n // Toggle between PRs and Issues\n this.list.key(['tab'], () => {\n this.toggleView();\n });\n }\n\n private formatPRItem(pr: PRData): string {\n const status = this.getPRStatusIcon(pr);\n const checks = this.getChecksStatus(pr.checks);\n const reviews = this.getReviewStatus(pr.reviews);\n \n let item = `${status} #${pr.number}: ${pr.title}\\n`;\n item += ` {gray-fg}@${pr.author.login} | ${checks} | ${reviews} | +${pr.additions}/-${pr.deletions}{/}`;\n \n if (pr.linkedIssues?.length) {\n item += ` | {cyan-fg}\uD83D\uDD17${pr.linkedIssues.length}{/}`;\n }\n \n return item;\n }\n\n private formatIssueItem(issue: IssueData): string {\n const status = issue.state === 'open' ? '{green-fg}\u25CF{/}' : '{gray-fg}\u25CF{/}';\n const assignees = issue.assignees?.map(a => `@${a}`).join(', ') || 'unassigned';\n \n let item = `${status} #${issue.number}: ${issue.title}\\n`;\n item += ` {gray-fg}@${issue.author.login} | ${assignees} | \uD83D\uDCAC${issue.comments}{/}`;\n \n return item;\n }\n\n private getPRStatusIcon(pr: PRData): string {\n if (pr.state === 'merged') return '{magenta-fg}\u2B24{/}';\n if (pr.state === 'closed') return '{red-fg}\u2B24{/}';\n if (pr.draft) return '{gray-fg}\u25CB{/}';\n return '{green-fg}\u25CF{/}';\n }\n\n private getChecksStatus(checks?: PRData['checks']): string {\n if (!checks) return '{gray-fg}no checks{/}';\n if (checks.failed > 0) return `{red-fg}\u2717 ${checks.failed}/${checks.total}{/}`;\n if (checks.pending > 0) return `{yellow-fg}\u23F3 ${checks.pending}/${checks.total}{/}`;\n return `{green-fg}\u2713 ${checks.passed}/${checks.total}{/}`;\n }\n\n private getReviewStatus(reviews: PRData['reviews']): string {\n const approved = reviews.filter(r => r.state === 'approved').length;\n const changes = reviews.filter(r => r.state === 'changes_requested').length;\n \n if (changes > 0) return `{red-fg}\uD83D\uDC4E${changes}{/}`;\n if (approved > 0) return `{green-fg}\uD83D\uDC4D${approved}{/}`;\n return '{gray-fg}no reviews{/}';\n }\n\n public update(data: { prs?: PRData[], issues?: IssueData[] }): void {\n if (data.prs) {\n this.prs.clear();\n data.prs.forEach(pr => this.prs.set(pr.id, pr));\n }\n \n if (data.issues) {\n this.issues.clear();\n data.issues.forEach(issue => this.issues.set(issue.id, issue));\n }\n \n this.refreshDisplay();\n }\n\n private refreshDisplay(): void {\n let items: string[] = [];\n let label = '';\n \n if (this.viewMode === 'prs') {\n items = Array.from(this.prs.values()).map(pr => this.formatPRItem(pr));\n const open = Array.from(this.prs.values()).filter(pr => pr.state === 'open').length;\n label = ` \uD83D\uDD00 Pull Requests (${open}/${this.prs.size}) [Tab] Issues `;\n } else {\n items = Array.from(this.issues.values()).map(issue => this.formatIssueItem(issue));\n const open = Array.from(this.issues.values()).filter(i => i.state === 'open').length;\n label = ` \uD83D\uDC1B Issues (${open}/${this.issues.size}) [Tab] PRs `;\n }\n \n this.list.setItems(items);\n if (this.list.parent && typeof (this.list.parent as any).setLabel === 'function') {\n (this.list.parent as any).setLabel(label);\n }\n \n this.list.screen.render();\n }\n\n private toggleView(): void {\n this.viewMode = this.viewMode === 'prs' ? 'issues' : 'prs';\n this.refreshDisplay();\n }\n\n private selectPR(prId: string): void {\n const pr = this.prs.get(prId);\n if (pr) {\n this.emit('pr:selected', pr);\n }\n }\n\n private selectIssue(issueId: string): void {\n const issue = this.issues.get(issueId);\n if (issue) {\n this.emit('issue:selected', issue);\n }\n }\n\n public focus(): void {\n this.list.focus();\n }\n\n public hasFocus(): boolean {\n return this.list === this.list.screen.focused;\n }\n}"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * PR/Issue Tracker Component\n * Displays GitHub PRs and issues with status indicators\n */\n\nimport blessed from 'blessed';\nimport { EventEmitter } from 'events';\nimport type { PRData, IssueData } from '../types.js';\n\nexport class PRTracker extends EventEmitter {\n private list: blessed.Widgets.ListElement;\n private prs: Map<string, PRData>;\n private issues: Map<string, IssueData>;\n private selectedItem: string | null = null;\n private viewMode: 'prs' | 'issues' = 'prs';\n\n constructor(list: blessed.Widgets.ListElement) {\n super();\n this.list = list;\n this.prs = new Map();\n this.issues = new Map();\n this.initializeUI();\n }\n\n private initializeUI(): void {\n this.list.on('select', (item, index) => {\n if (this.viewMode === 'prs') {\n const prId = Array.from(this.prs.keys())[index];\n this.selectPR(prId);\n } else {\n const issueId = Array.from(this.issues.keys())[index];\n this.selectIssue(issueId);\n }\n });\n\n // Toggle between PRs and Issues\n this.list.key(['tab'], () => {\n this.toggleView();\n });\n }\n\n private formatPRItem(pr: PRData): string {\n const status = this.getPRStatusIcon(pr);\n const checks = this.getChecksStatus(pr.checks);\n const reviews = this.getReviewStatus(pr.reviews);\n\n let item = `${status} #${pr.number}: ${pr.title}\\n`;\n item += ` {gray-fg}@${pr.author.login} | ${checks} | ${reviews} | +${pr.additions}/-${pr.deletions}{/}`;\n\n if (pr.linkedIssues?.length) {\n item += ` | {cyan-fg}\uD83D\uDD17${pr.linkedIssues.length}{/}`;\n }\n\n return item;\n }\n\n private formatIssueItem(issue: IssueData): string {\n const status = issue.state === 'open' ? '{green-fg}\u25CF{/}' : '{gray-fg}\u25CF{/}';\n const assignees =\n issue.assignees?.map((a: any) => `@${a}`).join(', ') || 'unassigned';\n\n let item = `${status} #${issue.number}: ${issue.title}\\n`;\n item += ` {gray-fg}@${issue.author.login} | ${assignees} | \uD83D\uDCAC${issue.comments}{/}`;\n\n return item;\n }\n\n private getPRStatusIcon(pr: PRData): string {\n if (pr.state === 'merged') return '{magenta-fg}\u2B24{/}';\n if (pr.state === 'closed') return '{red-fg}\u2B24{/}';\n if (pr.draft) return '{gray-fg}\u25CB{/}';\n return '{green-fg}\u25CF{/}';\n }\n\n private getChecksStatus(checks?: PRData['checks']): string {\n if (!checks) return '{gray-fg}no checks{/}';\n if (checks.failed > 0)\n return `{red-fg}\u2717 ${checks.failed}/${checks.total}{/}`;\n if (checks.pending > 0)\n return `{yellow-fg}\u23F3 ${checks.pending}/${checks.total}{/}`;\n return `{green-fg}\u2713 ${checks.passed}/${checks.total}{/}`;\n }\n\n private getReviewStatus(reviews: PRData['reviews']): string {\n const approved = reviews.filter((r: any) => r.state === 'approved').length;\n const changes = reviews.filter(\n (r: any) => r.state === 'changes_requested'\n ).length;\n\n if (changes > 0) return `{red-fg}\uD83D\uDC4E${changes}{/}`;\n if (approved > 0) return `{green-fg}\uD83D\uDC4D${approved}{/}`;\n return '{gray-fg}no reviews{/}';\n }\n\n public update(data: { prs?: PRData[]; issues?: IssueData[] }): void {\n if (data.prs) {\n this.prs.clear();\n data.prs.forEach((pr) => this.prs.set(pr.id, pr));\n }\n\n if (data.issues) {\n this.issues.clear();\n data.issues.forEach((issue) => this.issues.set(issue.id, issue));\n }\n\n this.refreshDisplay();\n }\n\n private refreshDisplay(): void {\n let items: string[] = [];\n let label = '';\n\n if (this.viewMode === 'prs') {\n items = Array.from(this.prs.values()).map((pr: any) =>\n this.formatPRItem(pr)\n );\n const open = Array.from(this.prs.values()).filter(\n (pr: any) => pr.state === 'open'\n ).length;\n label = ` \uD83D\uDD00 Pull Requests (${open}/${this.prs.size}) [Tab] Issues `;\n } else {\n items = Array.from(this.issues.values()).map((issue: any) =>\n this.formatIssueItem(issue)\n );\n const open = Array.from(this.issues.values()).filter(\n (i: any) => i.state === 'open'\n ).length;\n label = ` \uD83D\uDC1B Issues (${open}/${this.issues.size}) [Tab] PRs `;\n }\n\n this.list.setItems(items);\n if (\n this.list.parent &&\n typeof (this.list.parent as any).setLabel === 'function'\n ) {\n (this.list.parent as any).setLabel(label);\n }\n\n this.list.screen.render();\n }\n\n private toggleView(): void {\n this.viewMode = this.viewMode === 'prs' ? 'issues' : 'prs';\n this.refreshDisplay();\n }\n\n private selectPR(prId: string): void {\n const pr = this.prs.get(prId);\n if (pr) {\n this.emit('pr:selected', pr);\n }\n }\n\n private selectIssue(issueId: string): void {\n const issue = this.issues.get(issueId);\n if (issue) {\n this.emit('issue:selected', issue);\n }\n }\n\n public focus(): void {\n this.list.focus();\n }\n\n public hasFocus(): boolean {\n return this.list === this.list.screen.focused;\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }