specweave 0.32.10 → 0.33.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/CLAUDE.md +162 -1
  2. package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts +120 -0
  3. package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts.map +1 -0
  4. package/dist/plugins/specweave-ado/lib/per-us-sync.js +276 -0
  5. package/dist/plugins/specweave-ado/lib/per-us-sync.js.map +1 -0
  6. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +4 -1
  7. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
  8. package/dist/plugins/specweave-github/lib/github-client-v2.js +13 -3
  9. package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
  10. package/dist/plugins/specweave-github/lib/per-us-sync.d.ts +97 -0
  11. package/dist/plugins/specweave-github/lib/per-us-sync.d.ts.map +1 -0
  12. package/dist/plugins/specweave-github/lib/per-us-sync.js +274 -0
  13. package/dist/plugins/specweave-github/lib/per-us-sync.js.map +1 -0
  14. package/dist/plugins/specweave-jira/lib/per-us-sync.d.ts +113 -0
  15. package/dist/plugins/specweave-jira/lib/per-us-sync.d.ts.map +1 -0
  16. package/dist/plugins/specweave-jira/lib/per-us-sync.js +254 -0
  17. package/dist/plugins/specweave-jira/lib/per-us-sync.js.map +1 -0
  18. package/dist/src/cli/add-child-pid.d.ts +11 -0
  19. package/dist/src/cli/add-child-pid.d.ts.map +1 -0
  20. package/dist/src/cli/add-child-pid.js +42 -0
  21. package/dist/src/cli/add-child-pid.js.map +1 -0
  22. package/dist/src/cli/add-child-process.d.ts +15 -0
  23. package/dist/src/cli/add-child-process.d.ts.map +1 -0
  24. package/dist/src/cli/add-child-process.js +40 -0
  25. package/dist/src/cli/add-child-process.js.map +1 -0
  26. package/dist/src/cli/check-watchdog.d.ts +15 -0
  27. package/dist/src/cli/check-watchdog.d.ts.map +1 -0
  28. package/dist/src/cli/check-watchdog.js +47 -0
  29. package/dist/src/cli/check-watchdog.js.map +1 -0
  30. package/dist/src/cli/cleanup-zombies.d.ts +14 -0
  31. package/dist/src/cli/cleanup-zombies.d.ts.map +1 -0
  32. package/dist/src/cli/cleanup-zombies.js +268 -0
  33. package/dist/src/cli/cleanup-zombies.js.map +1 -0
  34. package/dist/src/cli/find-session-by-pid.d.ts +14 -0
  35. package/dist/src/cli/find-session-by-pid.d.ts.map +1 -0
  36. package/dist/src/cli/find-session-by-pid.js +45 -0
  37. package/dist/src/cli/find-session-by-pid.js.map +1 -0
  38. package/dist/src/cli/get-stale-sessions.d.ts +17 -0
  39. package/dist/src/cli/get-stale-sessions.d.ts.map +1 -0
  40. package/dist/src/cli/get-stale-sessions.js +36 -0
  41. package/dist/src/cli/get-stale-sessions.js.map +1 -0
  42. package/dist/src/cli/register-session.d.ts +16 -0
  43. package/dist/src/cli/register-session.d.ts.map +1 -0
  44. package/dist/src/cli/register-session.js +48 -0
  45. package/dist/src/cli/register-session.js.map +1 -0
  46. package/dist/src/cli/remove-session.d.ts +11 -0
  47. package/dist/src/cli/remove-session.d.ts.map +1 -0
  48. package/dist/src/cli/remove-session.js +36 -0
  49. package/dist/src/cli/remove-session.js.map +1 -0
  50. package/dist/src/cli/update-heartbeat.d.ts +11 -0
  51. package/dist/src/cli/update-heartbeat.d.ts.map +1 -0
  52. package/dist/src/cli/update-heartbeat.js +36 -0
  53. package/dist/src/cli/update-heartbeat.js.map +1 -0
  54. package/dist/src/config/types.d.ts +1208 -203
  55. package/dist/src/config/types.d.ts.map +1 -1
  56. package/dist/src/core/background/job-manager.d.ts +16 -0
  57. package/dist/src/core/background/job-manager.d.ts.map +1 -1
  58. package/dist/src/core/background/job-manager.js +110 -15
  59. package/dist/src/core/background/job-manager.js.map +1 -1
  60. package/dist/src/core/config/config-manager.d.ts.map +1 -1
  61. package/dist/src/core/config/config-manager.js +58 -0
  62. package/dist/src/core/config/config-manager.js.map +1 -1
  63. package/dist/src/core/config/types.d.ts +80 -0
  64. package/dist/src/core/config/types.d.ts.map +1 -1
  65. package/dist/src/core/config/types.js.map +1 -1
  66. package/dist/src/core/increment/increment-utils.d.ts +26 -1
  67. package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
  68. package/dist/src/core/increment/increment-utils.js +66 -4
  69. package/dist/src/core/increment/increment-utils.js.map +1 -1
  70. package/dist/src/core/increment/status-change-sync-trigger.d.ts +3 -1
  71. package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -1
  72. package/dist/src/core/increment/status-change-sync-trigger.js +5 -2
  73. package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -1
  74. package/dist/src/core/living-docs/cross-project-sync.d.ts +87 -15
  75. package/dist/src/core/living-docs/cross-project-sync.d.ts.map +1 -1
  76. package/dist/src/core/living-docs/cross-project-sync.js +147 -28
  77. package/dist/src/core/living-docs/cross-project-sync.js.map +1 -1
  78. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts.map +1 -1
  79. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js +48 -12
  80. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js.map +1 -1
  81. package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.d.ts +70 -0
  82. package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.d.ts.map +1 -0
  83. package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.js +188 -0
  84. package/dist/src/core/living-docs/intelligent-analyzer/cache-manager.js.map +1 -0
  85. package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.d.ts +33 -0
  86. package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.d.ts.map +1 -0
  87. package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.js +290 -0
  88. package/dist/src/core/living-docs/intelligent-analyzer/dashboard-generator.js.map +1 -0
  89. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts.map +1 -1
  90. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js +114 -11
  91. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js.map +1 -1
  92. package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.d.ts +23 -0
  93. package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.d.ts.map +1 -0
  94. package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.js +283 -0
  95. package/dist/src/core/living-docs/intelligent-analyzer/graph-visualizer.js.map +1 -0
  96. package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.d.ts +44 -0
  97. package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.d.ts.map +1 -0
  98. package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.js +61 -0
  99. package/dist/src/core/living-docs/intelligent-analyzer/mermaid-generator.js.map +1 -0
  100. package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.d.ts +126 -0
  101. package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.d.ts.map +1 -0
  102. package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.js +378 -0
  103. package/dist/src/core/living-docs/intelligent-analyzer/orchestrator.js.map +1 -0
  104. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts.map +1 -1
  105. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js +57 -0
  106. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js.map +1 -1
  107. package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.d.ts +82 -0
  108. package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.d.ts.map +1 -0
  109. package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.js +430 -0
  110. package/dist/src/core/living-docs/intelligent-analyzer/pattern-analyzer.js.map +1 -0
  111. package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.d.ts +84 -0
  112. package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.d.ts.map +1 -0
  113. package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.js +387 -0
  114. package/dist/src/core/living-docs/intelligent-analyzer/repo-scanner.js.map +1 -0
  115. package/dist/src/core/living-docs/intelligent-analyzer/report-writer.d.ts +61 -0
  116. package/dist/src/core/living-docs/intelligent-analyzer/report-writer.d.ts.map +1 -0
  117. package/dist/src/core/living-docs/intelligent-analyzer/report-writer.js +174 -0
  118. package/dist/src/core/living-docs/intelligent-analyzer/report-writer.js.map +1 -0
  119. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +1 -1
  120. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -1
  121. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  122. package/dist/src/core/living-docs/living-docs-sync.js +26 -22
  123. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  124. package/dist/src/core/living-docs/module-analyzer.d.ts +3 -0
  125. package/dist/src/core/living-docs/module-analyzer.d.ts.map +1 -1
  126. package/dist/src/core/living-docs/module-analyzer.js +40 -1
  127. package/dist/src/core/living-docs/module-analyzer.js.map +1 -1
  128. package/dist/src/core/living-docs/types.d.ts +24 -3
  129. package/dist/src/core/living-docs/types.d.ts.map +1 -1
  130. package/dist/src/core/qa/qa-runner.js +1 -1
  131. package/dist/src/core/qa/qa-runner.js.map +1 -1
  132. package/dist/src/core/scheduler/session-sync-executor.js +1 -1
  133. package/dist/src/core/scheduler/session-sync-executor.js.map +1 -1
  134. package/dist/src/core/status-line/status-line-updater.d.ts +1 -1
  135. package/dist/src/core/status-line/status-line-updater.d.ts.map +1 -1
  136. package/dist/src/core/status-line/status-line-updater.js +4 -3
  137. package/dist/src/core/status-line/status-line-updater.js.map +1 -1
  138. package/dist/src/core/types/config.d.ts +79 -0
  139. package/dist/src/core/types/config.d.ts.map +1 -1
  140. package/dist/src/core/types/config.js.map +1 -1
  141. package/dist/src/importers/jira-importer.d.ts.map +1 -1
  142. package/dist/src/importers/jira-importer.js +18 -9
  143. package/dist/src/importers/jira-importer.js.map +1 -1
  144. package/dist/src/init/architecture/types.d.ts +140 -33
  145. package/dist/src/init/architecture/types.d.ts.map +1 -1
  146. package/dist/src/init/compliance/types.d.ts +27 -30
  147. package/dist/src/init/compliance/types.d.ts.map +1 -1
  148. package/dist/src/init/repo/types.d.ts +34 -11
  149. package/dist/src/init/repo/types.d.ts.map +1 -1
  150. package/dist/src/init/research/src/config/types.d.ts +82 -15
  151. package/dist/src/init/research/src/config/types.d.ts.map +1 -1
  152. package/dist/src/init/research/types.d.ts +93 -38
  153. package/dist/src/init/research/types.d.ts.map +1 -1
  154. package/dist/src/init/team/types.d.ts +42 -4
  155. package/dist/src/init/team/types.d.ts.map +1 -1
  156. package/dist/src/sync/ado-reconciler.js +1 -1
  157. package/dist/src/sync/ado-reconciler.js.map +1 -1
  158. package/dist/src/sync/github-reconciler.js +1 -1
  159. package/dist/src/sync/github-reconciler.js.map +1 -1
  160. package/dist/src/sync/jira-reconciler.js +1 -1
  161. package/dist/src/sync/jira-reconciler.js.map +1 -1
  162. package/dist/src/sync/sync-coordinator.d.ts +20 -0
  163. package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
  164. package/dist/src/sync/sync-coordinator.js +258 -33
  165. package/dist/src/sync/sync-coordinator.js.map +1 -1
  166. package/dist/src/types/session.d.ts +65 -0
  167. package/dist/src/types/session.d.ts.map +1 -0
  168. package/dist/src/types/session.js +8 -0
  169. package/dist/src/types/session.js.map +1 -0
  170. package/dist/src/utils/lock-manager.d.ts +48 -0
  171. package/dist/src/utils/lock-manager.d.ts.map +1 -0
  172. package/dist/src/utils/lock-manager.js +195 -0
  173. package/dist/src/utils/lock-manager.js.map +1 -0
  174. package/dist/src/utils/notification-manager.d.ts +45 -0
  175. package/dist/src/utils/notification-manager.d.ts.map +1 -0
  176. package/dist/src/utils/notification-manager.js +130 -0
  177. package/dist/src/utils/notification-manager.js.map +1 -0
  178. package/dist/src/utils/platform-utils.d.ts +136 -0
  179. package/dist/src/utils/platform-utils.d.ts.map +1 -0
  180. package/dist/src/utils/platform-utils.js +366 -0
  181. package/dist/src/utils/platform-utils.js.map +1 -0
  182. package/dist/src/utils/project-resolver.d.ts +156 -0
  183. package/dist/src/utils/project-resolver.d.ts.map +1 -0
  184. package/dist/src/utils/project-resolver.js +587 -0
  185. package/dist/src/utils/project-resolver.js.map +1 -0
  186. package/dist/src/utils/session-registry.d.ts +142 -0
  187. package/dist/src/utils/session-registry.d.ts.map +1 -0
  188. package/dist/src/utils/session-registry.js +480 -0
  189. package/dist/src/utils/session-registry.js.map +1 -0
  190. package/package.json +5 -2
  191. package/plugins/specweave/commands/specweave-living-docs.md +42 -0
  192. package/plugins/specweave/hooks/hooks.json +20 -0
  193. package/plugins/specweave/hooks/lib/update-active-increment.sh +2 -2
  194. package/plugins/specweave/hooks/lib/update-status-line.sh +1 -1
  195. package/plugins/specweave/hooks/post-increment-status-change.sh +3 -3
  196. package/plugins/specweave/hooks/post-metadata-change.sh +1 -1
  197. package/plugins/specweave/hooks/universal/hook-wrapper.cmd +26 -26
  198. package/plugins/specweave/hooks/universal/session-start.cmd +16 -16
  199. package/plugins/specweave/hooks/universal/session-start.ps1 +16 -16
  200. package/plugins/specweave/hooks/user-prompt-submit.sh +107 -5
  201. package/plugins/specweave/hooks/v2/guards/increment-root-guard.sh +61 -0
  202. package/plugins/specweave/hooks/v2/guards/per-us-project-validator.sh +281 -0
  203. package/plugins/specweave/hooks/v2/handlers/living-specs-handler.sh +29 -0
  204. package/plugins/specweave/hooks/v2/session-end.sh +69 -0
  205. package/plugins/specweave/hooks/v2/session-start.sh +81 -0
  206. package/plugins/specweave/lib/vendor/sync/github-reconciler.js +1 -1
  207. package/plugins/specweave/lib/vendor/sync/github-reconciler.js.map +1 -1
  208. package/plugins/specweave/scripts/heartbeat.sh +110 -0
  209. package/plugins/specweave/scripts/progress.js +34 -4
  210. package/plugins/specweave/scripts/read-jobs.sh +1 -1
  211. package/plugins/specweave/scripts/read-progress.sh +50 -5
  212. package/plugins/specweave/scripts/read-workflow.sh +1 -1
  213. package/plugins/specweave/scripts/session-watchdog.sh +65 -0
  214. package/plugins/specweave/scripts/status.js +28 -11
  215. package/plugins/specweave-ado/lib/per-us-sync.js +247 -0
  216. package/plugins/specweave-ado/lib/per-us-sync.ts +410 -0
  217. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +738 -0
  218. package/plugins/specweave-github/lib/github-client-v2.js +10 -3
  219. package/plugins/specweave-github/lib/github-client-v2.ts +15 -3
  220. package/plugins/specweave-github/lib/per-us-sync.js +241 -0
  221. package/plugins/specweave-github/lib/per-us-sync.ts +375 -0
  222. package/plugins/specweave-jira/lib/per-us-sync.js +224 -0
  223. package/plugins/specweave-jira/lib/per-us-sync.ts +366 -0
  224. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1107 -0
@@ -0,0 +1,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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"}