@cleocode/core 2026.4.29 → 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.
- package/dist/bootstrap.d.ts +35 -0
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/code/index.d.ts +8 -4
- package/dist/code/index.d.ts.map +1 -1
- package/dist/code/parser.d.ts +22 -9
- package/dist/code/parser.d.ts.map +1 -1
- package/dist/hooks/handlers/session-hooks.d.ts +11 -4
- package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
- package/dist/hooks/payload-schemas.d.ts +6 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3859 -3008
- package/dist/index.js.map +4 -4
- package/dist/internal.d.ts +10 -7
- package/dist/internal.d.ts.map +1 -1
- package/dist/lib/tree-sitter-languages.d.ts +11 -7
- package/dist/lib/tree-sitter-languages.d.ts.map +1 -1
- package/dist/memory/auto-extract.d.ts +27 -15
- package/dist/memory/auto-extract.d.ts.map +1 -1
- package/dist/memory/brain-backfill.d.ts +59 -0
- package/dist/memory/brain-backfill.d.ts.map +1 -0
- package/dist/memory/brain-purge.d.ts +51 -0
- package/dist/memory/brain-purge.d.ts.map +1 -0
- package/dist/memory/brain-retrieval.d.ts.map +1 -1
- package/dist/memory/brain-search.d.ts.map +1 -1
- package/dist/memory/decisions.d.ts.map +1 -1
- package/dist/memory/engine-compat.d.ts +71 -0
- package/dist/memory/engine-compat.d.ts.map +1 -1
- package/dist/memory/graph-auto-populate.d.ts +65 -0
- package/dist/memory/graph-auto-populate.d.ts.map +1 -0
- package/dist/memory/graph-queries.d.ts +127 -0
- package/dist/memory/graph-queries.d.ts.map +1 -0
- package/dist/memory/learnings.d.ts +2 -0
- package/dist/memory/learnings.d.ts.map +1 -1
- package/dist/memory/patterns.d.ts +2 -0
- package/dist/memory/patterns.d.ts.map +1 -1
- package/dist/memory/quality-scoring.d.ts +90 -0
- package/dist/memory/quality-scoring.d.ts.map +1 -0
- package/dist/sessions/session-memory-bridge.d.ts +16 -10
- package/dist/sessions/session-memory-bridge.d.ts.map +1 -1
- package/dist/store/brain-accessor.d.ts +7 -0
- package/dist/store/brain-accessor.d.ts.map +1 -1
- package/dist/store/brain-schema.d.ts +185 -11
- package/dist/store/brain-schema.d.ts.map +1 -1
- package/dist/store/brain-sqlite.d.ts.map +1 -1
- package/dist/store/nexus-schema.d.ts +480 -2
- package/dist/store/nexus-schema.d.ts.map +1 -1
- package/dist/store/tasks-schema.d.ts +9 -9
- package/dist/store/validation-schemas.d.ts +44 -28
- package/dist/store/validation-schemas.d.ts.map +1 -1
- package/dist/system/dependencies.d.ts +43 -0
- package/dist/system/dependencies.d.ts.map +1 -0
- package/dist/system/health.d.ts +3 -0
- package/dist/system/health.d.ts.map +1 -1
- package/dist/tasks/complete.d.ts.map +1 -1
- package/package.json +19 -19
- package/src/bootstrap.ts +124 -0
- package/src/code/index.ts +20 -4
- package/src/code/parser.ts +310 -110
- package/src/hooks/handlers/__tests__/hook-automation-e2e.test.ts +19 -45
- package/src/hooks/handlers/__tests__/session-hooks.test.ts +42 -54
- package/src/hooks/handlers/session-hooks.ts +11 -33
- package/src/index.ts +14 -0
- package/src/internal.ts +37 -7
- package/src/lib/tree-sitter-languages.ts +11 -7
- package/src/memory/__tests__/auto-extract.test.ts +20 -82
- package/src/memory/__tests__/embedding-pipeline.test.ts +389 -0
- package/src/memory/auto-extract.ts +34 -120
- package/src/memory/brain-backfill.ts +471 -0
- package/src/memory/brain-purge.ts +315 -0
- package/src/memory/brain-retrieval.ts +43 -2
- package/src/memory/brain-search.ts +23 -6
- package/src/memory/decisions.ts +76 -3
- package/src/memory/engine-compat.ts +168 -0
- package/src/memory/graph-auto-populate.ts +173 -0
- package/src/memory/graph-queries.ts +424 -0
- package/src/memory/learnings.ts +55 -7
- package/src/memory/patterns.ts +66 -13
- package/src/memory/quality-scoring.ts +173 -0
- package/src/sessions/__tests__/session-memory-bridge.test.ts +27 -49
- package/src/sessions/session-memory-bridge.ts +19 -47
- package/src/store/__tests__/brain-accessor-pageindex.test.ts +93 -22
- package/src/store/brain-accessor.ts +48 -2
- package/src/store/brain-schema.ts +165 -13
- package/src/store/brain-sqlite.ts +35 -0
- package/src/store/nexus-schema.ts +257 -3
- package/src/system/dependencies.ts +534 -0
- package/src/system/health.ts +126 -22
- 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 };
|