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.
- package/CLAUDE.md +106 -1
- package/dist/src/cli/add-child-pid.d.ts +11 -0
- package/dist/src/cli/add-child-pid.d.ts.map +1 -0
- package/dist/src/cli/add-child-pid.js +42 -0
- package/dist/src/cli/add-child-pid.js.map +1 -0
- package/dist/src/cli/add-child-process.d.ts +15 -0
- package/dist/src/cli/add-child-process.d.ts.map +1 -0
- package/dist/src/cli/add-child-process.js +40 -0
- package/dist/src/cli/add-child-process.js.map +1 -0
- package/dist/src/cli/check-watchdog.d.ts +15 -0
- package/dist/src/cli/check-watchdog.d.ts.map +1 -0
- package/dist/src/cli/check-watchdog.js +47 -0
- package/dist/src/cli/check-watchdog.js.map +1 -0
- package/dist/src/cli/cleanup-zombies.d.ts +14 -0
- package/dist/src/cli/cleanup-zombies.d.ts.map +1 -0
- package/dist/src/cli/cleanup-zombies.js +268 -0
- package/dist/src/cli/cleanup-zombies.js.map +1 -0
- package/dist/src/cli/find-session-by-pid.d.ts +14 -0
- package/dist/src/cli/find-session-by-pid.d.ts.map +1 -0
- package/dist/src/cli/find-session-by-pid.js +45 -0
- package/dist/src/cli/find-session-by-pid.js.map +1 -0
- package/dist/src/cli/get-stale-sessions.d.ts +17 -0
- package/dist/src/cli/get-stale-sessions.d.ts.map +1 -0
- package/dist/src/cli/get-stale-sessions.js +36 -0
- package/dist/src/cli/get-stale-sessions.js.map +1 -0
- package/dist/src/cli/register-session.d.ts +16 -0
- package/dist/src/cli/register-session.d.ts.map +1 -0
- package/dist/src/cli/register-session.js +48 -0
- package/dist/src/cli/register-session.js.map +1 -0
- package/dist/src/cli/remove-session.d.ts +11 -0
- package/dist/src/cli/remove-session.d.ts.map +1 -0
- package/dist/src/cli/remove-session.js +36 -0
- package/dist/src/cli/remove-session.js.map +1 -0
- package/dist/src/cli/update-heartbeat.d.ts +11 -0
- package/dist/src/cli/update-heartbeat.d.ts.map +1 -0
- package/dist/src/cli/update-heartbeat.js +36 -0
- package/dist/src/cli/update-heartbeat.js.map +1 -0
- package/dist/src/config/types.d.ts +1208 -203
- package/dist/src/config/types.d.ts.map +1 -1
- package/dist/src/core/background/job-manager.d.ts +16 -0
- package/dist/src/core/background/job-manager.d.ts.map +1 -1
- package/dist/src/core/background/job-manager.js +110 -15
- package/dist/src/core/background/job-manager.js.map +1 -1
- package/dist/src/core/increment/increment-utils.d.ts +26 -1
- package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
- package/dist/src/core/increment/increment-utils.js +66 -4
- package/dist/src/core/increment/increment-utils.js.map +1 -1
- package/dist/src/core/increment/status-change-sync-trigger.d.ts +3 -1
- package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -1
- package/dist/src/core/increment/status-change-sync-trigger.js +5 -2
- package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js +48 -12
- package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.d.ts +70 -0
- package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.js +188 -0
- package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.d.ts +33 -0
- package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.js +290 -0
- package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js +114 -11
- package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.d.ts +23 -0
- package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.js +283 -0
- package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.d.ts +44 -0
- package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.js +61 -0
- package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.d.ts +126 -0
- package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.js +378 -0
- package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js +57 -0
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.d.ts +82 -0
- package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.js +430 -0
- package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.d.ts +84 -0
- package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.js +387 -0
- package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/report-writer.d.ts +61 -0
- package/dist/src/core/living-docs/intelligent-analyzer/report-writer.d.ts.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/report-writer.js +174 -0
- package/dist/src/core/living-docs/intelligent-analyzer/report-writer.js.map +1 -0
- package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -1
- package/dist/src/core/living-docs/module-analyzer.d.ts +3 -0
- package/dist/src/core/living-docs/module-analyzer.d.ts.map +1 -1
- package/dist/src/core/living-docs/module-analyzer.js +40 -1
- package/dist/src/core/living-docs/module-analyzer.js.map +1 -1
- package/dist/src/core/qa/qa-runner.js +1 -1
- package/dist/src/core/qa/qa-runner.js.map +1 -1
- package/dist/src/core/scheduler/session-sync-executor.js +1 -1
- package/dist/src/core/scheduler/session-sync-executor.js.map +1 -1
- package/dist/src/core/status-line/status-line-updater.d.ts +1 -1
- package/dist/src/core/status-line/status-line-updater.d.ts.map +1 -1
- package/dist/src/core/status-line/status-line-updater.js +4 -3
- package/dist/src/core/status-line/status-line-updater.js.map +1 -1
- package/dist/src/importers/jira-importer.d.ts.map +1 -1
- package/dist/src/importers/jira-importer.js +18 -9
- package/dist/src/importers/jira-importer.js.map +1 -1
- package/dist/src/init/architecture/types.d.ts +140 -33
- package/dist/src/init/architecture/types.d.ts.map +1 -1
- package/dist/src/init/compliance/types.d.ts +27 -30
- package/dist/src/init/compliance/types.d.ts.map +1 -1
- package/dist/src/init/repo/types.d.ts +34 -11
- package/dist/src/init/repo/types.d.ts.map +1 -1
- package/dist/src/init/research/src/config/types.d.ts +82 -15
- package/dist/src/init/research/src/config/types.d.ts.map +1 -1
- package/dist/src/init/research/types.d.ts +93 -38
- package/dist/src/init/research/types.d.ts.map +1 -1
- package/dist/src/init/team/types.d.ts +42 -4
- package/dist/src/init/team/types.d.ts.map +1 -1
- package/dist/src/sync/ado-reconciler.js +1 -1
- package/dist/src/sync/ado-reconciler.js.map +1 -1
- package/dist/src/sync/github-reconciler.js +1 -1
- package/dist/src/sync/github-reconciler.js.map +1 -1
- package/dist/src/sync/jira-reconciler.js +1 -1
- package/dist/src/sync/jira-reconciler.js.map +1 -1
- package/dist/src/types/session.d.ts +65 -0
- package/dist/src/types/session.d.ts.map +1 -0
- package/dist/src/types/session.js +8 -0
- package/dist/src/types/session.js.map +1 -0
- package/dist/src/utils/lock-manager.d.ts +48 -0
- package/dist/src/utils/lock-manager.d.ts.map +1 -0
- package/dist/src/utils/lock-manager.js +195 -0
- package/dist/src/utils/lock-manager.js.map +1 -0
- package/dist/src/utils/notification-manager.d.ts +45 -0
- package/dist/src/utils/notification-manager.d.ts.map +1 -0
- package/dist/src/utils/notification-manager.js +130 -0
- package/dist/src/utils/notification-manager.js.map +1 -0
- package/dist/src/utils/platform-utils.d.ts +136 -0
- package/dist/src/utils/platform-utils.d.ts.map +1 -0
- package/dist/src/utils/platform-utils.js +366 -0
- package/dist/src/utils/platform-utils.js.map +1 -0
- package/dist/src/utils/session-registry.d.ts +142 -0
- package/dist/src/utils/session-registry.d.ts.map +1 -0
- package/dist/src/utils/session-registry.js +480 -0
- package/dist/src/utils/session-registry.js.map +1 -0
- package/package.json +5 -2
- package/plugins/specweave/commands/specweave-living-docs.md +42 -0
- package/plugins/specweave/hooks/hooks.json +10 -0
- package/plugins/specweave/hooks/lib/update-active-increment.sh +2 -2
- package/plugins/specweave/hooks/lib/update-status-line.sh +1 -1
- package/plugins/specweave/hooks/post-increment-status-change.sh +3 -3
- package/plugins/specweave/hooks/post-metadata-change.sh +1 -1
- package/plugins/specweave/hooks/universal/hook-wrapper.cmd +26 -26
- package/plugins/specweave/hooks/universal/session-start.cmd +16 -16
- package/plugins/specweave/hooks/universal/session-start.ps1 +16 -16
- package/plugins/specweave/hooks/user-prompt-submit.sh +2 -2
- package/plugins/specweave/hooks/v2/guards/increment-root-guard.sh +61 -0
- package/plugins/specweave/hooks/v2/session-end.sh +69 -0
- package/plugins/specweave/hooks/v2/session-start.sh +81 -0
- package/plugins/specweave/lib/vendor/sync/github-reconciler.js +1 -1
- package/plugins/specweave/lib/vendor/sync/github-reconciler.js.map +1 -1
- package/plugins/specweave/scripts/heartbeat.sh +110 -0
- package/plugins/specweave/scripts/progress.js +34 -4
- package/plugins/specweave/scripts/read-jobs.sh +1 -1
- package/plugins/specweave/scripts/read-progress.sh +50 -5
- package/plugins/specweave/scripts/read-workflow.sh +1 -1
- package/plugins/specweave/scripts/session-watchdog.sh +65 -0
- package/plugins/specweave/scripts/status.js +28 -11
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +738 -0
- 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"}
|