specweave 0.32.10 → 0.33.2

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 (172) hide show
  1. package/CLAUDE.md +106 -1
  2. package/dist/src/cli/add-child-pid.d.ts +11 -0
  3. package/dist/src/cli/add-child-pid.d.ts.map +1 -0
  4. package/dist/src/cli/add-child-pid.js +42 -0
  5. package/dist/src/cli/add-child-pid.js.map +1 -0
  6. package/dist/src/cli/add-child-process.d.ts +15 -0
  7. package/dist/src/cli/add-child-process.d.ts.map +1 -0
  8. package/dist/src/cli/add-child-process.js +40 -0
  9. package/dist/src/cli/add-child-process.js.map +1 -0
  10. package/dist/src/cli/check-watchdog.d.ts +15 -0
  11. package/dist/src/cli/check-watchdog.d.ts.map +1 -0
  12. package/dist/src/cli/check-watchdog.js +47 -0
  13. package/dist/src/cli/check-watchdog.js.map +1 -0
  14. package/dist/src/cli/cleanup-zombies.d.ts +14 -0
  15. package/dist/src/cli/cleanup-zombies.d.ts.map +1 -0
  16. package/dist/src/cli/cleanup-zombies.js +268 -0
  17. package/dist/src/cli/cleanup-zombies.js.map +1 -0
  18. package/dist/src/cli/find-session-by-pid.d.ts +14 -0
  19. package/dist/src/cli/find-session-by-pid.d.ts.map +1 -0
  20. package/dist/src/cli/find-session-by-pid.js +45 -0
  21. package/dist/src/cli/find-session-by-pid.js.map +1 -0
  22. package/dist/src/cli/get-stale-sessions.d.ts +17 -0
  23. package/dist/src/cli/get-stale-sessions.d.ts.map +1 -0
  24. package/dist/src/cli/get-stale-sessions.js +36 -0
  25. package/dist/src/cli/get-stale-sessions.js.map +1 -0
  26. package/dist/src/cli/register-session.d.ts +16 -0
  27. package/dist/src/cli/register-session.d.ts.map +1 -0
  28. package/dist/src/cli/register-session.js +48 -0
  29. package/dist/src/cli/register-session.js.map +1 -0
  30. package/dist/src/cli/remove-session.d.ts +11 -0
  31. package/dist/src/cli/remove-session.d.ts.map +1 -0
  32. package/dist/src/cli/remove-session.js +36 -0
  33. package/dist/src/cli/remove-session.js.map +1 -0
  34. package/dist/src/cli/update-heartbeat.d.ts +11 -0
  35. package/dist/src/cli/update-heartbeat.d.ts.map +1 -0
  36. package/dist/src/cli/update-heartbeat.js +36 -0
  37. package/dist/src/cli/update-heartbeat.js.map +1 -0
  38. package/dist/src/config/types.d.ts +1208 -203
  39. package/dist/src/config/types.d.ts.map +1 -1
  40. package/dist/src/core/background/job-manager.d.ts +16 -0
  41. package/dist/src/core/background/job-manager.d.ts.map +1 -1
  42. package/dist/src/core/background/job-manager.js +110 -15
  43. package/dist/src/core/background/job-manager.js.map +1 -1
  44. package/dist/src/core/increment/increment-utils.d.ts +26 -1
  45. package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
  46. package/dist/src/core/increment/increment-utils.js +66 -4
  47. package/dist/src/core/increment/increment-utils.js.map +1 -1
  48. package/dist/src/core/increment/status-change-sync-trigger.d.ts +3 -1
  49. package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -1
  50. package/dist/src/core/increment/status-change-sync-trigger.js +5 -2
  51. package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -1
  52. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts.map +1 -1
  53. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js +48 -12
  54. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js.map +1 -1
  55. package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.d.ts +70 -0
  56. package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.d.ts.map +1 -0
  57. package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.js +188 -0
  58. package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.js.map +1 -0
  59. package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.d.ts +33 -0
  60. package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.d.ts.map +1 -0
  61. package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.js +290 -0
  62. package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.js.map +1 -0
  63. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts.map +1 -1
  64. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js +114 -11
  65. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js.map +1 -1
  66. package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.d.ts +23 -0
  67. package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.d.ts.map +1 -0
  68. package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.js +283 -0
  69. package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.js.map +1 -0
  70. package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.d.ts +44 -0
  71. package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.d.ts.map +1 -0
  72. package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.js +61 -0
  73. package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.js.map +1 -0
  74. package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.d.ts +126 -0
  75. package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.d.ts.map +1 -0
  76. package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.js +378 -0
  77. package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.js.map +1 -0
  78. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts.map +1 -1
  79. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js +57 -0
  80. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js.map +1 -1
  81. package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.d.ts +82 -0
  82. package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.d.ts.map +1 -0
  83. package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.js +430 -0
  84. package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.js.map +1 -0
  85. package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.d.ts +84 -0
  86. package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.d.ts.map +1 -0
  87. package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.js +387 -0
  88. package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.js.map +1 -0
  89. package/dist/src/core/living-docs/intelligent-analyzer/report-writer.d.ts +61 -0
  90. package/dist/src/core/living-docs/intelligent-analyzer/report-writer.d.ts.map +1 -0
  91. package/dist/src/core/living-docs/intelligent-analyzer/report-writer.js +174 -0
  92. package/dist/src/core/living-docs/intelligent-analyzer/report-writer.js.map +1 -0
  93. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +1 -1
  94. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -1
  95. package/dist/src/core/living-docs/module-analyzer.d.ts +3 -0
  96. package/dist/src/core/living-docs/module-analyzer.d.ts.map +1 -1
  97. package/dist/src/core/living-docs/module-analyzer.js +40 -1
  98. package/dist/src/core/living-docs/module-analyzer.js.map +1 -1
  99. package/dist/src/core/qa/qa-runner.js +1 -1
  100. package/dist/src/core/qa/qa-runner.js.map +1 -1
  101. package/dist/src/core/scheduler/session-sync-executor.js +1 -1
  102. package/dist/src/core/scheduler/session-sync-executor.js.map +1 -1
  103. package/dist/src/core/status-line/status-line-updater.d.ts +1 -1
  104. package/dist/src/core/status-line/status-line-updater.d.ts.map +1 -1
  105. package/dist/src/core/status-line/status-line-updater.js +4 -3
  106. package/dist/src/core/status-line/status-line-updater.js.map +1 -1
  107. package/dist/src/importers/jira-importer.d.ts.map +1 -1
  108. package/dist/src/importers/jira-importer.js +18 -9
  109. package/dist/src/importers/jira-importer.js.map +1 -1
  110. package/dist/src/init/architecture/types.d.ts +140 -33
  111. package/dist/src/init/architecture/types.d.ts.map +1 -1
  112. package/dist/src/init/compliance/types.d.ts +27 -30
  113. package/dist/src/init/compliance/types.d.ts.map +1 -1
  114. package/dist/src/init/repo/types.d.ts +34 -11
  115. package/dist/src/init/repo/types.d.ts.map +1 -1
  116. package/dist/src/init/research/src/config/types.d.ts +82 -15
  117. package/dist/src/init/research/src/config/types.d.ts.map +1 -1
  118. package/dist/src/init/research/types.d.ts +93 -38
  119. package/dist/src/init/research/types.d.ts.map +1 -1
  120. package/dist/src/init/team/types.d.ts +42 -4
  121. package/dist/src/init/team/types.d.ts.map +1 -1
  122. package/dist/src/sync/ado-reconciler.js +1 -1
  123. package/dist/src/sync/ado-reconciler.js.map +1 -1
  124. package/dist/src/sync/github-reconciler.js +1 -1
  125. package/dist/src/sync/github-reconciler.js.map +1 -1
  126. package/dist/src/sync/jira-reconciler.js +1 -1
  127. package/dist/src/sync/jira-reconciler.js.map +1 -1
  128. package/dist/src/types/session.d.ts +65 -0
  129. package/dist/src/types/session.d.ts.map +1 -0
  130. package/dist/src/types/session.js +8 -0
  131. package/dist/src/types/session.js.map +1 -0
  132. package/dist/src/utils/lock-manager.d.ts +48 -0
  133. package/dist/src/utils/lock-manager.d.ts.map +1 -0
  134. package/dist/src/utils/lock-manager.js +195 -0
  135. package/dist/src/utils/lock-manager.js.map +1 -0
  136. package/dist/src/utils/notification-manager.d.ts +45 -0
  137. package/dist/src/utils/notification-manager.d.ts.map +1 -0
  138. package/dist/src/utils/notification-manager.js +130 -0
  139. package/dist/src/utils/notification-manager.js.map +1 -0
  140. package/dist/src/utils/platform-utils.d.ts +136 -0
  141. package/dist/src/utils/platform-utils.d.ts.map +1 -0
  142. package/dist/src/utils/platform-utils.js +366 -0
  143. package/dist/src/utils/platform-utils.js.map +1 -0
  144. package/dist/src/utils/session-registry.d.ts +142 -0
  145. package/dist/src/utils/session-registry.d.ts.map +1 -0
  146. package/dist/src/utils/session-registry.js +480 -0
  147. package/dist/src/utils/session-registry.js.map +1 -0
  148. package/package.json +5 -2
  149. package/plugins/specweave/commands/specweave-living-docs.md +42 -0
  150. package/plugins/specweave/hooks/hooks.json +10 -0
  151. package/plugins/specweave/hooks/lib/update-active-increment.sh +2 -2
  152. package/plugins/specweave/hooks/lib/update-status-line.sh +1 -1
  153. package/plugins/specweave/hooks/post-increment-status-change.sh +3 -3
  154. package/plugins/specweave/hooks/post-metadata-change.sh +1 -1
  155. package/plugins/specweave/hooks/universal/hook-wrapper.cmd +26 -26
  156. package/plugins/specweave/hooks/universal/session-start.cmd +16 -16
  157. package/plugins/specweave/hooks/universal/session-start.ps1 +16 -16
  158. package/plugins/specweave/hooks/user-prompt-submit.sh +2 -2
  159. package/plugins/specweave/hooks/v2/guards/increment-root-guard.sh +61 -0
  160. package/plugins/specweave/hooks/v2/session-end.sh +69 -0
  161. package/plugins/specweave/hooks/v2/session-start.sh +81 -0
  162. package/plugins/specweave/lib/vendor/sync/github-reconciler.js +1 -1
  163. package/plugins/specweave/lib/vendor/sync/github-reconciler.js.map +1 -1
  164. package/plugins/specweave/scripts/heartbeat.sh +110 -0
  165. package/plugins/specweave/scripts/progress.js +34 -4
  166. package/plugins/specweave/scripts/read-jobs.sh +1 -1
  167. package/plugins/specweave/scripts/read-progress.sh +50 -5
  168. package/plugins/specweave/scripts/read-workflow.sh +1 -1
  169. package/plugins/specweave/scripts/session-watchdog.sh +65 -0
  170. package/plugins/specweave/scripts/status.js +28 -11
  171. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +738 -0
  172. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1107 -0
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Lock Manager - File-based locking with staleness detection
3
+ *
4
+ * Provides atomic file locking using directory creation (mkdir)
5
+ * with automatic stale lock detection and removal.
6
+ */
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ import { consoleLogger } from './logger.js';
10
+ const DEFAULT_STALE_THRESHOLD_SECONDS = 300; // 5 minutes
11
+ const LOCK_RETRY_DELAY_MS = 100;
12
+ const LOCK_TIMEOUT_MS = 10000; // 10 seconds
13
+ export class LockManager {
14
+ constructor(lockDir, staleThresholdSeconds = DEFAULT_STALE_THRESHOLD_SECONDS, options = {}) {
15
+ this.lockDir = lockDir;
16
+ this.staleThresholdSeconds = staleThresholdSeconds;
17
+ this.logger = options.logger ?? consoleLogger;
18
+ }
19
+ /**
20
+ * Acquires the lock with automatic stale lock detection
21
+ *
22
+ * @returns true if lock acquired, false if failed
23
+ */
24
+ async acquire() {
25
+ const startTime = Date.now();
26
+ while (Date.now() - startTime < LOCK_TIMEOUT_MS) {
27
+ // Check if lock exists
28
+ if (fs.existsSync(this.lockDir)) {
29
+ // Check if lock is stale
30
+ if (await this.isStale()) {
31
+ this.logger.warn('Stale lock detected, removing...');
32
+ await this.removeStale();
33
+ continue; // Try to acquire again
34
+ }
35
+ // Lock is active, wait and retry
36
+ await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_DELAY_MS));
37
+ continue;
38
+ }
39
+ // Try to acquire lock
40
+ try {
41
+ fs.mkdirSync(this.lockDir, { recursive: false });
42
+ // Write metadata
43
+ const pidFile = path.join(this.lockDir, 'pid');
44
+ const sessionFile = path.join(this.lockDir, 'session_id');
45
+ fs.writeFileSync(pidFile, String(process.pid));
46
+ fs.writeFileSync(sessionFile, process.env.SESSION_ID || 'unknown');
47
+ return true;
48
+ }
49
+ catch (err) {
50
+ if (err.code === 'EEXIST') {
51
+ // Another process acquired the lock, retry
52
+ continue;
53
+ }
54
+ this.logger.error('Failed to acquire lock:', err);
55
+ return false;
56
+ }
57
+ }
58
+ this.logger.error(`Lock acquisition timeout after ${LOCK_TIMEOUT_MS}ms`);
59
+ return false;
60
+ }
61
+ /**
62
+ * Releases the lock
63
+ */
64
+ async release() {
65
+ try {
66
+ if (!fs.existsSync(this.lockDir)) {
67
+ return;
68
+ }
69
+ // Remove metadata files
70
+ const pidFile = path.join(this.lockDir, 'pid');
71
+ const sessionFile = path.join(this.lockDir, 'session_id');
72
+ if (fs.existsSync(pidFile)) {
73
+ fs.unlinkSync(pidFile);
74
+ }
75
+ if (fs.existsSync(sessionFile)) {
76
+ fs.unlinkSync(sessionFile);
77
+ }
78
+ // Remove lock directory
79
+ fs.rmdirSync(this.lockDir);
80
+ }
81
+ catch (err) {
82
+ this.logger.error('Failed to release lock:', err);
83
+ }
84
+ }
85
+ /**
86
+ * Checks if the lock is stale
87
+ *
88
+ * A lock is stale if:
89
+ * 1. It's older than the threshold
90
+ * 2. The PID in the lock file no longer exists
91
+ *
92
+ * @returns true if lock is stale
93
+ */
94
+ async isStale() {
95
+ if (!fs.existsSync(this.lockDir)) {
96
+ return false;
97
+ }
98
+ try {
99
+ // Check lock age
100
+ const lockAge = await this.getLockAge();
101
+ if (lockAge < this.staleThresholdSeconds) {
102
+ return false; // Lock is fresh
103
+ }
104
+ // Check if PID exists
105
+ const pidFile = path.join(this.lockDir, 'pid');
106
+ if (!fs.existsSync(pidFile)) {
107
+ return true; // No PID file, assume stale
108
+ }
109
+ const pidStr = fs.readFileSync(pidFile, 'utf-8').trim();
110
+ const pid = parseInt(pidStr, 10);
111
+ if (isNaN(pid)) {
112
+ return true; // Invalid PID, assume stale
113
+ }
114
+ // Check if process exists
115
+ const pidExists = await this.checkPidExists(pid);
116
+ return !pidExists; // Stale if PID doesn't exist
117
+ }
118
+ catch (err) {
119
+ this.logger.error('Error checking lock staleness:', err);
120
+ return false; // Assume not stale on error
121
+ }
122
+ }
123
+ /**
124
+ * Removes a stale lock after validation
125
+ */
126
+ async removeStale() {
127
+ if (!(await this.isStale())) {
128
+ this.logger.warn('Attempted to remove non-stale lock, aborting');
129
+ return;
130
+ }
131
+ try {
132
+ const pidFile = path.join(this.lockDir, 'pid');
133
+ let pid = 'unknown';
134
+ if (fs.existsSync(pidFile)) {
135
+ pid = fs.readFileSync(pidFile, 'utf-8').trim();
136
+ }
137
+ this.logger.info(`Removing stale lock (PID: ${pid}, age: ${await this.getLockAge()}s)`);
138
+ // Remove lock
139
+ await this.release();
140
+ // Log removal
141
+ const logFile = path.join(path.dirname(this.lockDir), '..', 'logs', 'lock-cleanup.log');
142
+ const logDir = path.dirname(logFile);
143
+ if (!fs.existsSync(logDir)) {
144
+ fs.mkdirSync(logDir, { recursive: true });
145
+ }
146
+ const logEntry = `[${new Date().toISOString()}] Removed stale lock: ${this.lockDir} (PID: ${pid})\n`;
147
+ fs.appendFileSync(logFile, logEntry);
148
+ }
149
+ catch (err) {
150
+ this.logger.error('Failed to remove stale lock:', err);
151
+ }
152
+ }
153
+ /**
154
+ * Gets the age of the lock in seconds
155
+ */
156
+ async getLockAge() {
157
+ if (!fs.existsSync(this.lockDir)) {
158
+ return Infinity;
159
+ }
160
+ try {
161
+ const stats = fs.statSync(this.lockDir);
162
+ const mtimeMs = stats.mtimeMs;
163
+ const now = Date.now();
164
+ const ageMs = now - mtimeMs;
165
+ return ageMs / 1000;
166
+ }
167
+ catch (err) {
168
+ return Infinity;
169
+ }
170
+ }
171
+ /**
172
+ * Checks if a PID exists (cross-platform)
173
+ */
174
+ async checkPidExists(pid) {
175
+ try {
176
+ if (process.platform === 'win32') {
177
+ const { execSync } = await import('child_process');
178
+ const output = execSync(`tasklist /FI "PID eq ${pid}" /NH`, {
179
+ encoding: 'utf-8',
180
+ stdio: ['ignore', 'pipe', 'ignore'],
181
+ });
182
+ return output.includes(String(pid));
183
+ }
184
+ else {
185
+ const { execSync } = await import('child_process');
186
+ execSync(`kill -0 ${pid}`, { stdio: 'ignore' });
187
+ return true;
188
+ }
189
+ }
190
+ catch {
191
+ return false;
192
+ }
193
+ }
194
+ }
195
+ //# sourceMappingURL=lock-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock-manager.js","sourceRoot":"","sources":["../../../src/utils/lock-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Notification Manager - Cross-platform system notifications
3
+ *
4
+ * Sends native notifications on macOS, Linux, and Windows
5
+ */
6
+ import { Logger } from './logger.js';
7
+ export declare class NotificationManager {
8
+ private logger;
9
+ constructor(options?: {
10
+ logger?: Logger;
11
+ });
12
+ /**
13
+ * Sends a system notification (cross-platform)
14
+ *
15
+ * @param title - Notification title
16
+ * @param body - Notification message
17
+ * @param sound - Optional sound name (macOS only)
18
+ */
19
+ sendNotification(title: string, body: string, sound?: string): Promise<void>;
20
+ /**
21
+ * Sends notification on macOS using osascript
22
+ */
23
+ private sendMacOSNotification;
24
+ /**
25
+ * Sends notification on Linux using notify-send
26
+ */
27
+ private sendLinuxNotification;
28
+ /**
29
+ * Sends notification on Windows using PowerShell toast
30
+ */
31
+ private sendWindowsNotification;
32
+ /**
33
+ * Escapes string for AppleScript
34
+ */
35
+ private escapeForAppleScript;
36
+ /**
37
+ * Escapes string for shell
38
+ */
39
+ private escapeForShell;
40
+ /**
41
+ * Escapes string for PowerShell
42
+ */
43
+ private escapeForPowerShell;
44
+ }
45
+ //# sourceMappingURL=notification-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-manager.d.ts","sourceRoot":"","sources":["../../../src/utils/notification-manager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,MAAM,EAAiB,MAAM,aAAa,CAAC;AAEpD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;IAI7C;;;;;;OAMG;IACG,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBlF;;OAEG;YACW,qBAAqB;IAmBnC;;OAEG;YACW,qBAAqB;IAqBnC;;OAEG;YACW,uBAAuB;IA2BrC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAI5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAG5B"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Notification Manager - Cross-platform system notifications
3
+ *
4
+ * Sends native notifications on macOS, Linux, and Windows
5
+ */
6
+ import { exec } from 'child_process';
7
+ import { consoleLogger } from './logger.js';
8
+ export class NotificationManager {
9
+ constructor(options = {}) {
10
+ this.logger = options.logger ?? consoleLogger;
11
+ }
12
+ /**
13
+ * Sends a system notification (cross-platform)
14
+ *
15
+ * @param title - Notification title
16
+ * @param body - Notification message
17
+ * @param sound - Optional sound name (macOS only)
18
+ */
19
+ async sendNotification(title, body, sound) {
20
+ const platform = process.platform;
21
+ try {
22
+ if (platform === 'darwin') {
23
+ await this.sendMacOSNotification(title, body, sound);
24
+ }
25
+ else if (platform === 'linux') {
26
+ await this.sendLinuxNotification(title, body);
27
+ }
28
+ else if (platform === 'win32') {
29
+ await this.sendWindowsNotification(title, body);
30
+ }
31
+ else {
32
+ this.logger.warn(`Notifications not supported on platform: ${platform}`);
33
+ this.logger.info(`[Notification] ${title}: ${body}`);
34
+ }
35
+ }
36
+ catch (err) {
37
+ this.logger.error('Failed to send notification:', err);
38
+ // Fallback to console
39
+ this.logger.info(`[Notification] ${title}: ${body}`);
40
+ }
41
+ }
42
+ /**
43
+ * Sends notification on macOS using osascript
44
+ */
45
+ async sendMacOSNotification(title, body, sound) {
46
+ return new Promise((resolve, reject) => {
47
+ const soundParam = sound ? ` sound name "${sound}"` : '';
48
+ const script = `display notification "${this.escapeForAppleScript(body)}" with title "${this.escapeForAppleScript(title)}"${soundParam}`;
49
+ exec(`osascript -e '${script}'`, (error) => {
50
+ if (error) {
51
+ reject(error);
52
+ }
53
+ else {
54
+ resolve();
55
+ }
56
+ });
57
+ });
58
+ }
59
+ /**
60
+ * Sends notification on Linux using notify-send
61
+ */
62
+ async sendLinuxNotification(title, body) {
63
+ return new Promise((resolve, reject) => {
64
+ // Check if notify-send is available
65
+ exec('command -v notify-send', (error) => {
66
+ if (error) {
67
+ this.logger.warn('notify-send not found, skipping notification');
68
+ resolve();
69
+ return;
70
+ }
71
+ exec(`notify-send "${this.escapeForShell(title)}" "${this.escapeForShell(body)}"`, (error) => {
72
+ if (error) {
73
+ reject(error);
74
+ }
75
+ else {
76
+ resolve();
77
+ }
78
+ });
79
+ });
80
+ });
81
+ }
82
+ /**
83
+ * Sends notification on Windows using PowerShell toast
84
+ */
85
+ async sendWindowsNotification(title, body) {
86
+ return new Promise((resolve, reject) => {
87
+ const script = `
88
+ [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
89
+ [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null
90
+
91
+ $template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02)
92
+ $toastXml = [xml] $template.GetXml()
93
+ $toastXml.GetElementsByTagName("text")[0].AppendChild($toastXml.CreateTextNode("${this.escapeForPowerShell(title)}")) | Out-Null
94
+ $toastXml.GetElementsByTagName("text")[1].AppendChild($toastXml.CreateTextNode("${this.escapeForPowerShell(body)}")) | Out-Null
95
+
96
+ $xml = New-Object Windows.Data.Xml.Dom.XmlDocument
97
+ $xml.LoadXml($toastXml.OuterXml)
98
+ $toast = [Windows.UI.Notifications.ToastNotification]::new($xml)
99
+ [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("SpecWeave").Show($toast)
100
+ `;
101
+ exec(`powershell -Command "${script}"`, (error) => {
102
+ if (error) {
103
+ reject(error);
104
+ }
105
+ else {
106
+ resolve();
107
+ }
108
+ });
109
+ });
110
+ }
111
+ /**
112
+ * Escapes string for AppleScript
113
+ */
114
+ escapeForAppleScript(str) {
115
+ return str.replace(/"/g, '\\"').replace(/\\/g, '\\\\');
116
+ }
117
+ /**
118
+ * Escapes string for shell
119
+ */
120
+ escapeForShell(str) {
121
+ return str.replace(/"/g, '\\"').replace(/\$/g, '\\$');
122
+ }
123
+ /**
124
+ * Escapes string for PowerShell
125
+ */
126
+ escapeForPowerShell(str) {
127
+ return str.replace(/"/g, '`"').replace(/\$/g, '`$');
128
+ }
129
+ }
130
+ //# sourceMappingURL=notification-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-manager.js","sourceRoot":"","sources":["../../../src/utils/notification-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Platform Utilities - Cross-platform OS operations
3
+ *
4
+ * Abstracts platform-specific operations for macOS, Linux, and Windows
5
+ * Enhanced for process lifecycle management with async API, logger injection,
6
+ * and system notifications
7
+ */
8
+ import { Logger } from './logger.js';
9
+ export type Platform = 'darwin' | 'linux' | 'win32';
10
+ /**
11
+ * Gets the current platform
12
+ */
13
+ export declare function getCurrentPlatform(): Platform;
14
+ /**
15
+ * Checks if a process exists (cross-platform)
16
+ *
17
+ * @param pid - Process ID to check
18
+ * @returns true if process exists
19
+ */
20
+ export declare function checkProcessExists(pid: number): boolean;
21
+ /**
22
+ * Gets file modification time in seconds since epoch (cross-platform)
23
+ *
24
+ * @param filePath - Path to file
25
+ * @returns Modification time in seconds since epoch
26
+ */
27
+ export declare function getFileMtime(filePath: string): number;
28
+ /**
29
+ * Kills a process (cross-platform)
30
+ *
31
+ * @param pid - Process ID to kill
32
+ * @param signal - Signal to send (default: SIGTERM)
33
+ */
34
+ export declare function killProcess(pid: number, signal?: string): void;
35
+ /**
36
+ * Acquires a file lock using atomic directory creation (cross-platform)
37
+ *
38
+ * @param lockPath - Path to lock directory
39
+ * @returns true if lock acquired
40
+ */
41
+ export declare function acquireFileLock(lockPath: string): boolean;
42
+ export interface PlatformUtilsOptions {
43
+ logger?: Logger;
44
+ }
45
+ /**
46
+ * Enhanced cross-platform utility functions with async API and logger injection
47
+ */
48
+ export declare class PlatformUtils {
49
+ private platform;
50
+ private logger;
51
+ constructor(options?: PlatformUtilsOptions);
52
+ /**
53
+ * Get current platform
54
+ */
55
+ getPlatform(): Platform;
56
+ /**
57
+ * Check if current platform is Windows
58
+ */
59
+ isWindows(): boolean;
60
+ /**
61
+ * Check if current platform is POSIX (macOS or Linux)
62
+ */
63
+ isPosix(): boolean;
64
+ /**
65
+ * Check if a process exists by PID (async)
66
+ *
67
+ * - macOS/Linux: kill -0 $pid (signal 0 = existence check)
68
+ * - Windows: tasklist /FI "PID eq $pid"
69
+ *
70
+ * @param pid Process ID to check
71
+ * @returns true if process exists, false otherwise
72
+ */
73
+ checkProcessExistsAsync(pid: number): Promise<boolean>;
74
+ /**
75
+ * Get file modification time (async, uses fs.stat for cross-platform)
76
+ *
77
+ * @param path File path
78
+ * @returns Modification time in seconds since epoch
79
+ */
80
+ getFileMtimeAsync(path: string): Promise<number>;
81
+ /**
82
+ * Acquire file lock using atomic mkdir (async)
83
+ *
84
+ * @param lockPath Path to lock directory
85
+ * @param timeoutMs Maximum time to wait for lock (default: 5000ms)
86
+ * @returns true if lock acquired, false if timeout
87
+ */
88
+ acquireFileLockAsync(lockPath: string, timeoutMs?: number): Promise<boolean>;
89
+ /**
90
+ * Release file lock (async)
91
+ *
92
+ * @param lockPath Path to lock directory
93
+ */
94
+ releaseFileLockAsync(lockPath: string): Promise<void>;
95
+ /**
96
+ * Kill process with signal (async)
97
+ *
98
+ * @param pid Process ID to kill
99
+ * @param signal Signal to send (default: SIGTERM)
100
+ */
101
+ killProcessAsync(pid: number, signal?: NodeJS.Signals): Promise<void>;
102
+ /**
103
+ * Kill process with graceful fallback (SIGTERM → SIGKILL)
104
+ *
105
+ * @param pid Process ID to kill
106
+ * @param gracePeriodMs Time to wait after SIGTERM before SIGKILL (default: 2000ms)
107
+ */
108
+ killProcessGracefully(pid: number, gracePeriodMs?: number): Promise<void>;
109
+ /**
110
+ * Send system notification
111
+ *
112
+ * - macOS: osascript (AppleScript)
113
+ * - Linux: notify-send (if available)
114
+ * - Windows: PowerShell toast notification
115
+ *
116
+ * Fails gracefully if notification system is unavailable
117
+ *
118
+ * @param title Notification title
119
+ * @param body Notification body text
120
+ */
121
+ sendNotification(title: string, body: string): Promise<void>;
122
+ /**
123
+ * macOS notification using osascript
124
+ */
125
+ private sendNotificationMacOS;
126
+ /**
127
+ * Linux notification using notify-send
128
+ */
129
+ private sendNotificationLinux;
130
+ /**
131
+ * Windows notification using PowerShell toast
132
+ */
133
+ private sendNotificationWindows;
134
+ }
135
+ export declare const platformUtils: PlatformUtils;
136
+ //# sourceMappingURL=platform-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-utils.d.ts","sourceRoot":"","sources":["../../../src/utils/platform-utils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,MAAM,EAAiB,MAAM,aAAa,CAAC;AAEpD,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;AAEpD;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,QAAQ,CAE7C;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAevD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAmBrD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAkB,GAAG,IAAI,CAUzE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAWzD;AAID,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,GAAE,oBAAyB;IAK9C;;OAEG;IACH,WAAW,IAAI,QAAQ;IAIvB;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;;;;;;;OAQG;IACG,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA4B5D;;;;;OAKG;IACG,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUtD;;;;;;OAMG;IACG,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,GAAE,MAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAsBxF;;;;OAIG;IACG,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW3D;;;;;OAKG;IACG,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,OAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBtF;;;;;OAKG;IACG,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,GAAE,MAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAgCrF;;;;;;;;;;;OAWG;IACG,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAelE;;OAEG;YACW,qBAAqB;IAOnC;;OAEG;YACW,qBAAqB;IAWnC;;OAEG;YACW,uBAAuB;CAiBtC;AAGD,eAAO,MAAM,aAAa,eAAsB,CAAC"}