@cleocode/core 2026.4.30 → 2026.4.31

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 (89) hide show
  1. package/dist/bootstrap.d.ts +35 -0
  2. package/dist/bootstrap.d.ts.map +1 -1
  3. package/dist/code/index.d.ts +8 -4
  4. package/dist/code/index.d.ts.map +1 -1
  5. package/dist/code/parser.d.ts +22 -9
  6. package/dist/code/parser.d.ts.map +1 -1
  7. package/dist/hooks/handlers/session-hooks.d.ts +11 -4
  8. package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
  9. package/dist/hooks/payload-schemas.d.ts +6 -6
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +3859 -3008
  13. package/dist/index.js.map +4 -4
  14. package/dist/internal.d.ts +10 -7
  15. package/dist/internal.d.ts.map +1 -1
  16. package/dist/lib/tree-sitter-languages.d.ts +11 -7
  17. package/dist/lib/tree-sitter-languages.d.ts.map +1 -1
  18. package/dist/memory/auto-extract.d.ts +27 -15
  19. package/dist/memory/auto-extract.d.ts.map +1 -1
  20. package/dist/memory/brain-backfill.d.ts +59 -0
  21. package/dist/memory/brain-backfill.d.ts.map +1 -0
  22. package/dist/memory/brain-purge.d.ts +51 -0
  23. package/dist/memory/brain-purge.d.ts.map +1 -0
  24. package/dist/memory/brain-retrieval.d.ts.map +1 -1
  25. package/dist/memory/brain-search.d.ts.map +1 -1
  26. package/dist/memory/decisions.d.ts.map +1 -1
  27. package/dist/memory/engine-compat.d.ts +71 -0
  28. package/dist/memory/engine-compat.d.ts.map +1 -1
  29. package/dist/memory/graph-auto-populate.d.ts +65 -0
  30. package/dist/memory/graph-auto-populate.d.ts.map +1 -0
  31. package/dist/memory/graph-queries.d.ts +127 -0
  32. package/dist/memory/graph-queries.d.ts.map +1 -0
  33. package/dist/memory/learnings.d.ts +2 -0
  34. package/dist/memory/learnings.d.ts.map +1 -1
  35. package/dist/memory/patterns.d.ts +2 -0
  36. package/dist/memory/patterns.d.ts.map +1 -1
  37. package/dist/memory/quality-scoring.d.ts +90 -0
  38. package/dist/memory/quality-scoring.d.ts.map +1 -0
  39. package/dist/sessions/session-memory-bridge.d.ts +16 -10
  40. package/dist/sessions/session-memory-bridge.d.ts.map +1 -1
  41. package/dist/store/brain-accessor.d.ts +7 -0
  42. package/dist/store/brain-accessor.d.ts.map +1 -1
  43. package/dist/store/brain-schema.d.ts +185 -11
  44. package/dist/store/brain-schema.d.ts.map +1 -1
  45. package/dist/store/brain-sqlite.d.ts.map +1 -1
  46. package/dist/store/nexus-schema.d.ts +480 -2
  47. package/dist/store/nexus-schema.d.ts.map +1 -1
  48. package/dist/store/tasks-schema.d.ts +9 -9
  49. package/dist/store/validation-schemas.d.ts +44 -28
  50. package/dist/store/validation-schemas.d.ts.map +1 -1
  51. package/dist/system/dependencies.d.ts +43 -0
  52. package/dist/system/dependencies.d.ts.map +1 -0
  53. package/dist/system/health.d.ts +3 -0
  54. package/dist/system/health.d.ts.map +1 -1
  55. package/dist/tasks/complete.d.ts.map +1 -1
  56. package/package.json +19 -19
  57. package/src/bootstrap.ts +124 -0
  58. package/src/code/index.ts +20 -4
  59. package/src/code/parser.ts +310 -110
  60. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.ts +19 -45
  61. package/src/hooks/handlers/__tests__/session-hooks.test.ts +42 -54
  62. package/src/hooks/handlers/session-hooks.ts +11 -33
  63. package/src/index.ts +14 -0
  64. package/src/internal.ts +37 -7
  65. package/src/lib/tree-sitter-languages.ts +11 -7
  66. package/src/memory/__tests__/auto-extract.test.ts +20 -82
  67. package/src/memory/__tests__/embedding-pipeline.test.ts +389 -0
  68. package/src/memory/auto-extract.ts +34 -120
  69. package/src/memory/brain-backfill.ts +471 -0
  70. package/src/memory/brain-purge.ts +315 -0
  71. package/src/memory/brain-retrieval.ts +43 -2
  72. package/src/memory/brain-search.ts +23 -6
  73. package/src/memory/decisions.ts +76 -3
  74. package/src/memory/engine-compat.ts +168 -0
  75. package/src/memory/graph-auto-populate.ts +173 -0
  76. package/src/memory/graph-queries.ts +424 -0
  77. package/src/memory/learnings.ts +55 -7
  78. package/src/memory/patterns.ts +66 -13
  79. package/src/memory/quality-scoring.ts +173 -0
  80. package/src/sessions/__tests__/session-memory-bridge.test.ts +27 -49
  81. package/src/sessions/session-memory-bridge.ts +19 -47
  82. package/src/store/__tests__/brain-accessor-pageindex.test.ts +93 -22
  83. package/src/store/brain-accessor.ts +48 -2
  84. package/src/store/brain-schema.ts +165 -13
  85. package/src/store/brain-sqlite.ts +35 -0
  86. package/src/store/nexus-schema.ts +257 -3
  87. package/src/system/dependencies.ts +534 -0
  88. package/src/system/health.ts +126 -22
  89. package/src/tasks/complete.ts +40 -0
