@detergent-software/atk 0.12.1-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. package/build/cli.d.ts +3 -0
  2. package/build/cli.d.ts.map +1 -0
  3. package/build/cli.js +35 -0
  4. package/build/cli.js.map +1 -0
  5. package/build/commands/_app.d.ts +3 -0
  6. package/build/commands/_app.d.ts.map +1 -0
  7. package/build/commands/_app.js +12 -0
  8. package/build/commands/_app.js.map +1 -0
  9. package/build/commands/audit.d.ts +12 -0
  10. package/build/commands/audit.d.ts.map +1 -0
  11. package/build/commands/audit.js +78 -0
  12. package/build/commands/audit.js.map +1 -0
  13. package/build/commands/browse.d.ts +15 -0
  14. package/build/commands/browse.d.ts.map +1 -0
  15. package/build/commands/browse.js +284 -0
  16. package/build/commands/browse.js.map +1 -0
  17. package/build/commands/cache.d.ts +13 -0
  18. package/build/commands/cache.d.ts.map +1 -0
  19. package/build/commands/cache.js +67 -0
  20. package/build/commands/cache.js.map +1 -0
  21. package/build/commands/config.d.ts +13 -0
  22. package/build/commands/config.d.ts.map +1 -0
  23. package/build/commands/config.js +62 -0
  24. package/build/commands/config.js.map +1 -0
  25. package/build/commands/diff.d.ts +16 -0
  26. package/build/commands/diff.d.ts.map +1 -0
  27. package/build/commands/diff.js +158 -0
  28. package/build/commands/diff.js.map +1 -0
  29. package/build/commands/doctor.d.ts +11 -0
  30. package/build/commands/doctor.d.ts.map +1 -0
  31. package/build/commands/doctor.js +30 -0
  32. package/build/commands/doctor.js.map +1 -0
  33. package/build/commands/index.d.ts +3 -0
  34. package/build/commands/index.d.ts.map +1 -0
  35. package/build/commands/index.js +30 -0
  36. package/build/commands/index.js.map +1 -0
  37. package/build/commands/info.d.ts +25 -0
  38. package/build/commands/info.d.ts.map +1 -0
  39. package/build/commands/info.js +198 -0
  40. package/build/commands/info.js.map +1 -0
  41. package/build/commands/init.d.ts +17 -0
  42. package/build/commands/init.d.ts.map +1 -0
  43. package/build/commands/init.js +504 -0
  44. package/build/commands/init.js.map +1 -0
  45. package/build/commands/install.d.ts +18 -0
  46. package/build/commands/install.d.ts.map +1 -0
  47. package/build/commands/install.js +186 -0
  48. package/build/commands/install.js.map +1 -0
  49. package/build/commands/list.d.ts +20 -0
  50. package/build/commands/list.d.ts.map +1 -0
  51. package/build/commands/list.js +61 -0
  52. package/build/commands/list.js.map +1 -0
  53. package/build/commands/outdated.d.ts +21 -0
  54. package/build/commands/outdated.d.ts.map +1 -0
  55. package/build/commands/outdated.js +81 -0
  56. package/build/commands/outdated.js.map +1 -0
  57. package/build/commands/pin.d.ts +22 -0
  58. package/build/commands/pin.d.ts.map +1 -0
  59. package/build/commands/pin.js +94 -0
  60. package/build/commands/pin.js.map +1 -0
  61. package/build/commands/prune.d.ts +14 -0
  62. package/build/commands/prune.d.ts.map +1 -0
  63. package/build/commands/prune.js +203 -0
  64. package/build/commands/prune.js.map +1 -0
  65. package/build/commands/publish.d.ts +14 -0
  66. package/build/commands/publish.d.ts.map +1 -0
  67. package/build/commands/publish.js +345 -0
  68. package/build/commands/publish.js.map +1 -0
  69. package/build/commands/search.d.ts +16 -0
  70. package/build/commands/search.d.ts.map +1 -0
  71. package/build/commands/search.js +80 -0
  72. package/build/commands/search.js.map +1 -0
  73. package/build/commands/sync.d.ts +15 -0
  74. package/build/commands/sync.d.ts.map +1 -0
  75. package/build/commands/sync.js +209 -0
  76. package/build/commands/sync.js.map +1 -0
  77. package/build/commands/uninstall.d.ts +18 -0
  78. package/build/commands/uninstall.d.ts.map +1 -0
  79. package/build/commands/uninstall.js +304 -0
  80. package/build/commands/uninstall.js.map +1 -0
  81. package/build/commands/unpin.d.ts +22 -0
  82. package/build/commands/unpin.d.ts.map +1 -0
  83. package/build/commands/unpin.js +79 -0
  84. package/build/commands/unpin.js.map +1 -0
  85. package/build/commands/update.d.ts +17 -0
  86. package/build/commands/update.d.ts.map +1 -0
  87. package/build/commands/update.js +151 -0
  88. package/build/commands/update.js.map +1 -0
  89. package/build/commands/why.d.ts +23 -0
  90. package/build/commands/why.d.ts.map +1 -0
  91. package/build/commands/why.js +108 -0
  92. package/build/commands/why.js.map +1 -0
  93. package/build/components/AssetDetail.d.ts +32 -0
  94. package/build/components/AssetDetail.d.ts.map +1 -0
  95. package/build/components/AssetDetail.js +45 -0
  96. package/build/components/AssetDetail.js.map +1 -0
  97. package/build/components/AssetTable.d.ts +25 -0
  98. package/build/components/AssetTable.d.ts.map +1 -0
  99. package/build/components/AssetTable.js +96 -0
  100. package/build/components/AssetTable.js.map +1 -0
  101. package/build/components/BrowseExpandedBundle.d.ts +26 -0
  102. package/build/components/BrowseExpandedBundle.d.ts.map +1 -0
  103. package/build/components/BrowseExpandedBundle.js +59 -0
  104. package/build/components/BrowseExpandedBundle.js.map +1 -0
  105. package/build/components/BrowseExpandedRow.d.ts +14 -0
  106. package/build/components/BrowseExpandedRow.d.ts.map +1 -0
  107. package/build/components/BrowseExpandedRow.js +9 -0
  108. package/build/components/BrowseExpandedRow.js.map +1 -0
  109. package/build/components/BrowseList.d.ts +26 -0
  110. package/build/components/BrowseList.d.ts.map +1 -0
  111. package/build/components/BrowseList.js +101 -0
  112. package/build/components/BrowseList.js.map +1 -0
  113. package/build/components/BundleDetail.d.ts +7 -0
  114. package/build/components/BundleDetail.d.ts.map +1 -0
  115. package/build/components/BundleDetail.js +12 -0
  116. package/build/components/BundleDetail.js.map +1 -0
  117. package/build/components/DependencyTree.d.ts +19 -0
  118. package/build/components/DependencyTree.d.ts.map +1 -0
  119. package/build/components/DependencyTree.js +21 -0
  120. package/build/components/DependencyTree.js.map +1 -0
  121. package/build/components/DiagnosticList.d.ts +19 -0
  122. package/build/components/DiagnosticList.d.ts.map +1 -0
  123. package/build/components/DiagnosticList.js +17 -0
  124. package/build/components/DiagnosticList.js.map +1 -0
  125. package/build/components/DiffView.d.ts +30 -0
  126. package/build/components/DiffView.d.ts.map +1 -0
  127. package/build/components/DiffView.js +32 -0
  128. package/build/components/DiffView.js.map +1 -0
  129. package/build/components/DryRunBanner.d.ts +11 -0
  130. package/build/components/DryRunBanner.d.ts.map +1 -0
  131. package/build/components/DryRunBanner.js +6 -0
  132. package/build/components/DryRunBanner.js.map +1 -0
  133. package/build/components/Field.d.ts +12 -0
  134. package/build/components/Field.d.ts.map +1 -0
  135. package/build/components/Field.js +9 -0
  136. package/build/components/Field.js.map +1 -0
  137. package/build/components/FilterBar.d.ts +18 -0
  138. package/build/components/FilterBar.d.ts.map +1 -0
  139. package/build/components/FilterBar.js +21 -0
  140. package/build/components/FilterBar.js.map +1 -0
  141. package/build/components/Header.d.ts +13 -0
  142. package/build/components/Header.d.ts.map +1 -0
  143. package/build/components/Header.js +20 -0
  144. package/build/components/Header.js.map +1 -0
  145. package/build/components/HelpBar.d.ts +11 -0
  146. package/build/components/HelpBar.d.ts.map +1 -0
  147. package/build/components/HelpBar.js +9 -0
  148. package/build/components/HelpBar.js.map +1 -0
  149. package/build/components/InstallConfirmBar.d.ts +9 -0
  150. package/build/components/InstallConfirmBar.d.ts.map +1 -0
  151. package/build/components/InstallConfirmBar.js +23 -0
  152. package/build/components/InstallConfirmBar.js.map +1 -0
  153. package/build/components/InstallSummary.d.ts +29 -0
  154. package/build/components/InstallSummary.d.ts.map +1 -0
  155. package/build/components/InstallSummary.js +12 -0
  156. package/build/components/InstallSummary.js.map +1 -0
  157. package/build/components/SectionDivider.d.ts +5 -0
  158. package/build/components/SectionDivider.d.ts.map +1 -0
  159. package/build/components/SectionDivider.js +11 -0
  160. package/build/components/SectionDivider.js.map +1 -0
  161. package/build/components/Spinner.d.ts +13 -0
  162. package/build/components/Spinner.d.ts.map +1 -0
  163. package/build/components/Spinner.js +11 -0
  164. package/build/components/Spinner.js.map +1 -0
  165. package/build/components/StatusBadge.d.ts +23 -0
  166. package/build/components/StatusBadge.d.ts.map +1 -0
  167. package/build/components/StatusBadge.js +43 -0
  168. package/build/components/StatusBadge.js.map +1 -0
  169. package/build/components/index.d.ts +20 -0
  170. package/build/components/index.d.ts.map +1 -0
  171. package/build/components/index.js +20 -0
  172. package/build/components/index.js.map +1 -0
  173. package/build/hooks/useBrowseState.d.ts +122 -0
  174. package/build/hooks/useBrowseState.d.ts.map +1 -0
  175. package/build/hooks/useBrowseState.js +315 -0
  176. package/build/hooks/useBrowseState.js.map +1 -0
  177. package/build/hooks/useCommand.d.ts +23 -0
  178. package/build/hooks/useCommand.d.ts.map +1 -0
  179. package/build/hooks/useCommand.js +43 -0
  180. package/build/hooks/useCommand.js.map +1 -0
  181. package/build/lib/adapter.d.ts +40 -0
  182. package/build/lib/adapter.d.ts.map +1 -0
  183. package/build/lib/adapter.js +112 -0
  184. package/build/lib/adapter.js.map +1 -0
  185. package/build/lib/auth.d.ts +31 -0
  186. package/build/lib/auth.d.ts.map +1 -0
  187. package/build/lib/auth.js +101 -0
  188. package/build/lib/auth.js.map +1 -0
  189. package/build/lib/breakpoints.d.ts +3 -0
  190. package/build/lib/breakpoints.d.ts.map +1 -0
  191. package/build/lib/breakpoints.js +8 -0
  192. package/build/lib/breakpoints.js.map +1 -0
  193. package/build/lib/checksum.d.ts +31 -0
  194. package/build/lib/checksum.d.ts.map +1 -0
  195. package/build/lib/checksum.js +78 -0
  196. package/build/lib/checksum.js.map +1 -0
  197. package/build/lib/config.d.ts +42 -0
  198. package/build/lib/config.d.ts.map +1 -0
  199. package/build/lib/config.js +123 -0
  200. package/build/lib/config.js.map +1 -0
  201. package/build/lib/detector.d.ts +35 -0
  202. package/build/lib/detector.d.ts.map +1 -0
  203. package/build/lib/detector.js +125 -0
  204. package/build/lib/detector.js.map +1 -0
  205. package/build/lib/diagnostics.d.ts +10 -0
  206. package/build/lib/diagnostics.d.ts.map +1 -0
  207. package/build/lib/diagnostics.js +195 -0
  208. package/build/lib/diagnostics.js.map +1 -0
  209. package/build/lib/diff.d.ts +34 -0
  210. package/build/lib/diff.d.ts.map +1 -0
  211. package/build/lib/diff.js +78 -0
  212. package/build/lib/diff.js.map +1 -0
  213. package/build/lib/fetch-retry.d.ts +43 -0
  214. package/build/lib/fetch-retry.d.ts.map +1 -0
  215. package/build/lib/fetch-retry.js +137 -0
  216. package/build/lib/fetch-retry.js.map +1 -0
  217. package/build/lib/github.d.ts +57 -0
  218. package/build/lib/github.d.ts.map +1 -0
  219. package/build/lib/github.js +96 -0
  220. package/build/lib/github.js.map +1 -0
  221. package/build/lib/gitignore.d.ts +37 -0
  222. package/build/lib/gitignore.d.ts.map +1 -0
  223. package/build/lib/gitignore.js +142 -0
  224. package/build/lib/gitignore.js.map +1 -0
  225. package/build/lib/init.d.ts +100 -0
  226. package/build/lib/init.d.ts.map +1 -0
  227. package/build/lib/init.js +295 -0
  228. package/build/lib/init.js.map +1 -0
  229. package/build/lib/installer.d.ts +50 -0
  230. package/build/lib/installer.d.ts.map +1 -0
  231. package/build/lib/installer.js +435 -0
  232. package/build/lib/installer.js.map +1 -0
  233. package/build/lib/lockfile.d.ts +170 -0
  234. package/build/lib/lockfile.d.ts.map +1 -0
  235. package/build/lib/lockfile.js +437 -0
  236. package/build/lib/lockfile.js.map +1 -0
  237. package/build/lib/markdown.d.ts +9 -0
  238. package/build/lib/markdown.d.ts.map +1 -0
  239. package/build/lib/markdown.js +67 -0
  240. package/build/lib/markdown.js.map +1 -0
  241. package/build/lib/parse-json.d.ts +34 -0
  242. package/build/lib/parse-json.d.ts.map +1 -0
  243. package/build/lib/parse-json.js +52 -0
  244. package/build/lib/parse-json.js.map +1 -0
  245. package/build/lib/paths.d.ts +28 -0
  246. package/build/lib/paths.d.ts.map +1 -0
  247. package/build/lib/paths.js +68 -0
  248. package/build/lib/paths.js.map +1 -0
  249. package/build/lib/publisher.d.ts +49 -0
  250. package/build/lib/publisher.d.ts.map +1 -0
  251. package/build/lib/publisher.js +388 -0
  252. package/build/lib/publisher.js.map +1 -0
  253. package/build/lib/registry.d.ts +97 -0
  254. package/build/lib/registry.d.ts.map +1 -0
  255. package/build/lib/registry.js +248 -0
  256. package/build/lib/registry.js.map +1 -0
  257. package/build/lib/resolver.d.ts +50 -0
  258. package/build/lib/resolver.d.ts.map +1 -0
  259. package/build/lib/resolver.js +110 -0
  260. package/build/lib/resolver.js.map +1 -0
  261. package/build/lib/schemas/adapter.d.ts +33 -0
  262. package/build/lib/schemas/adapter.d.ts.map +1 -0
  263. package/build/lib/schemas/adapter.js +27 -0
  264. package/build/lib/schemas/adapter.js.map +1 -0
  265. package/build/lib/schemas/bundle.d.ts +38 -0
  266. package/build/lib/schemas/bundle.d.ts.map +1 -0
  267. package/build/lib/schemas/bundle.js +19 -0
  268. package/build/lib/schemas/bundle.js.map +1 -0
  269. package/build/lib/schemas/config.d.ts +12 -0
  270. package/build/lib/schemas/config.d.ts.map +1 -0
  271. package/build/lib/schemas/config.js +14 -0
  272. package/build/lib/schemas/config.js.map +1 -0
  273. package/build/lib/schemas/index.d.ts +7 -0
  274. package/build/lib/schemas/index.d.ts.map +1 -0
  275. package/build/lib/schemas/index.js +7 -0
  276. package/build/lib/schemas/index.js.map +1 -0
  277. package/build/lib/schemas/lockfile.d.ts +102 -0
  278. package/build/lib/schemas/lockfile.d.ts.map +1 -0
  279. package/build/lib/schemas/lockfile.js +50 -0
  280. package/build/lib/schemas/lockfile.js.map +1 -0
  281. package/build/lib/schemas/manifest.d.ts +91 -0
  282. package/build/lib/schemas/manifest.d.ts.map +1 -0
  283. package/build/lib/schemas/manifest.js +42 -0
  284. package/build/lib/schemas/manifest.js.map +1 -0
  285. package/build/lib/schemas/registry.d.ts +225 -0
  286. package/build/lib/schemas/registry.d.ts.map +1 -0
  287. package/build/lib/schemas/registry.js +59 -0
  288. package/build/lib/schemas/registry.js.map +1 -0
  289. package/build/lib/search.d.ts +23 -0
  290. package/build/lib/search.d.ts.map +1 -0
  291. package/build/lib/search.js +139 -0
  292. package/build/lib/search.js.map +1 -0
  293. package/build/lib/tool-resolver.d.ts +12 -0
  294. package/build/lib/tool-resolver.d.ts.map +1 -0
  295. package/build/lib/tool-resolver.js +24 -0
  296. package/build/lib/tool-resolver.js.map +1 -0
  297. package/build/lib/uninstaller.d.ts +109 -0
  298. package/build/lib/uninstaller.d.ts.map +1 -0
  299. package/build/lib/uninstaller.js +368 -0
  300. package/build/lib/uninstaller.js.map +1 -0
  301. package/build/lib/updater.d.ts +68 -0
  302. package/build/lib/updater.d.ts.map +1 -0
  303. package/build/lib/updater.js +230 -0
  304. package/build/lib/updater.js.map +1 -0
  305. package/build/lib/version.d.ts +13 -0
  306. package/build/lib/version.d.ts.map +1 -0
  307. package/build/lib/version.js +30 -0
  308. package/build/lib/version.js.map +1 -0
  309. package/build/windows-esm-loader.d.ts +15 -0
  310. package/build/windows-esm-loader.d.ts.map +1 -0
  311. package/build/windows-esm-loader.js +11 -0
  312. package/build/windows-esm-loader.js.map +1 -0
  313. package/package.json +67 -0
  314. package/tool-adapters/claude-code.json +34 -0
