@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.
Files changed (203) hide show
  1. package/README.md +245 -83
  2. package/dist/scripts/cancel-duplicate-tasks.d.ts +7 -0
  3. package/dist/scripts/cancel-duplicate-tasks.d.ts.map +1 -0
  4. package/dist/scripts/cancel-duplicate-tasks.js +171 -0
  5. package/dist/scripts/cancel-duplicate-tasks.js.map +1 -0
  6. package/dist/scripts/list-linear-tasks.d.ts +6 -0
  7. package/dist/scripts/list-linear-tasks.d.ts.map +1 -0
  8. package/dist/scripts/list-linear-tasks.js +122 -0
  9. package/dist/scripts/list-linear-tasks.js.map +1 -0
  10. package/dist/scripts/merge-linear-duplicates-safe.d.ts +7 -0
  11. package/dist/scripts/merge-linear-duplicates-safe.d.ts.map +1 -0
  12. package/dist/scripts/merge-linear-duplicates-safe.js +265 -0
  13. package/dist/scripts/merge-linear-duplicates-safe.js.map +1 -0
  14. package/dist/scripts/merge-linear-duplicates.d.ts +7 -0
  15. package/dist/scripts/merge-linear-duplicates.d.ts.map +1 -0
  16. package/dist/scripts/merge-linear-duplicates.js +126 -0
  17. package/dist/scripts/merge-linear-duplicates.js.map +1 -0
  18. package/dist/scripts/show-linear-summary.d.ts +6 -0
  19. package/dist/scripts/show-linear-summary.d.ts.map +1 -0
  20. package/dist/scripts/show-linear-summary.js +117 -0
  21. package/dist/scripts/show-linear-summary.js.map +1 -0
  22. package/dist/src/cli/__tests__/index.test.d.ts +5 -0
  23. package/dist/src/cli/__tests__/index.test.d.ts.map +1 -0
  24. package/dist/src/cli/__tests__/index.test.js +726 -0
  25. package/dist/src/cli/__tests__/index.test.js.map +1 -0
  26. package/dist/src/cli/auto-detect.d.ts +61 -0
  27. package/dist/src/cli/auto-detect.d.ts.map +1 -0
  28. package/dist/src/cli/auto-detect.js +350 -0
  29. package/dist/src/cli/auto-detect.js.map +1 -0
  30. package/dist/src/cli/claude-sm.d.ts +7 -0
  31. package/dist/src/cli/claude-sm.d.ts.map +1 -0
  32. package/dist/src/cli/claude-sm.js +357 -0
  33. package/dist/src/cli/claude-sm.js.map +1 -0
  34. package/dist/src/cli/commands/context.d.ts +7 -0
  35. package/dist/src/cli/commands/context.d.ts.map +1 -0
  36. package/dist/src/cli/commands/context.js +365 -0
  37. package/dist/src/cli/commands/context.js.map +1 -0
  38. package/dist/src/cli/commands/linear-test.d.ts +6 -0
  39. package/dist/src/cli/commands/linear-test.d.ts.map +1 -0
  40. package/dist/src/cli/commands/linear-test.js +123 -0
  41. package/dist/src/cli/commands/linear-test.js.map +1 -0
  42. package/dist/src/cli/commands/linear.d.ts +6 -0
  43. package/dist/src/cli/commands/linear.d.ts.map +1 -0
  44. package/dist/src/cli/commands/linear.js +317 -0
  45. package/dist/src/cli/commands/linear.js.map +1 -0
  46. package/dist/src/cli/commands/log.d.ts +7 -0
  47. package/dist/src/cli/commands/log.d.ts.map +1 -0
  48. package/dist/src/cli/commands/log.js +168 -0
  49. package/dist/src/cli/commands/log.js.map +1 -0
  50. package/dist/src/cli/commands/onboard.d.ts +8 -0
  51. package/dist/src/cli/commands/onboard.d.ts.map +1 -0
  52. package/dist/src/cli/commands/onboard.js +363 -0
  53. package/dist/src/cli/commands/onboard.js.map +1 -0
  54. package/dist/src/cli/commands/projects.js +1 -1
  55. package/dist/src/cli/commands/projects.js.map +1 -1
  56. package/dist/src/cli/commands/search.d.ts +7 -0
  57. package/dist/src/cli/commands/search.d.ts.map +1 -0
  58. package/dist/src/cli/commands/search.js +162 -0
  59. package/dist/src/cli/commands/search.js.map +1 -0
  60. package/dist/src/cli/commands/session.d.ts +7 -0
  61. package/dist/src/cli/commands/session.d.ts.map +1 -0
  62. package/dist/src/cli/commands/session.js +222 -0
  63. package/dist/src/cli/commands/session.js.map +1 -0
  64. package/dist/src/cli/commands/tasks.d.ts +7 -0
  65. package/dist/src/cli/commands/tasks.d.ts.map +1 -0
  66. package/dist/src/cli/commands/tasks.js +229 -0
  67. package/dist/src/cli/commands/tasks.js.map +1 -0
  68. package/dist/src/cli/commands/webhook.d.ts +3 -0
  69. package/dist/src/cli/commands/webhook.d.ts.map +1 -0
  70. package/dist/src/cli/commands/webhook.js +157 -0
  71. package/dist/src/cli/commands/webhook.js.map +1 -0
  72. package/dist/src/cli/commands/worktree.d.ts +8 -0
  73. package/dist/src/cli/commands/worktree.d.ts.map +1 -0
  74. package/dist/src/cli/commands/worktree.js +339 -0
  75. package/dist/src/cli/commands/worktree.js.map +1 -0
  76. package/dist/src/cli/index.d.ts +2 -1
  77. package/dist/src/cli/index.d.ts.map +1 -1
  78. package/dist/src/cli/index.js +290 -50
  79. package/dist/src/cli/index.js.map +1 -1
  80. package/dist/src/cli/utils/viewer.d.ts.map +1 -1
  81. package/dist/src/cli/utils/viewer.js +3 -1
  82. package/dist/src/cli/utils/viewer.js.map +1 -1
  83. package/dist/src/core/context/__tests__/frame-manager.test.d.ts +5 -0
  84. package/dist/src/core/context/__tests__/frame-manager.test.d.ts.map +1 -0
  85. package/dist/src/core/context/__tests__/frame-manager.test.js +892 -0
  86. package/dist/src/core/context/__tests__/frame-manager.test.js.map +1 -0
  87. package/dist/src/core/context/auto-context.d.ts +22 -0
  88. package/dist/src/core/context/auto-context.d.ts.map +1 -0
  89. package/dist/src/core/context/auto-context.js +77 -0
  90. package/dist/src/core/context/auto-context.js.map +1 -0
  91. package/dist/src/core/context/frame-manager.d.ts +4 -0
  92. package/dist/src/core/context/frame-manager.d.ts.map +1 -1
  93. package/dist/src/core/context/frame-manager.js +350 -144
  94. package/dist/src/core/context/frame-manager.js.map +1 -1
  95. package/dist/src/core/errors/__tests__/error-handling.test.d.ts +5 -0
  96. package/dist/src/core/errors/__tests__/error-handling.test.d.ts.map +1 -0
  97. package/dist/src/core/errors/__tests__/error-handling.test.js +239 -0
  98. package/dist/src/core/errors/__tests__/error-handling.test.js.map +1 -0
  99. package/dist/src/core/errors/index.d.ts +135 -0
  100. package/dist/src/core/errors/index.d.ts.map +1 -0
  101. package/dist/src/core/errors/index.js +274 -0
  102. package/dist/src/core/errors/index.js.map +1 -0
  103. package/dist/src/core/errors/recovery.d.ts +86 -0
  104. package/dist/src/core/errors/recovery.d.ts.map +1 -0
  105. package/dist/src/core/errors/recovery.js +274 -0
  106. package/dist/src/core/errors/recovery.js.map +1 -0
  107. package/dist/src/core/projects/project-manager.d.ts.map +1 -1
  108. package/dist/src/core/projects/project-manager.js +240 -122
  109. package/dist/src/core/projects/project-manager.js.map +1 -1
  110. package/dist/src/core/session/index.d.ts +2 -0
  111. package/dist/src/core/session/index.d.ts.map +1 -0
  112. package/dist/src/core/session/index.js +2 -0
  113. package/dist/src/core/session/index.js.map +1 -0
  114. package/dist/src/core/session/session-manager.d.ts +69 -0
  115. package/dist/src/core/session/session-manager.d.ts.map +1 -0
  116. package/dist/src/core/session/session-manager.js +311 -0
  117. package/dist/src/core/session/session-manager.js.map +1 -0
  118. package/dist/src/core/utils/update-checker.d.ts.map +1 -1
  119. package/dist/src/core/utils/update-checker.js +82 -25
  120. package/dist/src/core/utils/update-checker.js.map +1 -1
  121. package/dist/src/core/worktree/worktree-manager.d.ts +110 -0
  122. package/dist/src/core/worktree/worktree-manager.d.ts.map +1 -0
  123. package/dist/src/core/worktree/worktree-manager.js +456 -0
  124. package/dist/src/core/worktree/worktree-manager.js.map +1 -0
  125. package/dist/src/features/analytics/core/analytics-service.d.ts +6 -0
  126. package/dist/src/features/analytics/core/analytics-service.d.ts.map +1 -1
  127. package/dist/src/features/analytics/core/analytics-service.js +125 -10
  128. package/dist/src/features/analytics/core/analytics-service.js.map +1 -1
  129. package/dist/src/features/analytics/queries/metrics-queries.d.ts.map +1 -1
  130. package/dist/src/features/analytics/queries/metrics-queries.js +220 -163
  131. package/dist/src/features/analytics/queries/metrics-queries.js.map +1 -1
  132. package/dist/src/features/browser/browser-mcp.d.ts.map +1 -1
  133. package/dist/src/features/browser/browser-mcp.js +3 -0
  134. package/dist/src/features/browser/browser-mcp.js.map +1 -1
  135. package/dist/src/features/tasks/__tests__/pebbles-task-store.test.d.ts +5 -0
  136. package/dist/src/features/tasks/__tests__/pebbles-task-store.test.d.ts.map +1 -0
  137. package/dist/src/features/tasks/__tests__/pebbles-task-store.test.js +712 -0
  138. package/dist/src/features/tasks/__tests__/pebbles-task-store.test.js.map +1 -0
  139. package/dist/src/features/tasks/pebbles-task-store.d.ts +4 -0
  140. package/dist/src/features/tasks/pebbles-task-store.d.ts.map +1 -1
  141. package/dist/src/features/tasks/pebbles-task-store.js +299 -141
  142. package/dist/src/features/tasks/pebbles-task-store.js.map +1 -1
  143. package/dist/src/integrations/linear/__tests__/auth.test.d.ts +5 -0
  144. package/dist/src/integrations/linear/__tests__/auth.test.d.ts.map +1 -0
  145. package/dist/src/integrations/linear/__tests__/auth.test.js +517 -0
  146. package/dist/src/integrations/linear/__tests__/auth.test.js.map +1 -0
  147. package/dist/src/integrations/linear/__tests__/sync-service.test.d.ts +5 -0
  148. package/dist/src/integrations/linear/__tests__/sync-service.test.d.ts.map +1 -0
  149. package/dist/src/integrations/linear/__tests__/sync-service.test.js +700 -0
  150. package/dist/src/integrations/linear/__tests__/sync-service.test.js.map +1 -0
  151. package/dist/src/integrations/linear/client.d.ts +28 -1
  152. package/dist/src/integrations/linear/client.d.ts.map +1 -1
  153. package/dist/src/integrations/linear/client.js +87 -0
  154. package/dist/src/integrations/linear/client.js.map +1 -1
  155. package/dist/src/integrations/linear/sync-service.d.ts +25 -0
  156. package/dist/src/integrations/linear/sync-service.d.ts.map +1 -0
  157. package/dist/src/integrations/linear/sync-service.js +198 -0
  158. package/dist/src/integrations/linear/sync-service.js.map +1 -0
  159. package/dist/src/integrations/linear/sync.d.ts +23 -1
  160. package/dist/src/integrations/linear/sync.d.ts.map +1 -1
  161. package/dist/src/integrations/linear/sync.js +156 -9
  162. package/dist/src/integrations/linear/sync.js.map +1 -1
  163. package/dist/src/integrations/linear/types.d.ts +75 -0
  164. package/dist/src/integrations/linear/types.d.ts.map +1 -0
  165. package/dist/src/integrations/linear/types.js +2 -0
  166. package/dist/src/integrations/linear/types.js.map +1 -0
  167. package/dist/src/integrations/linear/webhook-server.d.ts +32 -0
  168. package/dist/src/integrations/linear/webhook-server.d.ts.map +1 -0
  169. package/dist/src/integrations/linear/webhook-server.js +188 -0
  170. package/dist/src/integrations/linear/webhook-server.js.map +1 -0
  171. package/dist/src/integrations/linear/webhook.d.ts +95 -0
  172. package/dist/src/integrations/linear/webhook.d.ts.map +1 -0
  173. package/dist/src/integrations/linear/webhook.js +204 -0
  174. package/dist/src/integrations/linear/webhook.js.map +1 -0
  175. package/dist/src/integrations/mcp/__tests__/server.test.d.ts +5 -0
  176. package/dist/src/integrations/mcp/__tests__/server.test.d.ts.map +1 -0
  177. package/dist/src/integrations/mcp/__tests__/server.test.js +790 -0
  178. package/dist/src/integrations/mcp/__tests__/server.test.js.map +1 -0
  179. package/dist/src/integrations/mcp/server.d.ts +6 -0
  180. package/dist/src/integrations/mcp/server.d.ts.map +1 -1
  181. package/dist/src/integrations/mcp/server.js +490 -54
  182. package/dist/src/integrations/mcp/server.js.map +1 -1
  183. package/dist/src/servers/production/auth-middleware.d.ts +2 -2
  184. package/dist/src/servers/production/auth-middleware.d.ts.map +1 -1
  185. package/dist/src/servers/production/auth-middleware.js +1 -1
  186. package/dist/src/servers/production/auth-middleware.js.map +1 -1
  187. package/dist/src/services/config-service.d.ts +44 -0
  188. package/dist/src/services/config-service.d.ts.map +1 -0
  189. package/dist/src/services/config-service.js +61 -0
  190. package/dist/src/services/config-service.js.map +1 -0
  191. package/dist/src/services/context-service.d.ts +17 -0
  192. package/dist/src/services/context-service.d.ts.map +1 -0
  193. package/dist/src/services/context-service.js +88 -0
  194. package/dist/src/services/context-service.js.map +1 -0
  195. package/dist/src/types/task.d.ts +17 -0
  196. package/dist/src/types/task.d.ts.map +1 -0
  197. package/dist/src/types/task.js +2 -0
  198. package/dist/src/types/task.js.map +1 -0
  199. package/dist/src/utils/logger.d.ts +13 -0
  200. package/dist/src/utils/logger.d.ts.map +1 -0
  201. package/dist/src/utils/logger.js +52 -0
  202. package/dist/src/utils/logger.js.map +1 -0
  203. package/package.json +16 -3
