@amsterdamdatalabs/enact-extensions 0.1.61 → 0.1.62

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.
@@ -16,7 +16,16 @@ export interface FileAction {
16
16
  action: "create" | "update" | "remove" | "remove-dir" | "skip";
17
17
  reason: string;
18
18
  }
19
+ interface FilePathStat {
20
+ isFile(): boolean;
21
+ isDirectory(): boolean;
22
+ isSymbolicLink(): boolean;
23
+ }
19
24
  export interface FileOps {
25
+ /** Observe the filesystem after all operations planned so far. */
26
+ lstat(rel: string): FilePathStat | undefined;
27
+ /** Whether a path exists after all operations planned so far. */
28
+ exists(rel: string): boolean;
20
29
  /** Write `content`; false when the file already has exactly those bytes. */
21
30
  write(rel: string, content: string | Buffer, reason?: string): boolean;
22
31
  /** Remove a file; false when it is not there. */
@@ -27,10 +36,9 @@ export interface FileOps {
27
36
  removeDir(rel: string, reason?: string): boolean;
28
37
  /** Remove a directory only when it is empty. */
29
38
  rmdirIfEmpty(rel: string): void;
30
- /** Create or repoint a managed symlink. */
31
- symlink(rel: string, target: string, reason?: string): boolean;
32
- /** Remove a managed symlink, dangling or not. */
39
+ /** Remove a legacy managed symlink, dangling or not, without following it. */
33
40
  removeSymlink(rel: string, reason?: string): boolean;
34
41
  }
35
42
  export declare function makeFileOps(repo: string, dryRun: boolean, actions: FileAction[]): FileOps;
43
+ export {};
36
44
  //# sourceMappingURL=file-ops.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-ops.d.ts","sourceRoot":"","sources":["../../src/internal/file-ops.ts"],"names":[],"mappings":"AAcA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,CAAC;IAC/D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,OAAO;IACtB,4EAA4E;IAC5E,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACvE,iDAAiD;IACjD,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9C,4EAA4E;IAC5E,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3C,kDAAkD;IAClD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjD,gDAAgD;IAChD,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,2CAA2C;IAC3C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/D,iDAAiD;IACjD,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtD;AAMD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,OAAO,CAmFzF"}
1
+ {"version":3,"file":"file-ops.d.ts","sourceRoot":"","sources":["../../src/internal/file-ops.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,CAAC;IAC/D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,YAAY;IACpB,MAAM,IAAI,OAAO,CAAC;IAClB,WAAW,IAAI,OAAO,CAAC;IACvB,cAAc,IAAI,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,OAAO;IACtB,kEAAkE;IAClE,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;IAC7C,iEAAiE;IACjE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7B,4EAA4E;IAC5E,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACvE,iDAAiD;IACjD,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9C,4EAA4E;IAC5E,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3C,kDAAkD;IAClD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjD,gDAAgD;IAChD,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,8EAA8E;IAC9E,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtD;AAMD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,OAAO,CAqJzF"}
@@ -1,31 +1,115 @@
1
- import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, readlinkSync, rmdirSync, rmSync, symlinkSync, writeFileSync, } from "node:fs";
1
+ import { lstatSync, mkdirSync, readdirSync, readFileSync, rmdirSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  const abs = (repo, rel) => join(repo, ...rel.split("/"));
4
4
  const DEFAULT_REASON = "managed by enact-repo install";
5
5
  export function makeFileOps(repo, dryRun, actions) {
6
- return {
6
+ const removedRoots = new Set();
7
+ const virtualFiles = new Map();
8
+ const virtualDirs = new Set();
9
+ const underRemovedRoot = (rel) => [...removedRoots].some((root) => rel === root || rel.startsWith(`${root}/`));
10
+ const virtualLstat = (rel) => {
11
+ if (virtualFiles.has(rel)) {
12
+ return { isFile: () => true, isDirectory: () => false, isSymbolicLink: () => false };
13
+ }
14
+ if (virtualDirs.has(rel)) {
15
+ return { isFile: () => false, isDirectory: () => true, isSymbolicLink: () => false };
16
+ }
17
+ if (underRemovedRoot(rel))
18
+ return undefined;
19
+ return lstatSync(abs(repo, rel), { throwIfNoEntry: false });
20
+ };
21
+ const hasSymlinkAncestor = (rel) => {
22
+ const parts = rel.split("/").slice(0, -1);
23
+ for (let i = 1; i <= parts.length; i++) {
24
+ const st = virtualLstat(parts.slice(0, i).join("/"));
25
+ if (!st)
26
+ return false;
27
+ if (st.isSymbolicLink())
28
+ return true;
29
+ if (!st.isDirectory())
30
+ return false;
31
+ }
32
+ return false;
33
+ };
34
+ const markRemoved = (rel) => {
35
+ for (const path of virtualFiles.keys())
36
+ if (path === rel || path.startsWith(`${rel}/`))
37
+ virtualFiles.delete(path);
38
+ for (const path of virtualDirs)
39
+ if (path === rel || path.startsWith(`${rel}/`))
40
+ virtualDirs.delete(path);
41
+ removedRoots.add(rel);
42
+ };
43
+ const markWritten = (rel, content) => {
44
+ const parts = rel.split("/").slice(0, -1);
45
+ for (let i = 1; i <= parts.length; i++) {
46
+ const dirRel = parts.slice(0, i).join("/");
47
+ if (!virtualLstat(dirRel))
48
+ virtualDirs.add(dirRel);
49
+ }
50
+ virtualFiles.set(rel, content);
51
+ };
52
+ const virtualDirectoryIsEmpty = (rel) => {
53
+ const prefix = `${rel}/`;
54
+ if ([...virtualFiles.keys(), ...virtualDirs].some((path) => path.startsWith(prefix)))
55
+ return false;
56
+ if (underRemovedRoot(rel))
57
+ return true;
58
+ return readdirSync(abs(repo, rel)).every((name) => virtualLstat(`${rel}/${name}`) === undefined);
59
+ };
60
+ const fileAlreadyHasContent = (rel, existing, content) => {
61
+ if (!existing?.isFile() || existing.isSymbolicLink())
62
+ return false;
63
+ return (virtualFiles.get(rel) ?? readFileSync(abs(repo, rel))).equals(content);
64
+ };
65
+ const writeToDisk = (path, existing, content) => {
66
+ if (dryRun)
67
+ return;
68
+ if (existing?.isSymbolicLink())
69
+ rmSync(path, { force: true });
70
+ mkdirSync(dirname(path), { recursive: true });
71
+ writeFileSync(path, content);
72
+ };
73
+ const ops = {
74
+ lstat(rel) {
75
+ return virtualLstat(rel);
76
+ },
77
+ exists(rel) {
78
+ return virtualLstat(rel) !== undefined;
79
+ },
7
80
  write(rel, content, reason) {
8
81
  const path = abs(repo, rel);
9
82
  const buf = Buffer.isBuffer(content) ? content : Buffer.from(content);
10
- const existed = existsSync(path);
83
+ if (hasSymlinkAncestor(rel)) {
84
+ throw new Error(`Refusing to write managed file through a symlinked ancestor: ${rel}`);
85
+ }
86
+ const existing = virtualLstat(rel);
87
+ const existed = existing !== undefined;
11
88
  // Byte-identical is a no-op, so a re-install reports nothing and stays
12
- // idempotent rather than churning mtimes and dry-run output.
13
- if (existed && readFileSync(path).equals(buf))
89
+ // idempotent rather than churning mtimes and dry-run output. A symlink is
90
+ // never a managed file even when its target has identical bytes.
91
+ if (fileAlreadyHasContent(rel, existing, buf))
14
92
  return false;
15
93
  actions.push({ file: rel, action: existed ? "update" : "create", reason: reason ?? DEFAULT_REASON });
16
- if (!dryRun) {
17
- mkdirSync(dirname(path), { recursive: true });
18
- writeFileSync(path, buf);
19
- }
94
+ writeToDisk(path, existing, buf);
95
+ markWritten(rel, buf);
20
96
  return true;
21
97
  },
22
98
  remove(rel, reason) {
23
99
  const path = abs(repo, rel);
24
- if (!existsSync(path))
100
+ if (hasSymlinkAncestor(rel))
101
+ return false;
102
+ const existing = virtualLstat(rel);
103
+ if (!existing)
25
104
  return false;
26
105
  actions.push({ file: rel, action: "remove", reason: reason ?? DEFAULT_REASON });
27
- if (!dryRun)
28
- rmSync(path, { force: true });
106
+ if (!dryRun) {
107
+ if (existing.isSymbolicLink())
108
+ unlinkSync(path);
109
+ else
110
+ rmSync(path, { force: true });
111
+ }
112
+ markRemoved(rel);
29
113
  return true;
30
114
  },
31
115
  // Recorded rather than silently passed over: a retained file that appears
@@ -36,58 +120,47 @@ export function makeFileOps(repo, dryRun, actions) {
36
120
  },
37
121
  removeDir(rel, reason) {
38
122
  const path = abs(repo, rel);
39
- if (!existsSync(path))
123
+ if (hasSymlinkAncestor(rel))
124
+ return false;
125
+ const existing = virtualLstat(rel);
126
+ if (!existing)
40
127
  return false;
41
128
  actions.push({ file: rel, action: "remove-dir", reason: reason ?? "directory" });
42
- // macOS may create .DS_Store while Finder or a launched host observes a
43
- // root being rebuilt. Retry Node's documented transient recursive-delete
44
- // errors (including ENOTEMPTY) rather than leaving an owned root half
45
- // removed.
46
- if (!dryRun)
47
- rmSync(path, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
129
+ if (!dryRun) {
130
+ if (existing.isSymbolicLink())
131
+ unlinkSync(path);
132
+ else
133
+ rmSync(path, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
134
+ }
135
+ markRemoved(rel);
48
136
  return true;
49
137
  },
50
138
  rmdirIfEmpty(rel) {
51
139
  const path = abs(repo, rel);
52
- if (existsSync(path) && readdirSync(path).length === 0) {
140
+ if (hasSymlinkAncestor(rel))
141
+ return;
142
+ const existing = virtualLstat(rel);
143
+ if (existing?.isDirectory() && !existing.isSymbolicLink() && virtualDirectoryIsEmpty(rel)) {
53
144
  actions.push({ file: rel, action: "remove-dir", reason: "empty directory" });
54
- if (!dryRun)
145
+ if (!dryRun && lstatSync(path, { throwIfNoEntry: false })?.isDirectory())
55
146
  rmdirSync(path);
147
+ markRemoved(rel);
56
148
  }
57
149
  },
58
- // Symlinks need lstat, not existsSync: existsSync FOLLOWS a symlink, so it
59
- // reports false for a dangling one -- exactly the state a link can be in
60
- // mid-uninstall, once its target is already gone.
61
- symlink(rel, target, reason) {
62
- const path = abs(repo, rel);
63
- const st = lstatSync(path, { throwIfNoEntry: false });
64
- if (st?.isSymbolicLink() && readlinkSync(path) === target)
65
- return false;
66
- actions.push({
67
- file: rel,
68
- action: st ? "update" : "create",
69
- reason: reason ?? (st ? "replacing stale entry with managed symlink" : "managed symlink"),
70
- });
71
- if (!dryRun) {
72
- // Whatever is there (a stale real directory from a pre-symlink install,
73
- // a wrong-target link, ...) sits inside a path this installer
74
- // exclusively generates -- safe to replace outright.
75
- if (st)
76
- rmSync(path, { recursive: true, force: true });
77
- mkdirSync(dirname(path), { recursive: true });
78
- symlinkSync(target, path);
79
- }
80
- return true;
81
- },
82
150
  removeSymlink(rel, reason) {
83
151
  const path = abs(repo, rel);
84
- if (!lstatSync(path, { throwIfNoEntry: false }))
152
+ if (hasSymlinkAncestor(rel))
153
+ return false;
154
+ const existing = virtualLstat(rel);
155
+ if (!existing?.isSymbolicLink())
85
156
  return false;
86
157
  actions.push({ file: rel, action: "remove", reason: reason ?? "managed symlink" });
87
158
  if (!dryRun)
88
- rmSync(path, { recursive: true, force: true });
159
+ unlinkSync(path);
160
+ markRemoved(rel);
89
161
  return true;
90
162
  },
91
163
  };
164
+ return ops;
92
165
  }
93
166
  //# sourceMappingURL=file-ops.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-ops.js","sourceRoot":"","sources":["../../src/internal/file-ops.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"file-ops.js","sourceRoot":"","sources":["../../src/internal/file-ops.ts"],"names":[],"mappings":"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"}
@@ -4,20 +4,6 @@ import type { ProjectionPlatform } from "./internal/types.js";
4
4
  * every skill whose SKILL.md sets `disable-model-invocation: true`.
5
5
  */
6
6
  export declare function materializeCodexSkillMetadata(skillsSource: string, targetSkills: string): void;
7
- /**
8
- * Project the bundle into a self-contained Claude Code plugin root at
9
- * `targetRoot` (`.claude-plugin/plugin.json`, skills/, host-flagged
10
- * hooks/hooks.json, .mcp.json, README.md) WITHOUT registering any marketplace
11
- * or touching a Claude home. The derived manifest is computed in memory from
12
- * the canonical `.agents/plugin.json`; nothing is written back to the source.
13
- *
14
- * `options.symlinkSkills` (default false, preserving prior behavior for any
15
- * other caller): when true, the `skills` component is NOT copied into
16
- * `targetRoot` at all. This is for callers (the repo-local claude installer)
17
- * that place a relative symlink to a shared skills tree at that path
18
- * themselves — copying here would just be overwritten or leave stale
19
- * duplicate bytes behind.
20
- */
21
7
  /**
22
8
  * Project the bundle into a self-contained plugin root for `platform` at
23
9
  * `targetRoot` (manifest, skills/, hooks, mcp, README) WITHOUT registering any
@@ -31,16 +17,12 @@ export declare function materializeCodexSkillMetadata(skillsSource: string, targ
31
17
  * name safe. Hand-writing loose per-host files instead was what forced a flat,
32
18
  * un-namespaced shared skills pool in the first place.
33
19
  */
34
- export declare function projectPluginRoot(pluginRoot: string, targetRoot: string, platform: ProjectionPlatform, options?: {
35
- symlinkSkills?: boolean;
36
- }): {
20
+ export declare function projectPluginRoot(pluginRoot: string, targetRoot: string, platform: ProjectionPlatform): {
37
21
  name: string;
38
22
  version: string;
39
23
  };
40
24
  /** Claude-specific alias, kept because it names the common case at call sites. */
41
- export declare function projectClaudePluginRoot(pluginRoot: string, targetRoot: string, options?: {
42
- symlinkSkills?: boolean;
43
- }): {
25
+ export declare function projectClaudePluginRoot(pluginRoot: string, targetRoot: string): {
44
26
  name: string;
45
27
  version: string;
46
28
  };
@@ -1 +1 @@
1
- {"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAkB,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAqE9E;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAY9F;AAOD;;;;;;;;;;;;;GAaG;AACH;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAiDnC;AAED,kFAAkF;AAClF,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAEnC"}
1
+ {"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAkB,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAqE9E;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAY9F;AAOD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,kBAAkB,GAC3B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CA+CnC;AAED,kFAAkF;AAClF,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAEjH"}
@@ -97,20 +97,6 @@ function stringField(manifest, key) {
97
97
  const value = manifest[key];
98
98
  return typeof value === "string" && value.trim() ? value : undefined;
99
99
  }
100
- /**
101
- * Project the bundle into a self-contained Claude Code plugin root at
102
- * `targetRoot` (`.claude-plugin/plugin.json`, skills/, host-flagged
103
- * hooks/hooks.json, .mcp.json, README.md) WITHOUT registering any marketplace
104
- * or touching a Claude home. The derived manifest is computed in memory from
105
- * the canonical `.agents/plugin.json`; nothing is written back to the source.
106
- *
107
- * `options.symlinkSkills` (default false, preserving prior behavior for any
108
- * other caller): when true, the `skills` component is NOT copied into
109
- * `targetRoot` at all. This is for callers (the repo-local claude installer)
110
- * that place a relative symlink to a shared skills tree at that path
111
- * themselves — copying here would just be overwritten or leave stale
112
- * duplicate bytes behind.
113
- */
114
100
  /**
115
101
  * Project the bundle into a self-contained plugin root for `platform` at
116
102
  * `targetRoot` (manifest, skills/, hooks, mcp, README) WITHOUT registering any
@@ -124,7 +110,7 @@ function stringField(manifest, key) {
124
110
  * name safe. Hand-writing loose per-host files instead was what forced a flat,
125
111
  * un-namespaced shared skills pool in the first place.
126
112
  */
127
- export function projectPluginRoot(pluginRoot, targetRoot, platform, options = {}) {
113
+ export function projectPluginRoot(pluginRoot, targetRoot, platform) {
128
114
  const enact = readManifestFile(pluginRoot, "enact");
129
115
  const manifest = manifestForPlatform(platform, enact);
130
116
  const validation = validateManifest(platform, manifest);
@@ -138,7 +124,6 @@ export function projectPluginRoot(pluginRoot, targetRoot, platform, options = {}
138
124
  rmSync(targetRoot, { recursive: true, force: true });
139
125
  mkdirSync(targetRoot, { recursive: true });
140
126
  const agentsEntry = cleanRel(manifest.agents) ?? cleanRel(DEFAULT_PATHS.agents);
141
- const skillsEntry = cleanRel(manifest.skills) ?? cleanRel(DEFAULT_PATHS.skills);
142
127
  const hooksFileRel = cleanRel(manifest.hooks) ?? cleanRel(DEFAULT_PATHS.hooks) ?? "hooks/hooks.json";
143
128
  // Hosts disagree on the MCP config filename: Cursor reads `mcp.json`, the
144
129
  // rest read `.mcp.json`. Copying the canonical name verbatim left Cursor
@@ -147,8 +132,6 @@ export function projectPluginRoot(pluginRoot, targetRoot, platform, options = {}
147
132
  const mcpEntry = cleanRel(manifest.mcpServers) ?? cleanRel(DEFAULT_PATHS.mcpServers);
148
133
  const mcpTargetName = DEFAULT_MCP_FILENAME[platform];
149
134
  for (const entry of projectionEntries(manifest, platform)) {
150
- if (options.symlinkSkills && entry === skillsEntry)
151
- continue;
152
135
  const sourcePath = resolve(pluginRoot, entry);
153
136
  if (!existsSync(sourcePath))
154
137
  continue;
@@ -174,7 +157,7 @@ export function projectPluginRoot(pluginRoot, targetRoot, platform, options = {}
174
157
  return { name, version: stringField(manifest, "version") ?? "0.0.0" };
175
158
  }
176
159
  /** Claude-specific alias, kept because it names the common case at call sites. */
177
- export function projectClaudePluginRoot(pluginRoot, targetRoot, options = {}) {
178
- return projectPluginRoot(pluginRoot, targetRoot, "claude", options);
160
+ export function projectClaudePluginRoot(pluginRoot, targetRoot) {
161
+ return projectPluginRoot(pluginRoot, targetRoot, "claude");
179
162
  }
180
163
  //# sourceMappingURL=projection.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"projection.js","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"projection.js","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACpH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEzE,6EAA6E;AAC7E,4EAA4E;AAC5E,yEAAyE;AACzE,iCAAiC;AAEjC,SAAS,SAAS,CAAC,IAAY,EAAE,KAAc;IAC7C,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC/D,OAAO,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAChE,CAAC;AAED;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,QAAwB,EAAE,QAA4B;IAC/E,MAAM,SAAS,GAAG,2BAA2B,CAAC,QAAQ,CAAC,CAAC;IACxD,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAA6B,CAAC,CAAC;QACtD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE;YAAE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC;IACjC,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,KAAK,MAAM,KAAK,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,EAAE,CAAC;YAC7C,MAAM,KAAK,GAAI,KAAiC,CAAC,KAAK,CAAC,CAAC;YACxD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE;gBAAE,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,CAAU,EAAE,CAAC;QACzE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QACzC,IAAI,OAAO,QAAQ,CAAC,KAAK,CAAC,KAAK,QAAQ,IAAK,QAAQ,CAAC,KAAK,CAAY,CAAC,IAAI,EAAE;YAAE,SAAS;QACxF,MAAM,GAAG,GAAI,aAAwC,CAAC,KAAK,CAAC,CAAC;QAC7D,IAAI,GAAG;YAAE,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;IACzF,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,OAAO;YAAE,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,KAAK,MAAM,UAAU,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/D,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;YACrC,IAAI,OAAO,EAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;gBACrC,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;gBACnC,IAAI,SAAS,KAAK,GAAG;oBAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAChD,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,4BAA4B,CAAC,SAAiB;IACrD,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,wCAAwC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClE,OAAO,wCAAwC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACzE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,6BAA6B,CAAC,YAAoB,EAAE,YAAoB;IACtF,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE;QAAE,OAAO;IAC/E,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,YAAY,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACvE,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;YAAE,SAAS;QACnC,MAAM,eAAe,GAAG,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QACnE,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,MAAM,EAAE;YAAE,SAAS;QAClF,IAAI,CAAC,4BAA4B,CAAC,eAAe,CAAC;YAAE,SAAS;QAE7D,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;QAC7E,SAAS,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,aAAa,CAAC,YAAY,EAAE,+CAA+C,EAAE,MAAM,CAAC,CAAC;IACvF,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,QAAwB,EAAE,GAAW;IACxD,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvE,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,iBAAiB,CAC/B,UAAkB,EAClB,UAAkB,EAClB,QAA4B;IAE5B,MAAM,KAAK,GAAG,gBAAgB,CAAC,UAAU,EAAE,OAAO,CAAmB,CAAC;IACtE,MAAM,QAAQ,GAAG,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACtD,MAAM,UAAU,GAAG,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACxD,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,WAAW,QAAQ,sBAAsB,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3F,CAAC;IACD,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC3C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,kCAAkC,CAAC,CAAC;IACjE,CAAC;IAED,MAAM,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAChF,MAAM,YAAY,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,kBAAkB,CAAC;IACrG,0EAA0E;IAC1E,yEAAyE;IACzE,6EAA6E;IAC7E,wDAAwD;IACxD,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IACrF,MAAM,aAAa,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IACrD,KAAK,MAAM,KAAK,IAAI,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;QAC1D,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAC9C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;YAAE,SAAS;QACtC,IAAI,KAAK,KAAK,QAAQ,IAAI,aAAa,IAAI,aAAa,KAAK,KAAK,EAAE,CAAC;YACnE,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACtF,SAAS;QACX,CAAC;QACD,uEAAuE;QACvE,IAAI,KAAK,KAAK,WAAW,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YAChE,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC;YAChE,SAAS;QACX,CAAC;QACD,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,4EAA4E;IAC5E,uEAAuE;IACvE,MAAM,WAAW,GAAG,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IACtD,IAAI,UAAU,CAAC,WAAW,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QACpE,yBAAyB,CAAC,WAAW,EAAE,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,QAAQ,CAAC,CAAC;IACnF,CAAC;IAED,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC/D,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC3D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,OAAO,EAAE,CAAC;AACxE,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,uBAAuB,CAAC,UAAkB,EAAE,UAAkB;IAC5E,OAAO,iBAAiB,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;AAC7D,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amsterdamdatalabs/enact-extensions",
3
- "version": "0.1.61",
3
+ "version": "0.1.62",
4
4
  "description": "Create and validate Enact multi-platform plugin manifests",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -47,7 +47,7 @@
47
47
 
48
48
  import { spawnSync } from "node:child_process";
49
49
  import { createHash } from "node:crypto";
50
- import { existsSync, lstatSync, readdirSync, readFileSync, readlinkSync } from "node:fs";
50
+ import { existsSync, lstatSync, readdirSync, readFileSync } from "node:fs";
51
51
  import { homedir } from "node:os";
52
52
  import { basename, join } from "node:path";
53
53
  import * as TOML from "@iarna/toml";
@@ -301,43 +301,57 @@ function runHookDoctor(repo, enactHook) {
301
301
  // lock integrity — every canonical managed file present + hash match, plus
302
302
  // no foreign content inside an owned root.
303
303
  // ---------------------------------------------------------------------------
304
+ function firstSymlinkComponent(repo, rel) {
305
+ const parts = rel.split("/");
306
+ for (let i = 1; i <= parts.length; i++) {
307
+ const component = parts.slice(0, i).join("/");
308
+ const st = lstatSync(abs(repo, component), { throwIfNoEntry: false });
309
+ if (!st) return null;
310
+ if (st.isSymbolicLink()) return component;
311
+ }
312
+ return null;
313
+ }
314
+
315
+ function managedSymlinkIssue(repo, rel) {
316
+ const component = firstSymlinkComponent(repo, rel);
317
+ if (!component) return null;
318
+ return component === rel
319
+ ? `${rel} (managed path is a symlink)`
320
+ : `${rel} (managed path traverses symlink ${component})`;
321
+ }
322
+
304
323
  function checkLockIntegrity(repo, name, lock) {
305
324
  const missingFiles = [];
306
325
  const driftedFiles = [];
326
+ const symlinkIssues = [];
327
+ const recordSymlinkIssue = (rel) => {
328
+ const issue = managedSymlinkIssue(repo, rel);
329
+ if (issue) symlinkIssues.push(issue);
330
+ return issue !== null;
331
+ };
332
+ recordSymlinkIssue(lockRel(name));
307
333
  for (const [rel, { sha256: want }] of Object.entries(lock.files)) {
334
+ if (recordSymlinkIssue(rel)) continue;
308
335
  if (!existsSync(abs(repo, rel))) missingFiles.push(rel);
309
336
  else if (fileSha(repo, rel) !== want) driftedFiles.push(rel);
310
337
  }
311
338
  const tomlIssues = [];
312
339
  for (const [rel, record] of Object.entries(lock.toml)) {
340
+ if (recordSymlinkIssue(rel)) continue;
313
341
  const path = abs(repo, rel);
314
342
  const found = existsSync(path) ? findBlock(readFileSync(path, "utf8"), name) : null;
315
343
  if (!found || sha256(found.block) !== record.sha256) tomlIssues.push(rel);
316
344
  }
317
- // Managed symlinks carry no content hash, so `lock.files`-style verification
318
- // does not reach them. Verify by the link's own mechanism: it must BE a
319
- // symlink, point where the lock says, and actually resolve.
320
- //
321
- // lstatSync, never existsSync: existsSync follows the link and returns false
322
- // for a dangling one, so it cannot tell "absent" from "present but broken".
323
- const symlinkIssues = [];
324
- for (const [rel, record] of Object.entries(lock.symlinks ?? {})) {
325
- const path = abs(repo, rel);
326
- const st = lstatSync(path, { throwIfNoEntry: false });
327
- if (!st) {
328
- symlinkIssues.push(`${rel} (missing)`);
329
- continue;
330
- }
331
- if (!st.isSymbolicLink()) {
332
- symlinkIssues.push(`${rel} (not a symlink)`);
333
- continue;
334
- }
335
- const target = readlinkSync(path);
336
- if (target !== record.target) {
337
- symlinkIssues.push(`${rel} (target is ${target}, expected ${record.target})`);
338
- continue;
339
- }
340
- if (!existsSync(path)) symlinkIssues.push(`${rel} (dangling -> ${target})`);
345
+ for (const rel of Object.keys(lock.gitignore ?? {})) recordSymlinkIssue(rel);
346
+ if (lock.config) recordSymlinkIssue(CONFIG_PATH);
347
+ for (const record of Object.values(lock.displaced?.files ?? {})) {
348
+ if (typeof record?.storedAt === "string") recordSymlinkIssue(record.storedAt);
349
+ }
350
+ const legacySymlinks = Object.keys(lock.symlinks ?? {}).sort();
351
+ if (legacySymlinks.length > 0) {
352
+ symlinkIssues.push(
353
+ `legacy lock records managed symlinks; re-run install to replace them with real files: ${legacySymlinks.join(", ")}`,
354
+ );
341
355
  }
342
356
  const rootIssues = [];
343
357
  for (const root of lock.ownedRoots ?? []) {
@@ -407,7 +421,6 @@ function checkNativeHostPresence(repo, lock, host) {
407
421
  ...Object.keys(lock.files ?? {}),
408
422
  ...Object.keys(lock.merged ?? {}),
409
423
  ...Object.keys(lock.toml ?? {}),
410
- ...Object.keys(lock.symlinks ?? {}),
411
424
  ].filter((rel) => isNativeHostPath(host, rel));
412
425
  const nativePaths = [...new Set(rels)].sort();
413
426
  return {
@@ -585,7 +585,6 @@ export function buildPlan(pluginRoot, bundle, hosts, repo) {
585
585
  json: new Map(),
586
586
  toml: new Map(),
587
587
  gitignore: new Map(),
588
- symlinks: new Map(),
589
588
  skills: new Set(),
590
589
  };
591
590
  plan.gitignore.set(GITIGNORE_PATH, GITIGNORE_LINES);
@@ -623,6 +622,37 @@ function assignJsonPath(doc, path, value) {
623
622
  at[path.at(-1)] = jsonValue(value);
624
623
  }
625
624
 
625
+ function renderRootJson(value, indent = 0, prefixWidth = 0) {
626
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
627
+ if (Array.isArray(value)) {
628
+ if (value.length === 0) return "[]";
629
+ const compact = `[${value.map((entry) => JSON.stringify(entry)).join(", ")}]`;
630
+ if (value.every((entry) => entry === null || typeof entry !== "object") && prefixWidth + compact.length <= 120) {
631
+ return compact;
632
+ }
633
+ const padding = " ".repeat(indent);
634
+ const childPadding = " ".repeat(indent + 2);
635
+ return `[\n${value
636
+ .map((entry, index) => {
637
+ const suffix = index === value.length - 1 ? "" : ",";
638
+ return `${childPadding}${renderRootJson(entry, indent + 2)}${suffix}`;
639
+ })
640
+ .join("\n")}\n${padding}]`;
641
+ }
642
+
643
+ const entries = Object.entries(value);
644
+ if (entries.length === 0) return "{}";
645
+ const padding = " ".repeat(indent);
646
+ const childPadding = " ".repeat(indent + 2);
647
+ return `{\n${entries
648
+ .map(([key, entry], index) => {
649
+ const prefix = `${childPadding}${JSON.stringify(key)}: `;
650
+ const suffix = index === entries.length - 1 ? "" : ",";
651
+ return `${prefix}${renderRootJson(entry, indent + 2, prefix.length)}${suffix}`;
652
+ })
653
+ .join("\n")}\n${padding}}`;
654
+ }
655
+
626
656
  /** Build the exact native files from the bundle, without reading a repo file. */
627
657
  function materializeAuthoritativeFiles(plan, bundle) {
628
658
  const files = new Map(plan.files);
@@ -640,7 +670,9 @@ function materializeAuthoritativeFiles(plan, bundle) {
640
670
  if (!Array.isArray(at[key])) at[key] = [];
641
671
  at[key].push(jsonValue(entry.value));
642
672
  }
643
- files.set(rel, Buffer.from(`${JSON.stringify(doc, null, 2)}\n`));
673
+ const rendered =
674
+ rel === ".mcp.json" || rel === "opencode.json" ? renderRootJson(doc) : JSON.stringify(doc, null, 2);
675
+ files.set(rel, Buffer.from(`${rendered}\n`));
644
676
  }
645
677
  for (const [rel, toml] of plan.toml) files.set(rel, Buffer.from(renderTomlBlock(bundle.name, toml.tables)));
646
678
  for (const [rel, lines] of plan.gitignore) files.set(rel, Buffer.from(`${lines.join("\n")}\n`));
@@ -659,7 +691,7 @@ function isBoundaryOrAncestor(rel, boundaries) {
659
691
 
660
692
  function clearOwnedRoot(repo, root, boundaries, ops) {
661
693
  const rootPath = abs(repo, root);
662
- const rootState = lstatSync(rootPath, { throwIfNoEntry: false });
694
+ const rootState = ops.lstat(root);
663
695
  if (!rootState) return;
664
696
  if (!rootState.isDirectory() || rootState.isSymbolicLink()) {
665
697
  ops.removeSymlink(root, "replacing non-directory owned root");
@@ -671,7 +703,7 @@ function clearOwnedRoot(repo, root, boundaries, ops) {
671
703
  }
672
704
  const visit = (rel) => {
673
705
  const path = abs(repo, rel);
674
- const state = lstatSync(path, { throwIfNoEntry: false });
706
+ const state = ops.lstat(rel);
675
707
  if (!state) return;
676
708
  // A boundary is entirely outside this deployment. Do not descend into it:
677
709
  // logs/caches are owned by their runtime, not by this installer.
@@ -719,6 +751,15 @@ function runAuthoritativeRepoInstall(pluginRoot, options, context) {
719
751
  );
720
752
  }
721
753
 
754
+ // Current plans cannot create links. Remove every exact link recorded by a
755
+ // legacy lock before rebuilding roots, first cutting any linked ancestor so
756
+ // a descendant record can never escape the repository. FileOps carries this
757
+ // state virtually during --dry-run, keeping its plan identical to a real run.
758
+ for (const rel of Object.keys(prev.symlinks ?? {})) {
759
+ replaceSymlinkAncestors(rel, ops);
760
+ ops.removeSymlink(rel, "legacy managed symlink hard cut");
761
+ }
762
+
722
763
  // A reconciliation owns the complete declared component surface, not just
723
764
  // the hosts selected for this invocation. Otherwise a previous host's
724
765
  // root would become an accidental preservation zone when the host list is
@@ -739,7 +780,7 @@ function runAuthoritativeRepoInstall(pluginRoot, options, context) {
739
780
 
740
781
  const files = {};
741
782
  for (const [rel, content] of desired) {
742
- replaceSymlinkAncestors(repo, rel, ops);
783
+ replaceSymlinkAncestors(rel, ops);
743
784
  ops.write(rel, content, "authoritative bundle projection");
744
785
  files[rel] = { sha256: sha256(content) };
745
786
  }
@@ -767,7 +808,6 @@ function runAuthoritativeRepoInstall(pluginRoot, options, context) {
767
808
  version: bundle.version,
768
809
  hosts,
769
810
  files: sortObject(files),
770
- symlinks: {},
771
811
  toml: {},
772
812
  gitignore: {},
773
813
  config,
@@ -848,12 +888,16 @@ export const fileSha = (repo, rel) => sha256(readFileSync(abs(repo, rel)));
848
888
  *
849
889
  * Scoped to ancestors strictly inside the repo, and only those that are links.
850
890
  */
851
- function replaceSymlinkAncestors(repo, rel, ops) {
891
+ function replaceSymlinkAncestors(rel, ops) {
852
892
  const parts = rel.split("/").slice(0, -1);
853
893
  for (let i = 1; i <= parts.length; i++) {
854
894
  const dirRel = parts.slice(0, i).join("/");
855
- const st = lstatSync(abs(repo, dirRel), { throwIfNoEntry: false });
856
- if (st?.isSymbolicLink()) ops.removeSymlink(dirRel, "replacing a symlinked directory with real content");
895
+ const st = ops.lstat(dirRel);
896
+ if (st?.isSymbolicLink()) {
897
+ ops.removeSymlink(dirRel, "replacing a symlinked directory with real content");
898
+ return;
899
+ }
900
+ if (!st || !st.isDirectory()) return;
857
901
  }
858
902
  }
859
903