specweave 0.32.10 → 0.33.3

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 (224) hide show
  1. package/CLAUDE.md +162 -1
  2. package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts +120 -0
  3. package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts.map +1 -0
  4. package/dist/plugins/specweave-ado/lib/per-us-sync.js +276 -0
  5. package/dist/plugins/specweave-ado/lib/per-us-sync.js.map +1 -0
  6. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +4 -1
  7. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
  8. package/dist/plugins/specweave-github/lib/github-client-v2.js +13 -3
  9. package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
  10. package/dist/plugins/specweave-github/lib/per-us-sync.d.ts +97 -0
  11. package/dist/plugins/specweave-github/lib/per-us-sync.d.ts.map +1 -0
  12. package/dist/plugins/specweave-github/lib/per-us-sync.js +274 -0
  13. package/dist/plugins/specweave-github/lib/per-us-sync.js.map +1 -0
  14. package/dist/plugins/specweave-jira/lib/per-us-sync.d.ts +113 -0
  15. package/dist/plugins/specweave-jira/lib/per-us-sync.d.ts.map +1 -0
  16. package/dist/plugins/specweave-jira/lib/per-us-sync.js +254 -0
  17. package/dist/plugins/specweave-jira/lib/per-us-sync.js.map +1 -0
  18. package/dist/src/cli/add-child-pid.d.ts +11 -0
  19. package/dist/src/cli/add-child-pid.d.ts.map +1 -0
  20. package/dist/src/cli/add-child-pid.js +42 -0
  21. package/dist/src/cli/add-child-pid.js.map +1 -0
  22. package/dist/src/cli/add-child-process.d.ts +15 -0
  23. package/dist/src/cli/add-child-process.d.ts.map +1 -0
  24. package/dist/src/cli/add-child-process.js +40 -0
  25. package/dist/src/cli/add-child-process.js.map +1 -0
  26. package/dist/src/cli/check-watchdog.d.ts +15 -0
  27. package/dist/src/cli/check-watchdog.d.ts.map +1 -0
  28. package/dist/src/cli/check-watchdog.js +47 -0
  29. package/dist/src/cli/check-watchdog.js.map +1 -0
  30. package/dist/src/cli/cleanup-zombies.d.ts +14 -0
  31. package/dist/src/cli/cleanup-zombies.d.ts.map +1 -0
  32. package/dist/src/cli/cleanup-zombies.js +268 -0
  33. package/dist/src/cli/cleanup-zombies.js.map +1 -0
  34. package/dist/src/cli/find-session-by-pid.d.ts +14 -0
  35. package/dist/src/cli/find-session-by-pid.d.ts.map +1 -0
  36. package/dist/src/cli/find-session-by-pid.js +45 -0
  37. package/dist/src/cli/find-session-by-pid.js.map +1 -0
  38. package/dist/src/cli/get-stale-sessions.d.ts +17 -0
  39. package/dist/src/cli/get-stale-sessions.d.ts.map +1 -0
  40. package/dist/src/cli/get-stale-sessions.js +36 -0
  41. package/dist/src/cli/get-stale-sessions.js.map +1 -0
  42. package/dist/src/cli/register-session.d.ts +16 -0
  43. package/dist/src/cli/register-session.d.ts.map +1 -0
  44. package/dist/src/cli/register-session.js +48 -0
  45. package/dist/src/cli/register-session.js.map +1 -0
  46. package/dist/src/cli/remove-session.d.ts +11 -0
  47. package/dist/src/cli/remove-session.d.ts.map +1 -0
  48. package/dist/src/cli/remove-session.js +36 -0
  49. package/dist/src/cli/remove-session.js.map +1 -0
  50. package/dist/src/cli/update-heartbeat.d.ts +11 -0
  51. package/dist/src/cli/update-heartbeat.d.ts.map +1 -0
  52. package/dist/src/cli/update-heartbeat.js +36 -0
  53. package/dist/src/cli/update-heartbeat.js.map +1 -0
  54. package/dist/src/config/types.d.ts +1208 -203
  55. package/dist/src/config/types.d.ts.map +1 -1
  56. package/dist/src/core/background/job-manager.d.ts +16 -0
  57. package/dist/src/core/background/job-manager.d.ts.map +1 -1
  58. package/dist/src/core/background/job-manager.js +110 -15
  59. package/dist/src/core/background/job-manager.js.map +1 -1
  60. package/dist/src/core/config/config-manager.d.ts.map +1 -1
  61. package/dist/src/core/config/config-manager.js +58 -0
  62. package/dist/src/core/config/config-manager.js.map +1 -1
  63. package/dist/src/core/config/types.d.ts +80 -0
  64. package/dist/src/core/config/types.d.ts.map +1 -1
  65. package/dist/src/core/config/types.js.map +1 -1
  66. package/dist/src/core/increment/increment-utils.d.ts +26 -1
  67. package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
  68. package/dist/src/core/increment/increment-utils.js +66 -4
  69. package/dist/src/core/increment/increment-utils.js.map +1 -1
  70. package/dist/src/core/increment/status-change-sync-trigger.d.ts +3 -1
  71. package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -1
  72. package/dist/src/core/increment/status-change-sync-trigger.js +5 -2
  73. package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -1
  74. package/dist/src/core/living-docs/cross-project-sync.d.ts +87 -15
  75. package/dist/src/core/living-docs/cross-project-sync.d.ts.map +1 -1
  76. package/dist/src/core/living-docs/cross-project-sync.js +147 -28
  77. package/dist/src/core/living-docs/cross-project-sync.js.map +1 -1
  78. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts.map +1 -1
  79. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js +48 -12
  80. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js.map +1 -1
  81. package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.d.ts +70 -0
  82. package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.d.ts.map +1 -0
  83. package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.js +188 -0
  84. package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.js.map +1 -0
  85. package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.d.ts +33 -0
  86. package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.d.ts.map +1 -0
  87. package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.js +290 -0
  88. package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.js.map +1 -0
  89. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts.map +1 -1
  90. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js +114 -11
  91. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js.map +1 -1
  92. package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.d.ts +23 -0
  93. package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.d.ts.map +1 -0
  94. package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.js +283 -0
  95. package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.js.map +1 -0
  96. package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.d.ts +44 -0
  97. package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.d.ts.map +1 -0
  98. package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.js +61 -0
  99. package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.js.map +1 -0
  100. package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.d.ts +126 -0
  101. package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.d.ts.map +1 -0
  102. package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.js +378 -0
  103. package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.js.map +1 -0
  104. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts.map +1 -1
  105. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js +57 -0
  106. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js.map +1 -1
  107. package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.d.ts +82 -0
  108. package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.d.ts.map +1 -0
  109. package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.js +430 -0
  110. package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.js.map +1 -0
  111. package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.d.ts +84 -0
  112. package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.d.ts.map +1 -0
  113. package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.js +387 -0
  114. package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.js.map +1 -0
  115. package/dist/src/core/living-docs/intelligent-analyzer/report-writer.d.ts +61 -0
  116. package/dist/src/core/living-docs/intelligent-analyzer/report-writer.d.ts.map +1 -0
  117. package/dist/src/core/living-docs/intelligent-analyzer/report-writer.js +174 -0
  118. package/dist/src/core/living-docs/intelligent-analyzer/report-writer.js.map +1 -0
  119. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +1 -1
  120. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -1
  121. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  122. package/dist/src/core/living-docs/living-docs-sync.js +26 -22
  123. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  124. package/dist/src/core/living-docs/module-analyzer.d.ts +3 -0
  125. package/dist/src/core/living-docs/module-analyzer.d.ts.map +1 -1
  126. package/dist/src/core/living-docs/module-analyzer.js +40 -1
  127. package/dist/src/core/living-docs/module-analyzer.js.map +1 -1
  128. package/dist/src/core/living-docs/types.d.ts +24 -3
  129. package/dist/src/core/living-docs/types.d.ts.map +1 -1
  130. package/dist/src/core/qa/qa-runner.js +1 -1
  131. package/dist/src/core/qa/qa-runner.js.map +1 -1
  132. package/dist/src/core/scheduler/session-sync-executor.js +1 -1
  133. package/dist/src/core/scheduler/session-sync-executor.js.map +1 -1
  134. package/dist/src/core/status-line/status-line-updater.d.ts +1 -1
  135. package/dist/src/core/status-line/status-line-updater.d.ts.map +1 -1
  136. package/dist/src/core/status-line/status-line-updater.js +4 -3
  137. package/dist/src/core/status-line/status-line-updater.js.map +1 -1
  138. package/dist/src/core/types/config.d.ts +79 -0
  139. package/dist/src/core/types/config.d.ts.map +1 -1
  140. package/dist/src/core/types/config.js.map +1 -1
  141. package/dist/src/importers/jira-importer.d.ts.map +1 -1
  142. package/dist/src/importers/jira-importer.js +18 -9
  143. package/dist/src/importers/jira-importer.js.map +1 -1
  144. package/dist/src/init/architecture/types.d.ts +140 -33
  145. package/dist/src/init/architecture/types.d.ts.map +1 -1
  146. package/dist/src/init/compliance/types.d.ts +27 -30
  147. package/dist/src/init/compliance/types.d.ts.map +1 -1
  148. package/dist/src/init/repo/types.d.ts +34 -11
  149. package/dist/src/init/repo/types.d.ts.map +1 -1
  150. package/dist/src/init/research/src/config/types.d.ts +82 -15
  151. package/dist/src/init/research/src/config/types.d.ts.map +1 -1
  152. package/dist/src/init/research/types.d.ts +93 -38
  153. package/dist/src/init/research/types.d.ts.map +1 -1
  154. package/dist/src/init/team/types.d.ts +42 -4
  155. package/dist/src/init/team/types.d.ts.map +1 -1
  156. package/dist/src/sync/ado-reconciler.js +1 -1
  157. package/dist/src/sync/ado-reconciler.js.map +1 -1
  158. package/dist/src/sync/github-reconciler.js +1 -1
  159. package/dist/src/sync/github-reconciler.js.map +1 -1
  160. package/dist/src/sync/jira-reconciler.js +1 -1
  161. package/dist/src/sync/jira-reconciler.js.map +1 -1
  162. package/dist/src/sync/sync-coordinator.d.ts +20 -0
  163. package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
  164. package/dist/src/sync/sync-coordinator.js +258 -33
  165. package/dist/src/sync/sync-coordinator.js.map +1 -1
  166. package/dist/src/types/session.d.ts +65 -0
  167. package/dist/src/types/session.d.ts.map +1 -0
  168. package/dist/src/types/session.js +8 -0
  169. package/dist/src/types/session.js.map +1 -0
  170. package/dist/src/utils/lock-manager.d.ts +48 -0
  171. package/dist/src/utils/lock-manager.d.ts.map +1 -0
  172. package/dist/src/utils/lock-manager.js +195 -0
  173. package/dist/src/utils/lock-manager.js.map +1 -0
  174. package/dist/src/utils/notification-manager.d.ts +45 -0
  175. package/dist/src/utils/notification-manager.d.ts.map +1 -0
  176. package/dist/src/utils/notification-manager.js +130 -0
  177. package/dist/src/utils/notification-manager.js.map +1 -0
  178. package/dist/src/utils/platform-utils.d.ts +136 -0
  179. package/dist/src/utils/platform-utils.d.ts.map +1 -0
  180. package/dist/src/utils/platform-utils.js +366 -0
  181. package/dist/src/utils/platform-utils.js.map +1 -0
  182. package/dist/src/utils/project-resolver.d.ts +156 -0
  183. package/dist/src/utils/project-resolver.d.ts.map +1 -0
  184. package/dist/src/utils/project-resolver.js +587 -0
  185. package/dist/src/utils/project-resolver.js.map +1 -0
  186. package/dist/src/utils/session-registry.d.ts +142 -0
  187. package/dist/src/utils/session-registry.d.ts.map +1 -0
  188. package/dist/src/utils/session-registry.js +480 -0
  189. package/dist/src/utils/session-registry.js.map +1 -0
  190. package/package.json +5 -2
  191. package/plugins/specweave/commands/specweave-living-docs.md +42 -0
  192. package/plugins/specweave/hooks/hooks.json +20 -0
  193. package/plugins/specweave/hooks/lib/update-active-increment.sh +2 -2
  194. package/plugins/specweave/hooks/lib/update-status-line.sh +1 -1
  195. package/plugins/specweave/hooks/post-increment-status-change.sh +3 -3
  196. package/plugins/specweave/hooks/post-metadata-change.sh +1 -1
  197. package/plugins/specweave/hooks/universal/hook-wrapper.cmd +26 -26
  198. package/plugins/specweave/hooks/universal/session-start.cmd +16 -16
  199. package/plugins/specweave/hooks/universal/session-start.ps1 +16 -16
  200. package/plugins/specweave/hooks/user-prompt-submit.sh +107 -5
  201. package/plugins/specweave/hooks/v2/guards/increment-root-guard.sh +61 -0
  202. package/plugins/specweave/hooks/v2/guards/per-us-project-validator.sh +281 -0
  203. package/plugins/specweave/hooks/v2/handlers/living-specs-handler.sh +29 -0
  204. package/plugins/specweave/hooks/v2/session-end.sh +69 -0
  205. package/plugins/specweave/hooks/v2/session-start.sh +81 -0
  206. package/plugins/specweave/lib/vendor/sync/github-reconciler.js +1 -1
  207. package/plugins/specweave/lib/vendor/sync/github-reconciler.js.map +1 -1
  208. package/plugins/specweave/scripts/heartbeat.sh +110 -0
  209. package/plugins/specweave/scripts/progress.js +34 -4
  210. package/plugins/specweave/scripts/read-jobs.sh +1 -1
  211. package/plugins/specweave/scripts/read-progress.sh +50 -5
  212. package/plugins/specweave/scripts/read-workflow.sh +1 -1
  213. package/plugins/specweave/scripts/session-watchdog.sh +65 -0
  214. package/plugins/specweave/scripts/status.js +28 -11
  215. package/plugins/specweave-ado/lib/per-us-sync.js +247 -0
  216. package/plugins/specweave-ado/lib/per-us-sync.ts +410 -0
  217. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +738 -0
  218. package/plugins/specweave-github/lib/github-client-v2.js +10 -3
  219. package/plugins/specweave-github/lib/github-client-v2.ts +15 -3
  220. package/plugins/specweave-github/lib/per-us-sync.js +241 -0
  221. package/plugins/specweave-github/lib/per-us-sync.ts +375 -0
  222. package/plugins/specweave-jira/lib/per-us-sync.js +224 -0
  223. package/plugins/specweave-jira/lib/per-us-sync.ts +366 -0
  224. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1107 -0
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Session Registry - Central tracking for all Claude Code sessions and child processes
3
+ *
4
+ * Provides atomic file-based operations for tracking active sessions, detecting
5
+ * stale/zombie processes, and coordinating cleanup across multiple sessions.
6
+ *
7
+ * Key Features:
8
+ * - Atomic operations using temp file + rename pattern
9
+ * - File locking via mkdir (cross-platform atomic operation)
10
+ * - Automatic corruption recovery with JSON validation
11
+ * - Concurrent-safe updates from multiple processes
12
+ *
13
+ * Registry Location: `.specweave/state/.session-registry.json`
14
+ */
15
+ import { SessionInfo, RegisterSessionOptions, StaleSessionInfo, SessionStatus } from '../types/session.js';
16
+ import { Logger } from './logger.js';
17
+ export declare class SessionRegistry {
18
+ private registryPath;
19
+ private lockPath;
20
+ private logger;
21
+ constructor(projectRoot: string, options?: {
22
+ logger?: Logger;
23
+ });
24
+ /**
25
+ * Acquires an exclusive lock on the registry file
26
+ *
27
+ * Uses mkdir as an atomic operation (works cross-platform)
28
+ * Waits up to LOCK_TIMEOUT_MS before failing
29
+ *
30
+ * @returns true if lock acquired, false if timeout
31
+ */
32
+ private acquireLock;
33
+ /**
34
+ * Releases the lock on the registry file
35
+ */
36
+ private releaseLock;
37
+ /**
38
+ * Reads the registry file with corruption recovery
39
+ *
40
+ * If registry doesn't exist, creates a new empty one
41
+ * If registry is corrupted (invalid JSON), creates backup and reinitializes
42
+ *
43
+ * @returns The current registry state
44
+ */
45
+ private readRegistry;
46
+ /**
47
+ * Writes registry to disk atomically
48
+ *
49
+ * Uses temp file + rename pattern for atomic write
50
+ * This ensures registry is never left in a corrupted state
51
+ *
52
+ * @param registry - The registry state to write
53
+ */
54
+ private writeRegistry;
55
+ /**
56
+ * Creates a new empty registry
57
+ */
58
+ private createEmptyRegistry;
59
+ /**
60
+ * Registers a new session in the registry
61
+ *
62
+ * @param sessionId - Unique session identifier
63
+ * @param pid - Process ID of the session
64
+ * @param options - Optional configuration
65
+ * @returns true if registration successful
66
+ */
67
+ registerSession(sessionId: string, pid: number, options?: RegisterSessionOptions): Promise<boolean>;
68
+ /**
69
+ * Updates the heartbeat timestamp for a session
70
+ *
71
+ * @param sessionId - Session to update
72
+ * @returns true if update successful
73
+ */
74
+ updateHeartbeat(sessionId: string): Promise<boolean>;
75
+ /**
76
+ * Adds a child process PID to a session
77
+ *
78
+ * @param sessionId - Parent session ID
79
+ * @param childPid - Child process PID to add
80
+ * @returns true if successful
81
+ */
82
+ addChildProcess(sessionId: string, childPid: number): Promise<boolean>;
83
+ /**
84
+ * Removes a session from the registry
85
+ *
86
+ * @param sessionId - Session to remove
87
+ * @returns true if successful
88
+ */
89
+ removeSession(sessionId: string): Promise<boolean>;
90
+ /**
91
+ * Gets a session by ID
92
+ *
93
+ * @param sessionId - Session to retrieve
94
+ * @returns Session info or undefined
95
+ */
96
+ getSession(sessionId: string): Promise<SessionInfo | undefined>;
97
+ /**
98
+ * Gets all sessions
99
+ *
100
+ * @returns Array of all session info
101
+ */
102
+ getAllSessions(): Promise<SessionInfo[]>;
103
+ /**
104
+ * Updates the status of a session
105
+ *
106
+ * @param sessionId - Session to update
107
+ * @param status - New status
108
+ * @returns true if successful
109
+ */
110
+ updateStatus(sessionId: string, status: SessionStatus): Promise<boolean>;
111
+ /**
112
+ * Gets all stale sessions based on heartbeat threshold
113
+ *
114
+ * A session is considered stale if its last_heartbeat is older than
115
+ * the specified threshold.
116
+ *
117
+ * @param thresholdSeconds - Max age of last heartbeat before considered stale
118
+ * @returns Array of stale session info with staleness details
119
+ */
120
+ getStaleSessions(thresholdSeconds: number): Promise<StaleSessionInfo[]>;
121
+ /**
122
+ * Cleans up old sessions based on retention period
123
+ *
124
+ * Removes sessions that have been completed or stale for longer
125
+ * than the retention period.
126
+ *
127
+ * @param retentionHours - How long to keep old sessions (in hours)
128
+ * @returns Number of sessions removed
129
+ */
130
+ cleanupOldSessions(retentionHours: number): Promise<number>;
131
+ /**
132
+ * Checks if a PID exists (cross-platform)
133
+ *
134
+ * Uses kill -0 on Unix-like systems, which doesn't actually send a signal
135
+ * but checks if the process exists.
136
+ *
137
+ * @param pid - Process ID to check
138
+ * @returns true if process exists
139
+ */
140
+ private checkPidExists;
141
+ }
142
+ //# sourceMappingURL=session-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-registry.d.ts","sourceRoot":"","sources":["../../../src/utils/session-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EACL,WAAW,EAEX,sBAAsB,EACtB,gBAAgB,EAChB,aAAa,EACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAiB,MAAM,aAAa,CAAC;AAOpD,qBAAa,eAAe;IAC1B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAS;gBAEX,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;IAclE;;;;;;;OAOG;YACW,WAAW;IA4BzB;;OAEG;IACH,OAAO,CAAC,WAAW;IAenB;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAsCpB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IAyBrB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAQ3B;;;;;;;OAOG;IACG,eAAe,CACnB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,OAAO,CAAC;IAuCnB;;;;;OAKG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAyB1D;;;;;;OAMG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgC5E;;;;;OAKG;IACG,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAwBxD;;;;;OAKG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAcrE;;;;OAIG;IACG,cAAc,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAc9C;;;;;;OAMG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAwB9E;;;;;;;;OAQG;IACG,gBAAgB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAyC7E;;;;;;;;OAQG;IACG,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgDjE;;;;;;;;OAQG;YACW,cAAc;CAuB7B"}
@@ -0,0 +1,480 @@
1
+ /**
2
+ * Session Registry - Central tracking for all Claude Code sessions and child processes
3
+ *
4
+ * Provides atomic file-based operations for tracking active sessions, detecting
5
+ * stale/zombie processes, and coordinating cleanup across multiple sessions.
6
+ *
7
+ * Key Features:
8
+ * - Atomic operations using temp file + rename pattern
9
+ * - File locking via mkdir (cross-platform atomic operation)
10
+ * - Automatic corruption recovery with JSON validation
11
+ * - Concurrent-safe updates from multiple processes
12
+ *
13
+ * Registry Location: `.specweave/state/.session-registry.json`
14
+ */
15
+ import * as fs from 'fs';
16
+ import * as path from 'path';
17
+ import { consoleLogger } from './logger.js';
18
+ const REGISTRY_FILENAME = '.session-registry.json';
19
+ const LOCK_TIMEOUT_MS = 5000; // 5 seconds max wait for lock
20
+ const LOCK_CHECK_INTERVAL_MS = 50; // Check lock every 50ms
21
+ const REGISTRY_VERSION = '1.0.0';
22
+ export class SessionRegistry {
23
+ constructor(projectRoot, options = {}) {
24
+ this.logger = options.logger ?? consoleLogger;
25
+ const stateDir = path.join(projectRoot, '.specweave', 'state');
26
+ // Ensure state directory exists
27
+ if (!fs.existsSync(stateDir)) {
28
+ fs.mkdirSync(stateDir, { recursive: true });
29
+ }
30
+ this.registryPath = path.join(stateDir, REGISTRY_FILENAME);
31
+ this.lockPath = path.join(stateDir, `${REGISTRY_FILENAME}.lock`);
32
+ }
33
+ /**
34
+ * Acquires an exclusive lock on the registry file
35
+ *
36
+ * Uses mkdir as an atomic operation (works cross-platform)
37
+ * Waits up to LOCK_TIMEOUT_MS before failing
38
+ *
39
+ * @returns true if lock acquired, false if timeout
40
+ */
41
+ async acquireLock() {
42
+ const startTime = Date.now();
43
+ while (Date.now() - startTime < LOCK_TIMEOUT_MS) {
44
+ try {
45
+ // mkdir is atomic on all platforms
46
+ fs.mkdirSync(this.lockPath, { recursive: false });
47
+ // Write our PID to lock directory for debugging
48
+ const lockPidFile = path.join(this.lockPath, 'pid');
49
+ fs.writeFileSync(lockPidFile, String(process.pid));
50
+ return true;
51
+ }
52
+ catch (err) {
53
+ if (err.code !== 'EEXIST') {
54
+ this.logger.error('Failed to acquire lock:', err);
55
+ return false;
56
+ }
57
+ // Lock exists, wait and retry
58
+ await new Promise((resolve) => setTimeout(resolve, LOCK_CHECK_INTERVAL_MS));
59
+ }
60
+ }
61
+ this.logger.error(`Lock acquisition timeout after ${LOCK_TIMEOUT_MS}ms`);
62
+ return false;
63
+ }
64
+ /**
65
+ * Releases the lock on the registry file
66
+ */
67
+ releaseLock() {
68
+ try {
69
+ // Remove PID file first
70
+ const lockPidFile = path.join(this.lockPath, 'pid');
71
+ if (fs.existsSync(lockPidFile)) {
72
+ fs.unlinkSync(lockPidFile);
73
+ }
74
+ // Remove lock directory
75
+ fs.rmdirSync(this.lockPath);
76
+ }
77
+ catch (err) {
78
+ this.logger.error('Failed to release lock:', err);
79
+ }
80
+ }
81
+ /**
82
+ * Reads the registry file with corruption recovery
83
+ *
84
+ * If registry doesn't exist, creates a new empty one
85
+ * If registry is corrupted (invalid JSON), creates backup and reinitializes
86
+ *
87
+ * @returns The current registry state
88
+ */
89
+ readRegistry() {
90
+ // If registry doesn't exist, create empty one
91
+ if (!fs.existsSync(this.registryPath)) {
92
+ return this.createEmptyRegistry();
93
+ }
94
+ try {
95
+ const content = fs.readFileSync(this.registryPath, 'utf-8');
96
+ const registry = JSON.parse(content);
97
+ // Validate basic structure
98
+ if (!registry.sessions || typeof registry.sessions !== 'object') {
99
+ throw new Error('Invalid registry structure: missing sessions object');
100
+ }
101
+ if (!registry.version) {
102
+ // Migrate old registry without version
103
+ registry.version = REGISTRY_VERSION;
104
+ }
105
+ return registry;
106
+ }
107
+ catch (err) {
108
+ this.logger.error('Registry corrupted, creating backup and reinitializing:', err);
109
+ // Backup corrupted registry
110
+ const backupPath = `${this.registryPath}.corrupted.${Date.now()}`;
111
+ try {
112
+ fs.copyFileSync(this.registryPath, backupPath);
113
+ this.logger.info(`Corrupted registry backed up to: ${backupPath}`);
114
+ }
115
+ catch (backupErr) {
116
+ this.logger.error('Failed to backup corrupted registry:', backupErr);
117
+ }
118
+ // Create new empty registry
119
+ return this.createEmptyRegistry();
120
+ }
121
+ }
122
+ /**
123
+ * Writes registry to disk atomically
124
+ *
125
+ * Uses temp file + rename pattern for atomic write
126
+ * This ensures registry is never left in a corrupted state
127
+ *
128
+ * @param registry - The registry state to write
129
+ */
130
+ writeRegistry(registry) {
131
+ const tempPath = `${this.registryPath}.tmp.${process.pid}`;
132
+ try {
133
+ // Write to temp file with pretty formatting
134
+ fs.writeFileSync(tempPath, JSON.stringify(registry, null, 2), 'utf-8');
135
+ // Atomic rename (overwrites existing file)
136
+ fs.renameSync(tempPath, this.registryPath);
137
+ }
138
+ catch (err) {
139
+ this.logger.error('Failed to write registry:', err);
140
+ // Clean up temp file if it exists
141
+ if (fs.existsSync(tempPath)) {
142
+ try {
143
+ fs.unlinkSync(tempPath);
144
+ }
145
+ catch (cleanupErr) {
146
+ // Ignore cleanup errors
147
+ }
148
+ }
149
+ throw err;
150
+ }
151
+ }
152
+ /**
153
+ * Creates a new empty registry
154
+ */
155
+ createEmptyRegistry() {
156
+ return {
157
+ sessions: {},
158
+ last_cleanup: new Date().toISOString(),
159
+ version: REGISTRY_VERSION,
160
+ };
161
+ }
162
+ /**
163
+ * Registers a new session in the registry
164
+ *
165
+ * @param sessionId - Unique session identifier
166
+ * @param pid - Process ID of the session
167
+ * @param options - Optional configuration
168
+ * @returns true if registration successful
169
+ */
170
+ async registerSession(sessionId, pid, options = {}) {
171
+ const lockAcquired = await this.acquireLock();
172
+ if (!lockAcquired) {
173
+ this.logger.error('Failed to acquire lock for session registration');
174
+ return false;
175
+ }
176
+ try {
177
+ const registry = this.readRegistry();
178
+ const now = new Date().toISOString();
179
+ // Check if session already exists
180
+ if (registry.sessions[sessionId]) {
181
+ this.logger.warn(`Session ${sessionId} already registered, updating...`);
182
+ }
183
+ // Create session entry
184
+ const sessionInfo = {
185
+ session_id: sessionId,
186
+ pid,
187
+ type: options.type ?? 'claude-code',
188
+ start_time: now,
189
+ last_heartbeat: now,
190
+ status: 'active',
191
+ child_pids: [],
192
+ parent_session: options.parent_session,
193
+ metadata: options.metadata,
194
+ };
195
+ registry.sessions[sessionId] = sessionInfo;
196
+ this.writeRegistry(registry);
197
+ this.logger.info(`Registered session: ${sessionId} (PID: ${pid})`);
198
+ return true;
199
+ }
200
+ finally {
201
+ this.releaseLock();
202
+ }
203
+ }
204
+ /**
205
+ * Updates the heartbeat timestamp for a session
206
+ *
207
+ * @param sessionId - Session to update
208
+ * @returns true if update successful
209
+ */
210
+ async updateHeartbeat(sessionId) {
211
+ const lockAcquired = await this.acquireLock();
212
+ if (!lockAcquired) {
213
+ return false;
214
+ }
215
+ try {
216
+ const registry = this.readRegistry();
217
+ const session = registry.sessions[sessionId];
218
+ if (!session) {
219
+ this.logger.error(`Session not found: ${sessionId}`);
220
+ return false;
221
+ }
222
+ session.last_heartbeat = new Date().toISOString();
223
+ session.status = 'active'; // Reset to active on heartbeat
224
+ this.writeRegistry(registry);
225
+ return true;
226
+ }
227
+ finally {
228
+ this.releaseLock();
229
+ }
230
+ }
231
+ /**
232
+ * Adds a child process PID to a session
233
+ *
234
+ * @param sessionId - Parent session ID
235
+ * @param childPid - Child process PID to add
236
+ * @returns true if successful
237
+ */
238
+ async addChildProcess(sessionId, childPid) {
239
+ const lockAcquired = await this.acquireLock();
240
+ if (!lockAcquired) {
241
+ return false;
242
+ }
243
+ try {
244
+ const registry = this.readRegistry();
245
+ const session = registry.sessions[sessionId];
246
+ if (!session) {
247
+ this.logger.error(`Session not found: ${sessionId}`);
248
+ return false;
249
+ }
250
+ if (!session.child_pids) {
251
+ session.child_pids = [];
252
+ }
253
+ // Avoid duplicates
254
+ if (!session.child_pids.includes(childPid)) {
255
+ session.child_pids.push(childPid);
256
+ this.writeRegistry(registry);
257
+ this.logger.info(`Added child PID ${childPid} to session ${sessionId}`);
258
+ }
259
+ return true;
260
+ }
261
+ finally {
262
+ this.releaseLock();
263
+ }
264
+ }
265
+ /**
266
+ * Removes a session from the registry
267
+ *
268
+ * @param sessionId - Session to remove
269
+ * @returns true if successful
270
+ */
271
+ async removeSession(sessionId) {
272
+ const lockAcquired = await this.acquireLock();
273
+ if (!lockAcquired) {
274
+ return false;
275
+ }
276
+ try {
277
+ const registry = this.readRegistry();
278
+ if (!registry.sessions[sessionId]) {
279
+ this.logger.warn(`Session not found for removal: ${sessionId}`);
280
+ return false;
281
+ }
282
+ delete registry.sessions[sessionId];
283
+ this.writeRegistry(registry);
284
+ this.logger.info(`Removed session: ${sessionId}`);
285
+ return true;
286
+ }
287
+ finally {
288
+ this.releaseLock();
289
+ }
290
+ }
291
+ /**
292
+ * Gets a session by ID
293
+ *
294
+ * @param sessionId - Session to retrieve
295
+ * @returns Session info or undefined
296
+ */
297
+ async getSession(sessionId) {
298
+ const lockAcquired = await this.acquireLock();
299
+ if (!lockAcquired) {
300
+ return undefined;
301
+ }
302
+ try {
303
+ const registry = this.readRegistry();
304
+ return registry.sessions[sessionId];
305
+ }
306
+ finally {
307
+ this.releaseLock();
308
+ }
309
+ }
310
+ /**
311
+ * Gets all sessions
312
+ *
313
+ * @returns Array of all session info
314
+ */
315
+ async getAllSessions() {
316
+ const lockAcquired = await this.acquireLock();
317
+ if (!lockAcquired) {
318
+ return [];
319
+ }
320
+ try {
321
+ const registry = this.readRegistry();
322
+ return Object.values(registry.sessions);
323
+ }
324
+ finally {
325
+ this.releaseLock();
326
+ }
327
+ }
328
+ /**
329
+ * Updates the status of a session
330
+ *
331
+ * @param sessionId - Session to update
332
+ * @param status - New status
333
+ * @returns true if successful
334
+ */
335
+ async updateStatus(sessionId, status) {
336
+ const lockAcquired = await this.acquireLock();
337
+ if (!lockAcquired) {
338
+ return false;
339
+ }
340
+ try {
341
+ const registry = this.readRegistry();
342
+ const session = registry.sessions[sessionId];
343
+ if (!session) {
344
+ this.logger.error(`Session not found: ${sessionId}`);
345
+ return false;
346
+ }
347
+ session.status = status;
348
+ this.writeRegistry(registry);
349
+ return true;
350
+ }
351
+ finally {
352
+ this.releaseLock();
353
+ }
354
+ }
355
+ /**
356
+ * Gets all stale sessions based on heartbeat threshold
357
+ *
358
+ * A session is considered stale if its last_heartbeat is older than
359
+ * the specified threshold.
360
+ *
361
+ * @param thresholdSeconds - Max age of last heartbeat before considered stale
362
+ * @returns Array of stale session info with staleness details
363
+ */
364
+ async getStaleSessions(thresholdSeconds) {
365
+ const lockAcquired = await this.acquireLock();
366
+ if (!lockAcquired) {
367
+ return [];
368
+ }
369
+ try {
370
+ const registry = this.readRegistry();
371
+ const now = Date.now();
372
+ const staleSessions = [];
373
+ for (const session of Object.values(registry.sessions)) {
374
+ const lastHeartbeat = new Date(session.last_heartbeat).getTime();
375
+ const ageSec = (now - lastHeartbeat) / 1000;
376
+ if (ageSec > thresholdSeconds) {
377
+ // Check if PID still exists (cross-platform)
378
+ const pidExists = await this.checkPidExists(session.pid);
379
+ staleSessions.push({
380
+ session,
381
+ stale_duration_seconds: ageSec,
382
+ pid_exists: pidExists,
383
+ });
384
+ // Mark session as stale
385
+ session.status = 'stale';
386
+ }
387
+ }
388
+ // Update registry with stale statuses
389
+ if (staleSessions.length > 0) {
390
+ this.writeRegistry(registry);
391
+ }
392
+ return staleSessions;
393
+ }
394
+ finally {
395
+ this.releaseLock();
396
+ }
397
+ }
398
+ /**
399
+ * Cleans up old sessions based on retention period
400
+ *
401
+ * Removes sessions that have been completed or stale for longer
402
+ * than the retention period.
403
+ *
404
+ * @param retentionHours - How long to keep old sessions (in hours)
405
+ * @returns Number of sessions removed
406
+ */
407
+ async cleanupOldSessions(retentionHours) {
408
+ const lockAcquired = await this.acquireLock();
409
+ if (!lockAcquired) {
410
+ return 0;
411
+ }
412
+ try {
413
+ const registry = this.readRegistry();
414
+ const now = Date.now();
415
+ const retentionMs = retentionHours * 60 * 60 * 1000;
416
+ const sessionsToRemove = [];
417
+ for (const [sessionId, session] of Object.entries(registry.sessions)) {
418
+ // Only clean up completed or stale sessions
419
+ if (session.status !== 'completed' && session.status !== 'stale') {
420
+ continue;
421
+ }
422
+ // Check age based on last_heartbeat
423
+ const lastActivity = new Date(session.last_heartbeat).getTime();
424
+ const ageMs = now - lastActivity;
425
+ if (ageMs > retentionMs) {
426
+ sessionsToRemove.push(sessionId);
427
+ }
428
+ }
429
+ // Remove old sessions
430
+ for (const sessionId of sessionsToRemove) {
431
+ delete registry.sessions[sessionId];
432
+ this.logger.info(`Cleaned up old session: ${sessionId} (age: ${Math.floor((now - new Date(registry.sessions[sessionId]?.last_heartbeat || now).getTime()) / 3600000)}h)`);
433
+ }
434
+ // Update last_cleanup timestamp
435
+ registry.last_cleanup = new Date().toISOString();
436
+ if (sessionsToRemove.length > 0) {
437
+ this.writeRegistry(registry);
438
+ }
439
+ return sessionsToRemove.length;
440
+ }
441
+ finally {
442
+ this.releaseLock();
443
+ }
444
+ }
445
+ /**
446
+ * Checks if a PID exists (cross-platform)
447
+ *
448
+ * Uses kill -0 on Unix-like systems, which doesn't actually send a signal
449
+ * but checks if the process exists.
450
+ *
451
+ * @param pid - Process ID to check
452
+ * @returns true if process exists
453
+ */
454
+ async checkPidExists(pid) {
455
+ try {
456
+ // On Unix-like systems (macOS, Linux), kill -0 checks process existence
457
+ // without actually sending a signal
458
+ if (process.platform === 'win32') {
459
+ // Windows: use tasklist
460
+ const { execSync } = await import('child_process');
461
+ const output = execSync(`tasklist /FI "PID eq ${pid}" /NH`, {
462
+ encoding: 'utf-8',
463
+ stdio: ['ignore', 'pipe', 'ignore'],
464
+ });
465
+ return output.includes(String(pid));
466
+ }
467
+ else {
468
+ // Unix-like (macOS, Linux)
469
+ const { execSync } = await import('child_process');
470
+ execSync(`kill -0 ${pid}`, { stdio: 'ignore' });
471
+ return true;
472
+ }
473
+ }
474
+ catch {
475
+ // Process doesn't exist or we don't have permission
476
+ return false;
477
+ }
478
+ }
479
+ }
480
+ //# sourceMappingURL=session-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-registry.js","sourceRoot":"","sources":["../../../src/utils/session-registry.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specweave",
3
- "version": "0.32.10",
3
+ "version": "0.33.3",
4
4
  "description": "Spec-driven development framework for Claude Code. AI-native workflow with living documentation, intelligent agents, and multilingual support (9 languages). Enterprise-grade traceability with permanent specs and temporary increments.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -19,9 +19,12 @@
