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.
- package/CLAUDE.md +162 -1
- package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts +120 -0
- package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/per-us-sync.js +276 -0
- package/dist/plugins/specweave-ado/lib/per-us-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +4 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js +13 -3
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/plugins/specweave-github/lib/per-us-sync.d.ts +97 -0
- package/dist/plugins/specweave-github/lib/per-us-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/per-us-sync.js +274 -0
- package/dist/plugins/specweave-github/lib/per-us-sync.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/per-us-sync.d.ts +113 -0
- package/dist/plugins/specweave-jira/lib/per-us-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/per-us-sync.js +254 -0
- package/dist/plugins/specweave-jira/lib/per-us-sync.js.map +1 -0
- 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/config/config-manager.d.ts.map +1 -1
- package/dist/src/core/config/config-manager.js +58 -0
- package/dist/src/core/config/config-manager.js.map +1 -1
- package/dist/src/core/config/types.d.ts +80 -0
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.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/cross-project-sync.d.ts +87 -15
- package/dist/src/core/living-docs/cross-project-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/cross-project-sync.js +147 -28
- package/dist/src/core/living-docs/cross-project-sync.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/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +26 -22
- package/dist/src/core/living-docs/living-docs-sync.js.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/living-docs/types.d.ts +24 -3
- package/dist/src/core/living-docs/types.d.ts.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/core/types/config.d.ts +79 -0
- package/dist/src/core/types/config.d.ts.map +1 -1
- package/dist/src/core/types/config.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/sync/sync-coordinator.d.ts +20 -0
- package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
- package/dist/src/sync/sync-coordinator.js +258 -33
- package/dist/src/sync/sync-coordinator.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/project-resolver.d.ts +156 -0
- package/dist/src/utils/project-resolver.d.ts.map +1 -0
- package/dist/src/utils/project-resolver.js +587 -0
- package/dist/src/utils/project-resolver.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 +20 -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 +107 -5
- package/plugins/specweave/hooks/v2/guards/increment-root-guard.sh +61 -0
- package/plugins/specweave/hooks/v2/guards/per-us-project-validator.sh +281 -0
- package/plugins/specweave/hooks/v2/handlers/living-specs-handler.sh +29 -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-ado/lib/per-us-sync.js +247 -0
- package/plugins/specweave-ado/lib/per-us-sync.ts +410 -0
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +738 -0
- package/plugins/specweave-github/lib/github-client-v2.js +10 -3
- package/plugins/specweave-github/lib/github-client-v2.ts +15 -3
- package/plugins/specweave-github/lib/per-us-sync.js +241 -0
- package/plugins/specweave-github/lib/per-us-sync.ts +375 -0
- package/plugins/specweave-jira/lib/per-us-sync.js +224 -0
- package/plugins/specweave-jira/lib/per-us-sync.ts +366 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1107 -0
|
@@ -0,0 +1,366 @@
|
|
|
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 { execSync } from 'child_process';
|
|
9
|
+
import { stat, mkdir, rmdir } from 'fs/promises';
|
|
10
|
+
import { consoleLogger } from './logger.js';
|
|
11
|
+
/**
|
|
12
|
+
* Gets the current platform
|
|
13
|
+
*/
|
|
14
|
+
export function getCurrentPlatform() {
|
|
15
|
+
return process.platform;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Checks if a process exists (cross-platform)
|
|
19
|
+
*
|
|
20
|
+
* @param pid - Process ID to check
|
|
21
|
+
* @returns true if process exists
|
|
22
|
+
*/
|
|
23
|
+
export function checkProcessExists(pid) {
|
|
24
|
+
try {
|
|
25
|
+
if (process.platform === 'win32') {
|
|
26
|
+
const output = execSync(`tasklist /FI "PID eq ${pid}" /NH`, {
|
|
27
|
+
encoding: 'utf-8',
|
|
28
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
29
|
+
});
|
|
30
|
+
return output.includes(String(pid));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
execSync(`kill -0 ${pid}`, { stdio: 'ignore' });
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Gets file modification time in seconds since epoch (cross-platform)
|
|
43
|
+
*
|
|
44
|
+
* @param filePath - Path to file
|
|
45
|
+
* @returns Modification time in seconds since epoch
|
|
46
|
+
*/
|
|
47
|
+
export function getFileMtime(filePath) {
|
|
48
|
+
try {
|
|
49
|
+
if (process.platform === 'darwin') {
|
|
50
|
+
const output = execSync(`stat -f %m "${filePath}"`, { encoding: 'utf-8' });
|
|
51
|
+
return parseInt(output.trim(), 10);
|
|
52
|
+
}
|
|
53
|
+
else if (process.platform === 'win32') {
|
|
54
|
+
const output = execSync(`powershell -Command "(Get-Item '${filePath}').LastWriteTime.ToFileTimeUtc()"`, { encoding: 'utf-8' });
|
|
55
|
+
const fileTime = parseInt(output.trim(), 10);
|
|
56
|
+
return Math.floor((fileTime - 116444736000000000) / 10000000);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
const output = execSync(`stat -c %Y "${filePath}"`, { encoding: 'utf-8' });
|
|
60
|
+
return parseInt(output.trim(), 10);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Kills a process (cross-platform)
|
|
69
|
+
*
|
|
70
|
+
* @param pid - Process ID to kill
|
|
71
|
+
* @param signal - Signal to send (default: SIGTERM)
|
|
72
|
+
*/
|
|
73
|
+
export function killProcess(pid, signal = 'SIGTERM') {
|
|
74
|
+
try {
|
|
75
|
+
if (process.platform === 'win32') {
|
|
76
|
+
execSync(`taskkill /F /PID ${pid}`, { stdio: 'ignore' });
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
process.kill(pid, signal);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Process may not exist, ignore
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Acquires a file lock using atomic directory creation (cross-platform)
|
|
88
|
+
*
|
|
89
|
+
* @param lockPath - Path to lock directory
|
|
90
|
+
* @returns true if lock acquired
|
|
91
|
+
*/
|
|
92
|
+
export function acquireFileLock(lockPath) {
|
|
93
|
+
const fs = require('fs');
|
|
94
|
+
try {
|
|
95
|
+
fs.mkdirSync(lockPath, { recursive: false });
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
if (err.code === 'EEXIST') {
|
|
100
|
+
return false; // Lock already exists
|
|
101
|
+
}
|
|
102
|
+
throw err;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Enhanced cross-platform utility functions with async API and logger injection
|
|
107
|
+
*/
|
|
108
|
+
export class PlatformUtils {
|
|
109
|
+
constructor(options = {}) {
|
|
110
|
+
this.platform = process.platform;
|
|
111
|
+
this.logger = options.logger ?? consoleLogger;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get current platform
|
|
115
|
+
*/
|
|
116
|
+
getPlatform() {
|
|
117
|
+
return this.platform;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Check if current platform is Windows
|
|
121
|
+
*/
|
|
122
|
+
isWindows() {
|
|
123
|
+
return this.platform === 'win32';
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Check if current platform is POSIX (macOS or Linux)
|
|
127
|
+
*/
|
|
128
|
+
isPosix() {
|
|
129
|
+
return this.platform === 'darwin' || this.platform === 'linux';
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Check if a process exists by PID (async)
|
|
133
|
+
*
|
|
134
|
+
* - macOS/Linux: kill -0 $pid (signal 0 = existence check)
|
|
135
|
+
* - Windows: tasklist /FI "PID eq $pid"
|
|
136
|
+
*
|
|
137
|
+
* @param pid Process ID to check
|
|
138
|
+
* @returns true if process exists, false otherwise
|
|
139
|
+
*/
|
|
140
|
+
async checkProcessExistsAsync(pid) {
|
|
141
|
+
try {
|
|
142
|
+
if (this.isWindows()) {
|
|
143
|
+
const output = execSync(`tasklist /FI "PID eq ${pid}" /NH`, {
|
|
144
|
+
encoding: 'utf-8',
|
|
145
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
146
|
+
windowsHide: true
|
|
147
|
+
});
|
|
148
|
+
return output.includes(pid.toString());
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
// Signal 0 = check existence without actually sending a signal
|
|
152
|
+
process.kill(pid, 0);
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
if (err.code === 'ESRCH') {
|
|
158
|
+
// No such process
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
if (err.code === 'EPERM') {
|
|
162
|
+
// Process exists but we don't have permission to signal it
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
this.logger.error(`Error checking process ${pid}:`, err);
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Get file modification time (async, uses fs.stat for cross-platform)
|
|
171
|
+
*
|
|
172
|
+
* @param path File path
|
|
173
|
+
* @returns Modification time in seconds since epoch
|
|
174
|
+
*/
|
|
175
|
+
async getFileMtimeAsync(path) {
|
|
176
|
+
try {
|
|
177
|
+
const stats = await stat(path);
|
|
178
|
+
return Math.floor(stats.mtimeMs / 1000);
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
this.logger.error(`Error getting mtime for ${path}:`, err);
|
|
182
|
+
throw err;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Acquire file lock using atomic mkdir (async)
|
|
187
|
+
*
|
|
188
|
+
* @param lockPath Path to lock directory
|
|
189
|
+
* @param timeoutMs Maximum time to wait for lock (default: 5000ms)
|
|
190
|
+
* @returns true if lock acquired, false if timeout
|
|
191
|
+
*/
|
|
192
|
+
async acquireFileLockAsync(lockPath, timeoutMs = 5000) {
|
|
193
|
+
const startTime = Date.now();
|
|
194
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
195
|
+
try {
|
|
196
|
+
await mkdir(lockPath, { recursive: false });
|
|
197
|
+
return true; // Lock acquired
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
if (err.code === 'EEXIST') {
|
|
201
|
+
// Lock exists, wait and retry
|
|
202
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
this.logger.error(`Error acquiring lock at ${lockPath}:`, err);
|
|
206
|
+
throw err;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
this.logger.warn(`Lock acquisition timeout after ${timeoutMs}ms: ${lockPath}`);
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Release file lock (async)
|
|
214
|
+
*
|
|
215
|
+
* @param lockPath Path to lock directory
|
|
216
|
+
*/
|
|
217
|
+
async releaseFileLockAsync(lockPath) {
|
|
218
|
+
try {
|
|
219
|
+
await rmdir(lockPath);
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
if (err.code !== 'ENOENT') {
|
|
223
|
+
this.logger.error(`Error releasing lock at ${lockPath}:`, err);
|
|
224
|
+
throw err;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Kill process with signal (async)
|
|
230
|
+
*
|
|
231
|
+
* @param pid Process ID to kill
|
|
232
|
+
* @param signal Signal to send (default: SIGTERM)
|
|
233
|
+
*/
|
|
234
|
+
async killProcessAsync(pid, signal = 'SIGTERM') {
|
|
235
|
+
try {
|
|
236
|
+
if (this.isWindows()) {
|
|
237
|
+
execSync(`taskkill /PID ${pid} /F`, {
|
|
238
|
+
stdio: 'ignore',
|
|
239
|
+
windowsHide: true
|
|
240
|
+
});
|
|
241
|
+
this.logger.debug(`Killed process ${pid} (Windows taskkill /F)`);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
process.kill(pid, signal);
|
|
245
|
+
this.logger.debug(`Killed process ${pid} with signal ${signal}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
if (err.code === 'ESRCH') {
|
|
250
|
+
this.logger.debug(`Process ${pid} doesn't exist (already terminated)`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
this.logger.error(`Error killing process ${pid}:`, err);
|
|
254
|
+
throw err;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Kill process with graceful fallback (SIGTERM → SIGKILL)
|
|
259
|
+
*
|
|
260
|
+
* @param pid Process ID to kill
|
|
261
|
+
* @param gracePeriodMs Time to wait after SIGTERM before SIGKILL (default: 2000ms)
|
|
262
|
+
*/
|
|
263
|
+
async killProcessGracefully(pid, gracePeriodMs = 2000) {
|
|
264
|
+
const exists = await this.checkProcessExistsAsync(pid);
|
|
265
|
+
if (!exists) {
|
|
266
|
+
this.logger.debug(`Process ${pid} doesn't exist, skipping kill`);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
// Step 1: Send SIGTERM (graceful shutdown)
|
|
271
|
+
await this.killProcessAsync(pid, 'SIGTERM');
|
|
272
|
+
this.logger.debug(`Sent SIGTERM to process ${pid}, waiting ${gracePeriodMs}ms`);
|
|
273
|
+
// Step 2: Wait for grace period
|
|
274
|
+
await new Promise(resolve => setTimeout(resolve, gracePeriodMs));
|
|
275
|
+
// Step 3: Check if process still exists
|
|
276
|
+
const stillExists = await this.checkProcessExistsAsync(pid);
|
|
277
|
+
if (!stillExists) {
|
|
278
|
+
this.logger.debug(`Process ${pid} terminated gracefully after SIGTERM`);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
// Step 4: Force kill with SIGKILL
|
|
282
|
+
this.logger.warn(`Process ${pid} didn't terminate after SIGTERM, sending SIGKILL`);
|
|
283
|
+
await this.killProcessAsync(pid, 'SIGKILL');
|
|
284
|
+
this.logger.debug(`Sent SIGKILL to process ${pid}`);
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
this.logger.error(`Error during graceful kill of process ${pid}:`, err);
|
|
288
|
+
throw err;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Send system notification
|
|
293
|
+
*
|
|
294
|
+
* - macOS: osascript (AppleScript)
|
|
295
|
+
* - Linux: notify-send (if available)
|
|
296
|
+
* - Windows: PowerShell toast notification
|
|
297
|
+
*
|
|
298
|
+
* Fails gracefully if notification system is unavailable
|
|
299
|
+
*
|
|
300
|
+
* @param title Notification title
|
|
301
|
+
* @param body Notification body text
|
|
302
|
+
*/
|
|
303
|
+
async sendNotification(title, body) {
|
|
304
|
+
try {
|
|
305
|
+
if (this.platform === 'darwin') {
|
|
306
|
+
await this.sendNotificationMacOS(title, body);
|
|
307
|
+
}
|
|
308
|
+
else if (this.platform === 'linux') {
|
|
309
|
+
await this.sendNotificationLinux(title, body);
|
|
310
|
+
}
|
|
311
|
+
else if (this.platform === 'win32') {
|
|
312
|
+
await this.sendNotificationWindows(title, body);
|
|
313
|
+
}
|
|
314
|
+
this.logger.debug(`Sent notification: ${title}`);
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
this.logger.warn(`Failed to send notification "${title}": ${err}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* macOS notification using osascript
|
|
322
|
+
*/
|
|
323
|
+
async sendNotificationMacOS(title, body) {
|
|
324
|
+
const escapedTitle = title.replace(/"/g, '\\"');
|
|
325
|
+
const escapedBody = body.replace(/"/g, '\\"');
|
|
326
|
+
const cmd = `osascript -e 'display notification "${escapedBody}" with title "${escapedTitle}"'`;
|
|
327
|
+
execSync(cmd, { stdio: 'ignore' });
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Linux notification using notify-send
|
|
331
|
+
*/
|
|
332
|
+
async sendNotificationLinux(title, body) {
|
|
333
|
+
try {
|
|
334
|
+
execSync('command -v notify-send', { stdio: 'ignore' });
|
|
335
|
+
const escapedTitle = title.replace(/"/g, '\\"');
|
|
336
|
+
const escapedBody = body.replace(/"/g, '\\"');
|
|
337
|
+
execSync(`notify-send "${escapedTitle}" "${escapedBody}"`, { stdio: 'ignore' });
|
|
338
|
+
}
|
|
339
|
+
catch (err) {
|
|
340
|
+
throw new Error('notify-send not available on this system');
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Windows notification using PowerShell toast
|
|
345
|
+
*/
|
|
346
|
+
async sendNotificationWindows(title, body) {
|
|
347
|
+
const escapedTitle = title.replace(/'/g, "''");
|
|
348
|
+
const escapedBody = body.replace(/'/g, "''");
|
|
349
|
+
const psScript = `
|
|
350
|
+
[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null;
|
|
351
|
+
$template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02);
|
|
352
|
+
$xml = [xml]$template.GetXml();
|
|
353
|
+
$xml.GetElementsByTagName('text')[0].AppendChild($xml.CreateTextNode('${escapedTitle}')) | Out-Null;
|
|
354
|
+
$xml.GetElementsByTagName('text')[1].AppendChild($xml.CreateTextNode('${escapedBody}')) | Out-Null;
|
|
355
|
+
$toast = [Windows.UI.Notifications.ToastNotification]::new($xml);
|
|
356
|
+
[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('SpecWeave').Show($toast);
|
|
357
|
+
`;
|
|
358
|
+
execSync(`powershell -Command "${psScript}"`, {
|
|
359
|
+
stdio: 'ignore',
|
|
360
|
+
windowsHide: true
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// Export singleton instance for convenience
|
|
365
|
+
export const platformUtils = new PlatformUtils();
|
|
366
|
+
//# sourceMappingURL=platform-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-utils.js","sourceRoot":"","sources":["../../../src/utils/platform-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Resolver
|
|
3
|
+
*
|
|
4
|
+
* Smart utility that auto-resolves project/board for user stories
|
|
5
|
+
* based on context analysis:
|
|
6
|
+
* - Single project → auto-select
|
|
7
|
+
* - Keyword matching → suggest with confidence
|
|
8
|
+
* - Cross-cutting detection → split across projects
|
|
9
|
+
* - Learned patterns from existing specs
|
|
10
|
+
*
|
|
11
|
+
* Part of increment 0137: Per-US Project/Board Enforcement
|
|
12
|
+
*
|
|
13
|
+
* @module utils/project-resolver
|
|
14
|
+
* @since v0.34.0
|
|
15
|
+
*/
|
|
16
|
+
import { type ProjectInfo, type BoardInfo } from './structure-level-detector.js';
|
|
17
|
+
import { Logger } from './logger.js';
|
|
18
|
+
import { CrossCuttingDetector, type CrossCuttingResult, type DetectedPattern } from './cross-cutting-detector.js';
|
|
19
|
+
/**
|
|
20
|
+
* Resolution result for a single user story
|
|
21
|
+
*/
|
|
22
|
+
export interface ProjectResolution {
|
|
23
|
+
/** Whether project was successfully resolved */
|
|
24
|
+
resolved: boolean;
|
|
25
|
+
/** Resolved project ID (if resolved) */
|
|
26
|
+
projectId?: string;
|
|
27
|
+
/** Resolved board ID (for 2-level structures) */
|
|
28
|
+
boardId?: string;
|
|
29
|
+
/** Confidence level */
|
|
30
|
+
confidence: 'high' | 'medium' | 'low';
|
|
31
|
+
/** Reason for resolution decision */
|
|
32
|
+
reason: string;
|
|
33
|
+
/** Keywords that matched (if any) */
|
|
34
|
+
matchedKeywords?: string[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Resolution result for an entire increment
|
|
38
|
+
*/
|
|
39
|
+
export interface IncrementResolution {
|
|
40
|
+
/** Whether this is a cross-project increment */
|
|
41
|
+
isCrossProject: boolean;
|
|
42
|
+
/** Default project for USs without explicit assignment */
|
|
43
|
+
defaultProject: string;
|
|
44
|
+
/** Default board for 2-level (optional) */
|
|
45
|
+
defaultBoard?: string;
|
|
46
|
+
/** Suggested project for each US type */
|
|
47
|
+
suggestions: Map<string, ProjectResolution>;
|
|
48
|
+
/** Structure level detected */
|
|
49
|
+
structureLevel: 1 | 2;
|
|
50
|
+
/** Cross-cutting detection result (when detected) */
|
|
51
|
+
crossCuttingResult?: CrossCuttingResult;
|
|
52
|
+
/** Detected patterns for display/logging */
|
|
53
|
+
detectedPatterns?: DetectedPattern[];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Project Resolver
|
|
57
|
+
*
|
|
58
|
+
* Resolves project/board context for user stories based on:
|
|
59
|
+
* 1. Single project auto-selection
|
|
60
|
+
* 2. Learned patterns from existing specs
|
|
61
|
+
* 3. Keyword-based matching
|
|
62
|
+
* 4. Cross-cutting detection
|
|
63
|
+
*/
|
|
64
|
+
export declare class ProjectResolver {
|
|
65
|
+
private projectRoot;
|
|
66
|
+
private logger;
|
|
67
|
+
private structureConfig;
|
|
68
|
+
private learnedPatterns;
|
|
69
|
+
private keywordPatterns;
|
|
70
|
+
private crossCuttingDetector;
|
|
71
|
+
constructor(projectRoot: string, options?: {
|
|
72
|
+
logger?: Logger;
|
|
73
|
+
});
|
|
74
|
+
/**
|
|
75
|
+
* Get structure level (1 or 2)
|
|
76
|
+
*/
|
|
77
|
+
getStructureLevel(): 1 | 2;
|
|
78
|
+
/**
|
|
79
|
+
* Get available projects
|
|
80
|
+
*/
|
|
81
|
+
getProjects(): ProjectInfo[];
|
|
82
|
+
/**
|
|
83
|
+
* Get boards for a project (2-level only)
|
|
84
|
+
*/
|
|
85
|
+
getBoards(projectId: string): BoardInfo[];
|
|
86
|
+
/**
|
|
87
|
+
* Learn keyword patterns from existing spec.md files
|
|
88
|
+
*/
|
|
89
|
+
learnFromExistingSpecs(): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Extract keyword → project patterns from a spec file
|
|
92
|
+
*/
|
|
93
|
+
private extractPatternsFromSpec;
|
|
94
|
+
/**
|
|
95
|
+
* Check if word is in default patterns
|
|
96
|
+
*/
|
|
97
|
+
private isDefaultPattern;
|
|
98
|
+
/**
|
|
99
|
+
* Resolve project for a single user story
|
|
100
|
+
*/
|
|
101
|
+
resolveForUserStory(usContent: string): ProjectResolution;
|
|
102
|
+
/**
|
|
103
|
+
* Map suggested project type (frontend, backend, etc.) to actual project ID
|
|
104
|
+
*/
|
|
105
|
+
private mapSuggestedTypeToProject;
|
|
106
|
+
/**
|
|
107
|
+
* Resolve board for a user story (2-level structures only)
|
|
108
|
+
*/
|
|
109
|
+
resolveBoard(usContent: string, projectId: string): ProjectResolution;
|
|
110
|
+
/**
|
|
111
|
+
* Match content against project keywords
|
|
112
|
+
*/
|
|
113
|
+
private matchProjectsByKeywords;
|
|
114
|
+
/**
|
|
115
|
+
* Resolve project context for an entire increment description
|
|
116
|
+
*/
|
|
117
|
+
resolveForIncrement(incrementDescription: string): IncrementResolution;
|
|
118
|
+
/**
|
|
119
|
+
* Detect if description indicates cross-project work
|
|
120
|
+
*/
|
|
121
|
+
private detectCrossProject;
|
|
122
|
+
/**
|
|
123
|
+
* Check if content matches any keywords
|
|
124
|
+
*/
|
|
125
|
+
private matchesKeywords;
|
|
126
|
+
/**
|
|
127
|
+
* Detect cross-cutting concerns using the integrated detector
|
|
128
|
+
* Returns full detection result for advanced use cases
|
|
129
|
+
*/
|
|
130
|
+
detectCrossCuttingConcerns(description: string): CrossCuttingResult;
|
|
131
|
+
/**
|
|
132
|
+
* Get the cross-cutting detector instance for direct access
|
|
133
|
+
*/
|
|
134
|
+
getCrossCuttingDetector(): CrossCuttingDetector;
|
|
135
|
+
/**
|
|
136
|
+
* Format available projects for display
|
|
137
|
+
*/
|
|
138
|
+
formatProjectsForDisplay(): string;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Create a project resolver instance (sync - no pattern learning)
|
|
142
|
+
*/
|
|
143
|
+
export declare function createProjectResolver(projectRoot: string, options?: {
|
|
144
|
+
logger?: Logger;
|
|
145
|
+
}): ProjectResolver;
|
|
146
|
+
/**
|
|
147
|
+
* Create a project resolver instance with pattern learning (async)
|
|
148
|
+
*
|
|
149
|
+
* This awaits learnFromExistingSpecs() to ensure patterns are available
|
|
150
|
+
* before the resolver is used.
|
|
151
|
+
*/
|
|
152
|
+
export declare function createProjectResolverWithPatterns(projectRoot: string, options?: {
|
|
153
|
+
logger?: Logger;
|
|
154
|
+
}): Promise<ProjectResolver>;
|
|
155
|
+
export type { CrossCuttingResult, DetectedPattern };
|
|
156
|
+
//# sourceMappingURL=project-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-resolver.d.ts","sourceRoot":"","sources":["../../../src/utils/project-resolver.ts"],"names":[],"mappings":"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"}
|