@stackmemoryai/stackmemory 0.5.23 → 0.5.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/cli/claude-sm.js +2 -0
  2. package/dist/cli/claude-sm.js.map +2 -2
  3. package/dist/cli/commands/discovery.js +279 -0
  4. package/dist/cli/commands/discovery.js.map +7 -0
  5. package/dist/cli/index.js +2 -0
  6. package/dist/cli/index.js.map +2 -2
  7. package/dist/core/retrieval/llm-context-retrieval.js +1 -3
  8. package/dist/core/retrieval/llm-context-retrieval.js.map +3 -3
  9. package/dist/integrations/mcp/handlers/discovery-handlers.js +497 -0
  10. package/dist/integrations/mcp/handlers/discovery-handlers.js.map +7 -0
  11. package/dist/integrations/mcp/handlers/index.js +40 -12
  12. package/dist/integrations/mcp/handlers/index.js.map +2 -2
  13. package/dist/integrations/mcp/server.js +270 -0
  14. package/dist/integrations/mcp/server.js.map +2 -2
  15. package/dist/integrations/mcp/tool-definitions.js +141 -5
  16. package/dist/integrations/mcp/tool-definitions.js.map +2 -2
  17. package/package.json +1 -1
  18. package/dist/cli/commands/agent.js +0 -286
  19. package/dist/cli/commands/agent.js.map +0 -7
  20. package/dist/cli/commands/chromadb.js +0 -482
  21. package/dist/cli/commands/chromadb.js.map +0 -7
  22. package/dist/cli/commands/gc.js +0 -251
  23. package/dist/cli/commands/gc.js.map +0 -7
  24. package/dist/cli/commands/infinite-storage.js +0 -292
  25. package/dist/cli/commands/infinite-storage.js.map +0 -7
  26. package/dist/cli/commands/linear-create.js +0 -171
  27. package/dist/cli/commands/linear-create.js.map +0 -7
  28. package/dist/cli/commands/linear-list.js +0 -103
  29. package/dist/cli/commands/linear-list.js.map +0 -7
  30. package/dist/cli/commands/linear-migrate.js +0 -64
  31. package/dist/cli/commands/linear-migrate.js.map +0 -7
  32. package/dist/cli/commands/linear-test.js +0 -134
  33. package/dist/cli/commands/linear-test.js.map +0 -7
  34. package/dist/cli/commands/tui.js +0 -77
  35. package/dist/cli/commands/tui.js.map +0 -7
  36. package/dist/cli/commands/webhook.js +0 -181
  37. package/dist/cli/commands/webhook.js.map +0 -7
  38. package/dist/cli/streamlined-cli.js +0 -144
  39. package/dist/cli/streamlined-cli.js.map +0 -7
  40. package/dist/core/events/event-bus.js +0 -110
  41. package/dist/core/events/event-bus.js.map +0 -7
  42. package/dist/core/frame/workflow-templates-stub.js +0 -42
  43. package/dist/core/frame/workflow-templates-stub.js.map +0 -7
  44. package/dist/core/plugins/plugin-interface.js +0 -87
  45. package/dist/core/plugins/plugin-interface.js.map +0 -7
  46. package/dist/core/session/clear-survival-stub.js +0 -53
  47. package/dist/core/session/clear-survival-stub.js.map +0 -7
  48. package/dist/core/storage/chromadb-simple.js +0 -172
  49. package/dist/core/storage/chromadb-simple.js.map +0 -7
  50. package/dist/core/storage/simplified-storage.js +0 -328
  51. package/dist/core/storage/simplified-storage.js.map +0 -7
  52. package/dist/features/tasks/pebbles-task-store.js +0 -647
  53. package/dist/features/tasks/pebbles-task-store.js.map +0 -7
  54. package/dist/integrations/linear/sync-enhanced.js +0 -202
  55. package/dist/integrations/linear/sync-enhanced.js.map +0 -7
  56. package/dist/plugins/linear/index.js +0 -166
  57. package/dist/plugins/linear/index.js.map +0 -7
  58. package/dist/plugins/loader.js +0 -57
  59. package/dist/plugins/loader.js.map +0 -7
  60. package/dist/plugins/plugin-interface.js +0 -67
  61. package/dist/plugins/plugin-interface.js.map +0 -7
  62. package/dist/plugins/ralph/simple-ralph-plugin.js +0 -305
  63. package/dist/plugins/ralph/simple-ralph-plugin.js.map +0 -7
  64. package/dist/plugins/ralph/use-cases/code-generator.js +0 -151
  65. package/dist/plugins/ralph/use-cases/code-generator.js.map +0 -7
  66. package/dist/plugins/ralph/use-cases/test-generator.js +0 -201
  67. package/dist/plugins/ralph/use-cases/test-generator.js.map +0 -7
  68. package/dist/utils/logger.js +0 -52
  69. package/dist/utils/logger.js.map +0 -7