19
19
  "prepublishOnly": "npm run rebuild",
20
20
  "test:unit": "vitest run tests/unit --coverage",
21
21
  "test:integration": "vitest run tests/integration --coverage",
22
+ "test:lifecycle": "npm run test:unit && npm run test:integration",
23
+ "test:e2e:lifecycle": "vitest run tests/e2e/normal-session-lifecycle.e2e.ts tests/e2e/crash-recovery.e2e.ts tests/e2e/multiple-sessions.e2e.ts",
24
+ "test:performance": "ts-node tests/performance/session-registry-bench.ts && bash tests/performance/heartbeat-overhead-bench.sh && ts-node tests/performance/compare-to-baseline.ts",
22
25
  "test:smoke": "bash tests/smoke/smoke-test.sh",
23
26
  "test:e2e": "playwright test tests/e2e/ --grep-invert=\"(should default to claude adapter|should use claude adapter when explicitly requested|should use generic adapter|should create .claude|should initialize project with specweave init|should create correct directory structure|should handle non-interactive mode correctly|should validate config.json structure|should create .specweave directory structure|should create CLAUDE.md and AGENTS.md|should initialize git repository|should install SpecWeave|should scaffold SaaS|should create proper directory|should create required configuration|should install core skills|should install core agents|should have deployment|should have Stripe|ADO Sync|Increment Discipline Blocking|Self-Reflection|Increment Discipline Enforcement)\"",
24
- "test:all": "npm run test:unit && npm run test:integration && npm run test:e2e",
27
+ "test:all": "npm run test:unit && npm run test:integration && npm run test:e2e && npm run test:e2e:lifecycle",
25
28
  "test:coverage": "vitest run --coverage",
26
29
  "test": "npm run test:smoke",
27
30
  "benchmark": "ts-node tests/performance/run-all-benchmarks.ts",