@stackmemoryai/stackmemory 0.3.17 → 0.3.19

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 (234) hide show
  1. package/dist/cli/claude-sm.js +51 -5
  2. package/dist/cli/claude-sm.js.map +2 -2
  3. package/dist/cli/codex-sm.js +52 -19
  4. package/dist/cli/codex-sm.js.map +2 -2
  5. package/dist/cli/commands/db.js +143 -0
  6. package/dist/cli/commands/db.js.map +7 -0
  7. package/dist/cli/commands/login.js +50 -0
  8. package/dist/cli/commands/login.js.map +7 -0
  9. package/dist/cli/commands/migrate.js +178 -0
  10. package/dist/cli/commands/migrate.js.map +7 -0
  11. package/dist/cli/commands/onboard.js +158 -2
  12. package/dist/cli/commands/onboard.js.map +2 -2
  13. package/dist/cli/commands/skills.js +15 -2
  14. package/dist/cli/commands/skills.js.map +2 -2
  15. package/dist/cli/index.js +118 -834
  16. package/dist/cli/index.js.map +3 -3
  17. package/dist/core/context/dual-stack-manager.js +1 -1
  18. package/dist/core/context/dual-stack-manager.js.map +1 -1
  19. package/dist/core/context/frame-database.js +1 -0
  20. package/dist/core/context/frame-database.js.map +2 -2
  21. package/dist/core/context/frame-manager.js +59 -2
  22. package/dist/core/context/frame-manager.js.map +2 -2
  23. package/dist/core/database/database-adapter.js +6 -1
  24. package/dist/core/database/database-adapter.js.map +2 -2
  25. package/dist/core/database/sqlite-adapter.js +60 -2
  26. package/dist/core/database/sqlite-adapter.js.map +2 -2
  27. package/dist/integrations/claude-code/subagent-client.js +106 -3
  28. package/dist/integrations/claude-code/subagent-client.js.map +2 -2
  29. package/dist/servers/railway/config.js +51 -0
  30. package/dist/servers/railway/config.js.map +7 -0
  31. package/dist/servers/railway/index-enhanced.js +156 -0
  32. package/dist/servers/railway/index-enhanced.js.map +7 -0
  33. package/dist/servers/railway/index.js +843 -82
  34. package/dist/servers/railway/index.js.map +3 -3
  35. package/dist/servers/railway/minimal.js +48 -3
  36. package/dist/servers/railway/minimal.js.map +2 -2
  37. package/dist/servers/railway/storage-test.js +455 -0
  38. package/dist/servers/railway/storage-test.js.map +7 -0
  39. package/dist/skills/claude-skills.js +13 -12
  40. package/dist/skills/claude-skills.js.map +2 -2
  41. package/dist/skills/recursive-agent-orchestrator.js +27 -18
  42. package/dist/skills/recursive-agent-orchestrator.js.map +2 -2
  43. package/dist/skills/unified-rlm-orchestrator.js.map +2 -2
  44. package/package.json +13 -21
  45. package/scripts/README-TESTING.md +186 -0
  46. package/scripts/analyze-cli-security.js +288 -0
  47. package/scripts/archive/add-phase-tasks-to-linear.js +163 -0
  48. package/scripts/archive/analyze-linear-duplicates.js +214 -0
  49. package/scripts/archive/analyze-remaining-duplicates.js +230 -0
  50. package/scripts/archive/analyze-sta-duplicates.js +292 -0
  51. package/scripts/archive/analyze-sta-graphql.js +399 -0
  52. package/scripts/archive/cancel-duplicate-tasks.ts +246 -0
  53. package/scripts/archive/check-all-duplicates.ts +419 -0
  54. package/scripts/archive/clean-duplicate-tasks.js +114 -0
  55. package/scripts/archive/cleanup-duplicate-tasks.ts +286 -0
  56. package/scripts/archive/create-phase-tasks.js +387 -0
  57. package/scripts/archive/delete-linear-duplicates.js +182 -0
  58. package/scripts/archive/delete-remaining-duplicates.js +158 -0
  59. package/scripts/archive/delete-sta-duplicates.js +201 -0
  60. package/scripts/archive/delete-sta-oauth.js +201 -0
  61. package/scripts/archive/export-sta-tasks.js +62 -0
  62. package/scripts/archive/install-auto-sync.js +266 -0
  63. package/scripts/archive/install-chromadb-hooks.sh +133 -0
  64. package/scripts/archive/install-enhanced-clear-hooks.sh +431 -0
  65. package/scripts/archive/install-post-task-hooks.sh +289 -0
  66. package/scripts/archive/install-stackmemory-hooks.sh +420 -0
  67. package/scripts/archive/merge-linear-duplicates-safe.ts +362 -0
  68. package/scripts/archive/merge-linear-duplicates.ts +180 -0
  69. package/scripts/archive/remove-sta-tasks.js +70 -0
  70. package/scripts/archive/setup-background-sync.sh +168 -0
  71. package/scripts/archive/setup-claude-auto-triggers.sh +181 -0
  72. package/scripts/archive/setup-claude-autostart.sh +305 -0
  73. package/scripts/archive/setup-git-hooks.sh +25 -0
  74. package/scripts/archive/setup-linear-oauth.sh +46 -0
  75. package/scripts/archive/setup-mcp.sh +113 -0
  76. package/scripts/archive/setup-railway-deployment.sh +81 -0
  77. package/scripts/auto-handoff.sh +262 -0
  78. package/scripts/background-sync-manager.js +416 -0
  79. package/scripts/benchmark-performance.ts +57 -0
  80. package/scripts/check-redis.ts +48 -0
  81. package/scripts/chromadb-auto-loader.sh +128 -0
  82. package/scripts/chromadb-context-loader.js +479 -0
  83. package/scripts/claude-chromadb-hook.js +460 -0
  84. package/scripts/claude-code-wrapper.sh +66 -0
  85. package/scripts/claude-linear-skill.js +455 -0
  86. package/scripts/claude-pre-commit.sh +302 -0
  87. package/scripts/claude-sm-autostart.js +532 -0
  88. package/scripts/claude-sm-setup.sh +367 -0
  89. package/scripts/claude-with-chromadb.sh +69 -0
  90. package/scripts/claude-worktree-manager.sh +323 -0
  91. package/scripts/claude-worktree-monitor.sh +371 -0
  92. package/scripts/claude-worktree-setup.sh +327 -0
  93. package/scripts/clean-linear-backlog.js +273 -0
  94. package/scripts/cleanup-old-sessions.sh +57 -0
  95. package/scripts/codex-wrapper.sh +88 -0
  96. package/scripts/create-sandbox.sh +269 -0
  97. package/scripts/debug-linear-update.js +174 -0
  98. package/scripts/delete-linear-tasks.js +167 -0
  99. package/scripts/deploy.sh +89 -0
  100. package/scripts/deployment/railway.sh +352 -0
  101. package/scripts/deployment/test-deployment.js +194 -0
  102. package/scripts/detect-and-rehydrate.js +162 -0
  103. package/scripts/detect-and-rehydrate.mjs +165 -0
  104. package/scripts/development/create-demo-tasks.js +143 -0
  105. package/scripts/development/debug-frame-test.js +16 -0
  106. package/scripts/development/demo-auto-sync.js +128 -0
  107. package/scripts/development/fix-all-imports.js +213 -0
  108. package/scripts/development/fix-imports.js +229 -0
  109. package/scripts/development/fix-lint-loop.cjs +103 -0
  110. package/scripts/development/fix-project-id.ts +161 -0
  111. package/scripts/development/fix-strict-mode-issues.ts +291 -0
  112. package/scripts/development/reorganize-structure.sh +228 -0
  113. package/scripts/development/test-persistence-direct.js +148 -0
  114. package/scripts/development/test-persistence.js +114 -0
  115. package/scripts/development/test-tasks.js +93 -0
  116. package/scripts/development/update-imports.js +212 -0
  117. package/scripts/fetch-linear-status.js +125 -0
  118. package/scripts/git-hooks/README.md +310 -0
  119. package/scripts/git-hooks/branch-context-manager.sh +342 -0
  120. package/scripts/git-hooks/post-checkout-stackmemory.sh +63 -0
  121. package/scripts/git-hooks/post-commit-stackmemory.sh +305 -0
  122. package/scripts/git-hooks/pre-commit-stackmemory.sh +275 -0
  123. package/scripts/hooks/cleanup-shell.sh +130 -0
  124. package/scripts/hooks/task-complete.sh +114 -0
  125. package/scripts/initialize.ts +129 -0
  126. package/scripts/install-claude-hooks-auto.js +104 -0
  127. package/scripts/install-claude-hooks.sh +133 -0
  128. package/scripts/install-global.sh +296 -0
  129. package/scripts/install.sh +235 -0
  130. package/scripts/linear-auto-sync.js +262 -0
  131. package/scripts/linear-auto-sync.sh +161 -0
  132. package/scripts/linear-sync-daemon.js +150 -0
  133. package/scripts/linear-task-review.js +237 -0
  134. package/scripts/list-linear-tasks.ts +178 -0
  135. package/scripts/mcp-proxy.js +66 -0
  136. package/scripts/opencode-wrapper.sh +85 -0
  137. package/scripts/publish-local.js +74 -0
  138. package/scripts/query-chromadb.ts +201 -0
  139. package/scripts/railway-env-setup.sh +39 -0
  140. package/scripts/reconcile-local-tasks.js +170 -0
  141. package/scripts/recreate-frames-db.js +89 -0
  142. package/scripts/setup/claude-integration.js +138 -0
  143. package/scripts/setup/configure-alias.js +125 -0
  144. package/scripts/setup/configure-codex-alias.js +161 -0
  145. package/scripts/setup/configure-opencode-alias.js +175 -0
  146. package/scripts/setup-claude-integration.js +204 -0
  147. package/scripts/setup-claude-integration.sh +183 -0
  148. package/scripts/setup-railway-deployment.sh +37 -0
  149. package/scripts/setup.sh +31 -0
  150. package/scripts/show-linear-summary.ts +172 -0
  151. package/scripts/stackmemory-auto-handoff.sh +231 -0
  152. package/scripts/stackmemory-daemon.sh +40 -0
  153. package/scripts/start-linear-sync-daemon.sh +141 -0
  154. package/scripts/start-temporal-paradox.sh +214 -0
  155. package/scripts/status.ts +159 -0
  156. package/scripts/sync-and-clean-tasks.js +258 -0
  157. package/scripts/sync-frames-from-railway.js +228 -0
  158. package/scripts/sync-linear-graphql.js +303 -0
  159. package/scripts/sync-linear-tasks.js +186 -0
  160. package/scripts/test-auto-triggers.sh +57 -0
  161. package/scripts/test-browser-mcp.js +74 -0
  162. package/scripts/test-chromadb-full.js +115 -0
  163. package/scripts/test-chromadb-hooks.sh +28 -0
  164. package/scripts/test-chromadb-sync.ts +245 -0
  165. package/scripts/test-cli-security.js +293 -0
  166. package/scripts/test-hooks-persistence.sh +220 -0
  167. package/scripts/test-installation-scenarios.sh +359 -0
  168. package/scripts/test-installation.sh +224 -0
  169. package/scripts/test-mcp.js +163 -0
  170. package/scripts/test-pre-publish-quick.sh +75 -0
  171. package/scripts/test-quality-gates.sh +263 -0
  172. package/scripts/test-railway-db.js +222 -0
  173. package/scripts/test-redis-storage.ts +490 -0
  174. package/scripts/test-rlm-basic.sh +122 -0
  175. package/scripts/test-rlm-comprehensive.sh +260 -0
  176. package/scripts/test-rlm-e2e.sh +268 -0
  177. package/scripts/test-rlm-simple.js +90 -0
  178. package/scripts/test-rlm.js +110 -0
  179. package/scripts/test-session-handoff.sh +165 -0
  180. package/scripts/test-shell-integration.sh +275 -0
  181. package/scripts/testing/ab-test-runner.ts +508 -0
  182. package/scripts/testing/collect-metrics.ts +457 -0
  183. package/scripts/testing/quick-effectiveness-demo.js +187 -0
  184. package/scripts/testing/real-performance-test.js +422 -0
  185. package/scripts/testing/run-effectiveness-tests.sh +176 -0
  186. package/scripts/testing/scripts/testing/ab-test-runner.js +363 -0
  187. package/scripts/testing/scripts/testing/collect-metrics.js +292 -0
  188. package/scripts/testing/simple-effectiveness-test.js +310 -0
  189. package/scripts/testing/src/core/context/context-bridge.js +253 -0
  190. package/scripts/testing/src/core/context/frame-manager.js +746 -0
  191. package/scripts/testing/src/core/context/shared-context-layer.js +437 -0
  192. package/scripts/testing/src/core/database/database-adapter.js +54 -0
  193. package/scripts/testing/src/core/errors/index.js +291 -0
  194. package/scripts/testing/src/core/errors/recovery.js +268 -0
  195. package/scripts/testing/src/core/monitoring/logger.js +145 -0
  196. package/scripts/testing/src/core/retrieval/context-retriever.js +516 -0
  197. package/scripts/testing/src/core/session/index.js +1 -0
  198. package/scripts/testing/src/core/session/session-manager.js +323 -0
  199. package/scripts/testing/src/core/trace/cli-trace-wrapper.js +140 -0
  200. package/scripts/testing/src/core/trace/db-trace-wrapper.js +251 -0
  201. package/scripts/testing/src/core/trace/debug-trace.js +398 -0
  202. package/scripts/testing/src/core/trace/index.js +120 -0
  203. package/scripts/testing/src/core/trace/linear-api-wrapper.js +204 -0
  204. package/scripts/update-linear-status.js +268 -0
  205. package/scripts/update-linear-tasks-fixed.js +284 -0
  206. package/scripts/verify-railway-schema.ts +35 -0
  207. package/templates/claude-hooks/hooks.json +5 -0
  208. package/templates/claude-hooks/on-clear.js +56 -0
  209. package/templates/claude-hooks/on-startup.js +56 -0
  210. package/templates/claude-hooks/tool-use-trace.js +67 -0
  211. package/dist/features/tui/components/analytics-panel.js +0 -157
  212. package/dist/features/tui/components/analytics-panel.js.map +0 -7
  213. package/dist/features/tui/components/frame-visualizer.js +0 -377
  214. package/dist/features/tui/components/frame-visualizer.js.map +0 -7
  215. package/dist/features/tui/components/pr-tracker.js +0 -135
  216. package/dist/features/tui/components/pr-tracker.js.map +0 -7
  217. package/dist/features/tui/components/session-monitor.js +0 -299
  218. package/dist/features/tui/components/session-monitor.js.map +0 -7
  219. package/dist/features/tui/components/subagent-fleet.js +0 -395
  220. package/dist/features/tui/components/subagent-fleet.js.map +0 -7
  221. package/dist/features/tui/components/task-board.js +0 -1139
  222. package/dist/features/tui/components/task-board.js.map +0 -7
  223. package/dist/features/tui/index.js +0 -408
  224. package/dist/features/tui/index.js.map +0 -7
  225. package/dist/features/tui/services/data-service.js +0 -641
  226. package/dist/features/tui/services/data-service.js.map +0 -7
  227. package/dist/features/tui/services/linear-task-reader.js +0 -102
  228. package/dist/features/tui/services/linear-task-reader.js.map +0 -7
  229. package/dist/features/tui/services/websocket-client.js +0 -162
  230. package/dist/features/tui/services/websocket-client.js.map +0 -7
  231. package/dist/features/tui/terminal-compat.js +0 -220
  232. package/dist/features/tui/terminal-compat.js.map +0 -7
  233. package/dist/features/tui/types.js +0 -1
  234. package/dist/features/tui/types.js.map +0 -7