@@ -0,0 +1,456 @@
1
+ /**
2
+ * Git Worktree Manager for StackMemory
3
+ * Manages multiple instances across git worktrees with context isolation
4
+ */
5
+ import { execSync } from 'child_process';
6
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
7
+ import { join, basename, dirname } from 'path';
8
+ import { homedir } from 'os';
9
+ import Database from 'better-sqlite3';
10
+ import { logger } from '../monitoring/logger.js';
11
+ import { ProjectManager } from '../projects/project-manager.js';
12
+ import { createErrorHandler, } from '../errors/index.js';
13
+ export class WorktreeManager {
14
+ static instance;
15
+ config;
16
+ configPath;
17
+ worktreeCache = new Map();
18
+ contextMap = new Map();
19
+ db;
20
+ constructor() {
21
+ this.configPath = join(homedir(), '.stackmemory', 'worktree-config.json');
22
+ this.config = this.loadConfig();
23
+ try {
24
+ if (this.config.enabled) {
25
+ this.initialize();
26
+ }
27
+ }
28
+ catch (error) {
29
+ logger.error('Failed to initialize WorktreeManager', error instanceof Error ? error : new Error(String(error)), {
30
+ configPath: this.configPath,
31
+ });
32
+ // Don't throw here - allow the manager to be created without worktree support
33
+ this.config.enabled = false;
34
+ }
35
+ }
36
+ static getInstance() {
37
+ if (!WorktreeManager.instance) {
38
+ WorktreeManager.instance = new WorktreeManager();
39
+ }
40
+ return WorktreeManager.instance;
41
+ }
42
+ /**
43
+ * Initialize worktree management
44
+ */
45
+ initialize() {
46
+ const dbPath = join(homedir(), '.stackmemory', 'worktrees.db');
47
+ const errorHandler = createErrorHandler({
48
+ operation: 'initialize',
49
+ dbPath,
50
+ });
51
+ try {
52
+ this.db = new Database(dbPath);
53
+ this.db.exec(`
54
+ CREATE TABLE IF NOT EXISTS worktrees (
55
+ id TEXT PRIMARY KEY,
56
+ path TEXT NOT NULL UNIQUE,
57
+ branch TEXT NOT NULL,
58
+ commit TEXT,
59
+ is_main BOOLEAN,
60
+ is_bare BOOLEAN,
61
+ is_detached BOOLEAN,
62
+ linked_path TEXT,
63
+ context_id TEXT UNIQUE,
64
+ project_id TEXT,
65
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
66
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
67
+ );
68
+
69
+ CREATE TABLE IF NOT EXISTS worktree_contexts (
70
+ context_id TEXT PRIMARY KEY,
71
+ worktree_id TEXT NOT NULL,
72
+ project_id TEXT,
73
+ branch TEXT,
74
+ context_path TEXT,
75
+ db_path TEXT,
76
+ last_synced DATETIME,
77
+ metadata JSON,
78
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
79
+ FOREIGN KEY (worktree_id) REFERENCES worktrees(id)
80
+ );
81
+
82
+ CREATE TABLE IF NOT EXISTS context_sync (
83
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
84
+ source_context TEXT,
85
+ target_context TEXT,
86
+ sync_type TEXT, -- 'push', 'pull', 'merge'
87
+ data JSON,
88
+ synced_at DATETIME DEFAULT CURRENT_TIMESTAMP
89
+ );
90
+
91
+ CREATE INDEX IF NOT EXISTS idx_worktrees_project ON worktrees(project_id);
92
+ CREATE INDEX IF NOT EXISTS idx_contexts_worktree ON worktree_contexts(worktree_id);
93
+ `);
94
+ }
95
+ catch (error) {
96
+ errorHandler(error);
97
+ }
98
+ }
99
+ /**
100
+ * Load configuration
101
+ */
102
+ loadConfig() {
103
+ if (existsSync(this.configPath)) {
104
+ try {
105
+ return JSON.parse(readFileSync(this.configPath, 'utf-8'));
106
+ }
107
+ catch (error) {
108
+ logger.error('Failed to load worktree config', error);
109
+ }
110
+ }
111
+ // Default config
112
+ return {
113
+ enabled: false,
114
+ autoDetect: true,
115
+ isolateContexts: true,
116
+ shareGlobalContext: false,
117
+ syncInterval: 15,
118
+ maxWorktrees: 10,
119
+ };
120
+ }
121
+ /**
122
+ * Save configuration
123
+ */
124
+ saveConfig(config) {
125
+ this.config = { ...this.config, ...config };
126
+ const configDir = dirname(this.configPath);
127
+ if (!existsSync(configDir)) {
128
+ mkdirSync(configDir, { recursive: true });
129
+ }
130
+ writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
131
+ // Reinitialize if just enabled
132
+ if (config.enabled && !this.db) {
133
+ this.initialize();
134
+ }
135
+ logger.info('Worktree configuration updated', { config: this.config });
136
+ }
137
+ /**
138
+ * Detect git worktrees in current repository
139
+ */
140
+ detectWorktrees(repoPath) {
141
+ const path = repoPath || process.cwd();
142
+ try {
143
+ // Get worktree list
144
+ const output = execSync('git worktree list --porcelain', {
145
+ cwd: path,
146
+ encoding: 'utf-8',
147
+ });
148
+ const worktrees = [];
149
+ const lines = output.split('\n');
150
+ let currentWorktree = {};
151
+ for (const line of lines) {
152
+ if (line.startsWith('worktree ')) {
153
+ if (currentWorktree.path) {
154
+ worktrees.push(this.finalizeWorktreeInfo(currentWorktree));
155
+ }
156
+ currentWorktree = { path: line.substring(9) };
157
+ }
158
+ else if (line.startsWith('HEAD ')) {
159
+ currentWorktree.commit = line.substring(5);
160
+ }
161
+ else if (line.startsWith('branch ')) {
162
+ currentWorktree.branch = line.substring(7);
163
+ }
164
+ else if (line === 'bare') {
165
+ currentWorktree.isBare = true;
166
+ }
167
+ else if (line === 'detached') {
168
+ currentWorktree.isDetached = true;
169
+ }
170
+ }
171
+ // Add last worktree
172
+ if (currentWorktree.path) {
173
+ worktrees.push(this.finalizeWorktreeInfo(currentWorktree));
174
+ }
175
+ // Determine main worktree
176
+ if (worktrees.length > 0) {
177
+ const mainPath = this.getMainWorktreePath(path);
178
+ worktrees.forEach(wt => {
179
+ wt.isMainWorktree = wt.path === mainPath;
180
+ if (!wt.isMainWorktree) {
181
+ wt.linkedPath = mainPath;
182
+ }
183
+ });
184
+ }
185
+ // Cache results
186
+ worktrees.forEach(wt => {
187
+ this.worktreeCache.set(wt.path, wt);
188
+ if (this.config.enabled) {
189
+ this.saveWorktree(wt);
190
+ }
191
+ });
192
+ logger.info(`Detected ${worktrees.length} worktrees`, {
193
+ count: worktrees.length,
194
+ branches: worktrees.map(w => w.branch).filter(Boolean)
195
+ });
196
+ return worktrees;
197
+ }
198
+ catch (error) {
199
+ logger.debug('Not a git repository or git worktree not available');
200
+ return [];
201
+ }
202
+ }
203
+ /**
204
+ * Get main worktree path
205
+ */
206
+ getMainWorktreePath(path) {
207
+ try {
208
+ const gitDir = execSync('git rev-parse --git-common-dir', {
209
+ cwd: path,
210
+ encoding: 'utf-8',
211
+ }).trim();
212
+ // If it's a worktree, find the main repo
213
+ if (gitDir.includes('/.git/worktrees/')) {
214
+ const mainGitDir = gitDir.replace(/\/\.git\/worktrees\/.*$/, '');
215
+ return mainGitDir;
216
+ }
217
+ // It's the main worktree
218
+ return dirname(gitDir);
219
+ }
220
+ catch {
221
+ return path;
222
+ }
223
+ }
224
+ /**
225
+ * Finalize worktree info with defaults
226
+ */
227
+ finalizeWorktreeInfo(partial) {
228
+ return {
229
+ path: partial.path || '',
230
+ branch: partial.branch || 'detached',
231
+ commit: partial.commit || '',
232
+ isMainWorktree: partial.isMainWorktree || false,
233
+ isBare: partial.isBare || false,
234
+ isDetached: partial.isDetached || false,
235
+ linkedPath: partial.linkedPath,
236
+ contextId: this.generateContextId(partial.path || '', partial.branch || ''),
237
+ };
238
+ }
239
+ /**
240
+ * Generate unique context ID for worktree
241
+ */
242
+ generateContextId(path, branch) {
243
+ const repoName = basename(dirname(path));
244
+ const sanitizedBranch = branch.replace(/[^a-zA-Z0-9-]/g, '_');
245
+ return `${repoName}-${sanitizedBranch}-${Buffer.from(path).toString('base64').substring(0, 8)}`;
246
+ }
247
+ /**
248
+ * Save worktree to database
249
+ */
250
+ async saveWorktree(worktree) {
251
+ if (!this.db)
252
+ return;
253
+ const projectManager = ProjectManager.getInstance();
254
+ const project = await projectManager.detectProject(worktree.path);
255
+ const stmt = this.db.prepare(`
256
+ INSERT OR REPLACE INTO worktrees
257
+ (id, path, branch, commit, is_main, is_bare, is_detached, linked_path, context_id, project_id, updated_at)
258
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
259
+ `);
260
+ stmt.run(worktree.contextId, worktree.path, worktree.branch, worktree.commit, worktree.isMainWorktree ? 1 : 0, worktree.isBare ? 1 : 0, worktree.isDetached ? 1 : 0, worktree.linkedPath, worktree.contextId, project.id);
261
+ }
262
+ /**
263
+ * Get or create context for worktree
264
+ */
265
+ getWorktreeContext(worktreePath) {
266
+ // Check cache
267
+ const cached = this.contextMap.get(worktreePath);
268
+ if (cached) {
269
+ return cached;
270
+ }
271
+ const worktree = this.worktreeCache.get(worktreePath) ||
272
+ this.detectWorktrees(worktreePath).find(w => w.path === worktreePath);
273
+ if (!worktree) {
274
+ throw new Error(`No worktree found at path: ${worktreePath}`);
275
+ }
276
+ // Create isolated context path
277
+ const contextBasePath = this.config.isolateContexts
278
+ ? join(homedir(), '.stackmemory', 'worktrees', worktree.contextId)
279
+ : join(worktreePath, '.stackmemory');
280
+ // Ensure directory exists
281
+ if (!existsSync(contextBasePath)) {
282
+ mkdirSync(contextBasePath, { recursive: true });
283
+ }
284
+ const context = {
285
+ worktreeId: worktree.contextId,
286
+ projectId: '', // Will be filled by project manager
287
+ branch: worktree.branch,
288
+ contextPath: contextBasePath,
289
+ dbPath: join(contextBasePath, 'context.db'),
290
+ lastSynced: new Date(),
291
+ metadata: {
292
+ isMainWorktree: worktree.isMainWorktree,
293
+ linkedPath: worktree.linkedPath,
294
+ },
295
+ };
296
+ // Save to database
297
+ if (this.db && this.config.enabled) {
298
+ this.saveContext(context);
299
+ }
300
+ // Cache it
301
+ this.contextMap.set(worktreePath, context);
302
+ logger.info('Created worktree context', {
303
+ worktree: worktree.branch,
304
+ path: contextBasePath,
305
+ });
306
+ return context;
307
+ }
308
+ /**
309
+ * Save context to database
310
+ */
311
+ saveContext(context) {
312
+ if (!this.db)
313
+ return;
314
+ const stmt = this.db.prepare(`
315
+ INSERT OR REPLACE INTO worktree_contexts
316
+ (context_id, worktree_id, project_id, branch, context_path, db_path, last_synced, metadata)
317
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
318
+ `);
319
+ stmt.run(context.worktreeId, context.worktreeId, context.projectId, context.branch, context.contextPath, context.dbPath, context.lastSynced.toISOString(), JSON.stringify(context.metadata));
320
+ }
321
+ /**
322
+ * Sync contexts between worktrees
323
+ */
324
+ async syncContexts(sourceWorktree, targetWorktree, syncType = 'merge') {
325
+ const source = this.getWorktreeContext(sourceWorktree);
326
+ const target = this.getWorktreeContext(targetWorktree);
327
+ logger.info('Syncing contexts between worktrees', {
328
+ source: source.branch,
329
+ target: target.branch,
330
+ type: syncType,
331
+ });
332
+ // Open both databases
333
+ const sourceDb = new Database(source.dbPath);
334
+ const targetDb = new Database(target.dbPath);
335
+ try {
336
+ // Get contexts from source
337
+ const contexts = sourceDb.prepare(`
338
+ SELECT * FROM contexts
339
+ WHERE created_at > datetime('now', '-7 days')
340
+ ORDER BY created_at DESC
341
+ `).all();
342
+ // Sync based on type
343
+ if (syncType === 'push' || syncType === 'merge') {
344
+ this.mergeContexts(contexts, targetDb, syncType === 'merge');
345
+ }
346
+ if (syncType === 'pull') {
347
+ const targetContexts = targetDb.prepare(`
348
+ SELECT * FROM contexts
349
+ WHERE created_at > datetime('now', '-7 days')
350
+ ORDER BY created_at DESC
351
+ `).all();
352
+ this.mergeContexts(targetContexts, sourceDb, false);
353
+ }
354
+ // Log sync operation
355
+ if (this.db) {
356
+ const stmt = this.db.prepare(`
357
+ INSERT INTO context_sync (source_context, target_context, sync_type, data)
358
+ VALUES (?, ?, ?, ?)
359
+ `);
360
+ stmt.run(source.worktreeId, target.worktreeId, syncType, JSON.stringify({ count: contexts.length }));
361
+ }
362
+ logger.info('Context sync completed', {
363
+ synced: contexts.length,
364
+ type: syncType,
365
+ });
366
+ }
367
+ finally {
368
+ sourceDb.close();
369
+ targetDb.close();
370
+ }
371
+ }
372
+ /**
373
+ * Merge contexts into target database
374
+ */
375
+ mergeContexts(contexts, targetDb, bidirectional) {
376
+ const stmt = targetDb.prepare(`
377
+ INSERT OR REPLACE INTO contexts (id, type, content, metadata, created_at)
378
+ VALUES (?, ?, ?, ?, ?)
379
+ `);
380
+ for (const ctx of contexts) {
381
+ try {
382
+ stmt.run(ctx.id, ctx.type, ctx.content, ctx.metadata, ctx.created_at);
383
+ }
384
+ catch (error) {
385
+ logger.warn('Failed to merge context', { id: ctx.id, error });
386
+ }
387
+ }
388
+ }
389
+ /**
390
+ * List all active worktrees
391
+ */
392
+ listActiveWorktrees() {
393
+ if (!this.db) {
394
+ return Array.from(this.worktreeCache.values());
395
+ }
396
+ const stmt = this.db.prepare(`
397
+ SELECT * FROM worktrees
398
+ ORDER BY is_main DESC, branch ASC
399
+ `);
400
+ const rows = stmt.all();
401
+ return rows.map(row => ({
402
+ path: row.path,
403
+ branch: row.branch,
404
+ commit: row.commit,
405
+ isMainWorktree: row.is_main === 1,
406
+ isBare: row.is_bare === 1,
407
+ isDetached: row.is_detached === 1,
408
+ linkedPath: row.linked_path,
409
+ contextId: row.context_id,
410
+ }));
411
+ }
412
+ /**
413
+ * Clean up stale worktree contexts
414
+ */
415
+ cleanupStaleContexts() {
416
+ if (!this.db)
417
+ return;
418
+ const activeWorktrees = this.detectWorktrees();
419
+ const activePaths = new Set(activeWorktrees.map(w => w.path));
420
+ // Get all stored worktrees
421
+ const stmt = this.db.prepare('SELECT * FROM worktrees');
422
+ const stored = stmt.all();
423
+ // Remove stale entries
424
+ const deleteStmt = this.db.prepare('DELETE FROM worktrees WHERE id = ?');
425
+ const deleteContextStmt = this.db.prepare('DELETE FROM worktree_contexts WHERE worktree_id = ?');
426
+ for (const worktree of stored) {
427
+ if (!activePaths.has(worktree.path)) {
428
+ deleteStmt.run(worktree.id);
429
+ deleteContextStmt.run(worktree.id);
430
+ logger.info('Cleaned up stale worktree context', {
431
+ path: worktree.path,
432
+ branch: worktree.branch,
433
+ });
434
+ }
435
+ }
436
+ }
437
+ /**
438
+ * Get configuration
439
+ */
440
+ getConfig() {
441
+ return { ...this.config };
442
+ }
443
+ /**
444
+ * Check if worktree support is enabled
445
+ */
446
+ isEnabled() {
447
+ return this.config.enabled;
448
+ }
449
+ /**
450
+ * Enable or disable worktree support
451
+ */
452
+ setEnabled(enabled) {
453
+ this.saveConfig({ enabled });
454
+ }
455
+ }
456
+ //# sourceMappingURL=worktree-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree-manager.js","sourceRoot":"","sources":["../../../../src/core/worktree/worktree-manager.ts"],"names":[],"mappings":"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"}
@@ -2,12 +2,18 @@ import { TaskAnalytics, DashboardState, AnalyticsQuery } from '../types/metrics.
2
2
  export declare class AnalyticsService {
3
3
  private metricsQueries;
4
4
  private linearClient?;
5
+ private taskStore?;
5
6
  private dbPath;
7
+ private projectPath;
6
8
  private updateCallbacks;
7
9
  constructor(projectPath?: string);
10
+ private initializeTaskStore;
8
11
  private ensureDirectoryExists;
9
12
  private initializeLinearIntegration;
10
13
  syncLinearTasks(): Promise<void>;
14
+ syncFromTaskStore(): Promise<number>;
15
+ private getAllTasksFromStore;
16
+ private mapTaskStatus;
11
17
  private mapLinearState;
12
18
  private mapLinearPriority;
13
19
  getDashboardState(query?: AnalyticsQuery): Promise<DashboardState>;
@@ -1 +1 @@
1
- {"version":3,"file":"analytics-service.d.ts","sourceRoot":"","sources":["../../../../../src/features/analytics/core/analytics-service.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,aAAa,EACb,cAAc,EAEd,cAAc,EACf,MAAM,qBAAqB,CAAC;AAK7B,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,YAAY,CAAC,CAAe;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,eAAe,CAAmD;gBAE9D,WAAW,CAAC,EAAE,MAAM;IAYhC,OAAO,CAAC,qBAAqB;YAOf,2BAA2B;IAiBnC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBtC,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,iBAAiB;IAOnB,iBAAiB,CAAC,KAAK,GAAE,cAAmB,GAAG,OAAO,CAAC,cAAc,CAAC;YA0B9D,cAAc;YAkCd,WAAW;IAKzB,OAAO,CAAC,mBAAmB;IAY3B,kBAAkB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,MAAM,IAAI;YAQ3D,YAAY;IAKpB,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAK3C,UAAU,CACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC;IAWhB,KAAK,IAAI,IAAI;CAGd"}
1
+ {"version":3,"file":"analytics-service.d.ts","sourceRoot":"","sources":["../../../../../src/features/analytics/core/analytics-service.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,aAAa,EACb,cAAc,EAEd,cAAc,EACf,MAAM,qBAAqB,CAAC;AAK7B,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,YAAY,CAAC,CAAe;IACpC,OAAO,CAAC,SAAS,CAAC,CAAmB;IACrC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAAmD;gBAE9D,WAAW,CAAC,EAAE,MAAM;IAehC,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,qBAAqB;YAOf,2BAA2B;IAiBnC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAoChC,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAmC1C,OAAO,CAAC,oBAAoB;IA4C5B,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,iBAAiB;IAOnB,iBAAiB,CAAC,KAAK,GAAE,cAAmB,GAAG,OAAO,CAAC,cAAc,CAAC;YA0B9D,cAAc;YAkCd,WAAW;IAKzB,OAAO,CAAC,mBAAmB;IAY3B,kBAAkB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,MAAM,IAAI;YAQ3D,YAAY;IAKpB,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAK3C,UAAU,CACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC;IAWhB,KAAK,IAAI,IAAI;CAGd"}
@@ -1,22 +1,40 @@
1
1
  import { MetricsQueries } from '../queries/metrics-queries.js';
2
2
  import { LinearClient } from '../../../integrations/linear/client.js';
3
+ import { PebblesTaskStore } from '../../tasks/pebbles-task-store.js';
4
+ import Database from 'better-sqlite3';
3
5
  import path from 'path';
4
6
  import fs from 'fs';
5
7
  import os from 'os';
6
8
  export class AnalyticsService {
7
9
  metricsQueries;
8
10
  linearClient;
11
+ taskStore;
9
12
  dbPath;
13
+ projectPath;
10
14
  updateCallbacks = new Set();
11
15
  constructor(projectPath) {
12
- const basePath = projectPath || process.cwd();
13
- this.dbPath = path.join(basePath, '.stackmemory', 'analytics.db');
16
+ this.projectPath = projectPath || process.cwd();
17
+ this.dbPath = path.join(this.projectPath, '.stackmemory', 'analytics.db');
14
18
  this.ensureDirectoryExists();
15
19
  this.metricsQueries = new MetricsQueries(this.dbPath);
20
+ // Initialize task store for syncing
21
+ this.initializeTaskStore();
16
22
  if (process.env.LINEAR_API_KEY) {
17
23
  this.initializeLinearIntegration();
18
24
  }
19
25
  }
26
+ initializeTaskStore() {
27
+ try {
28
+ const contextDbPath = path.join(this.projectPath, '.stackmemory', 'context.db');
29
+ if (fs.existsSync(contextDbPath)) {
30
+ const db = new Database(contextDbPath);
31
+ this.taskStore = new PebblesTaskStore(this.projectPath, db);
32
+ }
33
+ }
34
+ catch (error) {
35
+ console.error('Failed to initialize task store:', error);
36
+ }
37
+ }
20
38
  ensureDirectoryExists() {
21
39
  const dir = path.dirname(this.dbPath);
22
40
  if (!fs.existsSync(dir)) {
@@ -37,19 +55,116 @@ export class AnalyticsService {
37
55
  }
38
56
  }
39
57
  async syncLinearTasks() {
40
- if (!this.linearClient)
41
- return;
58
+ // First sync from task store (which includes Linear-synced tasks)
59
+ await this.syncFromTaskStore();
60
+ // Then try direct Linear sync if client available
61
+ if (this.linearClient) {
62
+ try {
63
+ const issues = await this.linearClient.getIssues({ limit: 100 });
64
+ for (const issue of issues) {
65
+ const task = {
66
+ id: issue.id,
67
+ title: issue.title,
68
+ state: this.mapLinearState(issue.state.type),
69
+ createdAt: new Date(issue.createdAt),
70
+ completedAt: issue.state.type === 'completed'
71
+ ? new Date(issue.updatedAt)
72
+ : undefined,
73
+ estimatedEffort: issue.estimate ? issue.estimate * 60 : undefined,
74
+ assigneeId: issue.assignee?.id,
75
+ priority: this.mapLinearPriority(issue.priority),
76
+ labels: Array.isArray(issue.labels)
77
+ ? issue.labels.map((l) => l.name)
78
+ : issue.labels?.nodes?.map((l) => l.name) || [],
79
+ blockingIssues: [],
80
+ };
81
+ this.metricsQueries.upsertTask(task);
82
+ }
83
+ }
84
+ catch (error) {
85
+ console.error('Failed to sync from Linear API:', error);
86
+ }
87
+ }
88
+ await this.notifyUpdate();
89
+ }
90
+ async syncFromTaskStore() {
91
+ if (!this.taskStore)
92
+ return 0;
42
93
  try {
43
- // For now, we'll stub this as LinearClient doesn't expose a public query method
44
- // In a real implementation, we'd need to add a public method to LinearClient
45
- // or use LinearSyncEngine from linear-sync.ts
46
- console.log('Linear sync not fully implemented - LinearClient needs public query method');
47
- await this.notifyUpdate();
94
+ // Get all tasks including completed ones
95
+ const allTasks = this.getAllTasksFromStore();
96
+ let synced = 0;
97
+ for (const task of allTasks) {
98
+ const analyticsTask = {
99
+ id: task.id,
100
+ title: task.title,
101
+ state: this.mapTaskStatus(task.status),
102
+ createdAt: new Date(task.created_at * 1000),
103
+ completedAt: task.completed_at
104
+ ? new Date(task.completed_at * 1000)
105
+ : undefined,
106
+ estimatedEffort: task.estimated_effort,
107
+ actualEffort: task.actual_effort,
108
+ assigneeId: task.assignee,
109
+ priority: task.priority,
110
+ labels: task.tags || [],
111
+ blockingIssues: task.depends_on || [],
112
+ };
113
+ this.metricsQueries.upsertTask(analyticsTask);
114
+ synced++;
115
+ }
116
+ return synced;
117
+ }
118
+ catch (error) {
119
+ console.error('Failed to sync from task store:', error);
120
+ return 0;
121
+ }
122
+ }
123
+ getAllTasksFromStore() {
124
+ if (!this.taskStore)
125
+ return [];
126
+ try {
127
+ // Access the db directly to get ALL tasks including completed
128
+ const contextDbPath = path.join(this.projectPath, '.stackmemory', 'context.db');
129
+ const db = new Database(contextDbPath);
130
+ const rows = db
131
+ .prepare(`
132
+ SELECT * FROM task_cache
133
+ ORDER BY created_at DESC
134
+ `)
135
+ .all();
136
+ db.close();
137
+ // Hydrate the rows
138
+ return rows.map((row) => ({
139
+ id: row.id,
140
+ title: row.title,
141
+ description: row.description,
142
+ status: row.status,
143
+ priority: row.priority,
144
+ created_at: row.created_at,
145
+ completed_at: row.completed_at,
146
+ estimated_effort: row.estimated_effort,
147
+ actual_effort: row.actual_effort,
148
+ assignee: row.assignee,
149
+ tags: JSON.parse(row.tags || '[]'),
150
+ depends_on: JSON.parse(row.depends_on || '[]'),
151
+ }));
48
152
  }
49
153
  catch (error) {
50
- console.error('Failed to sync Linear tasks:', error);
154
+ console.error('Failed to get all tasks:', error);
155
+ return [];
51
156
  }
52
157
  }
158
+ mapTaskStatus(status) {
159
+ const statusMap = {
160
+ pending: 'todo',
161
+ in_progress: 'in_progress',
162
+ completed: 'completed',
163
+ blocked: 'blocked',
164
+ cancelled: 'blocked',
165
+ };
166
+ return statusMap[status] || 'todo';
167
+ }
53
168
  mapLinearState(linearState) {
54
169
  const stateMap = {
55
170
  backlog: 'todo',