@amsterdamdatalabs/enact-extensions 0.1.54 → 0.1.55
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/README.md +33 -11
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/agents.d.ts +31 -6
- package/dist/internal/agents.d.ts.map +1 -1
- package/dist/internal/agents.js +234 -4
- package/dist/internal/agents.js.map +1 -1
- package/extensions/enact-repo/.agents/plugin.json +1 -1
- package/extensions/enact-repo/README.md +16 -9
- package/package.json +1 -1
- package/scripts/lib/run-repo-doctor.mjs +55 -34
- package/scripts/lib/run-repo-install.mjs +111 -285
|
@@ -14,16 +14,17 @@
|
|
|
14
14
|
* match; every merged JSON entry /
|
|
15
15
|
* TOML block present; list drifted
|
|
16
16
|
* or missing)
|
|
17
|
-
* - per-host status:
|
|
18
|
-
*
|
|
17
|
+
* - per-host status: direct native files recorded in the lock (never a
|
|
18
|
+
* plugin root or marketplace), codex (files present; read-only
|
|
19
19
|
* ~/.codex/config.toml projects."<repo>".trust_level; hooks-review-
|
|
20
20
|
* pending note), cursor (files present; MCP approval note), kimi
|
|
21
|
-
* (
|
|
22
|
-
*
|
|
21
|
+
* (native files present; global hooks are enact-vps-owned, so we only
|
|
22
|
+
* report whether ~/.kimi-code/config.toml has the
|
|
23
23
|
* `# BEGIN ENACT KIMI HOOKS` block; PLUS a read-only Kimi Code
|
|
24
24
|
* workspace-trust check — Kimi only loads project MCP servers, including
|
|
25
25
|
* `.kimi-code/mcp.json`, once the workspace is trusted), opencode
|
|
26
|
-
* (
|
|
26
|
+
* (native agents/skills and root `opencode.json` MCP entries when the
|
|
27
|
+
* selected bundle declares them; hooks are global enact-vps-owned)
|
|
27
28
|
* - required global binaries on PATH (enact-hook, lean-ctx,
|
|
28
29
|
* code-review-graph/crg, azure-devops-mcp) with versions
|
|
29
30
|
* - root `enact-config.toml` marker: present, parses as TOML, has our
|
|
@@ -332,14 +333,9 @@ function checkLockIntegrity(repo, name, lock) {
|
|
|
332
333
|
const found = existsSync(path) ? findBlock(readFileSync(path, "utf8"), name) : null;
|
|
333
334
|
if (!found || sha256(found.block) !== record.sha256) tomlIssues.push(rel);
|
|
334
335
|
}
|
|
335
|
-
// Managed symlinks
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
// check here. It is not a cosmetic gap -- a dangling link makes Claude Code
|
|
339
|
-
// report `Skills (0)` while the repo still looks installed, which is exactly
|
|
340
|
-
// the "presence is not armedness" failure AGENTS.md exists to catch. Verify
|
|
341
|
-
// by the link's own mechanism: it must BE a symlink, point where the lock
|
|
342
|
-
// says, and actually resolve.
|
|
336
|
+
// Managed symlinks carry no content hash, so `lock.files`-style verification
|
|
337
|
+
// does not reach them. Verify by the link's own mechanism: it must BE a
|
|
338
|
+
// symlink, point where the lock says, and actually resolve.
|
|
343
339
|
//
|
|
344
340
|
// lstatSync, never existsSync: existsSync follows the link and returns false
|
|
345
341
|
// for a dangling one, so it cannot tell "absent" from "present but broken".
|
|
@@ -374,48 +370,76 @@ function checkLockIntegrity(repo, name, lock) {
|
|
|
374
370
|
// ---------------------------------------------------------------------------
|
|
375
371
|
// per-host status
|
|
376
372
|
// ---------------------------------------------------------------------------
|
|
377
|
-
|
|
373
|
+
const NATIVE_HOST_PATHS = {
|
|
374
|
+
claude: { prefixes: [".claude/"], exact: [".mcp.json"] },
|
|
375
|
+
codex: { prefixes: [".codex/", ".agents/skills/"], exact: [] },
|
|
376
|
+
cursor: { prefixes: [".cursor/"], exact: [] },
|
|
377
|
+
kimi: { prefixes: [".kimi-code/"], exact: [] },
|
|
378
|
+
opencode: { prefixes: [".opencode/"], exact: ["opencode.json"] },
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
function isNativeHostPath(host, rel) {
|
|
382
|
+
const { prefixes, exact } = NATIVE_HOST_PATHS[host];
|
|
383
|
+
return exact.includes(rel) || prefixes.some((prefix) => rel.startsWith(prefix));
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// The lock is the authoritative ownership record. A host is present only
|
|
387
|
+
// when it owns at least one direct native path and every such path still
|
|
388
|
+
// exists. This deliberately does not infer installation from a plugin root,
|
|
389
|
+
// marketplace, or a host config file that another extension/user may own.
|
|
390
|
+
function checkNativeHostPresence(repo, lock, host) {
|
|
391
|
+
const rels = [
|
|
392
|
+
...Object.keys(lock.files ?? {}),
|
|
393
|
+
...Object.keys(lock.merged ?? {}),
|
|
394
|
+
...Object.keys(lock.toml ?? {}),
|
|
395
|
+
...Object.keys(lock.symlinks ?? {}),
|
|
396
|
+
].filter((rel) => isNativeHostPath(host, rel));
|
|
397
|
+
const nativePaths = [...new Set(rels)].sort();
|
|
398
|
+
return {
|
|
399
|
+
nativePaths,
|
|
400
|
+
present: nativePaths.length > 0 && nativePaths.every((rel) => existsSync(abs(repo, rel))),
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function checkHosts(repo, lock, home) {
|
|
378
405
|
const hosts = {};
|
|
379
406
|
for (const host of REPO_HOSTS) {
|
|
380
407
|
const targeted = lock.hosts.includes(host);
|
|
408
|
+
const native = targeted ? checkNativeHostPresence(repo, lock, host) : { nativePaths: [], present: true };
|
|
381
409
|
if (host === "claude") {
|
|
382
|
-
const present =
|
|
383
|
-
!targeted ||
|
|
384
|
-
(existsSync(abs(repo, ".claude-plugin/marketplace.json")) && existsSync(abs(repo, ".claude/settings.json")));
|
|
385
410
|
hosts.claude = {
|
|
386
411
|
targeted,
|
|
387
|
-
present,
|
|
412
|
+
present: native.present,
|
|
413
|
+
nativePaths: native.nativePaths,
|
|
388
414
|
note: targeted
|
|
389
|
-
? "
|
|
415
|
+
? "direct native Claude files are installed; Claude Code loads them after you TRUST this folder"
|
|
390
416
|
: "not targeted by this install",
|
|
391
417
|
};
|
|
392
418
|
} else if (host === "codex") {
|
|
393
|
-
const present =
|
|
394
|
-
!targeted || existsSync(abs(repo, ".codex/hooks.json")) || existsSync(abs(repo, ".codex/config.toml"));
|
|
395
419
|
hosts.codex = {
|
|
396
420
|
targeted,
|
|
397
|
-
present,
|
|
421
|
+
present: native.present,
|
|
422
|
+
nativePaths: native.nativePaths,
|
|
398
423
|
trust: checkCodexTrust(repo, home),
|
|
399
424
|
note: targeted
|
|
400
425
|
? "config/hooks load only after you trust this project; hooks review (/hooks) is pending until you review them in Codex"
|
|
401
426
|
: "not targeted by this install",
|
|
402
427
|
};
|
|
403
428
|
} else if (host === "cursor") {
|
|
404
|
-
const present =
|
|
405
|
-
!targeted || existsSync(abs(repo, ".cursor/hooks.json")) || existsSync(abs(repo, ".cursor/mcp.json"));
|
|
406
429
|
hosts.cursor = {
|
|
407
430
|
targeted,
|
|
408
|
-
present,
|
|
431
|
+
present: native.present,
|
|
432
|
+
nativePaths: native.nativePaths,
|
|
409
433
|
note: targeted
|
|
410
434
|
? "Cursor prompts for MCP server approval on first use -- approve code-review-graph/lean-ctx in Cursor's MCP settings"
|
|
411
435
|
: "not targeted by this install",
|
|
412
436
|
};
|
|
413
437
|
} else if (host === "kimi") {
|
|
414
|
-
const present = !targeted || existsSync(abs(repo, ".kimi-code/mcp.json"));
|
|
415
438
|
const globalHooks = checkKimiGlobalHooks(home);
|
|
416
439
|
hosts.kimi = {
|
|
417
440
|
targeted,
|
|
418
|
-
present,
|
|
441
|
+
present: native.present,
|
|
442
|
+
nativePaths: native.nativePaths,
|
|
419
443
|
globalHooksInstalled: globalHooks.hooksBlockPresent,
|
|
420
444
|
globalHooksNote: !globalHooks.configFound
|
|
421
445
|
? `${globalHooks.configPath} not found`
|
|
@@ -425,15 +449,12 @@ function checkHosts(repo, lock, home, contentOutdated) {
|
|
|
425
449
|
workspaceTrust: checkKimiWorkspaceTrust(repo),
|
|
426
450
|
};
|
|
427
451
|
} else if (host === "opencode") {
|
|
428
|
-
const rel = `.opencode/plugins/${lock.plugin}.ts`;
|
|
429
|
-
const present = !targeted || existsSync(abs(repo, rel));
|
|
430
|
-
const generatedFromCurrentBundle = targeted && present ? !contentOutdated.includes(rel) : null;
|
|
431
452
|
hosts.opencode = {
|
|
432
453
|
targeted,
|
|
433
|
-
present,
|
|
434
|
-
|
|
454
|
+
present: native.present,
|
|
455
|
+
nativePaths: native.nativePaths,
|
|
435
456
|
note: targeted
|
|
436
|
-
? "
|
|
457
|
+
? "direct native OpenCode files are installed; repo-local hooks are global enact-vps-owned"
|
|
437
458
|
: "not targeted by this install",
|
|
438
459
|
};
|
|
439
460
|
}
|
|
@@ -567,7 +588,7 @@ export function runRepoDoctor(pluginRoot, options = {}) {
|
|
|
567
588
|
);
|
|
568
589
|
}
|
|
569
590
|
|
|
570
|
-
const hosts = checkHosts(repo, lock, home
|
|
591
|
+
const hosts = checkHosts(repo, lock, home);
|
|
571
592
|
|
|
572
593
|
const binaries = {};
|
|
573
594
|
for (const bin of REQUIRED_BINARIES) binaries[bin] = checkBinary(bin);
|