@@ -0,0 +1,323 @@
1
+ /**
2
+ * Session Management for StackMemory
3
+ * Provides session persistence and recovery across CLI invocations
4
+ */
5
+ import { v4 as uuidv4 } from 'uuid';
6
+ import * as fs from 'fs/promises';
7
+ import * as path from 'path';
8
+ import { logger } from '../monitoring/logger.js';
9
+ import { SystemError, ErrorCode } from '../errors/index.js';
10
+ export var FrameQueryMode;
11
+ (function (FrameQueryMode) {
12
+ FrameQueryMode["CURRENT_SESSION"] = "current";
13
+ FrameQueryMode["PROJECT_ACTIVE"] = "project";
14
+ FrameQueryMode["ALL_ACTIVE"] = "all";
15
+ FrameQueryMode["HISTORICAL"] = "historical";
16
+ })(FrameQueryMode || (FrameQueryMode = {}));
17
+ export class SessionManager {
18
+ constructor() {
19
+ this.currentSession = null;
20
+ this.STALE_THRESHOLD = 24 * 60 * 60 * 1000; // 24 hours
21
+ const homeDir = process.env.HOME || process.env.USERPROFILE || '';
22
+ this.sessionsDir = path.join(homeDir, '.stackmemory', 'sessions');
23
+ }
24
+ static getInstance() {
25
+ if (!SessionManager.instance) {
26
+ SessionManager.instance = new SessionManager();
27
+ }
28
+ return SessionManager.instance;
29
+ }
30
+ async initialize() {
31
+ try {
32
+ await fs.mkdir(this.sessionsDir, { recursive: true });
33
+ await fs.mkdir(path.join(this.sessionsDir, 'projects'), {
34
+ recursive: true,
35
+ });
36
+ await fs.mkdir(path.join(this.sessionsDir, 'history'), {
37
+ recursive: true,
38
+ });
39
+ }
40
+ catch (error) {
41
+ throw new SystemError('Failed to initialize session directories', ErrorCode.INITIALIZATION_ERROR, { error, sessionsDir: this.sessionsDir });
42
+ }
43
+ }
44
+ async getOrCreateSession(options) {
45
+ // 1. Check explicit session ID
46
+ if (options?.sessionId) {
47
+ const session = await this.loadSession(options.sessionId);
48
+ if (session) {
49
+ this.currentSession = session;
50
+ return session;
51
+ }
52
+ }
53
+ // 2. Check environment variable
54
+ const envSessionId = process.env.STACKMEMORY_SESSION;
55
+ if (envSessionId) {
56
+ const session = await this.loadSession(envSessionId);
57
+ if (session) {
58
+ this.currentSession = session;
59
+ return session;
60
+ }
61
+ }
62
+ // 3. Check project + branch context
63
+ const projectHash = await this.getProjectHash(options?.projectPath);
64
+ const branch = options?.branch || (await this.getGitBranch(options?.projectPath));
65
+ if (projectHash) {
66
+ // Try project+branch session
67
+ const branchSession = await this.findProjectBranchSession(projectHash, branch);
68
+ if (branchSession && this.isSessionRecent(branchSession)) {
69
+ await this.touchSession(branchSession);
70
+ this.currentSession = branchSession;
71
+ return branchSession;
72
+ }
73
+ // Try last active for project
74
+ const lastActive = await this.findLastActiveSession(projectHash);
75
+ if (lastActive && this.isSessionRecent(lastActive)) {
76
+ await this.touchSession(lastActive);
77
+ this.currentSession = lastActive;
78
+ return lastActive;
79
+ }
80
+ }
81
+ // 4. Create new session
82
+ const newSession = await this.createSession({
83
+ projectId: projectHash || 'global',
84
+ branch,
85
+ metadata: options?.metadata,
86
+ });
87
+ this.currentSession = newSession;
88
+ return newSession;
89
+ }
90
+ async createSession(params) {
91
+ const session = {
92
+ sessionId: uuidv4(),
93
+ runId: uuidv4(),
94
+ projectId: params.projectId,
95
+ branch: params.branch,
96
+ startedAt: Date.now(),
97
+ lastActiveAt: Date.now(),
98
+ metadata: {
99
+ ...params.metadata,
100
+ user: process.env.USER,
101
+ environment: process.env.NODE_ENV || 'development',
102
+ cliVersion: process.env.npm_package_version,
103
+ },
104
+ state: 'active',
105
+ };
106
+ await this.saveSession(session);
107
+ await this.setProjectActiveSession(params.projectId, session.sessionId);
108
+ // Set as current session
109
+ this.currentSession = session;
110
+ logger.info('Created new session', {
111
+ sessionId: session.sessionId,
112
+ projectId: session.projectId,
113
+ branch: session.branch,
114
+ });
115
+ return session;
116
+ }
117
+ async loadSession(sessionId) {
118
+ try {
119
+ const sessionPath = path.join(this.sessionsDir, `${sessionId}.json`);
120
+ const data = await fs.readFile(sessionPath, 'utf-8');
121
+ return JSON.parse(data);
122
+ }
123
+ catch (error) {
124
+ // Check history
125
+ try {
126
+ const historyPath = path.join(this.sessionsDir, 'history', `${sessionId}.json`);
127
+ const data = await fs.readFile(historyPath, 'utf-8');
128
+ return JSON.parse(data);
129
+ }
130
+ catch {
131
+ return null;
132
+ }
133
+ }
134
+ }
135
+ async saveSession(session) {
136
+ const sessionPath = path.join(this.sessionsDir, `${session.sessionId}.json`);
137
+ await fs.writeFile(sessionPath, JSON.stringify(session, null, 2));
138
+ }
139
+ async suspendSession(sessionId) {
140
+ const id = sessionId || this.currentSession?.sessionId;
141
+ if (!id)
142
+ return;
143
+ const session = await this.loadSession(id);
144
+ if (session) {
145
+ session.state = 'suspended';
146
+ session.lastActiveAt = Date.now();
147
+ await this.saveSession(session);
148
+ }
149
+ }
150
+ async resumeSession(sessionId) {
151
+ const session = await this.loadSession(sessionId);
152
+ if (!session) {
153
+ throw new SystemError('Session not found', ErrorCode.NOT_FOUND, {
154
+ sessionId,
155
+ });
156
+ }
157
+ session.state = 'active';
158
+ session.lastActiveAt = Date.now();
159
+ await this.saveSession(session);
160
+ this.currentSession = session;
161
+ return session;
162
+ }
163
+ async closeSession(sessionId) {
164
+ const id = sessionId || this.currentSession?.sessionId;
165
+ if (!id)
166
+ return;
167
+ const session = await this.loadSession(id);
168
+ if (session) {
169
+ session.state = 'closed';
170
+ session.lastActiveAt = Date.now();
171
+ // Move to history
172
+ const sessionPath = path.join(this.sessionsDir, `${session.sessionId}.json`);
173
+ const historyPath = path.join(this.sessionsDir, 'history', `${session.sessionId}.json`);
174
+ await fs.rename(sessionPath, historyPath);
175
+ }
176
+ }
177
+ async listSessions(filter) {
178
+ const sessions = [];
179
+ // Load active sessions
180
+ const files = await fs.readdir(this.sessionsDir);
181
+ for (const file of files) {
182
+ if (file.endsWith('.json')) {
183
+ const session = await this.loadSession(file.replace('.json', ''));
184
+ if (session) {
185
+ sessions.push(session);
186
+ }
187
+ }
188
+ }
189
+ // Apply filters
190
+ return sessions.filter((s) => {
191
+ if (filter?.projectId && s.projectId !== filter.projectId)
192
+ return false;
193
+ if (filter?.state && s.state !== filter.state)
194
+ return false;
195
+ if (filter?.branch && s.branch !== filter.branch)
196
+ return false;
197
+ return true;
198
+ });
199
+ }
200
+ async mergeSessions(sourceId, targetId) {
201
+ const source = await this.loadSession(sourceId);
202
+ const target = await this.loadSession(targetId);
203
+ if (!source || !target) {
204
+ throw new SystemError('Session not found for merge', ErrorCode.NOT_FOUND, { sourceId, targetId });
205
+ }
206
+ // Merge metadata
207
+ target.metadata = {
208
+ ...target.metadata,
209
+ ...source.metadata,
210
+ tags: [...(target.metadata.tags || []), ...(source.metadata.tags || [])],
211
+ };
212
+ // Update timestamps
213
+ target.lastActiveAt = Date.now();
214
+ // Close source session
215
+ await this.closeSession(sourceId);
216
+ await this.saveSession(target);
217
+ logger.info('Merged sessions', {
218
+ source: sourceId,
219
+ target: targetId,
220
+ });
221
+ return target;
222
+ }
223
+ async cleanupStaleSessions(maxAge = 30 * 24 * 60 * 60 * 1000) {
224
+ const historyDir = path.join(this.sessionsDir, 'history');
225
+ const files = await fs.readdir(historyDir);
226
+ const cutoff = Date.now() - maxAge;
227
+ let cleaned = 0;
228
+ for (const file of files) {
229
+ if (file.endsWith('.json')) {
230
+ const filePath = path.join(historyDir, file);
231
+ const stats = await fs.stat(filePath);
232
+ if (stats.mtimeMs < cutoff) {
233
+ await fs.unlink(filePath);
234
+ cleaned++;
235
+ }
236
+ }
237
+ }
238
+ logger.info(`Cleaned up ${cleaned} stale sessions`);
239
+ return cleaned;
240
+ }
241
+ getCurrentSession() {
242
+ return this.currentSession;
243
+ }
244
+ getSessionRunId() {
245
+ return this.currentSession?.runId || uuidv4();
246
+ }
247
+ async getProjectHash(projectPath) {
248
+ try {
249
+ const cwd = projectPath || process.cwd();
250
+ const pathModule = await import('path');
251
+ // Try to get git remote first (consistent with project-manager)
252
+ let identifier;
253
+ try {
254
+ const { execSync } = await import('child_process');
255
+ identifier = execSync('git config --get remote.origin.url', {
256
+ cwd,
257
+ encoding: 'utf-8',
258
+ timeout: 5000,
259
+ }).trim();
260
+ }
261
+ catch {
262
+ // Fall back to directory path
263
+ identifier = cwd;
264
+ }
265
+ // Use same algorithm as project-manager.generateProjectId
266
+ const cleaned = identifier
267
+ .replace(/\.git$/, '')
268
+ .replace(/[^a-zA-Z0-9-]/g, '-')
269
+ .toLowerCase();
270
+ return cleaned.substring(cleaned.length - 50);
271
+ }
272
+ catch {
273
+ return null;
274
+ }
275
+ }
276
+ async getGitBranch(projectPath) {
277
+ try {
278
+ const { execSync } = await import('child_process');
279
+ const cwd = projectPath || process.cwd();
280
+ const branch = execSync('git rev-parse --abbrev-ref HEAD', {
281
+ cwd,
282
+ encoding: 'utf-8',
283
+ }).trim();
284
+ return branch;
285
+ }
286
+ catch {
287
+ return undefined;
288
+ }
289
+ }
290
+ async findProjectBranchSession(projectHash, branch) {
291
+ if (!branch)
292
+ return null;
293
+ const sessions = await this.listSessions({
294
+ projectId: projectHash,
295
+ state: 'active',
296
+ branch,
297
+ });
298
+ return sessions.sort((a, b) => b.lastActiveAt - a.lastActiveAt)[0] || null;
299
+ }
300
+ async findLastActiveSession(projectHash) {
301
+ const sessions = await this.listSessions({
302
+ projectId: projectHash,
303
+ state: 'active',
304
+ });
305
+ return sessions.sort((a, b) => b.lastActiveAt - a.lastActiveAt)[0] || null;
306
+ }
307
+ async setProjectActiveSession(projectId, sessionId) {
308
+ const projectFile = path.join(this.sessionsDir, 'projects', `${projectId}.json`);
309
+ await fs.writeFile(projectFile, JSON.stringify({
310
+ projectId,
311
+ activeSessionId: sessionId,
312
+ updatedAt: Date.now(),
313
+ }, null, 2));
314
+ }
315
+ isSessionRecent(session) {
316
+ return Date.now() - session.lastActiveAt < this.STALE_THRESHOLD;
317
+ }
318
+ async touchSession(session) {
319
+ session.lastActiveAt = Date.now();
320
+ await this.saveSession(session);
321
+ }
322
+ }
323
+ export const sessionManager = SessionManager.getInstance();
@@ -0,0 +1,140 @@
1
+ /**
2
+ * CLI Command Trace Wrapper
3
+ * Automatically wraps Commander.js commands with comprehensive tracing
4
+ */
5
+ import { trace } from './debug-trace.js';
6
+ import { logger } from '../monitoring/logger.js';
7
+ export function wrapCommand(command) {
8
+ const originalAction = command.action.bind(command);
9
+ command.action(async function (...args) {
10
+ // Extract command path and options
11
+ const commandPath = getCommandPath(command);
12
+ const options = args[args.length - 1];
13
+ const commandArgs = args.slice(0, -1);
14
+ // Build comprehensive context
15
+ const context = {
16
+ command: commandPath,
17
+ args: commandArgs,
18
+ options: typeof options === 'object' ? options : {},
19
+ cwd: process.cwd(),
20
+ env: {
21
+ NODE_ENV: process.env.NODE_ENV,
22
+ DEBUG_TRACE: process.env.DEBUG_TRACE,
23
+ LINEAR_API_KEY: process.env.LINEAR_API_KEY ? '[SET]' : '[NOT SET]',
24
+ },
25
+ timestamp: new Date().toISOString(),
26
+ };
27
+ // Log command start
28
+ logger.info(`CLI Command: ${commandPath}`, context);
29
+ // Wrap the actual action with tracing
30
+ await trace.command(commandPath, context, async () => {
31
+ try {
32
+ // Call the original action with wrapped handler
33
+ const result = await originalAction.apply(null, args);
34
+ // Log successful completion
35
+ logger.info(`CLI Command Completed: ${commandPath}`, {
36
+ duration: trace.exportTraces().find(t => t.name === commandPath)?.duration,
37
+ });
38
+ // Show execution summary if verbose
39
+ if (process.env.DEBUG_TRACE === 'true') {
40
+ console.log(trace.getExecutionSummary());
41
+ }
42
+ }
43
+ catch (error) {
44
+ // Enhanced error logging for CLI commands
45
+ logger.error(`CLI Command Failed: ${commandPath}`, error, context);
46
+ // Get the last error trace for debugging
47
+ const lastError = trace.getLastError();
48
+ if (lastError) {
49
+ console.error('\nšŸ“ Error occurred at:');
50
+ console.error(` ${lastError.name}`);
51
+ if (lastError.params) {
52
+ console.error(' With params:', JSON.stringify(lastError.params, null, 2));
53
+ }
54
+ console.error(' Error details:', lastError.error);
55
+ }
56
+ // Re-throw to maintain original error handling
57
+ throw error;
58
+ }
59
+ });
60
+ });
61
+ // Recursively wrap subcommands
62
+ command.commands.forEach(subcommand => {
63
+ wrapCommand(subcommand);
64
+ });
65
+ return command;
66
+ }
67
+ function getCommandPath(command) {
68
+ const parts = [];
69
+ let current = command;
70
+ while (current) {
71
+ if (current.name()) {
72
+ parts.unshift(current.name());
73
+ }
74
+ current = current.parent;
75
+ }
76
+ return parts.join(' ');
77
+ }
78
+ /**
79
+ * Wrap the main program with comprehensive tracing
80
+ */
81
+ export function wrapProgram(program) {
82
+ // Add global error handler with tracing
83
+ program.exitOverride((err) => {
84
+ if (err.code === 'commander.help' || err.code === 'commander.version') {
85
+ // Normal help/version display, not an error
86
+ process.exit(0);
87
+ }
88
+ // Log the error with full context
89
+ logger.error('CLI Error', err, {
90
+ code: err.code,
91
+ exitCode: err.exitCode,
92
+ command: process.argv.slice(2).join(' '),
93
+ });
94
+ // Show trace summary on error
95
+ if (process.env.DEBUG_TRACE === 'true') {
96
+ console.error('\n' + trace.getExecutionSummary());
97
+ }
98
+ process.exit(err.exitCode || 1);
99
+ });
100
+ // Add pre-action hook for setup
101
+ program.hook('preAction', (thisCommand) => {
102
+ // Initialize trace context for this command
103
+ trace.reset();
104
+ // Log command invocation
105
+ const commandPath = getCommandPath(thisCommand);
106
+ logger.debug(`Preparing to execute: ${commandPath}`, {
107
+ args: thisCommand.args,
108
+ opts: thisCommand.opts(),
109
+ });
110
+ });
111
+ // Add post-action hook for cleanup
112
+ program.hook('postAction', (thisCommand) => {
113
+ // Log completion
114
+ const commandPath = getCommandPath(thisCommand);
115
+ logger.debug(`Completed execution: ${commandPath}`);
116
+ });
117
+ // Wrap all existing commands
118
+ program.commands.forEach(command => {
119
+ wrapCommand(command);
120
+ });
121
+ return program;
122
+ }
123
+ /**
124
+ * Helper to wrap async functions with step tracing
125
+ */
126
+ export function traceStep(name, fn) {
127
+ return trace.step(name, fn);
128
+ }
129
+ /**
130
+ * Helper to wrap database queries
131
+ */
132
+ export function traceQuery(sql, params, fn) {
133
+ return trace.traceSync('query', sql.substring(0, 100), params, fn);
134
+ }
135
+ /**
136
+ * Helper to wrap API calls
137
+ */
138
+ export function traceAPI(method, url, body, fn) {
139
+ return trace.api(method, url, body, fn);
140
+ }