@stackmemoryai/stackmemory 0.2.6 ā 0.2.7
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.
- package/README.md +245 -83
- package/dist/scripts/cancel-duplicate-tasks.d.ts +7 -0
- package/dist/scripts/cancel-duplicate-tasks.d.ts.map +1 -0
- package/dist/scripts/cancel-duplicate-tasks.js +171 -0
- package/dist/scripts/cancel-duplicate-tasks.js.map +1 -0
- package/dist/scripts/list-linear-tasks.d.ts +6 -0
- package/dist/scripts/list-linear-tasks.d.ts.map +1 -0
- package/dist/scripts/list-linear-tasks.js +122 -0
- package/dist/scripts/list-linear-tasks.js.map +1 -0
- package/dist/scripts/merge-linear-duplicates-safe.d.ts +7 -0
- package/dist/scripts/merge-linear-duplicates-safe.d.ts.map +1 -0
- package/dist/scripts/merge-linear-duplicates-safe.js +265 -0
- package/dist/scripts/merge-linear-duplicates-safe.js.map +1 -0
- package/dist/scripts/merge-linear-duplicates.d.ts +7 -0
- package/dist/scripts/merge-linear-duplicates.d.ts.map +1 -0
- package/dist/scripts/merge-linear-duplicates.js +126 -0
- package/dist/scripts/merge-linear-duplicates.js.map +1 -0
- package/dist/scripts/show-linear-summary.d.ts +6 -0
- package/dist/scripts/show-linear-summary.d.ts.map +1 -0
- package/dist/scripts/show-linear-summary.js +117 -0
- package/dist/scripts/show-linear-summary.js.map +1 -0
- package/dist/src/cli/__tests__/index.test.d.ts +5 -0
- package/dist/src/cli/__tests__/index.test.d.ts.map +1 -0
- package/dist/src/cli/__tests__/index.test.js +726 -0
- package/dist/src/cli/__tests__/index.test.js.map +1 -0
- package/dist/src/cli/auto-detect.d.ts +61 -0
- package/dist/src/cli/auto-detect.d.ts.map +1 -0
- package/dist/src/cli/auto-detect.js +350 -0
- package/dist/src/cli/auto-detect.js.map +1 -0
- package/dist/src/cli/claude-sm.d.ts +7 -0
- package/dist/src/cli/claude-sm.d.ts.map +1 -0
- package/dist/src/cli/claude-sm.js +357 -0
- package/dist/src/cli/claude-sm.js.map +1 -0
- package/dist/src/cli/commands/context.d.ts +7 -0
- package/dist/src/cli/commands/context.d.ts.map +1 -0
- package/dist/src/cli/commands/context.js +365 -0
- package/dist/src/cli/commands/context.js.map +1 -0
- package/dist/src/cli/commands/linear-test.d.ts +6 -0
- package/dist/src/cli/commands/linear-test.d.ts.map +1 -0
- package/dist/src/cli/commands/linear-test.js +123 -0
- package/dist/src/cli/commands/linear-test.js.map +1 -0
- package/dist/src/cli/commands/linear.d.ts +6 -0
- package/dist/src/cli/commands/linear.d.ts.map +1 -0
- package/dist/src/cli/commands/linear.js +317 -0
- package/dist/src/cli/commands/linear.js.map +1 -0
- package/dist/src/cli/commands/log.d.ts +7 -0
- package/dist/src/cli/commands/log.d.ts.map +1 -0
- package/dist/src/cli/commands/log.js +168 -0
- package/dist/src/cli/commands/log.js.map +1 -0
- package/dist/src/cli/commands/onboard.d.ts +8 -0
- package/dist/src/cli/commands/onboard.d.ts.map +1 -0
- package/dist/src/cli/commands/onboard.js +363 -0
- package/dist/src/cli/commands/onboard.js.map +1 -0
- package/dist/src/cli/commands/projects.js +1 -1
- package/dist/src/cli/commands/projects.js.map +1 -1
- package/dist/src/cli/commands/search.d.ts +7 -0
- package/dist/src/cli/commands/search.d.ts.map +1 -0
- package/dist/src/cli/commands/search.js +162 -0
- package/dist/src/cli/commands/search.js.map +1 -0
- package/dist/src/cli/commands/session.d.ts +7 -0
- package/dist/src/cli/commands/session.d.ts.map +1 -0
- package/dist/src/cli/commands/session.js +222 -0
- package/dist/src/cli/commands/session.js.map +1 -0
- package/dist/src/cli/commands/tasks.d.ts +7 -0
- package/dist/src/cli/commands/tasks.d.ts.map +1 -0
- package/dist/src/cli/commands/tasks.js +229 -0
- package/dist/src/cli/commands/tasks.js.map +1 -0
- package/dist/src/cli/commands/webhook.d.ts +3 -0
- package/dist/src/cli/commands/webhook.d.ts.map +1 -0
- package/dist/src/cli/commands/webhook.js +157 -0
- package/dist/src/cli/commands/webhook.js.map +1 -0
- package/dist/src/cli/commands/worktree.d.ts +8 -0
- package/dist/src/cli/commands/worktree.d.ts.map +1 -0
- package/dist/src/cli/commands/worktree.js +339 -0
- package/dist/src/cli/commands/worktree.js.map +1 -0
- package/dist/src/cli/index.d.ts +2 -1
- package/dist/src/cli/index.d.ts.map +1 -1
- package/dist/src/cli/index.js +290 -50
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/utils/viewer.d.ts.map +1 -1
- package/dist/src/cli/utils/viewer.js +3 -1
- package/dist/src/cli/utils/viewer.js.map +1 -1
- package/dist/src/core/context/__tests__/frame-manager.test.d.ts +5 -0
- package/dist/src/core/context/__tests__/frame-manager.test.d.ts.map +1 -0
- package/dist/src/core/context/__tests__/frame-manager.test.js +892 -0
- package/dist/src/core/context/__tests__/frame-manager.test.js.map +1 -0
- package/dist/src/core/context/auto-context.d.ts +22 -0
- package/dist/src/core/context/auto-context.d.ts.map +1 -0
- package/dist/src/core/context/auto-context.js +77 -0
- package/dist/src/core/context/auto-context.js.map +1 -0
- package/dist/src/core/context/frame-manager.d.ts +4 -0
- package/dist/src/core/context/frame-manager.d.ts.map +1 -1
- package/dist/src/core/context/frame-manager.js +350 -144
- package/dist/src/core/context/frame-manager.js.map +1 -1
- package/dist/src/core/errors/__tests__/error-handling.test.d.ts +5 -0
- package/dist/src/core/errors/__tests__/error-handling.test.d.ts.map +1 -0
- package/dist/src/core/errors/__tests__/error-handling.test.js +239 -0
- package/dist/src/core/errors/__tests__/error-handling.test.js.map +1 -0
- package/dist/src/core/errors/index.d.ts +135 -0
- package/dist/src/core/errors/index.d.ts.map +1 -0
- package/dist/src/core/errors/index.js +274 -0
- package/dist/src/core/errors/index.js.map +1 -0
- package/dist/src/core/errors/recovery.d.ts +86 -0
- package/dist/src/core/errors/recovery.d.ts.map +1 -0
- package/dist/src/core/errors/recovery.js +274 -0
- package/dist/src/core/errors/recovery.js.map +1 -0
- package/dist/src/core/projects/project-manager.d.ts.map +1 -1
- package/dist/src/core/projects/project-manager.js +240 -122
- package/dist/src/core/projects/project-manager.js.map +1 -1
- package/dist/src/core/session/index.d.ts +2 -0
- package/dist/src/core/session/index.d.ts.map +1 -0
- package/dist/src/core/session/index.js +2 -0
- package/dist/src/core/session/index.js.map +1 -0
- package/dist/src/core/session/session-manager.d.ts +69 -0
- package/dist/src/core/session/session-manager.d.ts.map +1 -0
- package/dist/src/core/session/session-manager.js +311 -0
- package/dist/src/core/session/session-manager.js.map +1 -0
- package/dist/src/core/utils/update-checker.d.ts.map +1 -1
- package/dist/src/core/utils/update-checker.js +82 -25
- package/dist/src/core/utils/update-checker.js.map +1 -1
- package/dist/src/core/worktree/worktree-manager.d.ts +110 -0
- package/dist/src/core/worktree/worktree-manager.d.ts.map +1 -0
- package/dist/src/core/worktree/worktree-manager.js +456 -0
- package/dist/src/core/worktree/worktree-manager.js.map +1 -0
- package/dist/src/features/analytics/core/analytics-service.d.ts +6 -0
- package/dist/src/features/analytics/core/analytics-service.d.ts.map +1 -1
- package/dist/src/features/analytics/core/analytics-service.js +125 -10
- package/dist/src/features/analytics/core/analytics-service.js.map +1 -1
- package/dist/src/features/analytics/queries/metrics-queries.d.ts.map +1 -1
- package/dist/src/features/analytics/queries/metrics-queries.js +220 -163
- package/dist/src/features/analytics/queries/metrics-queries.js.map +1 -1
- package/dist/src/features/browser/browser-mcp.d.ts.map +1 -1
- package/dist/src/features/browser/browser-mcp.js +3 -0
- package/dist/src/features/browser/browser-mcp.js.map +1 -1
- package/dist/src/features/tasks/__tests__/pebbles-task-store.test.d.ts +5 -0
- package/dist/src/features/tasks/__tests__/pebbles-task-store.test.d.ts.map +1 -0
- package/dist/src/features/tasks/__tests__/pebbles-task-store.test.js +712 -0
- package/dist/src/features/tasks/__tests__/pebbles-task-store.test.js.map +1 -0
- package/dist/src/features/tasks/pebbles-task-store.d.ts +4 -0
- package/dist/src/features/tasks/pebbles-task-store.d.ts.map +1 -1
- package/dist/src/features/tasks/pebbles-task-store.js +299 -141
- package/dist/src/features/tasks/pebbles-task-store.js.map +1 -1
- package/dist/src/integrations/linear/__tests__/auth.test.d.ts +5 -0
- package/dist/src/integrations/linear/__tests__/auth.test.d.ts.map +1 -0
- package/dist/src/integrations/linear/__tests__/auth.test.js +517 -0
- package/dist/src/integrations/linear/__tests__/auth.test.js.map +1 -0
- package/dist/src/integrations/linear/__tests__/sync-service.test.d.ts +5 -0
- package/dist/src/integrations/linear/__tests__/sync-service.test.d.ts.map +1 -0
- package/dist/src/integrations/linear/__tests__/sync-service.test.js +700 -0
- package/dist/src/integrations/linear/__tests__/sync-service.test.js.map +1 -0
- package/dist/src/integrations/linear/client.d.ts +28 -1
- package/dist/src/integrations/linear/client.d.ts.map +1 -1
- package/dist/src/integrations/linear/client.js +87 -0
- package/dist/src/integrations/linear/client.js.map +1 -1
- package/dist/src/integrations/linear/sync-service.d.ts +25 -0
- package/dist/src/integrations/linear/sync-service.d.ts.map +1 -0
- package/dist/src/integrations/linear/sync-service.js +198 -0
- package/dist/src/integrations/linear/sync-service.js.map +1 -0
- package/dist/src/integrations/linear/sync.d.ts +23 -1
- package/dist/src/integrations/linear/sync.d.ts.map +1 -1
- package/dist/src/integrations/linear/sync.js +156 -9
- package/dist/src/integrations/linear/sync.js.map +1 -1
- package/dist/src/integrations/linear/types.d.ts +75 -0
- package/dist/src/integrations/linear/types.d.ts.map +1 -0
- package/dist/src/integrations/linear/types.js +2 -0
- package/dist/src/integrations/linear/types.js.map +1 -0
- package/dist/src/integrations/linear/webhook-server.d.ts +32 -0
- package/dist/src/integrations/linear/webhook-server.d.ts.map +1 -0
- package/dist/src/integrations/linear/webhook-server.js +188 -0
- package/dist/src/integrations/linear/webhook-server.js.map +1 -0
- package/dist/src/integrations/linear/webhook.d.ts +95 -0
- package/dist/src/integrations/linear/webhook.d.ts.map +1 -0
- package/dist/src/integrations/linear/webhook.js +204 -0
- package/dist/src/integrations/linear/webhook.js.map +1 -0
- package/dist/src/integrations/mcp/__tests__/server.test.d.ts +5 -0
- package/dist/src/integrations/mcp/__tests__/server.test.d.ts.map +1 -0
- package/dist/src/integrations/mcp/__tests__/server.test.js +790 -0
- package/dist/src/integrations/mcp/__tests__/server.test.js.map +1 -0
- package/dist/src/integrations/mcp/server.d.ts +6 -0
- package/dist/src/integrations/mcp/server.d.ts.map +1 -1
- package/dist/src/integrations/mcp/server.js +490 -54
- package/dist/src/integrations/mcp/server.js.map +1 -1
- package/dist/src/servers/production/auth-middleware.d.ts +2 -2
- package/dist/src/servers/production/auth-middleware.d.ts.map +1 -1
- package/dist/src/servers/production/auth-middleware.js +1 -1
- package/dist/src/servers/production/auth-middleware.js.map +1 -1
- package/dist/src/services/config-service.d.ts +44 -0
- package/dist/src/services/config-service.d.ts.map +1 -0
- package/dist/src/services/config-service.js +61 -0
- package/dist/src/services/config-service.js.map +1 -0
- package/dist/src/services/context-service.d.ts +17 -0
- package/dist/src/services/context-service.d.ts.map +1 -0
- package/dist/src/services/context-service.js +88 -0
- package/dist/src/services/context-service.js.map +1 -0
- package/dist/src/types/task.d.ts +17 -0
- package/dist/src/types/task.d.ts.map +1 -0
- package/dist/src/types/task.js +2 -0
- package/dist/src/types/task.js.map +1 -0
- package/dist/src/utils/logger.d.ts +13 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +52 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/package.json +16 -3
|
@@ -12,9 +12,8 @@ import { join, dirname } from 'path';
|
|
|
12
12
|
import { execSync } from 'child_process';
|
|
13
13
|
import { FrameManager } from '../../core/context/frame-manager.js';
|
|
14
14
|
import { PebblesTaskStore, } from '../../features/tasks/pebbles-task-store.js';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// import { LinearSyncEngine, DEFAULT_SYNC_CONFIG } from '../integrations/linear/sync.js';
|
|
15
|
+
import { LinearAuthManager } from '../linear/auth.js';
|
|
16
|
+
import { LinearSyncEngine, DEFAULT_SYNC_CONFIG } from '../linear/sync.js';
|
|
18
17
|
import { logger } from '../../core/monitoring/logger.js';
|
|
19
18
|
import { BrowserMCPIntegration } from '../../features/browser/browser-mcp.js';
|
|
20
19
|
// ============================================
|
|
@@ -26,9 +25,8 @@ class LocalStackMemoryMCP {
|
|
|
26
25
|
projectRoot;
|
|
27
26
|
frameManager;
|
|
28
27
|
taskStore;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// private linearSync: LinearSyncEngine;
|
|
28
|
+
linearAuthManager;
|
|
29
|
+
linearSync;
|
|
32
30
|
projectId;
|
|
33
31
|
contexts = new Map();
|
|
34
32
|
browserMCP;
|
|
@@ -49,13 +47,9 @@ class LocalStackMemoryMCP {
|
|
|
49
47
|
this.frameManager = new FrameManager(this.db, this.projectId);
|
|
50
48
|
// Initialize task store
|
|
51
49
|
this.taskStore = new PebblesTaskStore(this.projectRoot, this.db);
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// this.taskStore,
|
|
56
|
-
// this.linearAuthManager,
|
|
57
|
-
// DEFAULT_SYNC_CONFIG
|
|
58
|
-
// );
|
|
50
|
+
// Initialize Linear integration
|
|
51
|
+
this.linearAuthManager = new LinearAuthManager(this.projectRoot);
|
|
52
|
+
this.linearSync = new LinearSyncEngine(this.taskStore, this.linearAuthManager, DEFAULT_SYNC_CONFIG);
|
|
59
53
|
// Initialize MCP server
|
|
60
54
|
this.server = new Server({
|
|
61
55
|
name: 'stackmemory-local',
|
|
@@ -388,7 +382,7 @@ class LocalStackMemoryMCP {
|
|
|
388
382
|
},
|
|
389
383
|
{
|
|
390
384
|
name: 'get_active_tasks',
|
|
391
|
-
description: 'Get currently active tasks',
|
|
385
|
+
description: 'Get currently active tasks synced from Linear',
|
|
392
386
|
inputSchema: {
|
|
393
387
|
type: 'object',
|
|
394
388
|
properties: {
|
|
@@ -396,6 +390,30 @@ class LocalStackMemoryMCP {
|
|
|
396
390
|
type: 'string',
|
|
397
391
|
description: 'Filter by specific frame ID',
|
|
398
392
|
},
|
|
393
|
+
status: {
|
|
394
|
+
type: 'string',
|
|
395
|
+
enum: [
|
|
396
|
+
'pending',
|
|
397
|
+
'in_progress',
|
|
398
|
+
'completed',
|
|
399
|
+
'blocked',
|
|
400
|
+
'cancelled',
|
|
401
|
+
],
|
|
402
|
+
description: 'Filter by status',
|
|
403
|
+
},
|
|
404
|
+
priority: {
|
|
405
|
+
type: 'string',
|
|
406
|
+
enum: ['low', 'medium', 'high', 'urgent'],
|
|
407
|
+
description: 'Filter by priority',
|
|
408
|
+
},
|
|
409
|
+
search: {
|
|
410
|
+
type: 'string',
|
|
411
|
+
description: 'Search in task title or description',
|
|
412
|
+
},
|
|
413
|
+
limit: {
|
|
414
|
+
type: 'number',
|
|
415
|
+
description: 'Max number of tasks to return (default: 20)',
|
|
416
|
+
},
|
|
399
417
|
},
|
|
400
418
|
},
|
|
401
419
|
},
|
|
@@ -425,6 +443,78 @@ class LocalStackMemoryMCP {
|
|
|
425
443
|
required: ['taskId', 'dependsOnId'],
|
|
426
444
|
},
|
|
427
445
|
},
|
|
446
|
+
{
|
|
447
|
+
name: 'linear_sync',
|
|
448
|
+
description: 'Sync tasks with Linear',
|
|
449
|
+
inputSchema: {
|
|
450
|
+
type: 'object',
|
|
451
|
+
properties: {
|
|
452
|
+
direction: {
|
|
453
|
+
type: 'string',
|
|
454
|
+
enum: ['bidirectional', 'to_linear', 'from_linear'],
|
|
455
|
+
description: 'Sync direction',
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
name: 'linear_update_task',
|
|
462
|
+
description: 'Update a Linear task status',
|
|
463
|
+
inputSchema: {
|
|
464
|
+
type: 'object',
|
|
465
|
+
properties: {
|
|
466
|
+
issueId: {
|
|
467
|
+
type: 'string',
|
|
468
|
+
description: 'Linear issue ID or identifier (e.g., STA-34)',
|
|
469
|
+
},
|
|
470
|
+
status: {
|
|
471
|
+
type: 'string',
|
|
472
|
+
enum: ['todo', 'in-progress', 'done', 'canceled'],
|
|
473
|
+
description: 'New status for the task',
|
|
474
|
+
},
|
|
475
|
+
title: {
|
|
476
|
+
type: 'string',
|
|
477
|
+
description: 'Update task title (optional)',
|
|
478
|
+
},
|
|
479
|
+
description: {
|
|
480
|
+
type: 'string',
|
|
481
|
+
description: 'Update task description (optional)',
|
|
482
|
+
},
|
|
483
|
+
priority: {
|
|
484
|
+
type: 'number',
|
|
485
|
+
enum: [1, 2, 3, 4],
|
|
486
|
+
description: 'Priority (1=urgent, 2=high, 3=medium, 4=low)',
|
|
487
|
+
},
|
|
488
|
+
},
|
|
489
|
+
required: ['issueId'],
|
|
490
|
+
},
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
name: 'linear_get_tasks',
|
|
494
|
+
description: 'Get Linear tasks',
|
|
495
|
+
inputSchema: {
|
|
496
|
+
type: 'object',
|
|
497
|
+
properties: {
|
|
498
|
+
status: {
|
|
499
|
+
type: 'string',
|
|
500
|
+
enum: ['todo', 'in-progress', 'done', 'all'],
|
|
501
|
+
description: 'Filter by status',
|
|
502
|
+
},
|
|
503
|
+
limit: {
|
|
504
|
+
type: 'number',
|
|
505
|
+
description: 'Maximum number of tasks to return',
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
name: 'linear_status',
|
|
512
|
+
description: 'Get Linear integration status',
|
|
513
|
+
inputSchema: {
|
|
514
|
+
type: 'object',
|
|
515
|
+
properties: {},
|
|
516
|
+
},
|
|
517
|
+
},
|
|
428
518
|
],
|
|
429
519
|
};
|
|
430
520
|
});
|
|
@@ -437,32 +527,84 @@ class LocalStackMemoryMCP {
|
|
|
437
527
|
}),
|
|
438
528
|
}), async (request) => {
|
|
439
529
|
const { name, arguments: args } = request.params;
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
530
|
+
// Log tool call event before execution
|
|
531
|
+
const currentFrameId = this.frameManager.getCurrentFrameId();
|
|
532
|
+
if (currentFrameId) {
|
|
533
|
+
this.frameManager.addEvent('tool_call', {
|
|
534
|
+
tool_name: name,
|
|
535
|
+
arguments: args,
|
|
536
|
+
timestamp: Date.now(),
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
let result;
|
|
540
|
+
let error;
|
|
541
|
+
try {
|
|
542
|
+
switch (name) {
|
|
543
|
+
case 'get_context':
|
|
544
|
+
result = await this.handleGetContext(args);
|
|
545
|
+
break;
|
|
546
|
+
case 'add_decision':
|
|
547
|
+
result = await this.handleAddDecision(args);
|
|
548
|
+
break;
|
|
549
|
+
case 'start_frame':
|
|
550
|
+
result = await this.handleStartFrame(args);
|
|
551
|
+
break;
|
|
552
|
+
case 'close_frame':
|
|
553
|
+
result = await this.handleCloseFrame(args);
|
|
554
|
+
break;
|
|
555
|
+
case 'add_anchor':
|
|
556
|
+
result = await this.handleAddAnchor(args);
|
|
557
|
+
break;
|
|
558
|
+
case 'get_hot_stack':
|
|
559
|
+
result = await this.handleGetHotStack(args);
|
|
560
|
+
break;
|
|
561
|
+
case 'create_task':
|
|
562
|
+
result = await this.handleCreateTask(args);
|
|
563
|
+
break;
|
|
564
|
+
case 'update_task_status':
|
|
565
|
+
result = await this.handleUpdateTaskStatus(args);
|
|
566
|
+
break;
|
|
567
|
+
case 'get_active_tasks':
|
|
568
|
+
result = await this.handleGetActiveTasks(args);
|
|
569
|
+
break;
|
|
570
|
+
case 'get_task_metrics':
|
|
571
|
+
result = await this.handleGetTaskMetrics(args);
|
|
572
|
+
break;
|
|
573
|
+
case 'add_task_dependency':
|
|
574
|
+
result = await this.handleAddTaskDependency(args);
|
|
575
|
+
break;
|
|
576
|
+
case 'linear_sync':
|
|
577
|
+
result = await this.handleLinearSync(args);
|
|
578
|
+
break;
|
|
579
|
+
case 'linear_update_task':
|
|
580
|
+
result = await this.handleLinearUpdateTask(args);
|
|
581
|
+
break;
|
|
582
|
+
case 'linear_get_tasks':
|
|
583
|
+
result = await this.handleLinearGetTasks(args);
|
|
584
|
+
break;
|
|
585
|
+
case 'linear_status':
|
|
586
|
+
result = await this.handleLinearStatus(args);
|
|
587
|
+
break;
|
|
588
|
+
default:
|
|
589
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
catch (err) {
|
|
593
|
+
error = err;
|
|
594
|
+
throw err;
|
|
595
|
+
}
|
|
596
|
+
finally {
|
|
597
|
+
// Log tool result event after execution (success or failure)
|
|
598
|
+
if (currentFrameId) {
|
|
599
|
+
this.frameManager.addEvent('tool_result', {
|
|
600
|
+
tool_name: name,
|
|
601
|
+
success: !error,
|
|
602
|
+
result: error ? { error: error.message } : result,
|
|
603
|
+
timestamp: Date.now(),
|
|
604
|
+
});
|
|
605
|
+
}
|
|
465
606
|
}
|
|
607
|
+
return result;
|
|
466
608
|
});
|
|
467
609
|
}
|
|
468
610
|
async handleGetContext(args) {
|
|
@@ -712,31 +854,70 @@ class LocalStackMemoryMCP {
|
|
|
712
854
|
}
|
|
713
855
|
}
|
|
714
856
|
async handleGetActiveTasks(args) {
|
|
715
|
-
const { frameId } = args;
|
|
716
|
-
|
|
717
|
-
|
|
857
|
+
const { frameId, status, priority, search, limit = 20 } = args;
|
|
858
|
+
let tasks = this.taskStore.getActiveTasks(frameId);
|
|
859
|
+
// Apply filters
|
|
860
|
+
if (status) {
|
|
861
|
+
tasks = tasks.filter((t) => t.status === status);
|
|
862
|
+
}
|
|
863
|
+
if (priority) {
|
|
864
|
+
tasks = tasks.filter((t) => t.priority === priority);
|
|
865
|
+
}
|
|
866
|
+
if (search) {
|
|
867
|
+
const searchLower = search.toLowerCase();
|
|
868
|
+
tasks = tasks.filter((t) => t.title.toLowerCase().includes(searchLower) ||
|
|
869
|
+
(t.description && t.description.toLowerCase().includes(searchLower)));
|
|
870
|
+
}
|
|
871
|
+
// Sort by priority (urgent first) then by created_at
|
|
872
|
+
const priorityOrder = { urgent: 0, high: 1, medium: 2, low: 3 };
|
|
873
|
+
tasks.sort((a, b) => {
|
|
874
|
+
const pa = priorityOrder[a.priority] ?? 2;
|
|
875
|
+
const pb = priorityOrder[b.priority] ?? 2;
|
|
876
|
+
if (pa !== pb)
|
|
877
|
+
return pa - pb;
|
|
878
|
+
return b.created_at - a.created_at;
|
|
879
|
+
});
|
|
880
|
+
// Limit results
|
|
881
|
+
tasks = tasks.slice(0, limit);
|
|
882
|
+
if (tasks.length === 0) {
|
|
718
883
|
return {
|
|
719
884
|
content: [
|
|
720
885
|
{
|
|
721
886
|
type: 'text',
|
|
722
|
-
text:
|
|
723
|
-
? `š No
|
|
724
|
-
: 'š No active tasks
|
|
887
|
+
text: search
|
|
888
|
+
? `š No tasks matching "${search}"`
|
|
889
|
+
: 'š No active tasks found',
|
|
725
890
|
},
|
|
726
891
|
],
|
|
727
892
|
};
|
|
728
893
|
}
|
|
729
|
-
let response =
|
|
730
|
-
|
|
731
|
-
const
|
|
732
|
-
|
|
894
|
+
let response = `š **Tasks** (${tasks.length} found)\n\n`;
|
|
895
|
+
tasks.forEach((task) => {
|
|
896
|
+
const priorityIcon = { urgent: 'š“', high: 'š ', medium: 'š”', low: 'š¢' }[task.priority] ||
|
|
897
|
+
'āŖ';
|
|
898
|
+
const statusIcon = {
|
|
899
|
+
pending: 'ā³',
|
|
900
|
+
in_progress: 'š',
|
|
901
|
+
completed: 'ā
',
|
|
902
|
+
blocked: 'š«',
|
|
903
|
+
cancelled: 'ā',
|
|
904
|
+
}[task.status] || 'āŖ';
|
|
733
905
|
const effort = task.estimated_effort
|
|
734
|
-
? ` (
|
|
906
|
+
? ` (~${task.estimated_effort}m)`
|
|
735
907
|
: '';
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
908
|
+
// Extract Linear ID from title if present
|
|
909
|
+
const linearMatch = task.title.match(/\[ENG-\d+\]/);
|
|
910
|
+
const linearId = linearMatch ? linearMatch[0] : '';
|
|
911
|
+
const title = linearId
|
|
912
|
+
? task.title.replace(linearId, '').trim()
|
|
913
|
+
: task.title;
|
|
914
|
+
response += `${statusIcon} ${priorityIcon} **${linearId || task.id}** ${title}${effort}\n`;
|
|
915
|
+
if (task.description) {
|
|
916
|
+
const desc = task.description.split('\n')[0].slice(0, 100);
|
|
917
|
+
response += ` ${desc}${task.description.length > 100 ? '...' : ''}\n`;
|
|
918
|
+
}
|
|
919
|
+
if (task.tags && task.tags.length > 0) {
|
|
920
|
+
response += ` š·ļø ${task.tags.join(', ')}\n`;
|
|
740
921
|
}
|
|
741
922
|
response += '\n';
|
|
742
923
|
});
|
|
@@ -807,6 +988,261 @@ class LocalStackMemoryMCP {
|
|
|
807
988
|
};
|
|
808
989
|
}
|
|
809
990
|
}
|
|
991
|
+
// Linear Integration Handlers
|
|
992
|
+
async handleLinearSync(args) {
|
|
993
|
+
try {
|
|
994
|
+
const tokens = this.linearAuthManager.loadTokens();
|
|
995
|
+
if (!tokens) {
|
|
996
|
+
return {
|
|
997
|
+
content: [
|
|
998
|
+
{
|
|
999
|
+
type: 'text',
|
|
1000
|
+
text: 'ā Linear not authenticated. Run: stackmemory linear setup',
|
|
1001
|
+
},
|
|
1002
|
+
],
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
const syncConfig = { ...DEFAULT_SYNC_CONFIG, enabled: true };
|
|
1006
|
+
if (args.direction) {
|
|
1007
|
+
syncConfig.direction = args.direction;
|
|
1008
|
+
}
|
|
1009
|
+
// Update sync engine configuration for this sync
|
|
1010
|
+
this.linearSync.updateConfig(syncConfig);
|
|
1011
|
+
const result = await this.linearSync.sync();
|
|
1012
|
+
return {
|
|
1013
|
+
content: [
|
|
1014
|
+
{
|
|
1015
|
+
type: 'text',
|
|
1016
|
+
text: `ā
Linear sync completed\n- To Linear: ${result.synced.toLinear} tasks\n- From Linear: ${result.synced.fromLinear} tasks\n- Updated: ${result.synced.updated} tasks`,
|
|
1017
|
+
},
|
|
1018
|
+
],
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
catch (error) {
|
|
1022
|
+
return {
|
|
1023
|
+
content: [
|
|
1024
|
+
{
|
|
1025
|
+
type: 'text',
|
|
1026
|
+
text: `ā Linear sync failed: ${error.message}`,
|
|
1027
|
+
},
|
|
1028
|
+
],
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
async handleLinearUpdateTask(args) {
|
|
1033
|
+
try {
|
|
1034
|
+
const { LinearClient } = await import('../linear/client.js');
|
|
1035
|
+
const tokens = this.linearAuthManager.loadTokens();
|
|
1036
|
+
if (!tokens) {
|
|
1037
|
+
return {
|
|
1038
|
+
content: [
|
|
1039
|
+
{
|
|
1040
|
+
type: 'text',
|
|
1041
|
+
text: 'ā Linear not authenticated. Run: stackmemory linear setup',
|
|
1042
|
+
},
|
|
1043
|
+
],
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
const client = new LinearClient({
|
|
1047
|
+
apiKey: tokens.accessToken,
|
|
1048
|
+
});
|
|
1049
|
+
// Find the issue
|
|
1050
|
+
let issue = await client.getIssue(args.issueId);
|
|
1051
|
+
if (!issue) {
|
|
1052
|
+
issue = await client.findIssueByIdentifier(args.issueId);
|
|
1053
|
+
}
|
|
1054
|
+
if (!issue) {
|
|
1055
|
+
return {
|
|
1056
|
+
content: [
|
|
1057
|
+
{
|
|
1058
|
+
type: 'text',
|
|
1059
|
+
text: `ā Linear issue ${args.issueId} not found`,
|
|
1060
|
+
},
|
|
1061
|
+
],
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
const updates = {};
|
|
1065
|
+
// Handle status update
|
|
1066
|
+
if (args.status) {
|
|
1067
|
+
const team = await client.getTeam();
|
|
1068
|
+
const states = await client.getWorkflowStates(team.id);
|
|
1069
|
+
const statusMap = {
|
|
1070
|
+
todo: 'unstarted',
|
|
1071
|
+
'in-progress': 'started',
|
|
1072
|
+
done: 'completed',
|
|
1073
|
+
canceled: 'cancelled',
|
|
1074
|
+
};
|
|
1075
|
+
const targetType = statusMap[args.status] || args.status;
|
|
1076
|
+
const targetState = states.find((s) => s.type === targetType);
|
|
1077
|
+
if (!targetState) {
|
|
1078
|
+
return {
|
|
1079
|
+
content: [
|
|
1080
|
+
{
|
|
1081
|
+
type: 'text',
|
|
1082
|
+
text: `ā Invalid status: ${args.status}`,
|
|
1083
|
+
},
|
|
1084
|
+
],
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
updates.stateId = targetState.id;
|
|
1088
|
+
}
|
|
1089
|
+
if (args.title)
|
|
1090
|
+
updates.title = args.title;
|
|
1091
|
+
if (args.description)
|
|
1092
|
+
updates.description = args.description;
|
|
1093
|
+
if (args.priority)
|
|
1094
|
+
updates.priority = args.priority;
|
|
1095
|
+
const updatedIssue = await client.updateIssue(issue.id, updates);
|
|
1096
|
+
// Auto-sync to local tasks after update
|
|
1097
|
+
this.linearSync.updateConfig({ ...DEFAULT_SYNC_CONFIG, enabled: true, direction: 'from_linear' });
|
|
1098
|
+
const syncResult = await this.linearSync.sync();
|
|
1099
|
+
let response = `ā
Updated ${updatedIssue.identifier}: ${updatedIssue.title}\n`;
|
|
1100
|
+
if (args.status) {
|
|
1101
|
+
response += `Status: ${updatedIssue.state.name}\n`;
|
|
1102
|
+
}
|
|
1103
|
+
response += `URL: ${updatedIssue.url}\n`;
|
|
1104
|
+
response += `\nš Local sync: ${syncResult.synced.fromLinear} new, ${syncResult.synced.updated} updated`;
|
|
1105
|
+
return {
|
|
1106
|
+
content: [
|
|
1107
|
+
{
|
|
1108
|
+
type: 'text',
|
|
1109
|
+
text: response,
|
|
1110
|
+
},
|
|
1111
|
+
],
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
catch (error) {
|
|
1115
|
+
return {
|
|
1116
|
+
content: [
|
|
1117
|
+
{
|
|
1118
|
+
type: 'text',
|
|
1119
|
+
text: `ā Failed to update Linear task: ${error.message}`,
|
|
1120
|
+
},
|
|
1121
|
+
],
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
async handleLinearGetTasks(args) {
|
|
1126
|
+
try {
|
|
1127
|
+
const { LinearClient } = await import('../linear/client.js');
|
|
1128
|
+
const tokens = this.linearAuthManager.loadTokens();
|
|
1129
|
+
if (!tokens) {
|
|
1130
|
+
return {
|
|
1131
|
+
content: [
|
|
1132
|
+
{
|
|
1133
|
+
type: 'text',
|
|
1134
|
+
text: 'ā Linear not authenticated. Run: stackmemory linear setup',
|
|
1135
|
+
},
|
|
1136
|
+
],
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
const client = new LinearClient({
|
|
1140
|
+
apiKey: tokens.accessToken,
|
|
1141
|
+
});
|
|
1142
|
+
let stateType = undefined;
|
|
1143
|
+
if (args.status && args.status !== 'all') {
|
|
1144
|
+
const statusMap = {
|
|
1145
|
+
todo: 'unstarted',
|
|
1146
|
+
'in-progress': 'started',
|
|
1147
|
+
done: 'completed',
|
|
1148
|
+
};
|
|
1149
|
+
stateType = statusMap[args.status] || args.status;
|
|
1150
|
+
}
|
|
1151
|
+
const issues = await client.getIssues({
|
|
1152
|
+
stateType,
|
|
1153
|
+
limit: args.limit || 20,
|
|
1154
|
+
});
|
|
1155
|
+
if (!issues || issues.length === 0) {
|
|
1156
|
+
return {
|
|
1157
|
+
content: [
|
|
1158
|
+
{
|
|
1159
|
+
type: 'text',
|
|
1160
|
+
text: 'No Linear tasks found',
|
|
1161
|
+
},
|
|
1162
|
+
],
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
let response = `š **Linear Tasks** (${issues.length} items)\n\n`;
|
|
1166
|
+
issues.forEach((issue) => {
|
|
1167
|
+
const priority = issue.priority ? `P${issue.priority}` : '-';
|
|
1168
|
+
response += `- **${issue.identifier}**: ${issue.title}\n`;
|
|
1169
|
+
response += ` Status: ${issue.state.name} | Priority: ${priority}\n`;
|
|
1170
|
+
if (issue.assignee) {
|
|
1171
|
+
response += ` Assignee: ${issue.assignee.name}\n`;
|
|
1172
|
+
}
|
|
1173
|
+
response += ` ${issue.url}\n\n`;
|
|
1174
|
+
});
|
|
1175
|
+
return {
|
|
1176
|
+
content: [
|
|
1177
|
+
{
|
|
1178
|
+
type: 'text',
|
|
1179
|
+
text: response,
|
|
1180
|
+
},
|
|
1181
|
+
],
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
catch (error) {
|
|
1185
|
+
return {
|
|
1186
|
+
content: [
|
|
1187
|
+
{
|
|
1188
|
+
type: 'text',
|
|
1189
|
+
text: `ā Failed to get Linear tasks: ${error.message}`,
|
|
1190
|
+
},
|
|
1191
|
+
],
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
async handleLinearStatus(_args) {
|
|
1196
|
+
try {
|
|
1197
|
+
const { LinearClient } = await import('../linear/client.js');
|
|
1198
|
+
const tokens = this.linearAuthManager.loadTokens();
|
|
1199
|
+
if (!tokens) {
|
|
1200
|
+
return {
|
|
1201
|
+
content: [
|
|
1202
|
+
{
|
|
1203
|
+
type: 'text',
|
|
1204
|
+
text: 'ā Linear integration not configured\nRun: stackmemory linear setup',
|
|
1205
|
+
},
|
|
1206
|
+
],
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
try {
|
|
1210
|
+
const client = new LinearClient({
|
|
1211
|
+
apiKey: tokens.accessToken,
|
|
1212
|
+
});
|
|
1213
|
+
const viewer = await client.getViewer();
|
|
1214
|
+
const team = await client.getTeam();
|
|
1215
|
+
return {
|
|
1216
|
+
content: [
|
|
1217
|
+
{
|
|
1218
|
+
type: 'text',
|
|
1219
|
+
text: `ā
**Linear Integration Status**\n\nConnected as: ${viewer.name} (${viewer.email})\nTeam: ${team.name} (${team.key})\nTokens: Valid`,
|
|
1220
|
+
},
|
|
1221
|
+
],
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
catch (error) {
|
|
1225
|
+
return {
|
|
1226
|
+
content: [
|
|
1227
|
+
{
|
|
1228
|
+
type: 'text',
|
|
1229
|
+
text: `ā ļø Linear configured but connection failed: ${error.message}`,
|
|
1230
|
+
},
|
|
1231
|
+
],
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
catch (error) {
|
|
1236
|
+
return {
|
|
1237
|
+
content: [
|
|
1238
|
+
{
|
|
1239
|
+
type: 'text',
|
|
1240
|
+
text: `ā Linear status check failed: ${error.message}`,
|
|
1241
|
+
},
|
|
1242
|
+
],
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
810
1246
|
async start() {
|
|
811
1247
|
const transport = new StdioServerTransport();
|
|
812
1248
|
await this.server.connect(transport);
|