@@ -0,0 +1,437 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
3
+ import { dirname, join } from 'node:path';
4
+ import { setTimeout as delay } from 'node:timers/promises';
5
+ import { normalizeLockfilePath } from './paths.js';
6
+ import { LockfileSchema } from './schemas/lockfile.js';
7
+ const LOCKFILE_NAME = '.atk-lock.json';
8
+ const LOCK_SENTINEL_NAME = '.atk-lock.json.lock';
9
+ const CURRENT_LOCK_VERSION = 1;
10
+ /** Default timeout (ms) after which a lock is considered stale even if the PID is alive. */
11
+ const DEFAULT_STALE_TIMEOUT_MS = 60_000;
12
+ /** Default interval (ms) between retry attempts when the lock is held. */
13
+ const DEFAULT_RETRY_INTERVAL_MS = 200;
14
+ /** Default maximum time (ms) to wait to acquire the lock before giving up. */
15
+ const DEFAULT_ACQUIRE_TIMEOUT_MS = 30_000;
16
+ export class LockfileError extends Error {
17
+ constructor(message) {
18
+ super(message);
19
+ this.name = 'LockfileError';
20
+ }
21
+ }
22
+ export class LockfileLockedError extends Error {
23
+ constructor(pid, sentinelPath) {
24
+ super(`Lockfile is held by PID ${pid} (sentinel: ${sentinelPath})`);
25
+ this.name = 'LockfileLockedError';
26
+ }
27
+ }
28
+ /**
29
+ * Acquire an advisory lock on the lockfile.
30
+ *
31
+ * Creates a sentinel file (`.atk-lock.json.lock`) atomically using the `'wx'`
32
+ * flag. If the sentinel already exists the function will:
33
+ * 1. Read the sentinel to get the holding PID and timestamp.
34
+ * 2. If the holding PID is dead **or** the lock is older than `staleTimeout`,
35
+ * remove the stale sentinel and retry immediately.
36
+ * 3. Otherwise wait `retryInterval` ms and retry, up to `acquireTimeout` ms total.
37
+ *
38
+ * @throws {LockfileLockedError} if the lock cannot be acquired within the timeout.
39
+ */
40
+ export async function acquireLockfileLock(projectDir, options) {
41
+ const sentinelPath = getLockSentinelPath(projectDir);
42
+ const acquireTimeout = options?.acquireTimeout ?? DEFAULT_ACQUIRE_TIMEOUT_MS;
43
+ const retryInterval = options?.retryInterval ?? DEFAULT_RETRY_INTERVAL_MS;
44
+ const staleTimeout = options?.staleTimeout ?? DEFAULT_STALE_TIMEOUT_MS;
45
+ const deadline = Date.now() + acquireTimeout;
46
+ while (true) {
47
+ try {
48
+ const data = { pid: process.pid, timestamp: Date.now() };
49
+ await writeFile(sentinelPath, JSON.stringify(data), { flag: 'wx' });
50
+ // Sentinel created successfully — lock acquired.
51
+ return;
52
+ }
53
+ catch (err) {
54
+ if (!isEnoentOrExist(err, 'EEXIST')) {
55
+ throw err;
56
+ }
57
+ // Sentinel already exists — inspect it.
58
+ let existing;
59
+ try {
60
+ const raw = await readFile(sentinelPath, 'utf-8');
61
+ existing = JSON.parse(raw);
62
+ }
63
+ catch {
64
+ // If we cannot read/parse the sentinel, treat it as stale and
65
+ // remove it so we can retry on the next iteration.
66
+ try {
67
+ await unlink(sentinelPath);
68
+ }
69
+ catch {
70
+ // Another process may have already removed it — ignore.
71
+ }
72
+ continue;
73
+ }
74
+ const isStale = !isProcessAlive(existing.pid) || Date.now() - existing.timestamp > staleTimeout;
75
+ if (isStale) {
76
+ try {
77
+ await unlink(sentinelPath);
78
+ }
79
+ catch {
80
+ // Another process may have already removed it — ignore.
81
+ }
82
+ continue;
83
+ }
84
+ // Lock is held by a live process and is not stale — wait and retry.
85
+ if (Date.now() >= deadline) {
86
+ throw new LockfileLockedError(existing.pid, sentinelPath);
87
+ }
88
+ await delay(retryInterval);
89
+ if (Date.now() >= deadline) {
90
+ throw new LockfileLockedError(existing.pid, sentinelPath);
91
+ }
92
+ }
93
+ }
94
+ }
95
+ /**
96
+ * Add an installed asset to the lockfile.
97
+ * If the asset already exists, it is replaced (updated).
98
+ */
99
+ export function addAssetToLockfile(lockfile, asset) {
100
+ const filtered = (lockfile.assets ?? []).filter((a) => !(a.name === asset.name && a.type === asset.type));
101
+ return {
102
+ ...lockfile,
103
+ assets: [...filtered, asset],
104
+ };
105
+ }
106
+ /**
107
+ * Find all orphaned dependency assets in the lockfile.
108
+ * An asset is orphaned if it was installed as a dependency but no longer has
109
+ * a valid reverse dependency chain leading to a direct-install root.
110
+ */
111
+ export function findAllOrphanedAssets(lockfile) {
112
+ return (lockfile.assets ?? []).filter((asset) => {
113
+ if ((asset.installReason ?? 'direct') !== 'dependency')
114
+ return false;
115
+ const result = traceDependencyChain(lockfile, asset.name, asset.type);
116
+ return result.status === 'orphaned';
117
+ });
118
+ }
119
+ /**
120
+ * Find installed assets that depend on the given asset.
121
+ * Returns those whose `dependencies` array includes the given asset name (and optionally type).
122
+ */
123
+ export function findDependents(lockfile, assetName, assetType) {
124
+ return (lockfile.assets ?? []).filter((a) => (a.dependencies ?? []).some((d) => d.name === assetName && (!assetType || d.type === assetType)));
125
+ }
126
+ /**
127
+ * Find an installed asset in the lockfile.
128
+ */
129
+ export function findInstalledAsset(lockfile, name, type) {
130
+ return (lockfile.assets ?? []).find((a) => a.name === name && (!type || a.type === type));
131
+ }
132
+ /**
133
+ * Find dependencies of assetBeingRemoved that would become orphaned after removal.
134
+ * A dependency is orphaned if:
135
+ * (a) it has installReason === 'dependency', and
136
+ * (b) no other remaining installed asset (excluding those in otherNamesBeingRemoved) lists it in dependencies.
137
+ */
138
+ export function findOrphanedDependencies(lockfile, assetBeingRemoved, otherNamesBeingRemoved = []) {
139
+ const orphans = [];
140
+ const beingRemoved = new Set([assetBeingRemoved.name, ...otherNamesBeingRemoved]);
141
+ for (const dep of assetBeingRemoved.dependencies ?? []) {
142
+ const depAsset = findInstalledAsset(lockfile, dep.name, dep.type);
143
+ if (!depAsset)
144
+ continue;
145
+ // Only auto-remove dependencies that were installed as dependencies
146
+ if ((depAsset.installReason ?? 'direct') !== 'dependency')
147
+ continue;
148
+ // Check if any other remaining asset still depends on this
149
+ const otherDependents = (lockfile.assets ?? []).filter((a) => !beingRemoved.has(a.name) && (a.dependencies ?? []).some((d) => d.name === dep.name && d.type === dep.type));
150
+ if (otherDependents.length === 0) {
151
+ orphans.push(depAsset);
152
+ }
153
+ }
154
+ return orphans;
155
+ }
156
+ /**
157
+ * Get all installed assets, optionally filtered by type.
158
+ */
159
+ export function getInstalledAssets(lockfile, type) {
160
+ const assets = lockfile.assets ?? [];
161
+ if (!type) {
162
+ return assets;
163
+ }
164
+ return assets.filter((a) => a.type === type);
165
+ }
166
+ /**
167
+ * Get the count of installed assets.
168
+ */
169
+ export function getInstalledCount(lockfile, type) {
170
+ return getInstalledAssets(lockfile, type).length;
171
+ }
172
+ /**
173
+ * Get the path to the lockfile in the given project directory.
174
+ */
175
+ export function getLockfilePath(projectDir) {
176
+ return join(projectDir, LOCKFILE_NAME);
177
+ }
178
+ /**
179
+ * Get the path to the lock sentinel file for the given project directory.
180
+ */
181
+ export function getLockSentinelPath(projectDir) {
182
+ return join(projectDir, LOCK_SENTINEL_NAME);
183
+ }
184
+ /**
185
+ * Check if an asset is installed.
186
+ */
187
+ export function isInstalled(lockfile, name, type) {
188
+ return findInstalledAsset(lockfile, name, type) !== undefined;
189
+ }
190
+ /**
191
+ * Check whether an asset has a pinned version in the lockfile.
192
+ * Returns `true` if the asset exists and has a truthy `pinnedVersion`, `false` otherwise.
193
+ * This is a read-only query that never throws.
194
+ */
195
+ export function isPinned(lockfile, name, type) {
196
+ const asset = findInstalledAsset(lockfile, name, type);
197
+ return asset !== undefined && !!asset.pinnedVersion;
198
+ }
199
+ /**
200
+ * Pin an installed asset to a specific version.
201
+ * Only direct installs can be pinned; pinning a dependency throws an error.
202
+ *
203
+ * @throws {LockfileError} if the asset is not found or is not a direct install
204
+ */
205
+ export function pinAssetVersion(lockfile, name, version, type) {
206
+ const asset = findInstalledAsset(lockfile, name, type);
207
+ if (!asset) {
208
+ throw new LockfileError(`Asset '${name}' is not installed`);
209
+ }
210
+ if ((asset.installReason ?? 'direct') === 'dependency') {
211
+ throw new LockfileError(`Asset '${name}' is a dependency and cannot be pinned — only directly installed assets can be pinned`);
212
+ }
213
+ return {
214
+ ...lockfile,
215
+ assets: (lockfile.assets ?? []).map((a) => {
216
+ if (a.name === name && (!type || a.type === type)) {
217
+ return { ...a, pinnedVersion: version };
218
+ }
219
+ return a;
220
+ }),
221
+ };
222
+ }
223
+ /**
224
+ * Read the lockfile from the project directory.
225
+ * Returns a default empty lockfile if none exists.
226
+ */
227
+ export async function readLockfile(projectDir, tool) {
228
+ const lockfilePath = getLockfilePath(projectDir);
229
+ if (!existsSync(lockfilePath)) {
230
+ return { assets: [], lockVersion: CURRENT_LOCK_VERSION, tool };
231
+ }
232
+ const raw = await readFile(lockfilePath, 'utf-8');
233
+ let parsed;
234
+ try {
235
+ parsed = JSON.parse(raw);
236
+ }
237
+ catch {
238
+ throw new LockfileError(`Invalid lockfile at ${lockfilePath}: Invalid JSON`);
239
+ }
240
+ const result = LockfileSchema.safeParse(parsed);
241
+ if (result.success) {
242
+ const lockfile = result.data;
243
+ // Defensive normalization: convert any backslash paths (from pre-P0 lockfiles) to forward slashes.
244
+ // This is a pure in-memory transformation — the lockfile on disk is not modified.
245
+ for (const asset of lockfile.assets ?? []) {
246
+ for (const file of asset.files) {
247
+ file.installedPath = normalizeLockfilePath(file.installedPath);
248
+ }
249
+ for (const entry of asset.configEntries ?? []) {
250
+ entry.file = normalizeLockfilePath(entry.file);
251
+ }
252
+ }
253
+ return lockfile;
254
+ }
255
+ throw new LockfileError(`Invalid lockfile at ${lockfilePath}: ${result.error.issues.map((i) => i.message).join(', ')}`);
256
+ }
257
+ /**
258
+ * Release the advisory lock by removing the sentinel file.
259
+ * Silently ignores `ENOENT` (the sentinel was already removed).
260
+ */
261
+ export async function releaseLockfileLock(projectDir) {
262
+ const sentinelPath = getLockSentinelPath(projectDir);
263
+ try {
264
+ await unlink(sentinelPath);
265
+ }
266
+ catch (err) {
267
+ if (!isEnoentOrExist(err, 'ENOENT')) {
268
+ throw err;
269
+ }
270
+ }
271
+ }
272
+ /**
273
+ * Remove an asset from the lockfile by name and optional type.
274
+ */
275
+ export function removeAssetFromLockfile(lockfile, name, type) {
276
+ return {
277
+ ...lockfile,
278
+ assets: (lockfile.assets ?? []).filter((a) => !(a.name === name && (!type || a.type === type))),
279
+ };
280
+ }
281
+ /**
282
+ * Trace the reverse dependency chain from a given asset upward to all
283
+ * direct-install roots.
284
+ *
285
+ * Returns a discriminated union indicating whether the asset is:
286
+ * - `direct`: explicitly installed by the user
287
+ * - `dependency`: pulled in as a dependency, with chain(s) to direct root(s)
288
+ * - `not_installed`: not found in the lockfile
289
+ * - `orphaned`: installed as a dependency but no path to a direct root exists
290
+ */
291
+ export function traceDependencyChain(lockfile, assetName, assetType) {
292
+ const asset = findInstalledAsset(lockfile, assetName, assetType);
293
+ if (!asset) {
294
+ return { name: assetName, status: 'not_installed' };
295
+ }
296
+ if ((asset.installReason ?? 'direct') === 'direct') {
297
+ return { asset, status: 'direct' };
298
+ }
299
+ // Walk the reverse dependency graph upward to find direct-install roots
300
+ const visiting = new Set();
301
+ const chains = buildReverseChains(lockfile, asset, visiting);
302
+ if (chains.length === 0) {
303
+ return { asset, status: 'orphaned' };
304
+ }
305
+ return { asset, chains, status: 'dependency' };
306
+ }
307
+ /**
308
+ * Remove the version pin from an installed asset.
309
+ *
310
+ * @throws {LockfileError} if the asset is not found
311
+ */
312
+ export function unpinAssetVersion(lockfile, name, type) {
313
+ const asset = findInstalledAsset(lockfile, name, type);
314
+ if (!asset) {
315
+ throw new LockfileError(`Asset '${name}' is not installed`);
316
+ }
317
+ return {
318
+ ...lockfile,
319
+ assets: (lockfile.assets ?? []).map((a) => {
320
+ if (a.name === name && (!type || a.type === type)) {
321
+ const { pinnedVersion: _, ...rest } = a;
322
+ return rest;
323
+ }
324
+ return a;
325
+ }),
326
+ };
327
+ }
328
+ /**
329
+ * Upgrade an asset's installReason from 'dependency' to 'direct'.
330
+ * Used when a user explicitly installs something that was previously auto-installed.
331
+ */
332
+ export function upgradeToDirectInstall(lockfile, name, type) {
333
+ return {
334
+ ...lockfile,
335
+ assets: (lockfile.assets ?? []).map((a) => {
336
+ if (a.name === name && (!type || a.type === type)) {
337
+ return { ...a, installReason: 'direct' };
338
+ }
339
+ return a;
340
+ }),
341
+ };
342
+ }
343
+ /**
344
+ * Execute `fn` while holding the advisory lockfile lock.
345
+ * The lock is always released in a `finally` block, even if `fn` throws.
346
+ */
347
+ export async function withLockfileLock(projectDir, fn) {
348
+ await acquireLockfileLock(projectDir);
349
+ try {
350
+ return await fn();
351
+ }
352
+ finally {
353
+ await releaseLockfileLock(projectDir);
354
+ }
355
+ }
356
+ /**
357
+ * Write the lockfile to the project directory.
358
+ */
359
+ export async function writeLockfile(projectDir, lockfile) {
360
+ const lockfilePath = getLockfilePath(projectDir);
361
+ const dir = dirname(lockfilePath);
362
+ if (!existsSync(dir)) {
363
+ await mkdir(dir, { recursive: true });
364
+ }
365
+ await writeFile(lockfilePath, JSON.stringify(lockfile, null, '\t') + '\n', 'utf-8');
366
+ }
367
+ /**
368
+ * Recursively build reverse dependency chain nodes by finding all dependents
369
+ * of the given asset and walking upward until reaching direct-install roots.
370
+ *
371
+ * Uses a `visiting` set with `type:name` composite keys for cycle protection,
372
+ * mirroring the pattern in resolver.ts.
373
+ */
374
+ function buildReverseChains(lockfile, asset, visiting) {
375
+ const key = `${asset.type}:${asset.name}`;
376
+ // Cycle protection: if we're already visiting this asset, stop recursion
377
+ if (visiting.has(key)) {
378
+ return [];
379
+ }
380
+ visiting.add(key);
381
+ const dependents = findDependents(lockfile, asset.name, asset.type);
382
+ const nodes = [];
383
+ for (const dependent of dependents) {
384
+ const reason = (dependent.installReason ?? 'direct');
385
+ if (reason === 'direct') {
386
+ // Reached a direct-install root — this is a leaf in our chain
387
+ nodes.push({
388
+ children: [],
389
+ installReason: 'direct',
390
+ name: dependent.name,
391
+ type: dependent.type,
392
+ version: dependent.version,
393
+ });
394
+ }
395
+ else {
396
+ // Continue walking upward through this dependency
397
+ const children = buildReverseChains(lockfile, dependent, visiting);
398
+ // Only include this node if it leads to at least one direct root
399
+ if (children.length > 0) {
400
+ nodes.push({
401
+ children,
402
+ installReason: 'dependency',
403
+ name: dependent.name,
404
+ type: dependent.type,
405
+ version: dependent.version,
406
+ });
407
+ }
408
+ }
409
+ }
410
+ visiting.delete(key);
411
+ return nodes;
412
+ }
413
+ /**
414
+ * Type-guard that checks whether an unknown `err` is a Node.js filesystem
415
+ * error with the specified `code` (`'EEXIST'` or `'ENOENT'`).
416
+ */
417
+ function isEnoentOrExist(err, code) {
418
+ return err instanceof Error && err.code === code;
419
+ }
420
+ // ---------------------------------------------------------------------------
421
+ // Helpers
422
+ // ---------------------------------------------------------------------------
423
+ /**
424
+ * Check whether a process with the given PID is still running.
425
+ * Uses `process.kill(pid, 0)` which sends no signal but throws if the
426
+ * process does not exist.
427
+ */
428
+ function isProcessAlive(pid) {
429
+ try {
430
+ process.kill(pid, 0);
431
+ return true;
432
+ }
433
+ catch {
434
+ return false;
435
+ }
436
+ }
437
+ //# sourceMappingURL=lockfile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../../src/lib/lockfile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Convert markdown text to chalk-styled terminal output.
3
+ *
4
+ * Performs line-by-line transformations for common markdown constructs:
5
+ * headings, fenced code blocks, unordered lists, inline bold, and inline code.
6
+ * Lines that don't match any pattern pass through unchanged.
7
+ */
8
+ export declare function renderMarkdown(text: string): string;
9
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/lib/markdown.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAsDnD"}
@@ -0,0 +1,67 @@
1
+ import chalk from 'chalk';
2
+ /**
3
+ * Convert markdown text to chalk-styled terminal output.
4
+ *
5
+ * Performs line-by-line transformations for common markdown constructs:
6
+ * headings, fenced code blocks, unordered lists, inline bold, and inline code.
7
+ * Lines that don't match any pattern pass through unchanged.
8
+ */
9
+ export function renderMarkdown(text) {
10
+ const lines = text.split('\n');
11
+ const result = [];
12
+ let inCodeBlock = false;
13
+ for (const line of lines) {
14
+ // Toggle fenced code block state on ``` delimiters
15
+ if (line.trimStart().startsWith('```')) {
16
+ inCodeBlock = !inCodeBlock;
17
+ // Render the delimiter itself as dim
18
+ result.push(chalk.dim(line));
19
+ continue;
20
+ }
21
+ // Lines inside a fenced code block are rendered dim
22
+ if (inCodeBlock) {
23
+ result.push(chalk.dim(line));
24
+ continue;
25
+ }
26
+ // H3 headings (### ...)
27
+ const h3Match = /^###\s+(.*)$/.exec(line);
28
+ if (h3Match) {
29
+ result.push(chalk.bold(h3Match[1]));
30
+ continue;
31
+ }
32
+ // H2 headings (## ...)
33
+ const h2Match = /^##\s+(.*)$/.exec(line);
34
+ if (h2Match) {
35
+ result.push(chalk.bold(h2Match[1]));
36
+ continue;
37
+ }
38
+ // H1 headings (# ...)
39
+ const h1Match = /^#\s+(.*)$/.exec(line);
40
+ if (h1Match) {
41
+ result.push(chalk.bold(h1Match[1]));
42
+ continue;
43
+ }
44
+ // Unordered list items (- ... or * ...)
45
+ const listMatch = /^(\s*)[-*]\s+(.*)$/.exec(line);
46
+ if (listMatch) {
47
+ const formatted = applyInlineFormatting(listMatch[2]);
48
+ result.push(`${listMatch[1]}\u2022 ${formatted}`);
49
+ continue;
50
+ }
51
+ // All other lines — apply inline formatting only
52
+ result.push(applyInlineFormatting(line));
53
+ }
54
+ return result.join('\n');
55
+ }
56
+ /**
57
+ * Apply inline markdown formatting (bold and inline code) to a single line.
58
+ */
59
+ function applyInlineFormatting(line) {
60
+ let formatted = line;
61
+ // Inline bold: **text**
62
+ formatted = formatted.replace(/\*\*([^*]+)\*\*/g, (_match, content) => chalk.bold(content));
63
+ // Inline code: `text` (but not fenced code block delimiters)
64
+ formatted = formatted.replace(/`([^`]+)`/g, (_match, content) => chalk.dim(content));
65
+ return formatted;
66
+ }
67
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/lib/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,mDAAmD;QACnD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACvC,WAAW,GAAG,CAAC,WAAW,CAAC;YAC3B,qCAAqC;YACrC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;YAC7B,SAAS;QACX,CAAC;QAED,oDAAoD;QACpD,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;YAC7B,SAAS;QACX,CAAC;QAED,wBAAwB;QACxB,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACpC,SAAS;QACX,CAAC;QAED,uBAAuB;QACvB,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACpC,SAAS;QACX,CAAC;QAED,sBAAsB;QACtB,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACpC,SAAS;QACX,CAAC;QAED,wCAAwC;QACxC,MAAM,SAAS,GAAG,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,SAAS,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;YACtD,MAAM,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,UAAU,SAAS,EAAE,CAAC,CAAC;YAClD,SAAS;QACX,CAAC;QAED,iDAAiD;QACjD,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAAC,IAAY;IACzC,IAAI,SAAS,GAAG,IAAI,CAAC;IAErB,wBAAwB;IACxB,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC,MAAM,EAAE,OAAe,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAEpG,6DAA6D;IAC7D,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,OAAe,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IAE7F,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,34 @@
1
+ import type { z } from 'zod';
2
+ /**
3
+ * Result type for the non-throwing `safeParseJson` variant.
4
+ */
5
+ export type SafeParseJsonResult<T> = {
6
+ data: T;
7
+ success: true;
8
+ } | {
9
+ error: string;
10
+ success: false;
11
+ };
12
+ /**
13
+ * Parse a raw JSON string and validate against a Zod schema.
14
+ * Throws with a formatted error message on failure.
15
+ *
16
+ * @param raw - The raw JSON string to parse
17
+ * @param schema - The Zod schema to validate against
18
+ * @param context - A descriptive context string included in error messages (e.g. "Invalid config at /path")
19
+ * @returns The validated and typed data
20
+ * @throws Error with formatted message including context, JSON parse errors, or Zod validation issues
21
+ */
22
+ export declare function parseAndValidate<T>(raw: string, schema: z.ZodType<T>, context: string): T;
23
+ /**
24
+ * Parse a raw JSON string and validate against a Zod schema.
25
+ * Returns a result object instead of throwing, for use in contexts
26
+ * where failure is non-fatal (e.g. cache reads, diagnostic loading).
27
+ *
28
+ * @param raw - The raw JSON string to parse
29
+ * @param schema - The Zod schema to validate against
30
+ * @param context - A descriptive context string included in the error message
31
+ * @returns An object with `success: true` and `data`, or `success: false` and `error`
32
+ */
33
+ export declare function safeParseJson<T>(raw: string, schema: z.ZodType<T>, context: string): SafeParseJsonResult<T>;
34
+ //# sourceMappingURL=parse-json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-json.d.ts","sourceRoot":"","sources":["../../src/lib/parse-json.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B;;GAEG;AACH,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI;IAAE,IAAI,EAAE,CAAC,CAAC;IAAC,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,KAAK,CAAA;CAAE,CAAC;AAEpG;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAczF;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAiB3G"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Parse a raw JSON string and validate against a Zod schema.
3
+ * Throws with a formatted error message on failure.
4
+ *
5
+ * @param raw - The raw JSON string to parse
6
+ * @param schema - The Zod schema to validate against
7
+ * @param context - A descriptive context string included in error messages (e.g. "Invalid config at /path")
8
+ * @returns The validated and typed data
9
+ * @throws Error with formatted message including context, JSON parse errors, or Zod validation issues
10
+ */
11
+ export function parseAndValidate(raw, schema, context) {
12
+ let parsed;
13
+ try {
14
+ parsed = JSON.parse(raw);
15
+ }
16
+ catch {
17
+ throw new Error(`${context}: Invalid JSON`);
18
+ }
19
+ const result = schema.safeParse(parsed);
20
+ if (!result.success) {
21
+ throw new Error(`${context}: ${result.error.issues.map((i) => i.message).join(', ')}`);
22
+ }
23
+ return result.data;
24
+ }
25
+ /**
26
+ * Parse a raw JSON string and validate against a Zod schema.
27
+ * Returns a result object instead of throwing, for use in contexts
28
+ * where failure is non-fatal (e.g. cache reads, diagnostic loading).
29
+ *
30
+ * @param raw - The raw JSON string to parse
31
+ * @param schema - The Zod schema to validate against
32
+ * @param context - A descriptive context string included in the error message
33
+ * @returns An object with `success: true` and `data`, or `success: false` and `error`
34
+ */
35
+ export function safeParseJson(raw, schema, context) {
36
+ let parsed;
37
+ try {
38
+ parsed = JSON.parse(raw);
39
+ }
40
+ catch {
41
+ return { error: `${context}: Invalid JSON`, success: false };
42
+ }
43
+ const result = schema.safeParse(parsed);
44
+ if (!result.success) {
45
+ return {
46
+ error: `${context}: ${result.error.issues.map((i) => i.message).join(', ')}`,
47
+ success: false,
48
+ };
49
+ }
50
+ return { data: result.data, success: true };
51
+ }
52
+ //# sourceMappingURL=parse-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-json.js","sourceRoot":"","sources":["../../src/lib/parse-json.ts"],"names":[],"mappings":"AAOA;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAI,GAAW,EAAE,MAAoB,EAAE,OAAe;IACpF,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,gBAAgB,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,GAAG,OAAO,KAAK,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzF,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAI,GAAW,EAAE,MAAoB,EAAE,OAAe;IACjF,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,KAAK,EAAE,GAAG,OAAO,gBAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC/D,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO;YACL,KAAK,EAAE,GAAG,OAAO,KAAK,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YAC5E,OAAO,EAAE,KAAK;SACf,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Replace all backslashes with forward slashes in a path string.
3
+ * Used to normalize Windows-style paths for consistent lockfile storage.
4
+ */
5
+ export declare function normalizeLockfilePath(p: string): string;
6
+ /**
7
+ * Convert an absolute path to a project-relative path with forward slashes.
8
+ * Used to store portable paths in the lockfile.
9
+ */
10
+ export declare function toRelativePosix(projectDir: string, absolutePath: string): string;
11
+ /**
12
+ * Walk up from `startDir` looking for project root markers.
13
+ *
14
+ * Discovery priority:
15
+ * 1. `.atk-lock.json` file
16
+ * 2. `.claude/` directory
17
+ * 3. `.git/` directory
18
+ * 4. Fallback to the resolved `startDir`
19
+ *
20
+ * Results are cached per starting directory for the lifetime of the process.
21
+ */
22
+ export declare function findProjectRoot(startDir?: string): string;
23
+ /**
24
+ * Clear the project-root cache. Intended for use in tests so each test
25
+ * can control the filesystem layout without stale cached values.
26
+ */
27
+ export declare function resetProjectRootCache(): void;
28
+ //# sourceMappingURL=paths.d.ts.map