@@ -0,0 +1,534 @@
1
+ /**
2
+ * Central dependency registry for CLEO runtime dependency verification.
3
+ *
4
+ * This is the SSoT for all external dependency checks. Every CLEO subsystem
5
+ * that needs to gate a feature on tool availability MUST use functions from
6
+ * this module rather than implementing ad-hoc `which` / `commandExists` calls.
7
+ *
8
+ * Relationship to other modules:
9
+ * - `platform.ts` — provides `commandExists()` and `PLATFORM` utilities.
10
+ * - `health.ts` — calls `checkAllDependencies()` inside `coreDoctorReport()`.
11
+ * - `code/parser.ts` — `isTreeSitterAvailable()` is delegated here for the
12
+ * `tree-sitter` dependency check.
13
+ *
14
+ * @task T507
15
+ */
16
+
17
+ import { execFileSync } from 'node:child_process';
18
+ import { createRequire } from 'node:module';
19
+ import { dirname } from 'node:path';
20
+ import { fileURLToPath } from 'node:url';
21
+ import type {
22
+ DependencyCategory,
23
+ DependencyCheckResult,
24
+ DependencyReport,
25
+ DependencySpec,
26
+ } from '@cleocode/contracts';
27
+ import { PLATFORM } from '../platform.js';
28
+
29
+ /** ESM-safe require function for loading native addons. */
30
+ const _require = createRequire(import.meta.url);
31
+ /** Directory of this compiled file (packages/core/dist/system/). */
32
+ const _dirname = dirname(fileURLToPath(import.meta.url));
33
+
34
+ // ============================================================================
35
+ // Internal helpers
36
+ // ============================================================================
37
+
38
+ /**
39
+ * Run a command and return its stdout trimmed, or `null` on failure.
40
+ *
41
+ * @param cmd - Executable name or path.
42
+ * @param args - Arguments to pass.
43
+ * @param timeoutMs - Max execution time in milliseconds (default: 3000).
44
+ */
45
+ function tryExec(cmd: string, args: string[], timeoutMs = 3000): string | null {
46
+ try {
47
+ return execFileSync(cmd, args, {
48
+ stdio: ['pipe', 'pipe', 'pipe'],
49
+ timeout: timeoutMs,
50
+ })
51
+ .toString()
52
+ .trim();
53
+ } catch {
54
+ return null;
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Resolve the absolute path of a command on PATH, or `null` if absent.
60
+ *
61
+ * Uses `which` on POSIX and `where` on Windows, matching `commandExists()`
62
+ * in `platform.ts`.
63
+ */
64
+ function which(cmd: string): string | null {
65
+ const tool = PLATFORM === 'windows' ? 'where' : 'which';
66
+ return tryExec(tool, [cmd]);
67
+ }
68
+
69
+ /**
70
+ * Parse a semver-style major version from an arbitrary version string.
71
+ * Returns the numeric major version, or `null` if unparseable.
72
+ *
73
+ * @param raw - Raw version string (e.g. "git version 2.43.0", "v24.1.0").
74
+ */
75
+ function parseMajorVersion(raw: string): number | null {
76
+ // Match first sequence of digits (possibly preceded by "v")
77
+ const match = /\bv?(\d+)/.exec(raw);
78
+ if (!match) return null;
79
+ const major = Number(match[1]);
80
+ return Number.isNaN(major) ? null : major;
81
+ }
82
+
83
+ // ============================================================================
84
+ // Dependency registry — static specifications
85
+ // ============================================================================
86
+
87
+ /** All known CLEO dependencies, ordered: required first, then optional/feature. */
88
+ const DEPENDENCY_SPECS: DependencySpec[] = [
89
+ // ── Required ────────────────────────────────────────────────────────────
90
+ {
91
+ name: 'node',
92
+ category: 'required',
93
+ description: 'Node.js runtime — the execution environment for all CLEO operations.',
94
+ versionConstraint: '>=24.0.0',
95
+ documentationUrl: 'https://nodejs.org/en/download/',
96
+ installCommand: 'curl -fsSL https://fnm.vercel.app/install | bash && fnm install 24',
97
+ },
98
+ {
99
+ name: 'git',
100
+ category: 'required',
101
+ description:
102
+ 'Git version control — required for cleo checkpoint, cleo init, and hook management.',
103
+ documentationUrl: 'https://git-scm.com/downloads',
104
+ installCommand: 'apt install git # or: brew install git',
105
+ },
106
+
107
+ // ── Optional (feature-gating) ────────────────────────────────────────────
108
+ {
109
+ name: 'tree-sitter',
110
+ category: 'optional',
111
+ description:
112
+ 'Tree-sitter native Node module — enables cleo code outline, cleo code search, and AST analysis. Bundled as a regular dependency; re-run pnpm install if missing.',
113
+ documentationUrl: 'https://tree-sitter.github.io/tree-sitter/',
114
+ installCommand: 'pnpm install',
115
+ },
116
+ {
117
+ name: 'gh',
118
+ category: 'optional',
119
+ description: 'GitHub CLI — enables cleo issue, cleo release, and GitHub-integrated workflows.',
120
+ documentationUrl: 'https://cli.github.com/',
121
+ installCommand: 'brew install gh # or: apt install gh',
122
+ },
123
+ {
124
+ name: 'unzip',
125
+ category: 'optional',
126
+ description:
127
+ 'unzip — required for cleo agent archive import (extracting .cleo-export bundles).',
128
+ documentationUrl: 'https://infozip.sourceforge.net/',
129
+ installCommand: 'apt install unzip # or: brew install unzip',
130
+ platforms: ['linux', 'darwin'],
131
+ },
132
+ {
133
+ name: 'zip',
134
+ category: 'optional',
135
+ description: 'zip — required for cleo agent archive export (creating .cleo-export bundles).',
136
+ documentationUrl: 'https://infozip.sourceforge.net/',
137
+ installCommand: 'apt install zip # or: brew install zip',
138
+ platforms: ['linux', 'darwin'],
139
+ },
140
+
141
+ // ── Feature (native addons — internally managed) ─────────────────────────
142
+ {
143
+ name: 'cant-napi',
144
+ category: 'feature',
145
+ description:
146
+ 'Native CANT parser (Rust/napi-rs) — accelerates CANT DSL parsing. Falls back to TS implementation when absent.',
147
+ documentationUrl: 'https://github.com/kryptobaseddev/cleocode',
148
+ installCommand: 'cargo build --release -p cant-napi',
149
+ },
150
+ {
151
+ name: 'lafs-napi',
152
+ category: 'feature',
153
+ description:
154
+ 'Native LAFS validator (Rust/napi-rs) — accelerates envelope validation. Falls back to AJV when absent.',
155
+ documentationUrl: 'https://github.com/kryptobaseddev/cleocode',
156
+ installCommand: 'cargo build --release -p lafs-napi',
157
+ },
158
+ ];
159
+
160
+ // ============================================================================
161
+ // Individual dependency check functions
162
+ // ============================================================================
163
+
164
+ /**
165
+ * Check Node.js availability and version constraint (>=24.0.0).
166
+ *
167
+ * Node is always present when CLEO runs — this check validates the VERSION
168
+ * meets the minimum requirement, not merely that Node exists.
169
+ */
170
+ function checkNode(): DependencyCheckResult {
171
+ const raw = process.version; // e.g. "v24.1.0"
172
+ const version = raw.replace(/^v/, '');
173
+ const major = parseMajorVersion(raw) ?? 0;
174
+ const healthy = major >= 24;
175
+
176
+ return {
177
+ name: 'node',
178
+ category: 'required',
179
+ installed: true,
180
+ version,
181
+ location: process.execPath,
182
+ healthy,
183
+ ...(healthy
184
+ ? {}
185
+ : {
186
+ error: `Node.js ${version} does not meet the minimum requirement of >=24.0.0`,
187
+ suggestedFix:
188
+ 'Upgrade Node.js: curl -fsSL https://fnm.vercel.app/install | bash && fnm install 24',
189
+ }),
190
+ };
191
+ }
192
+
193
+ /**
194
+ * Check git availability.
195
+ *
196
+ * Runs `git --version` to detect the installed version string.
197
+ */
198
+ function checkGit(): DependencyCheckResult {
199
+ const location = which('git');
200
+
201
+ if (!location) {
202
+ return {
203
+ name: 'git',
204
+ category: 'required',
205
+ installed: false,
206
+ healthy: false,
207
+ error: 'git not found on PATH',
208
+ suggestedFix: 'Install git: https://git-scm.com/downloads',
209
+ };
210
+ }
211
+
212
+ const raw = tryExec('git', ['--version']) ?? '';
213
+ // "git version 2.43.0" -> "2.43.0"
214
+ const version = raw.replace(/^git version\s*/i, '').split(/\s/)[0] ?? raw;
215
+
216
+ return {
217
+ name: 'git',
218
+ category: 'required',
219
+ installed: true,
220
+ version: version || undefined,
221
+ location: location.split('\n')[0] ?? location, // `where` may return multiple lines on Windows
222
+ healthy: true,
223
+ };
224
+ }
225
+
226
+ /**
227
+ * Check tree-sitter native Node module availability.
228
+ *
229
+ * Delegates to `isTreeSitterAvailable()` from `packages/core/src/code/parser.ts`
230
+ * which probes whether the `tree-sitter` native module loads successfully.
231
+ * No CLI binary on PATH is required — the module ships as a bundled dependency.
232
+ */
233
+ async function checkTreeSitter(): Promise<DependencyCheckResult> {
234
+ // Lazy import to avoid circular dependency and to keep this module lightweight
235
+ const { isTreeSitterAvailable } = await import('../code/parser.js');
236
+ const available = isTreeSitterAvailable();
237
+
238
+ // Read the installed package version from its package.json if available
239
+ let version: string | undefined;
240
+ if (available) {
241
+ try {
242
+ const pkgJson = _require('tree-sitter/package.json') as { version?: string };
243
+ version = pkgJson.version;
244
+ } catch {
245
+ version = undefined;
246
+ }
247
+ }
248
+
249
+ return {
250
+ name: 'tree-sitter',
251
+ category: 'optional',
252
+ installed: available,
253
+ version,
254
+ location: available ? 'node_modules/tree-sitter (native binding)' : undefined,
255
+ // Optional: missing is healthy (feature simply disabled)
256
+ healthy: true,
257
+ ...(available
258
+ ? {}
259
+ : {
260
+ error:
261
+ 'tree-sitter native module not loaded — cleo code outline/search/unfold features are disabled',
262
+ suggestedFix: 'pnpm install',
263
+ }),
264
+ };
265
+ }
266
+
267
+ /**
268
+ * Check GitHub CLI (`gh`) availability.
269
+ */
270
+ function checkGh(): DependencyCheckResult {
271
+ const location = which('gh');
272
+
273
+ if (!location) {
274
+ return {
275
+ name: 'gh',
276
+ category: 'optional',
277
+ installed: false,
278
+ healthy: true, // optional — absence is acceptable
279
+ error: 'gh not found — cleo issue and cleo release features are disabled',
280
+ suggestedFix: 'Install GitHub CLI: https://cli.github.com/',
281
+ };
282
+ }
283
+
284
+ const versionRaw = tryExec('gh', ['--version']) ?? '';
285
+ // "gh version 2.40.1 (2024-01-15)" -> "2.40.1"
286
+ const versionMatch = /\bgh version\s+(\S+)/.exec(versionRaw);
287
+ const version = versionMatch?.[1] ?? undefined;
288
+
289
+ return {
290
+ name: 'gh',
291
+ category: 'optional',
292
+ installed: true,
293
+ version,
294
+ location: location.split('\n')[0] ?? location,
295
+ healthy: true,
296
+ };
297
+ }
298
+
299
+ /**
300
+ * Check availability of a simple CLI tool (unzip, zip, etc.).
301
+ *
302
+ * @param name - Tool name as it appears on PATH.
303
+ * @param category - Dependency category for the spec.
304
+ * @param errorMsg - Human-readable description of what is disabled when absent.
305
+ * @param fixHint - Install hint to show the user.
306
+ */
307
+ function checkSimpleTool(
308
+ name: string,
309
+ category: DependencyCategory,
310
+ errorMsg: string,
311
+ fixHint: string,
312
+ ): DependencyCheckResult {
313
+ const location = which(name);
314
+
315
+ if (!location) {
316
+ return {
317
+ name,
318
+ category,
319
+ installed: false,
320
+ healthy: true, // optional — absence is acceptable
321
+ error: errorMsg,
322
+ suggestedFix: fixHint,
323
+ };
324
+ }
325
+
326
+ return {
327
+ name,
328
+ category,
329
+ installed: true,
330
+ location: location.split('\n')[0] ?? location,
331
+ healthy: true,
332
+ };
333
+ }
334
+
335
+ /**
336
+ * Check cant-napi native addon availability.
337
+ *
338
+ * Probes the native binary directly using the same path resolution that
339
+ * `packages/cant/src/native-loader.ts` uses, without importing the cant
340
+ * package (which would create a circular dep: core → cant → core).
341
+ *
342
+ * The resolution order mirrors the cant native-loader:
343
+ * 1. `packages/cant/napi/cant.linux-x64-gnu.node` (installed binary)
344
+ * 2. `crates/cant-napi/index.cjs` (dev build output)
345
+ */
346
+ function checkCantNapi(): DependencyCheckResult {
347
+ let available = false;
348
+
349
+ try {
350
+ // From packages/core/dist/system/ the relative path to the cant binary:
351
+ // ../../../cant/napi/cant.linux-x64-gnu.node
352
+ const binary = _require.resolve('../../../cant/napi/cant.linux-x64-gnu.node', {
353
+ paths: [_dirname],
354
+ });
355
+ _require(binary);
356
+ available = true;
357
+ } catch {
358
+ try {
359
+ const fallback = _require.resolve('../../../../crates/cant-napi/index.cjs', {
360
+ paths: [_dirname],
361
+ });
362
+ _require(fallback);
363
+ available = true;
364
+ } catch {
365
+ available = false;
366
+ }
367
+ }
368
+
369
+ return {
370
+ name: 'cant-napi',
371
+ category: 'feature',
372
+ installed: available,
373
+ healthy: true, // feature: TypeScript fallback is always present
374
+ ...(available
375
+ ? {}
376
+ : {
377
+ error: 'cant-napi native addon not loaded — using TypeScript CANT parser (slower)',
378
+ suggestedFix: 'cargo build --release -p cant-napi',
379
+ }),
380
+ };
381
+ }
382
+
383
+ /**
384
+ * Check lafs-napi native addon availability.
385
+ *
386
+ * Attempts to load the native module via `createRequire` using the same
387
+ * resolution strategy as `packages/lafs/src/native-loader.ts`, without
388
+ * importing the lafs package as a module (the `./native-loader` subpath is
389
+ * not exported in the lafs `package.json` exports map).
390
+ *
391
+ * The resolution order mirrors the lafs native-loader:
392
+ * 1. `@cleocode/lafs-native` npm package
393
+ * 2. `crates/lafs-napi/index.cjs` (dev build output)
394
+ */
395
+ function checkLafsNapi(): DependencyCheckResult {
396
+ let available = false;
397
+
398
+ try {
399
+ _require.resolve('@cleocode/lafs-native', { paths: [_dirname] });
400
+ _require('@cleocode/lafs-native');
401
+ available = true;
402
+ } catch {
403
+ try {
404
+ const fallback = _require.resolve('../../../../crates/lafs-napi/index.cjs', {
405
+ paths: [_dirname],
406
+ });
407
+ _require(fallback);
408
+ available = true;
409
+ } catch {
410
+ available = false;
411
+ }
412
+ }
413
+
414
+ return {
415
+ name: 'lafs-napi',
416
+ category: 'feature',
417
+ installed: available,
418
+ healthy: true, // feature: AJV fallback is always present
419
+ ...(available
420
+ ? {}
421
+ : {
422
+ error: 'lafs-napi native addon not loaded — using AJV validator (slower)',
423
+ suggestedFix: 'cargo build --release -p lafs-napi',
424
+ }),
425
+ };
426
+ }
427
+
428
+ // ============================================================================
429
+ // Public API
430
+ // ============================================================================
431
+
432
+ /**
433
+ * Return all registered dependency specifications.
434
+ *
435
+ * Useful for generating documentation, help text, or install guides without
436
+ * running any system checks.
437
+ *
438
+ * @returns Immutable array of {@link DependencySpec} objects.
439
+ */
440
+ export function getDependencySpecs(): DependencySpec[] {
441
+ return DEPENDENCY_SPECS;
442
+ }
443
+
444
+ /**
445
+ * Check a single dependency by name and return its runtime result.
446
+ *
447
+ * @param name - Canonical dependency identifier (must match a registered spec).
448
+ * @returns The check result, or an error result if the name is unknown.
449
+ */
450
+ export async function checkDependency(name: string): Promise<DependencyCheckResult> {
451
+ switch (name) {
452
+ case 'node':
453
+ return Promise.resolve(checkNode());
454
+ case 'git':
455
+ return Promise.resolve(checkGit());
456
+ case 'tree-sitter':
457
+ return checkTreeSitter();
458
+ case 'gh':
459
+ return Promise.resolve(checkGh());
460
+ case 'unzip':
461
+ return Promise.resolve(
462
+ checkSimpleTool(
463
+ 'unzip',
464
+ 'optional',
465
+ 'unzip not found — cleo agent archive import is disabled',
466
+ 'apt install unzip # or: brew install unzip',
467
+ ),
468
+ );
469
+ case 'zip':
470
+ return Promise.resolve(
471
+ checkSimpleTool(
472
+ 'zip',
473
+ 'optional',
474
+ 'zip not found — cleo agent archive export is disabled',
475
+ 'apt install zip # or: brew install zip',
476
+ ),
477
+ );
478
+ case 'cant-napi':
479
+ return Promise.resolve(checkCantNapi());
480
+ case 'lafs-napi':
481
+ return Promise.resolve(checkLafsNapi());
482
+ default:
483
+ return Promise.resolve({
484
+ name,
485
+ category: 'optional',
486
+ installed: false,
487
+ healthy: false,
488
+ error: `Unknown dependency: "${name}" — not registered in the CLEO dependency registry`,
489
+ });
490
+ }
491
+ }
492
+
493
+ /**
494
+ * Check all registered CLEO dependencies and return a consolidated report.
495
+ *
496
+ * Skips platform-specific dependencies that do not apply to the current OS.
497
+ * For example, `unzip` and `zip` are only checked on Linux and macOS.
498
+ *
499
+ * @returns A {@link DependencyReport} with results for every applicable dependency.
500
+ */
501
+ export async function checkAllDependencies(): Promise<DependencyReport> {
502
+ const timestamp = new Date().toISOString();
503
+ const platform = process.platform;
504
+ const nodeVersion = process.version.replace(/^v/, '');
505
+
506
+ const applicableSpecs = DEPENDENCY_SPECS.filter((spec) => {
507
+ if (!spec.platforms) return true;
508
+ return spec.platforms.includes(platform as 'linux' | 'darwin' | 'win32');
509
+ });
510
+
511
+ const results = await Promise.all(applicableSpecs.map((spec) => checkDependency(spec.name)));
512
+
513
+ const requiredResults = results.filter((r) => r.category === 'required');
514
+ const allRequiredMet = requiredResults.every((r) => r.healthy);
515
+
516
+ const warnings: string[] = [];
517
+ for (const result of results) {
518
+ if (!result.installed && result.error) {
519
+ warnings.push(result.error);
520
+ }
521
+ }
522
+
523
+ return {
524
+ timestamp,
525
+ platform,
526
+ nodeVersion,
527
+ results,
528
+ allRequiredMet,
529
+ warnings,
530
+ };
531
+ }
532
+
533
+ // Re-export types for convenience of callers within core/
534
+ export type { DependencyCategory, DependencyCheckResult, DependencyReport, DependencySpec };