@@ -1,647 +0,0 @@
1
- import { EventEmitter } from "events";
2
- import { createHash } from "crypto";
3
- import { appendFile, existsSync, mkdirSync, readFileSync } from "fs";
4
- import { join } from "path";
5
- import { logger } from "../../core/monitoring/logger.js";
6
- import {
7
- DatabaseError,
8
- TaskError,
9
- SystemError,
10
- ErrorCode,
11
- createErrorHandler
12
- } from "../../core/errors/index.js";
13
- import { retry } from "../../core/errors/recovery.js";
14
- import { StreamingJSONLParser } from "../../core/performance/streaming-jsonl-parser.js";
15
- import { ContextCache } from "../../core/performance/context-cache.js";
16
- class PebblesTaskStore extends EventEmitter {
17
- db;
18
- projectRoot;
19
- tasksFile;
20
- cacheFile;
21
- jsonlParser;
22
- taskCache;
23
- constructor(projectRoot, db) {
24
- super();
25
- this.projectRoot = projectRoot;
26
- this.db = db;
27
- const stackmemoryDir = join(projectRoot, ".stackmemory");
28
- if (!existsSync(stackmemoryDir)) {
29
- mkdirSync(stackmemoryDir, { recursive: true });
30
- }
31
- this.tasksFile = join(stackmemoryDir, "tasks.jsonl");
32
- this.cacheFile = join(stackmemoryDir, "cache.db");
33
- this.jsonlParser = new StreamingJSONLParser();
34
- this.taskCache = new ContextCache({
35
- maxSize: 10 * 1024 * 1024,
36
- // 10MB for tasks
37
- maxItems: 1e3,
38
- defaultTTL: 36e5
39
- // 1 hour
40
- });
41
- this.initializeCache();
42
- this.loadFromJSONLSync();
43
- }
44
- initializeCache() {
45
- const errorHandler = createErrorHandler({
46
- operation: "initializeCache",
47
- projectRoot: this.projectRoot
48
- });
49
- try {
50
- this.db.exec(`
51
- CREATE TABLE IF NOT EXISTS task_cache (
52
- id TEXT PRIMARY KEY,
53
- type TEXT NOT NULL,
54
- timestamp INTEGER NOT NULL,
55
- parent_id TEXT,
56
- frame_id TEXT NOT NULL,
57
- title TEXT NOT NULL,
58
- description TEXT,
59
- status TEXT NOT NULL,
60
- priority TEXT NOT NULL,
61
- assignee TEXT,
62
- created_at INTEGER NOT NULL,
63
- started_at INTEGER,
64
- completed_at INTEGER,
65
- estimated_effort INTEGER,
66
- actual_effort INTEGER,
67
- depends_on TEXT DEFAULT '[]',
68
- blocks TEXT DEFAULT '[]',
69
- tags TEXT DEFAULT '[]',
70
- external_refs TEXT DEFAULT '{}',
71
- context_score REAL DEFAULT 0.5,
72
- last_accessed INTEGER
73
- );
74
-
75
- CREATE INDEX IF NOT EXISTS idx_task_status ON task_cache(status);
76
- CREATE INDEX IF NOT EXISTS idx_task_priority ON task_cache(priority);
77
- CREATE INDEX IF NOT EXISTS idx_task_frame ON task_cache(frame_id);
78
- CREATE INDEX IF NOT EXISTS idx_task_timestamp ON task_cache(timestamp);
79
- CREATE INDEX IF NOT EXISTS idx_task_parent ON task_cache(parent_id);
80
- `);
81
- } catch (error) {
82
- const dbError = errorHandler(error, {
83
- operation: "initializeCache",
84
- schema: "task_cache"
85
- });
86
- throw new DatabaseError(
87
- "Failed to initialize task cache schema",
88
- ErrorCode.DB_MIGRATION_FAILED,
89
- {
90
- projectRoot: this.projectRoot,
91
- cacheFile: this.cacheFile,
92
- operation: "initializeCache"
93
- },
94
- error instanceof Error ? error : void 0
95
- );
96
- }
97
- }
98
- /**
99
- * Load existing tasks from JSONL into SQLite cache (optimized)
100
- */
101
- async loadFromJSONL() {
102
- if (!existsSync(this.tasksFile)) return;
103
- const errorHandler = createErrorHandler({
104
- operation: "loadFromJSONL",
105
- tasksFile: this.tasksFile
106
- });
107
- try {
108
- let loaded = 0;
109
- let errors = 0;
110
- for await (const batch of this.jsonlParser.parseStream(
111
- this.tasksFile,
112
- {
113
- batchSize: 100,
114
- filter: (obj) => obj.type && obj.id && obj.title,
115
- // Basic validation
116
- onProgress: (count) => {
117
- if (count % 500 === 0) {
118
- logger.debug("Loading tasks progress", { loaded: count });
119
- }
120
- }
121
- }
122
- )) {
123
- for (const task of batch) {
124
- try {
125
- this.upsertToCache(task);
126
- this.taskCache.set(task.id, task, {
127
- ttl: 36e5
128
- // 1 hour cache
129
- });
130
- loaded++;
131
- } catch (error) {
132
- errors++;
133
- logger.warn("Failed to cache task", {
134
- taskId: task.id,
135
- error: error instanceof Error ? error.message : String(error)
136
- });
137
- }
138
- }
139
- }
140
- logger.info("Loaded tasks from JSONL", {
141
- loaded,
142
- errors,
143
- file: this.tasksFile,
144
- cacheStats: this.taskCache.getStats()
145
- });
146
- } catch (error) {
147
- const systemError = errorHandler(error, {
148
- operation: "loadFromJSONL",
149
- file: this.tasksFile
150
- });
151
- throw new SystemError(
152
- "Failed to load tasks from JSONL file",
153
- ErrorCode.INTERNAL_ERROR,
154
- {
155
- tasksFile: this.tasksFile,
156
- operation: "loadFromJSONL"
157
- },
158
- error instanceof Error ? error : void 0
159
- );
160
- }
161
- }
162
- /**
163
- * Load existing tasks from JSONL synchronously (for constructor)
164
- */
165
- loadFromJSONLSync() {
166
- if (!existsSync(this.tasksFile)) return;
167
- try {
168
- const content = readFileSync(this.tasksFile, "utf-8");
169
- const lines = content.split("\n").filter((line) => line.trim());
170
- let loaded = 0;
171
- let errors = 0;
172
- for (const line of lines) {
173
- try {
174
- const task = JSON.parse(line);
175
- if (task.type && task.id && task.title) {
176
- this.upsertToCache(task);
177
- this.taskCache.set(task.id, task, {
178
- ttl: 36e5
179
- // 1 hour cache
180
- });
181
- loaded++;
182
- }
183
- } catch (error) {
184
- errors++;
185
- logger.warn("Failed to parse JSONL line", {
186
- error: error instanceof Error ? error.message : String(error)
187
- });
188
- }
189
- }
190
- logger.info("Loaded tasks from JSONL", {
191
- loaded,
192
- errors,
193
- file: this.tasksFile
194
- });
195
- } catch (error) {
196
- logger.error("Failed to load tasks from JSONL", error);
197
- }
198
- }
199
- /**
200
- * Create a new task with content-hash ID
201
- */
202
- createTask(options) {
203
- const now = Math.floor(Date.now() / 1e3);
204
- const content = `${options.title}:${options.frameId}:${now}:${Math.random()}`;
205
- const id = this.generateTaskId(content);
206
- const task = {
207
- id,
208
- type: "task_create",
209
- timestamp: now,
210
- parent_id: options.parentId,
211
- frame_id: options.frameId,
212
- title: options.title,
213
- description: options.description,
214
- status: "pending",
215
- priority: options.priority || "medium",
216
- assignee: options.assignee,
217
- created_at: now,
218
- estimated_effort: options.estimatedEffort,
219
- depends_on: options.dependsOn || [],
220
- blocks: [],
221
- tags: options.tags || [],
222
- context_score: 0.5
223
- };
224
- this.appendTask(task);
225
- this.emit("task:created", task);
226
- this.emit("sync:needed", "task:created");
227
- return id;
228
- }
229
- /**
230
- * Update task status with new event
231
- */
232
- updateTaskStatus(taskId, newStatus, _reason) {
233
- const existing = this.getTask(taskId);
234
- if (!existing) {
235
- throw new TaskError(
236
- `Task not found: ${taskId}`,
237
- ErrorCode.TASK_NOT_FOUND,
238
- {
239
- taskId,
240
- newStatus,
241
- operation: "updateTaskStatus"
242
- }
243
- );
244
- }
245
- if (existing.status === "completed" && newStatus !== "cancelled") {
246
- throw new TaskError(
247
- `Cannot change completed task status from ${existing.status} to ${newStatus}`,
248
- ErrorCode.TASK_INVALID_STATE,
249
- {
250
- taskId,
251
- currentStatus: existing.status,
252
- newStatus,
253
- operation: "updateTaskStatus"
254
- }
255
- );
256
- }
257
- const now = Math.floor(Date.now() / 1e3);
258
- const updates = {
259
- status: newStatus,
260
- timestamp: now
261
- };
262
- if (newStatus === "in_progress" && existing.status === "pending") {
263
- updates.started_at = now;
264
- updates.type = "task_update";
265
- } else if (newStatus === "completed" && existing.status === "in_progress") {
266
- updates.completed_at = now;
267
- updates.type = "task_complete";
268
- if (existing.started_at) {
269
- updates.actual_effort = Math.floor((now - existing.started_at) / 60);
270
- }
271
- } else if (newStatus === "blocked") {
272
- updates.type = "task_block";
273
- }
274
- const updatedTask = { ...existing, ...updates };
275
- this.appendTask(updatedTask);
276
- if (newStatus === "completed") {
277
- this.emit("task:completed", updatedTask);
278
- }
279
- this.emit("sync:needed", "task:updated");
280
- }
281
- /**
282
- * Add dependency relationship
283
- */
284
- addDependency(taskId, dependsOnId) {
285
- const task = this.getTask(taskId);
286
- const dependsOnTask = this.getTask(dependsOnId);
287
- if (!task) {
288
- throw new TaskError(
289
- `Task not found: ${taskId}`,
290
- ErrorCode.TASK_NOT_FOUND,
291
- {
292
- taskId,
293
- operation: "addDependency"
294
- }
295
- );
296
- }
297
- if (!dependsOnTask) {
298
- throw new TaskError(
299
- `Dependency task not found: ${dependsOnId}`,
300
- ErrorCode.TASK_NOT_FOUND,
301
- {
302
- dependsOnId,
303
- taskId,
304
- operation: "addDependency"
305
- }
306
- );
307
- }
308
- if (this.wouldCreateCircularDependency(taskId, dependsOnId)) {
309
- throw new TaskError(
310
- `Adding dependency would create circular dependency: ${taskId} -> ${dependsOnId}`,
311
- ErrorCode.TASK_CIRCULAR_DEPENDENCY,
312
- {
313
- taskId,
314
- dependsOnId,
315
- operation: "addDependency"
316
- }
317
- );
318
- }
319
- const updatedTask = {
320
- ...task,
321
- depends_on: [.../* @__PURE__ */ new Set([...task.depends_on, dependsOnId])],
322
- timestamp: Math.floor(Date.now() / 1e3),
323
- type: "task_update"
324
- };
325
- const updatedBlockingTask = {
326
- ...dependsOnTask,
327
- blocks: [.../* @__PURE__ */ new Set([...dependsOnTask.blocks, taskId])],
328
- timestamp: Math.floor(Date.now() / 1e3),
329
- type: "task_update"
330
- };
331
- this.appendTask(updatedTask);
332
- this.appendTask(updatedBlockingTask);
333
- }
334
- /**
335
- * Get current active tasks
336
- */
337
- getActiveTasks(frameId) {
338
- try {
339
- let query = `
340
- SELECT * FROM task_cache
341
- WHERE status IN ('pending', 'in_progress')
342
- `;
343
- const params = [];
344
- if (frameId) {
345
- query += ` AND frame_id = ?`;
346
- params.push(frameId);
347
- }
348
- query += ` ORDER BY priority DESC, created_at ASC`;
349
- const rows = this.db.prepare(query).all(...params);
350
- return this.hydrateTasks(rows);
351
- } catch (error) {
352
- throw new DatabaseError(
353
- "Failed to get active tasks",
354
- ErrorCode.DB_QUERY_FAILED,
355
- {
356
- frameId,
357
- operation: "getActiveTasks"
358
- },
359
- error instanceof Error ? error : void 0
360
- );
361
- }
362
- }
363
- /**
364
- * Get task by ID (latest version)
365
- */
366
- getTask(taskId) {
367
- try {
368
- const row = this.db.prepare(
369
- `
370
- SELECT * FROM task_cache WHERE id = ?
371
- `
372
- ).get(taskId);
373
- return row ? this.hydrateTask(row) : void 0;
374
- } catch (error) {
375
- throw new DatabaseError(
376
- `Failed to get task: ${taskId}`,
377
- ErrorCode.DB_QUERY_FAILED,
378
- {
379
- taskId,
380
- operation: "getTask"
381
- },
382
- error instanceof Error ? error : void 0
383
- );
384
- }
385
- }
386
- /**
387
- * Get tasks that are blocking other tasks
388
- */
389
- getBlockingTasks() {
390
- try {
391
- const rows = this.db.prepare(
392
- `
393
- SELECT * FROM task_cache
394
- WHERE JSON_ARRAY_LENGTH(blocks) > 0
395
- AND status NOT IN ('completed', 'cancelled')
396
- ORDER BY priority DESC
397
- `
398
- ).all();
399
- return this.hydrateTasks(rows);
400
- } catch (error) {
401
- throw new DatabaseError(
402
- "Failed to get blocking tasks",
403
- ErrorCode.DB_QUERY_FAILED,
404
- {
405
- operation: "getBlockingTasks"
406
- },
407
- error instanceof Error ? error : void 0
408
- );
409
- }
410
- }
411
- /**
412
- * Get metrics for current project
413
- */
414
- getMetrics() {
415
- try {
416
- const statusCounts = this.db.prepare(
417
- `
418
- SELECT status, COUNT(*) as count
419
- FROM task_cache
420
- GROUP BY status
421
- `
422
- ).all();
423
- const priorityCounts = this.db.prepare(
424
- `
425
- SELECT priority, COUNT(*) as count
426
- FROM task_cache
427
- GROUP BY priority
428
- `
429
- ).all();
430
- const totalTasks = statusCounts.reduce((sum, s) => sum + s.count, 0);
431
- const completedTasks = statusCounts.find((s) => s.status === "completed")?.count || 0;
432
- const blockedTasks = statusCounts.find((s) => s.status === "blocked")?.count || 0;
433
- const effortRows = this.db.prepare(
434
- `
435
- SELECT estimated_effort, actual_effort
436
- FROM task_cache
437
- WHERE estimated_effort IS NOT NULL
438
- AND actual_effort IS NOT NULL
439
- `
440
- ).all();
441
- let avgEffortAccuracy = 0;
442
- if (effortRows.length > 0) {
443
- const accuracies = effortRows.map(
444
- (r) => 1 - Math.abs(r.estimated_effort - r.actual_effort) / Math.max(r.estimated_effort, 1)
445
- );
446
- avgEffortAccuracy = accuracies.reduce((sum, acc) => sum + acc, 0) / accuracies.length;
447
- }
448
- return {
449
- total_tasks: totalTasks,
450
- by_status: Object.fromEntries(
451
- statusCounts.map((s) => [s.status, s.count])
452
- ),
453
- by_priority: Object.fromEntries(
454
- priorityCounts.map((p) => [p.priority, p.count])
455
- ),
456
- completion_rate: totalTasks > 0 ? completedTasks / totalTasks : 0,
457
- avg_effort_accuracy: avgEffortAccuracy,
458
- blocked_tasks: blockedTasks,
459
- overdue_tasks: 0
460
- // TODO: implement due dates
461
- };
462
- } catch (error) {
463
- throw new DatabaseError(
464
- "Failed to get task metrics",
465
- ErrorCode.DB_QUERY_FAILED,
466
- {
467
- operation: "getMetrics"
468
- },
469
- error instanceof Error ? error : void 0
470
- );
471
- }
472
- }
473
- /**
474
- * Export tasks for Linear integration (Phase 2)
475
- */
476
- exportForLinear() {
477
- const tasks = this.db.prepare(
478
- `
479
- SELECT * FROM task_cache
480
- WHERE external_refs IS NULL OR JSON_EXTRACT(external_refs, '$.linear') IS NULL
481
- ORDER BY created_at ASC
482
- `
483
- ).all();
484
- return tasks.map((task) => ({
485
- title: task.title,
486
- description: task.description,
487
- priority: this.mapPriorityToLinear(task.priority),
488
- status: this.mapStatusToLinear(task.status),
489
- estimate: task.estimated_effort,
490
- dependencies: JSON.parse(task.depends_on || "[]")
491
- }));
492
- }
493
- // Private methods
494
- appendTask(task) {
495
- try {
496
- const jsonLine = JSON.stringify(task) + "\n";
497
- appendFile(this.tasksFile, jsonLine, (err) => {
498
- if (err) {
499
- logger.error(
500
- `Failed to append task ${task.id} to JSONL: ${err.message}`,
501
- err,
502
- {
503
- taskId: task.id,
504
- tasksFile: this.tasksFile
505
- }
506
- );
507
- }
508
- });
509
- retry(() => Promise.resolve(this.upsertToCache(task)), {
510
- maxAttempts: 3,
511
- initialDelay: 100,
512
- onRetry: (attempt, error) => {
513
- logger.warn(`Retrying task cache upsert (attempt ${attempt})`, {
514
- taskId: task.id,
515
- errorMessage: error instanceof Error ? error.message : String(error)
516
- });
517
- }
518
- }).catch((error) => {
519
- logger.error(
520
- "Failed to upsert task to cache after retries",
521
- error instanceof Error ? error : new Error(String(error)),
522
- {
523
- taskId: task.id
524
- }
525
- );
526
- throw error;
527
- });
528
- logger.info("Appended task", {
529
- id: task.id,
530
- type: task.type,
531
- title: task.title,
532
- status: task.status
533
- });
534
- } catch (error) {
535
- throw new SystemError(
536
- `Failed to append task: ${task.id}`,
537
- ErrorCode.INTERNAL_ERROR,
538
- {
539
- taskId: task.id,
540
- operation: "appendTask"
541
- },
542
- error instanceof Error ? error : void 0
543
- );
544
- }
545
- }
546
- upsertToCache(task) {
547
- try {
548
- this.db.prepare(
549
- `
550
- INSERT OR REPLACE INTO task_cache (
551
- id, type, timestamp, parent_id, frame_id, title, description,
552
- status, priority, assignee, created_at, started_at, completed_at,
553
- estimated_effort, actual_effort, depends_on, blocks, tags,
554
- external_refs, context_score, last_accessed
555
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
556
- `
557
- ).run(
558
- task.id,
559
- task.type,
560
- task.timestamp,
561
- task.parent_id,
562
- task.frame_id,
563
- task.title,
564
- task.description,
565
- task.status,
566
- task.priority,
567
- task.assignee,
568
- task.created_at,
569
- task.started_at,
570
- task.completed_at,
571
- task.estimated_effort,
572
- task.actual_effort,
573
- JSON.stringify(task.depends_on),
574
- JSON.stringify(task.blocks),
575
- JSON.stringify(task.tags),
576
- JSON.stringify(task.external_refs || {}),
577
- task.context_score,
578
- task.last_accessed
579
- );
580
- } catch (error) {
581
- throw new DatabaseError(
582
- `Failed to upsert task to cache: ${task.id}`,
583
- ErrorCode.DB_QUERY_FAILED,
584
- {
585
- taskId: task.id,
586
- taskTitle: task.title,
587
- operation: "upsertToCache"
588
- },
589
- error instanceof Error ? error : void 0
590
- );
591
- }
592
- }
593
- generateTaskId(content) {
594
- const hash = createHash("sha256").update(content).digest("hex");
595
- return `tsk-${hash.substring(0, 8)}`;
596
- }
597
- hydrateTask = (row) => ({
598
- ...row,
599
- depends_on: JSON.parse(row.depends_on || "[]"),
600
- blocks: JSON.parse(row.blocks || "[]"),
601
- tags: JSON.parse(row.tags || "[]"),
602
- external_refs: JSON.parse(row.external_refs || "{}")
603
- });
604
- hydrateTasks(rows) {
605
- return rows.map(this.hydrateTask);
606
- }
607
- mapPriorityToLinear(priority) {
608
- const map = { low: 1, medium: 2, high: 3, urgent: 4 };
609
- return map[priority] || 2;
610
- }
611
- mapStatusToLinear(status) {
612
- const map = {
613
- pending: "Backlog",
614
- in_progress: "In Progress",
615
- completed: "Done",
616
- blocked: "Blocked",
617
- cancelled: "Cancelled"
618
- };
619
- return map[status] || "Backlog";
620
- }
621
- /**
622
- * Check if adding a dependency would create a circular dependency
623
- */
624
- wouldCreateCircularDependency(taskId, dependsOnId) {
625
- const visited = /* @__PURE__ */ new Set();
626
- const stack = [dependsOnId];
627
- while (stack.length > 0) {
628
- const currentId = stack.pop();
629
- if (currentId === taskId) {
630
- return true;
631
- }
632
- if (visited.has(currentId)) {
633
- continue;
634
- }
635
- visited.add(currentId);
636
- const currentTask = this.getTask(currentId);
637
- if (currentTask) {
638
- stack.push(...currentTask.depends_on);
639
- }
640
- }
641
- return false;
642
- }
643
- }
644
- export {
645
- PebblesTaskStore
646
- };
647
- //# sourceMappingURL=pebbles-task-store.js.map