@pieai/pro-gov 0.9.2 → 0.9.4

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 (4) hide show
  1. package/README.md +11 -0
  2. package/cli-guide.md +13 -0
  3. package/dist/cli.js +1688 -590
  4. package/package.json +3 -3
package/dist/cli.js CHANGED
@@ -43,8 +43,8 @@ function isPlatformMetadata(name) {
43
43
  }
44
44
 
45
45
  // src/commands/assets.ts
46
- import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
47
- import { dirname as dirname5, join as join11 } from "node:path";
46
+ import { existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "node:fs";
47
+ import { dirname as dirname6, join as join12 } from "node:path";
48
48
 
49
49
  // src/asset-bundles/bundles.ts
50
50
  import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync } from "node:fs";
@@ -58,27 +58,1001 @@ function loadAgentAssetBundles(agentAssetsDir) {
58
58
  }).sort((a, b) => a.id.localeCompare(b.id));
59
59
  }
60
60
 
61
- // src/asset-npx/maintenance.ts
61
+ // src/asset-catalog/catalog.ts
62
62
  import { createHash } from "node:crypto";
63
- import { spawnSync } from "node:child_process";
64
63
  import {
65
- cpSync,
66
64
  existsSync as existsSync3,
65
+ lstatSync,
67
66
  mkdirSync,
68
67
  mkdtempSync,
69
- readdirSync as readdirSync3,
70
68
  readFileSync as readFileSync2,
71
- statSync
69
+ readdirSync as readdirSync3,
70
+ renameSync,
71
+ rmSync,
72
+ statSync,
73
+ symlinkSync,
74
+ writeFileSync
72
75
  } from "node:fs";
73
- import { join as join3, relative as relative2 } from "node:path";
76
+ import { basename, dirname as dirname2, extname, isAbsolute, join as join3, relative as relative2, resolve } from "node:path";
77
+ var portableManifestName = ".asset-catalog-manifest.json";
78
+ var localLinksManifestName = ".asset-catalog-local-links.json";
79
+ var legacyManagedSentinel = ".managed-by-pro-gov-asset-catalog";
80
+ var legacyManagedDirectoryNames = ["generated", "native-links"];
81
+ var manifestGenerator = "@pieai/pro-gov asset-catalog";
82
+ var ignoredTraversalNames = /* @__PURE__ */ new Set([
83
+ ".DS_Store",
84
+ "Thumbs.db",
85
+ "__pycache__",
86
+ ".cache",
87
+ ".pytest_cache",
88
+ ".mypy_cache",
89
+ "node_modules",
90
+ "dist",
91
+ "build"
92
+ ]);
93
+ var selectorFields = /* @__PURE__ */ new Set([
94
+ "id",
95
+ "title",
96
+ "family",
97
+ "kind",
98
+ "domain",
99
+ "tag",
100
+ "visibility",
101
+ "origin",
102
+ "sourcePath"
103
+ ]);
104
+ var slugPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
105
+ function loadAssetCatalogConfig(catalogRoot) {
106
+ const configPath = join3(catalogRoot, "catalog.config.json");
107
+ if (!existsSync3(configPath)) {
108
+ throw new Error(`Missing asset catalog config: ${configPath}`);
109
+ }
110
+ const config = JSON.parse(readFileSync2(configPath, "utf8"));
111
+ const issues = validateAssetCatalogConfig(config);
112
+ if (issues.length > 0) {
113
+ throw new Error(
114
+ `Invalid asset catalog config:
115
+ ${issues.map((issue) => `- ${issue}`).join("\n")}`
116
+ );
117
+ }
118
+ return config;
119
+ }
120
+ function buildAssetCatalog(options) {
121
+ const snapshot = createAssetCatalogSnapshot(options);
122
+ const warnings = [...writePortableCatalog(options.catalogRoot, snapshot.files)];
123
+ let nativeLinksRoot;
124
+ let nativeLinkCount = 0;
125
+ if (options.nativeLinks) {
126
+ nativeLinksRoot = join3(options.catalogRoot, "by-topic");
127
+ const nativeResult = writeNativeLinks({
128
+ agentAssetsDir: options.agentAssetsDir,
129
+ catalogRoot: options.catalogRoot,
130
+ entries: snapshot.entries,
131
+ platform: options.platform ?? process.platform
132
+ });
133
+ nativeLinkCount = nativeResult.linkCount;
134
+ warnings.push(...nativeResult.warnings);
135
+ }
136
+ return {
137
+ catalogRoot: options.catalogRoot,
138
+ nativeLinksRoot,
139
+ assetCount: snapshot.document.assetCount,
140
+ topicCount: snapshot.document.topicCount,
141
+ portableFileCount: snapshot.files.size + 1,
142
+ nativeLinkCount,
143
+ warnings
144
+ };
145
+ }
146
+ function checkAssetCatalog(options) {
147
+ const snapshot = createAssetCatalogSnapshot(options);
148
+ const expected = new Map(snapshot.files);
149
+ expected.set(portableManifestName, renderPortableManifest(snapshot.files));
150
+ const issues = [];
151
+ for (const [relativePath, expectedContent] of expected) {
152
+ const targetPath = join3(options.catalogRoot, ...relativePath.split("/"));
153
+ if (!existsSync3(targetPath)) {
154
+ issues.push(`missing generated file: ${relativePath}`);
155
+ continue;
156
+ }
157
+ const stats = lstatSync(targetPath);
158
+ if (!stats.isFile() || stats.isSymbolicLink()) {
159
+ issues.push(`generated path is not a regular file: ${relativePath}`);
160
+ } else if (readFileSync2(targetPath, "utf8") !== expectedContent) {
161
+ issues.push(`stale generated file: ${relativePath}`);
162
+ }
163
+ }
164
+ try {
165
+ const manifest = readPortableManifest(options.catalogRoot);
166
+ if (manifest) {
167
+ for (const relativePath of manifest.portableFiles) {
168
+ if (!snapshot.files.has(relativePath)) {
169
+ issues.push(`unexpected managed file: ${relativePath}`);
170
+ }
171
+ }
172
+ }
173
+ } catch (error) {
174
+ issues.push(error instanceof Error ? error.message : String(error));
175
+ }
176
+ for (const name of legacyManagedDirectoryNames) {
177
+ if (existsSync3(join3(options.catalogRoot, name))) {
178
+ issues.push(`legacy catalog directory remains: ${name}`);
179
+ }
180
+ }
181
+ return {
182
+ catalogRoot: options.catalogRoot,
183
+ assetCount: snapshot.document.assetCount,
184
+ issues: issues.sort()
185
+ };
186
+ }
187
+ function nativeLinkStrategy(platform, sourceType) {
188
+ if (platform !== "win32") return "relative-symlink";
189
+ return sourceType === "directory" ? "junction" : "file-symlink";
190
+ }
191
+ function createAssetCatalogSnapshot(options) {
192
+ const config = loadAssetCatalogConfig(options.catalogRoot);
193
+ const agentAssetsDir = resolve(options.agentAssetsDir);
194
+ const registryRaw = readFileSync2(join3(agentAssetsDir, "registry.json"), "utf8");
195
+ const configRaw = readFileSync2(join3(options.catalogRoot, "catalog.config.json"), "utf8");
196
+ const topicIds = new Set(config.topics.map((topic) => topic.id));
197
+ const bundleMembership = createBundleMembership(options.bundles);
198
+ const entries = [
199
+ ...options.registry.assets.map(
200
+ (asset) => createRegisteredEntry(asset, agentAssetsDir, config, topicIds, bundleMembership)
201
+ ),
202
+ ...config.extraEntries.map(
203
+ (asset) => createExtraEntry(asset, agentAssetsDir, topicIds, bundleMembership)
204
+ )
205
+ ].sort(compareCatalogEntries);
206
+ assertUniqueEntries(entries);
207
+ const uncategorized = entries.filter((entry) => entry.topics.length === 0);
208
+ if (uncategorized.length > 0) {
209
+ throw new Error(
210
+ `Uncategorized catalog assets:
211
+ ${uncategorized.map((entry) => `- ${entry.id}`).join("\n")}`
212
+ );
213
+ }
214
+ const uncovered = findUncoveredSourcePaths(
215
+ agentAssetsDir,
216
+ entries,
217
+ config.ignoredSourcePaths ?? []
218
+ );
219
+ if (uncovered.length > 0) {
220
+ throw new Error(
221
+ `Agent asset sources are not represented in the catalog:
222
+ ${uncovered.map((path) => `- ${path}`).join("\n")}`
223
+ );
224
+ }
225
+ const usedBundleIds = new Set(entries.flatMap((entry) => entry.bundles));
226
+ const document = {
227
+ schemaVersion: 1,
228
+ title: config.title,
229
+ description: config.description,
230
+ registryAssetCount: options.registry.assets.length,
231
+ extraEntryCount: config.extraEntries.length,
232
+ assetCount: entries.length,
233
+ topicCount: config.topics.length,
234
+ bundleCount: usedBundleIds.size,
235
+ registryDigest: sha256(registryRaw),
236
+ configDigest: sha256(configRaw),
237
+ topics: config.topics.map((topic) => ({
238
+ id: topic.id,
239
+ title: topic.title,
240
+ description: topic.description,
241
+ assetCount: entries.filter((entry) => entry.topics.includes(topic.id)).length
242
+ })),
243
+ entries
244
+ };
245
+ return {
246
+ document,
247
+ entries,
248
+ files: renderPortableCatalog(document, config, options.catalogRoot, agentAssetsDir)
249
+ };
250
+ }
251
+ function createRegisteredEntry(asset, agentAssetsDir, config, topicIds, bundleMembership) {
252
+ const sourceAbsolutePath = resolveSafeSourcePath(agentAssetsDir, asset.sourcePath, asset.id);
253
+ const sourceType = statSync(sourceAbsolutePath).isDirectory() ? "directory" : "file";
254
+ const draft = {
255
+ id: asset.id,
256
+ title: asset.title,
257
+ description: readAssetDescription(sourceAbsolutePath, sourceType, asset.title),
258
+ family: asset.family,
259
+ kind: asset.kind,
260
+ domain: asset.domain,
261
+ visibility: asset.visibility,
262
+ sourceKind: asset.sourceKind,
263
+ sourcePath: normalizeRelativePath(asset.sourcePath),
264
+ sourceType,
265
+ tags: [...asset.tags],
266
+ bundles: [...bundleMembership.get(asset.id) ?? []],
267
+ origin: asset.origin,
268
+ registered: true
269
+ };
270
+ const topics = config.topics.filter((topic) => matchesTopic(draft, topic)).map((topic) => topic.id);
271
+ assertKnownTopics(asset.id, topics, topicIds);
272
+ return { ...draft, topics };
273
+ }
274
+ function createExtraEntry(asset, agentAssetsDir, topicIds, bundleMembership) {
275
+ const sourceAbsolutePath = resolveSafeSourcePath(agentAssetsDir, asset.sourcePath, asset.id);
276
+ const sourceType = statSync(sourceAbsolutePath).isDirectory() ? "directory" : "file";
277
+ assertKnownTopics(asset.id, asset.topics, topicIds);
278
+ return {
279
+ id: asset.id,
280
+ title: asset.title,
281
+ description: asset.description,
282
+ family: asset.family,
283
+ kind: asset.kind,
284
+ visibility: "private",
285
+ sourceKind: "local",
286
+ sourcePath: normalizeRelativePath(asset.sourcePath),
287
+ sourceType,
288
+ tags: [...asset.tags],
289
+ topics: [...new Set(asset.topics)].sort(),
290
+ bundles: [...bundleMembership.get(asset.id) ?? []],
291
+ origin: asset.origin,
292
+ registered: false
293
+ };
294
+ }
295
+ function validateAssetCatalogConfig(config) {
296
+ const issues = [];
297
+ if (config.schemaVersion !== 1) issues.push("schemaVersion must be 1");
298
+ if (!config.title?.trim()) issues.push("title is required");
299
+ if (!config.description?.trim()) issues.push("description is required");
300
+ if (!Array.isArray(config.topics) || config.topics.length === 0) {
301
+ issues.push("topics must be a non-empty array");
302
+ }
303
+ if (!Array.isArray(config.extraEntries)) issues.push("extraEntries must be an array");
304
+ const topicIds = /* @__PURE__ */ new Set();
305
+ for (const topic of config.topics ?? []) {
306
+ if (!slugPattern.test(topic.id)) issues.push(`invalid topic id: ${topic.id}`);
307
+ if (topicIds.has(topic.id)) issues.push(`duplicate topic id: ${topic.id}`);
308
+ topicIds.add(topic.id);
309
+ if (!topic.title?.trim()) issues.push(`topic ${topic.id} is missing title`);
310
+ if (!topic.description?.trim()) issues.push(`topic ${topic.id} is missing description`);
311
+ if (!Array.isArray(topic.selectors))
312
+ issues.push(`topic ${topic.id} selectors must be an array`);
313
+ for (const selector of [...topic.selectors ?? [], ...topic.excludeSelectors ?? []]) {
314
+ const separator = selector.indexOf(":");
315
+ const field = separator > 0 ? selector.slice(0, separator) : "";
316
+ const pattern = separator > 0 ? selector.slice(separator + 1) : "";
317
+ if (!selectorFields.has(field) || !pattern) {
318
+ issues.push(`invalid selector in ${topic.id}: ${selector}`);
319
+ }
320
+ }
321
+ }
322
+ const extraIds = /* @__PURE__ */ new Set();
323
+ for (const entry of config.extraEntries ?? []) {
324
+ if (!entry.id?.includes("/")) issues.push(`invalid extra entry id: ${entry.id}`);
325
+ if (extraIds.has(entry.id)) issues.push(`duplicate extra entry id: ${entry.id}`);
326
+ extraIds.add(entry.id);
327
+ if (!entry.title?.trim()) issues.push(`extra entry ${entry.id} is missing title`);
328
+ if (!entry.family?.trim()) issues.push(`extra entry ${entry.id} is missing family`);
329
+ if (!entry.kind?.trim()) issues.push(`extra entry ${entry.id} is missing kind`);
330
+ if (!entry.description?.trim()) issues.push(`extra entry ${entry.id} is missing description`);
331
+ if (!Array.isArray(entry.tags)) issues.push(`extra entry ${entry.id} tags must be an array`);
332
+ if (!Array.isArray(entry.topics) || entry.topics.length === 0) {
333
+ issues.push(`extra entry ${entry.id} must name at least one topic`);
334
+ }
335
+ for (const topic of entry.topics ?? []) {
336
+ if (!topicIds.has(topic)) issues.push(`extra entry ${entry.id} has unknown topic: ${topic}`);
337
+ }
338
+ if (!isSafeSourcePath(entry.sourcePath)) {
339
+ issues.push(`extra entry ${entry.id} has unsafe sourcePath: ${entry.sourcePath}`);
340
+ }
341
+ }
342
+ for (const path of config.ignoredSourcePaths ?? []) {
343
+ if (!isSafeSourcePath(path)) issues.push(`unsafe ignoredSourcePath: ${path}`);
344
+ }
345
+ return issues;
346
+ }
347
+ function matchesTopic(entry, topic) {
348
+ if (topic.excludeSelectors?.some((selector) => matchesSelector(entry, selector))) return false;
349
+ return topic.selectors.some((selector) => matchesSelector(entry, selector));
350
+ }
351
+ function matchesSelector(entry, selector) {
352
+ const separator = selector.indexOf(":");
353
+ const field = selector.slice(0, separator);
354
+ const pattern = selector.slice(separator + 1);
355
+ const values = field === "tag" ? entry.tags : [String(entry[field] ?? "")];
356
+ return values.some((value) => wildcardMatches(value, pattern));
357
+ }
358
+ function wildcardMatches(value, pattern) {
359
+ const expression = pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*");
360
+ return new RegExp(`^${expression}$`, "iu").test(value);
361
+ }
362
+ function renderPortableCatalog(document, config, catalogRoot, agentAssetsDir) {
363
+ const files = /* @__PURE__ */ new Map();
364
+ files.set("catalog.json", `${JSON.stringify(document, null, 2)}
365
+ `);
366
+ files.set("INDEX.md", renderMainIndex(document));
367
+ files.set("index.html", renderHtmlCatalog(document, catalogRoot, agentAssetsDir));
368
+ for (const topic of config.topics) {
369
+ const entries = document.entries.filter((entry) => entry.topics.includes(topic.id));
370
+ const indexPath = join3(catalogRoot, "by-topic", topic.id, "README.md");
371
+ files.set(
372
+ `by-topic/${topic.id}/README.md`,
373
+ renderGroupIndex(topic.title, topic.description, entries, indexPath, agentAssetsDir)
374
+ );
375
+ }
376
+ return files;
377
+ }
378
+ function renderHtmlCatalog(document, catalogRoot, agentAssetsDir) {
379
+ const viewEntries = document.entries.map((entry) => ({
380
+ ...entry,
381
+ sourceHref: encodeRelativeHref(
382
+ relative2(catalogRoot, join3(agentAssetsDir, ...entry.sourcePath.split("/")))
383
+ )
384
+ }));
385
+ const data = JSON.stringify({ ...document, entries: viewEntries }).replaceAll("<", "\\u003c");
386
+ return `<!doctype html>
387
+ <html lang="zh-CN">
388
+ <head>
389
+ <meta charset="utf-8">
390
+ <meta name="viewport" content="width=device-width, initial-scale=1">
391
+ <title>${escapeHtml(document.title)}</title>
392
+ <style>
393
+ :root { color-scheme: light; --ink:#17202a; --muted:#607080; --line:#dbe3ea; --paper:#fff; --wash:#f4f7fa; --accent:#1769aa; --chip:#eaf3fb; }
394
+ * { box-sizing:border-box; }
395
+ body { margin:0; background:var(--wash); color:var(--ink); font:15px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif; }
396
+ header { padding:38px clamp(20px,5vw,72px) 28px; color:#fff; background:linear-gradient(125deg,#10283d,#1769aa 70%,#1e8b8a); }
397
+ header h1 { margin:0 0 8px; font-size:clamp(28px,4vw,44px); letter-spacing:-.03em; }
398
+ header p { max-width:800px; margin:0; opacity:.88; }
399
+ main { max-width:1440px; margin:auto; padding:24px clamp(16px,4vw,56px) 60px; }
400
+ .toolbar { position:sticky; top:0; z-index:2; display:grid; grid-template-columns:minmax(240px,2fr) repeat(3,minmax(150px,1fr)); gap:10px; padding:14px 0; background:color-mix(in srgb,var(--wash) 92%,transparent); backdrop-filter:blur(12px); }
401
+ input,select { width:100%; border:1px solid var(--line); border-radius:10px; background:var(--paper); padding:11px 12px; color:var(--ink); font:inherit; }
402
+ .summary { display:flex; justify-content:space-between; gap:16px; align-items:center; margin:10px 0 18px; color:var(--muted); }
403
+ .grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(310px,1fr)); gap:14px; }
404
+ article { min-width:0; padding:18px; border:1px solid var(--line); border-radius:14px; background:var(--paper); box-shadow:0 4px 18px rgba(28,55,75,.05); }
405
+ article h2 { margin:0 0 5px; font-size:18px; line-height:1.3; }
406
+ article p { min-height:3.1em; margin:8px 0 12px; color:#405160; }
407
+ .meta { color:var(--muted); font-size:13px; overflow-wrap:anywhere; }
408
+ .chips { display:flex; flex-wrap:wrap; gap:6px; margin:12px 0; }
409
+ .chip { padding:3px 8px; border-radius:999px; background:var(--chip); color:#155582; font-size:12px; }
410
+ .actions { display:flex; gap:12px; margin-top:13px; }
411
+ a { color:var(--accent); text-decoration:none; font-weight:600; }
412
+ a:hover { text-decoration:underline; }
413
+ .empty { padding:44px; text-align:center; color:var(--muted); border:1px dashed var(--line); border-radius:14px; background:var(--paper); }
414
+ @media (max-width:850px) { .toolbar { grid-template-columns:1fr 1fr; } }
415
+ @media (max-width:540px) { .toolbar { position:static; grid-template-columns:1fr; } }
416
+ </style>
417
+ </head>
418
+ <body>
419
+ <header>
420
+ <h1>${escapeHtml(document.title)}</h1>
421
+ <p>${escapeHtml(document.description)} \u5F53\u524D\u5171 ${document.assetCount} \u9879\uFF1B\u540C\u4E00\u8D44\u4EA7\u53EF\u4EE5\u5C5E\u4E8E\u591A\u4E2A\u4E3B\u9898\u3002</p>
422
+ </header>
423
+ <main>
424
+ <section class="toolbar" aria-label="\u76EE\u5F55\u7B5B\u9009">
425
+ <input id="query" type="search" placeholder="\u641C\u7D22\u540D\u79F0\u3001\u7528\u9014\u3001\u6807\u7B7E\u3001\u4F5C\u8005\u6216\u8DEF\u5F84\u2026" autofocus>
426
+ <select id="topic" aria-label="\u6309\u4E3B\u9898\u7B5B\u9009"><option value="">\u5168\u90E8\u4E3B\u9898</option></select>
427
+ <select id="family" aria-label="\u6309\u6765\u6E90\u7B5B\u9009"><option value="">\u5168\u90E8\u6765\u6E90</option></select>
428
+ <select id="kind" aria-label="\u6309\u7C7B\u578B\u7B5B\u9009"><option value="">\u5168\u90E8\u7C7B\u578B</option></select>
429
+ </section>
430
+ <div class="summary"><span id="count"></span><a href="INDEX.md">\u6253\u5F00 Markdown \u76EE\u5F55</a></div>
431
+ <section id="grid" class="grid"></section>
432
+ </main>
433
+ <script id="catalog-data" type="application/json">${data}</script>
434
+ <script>
435
+ const data = JSON.parse(document.getElementById('catalog-data').textContent);
436
+ const byId = (id) => document.getElementById(id);
437
+ const query = byId('query');
438
+ const topic = byId('topic');
439
+ const family = byId('family');
440
+ const kind = byId('kind');
441
+ const grid = byId('grid');
442
+ const count = byId('count');
443
+ const esc = (value) => String(value).replace(/[&<>"']/g, (char) => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[char]));
444
+ const options = (select, values, labels = {}) => {
445
+ for (const value of [...new Set(values)].sort((a,b) => a.localeCompare(b))) {
446
+ const option = document.createElement('option'); option.value = value; option.textContent = labels[value] || value; select.append(option);
447
+ }
448
+ };
449
+ options(topic, data.topics.map((item) => item.id), Object.fromEntries(data.topics.map((item) => [item.id, item.title])));
450
+ options(family, data.entries.map((item) => item.family));
451
+ options(kind, data.entries.map((item) => item.kind));
452
+ const render = () => {
453
+ const needle = query.value.trim().toLocaleLowerCase();
454
+ const matches = data.entries.filter((entry) => {
455
+ const haystack = [entry.title,entry.id,entry.description,entry.family,entry.kind,entry.origin,entry.sourceKind,entry.visibility,entry.sourcePath,...entry.tags,...entry.topics,...entry.bundles].join(' ').toLocaleLowerCase();
456
+ return (!needle || haystack.includes(needle)) && (!topic.value || entry.topics.includes(topic.value)) && (!family.value || entry.family === family.value) && (!kind.value || entry.kind === kind.value);
457
+ });
458
+ count.textContent = '\u663E\u793A ' + matches.length + ' / ' + data.entries.length + ' \u9879';
459
+ grid.innerHTML = matches.length ? matches.map((entry) => '<article><h2>' + esc(entry.title) + '</h2><div class="meta">' + esc(entry.id) + ' \xB7 ' + esc(entry.kind) + ' \xB7 ' + esc(entry.family) + '</div><p>' + esc(entry.description) + '</p><div class="chips">' + entry.topics.map((item) => '<span class="chip">' + esc(item) + '</span>').join('') + '</div><div class="meta">' + esc(entry.origin) + '</div><div class="actions"><a href="' + esc(entry.sourceHref) + '">\u6253\u5F00\u539F\u59CB\u8D44\u4EA7</a></div></article>').join('') : '<div class="empty">\u6CA1\u6709\u5339\u914D\u9879\u3002\u8BD5\u8BD5\u66F4\u77ED\u7684\u5173\u952E\u8BCD\uFF0C\u6216\u6E05\u7A7A\u4E00\u4E2A\u7B5B\u9009\u6761\u4EF6\u3002</div>';
460
+ };
461
+ for (const control of [query,topic,family,kind]) control.addEventListener('input', render);
462
+ render();
463
+ </script>
464
+ </body>
465
+ </html>
466
+ `;
467
+ }
468
+ function renderMainIndex(document) {
469
+ const lines = [
470
+ `# ${document.title}`,
471
+ "",
472
+ document.description,
473
+ "",
474
+ `\u5F53\u524D\u6536\u5F55 **${document.assetCount}** \u9879\uFF1A\u6CE8\u518C\u8868\u8D44\u4EA7 ${document.registryAssetCount} \u9879\uFF0C\u8865\u5145\u96C6\u5408 ${document.extraEntryCount} \u9879\u3002`,
475
+ "",
476
+ "## \u6309\u4E3B\u9898\u627E",
477
+ "",
478
+ "| \u4E3B\u9898 | \u6570\u91CF | \u9002\u7528\u8303\u56F4 |",
479
+ "| --- | ---: | --- |"
480
+ ];
481
+ for (const topic of document.topics) {
482
+ lines.push(
483
+ `| [${escapeTable(topic.title)}](<by-topic/${topic.id}/README.md>) | ${topic.assetCount} | ${escapeTable(topic.description)} |`
484
+ );
485
+ }
486
+ lines.push(
487
+ "",
488
+ "## \u600E\u4E48\u7528",
489
+ "",
490
+ "- \u53CC\u51FB [\u672C\u5730\u641C\u7D22\u9875](<index.html>)\uFF0C\u53EF\u6309\u5173\u952E\u8BCD\u3001\u4E3B\u9898\u3001\u6765\u6E90\u548C\u7C7B\u578B\u7B5B\u9009\u3002",
491
+ "- \u76F4\u63A5\u8FDB\u5165 `by-topic/`\uFF0C\u6309\u7528\u9014\u6D4F\u89C8\uFF1B\u6BCF\u4E2A\u4E3B\u9898\u7684 README \u90FD\u76F4\u63A5\u94FE\u63A5\u539F\u59CB\u8D44\u4EA7\u3002",
492
+ "- \u6765\u6E90\u3001\u4F5C\u8005\u3001\u7C7B\u578B\u548C Bundle \u662F\u8D44\u4EA7\u5C5E\u6027\uFF0C\u53EA\u4FDD\u7559\u5728\u641C\u7D22\u4E0E\u6570\u636E\u4E2D\uFF0C\u4E0D\u518D\u590D\u5236\u6210\u591A\u5957\u6587\u4EF6\u5939\u3002",
493
+ "",
494
+ "\u672C\u673A\u6267\u884C\u5E26 `--native-links` \u7684\u6784\u5EFA\u540E\uFF0C\u5FEB\u6377\u94FE\u63A5\u4F1A\u76F4\u63A5\u51FA\u73B0\u5728\u5404\u4E3B\u9898\u6587\u4EF6\u5939\u4E2D\uFF0C\u5E76\u4EE5 `.asset-link` \u7ED3\u5C3E\u3002\u94FE\u63A5\u662F\u53EF\u5220\u9664\u3001\u53EF\u91CD\u5EFA\u7684\u672C\u673A\u7F13\u5B58\uFF1BREADME\u3001HTML \u548C JSON \u624D\u662F\u8DE8\u5E73\u53F0\u4E8B\u5B9E\u5C42\u3002",
495
+ ""
496
+ );
497
+ return lines.join("\n");
498
+ }
499
+ function renderGroupIndex(title, description, entries, indexAbsolutePath, agentAssetsDir) {
500
+ const lines = [
501
+ `# ${title}`,
502
+ "",
503
+ description,
504
+ "",
505
+ `\u5171 ${entries.length} \u9879\u3002`,
506
+ "",
507
+ "| \u540D\u79F0 | \u7C7B\u578B | \u6765\u6E90 | \u8BF4\u660E |",
508
+ "| --- | --- | --- | --- |"
509
+ ];
510
+ for (const entry of entries) {
511
+ const sourcePath = normalizeRelativePath(
512
+ relative2(dirname2(indexAbsolutePath), join3(agentAssetsDir, ...entry.sourcePath.split("/")))
513
+ );
514
+ lines.push(
515
+ `| [${escapeTable(entry.title)}](<${sourcePath}>) | ${escapeTable(entry.kind)} | ${escapeTable(entry.family)} | ${escapeTable(entry.description)} |`
516
+ );
517
+ }
518
+ lines.push(
519
+ "",
520
+ "\u672C\u673A\u5FEB\u6377\u94FE\u63A5\uFF08\u5982\u5DF2\u751F\u6210\uFF09\u5C31\u5728\u672C\u76EE\u5F55\uFF0C\u6587\u4EF6\u540D\u4EE5 `.asset-link` \u7ED3\u5C3E\uFF1B\u6CA1\u6709\u94FE\u63A5\u65F6\uFF0C\u4E0A\u8868\u4ECD\u53EF\u76F4\u63A5\u6253\u5F00\u539F\u59CB\u8D44\u4EA7\u3002",
521
+ "",
522
+ "[\u8FD4\u56DE\u603B\u7D22\u5F15](<../../INDEX.md>)",
523
+ ""
524
+ );
525
+ return lines.join("\n");
526
+ }
527
+ function writeNativeLinks(options) {
528
+ const specs = createNativeLinkSpecs(options.agentAssetsDir, options.entries);
529
+ const previous = readLocalLinksManifest(options.catalogRoot);
530
+ const previousLinks = new Set(previous?.links ?? []);
531
+ for (const relativePath of previousLinks) {
532
+ assertManagedLocalLinkPath(relativePath);
533
+ const linkPath = resolveCatalogPath(options.catalogRoot, relativePath);
534
+ if (pathEntryExists(linkPath) && !lstatSync(linkPath).isSymbolicLink()) {
535
+ throw new Error(
536
+ `Refusing to replace a managed link that is no longer a symlink: ${relativePath}`
537
+ );
538
+ }
539
+ }
540
+ for (const spec of specs) {
541
+ assertManagedLocalLinkPath(spec.relativePath);
542
+ assertCatalogPathParents(options.catalogRoot, spec.relativePath);
543
+ const linkPath = resolveCatalogPath(options.catalogRoot, spec.relativePath);
544
+ if (pathEntryExists(linkPath) && !previousLinks.has(spec.relativePath)) {
545
+ throw new Error(`Refusing to replace unmanaged catalog path: ${spec.relativePath}`);
546
+ }
547
+ }
548
+ const warnings = [];
549
+ const stagedLinks = [];
550
+ const stageRoot = mkdtempSync(join3(dirname2(options.catalogRoot), ".asset-catalog-links-stage-"));
551
+ const backupRoot = mkdtempSync(
552
+ join3(dirname2(options.catalogRoot), ".asset-catalog-links-backup-")
553
+ );
554
+ const installed = [];
555
+ const backedUp = [];
556
+ try {
557
+ for (const spec of specs) {
558
+ const stagedPath = resolveCatalogPath(stageRoot, spec.relativePath);
559
+ const finalPath = resolveCatalogPath(options.catalogRoot, spec.relativePath);
560
+ mkdirSync(dirname2(stagedPath), { recursive: true });
561
+ const strategy = nativeLinkStrategy(options.platform, spec.sourceType);
562
+ try {
563
+ if (strategy === "relative-symlink") {
564
+ const target = relative2(dirname2(finalPath), spec.sourceAbsolutePath);
565
+ symlinkSync(target, stagedPath, spec.sourceType === "directory" ? "dir" : "file");
566
+ } else if (strategy === "junction") {
567
+ symlinkSync(spec.sourceAbsolutePath, stagedPath, "junction");
568
+ } else {
569
+ symlinkSync(spec.sourceAbsolutePath, stagedPath, "file");
570
+ }
571
+ stagedLinks.push(spec.relativePath);
572
+ } catch (error) {
573
+ const reason = error instanceof Error ? error.message : String(error);
574
+ warnings.push(`native link skipped (${spec.relativePath}): ${reason}`);
575
+ }
576
+ }
577
+ const localManifest = renderLocalLinksManifest(stagedLinks);
578
+ writeFileSync(join3(stageRoot, localLinksManifestName), localManifest);
579
+ for (const relativePath of [...previousLinks, localLinksManifestName]) {
580
+ const sourcePath = resolveCatalogPath(options.catalogRoot, relativePath);
581
+ if (!pathEntryExists(sourcePath)) continue;
582
+ const backupPath = resolveCatalogPath(backupRoot, relativePath);
583
+ mkdirSync(dirname2(backupPath), { recursive: true });
584
+ renameSync(sourcePath, backupPath);
585
+ backedUp.push(relativePath);
586
+ }
587
+ for (const relativePath of [...stagedLinks, localLinksManifestName]) {
588
+ const sourcePath = resolveCatalogPath(stageRoot, relativePath);
589
+ const targetPath = resolveCatalogPath(options.catalogRoot, relativePath);
590
+ mkdirSync(dirname2(targetPath), { recursive: true });
591
+ renameSync(sourcePath, targetPath);
592
+ installed.push(relativePath);
593
+ }
594
+ for (const relativePath of previousLinks) {
595
+ pruneEmptyCatalogParents(options.catalogRoot, dirname2(relativePath));
596
+ }
597
+ } catch (error) {
598
+ for (const relativePath of installed.reverse()) {
599
+ const targetPath = resolveCatalogPath(options.catalogRoot, relativePath);
600
+ if (pathEntryExists(targetPath)) rmSync(targetPath, { recursive: true, force: true });
601
+ }
602
+ for (const relativePath of backedUp.reverse()) {
603
+ const backupPath = resolveCatalogPath(backupRoot, relativePath);
604
+ const targetPath = resolveCatalogPath(options.catalogRoot, relativePath);
605
+ if (!pathEntryExists(backupPath)) continue;
606
+ mkdirSync(dirname2(targetPath), { recursive: true });
607
+ renameSync(backupPath, targetPath);
608
+ }
609
+ throw error;
610
+ } finally {
611
+ removeTemporaryTree(stageRoot, warnings);
612
+ removeTemporaryTree(backupRoot, warnings);
613
+ }
614
+ return { linkCount: stagedLinks.length, warnings };
615
+ }
616
+ function createNativeLinkSpecs(agentAssetsDir, entries) {
617
+ const specs = [];
618
+ const add = (topic, entry) => {
619
+ specs.push({
620
+ relativePath: `by-topic/${safePathSegment(topic)}/${nativeLinkName(entry)}`,
621
+ sourceAbsolutePath: join3(agentAssetsDir, ...entry.sourcePath.split("/")),
622
+ sourceType: entry.sourceType
623
+ });
624
+ };
625
+ for (const entry of entries) {
626
+ for (const topic of entry.topics) add(topic, entry);
627
+ }
628
+ const sorted = specs.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
629
+ for (let index = 1; index < sorted.length; index += 1) {
630
+ if (sorted[index - 1].relativePath === sorted[index].relativePath) {
631
+ throw new Error(`Duplicate native catalog link path: ${sorted[index].relativePath}`);
632
+ }
633
+ }
634
+ return sorted;
635
+ }
636
+ function writePortableCatalog(catalogRoot, files) {
637
+ mkdirSync(catalogRoot, { recursive: true });
638
+ const warnings = [];
639
+ const previous = readPortableManifest(catalogRoot);
640
+ const previousFiles = new Set(previous?.portableFiles ?? []);
641
+ const expectedFiles = [...files.keys()].sort();
642
+ for (const relativePath of previousFiles) {
643
+ assertManagedPortablePath(relativePath);
644
+ const targetPath = resolveCatalogPath(catalogRoot, relativePath);
645
+ if (pathEntryExists(targetPath)) {
646
+ const stats = lstatSync(targetPath);
647
+ if (!stats.isFile() || stats.isSymbolicLink()) {
648
+ throw new Error(
649
+ `Refusing to replace a managed file that is no longer regular: ${relativePath}`
650
+ );
651
+ }
652
+ }
653
+ }
654
+ for (const relativePath of expectedFiles) {
655
+ assertManagedPortablePath(relativePath);
656
+ assertCatalogPathParents(catalogRoot, relativePath);
657
+ const targetPath = resolveCatalogPath(catalogRoot, relativePath);
658
+ if (pathEntryExists(targetPath) && !previousFiles.has(relativePath)) {
659
+ throw new Error(`Refusing to replace unmanaged catalog path: ${relativePath}`);
660
+ }
661
+ }
662
+ const legacyDirectories = findLegacyManagedDirectories(catalogRoot);
663
+ const stageRoot = mkdtempSync(join3(dirname2(catalogRoot), ".asset-catalog-stage-"));
664
+ const backupRoot = mkdtempSync(join3(dirname2(catalogRoot), ".asset-catalog-backup-"));
665
+ const installed = [];
666
+ const backedUp = [];
667
+ const legacyBackedUp = [];
668
+ try {
669
+ for (const [relativePath, content] of files) {
670
+ const stagedPath = resolveCatalogPath(stageRoot, relativePath);
671
+ mkdirSync(dirname2(stagedPath), { recursive: true });
672
+ writeFileSync(stagedPath, content);
673
+ }
674
+ writeFileSync(join3(stageRoot, portableManifestName), renderPortableManifest(files));
675
+ for (const relativePath of [...previousFiles, portableManifestName]) {
676
+ const sourcePath = resolveCatalogPath(catalogRoot, relativePath);
677
+ if (!pathEntryExists(sourcePath)) continue;
678
+ const backupPath = resolveCatalogPath(backupRoot, `portable/${relativePath}`);
679
+ mkdirSync(dirname2(backupPath), { recursive: true });
680
+ renameSync(sourcePath, backupPath);
681
+ backedUp.push(relativePath);
682
+ }
683
+ for (const name of legacyDirectories) {
684
+ const sourcePath = join3(catalogRoot, name);
685
+ const backupPath = resolveCatalogPath(backupRoot, `legacy/${name}`);
686
+ mkdirSync(dirname2(backupPath), { recursive: true });
687
+ renameSync(sourcePath, backupPath);
688
+ legacyBackedUp.push(name);
689
+ }
690
+ for (const relativePath of [...expectedFiles, portableManifestName]) {
691
+ const sourcePath = resolveCatalogPath(stageRoot, relativePath);
692
+ const targetPath = resolveCatalogPath(catalogRoot, relativePath);
693
+ mkdirSync(dirname2(targetPath), { recursive: true });
694
+ renameSync(sourcePath, targetPath);
695
+ installed.push(relativePath);
696
+ }
697
+ for (const relativePath of previousFiles) {
698
+ pruneEmptyCatalogParents(catalogRoot, dirname2(relativePath));
699
+ }
700
+ } catch (error) {
701
+ for (const relativePath of installed.reverse()) {
702
+ const targetPath = resolveCatalogPath(catalogRoot, relativePath);
703
+ if (pathEntryExists(targetPath)) rmSync(targetPath, { recursive: true, force: true });
704
+ }
705
+ for (const relativePath of backedUp.reverse()) {
706
+ const backupPath = resolveCatalogPath(backupRoot, `portable/${relativePath}`);
707
+ const targetPath = resolveCatalogPath(catalogRoot, relativePath);
708
+ if (!pathEntryExists(backupPath)) continue;
709
+ mkdirSync(dirname2(targetPath), { recursive: true });
710
+ renameSync(backupPath, targetPath);
711
+ }
712
+ for (const name of legacyBackedUp.reverse()) {
713
+ const backupPath = resolveCatalogPath(backupRoot, `legacy/${name}`);
714
+ const targetPath = join3(catalogRoot, name);
715
+ if (pathEntryExists(backupPath)) renameSync(backupPath, targetPath);
716
+ }
717
+ throw error;
718
+ } finally {
719
+ removeTemporaryTree(stageRoot, warnings);
720
+ removeTemporaryTree(backupRoot, warnings);
721
+ }
722
+ return warnings;
723
+ }
724
+ function renderPortableManifest(files) {
725
+ const manifest = {
726
+ schemaVersion: 1,
727
+ generator: manifestGenerator,
728
+ portableFiles: [...files.keys()].sort()
729
+ };
730
+ return `${JSON.stringify(manifest, null, 2)}
731
+ `;
732
+ }
733
+ function renderLocalLinksManifest(links) {
734
+ const manifest = {
735
+ schemaVersion: 1,
736
+ generator: manifestGenerator,
737
+ links: [...links].sort()
738
+ };
739
+ return `${JSON.stringify(manifest, null, 2)}
740
+ `;
741
+ }
742
+ function readPortableManifest(catalogRoot) {
743
+ const path = join3(catalogRoot, portableManifestName);
744
+ if (!existsSync3(path)) return void 0;
745
+ const value = readManifest(path, "portableFiles");
746
+ for (const relativePath of value.items) assertManagedPortablePath(relativePath);
747
+ return {
748
+ schemaVersion: 1,
749
+ generator: manifestGenerator,
750
+ portableFiles: value.items
751
+ };
752
+ }
753
+ function readLocalLinksManifest(catalogRoot) {
754
+ const path = join3(catalogRoot, localLinksManifestName);
755
+ if (!existsSync3(path)) return void 0;
756
+ const value = readManifest(path, "links");
757
+ for (const relativePath of value.items) assertManagedLocalLinkPath(relativePath);
758
+ return { schemaVersion: 1, generator: manifestGenerator, links: value.items };
759
+ }
760
+ function readManifest(path, listKey) {
761
+ const stats = lstatSync(path);
762
+ if (!stats.isFile() || stats.isSymbolicLink()) {
763
+ throw new Error(`Invalid asset catalog manifest file: ${path}`);
764
+ }
765
+ let parsed;
766
+ try {
767
+ parsed = JSON.parse(readFileSync2(path, "utf8"));
768
+ } catch {
769
+ throw new Error(`Invalid asset catalog manifest JSON: ${path}`);
770
+ }
771
+ const items = parsed[listKey];
772
+ if (parsed.schemaVersion !== 1 || parsed.generator !== manifestGenerator || !Array.isArray(items) || items.some((item) => typeof item !== "string") || new Set(items).size !== items.length) {
773
+ throw new Error(`Invalid asset catalog manifest shape: ${path}`);
774
+ }
775
+ return { items: [...items].sort() };
776
+ }
777
+ function findLegacyManagedDirectories(catalogRoot) {
778
+ const result = [];
779
+ for (const name of legacyManagedDirectoryNames) {
780
+ const root = join3(catalogRoot, name);
781
+ if (!existsSync3(root)) continue;
782
+ const stats = lstatSync(root);
783
+ if (!stats.isDirectory() || stats.isSymbolicLink()) {
784
+ throw new Error(`Refusing to migrate non-directory legacy catalog output: ${root}`);
785
+ }
786
+ const sentinel = join3(root, legacyManagedSentinel);
787
+ if (!existsSync3(sentinel) || !lstatSync(sentinel).isFile()) {
788
+ throw new Error(`Refusing to migrate unmanaged legacy catalog directory: ${root}`);
789
+ }
790
+ result.push(name);
791
+ }
792
+ return result;
793
+ }
794
+ function assertManagedPortablePath(relativePath) {
795
+ if (relativePath !== "INDEX.md" && relativePath !== "index.html" && relativePath !== "catalog.json" && !/^by-topic\/[a-z0-9]+(?:-[a-z0-9]+)*\/README\.md$/u.test(relativePath)) {
796
+ throw new Error(`Unsafe managed portable catalog path: ${relativePath}`);
797
+ }
798
+ }
799
+ function assertManagedLocalLinkPath(relativePath) {
800
+ if (!/^by-topic\/[a-z0-9]+(?:-[a-z0-9]+)*\/[a-z0-9][a-z0-9._-]*\.asset-link$/u.test(relativePath)) {
801
+ throw new Error(`Unsafe managed local catalog link path: ${relativePath}`);
802
+ }
803
+ }
804
+ function resolveCatalogPath(root, relativePath) {
805
+ const absoluteRoot = resolve(root);
806
+ const absolutePath = resolve(absoluteRoot, ...normalizeRelativePath(relativePath).split("/"));
807
+ const back = normalizeRelativePath(relative2(absoluteRoot, absolutePath));
808
+ if (back === ".." || back.startsWith("../") || isAbsolute(back)) {
809
+ throw new Error(`Catalog path escapes its managed root: ${relativePath}`);
810
+ }
811
+ return absolutePath;
812
+ }
813
+ function assertCatalogPathParents(catalogRoot, relativePath) {
814
+ const absoluteRoot = resolve(catalogRoot);
815
+ let current = dirname2(resolveCatalogPath(catalogRoot, relativePath));
816
+ while (current !== absoluteRoot) {
817
+ if (existsSync3(current)) {
818
+ const stats = lstatSync(current);
819
+ if (!stats.isDirectory() || stats.isSymbolicLink()) {
820
+ throw new Error(
821
+ `Refusing to write through a non-directory catalog parent: ${normalizeRelativePath(relative2(absoluteRoot, current))}`
822
+ );
823
+ }
824
+ }
825
+ const parent = dirname2(current);
826
+ if (parent === current) throw new Error(`Catalog parent escapes its root: ${relativePath}`);
827
+ current = parent;
828
+ }
829
+ }
830
+ function pruneEmptyCatalogParents(catalogRoot, relativeDirectory) {
831
+ const absoluteRoot = resolve(catalogRoot);
832
+ let current = resolveCatalogPath(catalogRoot, relativeDirectory);
833
+ while (current !== absoluteRoot && existsSync3(current)) {
834
+ const stats = lstatSync(current);
835
+ if (!stats.isDirectory() || stats.isSymbolicLink() || readdirSync3(current).length > 0) return;
836
+ rmSync(current, { recursive: true, force: true });
837
+ current = dirname2(current);
838
+ }
839
+ }
840
+ function pathEntryExists(path) {
841
+ try {
842
+ lstatSync(path);
843
+ return true;
844
+ } catch (error) {
845
+ if (error.code === "ENOENT") return false;
846
+ throw error;
847
+ }
848
+ }
849
+ function removeTemporaryTree(path, warnings) {
850
+ try {
851
+ rmSync(path, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
852
+ } catch (error) {
853
+ const reason = error instanceof Error ? error.message : String(error);
854
+ warnings.push(`temporary catalog cleanup pending (${path}): ${reason}`);
855
+ }
856
+ }
857
+ function findUncoveredSourcePaths(agentAssetsDir, entries, ignoredSourcePaths) {
858
+ const candidates = /* @__PURE__ */ new Set();
859
+ for (const filePath of listFiles2(join3(agentAssetsDir, "skills"))) {
860
+ if (basename(filePath) === "SKILL.md") {
861
+ candidates.add(normalizeRelativePath(relative2(agentAssetsDir, dirname2(filePath))));
862
+ }
863
+ }
864
+ for (const root of ["rules", "commands", "prompts"]) {
865
+ for (const filePath of listFiles2(join3(agentAssetsDir, root))) {
866
+ if (extname(filePath).toLowerCase() === ".md") {
867
+ candidates.add(normalizeRelativePath(relative2(agentAssetsDir, filePath)));
868
+ }
869
+ }
870
+ }
871
+ for (const filePath of listFiles2(join3(agentAssetsDir, "tooling"))) {
872
+ candidates.add(normalizeRelativePath(relative2(agentAssetsDir, filePath)));
873
+ }
874
+ return [...candidates].filter((candidate) => !isCoveredByAnySource(candidate, entries, ignoredSourcePaths)).sort();
875
+ }
876
+ function isCoveredByAnySource(candidate, entries, ignoredSourcePaths) {
877
+ const covers = (sourcePath) => candidate === sourcePath || candidate.startsWith(`${sourcePath}/`);
878
+ return entries.some((entry) => covers(entry.sourcePath)) || ignoredSourcePaths.some(covers);
879
+ }
880
+ function listFiles2(root) {
881
+ if (!existsSync3(root)) return [];
882
+ const stats = lstatSync(root);
883
+ if (stats.isSymbolicLink()) return [];
884
+ if (stats.isFile()) return [root];
885
+ const files = [];
886
+ for (const entry of readdirSync3(root, { withFileTypes: true })) {
887
+ if (ignoredTraversalNames.has(entry.name) || entry.name.startsWith("._")) continue;
888
+ const path = join3(root, entry.name);
889
+ if (entry.isDirectory()) files.push(...listFiles2(path));
890
+ else if (entry.isFile()) files.push(path);
891
+ }
892
+ return files.sort();
893
+ }
894
+ function readAssetDescription(sourceAbsolutePath, sourceType, fallback) {
895
+ const documentPath = sourceType === "directory" ? join3(sourceAbsolutePath, "SKILL.md") : sourceAbsolutePath;
896
+ if (!existsSync3(documentPath)) return fallback;
897
+ const text = readFileSync2(documentPath, "utf8");
898
+ const frontmatter = text.match(/^---\r?\n([\s\S]*?)\r?\n---/u)?.[1] ?? "";
899
+ const description = extractFrontmatterDescription(frontmatter);
900
+ if (description) return compactDescription(description, fallback);
901
+ const withoutFrontmatter = text.replace(/^---\r?\n[\s\S]*?\r?\n---\s*/u, "");
902
+ const paragraph = withoutFrontmatter.split(/\r?\n\r?\n/u).map((part) => part.replace(/\r?\n/g, " ").trim()).find((part) => part && !part.startsWith("#") && !part.startsWith("```"));
903
+ return compactDescription(paragraph ?? fallback, fallback);
904
+ }
905
+ function extractFrontmatterDescription(frontmatter) {
906
+ const lines = frontmatter.split(/\r?\n/u);
907
+ const index = lines.findIndex((line) => /^description\s*:/u.test(line));
908
+ if (index < 0) return void 0;
909
+ const inlineValue = lines[index].replace(/^description\s*:\s*/u, "").trim();
910
+ if (!/^[|>][+-]?$/u.test(inlineValue)) {
911
+ return inlineValue ? stripWrappingQuotes(inlineValue) : void 0;
912
+ }
913
+ const block = [];
914
+ for (const line of lines.slice(index + 1)) {
915
+ if (line.trim() && !/^\s/u.test(line)) break;
916
+ block.push(line.replace(/^\s+/u, "").trim());
917
+ }
918
+ const separator = inlineValue.startsWith("|") ? "\n" : " ";
919
+ const value = block.join(separator).trim();
920
+ return value || void 0;
921
+ }
922
+ function compactDescription(value, fallback) {
923
+ const compact = value.replace(/\[([^\]]+)]\([^)]*\)/g, "$1").replace(/[`*_>#]/g, "").replace(/\s+/g, " ").trim();
924
+ if (!compact) return fallback;
925
+ return compact.length > 220 ? `${compact.slice(0, 217)}...` : compact;
926
+ }
927
+ function stripWrappingQuotes(value) {
928
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
929
+ return value.slice(1, -1);
930
+ }
931
+ return value;
932
+ }
933
+ function resolveSafeSourcePath(agentAssetsDir, sourcePath, id) {
934
+ if (!isSafeSourcePath(sourcePath)) throw new Error(`Unsafe sourcePath for ${id}: ${sourcePath}`);
935
+ const absolutePath = resolve(agentAssetsDir, ...normalizeRelativePath(sourcePath).split("/"));
936
+ const relativePath = normalizeRelativePath(relative2(resolve(agentAssetsDir), absolutePath));
937
+ if (relativePath === ".." || relativePath.startsWith("../") || isAbsolute(relativePath)) {
938
+ throw new Error(`Source path escapes agent-assets for ${id}: ${sourcePath}`);
939
+ }
940
+ if (!existsSync3(absolutePath)) throw new Error(`Missing catalog source for ${id}: ${sourcePath}`);
941
+ return absolutePath;
942
+ }
943
+ function isSafeSourcePath(sourcePath) {
944
+ if (!sourcePath || isAbsolute(sourcePath) || sourcePath.includes("\\")) return false;
945
+ const normalized = normalizeRelativePath(sourcePath);
946
+ return normalized !== "." && normalized !== ".." && !normalized.startsWith("../");
947
+ }
948
+ function createBundleMembership(bundles) {
949
+ const membership = /* @__PURE__ */ new Map();
950
+ for (const bundle of bundles) {
951
+ for (const assetId of bundle.assets) {
952
+ membership.set(assetId, [...membership.get(assetId) ?? [], bundle.id]);
953
+ }
954
+ }
955
+ for (const [id, bundleIds] of membership) membership.set(id, uniqueSorted(bundleIds));
956
+ return membership;
957
+ }
958
+ function assertUniqueEntries(entries) {
959
+ const ids = /* @__PURE__ */ new Set();
960
+ for (const entry of entries) {
961
+ if (ids.has(entry.id)) throw new Error(`Duplicate catalog entry id: ${entry.id}`);
962
+ ids.add(entry.id);
963
+ }
964
+ }
965
+ function assertKnownTopics(id, topics, topicIds) {
966
+ for (const topic of topics) {
967
+ if (!topicIds.has(topic)) throw new Error(`Unknown topic ${topic} on catalog entry ${id}`);
968
+ }
969
+ }
970
+ function nativeLinkName(entry) {
971
+ const base = safePathSegment(entry.id.replace("/", "--"));
972
+ return `${base}.asset-link`;
973
+ }
974
+ function safePathSegment(value) {
975
+ const safe = value.normalize("NFKD").replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase();
976
+ if (!safe || safe === "." || safe === "..")
977
+ throw new Error(`Unsafe catalog path segment: ${value}`);
978
+ return safe;
979
+ }
980
+ function normalizeRelativePath(path) {
981
+ return path.replaceAll("\\", "/");
982
+ }
983
+ function encodeRelativeHref(path) {
984
+ return normalizeRelativePath(path).split("/").map((part) => part === "." || part === ".." ? part : encodeURIComponent(part)).join("/");
985
+ }
986
+ function uniqueSorted(values) {
987
+ return [...new Set(values)].sort((left, right) => left.localeCompare(right));
988
+ }
989
+ function compareCatalogEntries(left, right) {
990
+ return `${left.family}:${left.title}:${left.id}`.localeCompare(
991
+ `${right.family}:${right.title}:${right.id}`
992
+ );
993
+ }
994
+ function escapeTable(value) {
995
+ return value.replaceAll("|", "\\|").replace(/\r?\n/g, " ");
996
+ }
997
+ function escapeHtml(value) {
998
+ return value.replace(/[&<>"']/g, (character) => {
999
+ const entities = {
1000
+ "&": "&amp;",
1001
+ "<": "&lt;",
1002
+ ">": "&gt;",
1003
+ '"': "&quot;",
1004
+ "'": "&#39;"
1005
+ };
1006
+ return entities[character] ?? character;
1007
+ });
1008
+ }
1009
+ function sha256(value) {
1010
+ return `sha256:${createHash("sha256").update(value).digest("hex")}`;
1011
+ }
1012
+
1013
+ // src/asset-npx/maintenance.ts
1014
+ import { createHash as createHash2 } from "node:crypto";
1015
+ import {
1016
+ cpSync,
1017
+ existsSync as existsSync4,
1018
+ mkdirSync as mkdirSync2,
1019
+ mkdtempSync as mkdtempSync2,
1020
+ readdirSync as readdirSync4,
1021
+ readFileSync as readFileSync3,
1022
+ statSync as statSync2
1023
+ } from "node:fs";
1024
+ import { join as join4, relative as relative3 } from "node:path";
74
1025
  import { tmpdir } from "node:os";
1026
+
1027
+ // src/command-runner.ts
1028
+ import {
1029
+ spawnSync
1030
+ } from "node:child_process";
1031
+ function spawnPlatformSync(command2, args, options) {
1032
+ if (process.platform !== "win32") return spawnSync(command2, args, options);
1033
+ const commandLine = [command2, ...args].map(quoteWindowsArgument).join(" ");
1034
+ return spawnSync(process.env.ComSpec ?? "cmd.exe", ["/d", "/s", "/c", `"${commandLine}"`], {
1035
+ ...options,
1036
+ // The command line above is intentionally assembled for cmd.exe. Without
1037
+ // this flag Node escapes its quotes before cmd.exe can parse them.
1038
+ windowsVerbatimArguments: true
1039
+ });
1040
+ }
1041
+ function quoteWindowsArgument(value) {
1042
+ if (value.length === 0 || /[\s"&|<>^]/.test(value)) {
1043
+ return `"${value.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\*)$/, "$1$1")}"`;
1044
+ }
1045
+ return value;
1046
+ }
1047
+
1048
+ // src/asset-npx/maintenance.ts
75
1049
  function createNpxSkillsMaintenancePlan(options) {
76
1050
  assertNativeNpxRoot(options.npxRoot);
77
1051
  if (options.operation === "add" && !options.source) {
78
1052
  throw new Error("npx skills add requires a source.");
79
1053
  }
80
1054
  const before = snapshotFiles(options.npxRoot);
81
- const tempRoot = mkdtempSync(join3(tmpdir(), "pro-gov-npx-skills-"));
1055
+ const tempRoot = mkdtempSync2(join4(tmpdir(), "pro-gov-npx-skills-"));
82
1056
  cpSync(options.npxRoot, tempRoot, { recursive: true, dereference: false });
83
1057
  const command2 = buildNpxCommand(options);
84
1058
  const runner = options.runner ?? defaultRunner;
@@ -122,10 +1096,10 @@ ${stderr}`.replace(ansiEscape, "");
122
1096
  }
123
1097
  }
124
1098
  function assertNativeNpxRoot(npxRoot) {
125
- if (!existsSync3(join3(npxRoot, "skills-lock.json"))) {
1099
+ if (!existsSync4(join4(npxRoot, "skills-lock.json"))) {
126
1100
  throw new Error(`npx skills root is missing skills-lock.json: ${npxRoot}`);
127
1101
  }
128
- if (!existsSync3(join3(npxRoot, ".agents/skills"))) {
1102
+ if (!existsSync4(join4(npxRoot, ".agents/skills"))) {
129
1103
  throw new Error(`npx skills root is missing .agents/skills: ${npxRoot}`);
130
1104
  }
131
1105
  }
@@ -140,7 +1114,8 @@ function buildNpxCommand(options) {
140
1114
  return command2;
141
1115
  }
142
1116
  function defaultRunner({ command: command2, cwd, timeoutMs }) {
143
- const result = spawnSync(command2[0] ?? "npx", command2.slice(1), {
1117
+ const commandName = command2[0] ?? "npx";
1118
+ const result = spawnPlatformSync(commandName, command2.slice(1), {
144
1119
  cwd,
145
1120
  encoding: "utf8",
146
1121
  timeout: timeoutMs
@@ -155,21 +1130,21 @@ function defaultRunner({ command: command2, cwd, timeoutMs }) {
155
1130
  }
156
1131
  function snapshotFiles(root) {
157
1132
  const snapshot = /* @__PURE__ */ new Map();
158
- for (const filePath of listFiles2(root)) {
159
- const relativePath = toUnixPath2(relative2(root, filePath));
1133
+ for (const filePath of listFiles3(root)) {
1134
+ const relativePath = toUnixPath2(relative3(root, filePath));
160
1135
  snapshot.set(relativePath, hashFile(filePath));
161
1136
  }
162
1137
  return snapshot;
163
1138
  }
164
- function listFiles2(root) {
1139
+ function listFiles3(root) {
165
1140
  const files = [];
166
1141
  collectFiles(root, root, files);
167
1142
  return files.sort();
168
1143
  }
169
1144
  function collectFiles(root, current, files) {
170
- mkdirSync(root, { recursive: true });
171
- for (const entry of readdirSync3(current, { withFileTypes: true })) {
172
- const entryPath = join3(current, entry.name);
1145
+ mkdirSync2(root, { recursive: true });
1146
+ for (const entry of readdirSync4(current, { withFileTypes: true })) {
1147
+ const entryPath = join4(current, entry.name);
173
1148
  if (entry.isDirectory()) {
174
1149
  collectFiles(root, entryPath, files);
175
1150
  } else if (entry.isFile()) {
@@ -178,11 +1153,11 @@ function collectFiles(root, current, files) {
178
1153
  }
179
1154
  }
180
1155
  function hashFile(path) {
181
- const hash = createHash("sha256");
182
- const stats = statSync(path);
1156
+ const hash = createHash2("sha256");
1157
+ const stats = statSync2(path);
183
1158
  hash.update(String(stats.size));
184
1159
  hash.update("\0");
185
- hash.update(readFileSync2(path));
1160
+ hash.update(readFileSync3(path));
186
1161
  return hash.digest("hex");
187
1162
  }
188
1163
  function diffSnapshots(before, after) {
@@ -209,14 +1184,14 @@ function toUnixPath2(path) {
209
1184
  }
210
1185
 
211
1186
  // src/asset-registry/loader.ts
212
- import { createHash as createHash2 } from "node:crypto";
213
- import { existsSync as existsSync5, readdirSync as readdirSync5, readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
214
- import { dirname as dirname2, join as join5, relative as relative4 } from "node:path";
1187
+ import { createHash as createHash3 } from "node:crypto";
1188
+ import { existsSync as existsSync6, readdirSync as readdirSync6, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
1189
+ import { dirname as dirname3, join as join6, relative as relative5 } from "node:path";
215
1190
  import { fileURLToPath as fileURLToPath2 } from "node:url";
216
1191
 
217
1192
  // src/asset-registry/registry.ts
218
- import { existsSync as existsSync4, lstatSync, readdirSync as readdirSync4, realpathSync } from "node:fs";
219
- import { isAbsolute, join as join4, posix, relative as relative3, sep } from "node:path";
1193
+ import { existsSync as existsSync5, lstatSync as lstatSync2, readdirSync as readdirSync5, realpathSync } from "node:fs";
1194
+ import { isAbsolute as isAbsolute2, join as join5, posix, relative as relative4, sep } from "node:path";
220
1195
  var supportedFamilies = /* @__PURE__ */ new Set([
221
1196
  "pie-skills",
222
1197
  "npx-skills",
@@ -260,7 +1235,7 @@ function assetSkillInstallName(asset) {
260
1235
  return asset.installName ?? posix.basename(asset.sourcePath);
261
1236
  }
262
1237
  function isValidAssetProjectTargetPath(kind, projectTargetPath) {
263
- if (typeof projectTargetPath !== "string" || projectTargetPath.length === 0 || isAbsolute(projectTargetPath) || projectTargetPath.includes("\\")) {
1238
+ if (typeof projectTargetPath !== "string" || projectTargetPath.length === 0 || isAbsolute2(projectTargetPath) || projectTargetPath.includes("\\")) {
264
1239
  return false;
265
1240
  }
266
1241
  const segments = projectTargetPath.split("/");
@@ -416,11 +1391,11 @@ function validateAssetRegistry(registry, options = {}) {
416
1391
  });
417
1392
  }
418
1393
  if (options.agentAssetsDir && isSafeRegistrySourcePath(asset.sourcePath)) {
419
- const sourceAbsolutePath = join4(
1394
+ const sourceAbsolutePath = join5(
420
1395
  options.agentAssetsDir,
421
1396
  normalizeRegistrySourcePath(asset.sourcePath)
422
1397
  );
423
- if (!existsSync4(sourceAbsolutePath)) {
1398
+ if (!existsSync5(sourceAbsolutePath)) {
424
1399
  issues.push({
425
1400
  type: "missing-source-path",
426
1401
  id: asset.id,
@@ -444,7 +1419,7 @@ function validateAssetRegistry(registry, options = {}) {
444
1419
  message: `Local skill pack must contain .codex-plugin/plugin.json and at least one skills/*/SKILL.md: ${asset.sourcePath}`
445
1420
  });
446
1421
  }
447
- } else if (!existsSync4(join4(sourceAbsolutePath, "SKILL.md"))) {
1422
+ } else if (!existsSync5(join5(sourceAbsolutePath, "SKILL.md"))) {
448
1423
  issues.push({
449
1424
  type: "missing-skill-file",
450
1425
  id: asset.id,
@@ -456,7 +1431,7 @@ function validateAssetRegistry(registry, options = {}) {
456
1431
  }
457
1432
  }
458
1433
  if (options.agentAssetsDir) {
459
- const npxCompatibilityLayer = join4(options.agentAssetsDir, "skills/npx-skills/skills");
1434
+ const npxCompatibilityLayer = join5(options.agentAssetsDir, "skills/npx-skills/skills");
460
1435
  if (pathExistsEvenIfDanglingSymlink(npxCompatibilityLayer)) {
461
1436
  issues.push({
462
1437
  type: "internal-npx-compatibility-layer",
@@ -469,20 +1444,20 @@ function validateAssetRegistry(registry, options = {}) {
469
1444
  return issues;
470
1445
  }
471
1446
  function isLocalSkillPack(sourceAbsolutePath) {
472
- const skillsRoot = join4(sourceAbsolutePath, "skills");
473
- if (!existsSync4(join4(sourceAbsolutePath, ".codex-plugin/plugin.json")) || !existsSync4(skillsRoot)) {
1447
+ const skillsRoot = join5(sourceAbsolutePath, "skills");
1448
+ if (!existsSync5(join5(sourceAbsolutePath, ".codex-plugin/plugin.json")) || !existsSync5(skillsRoot)) {
474
1449
  return false;
475
1450
  }
476
1451
  try {
477
- return lstatSync(skillsRoot).isDirectory() && readdirSync4(skillsRoot, { withFileTypes: true }).some(
478
- (entry) => entry.isDirectory() && existsSync4(join4(skillsRoot, entry.name, "SKILL.md"))
1452
+ return lstatSync2(skillsRoot).isDirectory() && readdirSync5(skillsRoot, { withFileTypes: true }).some(
1453
+ (entry) => entry.isDirectory() && existsSync5(join5(skillsRoot, entry.name, "SKILL.md"))
479
1454
  );
480
1455
  } catch {
481
1456
  return false;
482
1457
  }
483
1458
  }
484
1459
  function isSafeRegistrySourcePath(sourcePath) {
485
- if (!sourcePath || isAbsolute(sourcePath) || sourcePath.startsWith("/")) return false;
1460
+ if (!sourcePath || isAbsolute2(sourcePath) || sourcePath.startsWith("/")) return false;
486
1461
  const normalized = normalizeRegistrySourcePath(sourcePath);
487
1462
  if (normalized === "." || normalized.startsWith("../") || normalized === "..") return false;
488
1463
  return !normalized.split("/").includes("..");
@@ -494,15 +1469,15 @@ function isWithinAgentAssetsDir(agentAssetsDir, sourceAbsolutePath) {
494
1469
  try {
495
1470
  const agentAssetsRealPath = realpathSync(agentAssetsDir);
496
1471
  const sourceRealPath = realpathSync(sourceAbsolutePath);
497
- const relativePath = relative3(agentAssetsRealPath, sourceRealPath);
498
- return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath);
1472
+ const relativePath = relative4(agentAssetsRealPath, sourceRealPath);
1473
+ return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute2(relativePath);
499
1474
  } catch {
500
1475
  return false;
501
1476
  }
502
1477
  }
503
1478
  function pathExistsEvenIfDanglingSymlink(path) {
504
1479
  try {
505
- lstatSync(path);
1480
+ lstatSync2(path);
506
1481
  return true;
507
1482
  } catch {
508
1483
  return false;
@@ -519,7 +1494,7 @@ function createAgentAssetRegistryProvenance(registry, selectedAssetIds) {
519
1494
  (left, right) => left.id < right.id ? -1 : left.id > right.id ? 1 : 0
520
1495
  )
521
1496
  });
522
- const hash = createHash2("sha256").update(JSON.stringify(canonicalRegistry)).digest("hex");
1497
+ const hash = createHash3("sha256").update(JSON.stringify(canonicalRegistry)).digest("hex");
523
1498
  return {
524
1499
  schema: "agent-assets-registry",
525
1500
  version: registry.schemaVersion,
@@ -529,8 +1504,8 @@ function createAgentAssetRegistryProvenance(registry, selectedAssetIds) {
529
1504
  }
530
1505
  function loadAgentAssetRegistry(options = {}) {
531
1506
  const agentAssetsDir = options.agentAssetsDir ?? findDefaultAgentAssetsDir();
532
- const registryPath = join5(agentAssetsDir, "registry.json");
533
- if (!existsSync5(registryPath)) {
1507
+ const registryPath = join6(agentAssetsDir, "registry.json");
1508
+ if (!existsSync6(registryPath)) {
534
1509
  return {
535
1510
  registry: { schemaVersion: 1, assets: [] },
536
1511
  agentAssetsDir,
@@ -538,7 +1513,7 @@ function loadAgentAssetRegistry(options = {}) {
538
1513
  issues: []
539
1514
  };
540
1515
  }
541
- const registry = JSON.parse(readFileSync3(registryPath, "utf8"));
1516
+ const registry = JSON.parse(readFileSync4(registryPath, "utf8"));
542
1517
  return {
543
1518
  registry,
544
1519
  agentAssetsDir,
@@ -556,15 +1531,15 @@ function createAgentAssetLockEntries(registry, agentAssetsDir, assetIds) {
556
1531
  })).sort((a, b) => a.id.localeCompare(b.id));
557
1532
  }
558
1533
  function hashAgentAssetContent(asset, agentAssetsDir) {
559
- return hashAssetPathContent(join5(agentAssetsDir, asset.sourcePath));
1534
+ return hashAssetPathContent(join6(agentAssetsDir, asset.sourcePath));
560
1535
  }
561
1536
  function hashAssetPathContent(sourceAbsolutePath) {
562
- const hash = createHash2("sha256");
563
- for (const filePath of listFiles3(sourceAbsolutePath)) {
564
- const relativePath = toUnixPath3(relative4(sourceAbsolutePath, filePath));
1537
+ const hash = createHash3("sha256");
1538
+ for (const filePath of listFiles4(sourceAbsolutePath)) {
1539
+ const relativePath = toUnixPath3(relative5(sourceAbsolutePath, filePath));
565
1540
  hash.update(relativePath);
566
1541
  hash.update("\0");
567
- hash.update(readFileSync3(filePath));
1542
+ hash.update(readFileSync4(filePath));
568
1543
  hash.update("\0");
569
1544
  }
570
1545
  return `sha256:${hash.digest("hex")}`;
@@ -579,40 +1554,40 @@ function canonicalizeValue(value) {
579
1554
  return value;
580
1555
  }
581
1556
  function findDefaultAgentAssetsDir() {
582
- const packageRoot2 = findPackageRoot(dirname2(fileURLToPath2(import.meta.url)));
583
- const repoRoot = join5(packageRoot2, "..", "..");
1557
+ const packageRoot2 = findPackageRoot(dirname3(fileURLToPath2(import.meta.url)));
1558
+ const repoRoot = join6(packageRoot2, "..", "..");
584
1559
  const candidates = [
585
- join5(packageRoot2, "assets/agent-assets"),
586
- join5(repoRoot, "agent-assets"),
587
- join5(packageRoot2, "assets/public-agent-assets"),
588
- join5(repoRoot, "public-agent-assets")
1560
+ join6(packageRoot2, "assets/agent-assets"),
1561
+ join6(repoRoot, "agent-assets"),
1562
+ join6(packageRoot2, "assets/public-agent-assets"),
1563
+ join6(repoRoot, "public-agent-assets")
589
1564
  ];
590
- return candidates.find((candidate) => existsSync5(join5(candidate, "registry.json"))) ?? candidates[0];
1565
+ return candidates.find((candidate) => existsSync6(join6(candidate, "registry.json"))) ?? candidates[0];
591
1566
  }
592
1567
  function findPackageRoot(startDir) {
593
1568
  let current = startDir;
594
- while (current !== dirname2(current)) {
595
- const packageJsonPath = join5(current, "package.json");
596
- if (existsSync5(packageJsonPath)) {
1569
+ while (current !== dirname3(current)) {
1570
+ const packageJsonPath = join6(current, "package.json");
1571
+ if (existsSync6(packageJsonPath)) {
597
1572
  try {
598
- const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
1573
+ const packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
599
1574
  if (packageJson.name === "@pieai/pro-gov") return current;
600
1575
  } catch {
601
1576
  }
602
1577
  }
603
- current = dirname2(current);
1578
+ current = dirname3(current);
604
1579
  }
605
1580
  return startDir;
606
1581
  }
607
- function listFiles3(absolutePath) {
608
- const stats = statSync2(absolutePath);
1582
+ function listFiles4(absolutePath) {
1583
+ const stats = statSync3(absolutePath);
609
1584
  if (stats.isFile()) return [absolutePath];
610
1585
  const files = [];
611
- for (const entry of readdirSync5(absolutePath, { withFileTypes: true })) {
1586
+ for (const entry of readdirSync6(absolutePath, { withFileTypes: true })) {
612
1587
  if (shouldIgnoreAssetHashEntry(entry.name)) continue;
613
- const entryPath = join5(absolutePath, entry.name);
1588
+ const entryPath = join6(absolutePath, entry.name);
614
1589
  if (entry.isDirectory()) {
615
- files.push(...listFiles3(entryPath));
1590
+ files.push(...listFiles4(entryPath));
616
1591
  } else if (entry.isFile()) {
617
1592
  files.push(entryPath);
618
1593
  }
@@ -627,8 +1602,8 @@ function toUnixPath3(path) {
627
1602
  }
628
1603
 
629
1604
  // src/asset-registry/public-promotion.ts
630
- import { existsSync as existsSync6 } from "node:fs";
631
- import { isAbsolute as isAbsolute2, join as join6, posix as posix2 } from "node:path";
1605
+ import { existsSync as existsSync7 } from "node:fs";
1606
+ import { isAbsolute as isAbsolute3, join as join7, posix as posix2 } from "node:path";
632
1607
  function checkPublicAssetPromotions(options) {
633
1608
  const issues = [];
634
1609
  let checked = 0;
@@ -666,7 +1641,7 @@ function checkPublicAssetPromotions(options) {
666
1641
  });
667
1642
  continue;
668
1643
  }
669
- if (!existsSync6(privatePathResult.path)) {
1644
+ if (!existsSync7(privatePathResult.path)) {
670
1645
  issues.push({
671
1646
  type: "missing-private-source",
672
1647
  id: asset.id,
@@ -686,7 +1661,7 @@ function checkPublicAssetPromotions(options) {
686
1661
  });
687
1662
  }
688
1663
  }
689
- if (!existsSync6(publicPathResult.path)) {
1664
+ if (!existsSync7(publicPathResult.path)) {
690
1665
  issues.push({
691
1666
  type: "missing-public-source",
692
1667
  id: asset.id,
@@ -713,32 +1688,39 @@ function needsPromotionCheck(asset) {
713
1688
  return asset.visibility === "public" && asset.publishable;
714
1689
  }
715
1690
  function resolveSafePath(root, sourcePath) {
716
- if (!sourcePath || isAbsolute2(sourcePath) || sourcePath.startsWith("/")) return { ok: false };
1691
+ if (!sourcePath || isAbsolute3(sourcePath) || sourcePath.startsWith("/")) return { ok: false };
717
1692
  const normalized = posix2.normalize(sourcePath.replaceAll("\\", "/"));
718
1693
  if (normalized === "." || normalized === ".." || normalized.startsWith("../")) {
719
1694
  return { ok: false };
720
1695
  }
721
1696
  if (normalized.split("/").includes("..")) return { ok: false };
722
- return { ok: true, path: join6(root, normalized) };
1697
+ return { ok: true, path: join7(root, normalized) };
723
1698
  }
724
1699
 
725
1700
  // src/asset-targets/apply.ts
726
- import { createHash as createHash3 } from "node:crypto";
1701
+ import { createHash as createHash4 } from "node:crypto";
727
1702
  import {
728
- existsSync as existsSync8,
729
- lstatSync as lstatSync3,
730
- mkdirSync as mkdirSync2,
1703
+ existsSync as existsSync9,
1704
+ lstatSync as lstatSync4,
1705
+ mkdirSync as mkdirSync3,
731
1706
  readlinkSync as readlinkSync2,
732
1707
  realpathSync as realpathSync3,
733
- symlinkSync,
1708
+ symlinkSync as symlinkSync2,
734
1709
  unlinkSync,
735
- writeFileSync
1710
+ writeFileSync as writeFileSync2
736
1711
  } from "node:fs";
737
- import { dirname as dirname4, join as join8, relative as relative5, resolve as resolve2 } from "node:path";
1712
+ import { dirname as dirname5, join as join9, relative as relative6, resolve as resolve3 } from "node:path";
1713
+
1714
+ // src/asset-targets/install-plan.ts
1715
+ import { existsSync as existsSync8, lstatSync as lstatSync3, readFileSync as readFileSync5, readlinkSync, realpathSync as realpathSync2, statSync as statSync4 } from "node:fs";
1716
+ import { basename as basename2, dirname as dirname4, join as join8, resolve as resolve2 } from "node:path";
1717
+
1718
+ // src/symlinks.ts
1719
+ function normalizeSymlinkTarget(target) {
1720
+ return target.replaceAll("\\", "/");
1721
+ }
738
1722
 
739
1723
  // src/asset-targets/install-plan.ts
740
- import { existsSync as existsSync7, lstatSync as lstatSync2, readFileSync as readFileSync4, readlinkSync, realpathSync as realpathSync2, statSync as statSync3 } from "node:fs";
741
- import { basename, dirname as dirname3, join as join7, resolve } from "node:path";
742
1724
  function createAssetInstallPlan(options) {
743
1725
  const placement = options.placement ?? "registry";
744
1726
  const assetsById = new Map(options.registry.assets.map((asset) => [asset.id, asset]));
@@ -858,9 +1840,9 @@ function createAssetAction(asset, agentAssetsDir, targetDir, host, placement, ma
858
1840
  `User-scoped asset ${asset.id} must be linked at the user level, not installed into a project target.`
859
1841
  );
860
1842
  }
861
- const sourcePath = join7(agentAssetsDir, asset.sourcePath);
1843
+ const sourcePath = join8(agentAssetsDir, asset.sourcePath);
862
1844
  const targetPath = resolveHostTargetPath(asset, host, placement);
863
- const targetAbsolutePath = join7(targetDir, targetPath);
1845
+ const targetAbsolutePath = join8(targetDir, targetPath);
864
1846
  const targetExists = pathExistsEvenIfDanglingSymlink2(targetAbsolutePath);
865
1847
  const managedEntry = managedEntries.find(
866
1848
  (entry) => entry.id === asset.id && entry.targetPath === targetPath
@@ -877,7 +1859,7 @@ function createAssetAction(asset, agentAssetsDir, targetDir, host, placement, ma
877
1859
  });
878
1860
  }
879
1861
  if (targetExists) {
880
- const stats = lstatSync2(targetAbsolutePath);
1862
+ const stats = lstatSync3(targetAbsolutePath);
881
1863
  if (stats.isSymbolicLink() && managedEntry && (managedEntry.delivery ?? "symlink") === "symlink") {
882
1864
  return {
883
1865
  type: "update-symlink",
@@ -889,7 +1871,7 @@ function createAssetAction(asset, agentAssetsDir, targetDir, host, placement, ma
889
1871
  if (managedEntry && managedEntry.delivery === "snapshot") {
890
1872
  throw new Error(`Refusing to replace managed snapshot with a symlink: ${targetPath}`);
891
1873
  }
892
- if (stats.isSymbolicLink() && existsSync7(targetAbsolutePath) && realpathSync2(targetAbsolutePath) === realpathSync2(sourcePath)) {
1874
+ if (stats.isSymbolicLink() && existsSync8(targetAbsolutePath) && realpathSync2(targetAbsolutePath) === realpathSync2(sourcePath)) {
893
1875
  return {
894
1876
  type: "adopt-existing-symlink",
895
1877
  assetId: asset.id,
@@ -912,10 +1894,10 @@ function createSnapshotAction(options) {
912
1894
  `Snapshot delivery requires a rule target under docs/policy/shared-rules/: ${options.asset.id}`
913
1895
  );
914
1896
  }
915
- if (!statSync3(options.sourcePath).isFile()) {
1897
+ if (!statSync4(options.sourcePath).isFile()) {
916
1898
  throw new Error(`Snapshot source must be a regular file: ${options.asset.sourcePath}`);
917
1899
  }
918
- const content = readFileSync4(options.sourcePath);
1900
+ const content = readFileSync5(options.sourcePath);
919
1901
  const contentBase64 = content.toString("base64");
920
1902
  const contentHash = hashAgentAssetContent(options.asset, options.agentAssetsDir);
921
1903
  const managedDelivery = options.managedEntry?.delivery ?? "symlink";
@@ -928,9 +1910,9 @@ function createSnapshotAction(options) {
928
1910
  contentHash
929
1911
  };
930
1912
  }
931
- const stats = lstatSync2(options.targetAbsolutePath);
1913
+ const stats = lstatSync3(options.targetAbsolutePath);
932
1914
  if (stats.isSymbolicLink()) {
933
- if (existsSync7(options.targetAbsolutePath) && realpathSync2(options.targetAbsolutePath) === realpathSync2(options.sourcePath) && hashAssetPathContent(options.targetAbsolutePath) === contentHash) {
1915
+ if (existsSync8(options.targetAbsolutePath) && realpathSync2(options.targetAbsolutePath) === realpathSync2(options.sourcePath) && hashAssetPathContent(options.targetAbsolutePath) === contentHash) {
934
1916
  return {
935
1917
  type: "migrate-symlink-to-snapshot",
936
1918
  assetId: options.asset.id,
@@ -986,9 +1968,9 @@ function resolveHostTargetPath(asset, _host, placement) {
986
1968
  return `.agents/skills/${assetSkillInstallName(asset)}`;
987
1969
  }
988
1970
  if (asset.kind === "rule") {
989
- return `.pro-gov/agent-assets/rules/${basename(asset.sourcePath)}`;
1971
+ return `.pro-gov/agent-assets/rules/${basename2(asset.sourcePath)}`;
990
1972
  }
991
- return `.pro-gov/agent-assets/commands/${basename(asset.sourcePath)}`;
1973
+ return `.pro-gov/agent-assets/commands/${basename2(asset.sourcePath)}`;
992
1974
  }
993
1975
  function resolveSkillPlacement(asset, placement) {
994
1976
  if (placement !== "registry") return placement;
@@ -998,16 +1980,16 @@ function createDirectoryActions(actions) {
998
1980
  const directories = /* @__PURE__ */ new Set();
999
1981
  for (const action of actions) {
1000
1982
  if (action.type === "create-dir") continue;
1001
- const directory = dirname3(action.targetPath);
1983
+ const directory = dirname4(action.targetPath);
1002
1984
  if (directory !== ".") directories.add(directory);
1003
1985
  }
1004
1986
  return [...directories].sort().map((targetPath) => ({ type: "create-dir", targetPath }));
1005
1987
  }
1006
1988
  function readManagedLock(targetDir) {
1007
- const lockfilePath = join7(targetDir, ".pro-gov/assets.lock.json");
1008
- if (!existsSync7(lockfilePath)) return { entries: [] };
1989
+ const lockfilePath = join8(targetDir, ".pro-gov/assets.lock.json");
1990
+ if (!existsSync8(lockfilePath)) return { entries: [] };
1009
1991
  try {
1010
- const lockfile = JSON.parse(readFileSync4(lockfilePath, "utf8"));
1992
+ const lockfile = JSON.parse(readFileSync5(lockfilePath, "utf8"));
1011
1993
  return {
1012
1994
  host: typeof lockfile.host === "string" ? lockfile.host : void 0,
1013
1995
  entries: (lockfile.assets ?? []).filter(
@@ -1045,10 +2027,10 @@ function createLegacyClaudeAdoptions(options) {
1045
2027
  throw new Error(`Legacy Claude lock entry cannot be safely normalized: ${entry.targetPath}`);
1046
2028
  }
1047
2029
  const targetPath = `.agents/skills/${skillName}`;
1048
- const targetAbsolutePath = join7(options.targetDir, targetPath);
1049
- const compatibilityRootPath = join7(options.targetDir, ".claude/skills");
1050
- const canonicalRootPath = join7(options.targetDir, ".agents/skills");
1051
- if (!lstatSync2(canonicalRootPath).isDirectory() || !lstatSync2(compatibilityRootPath).isSymbolicLink() || readlinkSync(compatibilityRootPath) !== "../.agents/skills" || realpathSync2(compatibilityRootPath) !== realpathSync2(canonicalRootPath)) {
2030
+ const targetAbsolutePath = join8(options.targetDir, targetPath);
2031
+ const compatibilityRootPath = join8(options.targetDir, ".claude/skills");
2032
+ const canonicalRootPath = join8(options.targetDir, ".agents/skills");
2033
+ if (!lstatSync3(canonicalRootPath).isDirectory() || !lstatSync3(compatibilityRootPath).isSymbolicLink() || normalizeSymlinkTarget(readlinkSync(compatibilityRootPath)) !== "../.agents/skills" || realpathSync2(compatibilityRootPath) !== realpathSync2(canonicalRootPath)) {
1052
2034
  throw new Error(
1053
2035
  `Legacy Claude compatibility root is not the exact canonical alias for ${entry.id}.`
1054
2036
  );
@@ -1057,10 +2039,10 @@ function createLegacyClaudeAdoptions(options) {
1057
2039
  consumedTargetPaths.add(entry.targetPath);
1058
2040
  continue;
1059
2041
  }
1060
- const targetStat = lstatSync2(targetAbsolutePath);
1061
- const legacyAbsolutePath = join7(options.targetDir, entry.targetPath);
1062
- const legacyStat = lstatSync2(legacyAbsolutePath);
1063
- const expectedSourcePath = join7(options.agentAssetsDir, entry.sourcePath);
2042
+ const targetStat = lstatSync3(targetAbsolutePath);
2043
+ const legacyAbsolutePath = join8(options.targetDir, entry.targetPath);
2044
+ const legacyStat = lstatSync3(legacyAbsolutePath);
2045
+ const expectedSourcePath = join8(options.agentAssetsDir, entry.sourcePath);
1064
2046
  if (!targetStat.isSymbolicLink() || !legacyStat.isSymbolicLink() || targetStat.dev !== legacyStat.dev || targetStat.ino !== legacyStat.ino || realpathSync2(targetAbsolutePath) !== realpathSync2(expectedSourcePath)) {
1065
2047
  throw new Error(`Legacy Claude skill target cannot be safely adopted: ${entry.targetPath}`);
1066
2048
  }
@@ -1090,9 +2072,9 @@ function createRemovalActions(targetDir, agentAssetsDir, managedEntries, expecte
1090
2072
  `Refusing to remove managed asset outside supported roots: ${entry.targetPath}`
1091
2073
  );
1092
2074
  }
1093
- const targetAbsolutePath = join7(targetDir, entry.targetPath);
2075
+ const targetAbsolutePath = join8(targetDir, entry.targetPath);
1094
2076
  if (!pathExistsEvenIfDanglingSymlink2(targetAbsolutePath)) continue;
1095
- const stats = lstatSync2(targetAbsolutePath);
2077
+ const stats = lstatSync3(targetAbsolutePath);
1096
2078
  if ((entry.delivery ?? "symlink") === "snapshot") {
1097
2079
  if (!isValidSnapshotProjectTargetPath(entry.targetPath)) {
1098
2080
  throw new Error(
@@ -1121,9 +2103,9 @@ function createRemovalActions(targetDir, agentAssetsDir, managedEntries, expecte
1121
2103
  `Refusing to remove path that is no longer a managed symlink: ${entry.targetPath}`
1122
2104
  );
1123
2105
  }
1124
- const expectedSourcePath = join7(agentAssetsDir, entry.sourcePath);
1125
- const actualSourcePath = resolve(dirname3(targetAbsolutePath), readlinkSync(targetAbsolutePath));
1126
- if (actualSourcePath !== resolve(expectedSourcePath)) {
2106
+ const expectedSourcePath = join8(agentAssetsDir, entry.sourcePath);
2107
+ const actualSourcePath = resolve2(dirname4(targetAbsolutePath), readlinkSync(targetAbsolutePath));
2108
+ if (actualSourcePath !== resolve2(expectedSourcePath)) {
1127
2109
  throw new Error(
1128
2110
  `Refusing to remove managed symlink with changed target: ${entry.targetPath}`
1129
2111
  );
@@ -1147,7 +2129,7 @@ function isLegacyClaudeSkillTargetPath(path) {
1147
2129
  }
1148
2130
  function pathExistsEvenIfDanglingSymlink2(path) {
1149
2131
  try {
1150
- lstatSync2(path);
2132
+ lstatSync3(path);
1151
2133
  return true;
1152
2134
  } catch {
1153
2135
  return false;
@@ -1171,7 +2153,7 @@ function applyAssetInstallPlan(plan) {
1171
2153
  return { appliedActions };
1172
2154
  }
1173
2155
  function applyAction(targetDir, action) {
1174
- const targetAbsolutePath = join8(targetDir, action.targetPath);
2156
+ const targetAbsolutePath = join9(targetDir, action.targetPath);
1175
2157
  if (action.type === "remove-symlink") {
1176
2158
  removeManagedSymlink(targetAbsolutePath, action);
1177
2159
  return;
@@ -1182,41 +2164,41 @@ function applyAction(targetDir, action) {
1182
2164
  return;
1183
2165
  }
1184
2166
  if (action.type === "create-dir") {
1185
- mkdirSync2(targetAbsolutePath, { recursive: true });
2167
+ mkdirSync3(targetAbsolutePath, { recursive: true });
1186
2168
  return;
1187
2169
  }
1188
2170
  if (action.type === "write-file") {
1189
- mkdirSync2(dirname4(targetAbsolutePath), { recursive: true });
1190
- writeFileSync(targetAbsolutePath, action.content);
2171
+ mkdirSync3(dirname5(targetAbsolutePath), { recursive: true });
2172
+ writeFileSync2(targetAbsolutePath, action.content);
1191
2173
  return;
1192
2174
  }
1193
- mkdirSync2(dirname4(targetAbsolutePath), { recursive: true });
1194
- const sourceAbsolutePath = resolve2(action.sourcePath);
1195
- const symlinkTarget = relative5(realpathSync3(dirname4(targetAbsolutePath)), realpathSync3(sourceAbsolutePath)) || ".";
2175
+ mkdirSync3(dirname5(targetAbsolutePath), { recursive: true });
2176
+ const sourceAbsolutePath = resolve3(action.sourcePath);
2177
+ const symlinkTarget = relative6(realpathSync3(dirname5(targetAbsolutePath)), realpathSync3(sourceAbsolutePath)) || ".";
1196
2178
  if (action.type === "symlink") {
1197
2179
  if (pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1198
2180
  throw new Error(`Refusing to overwrite unmanaged target: ${action.targetPath}`);
1199
2181
  }
1200
- symlinkSync(symlinkTarget, targetAbsolutePath);
2182
+ symlinkSync2(symlinkTarget, targetAbsolutePath);
1201
2183
  return;
1202
2184
  }
1203
2185
  if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1204
- symlinkSync(symlinkTarget, targetAbsolutePath);
2186
+ symlinkSync2(symlinkTarget, targetAbsolutePath);
1205
2187
  return;
1206
2188
  }
1207
- const stats = lstatSync3(targetAbsolutePath);
2189
+ const stats = lstatSync4(targetAbsolutePath);
1208
2190
  if (!stats.isSymbolicLink()) {
1209
2191
  throw new Error(`Refusing to overwrite unmanaged target: ${action.targetPath}`);
1210
2192
  }
1211
2193
  unlinkSync(targetAbsolutePath);
1212
- symlinkSync(symlinkTarget, targetAbsolutePath);
2194
+ symlinkSync2(symlinkTarget, targetAbsolutePath);
1213
2195
  }
1214
2196
  function validateExistingSymlink(targetDir, action) {
1215
2197
  if (!isManagedAssetTargetPath(action.targetPath)) {
1216
2198
  throw new Error(`Refusing unsafe existing symlink adoption: ${action.targetPath}`);
1217
2199
  }
1218
- const targetAbsolutePath = join8(targetDir, action.targetPath);
1219
- if (!lstatSync3(targetAbsolutePath).isSymbolicLink() || realpathSync3(targetAbsolutePath) !== realpathSync3(action.sourcePath)) {
2200
+ const targetAbsolutePath = join9(targetDir, action.targetPath);
2201
+ if (!lstatSync4(targetAbsolutePath).isSymbolicLink() || realpathSync3(targetAbsolutePath) !== realpathSync3(action.sourcePath)) {
1220
2202
  throw new Error(`Existing skill symlink changed before apply: ${action.targetPath}`);
1221
2203
  }
1222
2204
  }
@@ -1227,7 +2209,7 @@ function validateSnapshotAction(targetDir, action) {
1227
2209
  if ("contentBase64" in action && hashSnapshotBytes(Buffer.from(action.contentBase64, "base64")) !== action.contentHash) {
1228
2210
  throw new Error(`Snapshot content hash is invalid: ${action.targetPath}`);
1229
2211
  }
1230
- const targetAbsolutePath = join8(targetDir, action.targetPath);
2212
+ const targetAbsolutePath = join9(targetDir, action.targetPath);
1231
2213
  if (action.type === "snapshot") {
1232
2214
  if (pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1233
2215
  throw new Error(`Snapshot target changed before apply: ${action.targetPath}`);
@@ -1243,7 +2225,7 @@ function validateSnapshotAction(targetDir, action) {
1243
2225
  return;
1244
2226
  }
1245
2227
  if (action.type === "migrate-symlink-to-snapshot") {
1246
- if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath) || !lstatSync3(targetAbsolutePath).isSymbolicLink() || !existsSync8(targetAbsolutePath) || realpathSync3(targetAbsolutePath) !== realpathSync3(action.sourcePath) || hashAssetPathContent(targetAbsolutePath) !== action.contentHash) {
2228
+ if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath) || !lstatSync4(targetAbsolutePath).isSymbolicLink() || !existsSync9(targetAbsolutePath) || realpathSync3(targetAbsolutePath) !== realpathSync3(action.sourcePath) || hashAssetPathContent(targetAbsolutePath) !== action.contentHash) {
1247
2229
  throw new Error(`Snapshot symlink changed before apply: ${action.targetPath}`);
1248
2230
  }
1249
2231
  return;
@@ -1251,30 +2233,30 @@ function validateSnapshotAction(targetDir, action) {
1251
2233
  assertRegularSnapshotHash(targetAbsolutePath, action.expectedContentHash, action.targetPath);
1252
2234
  }
1253
2235
  function applySnapshotAction(targetDir, action) {
1254
- const targetAbsolutePath = join8(targetDir, action.targetPath);
2236
+ const targetAbsolutePath = join9(targetDir, action.targetPath);
1255
2237
  if (action.type === "adopt-snapshot") return;
1256
2238
  if (action.type === "remove-snapshot") {
1257
2239
  unlinkSync(targetAbsolutePath);
1258
2240
  return;
1259
2241
  }
1260
2242
  if (action.type === "migrate-symlink-to-snapshot") unlinkSync(targetAbsolutePath);
1261
- mkdirSync2(dirname4(targetAbsolutePath), { recursive: true });
2243
+ mkdirSync3(dirname5(targetAbsolutePath), { recursive: true });
1262
2244
  if (action.type === "snapshot" && pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1263
2245
  throw new Error(`Refusing to overwrite unmanaged target: ${action.targetPath}`);
1264
2246
  }
1265
- writeFileSync(targetAbsolutePath, Buffer.from(action.contentBase64, "base64"));
2247
+ writeFileSync2(targetAbsolutePath, Buffer.from(action.contentBase64, "base64"));
1266
2248
  }
1267
2249
  function assertRegularSnapshotHash(targetAbsolutePath, expectedHash, targetPath) {
1268
2250
  if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1269
2251
  throw new Error(`Snapshot target changed before apply: ${targetPath}`);
1270
2252
  }
1271
- const stats = lstatSync3(targetAbsolutePath);
2253
+ const stats = lstatSync4(targetAbsolutePath);
1272
2254
  if (!stats.isFile() || hashAssetPathContent(targetAbsolutePath) !== expectedHash) {
1273
2255
  throw new Error(`Snapshot target hash changed before apply: ${targetPath}`);
1274
2256
  }
1275
2257
  }
1276
2258
  function hashSnapshotBytes(content) {
1277
- const hash = createHash3("sha256");
2259
+ const hash = createHash4("sha256");
1278
2260
  hash.update("");
1279
2261
  hash.update("\0");
1280
2262
  hash.update(content);
@@ -1285,17 +2267,17 @@ function validateAdoptedSymlink(targetDir, action) {
1285
2267
  if (!isManagedAssetTargetPath(action.targetPath) || !isLegacyClaudeSkillTargetPath(action.legacyTargetPath) || action.compatibilityRootPath !== ".claude/skills" || action.expectedCompatibilityRawTarget !== "../.agents/skills") {
1286
2268
  throw new Error(`Refusing unsafe legacy Claude adoption: ${action.legacyTargetPath}`);
1287
2269
  }
1288
- const canonicalRootPath = join8(targetDir, ".agents/skills");
1289
- const compatibilityRootPath = join8(targetDir, action.compatibilityRootPath);
1290
- const targetAbsolutePath = join8(targetDir, action.targetPath);
1291
- const legacyAbsolutePath = join8(targetDir, action.legacyTargetPath);
1292
- if (!lstatSync3(canonicalRootPath).isDirectory() || !lstatSync3(compatibilityRootPath).isSymbolicLink() || readlinkSync2(compatibilityRootPath) !== action.expectedCompatibilityRawTarget || realpathSync3(compatibilityRootPath) !== realpathSync3(canonicalRootPath)) {
2270
+ const canonicalRootPath = join9(targetDir, ".agents/skills");
2271
+ const compatibilityRootPath = join9(targetDir, action.compatibilityRootPath);
2272
+ const targetAbsolutePath = join9(targetDir, action.targetPath);
2273
+ const legacyAbsolutePath = join9(targetDir, action.legacyTargetPath);
2274
+ if (!lstatSync4(canonicalRootPath).isDirectory() || !lstatSync4(compatibilityRootPath).isSymbolicLink() || normalizeSymlinkTarget(readlinkSync2(compatibilityRootPath)) !== action.expectedCompatibilityRawTarget || realpathSync3(compatibilityRootPath) !== realpathSync3(canonicalRootPath)) {
1293
2275
  throw new Error(
1294
2276
  `Legacy Claude compatibility alias changed before apply: ${action.compatibilityRootPath}`
1295
2277
  );
1296
2278
  }
1297
- const targetStat = lstatSync3(targetAbsolutePath);
1298
- const legacyStat = lstatSync3(legacyAbsolutePath);
2279
+ const targetStat = lstatSync4(targetAbsolutePath);
2280
+ const legacyStat = lstatSync4(legacyAbsolutePath);
1299
2281
  if (!targetStat.isSymbolicLink() || !legacyStat.isSymbolicLink() || targetStat.dev !== legacyStat.dev || targetStat.ino !== legacyStat.ino || targetStat.dev !== action.expectedDevice || targetStat.ino !== action.expectedInode || realpathSync3(targetAbsolutePath) !== realpathSync3(action.sourcePath)) {
1300
2282
  throw new Error(`Legacy Claude skill target changed before apply: ${action.targetPath}`);
1301
2283
  }
@@ -1307,33 +2289,33 @@ function removeManagedSymlink(targetAbsolutePath, action) {
1307
2289
  );
1308
2290
  }
1309
2291
  if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) return;
1310
- const stats = lstatSync3(targetAbsolutePath);
2292
+ const stats = lstatSync4(targetAbsolutePath);
1311
2293
  if (!stats.isSymbolicLink()) {
1312
2294
  throw new Error(
1313
2295
  `Refusing to remove path that is no longer a managed symlink: ${action.targetPath}`
1314
2296
  );
1315
2297
  }
1316
- const actualSourcePath = resolve2(dirname4(targetAbsolutePath), readlinkSync2(targetAbsolutePath));
1317
- if (actualSourcePath !== resolve2(action.expectedSourcePath)) {
2298
+ const actualSourcePath = resolve3(dirname5(targetAbsolutePath), readlinkSync2(targetAbsolutePath));
2299
+ if (actualSourcePath !== resolve3(action.expectedSourcePath)) {
1318
2300
  throw new Error(`Refusing to remove managed symlink with changed target: ${action.targetPath}`);
1319
2301
  }
1320
2302
  unlinkSync(targetAbsolutePath);
1321
2303
  }
1322
2304
  function pathExistsEvenIfDanglingSymlink3(path) {
1323
2305
  try {
1324
- lstatSync3(path);
2306
+ lstatSync4(path);
1325
2307
  return true;
1326
2308
  } catch {
1327
- return existsSync8(path);
2309
+ return existsSync9(path);
1328
2310
  }
1329
2311
  }
1330
2312
 
1331
2313
  // src/asset-targets/check.ts
1332
- import { existsSync as existsSync9, lstatSync as lstatSync4, readFileSync as readFileSync5, realpathSync as realpathSync4 } from "node:fs";
1333
- import { join as join9 } from "node:path";
2314
+ import { existsSync as existsSync10, lstatSync as lstatSync5, readFileSync as readFileSync6, realpathSync as realpathSync4 } from "node:fs";
2315
+ import { join as join10 } from "node:path";
1334
2316
  function checkInstalledAssets(options) {
1335
- const lockfilePath = join9(options.targetDir, ".pro-gov/assets.lock.json");
1336
- if (!existsSync9(lockfilePath)) {
2317
+ const lockfilePath = join10(options.targetDir, ".pro-gov/assets.lock.json");
2318
+ if (!existsSync10(lockfilePath)) {
1337
2319
  return {
1338
2320
  targetDir: options.targetDir,
1339
2321
  issues: [
@@ -1345,7 +2327,7 @@ function checkInstalledAssets(options) {
1345
2327
  };
1346
2328
  }
1347
2329
  const registryById = new Map(options.registry.assets.map((asset) => [asset.id, asset]));
1348
- const lockfile = JSON.parse(readFileSync5(lockfilePath, "utf8"));
2330
+ const lockfile = JSON.parse(readFileSync6(lockfilePath, "utf8"));
1349
2331
  const issues = [];
1350
2332
  const strictRegistry = options.strictRegistry ?? false;
1351
2333
  const selectedAssetIds = (lockfile.assets ?? []).map((entry) => entry.id);
@@ -1374,7 +2356,7 @@ function checkInstalledAssets(options) {
1374
2356
  continue;
1375
2357
  }
1376
2358
  const asset = registryById.get(entry.id);
1377
- const targetAbsolutePath = join9(options.targetDir, entry.targetPath);
2359
+ const targetAbsolutePath = join10(options.targetDir, entry.targetPath);
1378
2360
  const delivery = entry.delivery ?? "symlink";
1379
2361
  const portableDeferredSkill = delivery === "symlink" && !strictRegistry && isProjectSkillTarget(entry.targetPath);
1380
2362
  if (delivery !== "symlink" && delivery !== "snapshot") {
@@ -1436,7 +2418,7 @@ function checkInstalledAssets(options) {
1436
2418
  });
1437
2419
  continue;
1438
2420
  }
1439
- const targetStats = lstatSync4(targetAbsolutePath);
2421
+ const targetStats = lstatSync5(targetAbsolutePath);
1440
2422
  if (delivery === "snapshot" && !targetStats.isFile()) {
1441
2423
  issues.push({
1442
2424
  type: "snapshot-not-regular-file",
@@ -1455,7 +2437,7 @@ function checkInstalledAssets(options) {
1455
2437
  });
1456
2438
  continue;
1457
2439
  }
1458
- if (delivery === "symlink" && !existsSync9(targetAbsolutePath)) {
2440
+ if (delivery === "symlink" && !existsSync10(targetAbsolutePath)) {
1459
2441
  if (portableDeferredSkill) continue;
1460
2442
  issues.push({
1461
2443
  type: "dangling-symlink",
@@ -1476,8 +2458,8 @@ function checkInstalledAssets(options) {
1476
2458
  });
1477
2459
  }
1478
2460
  if (!asset || !strictRegistry || registryProvenanceMismatch) continue;
1479
- const sourceAbsolutePath = join9(options.agentAssetsDir, asset.sourcePath);
1480
- if (!existsSync9(sourceAbsolutePath)) {
2461
+ const sourceAbsolutePath = join10(options.agentAssetsDir, asset.sourcePath);
2462
+ if (!existsSync10(sourceAbsolutePath)) {
1481
2463
  issues.push({
1482
2464
  type: "missing-source",
1483
2465
  id: entry.id,
@@ -1541,7 +2523,7 @@ function checkDuplicateSkillPlacements(targetDir, registry) {
1541
2523
  const skillName = assetSkillInstallName(asset);
1542
2524
  const autoPath = `.agents/skills/${skillName}`;
1543
2525
  const manualPath = `.agents/manual-skills/${skillName}`;
1544
- if (pathExistsEvenIfDanglingSymlink4(join9(targetDir, autoPath)) && pathExistsEvenIfDanglingSymlink4(join9(targetDir, manualPath))) {
2526
+ if (pathExistsEvenIfDanglingSymlink4(join10(targetDir, autoPath)) && pathExistsEvenIfDanglingSymlink4(join10(targetDir, manualPath))) {
1545
2527
  issues.push({
1546
2528
  type: "duplicate-skill-placement",
1547
2529
  id: asset.id,
@@ -1587,7 +2569,7 @@ function expectedRegistrySkillTargetPath(host, skillName, placement) {
1587
2569
  }
1588
2570
  function pathExistsEvenIfDanglingSymlink4(path) {
1589
2571
  try {
1590
- lstatSync4(path);
2572
+ lstatSync5(path);
1591
2573
  return true;
1592
2574
  } catch {
1593
2575
  return false;
@@ -1598,8 +2580,8 @@ function isProjectSkillTarget(targetPath) {
1598
2580
  }
1599
2581
 
1600
2582
  // src/asset-targets/recommend.ts
1601
- import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync6 } from "node:fs";
1602
- import { join as join10 } from "node:path";
2583
+ import { existsSync as existsSync11, readdirSync as readdirSync7, readFileSync as readFileSync7 } from "node:fs";
2584
+ import { join as join11 } from "node:path";
1603
2585
  var frontendPackages = /* @__PURE__ */ new Set([
1604
2586
  "@vitejs/plugin-react",
1605
2587
  "astro",
@@ -1613,21 +2595,21 @@ var frontendPackages = /* @__PURE__ */ new Set([
1613
2595
  ]);
1614
2596
  var agentEntryCandidates = ["AGENTS.md", "CLAUDE.md"];
1615
2597
  function discoverTargetSignals(targetDir) {
1616
- const packageJson = readJson(join10(targetDir, "package.json"));
2598
+ const packageJson = readJson(join11(targetDir, "package.json"));
1617
2599
  const dependencyNames = packageJson ? Object.keys({ ...packageJson.dependencies, ...packageJson.devDependencies }) : [];
1618
2600
  const frontendSignals = dependencyNames.filter((name) => frontendPackages.has(name)).sort();
1619
- const hasAgentEntry = agentEntryCandidates.some((file) => existsSync10(join10(targetDir, file)));
2601
+ const hasAgentEntry = agentEntryCandidates.some((file) => existsSync11(join11(targetDir, file)));
1620
2602
  const researchSignals = [
1621
- existsSync10(join10(targetDir, "docs/research")) ? "docs/research" : "",
1622
- existsSync10(join10(targetDir, "research")) ? "research" : "",
2603
+ existsSync11(join11(targetDir, "docs/research")) ? "docs/research" : "",
2604
+ existsSync11(join11(targetDir, "research")) ? "research" : "",
1623
2605
  hasBookChildDirectory(targetDir, "research") ? "books/*/research" : "",
1624
- textFileIncludes(join10(targetDir, "README.md"), ["research", "\u8C03\u7814"]) ? "README research" : ""
2606
+ textFileIncludes(join11(targetDir, "README.md"), ["research", "\u8C03\u7814"]) ? "README research" : ""
1625
2607
  ].filter(Boolean);
1626
2608
  const writingSignals = [
1627
- existsSync10(join10(targetDir, "chapters")) ? "chapters" : "",
1628
- existsSync10(join10(targetDir, "src/chapters")) ? "src/chapters" : "",
2609
+ existsSync11(join11(targetDir, "chapters")) ? "chapters" : "",
2610
+ existsSync11(join11(targetDir, "src/chapters")) ? "src/chapters" : "",
1629
2611
  hasBookChildDirectory(targetDir, "chapters") ? "books/*/chapters" : "",
1630
- textFileIncludes(join10(targetDir, "AGENTS.md"), [
2612
+ textFileIncludes(join11(targetDir, "AGENTS.md"), [
1631
2613
  "writing mode",
1632
2614
  "novel chapter",
1633
2615
  "book content"
@@ -1672,24 +2654,24 @@ function recommendBundlesForTarget(targetDir) {
1672
2654
  return recommendations;
1673
2655
  }
1674
2656
  function readJson(path) {
1675
- if (!existsSync10(path)) return void 0;
2657
+ if (!existsSync11(path)) return void 0;
1676
2658
  try {
1677
- return JSON.parse(readFileSync6(path, "utf8"));
2659
+ return JSON.parse(readFileSync7(path, "utf8"));
1678
2660
  } catch {
1679
2661
  return void 0;
1680
2662
  }
1681
2663
  }
1682
2664
  function textFileIncludes(path, needles) {
1683
- if (!existsSync10(path)) return false;
1684
- const contents = readFileSync6(path, "utf8").toLowerCase();
2665
+ if (!existsSync11(path)) return false;
2666
+ const contents = readFileSync7(path, "utf8").toLowerCase();
1685
2667
  return needles.some((needle) => contents.includes(needle.toLowerCase()));
1686
2668
  }
1687
2669
  function hasBookChildDirectory(targetDir, childName) {
1688
- const booksDir = join10(targetDir, "books");
1689
- if (!existsSync10(booksDir)) return false;
2670
+ const booksDir = join11(targetDir, "books");
2671
+ if (!existsSync11(booksDir)) return false;
1690
2672
  try {
1691
- return readdirSync6(booksDir, { withFileTypes: true }).some(
1692
- (entry) => entry.isDirectory() && existsSync10(join10(booksDir, entry.name, childName))
2673
+ return readdirSync7(booksDir, { withFileTypes: true }).some(
2674
+ (entry) => entry.isDirectory() && existsSync11(join11(booksDir, entry.name, childName))
1693
2675
  );
1694
2676
  } catch {
1695
2677
  return false;
@@ -1723,9 +2705,67 @@ function runAssets(args) {
1723
2705
  if (subcommand2 === "npx") {
1724
2706
  return runAssetsNpx(rest);
1725
2707
  }
2708
+ if (subcommand2 === "catalog") {
2709
+ return runAssetsCatalog(rest);
2710
+ }
1726
2711
  printUsage();
1727
2712
  return 1;
1728
2713
  }
2714
+ function runAssetsCatalog(args) {
2715
+ const [operation, ...rest] = args;
2716
+ if (!operation || operation === "--help" || operation === "-h") {
2717
+ printCatalogUsage();
2718
+ return operation ? 0 : 1;
2719
+ }
2720
+ if (operation !== "build" && operation !== "check") {
2721
+ printCatalogUsage();
2722
+ return 1;
2723
+ }
2724
+ const options = parseCatalogOptions(operation, rest);
2725
+ if (!options.ok) {
2726
+ console.error(options.error);
2727
+ printCatalogUsage();
2728
+ return 1;
2729
+ }
2730
+ try {
2731
+ const loaded = loadAgentAssetRegistry(
2732
+ options.value.agentAssetsDir ? { agentAssetsDir: options.value.agentAssetsDir } : void 0
2733
+ );
2734
+ if (loaded.issues.length > 0) {
2735
+ for (const issue of loaded.issues) console.error(`${issue.type}: ${issue.message}`);
2736
+ return 1;
2737
+ }
2738
+ const catalogRoot = options.value.catalogRoot ?? join12(loaded.agentAssetsDir, "#catalogs");
2739
+ const common = {
2740
+ agentAssetsDir: loaded.agentAssetsDir,
2741
+ catalogRoot,
2742
+ registry: loaded.registry,
2743
+ bundles: loadAgentAssetBundles(loaded.agentAssetsDir)
2744
+ };
2745
+ if (operation === "check") {
2746
+ const result2 = checkAssetCatalog(common);
2747
+ if (options.value.json) console.log(JSON.stringify(result2, null, 2));
2748
+ else if (result2.issues.length === 0)
2749
+ console.log(`asset catalog check passed (${result2.assetCount} assets)`);
2750
+ else for (const issue of result2.issues) console.log(issue);
2751
+ return result2.issues.length === 0 ? 0 : 1;
2752
+ }
2753
+ const result = buildAssetCatalog({ ...common, nativeLinks: options.value.nativeLinks });
2754
+ if (options.value.json) console.log(JSON.stringify(result, null, 2));
2755
+ else {
2756
+ console.log(`catalog: ${result.catalogRoot}`);
2757
+ console.log(`assets: ${result.assetCount}`);
2758
+ console.log(`topics: ${result.topicCount}`);
2759
+ console.log(`portable-files: ${result.portableFileCount}`);
2760
+ console.log(`native-links: ${result.nativeLinkCount}`);
2761
+ for (const warning of result.warnings) console.warn(`warning: ${warning}`);
2762
+ }
2763
+ return 0;
2764
+ } catch (error) {
2765
+ console.error(error instanceof Error ? error.message : String(error));
2766
+ return 1;
2767
+ }
2768
+ }
1729
2769
  function runAssetsPublicCheck(args) {
1730
2770
  const options = parsePublicCheckOptions(args);
1731
2771
  if (!options.ok) {
@@ -1805,7 +2845,7 @@ function runAssetsApply(args) {
1805
2845
  return 1;
1806
2846
  }
1807
2847
  try {
1808
- const plan = JSON.parse(readFileSync7(options.value.planPath, "utf8"));
2848
+ const plan = JSON.parse(readFileSync8(options.value.planPath, "utf8"));
1809
2849
  const result = applyAssetInstallPlan(plan);
1810
2850
  console.log(`applied-actions: ${result.appliedActions.length}`);
1811
2851
  return 0;
@@ -1930,8 +2970,8 @@ function runAssetsPlan(args) {
1930
2970
  console.log("dry-run: true");
1931
2971
  }
1932
2972
  if (options.value.outPath) {
1933
- mkdirSync3(dirname5(options.value.outPath), { recursive: true });
1934
- writeFileSync2(options.value.outPath, `${JSON.stringify(plan, null, 2)}
2973
+ mkdirSync4(dirname6(options.value.outPath), { recursive: true });
2974
+ writeFileSync3(options.value.outPath, `${JSON.stringify(plan, null, 2)}
1935
2975
  `);
1936
2976
  if (!options.value.json) {
1937
2977
  console.log(`plan: ${options.value.outPath}`);
@@ -1943,6 +2983,33 @@ function runAssetsPlan(args) {
1943
2983
  return 1;
1944
2984
  }
1945
2985
  }
2986
+ function parseCatalogOptions(operation, args) {
2987
+ const options = { nativeLinks: false, json: false };
2988
+ for (let index = 0; index < args.length; index += 1) {
2989
+ const arg = args[index];
2990
+ if (arg === "--agent-assets") {
2991
+ const value = args[index + 1];
2992
+ if (!value) return { ok: false, error: "Expected --agent-assets <path>" };
2993
+ options.agentAssetsDir = value;
2994
+ index += 1;
2995
+ } else if (arg === "--catalog-root") {
2996
+ const value = args[index + 1];
2997
+ if (!value) return { ok: false, error: "Expected --catalog-root <path>" };
2998
+ options.catalogRoot = value;
2999
+ index += 1;
3000
+ } else if (arg === "--native-links") {
3001
+ if (operation !== "build") {
3002
+ return { ok: false, error: "--native-links is only valid with assets catalog build" };
3003
+ }
3004
+ options.nativeLinks = true;
3005
+ } else if (arg === "--json") {
3006
+ options.json = true;
3007
+ } else {
3008
+ return { ok: false, error: `Unknown assets catalog option: ${arg}` };
3009
+ }
3010
+ }
3011
+ return { ok: true, value: options };
3012
+ }
1946
3013
  function parseListOptions(args) {
1947
3014
  const options = {
1948
3015
  json: false,
@@ -2129,10 +3196,10 @@ function parsePublicCheckOptions(args) {
2129
3196
  return { ok: false, error: `Unknown assets public-check option: ${arg}` };
2130
3197
  }
2131
3198
  }
2132
- if (!existsSync11(options.publicRoot)) {
3199
+ if (!existsSync12(options.publicRoot)) {
2133
3200
  return { ok: false, error: `Public agent assets root does not exist: ${options.publicRoot}` };
2134
3201
  }
2135
- if (!existsSync11(options.privateRoot)) {
3202
+ if (!existsSync12(options.privateRoot)) {
2136
3203
  return { ok: false, error: `Private agent assets root does not exist: ${options.privateRoot}` };
2137
3204
  }
2138
3205
  return { ok: true, value: options };
@@ -2141,13 +3208,13 @@ function getDefaultPublicCheckRoots() {
2141
3208
  const defaultRegistryRoot = loadAgentAssetRegistry().agentAssetsDir;
2142
3209
  if (defaultRegistryRoot.endsWith("public-agent-assets")) {
2143
3210
  return {
2144
- privateRoot: join11(dirname5(defaultRegistryRoot), "agent-assets"),
3211
+ privateRoot: join12(dirname6(defaultRegistryRoot), "agent-assets"),
2145
3212
  publicRoot: defaultRegistryRoot
2146
3213
  };
2147
3214
  }
2148
3215
  return {
2149
3216
  privateRoot: defaultRegistryRoot,
2150
- publicRoot: join11(dirname5(defaultRegistryRoot), "public-agent-assets")
3217
+ publicRoot: join12(dirname6(defaultRegistryRoot), "public-agent-assets")
2151
3218
  };
2152
3219
  }
2153
3220
  function listRegistryAssets(options) {
@@ -2208,6 +3275,9 @@ function printUsage() {
2208
3275
  );
2209
3276
  console.error(" pro-gov assets npx add <source> [--skill <name>] --plan [--root <path>]");
2210
3277
  console.error(" pro-gov assets npx update [--skill <name>] --plan [--root <path>]");
3278
+ console.error(
3279
+ " pro-gov assets catalog build|check [--agent-assets <path>] [--catalog-root <path>] [--native-links] [--json]"
3280
+ );
2211
3281
  }
2212
3282
  function printNpxUsage() {
2213
3283
  console.log("Usage: pro-gov assets npx add <source> [--skill <name>] --plan [--root <path>]");
@@ -2215,12 +3285,19 @@ function printNpxUsage() {
2215
3285
  console.log("");
2216
3286
  console.log("Runs npx skills only in a temporary copy and prints a reviewable plan.");
2217
3287
  }
3288
+ function printCatalogUsage() {
3289
+ console.log(
3290
+ "Usage: pro-gov assets catalog build [--agent-assets <path>] [--catalog-root <path>] [--native-links] [--json]"
3291
+ );
3292
+ console.log(
3293
+ "Usage: pro-gov assets catalog check [--agent-assets <path>] [--catalog-root <path>] [--json]"
3294
+ );
3295
+ }
2218
3296
 
2219
3297
  // src/commands/doctor.ts
2220
- import { spawnSync as spawnSync2 } from "node:child_process";
2221
- import { existsSync as existsSync12 } from "node:fs";
3298
+ import { existsSync as existsSync13 } from "node:fs";
2222
3299
  import { createRequire } from "node:module";
2223
- import { dirname as dirname6, join as join12 } from "node:path";
3300
+ import { dirname as dirname7, join as join13 } from "node:path";
2224
3301
  var REQUIRED_ASSETS = [
2225
3302
  "starter/.agents/skills/.gitkeep",
2226
3303
  "starter/AGENTS.template.md",
@@ -2252,10 +3329,12 @@ function runDoctor(args) {
2252
3329
  return runPackageDoctor(args);
2253
3330
  }
2254
3331
  function checkDocGov(options = {}) {
2255
- const run = options.run ?? ((command2, args) => spawnSync2(command2, args, {
2256
- encoding: "utf8",
2257
- stdio: "ignore"
2258
- }));
3332
+ const run = options.run ?? ((command2, args) => {
3333
+ return spawnPlatformSync(command2, args, {
3334
+ encoding: "utf8",
3335
+ stdio: "ignore"
3336
+ });
3337
+ });
2259
3338
  const dependencyCli = (options.resolveDependencyCli ?? resolveDocGovDependencyCli)();
2260
3339
  if (!dependencyCli) {
2261
3340
  const fromPath = run("doc-gov", ["--help"]);
@@ -2283,16 +3362,16 @@ function resolveDocGovDependencyCli() {
2283
3362
  try {
2284
3363
  const require2 = createRequire(import.meta.url);
2285
3364
  const packageJsonPath = require2.resolve("@pieai/doc-gov/package.json");
2286
- const cliPath = join12(dirname6(packageJsonPath), "dist/cli.js");
2287
- return existsSync12(cliPath) ? cliPath : null;
3365
+ const cliPath = join13(dirname7(packageJsonPath), "dist/cli.js");
3366
+ return existsSync13(cliPath) ? cliPath : null;
2288
3367
  } catch {
2289
3368
  return null;
2290
3369
  }
2291
3370
  }
2292
3371
 
2293
3372
  // src/commands/init.ts
2294
- import { lstatSync as lstatSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync8, symlinkSync as symlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
2295
- import { basename as basename2, dirname as dirname7, join as join13 } from "node:path";
3373
+ import { lstatSync as lstatSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync9, symlinkSync as symlinkSync3, writeFileSync as writeFileSync4 } from "node:fs";
3374
+ import { basename as basename3, dirname as dirname8, join as join14 } from "node:path";
2296
3375
 
2297
3376
  // src/commands/shared.ts
2298
3377
  function planStarterFiles(profile) {
@@ -2379,7 +3458,7 @@ function runInit(args) {
2379
3458
  function applyStarterFiles(files, profile) {
2380
3459
  const root = process.cwd();
2381
3460
  const conflicts = files.filter((file) => {
2382
- const targetPath = join13(root, file.targetPath);
3461
+ const targetPath = join14(root, file.targetPath);
2383
3462
  const stat = safeLstat(targetPath);
2384
3463
  if (!stat) return false;
2385
3464
  return file.kind !== "directory" || !stat.isDirectory();
@@ -2391,19 +3470,19 @@ function applyStarterFiles(files, profile) {
2391
3470
  return 1;
2392
3471
  }
2393
3472
  for (const file of files) {
2394
- const targetPath = join13(root, file.targetPath);
2395
- mkdirSync4(dirname7(targetPath), { recursive: true });
3473
+ const targetPath = join14(root, file.targetPath);
3474
+ mkdirSync5(dirname8(targetPath), { recursive: true });
2396
3475
  if (file.kind === "directory") {
2397
- mkdirSync4(targetPath, { recursive: true });
3476
+ mkdirSync5(targetPath, { recursive: true });
2398
3477
  continue;
2399
3478
  }
2400
3479
  if (file.kind === "symlink") {
2401
- symlinkSync2(file.linkTarget, targetPath);
3480
+ symlinkSync3(file.linkTarget, targetPath);
2402
3481
  continue;
2403
3482
  }
2404
- const source = readFileSync8(file.absoluteSourcePath);
2405
- const content = file.targetPath === "AGENTS.md" ? renderAgentsTemplate(source.toString("utf8"), basename2(root), profile) : source;
2406
- writeFileSync3(targetPath, content);
3483
+ const source = readFileSync9(file.absoluteSourcePath);
3484
+ const content = file.targetPath === "AGENTS.md" ? renderAgentsTemplate(source.toString("utf8"), basename3(root), profile) : source;
3485
+ writeFileSync4(targetPath, content);
2407
3486
  }
2408
3487
  console.log("pro-gov init APPLIED");
2409
3488
  console.log(`profile: ${profile}`);
@@ -2420,7 +3499,7 @@ function renderAgentsTemplate(template, projectName, profile) {
2420
3499
  }
2421
3500
  function safeLstat(path) {
2422
3501
  try {
2423
- return lstatSync5(path);
3502
+ return lstatSync6(path);
2424
3503
  } catch {
2425
3504
  return void 0;
2426
3505
  }
@@ -2434,22 +3513,22 @@ function readFlag(args, flag) {
2434
3513
  }
2435
3514
 
2436
3515
  // src/commands/host-lens.ts
2437
- import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "node:fs";
2438
- import { dirname as dirname10, resolve as resolve5 } from "node:path";
3516
+ import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "node:fs";
3517
+ import { dirname as dirname11, resolve as resolve6 } from "node:path";
2439
3518
 
2440
3519
  // src/host-lens.ts
2441
3520
  import { execFileSync } from "node:child_process";
2442
3521
  import {
2443
3522
  cpSync as cpSync2,
2444
- existsSync as existsSync13,
2445
- lstatSync as lstatSync6,
2446
- mkdirSync as mkdirSync5,
2447
- readdirSync as readdirSync7,
2448
- statSync as statSync4,
2449
- writeFileSync as writeFileSync4
3523
+ existsSync as existsSync14,
3524
+ lstatSync as lstatSync7,
3525
+ mkdirSync as mkdirSync6,
3526
+ readdirSync as readdirSync8,
3527
+ statSync as statSync5,
3528
+ writeFileSync as writeFileSync5
2450
3529
  } from "node:fs";
2451
3530
  import { homedir } from "node:os";
2452
- import { dirname as dirname8, join as join14, relative as relative6, resolve as resolve3 } from "node:path";
3531
+ import { dirname as dirname9, join as join15, relative as relative7, resolve as resolve4 } from "node:path";
2453
3532
  import { fileURLToPath as fileURLToPath3 } from "node:url";
2454
3533
  var ROOT_DEFINITIONS = [
2455
3534
  {
@@ -2504,36 +3583,36 @@ var ROOT_DEFINITIONS = [
2504
3583
  {
2505
3584
  id: "pnpm-store",
2506
3585
  label: "pnpm Store",
2507
- relativePath: "Library/pnpm/store",
3586
+ relativePath: process.platform === "win32" ? "AppData/Local/pnpm/store" : "Library/pnpm/store",
2508
3587
  kind: "package-cache",
2509
3588
  note: "pnpm \u5185\u5BB9\u5BFB\u5740\u4ED3\u5E93\uFF1B\u6E05\u7406\u65F6\u53EA\u5EFA\u8BAE pnpm store prune\u3002"
2510
3589
  },
2511
3590
  {
2512
3591
  id: "playwright",
2513
3592
  label: "Playwright \u6D4F\u89C8\u5668",
2514
- relativePath: "Library/Caches/ms-playwright",
3593
+ relativePath: process.platform === "win32" ? "AppData/Local/ms-playwright" : "Library/Caches/ms-playwright",
2515
3594
  kind: "browser-cache",
2516
3595
  note: "\u6D4B\u8BD5\u6D4F\u89C8\u5668\u4E8C\u8FDB\u5236\u7F13\u5B58\uFF1B\u7248\u672C\u5806\u79EF\u53EF\u80FD\u9020\u6210\u663E\u8457\u78C1\u76D8\u5360\u7528\u3002"
2517
3596
  },
2518
3597
  {
2519
3598
  id: "claude-desktop",
2520
3599
  label: "Claude Desktop",
2521
- relativePath: "Library/Application Support/Claude",
3600
+ relativePath: process.platform === "win32" ? "AppData/Roaming/Claude" : "Library/Application Support/Claude",
2522
3601
  kind: "ai-host",
2523
3602
  note: "\u684C\u9762\u5E94\u7528\u8FD0\u884C\u6570\u636E\uFF0C\u9ED8\u8BA4\u53D7\u4FDD\u62A4\u3002"
2524
3603
  },
2525
3604
  {
2526
3605
  id: "cursor-desktop",
2527
3606
  label: "Cursor Desktop",
2528
- relativePath: "Library/Application Support/Cursor",
3607
+ relativePath: process.platform === "win32" ? "AppData/Roaming/Cursor" : "Library/Application Support/Cursor",
2529
3608
  kind: "ai-host",
2530
3609
  note: "\u684C\u9762\u5E94\u7528\u8FD0\u884C\u6570\u636E\uFF0C\u9ED8\u8BA4\u53D7\u4FDD\u62A4\u3002"
2531
3610
  }
2532
3611
  ];
2533
3612
  function inspectHost(options = {}) {
2534
- const homePath = resolve3(options.homeDir ?? homedir());
3613
+ const homePath = resolve4(options.homeDir ?? homedir());
2535
3614
  const now = options.now ?? /* @__PURE__ */ new Date();
2536
- const rootPaths = ROOT_DEFINITIONS.map((definition) => join14(homePath, definition.relativePath));
3615
+ const rootPaths = ROOT_DEFINITIONS.map((definition) => join15(homePath, definition.relativePath));
2537
3616
  const rootSizes = measurePaths(rootPaths);
2538
3617
  const findings = inspectFindings(homePath);
2539
3618
  const rootFindingIds = /* @__PURE__ */ new Map();
@@ -2550,7 +3629,7 @@ function inspectHost(options = {}) {
2550
3629
  label: definition.label,
2551
3630
  path: displayPath(path, homePath),
2552
3631
  kind: definition.kind,
2553
- exists: existsSync13(path),
3632
+ exists: existsSync14(path),
2554
3633
  bytes: rootSizes.get(path) ?? 0,
2555
3634
  status: rootFindings.length > 0 ? "attention" : "healthy",
2556
3635
  note: definition.note
@@ -2593,7 +3672,7 @@ function inspectHost(options = {}) {
2593
3672
  function inspectAgentBrowser(options) {
2594
3673
  const command2 = options.command ?? "agent-browser";
2595
3674
  try {
2596
- const output = execFileSync(command2, ["--version"], {
3675
+ const result = spawnPlatformSync(command2, ["--version"], {
2597
3676
  encoding: "utf8",
2598
3677
  stdio: ["ignore", "pipe", "ignore"],
2599
3678
  // Host scans can run alongside the full portfolio test matrix. Keep the
@@ -2601,7 +3680,9 @@ function inspectAgentBrowser(options) {
2601
3680
  // spawn the pinned CLI instead of misclassifying a slow start as an
2602
3681
  // unparseable version.
2603
3682
  timeout: 1e4
2604
- }).trim();
3683
+ });
3684
+ if (result.error || result.status !== 0) throw result.error ?? new Error("command failed");
3685
+ const output = result.stdout.trim();
2605
3686
  const installedVersion = output.match(/(?:^|\s)(\d+\.\d+\.\d+)(?:\s|$)/)?.[1];
2606
3687
  if (!installedVersion) {
2607
3688
  return {
@@ -2655,25 +3736,25 @@ function createHostLensCleanupPlan(report) {
2655
3736
  };
2656
3737
  }
2657
3738
  function writeHostLensReport(report, outDir) {
2658
- mkdirSync5(outDir, { recursive: true });
3739
+ mkdirSync6(outDir, { recursive: true });
2659
3740
  const assets = findHostDashboardAssets();
2660
3741
  for (const file of ["index.html", "app.js", "app.css"]) {
2661
- const source = join14(assets, file);
2662
- if (!existsSync13(source)) throw new Error(`HostLens dashboard asset is missing: ${source}`);
2663
- cpSync2(source, join14(outDir, file));
3742
+ const source = join15(assets, file);
3743
+ if (!existsSync14(source)) throw new Error(`HostLens dashboard asset is missing: ${source}`);
3744
+ cpSync2(source, join15(outDir, file));
2664
3745
  }
2665
- const jsonPath = join14(outDir, "host-lens.json");
2666
- const htmlPath = join14(outDir, "index.html");
2667
- writeFileSync4(jsonPath, `${JSON.stringify(report, null, 2)}
3746
+ const jsonPath = join15(outDir, "host-lens.json");
3747
+ const htmlPath = join15(outDir, "index.html");
3748
+ writeFileSync5(jsonPath, `${JSON.stringify(report, null, 2)}
2668
3749
  `);
2669
- writeFileSync4(join14(outDir, "data.js"), `window.__HOST_LENS__ = ${safeJavaScriptJson(report)};
3750
+ writeFileSync5(join15(outDir, "data.js"), `window.__HOST_LENS__ = ${safeJavaScriptJson(report)};
2670
3751
  `);
2671
3752
  return { jsonPath, htmlPath };
2672
3753
  }
2673
3754
  function inspectFindings(homePath) {
2674
3755
  const candidates = [];
2675
3756
  const addGroup = (value) => {
2676
- const paths = value.paths.filter((path) => existsSync13(path));
3757
+ const paths = value.paths.filter((path) => existsSync14(path));
2677
3758
  if (paths.length === 0) return;
2678
3759
  candidates.push({ ...value, paths: paths.map((path) => displayPath(path, homePath)) });
2679
3760
  };
@@ -2681,7 +3762,7 @@ function inspectFindings(homePath) {
2681
3762
  id: "codex-backups",
2682
3763
  rootId: "codex",
2683
3764
  label: "Codex \u5386\u53F2\u5907\u4EFD",
2684
- paths: matchingChildren(join14(homePath, ".codex"), (name) => /^backup-/i.test(name)),
3765
+ paths: matchingChildren(join15(homePath, ".codex"), (name) => /^backup-/i.test(name)),
2685
3766
  category: "backup",
2686
3767
  confidence: "medium",
2687
3768
  disposition: "manual-review",
@@ -2692,7 +3773,7 @@ function inspectFindings(homePath) {
2692
3773
  id: "codex-cache",
2693
3774
  rootId: "codex",
2694
3775
  label: "Codex \u53EF\u518D\u751F\u6210\u7F13\u5B58",
2695
- paths: [join14(homePath, ".codex/cache")],
3776
+ paths: [join15(homePath, ".codex/cache")],
2696
3777
  category: "cache",
2697
3778
  confidence: "high",
2698
3779
  disposition: "report-only",
@@ -2703,7 +3784,7 @@ function inspectFindings(homePath) {
2703
3784
  id: "claude-temporary",
2704
3785
  rootId: "claude-code",
2705
3786
  label: "Claude Code \u4E34\u65F6\u76EE\u5F55",
2706
- paths: matchingChildren(join14(homePath, ".claude"), (name) => /^(temp|tmp)[-_]/i.test(name)),
3787
+ paths: matchingChildren(join15(homePath, ".claude"), (name) => /^(temp|tmp)[-_]/i.test(name)),
2707
3788
  category: "temporary",
2708
3789
  confidence: "high",
2709
3790
  disposition: "manual-review",
@@ -2714,7 +3795,7 @@ function inspectFindings(homePath) {
2714
3795
  id: "claude-cache",
2715
3796
  rootId: "claude-code",
2716
3797
  label: "Claude Code \u7F13\u5B58",
2717
- paths: [join14(homePath, ".claude/cache")],
3798
+ paths: [join15(homePath, ".claude/cache")],
2718
3799
  category: "cache",
2719
3800
  confidence: "high",
2720
3801
  disposition: "report-only",
@@ -2725,7 +3806,7 @@ function inspectFindings(homePath) {
2725
3806
  id: "copilot-session-history",
2726
3807
  rootId: "copilot",
2727
3808
  label: "Copilot \u4F1A\u8BDD\u72B6\u6001",
2728
- paths: [join14(homePath, ".copilot/session-state")],
3809
+ paths: [join15(homePath, ".copilot/session-state")],
2729
3810
  category: "session-history",
2730
3811
  confidence: "low",
2731
3812
  disposition: "manual-review",
@@ -2736,7 +3817,7 @@ function inspectFindings(homePath) {
2736
3817
  id: "npm-npx-cache",
2737
3818
  rootId: "npm-cache",
2738
3819
  label: "npx \u4E34\u65F6\u5B89\u88C5\u7F13\u5B58",
2739
- paths: [join14(homePath, ".npm/_npx")],
3820
+ paths: [join15(homePath, ".npm/_npx")],
2740
3821
  category: "cache",
2741
3822
  confidence: "high",
2742
3823
  disposition: "native-tool",
@@ -2748,7 +3829,7 @@ function inspectFindings(homePath) {
2748
3829
  id: "npm-content-cache",
2749
3830
  rootId: "npm-cache",
2750
3831
  label: "npm \u5185\u5BB9\u7F13\u5B58",
2751
- paths: [join14(homePath, ".npm/_cacache")],
3832
+ paths: [join15(homePath, ".npm/_cacache")],
2752
3833
  category: "cache",
2753
3834
  confidence: "high",
2754
3835
  disposition: "native-tool",
@@ -2760,7 +3841,12 @@ function inspectFindings(homePath) {
2760
3841
  id: "pnpm-store",
2761
3842
  rootId: "pnpm-store",
2762
3843
  label: "pnpm \u672A\u5F15\u7528\u5305\u5019\u9009",
2763
- paths: [join14(homePath, "Library/pnpm/store")],
3844
+ paths: [
3845
+ join15(
3846
+ homePath,
3847
+ process.platform === "win32" ? "AppData/Local/pnpm/store" : "Library/pnpm/store"
3848
+ )
3849
+ ],
2764
3850
  category: "cache",
2765
3851
  confidence: "high",
2766
3852
  disposition: "native-tool",
@@ -2772,7 +3858,13 @@ function inspectFindings(homePath) {
2772
3858
  id: "playwright-browsers",
2773
3859
  rootId: "playwright",
2774
3860
  label: "Playwright \u6D4F\u89C8\u5668\u7248\u672C",
2775
- paths: [join14(homePath, "Library/Caches/ms-playwright"), join14(homePath, ".cache/ms-playwright")],
3861
+ paths: [
3862
+ join15(
3863
+ homePath,
3864
+ process.platform === "win32" ? "AppData/Local/ms-playwright" : "Library/Caches/ms-playwright"
3865
+ ),
3866
+ join15(homePath, ".cache/ms-playwright")
3867
+ ],
2776
3868
  category: "duplicate-runtime",
2777
3869
  confidence: "medium",
2778
3870
  disposition: "native-tool",
@@ -2794,6 +3886,8 @@ function inspectFindings(homePath) {
2794
3886
  });
2795
3887
  }
2796
3888
  function buildProtections(homePath) {
3889
+ const claudeDesktopPath = process.platform === "win32" ? "~/AppData/Roaming/Claude" : "~/Library/Application Support/Claude";
3890
+ const cursorDesktopPath = process.platform === "win32" ? "~/AppData/Roaming/Cursor" : "~/Library/Application Support/Cursor";
2797
3891
  return [
2798
3892
  {
2799
3893
  label: "\u914D\u7F6E\u4E0E\u8EAB\u4EFD\u8FB9\u754C",
@@ -2812,7 +3906,7 @@ function buildProtections(homePath) {
2812
3906
  },
2813
3907
  {
2814
3908
  label: "\u684C\u9762\u5BBF\u4E3B\u8FD0\u884C\u6570\u636E",
2815
- paths: ["~/Library/Application Support/Claude", "~/Library/Application Support/Cursor"],
3909
+ paths: [claudeDesktopPath, cursorDesktopPath],
2816
3910
  reason: "\u5E94\u7528\u6570\u636E\u5E93\u4E0E\u8FD0\u884C\u72B6\u6001\u6DF7\u5728\u5176\u4E2D\uFF0C\u4E0D\u80FD\u628A\u6574\u4E2A\u76EE\u5F55\u5F53\u7F13\u5B58\u3002"
2817
3911
  }
2818
3912
  ].map((protection) => ({
@@ -2821,16 +3915,16 @@ function buildProtections(homePath) {
2821
3915
  }));
2822
3916
  }
2823
3917
  function matchingChildren(root, predicate) {
2824
- if (!existsSync13(root)) return [];
3918
+ if (!existsSync14(root)) return [];
2825
3919
  try {
2826
- return readdirSync7(root, { withFileTypes: true }).filter((entry) => predicate(entry.name)).map((entry) => join14(root, entry.name));
3920
+ return readdirSync8(root, { withFileTypes: true }).filter((entry) => predicate(entry.name)).map((entry) => join15(root, entry.name));
2827
3921
  } catch {
2828
3922
  return [];
2829
3923
  }
2830
3924
  }
2831
3925
  function measurePaths(paths) {
2832
- const uniquePaths = [...new Set(paths.map((path) => resolve3(path)))].filter(
2833
- (path) => existsSync13(path)
3926
+ const uniquePaths = [...new Set(paths.map((path) => resolve4(path)))].filter(
3927
+ (path) => existsSync14(path)
2834
3928
  );
2835
3929
  const result = /* @__PURE__ */ new Map();
2836
3930
  if (uniquePaths.length === 0) return result;
@@ -2843,7 +3937,7 @@ function measurePaths(paths) {
2843
3937
  for (const line of output.split(/\r?\n/)) {
2844
3938
  const match = line.match(/^(\d+)\s+(.+)$/);
2845
3939
  if (!match) continue;
2846
- result.set(resolve3(match[2]), Number(match[1]) * 1024);
3940
+ result.set(resolve4(match[2]), Number(match[1]) * 1024);
2847
3941
  }
2848
3942
  return result;
2849
3943
  } catch {
@@ -2853,7 +3947,7 @@ function measurePaths(paths) {
2853
3947
  }
2854
3948
  function fallbackMeasure(root) {
2855
3949
  try {
2856
- const rootStats = lstatSync6(root);
3950
+ const rootStats = lstatSync7(root);
2857
3951
  if (!rootStats.isDirectory()) return rootStats.size;
2858
3952
  } catch {
2859
3953
  return 0;
@@ -2866,17 +3960,17 @@ function fallbackMeasure(root) {
2866
3960
  if (!current) continue;
2867
3961
  let entries;
2868
3962
  try {
2869
- entries = readdirSync7(current, { withFileTypes: true });
3963
+ entries = readdirSync8(current, { withFileTypes: true });
2870
3964
  } catch {
2871
3965
  continue;
2872
3966
  }
2873
3967
  for (const entry of entries) {
2874
3968
  visited += 1;
2875
- const path = join14(current, entry.name);
3969
+ const path = join15(current, entry.name);
2876
3970
  if (entry.isDirectory()) pending.push(path);
2877
3971
  else if (entry.isFile()) {
2878
3972
  try {
2879
- bytes += statSync4(path).size;
3973
+ bytes += statSync5(path).size;
2880
3974
  } catch {
2881
3975
  }
2882
3976
  }
@@ -2887,35 +3981,35 @@ function fallbackMeasure(root) {
2887
3981
  }
2888
3982
  function countImmediateEntries(path) {
2889
3983
  try {
2890
- return lstatSync6(path).isDirectory() ? readdirSync7(path).length : 1;
3984
+ return lstatSync7(path).isDirectory() ? readdirSync8(path).length : 1;
2891
3985
  } catch {
2892
3986
  return 0;
2893
3987
  }
2894
3988
  }
2895
3989
  function displayPath(path, homePath) {
2896
- const absolute = resolve3(path);
2897
- const withinHome = relative6(homePath, absolute);
3990
+ const absolute = resolve4(path);
3991
+ const withinHome = relative7(homePath, absolute);
2898
3992
  if (withinHome === "") return "~";
2899
- if (!withinHome.startsWith("..")) return `~/${withinHome}`;
3993
+ if (!withinHome.startsWith("..")) return `~/${withinHome.replaceAll("\\", "/")}`;
2900
3994
  return absolute;
2901
3995
  }
2902
3996
  function undisplayPath(path, homePath) {
2903
3997
  if (path === "~") return homePath;
2904
- if (path.startsWith("~/")) return join14(homePath, path.slice(2));
2905
- return resolve3(path);
3998
+ if (path.startsWith("~/")) return join15(homePath, path.slice(2));
3999
+ return resolve4(path);
2906
4000
  }
2907
4001
  function findHostDashboardAssets() {
2908
- const packageRoot2 = dirname8(dirname8(fileURLToPath3(import.meta.url)));
4002
+ const packageRoot2 = dirname9(dirname9(fileURLToPath3(import.meta.url)));
2909
4003
  const candidates = [
2910
4004
  process.env.PGS_HOST_DASHBOARD_ASSETS_DIR,
2911
- join14(packageRoot2, ".host-dashboard-build"),
2912
- join14(packageRoot2, "assets/host-dashboard"),
2913
- join14(process.cwd(), ".host-dashboard-build"),
2914
- join14(process.cwd(), "assets/host-dashboard"),
2915
- join14(process.cwd(), "packages/pro-gov/.host-dashboard-build"),
2916
- join14(process.cwd(), "packages/pro-gov/assets/host-dashboard")
4005
+ join15(packageRoot2, ".host-dashboard-build"),
4006
+ join15(packageRoot2, "assets/host-dashboard"),
4007
+ join15(process.cwd(), ".host-dashboard-build"),
4008
+ join15(process.cwd(), "assets/host-dashboard"),
4009
+ join15(process.cwd(), "packages/pro-gov/.host-dashboard-build"),
4010
+ join15(process.cwd(), "packages/pro-gov/assets/host-dashboard")
2917
4011
  ].filter((value) => Boolean(value));
2918
- const match = candidates.find((path) => existsSync13(join14(path, "index.html")));
4012
+ const match = candidates.find((path) => existsSync14(join15(path, "index.html")));
2919
4013
  if (!match)
2920
4014
  throw new Error(
2921
4015
  "HostLens dashboard assets were not built. Run pnpm --filter @pieai/pro-gov build."
@@ -2927,12 +4021,12 @@ function safeJavaScriptJson(value) {
2927
4021
  }
2928
4022
 
2929
4023
  // src/portfolio/manifest.ts
2930
- import { existsSync as existsSync14, readFileSync as readFileSync9 } from "node:fs";
2931
- import { dirname as dirname9, isAbsolute as isAbsolute3, resolve as resolve4 } from "node:path";
4024
+ import { existsSync as existsSync15, readFileSync as readFileSync10 } from "node:fs";
4025
+ import { dirname as dirname10, isAbsolute as isAbsolute4, resolve as resolve5 } from "node:path";
2932
4026
  function loadPortfolioManifest(configPath) {
2933
4027
  let parsed;
2934
4028
  try {
2935
- parsed = JSON.parse(readFileSync9(configPath, "utf8"));
4029
+ parsed = JSON.parse(readFileSync10(configPath, "utf8"));
2936
4030
  } catch (error) {
2937
4031
  return {
2938
4032
  configPath,
@@ -2944,7 +4038,7 @@ function loadPortfolioManifest(configPath) {
2944
4038
  ]
2945
4039
  };
2946
4040
  }
2947
- const normalized = resolveManifestPaths(parsed, dirname9(resolve4(configPath)));
4041
+ const normalized = resolveManifestPaths(parsed, dirname10(resolve5(configPath)));
2948
4042
  const issues = validatePortfolioManifest(normalized);
2949
4043
  return {
2950
4044
  configPath,
@@ -2955,16 +4049,16 @@ function loadPortfolioManifest(configPath) {
2955
4049
  function resolveManifestPaths(value, configDir) {
2956
4050
  if (!isRecord(value)) return value;
2957
4051
  const resolveEndpoint = (endpoint) => {
2958
- if (!isRecord(endpoint) || typeof endpoint.path !== "string" || isAbsolute3(endpoint.path)) {
4052
+ if (!isRecord(endpoint) || typeof endpoint.path !== "string" || isAbsolute4(endpoint.path)) {
2959
4053
  return endpoint;
2960
4054
  }
2961
- return { ...endpoint, path: resolve4(configDir, endpoint.path) };
4055
+ return { ...endpoint, path: resolve5(configDir, endpoint.path) };
2962
4056
  };
2963
4057
  return {
2964
4058
  ...value,
2965
- technologyGovernance: isRecord(value.technologyGovernance) && typeof value.technologyGovernance.strategySource === "string" && !isAbsolute3(value.technologyGovernance.strategySource) ? {
4059
+ technologyGovernance: isRecord(value.technologyGovernance) && typeof value.technologyGovernance.strategySource === "string" && !isAbsolute4(value.technologyGovernance.strategySource) ? {
2966
4060
  ...value.technologyGovernance,
2967
- strategySource: resolve4(configDir, value.technologyGovernance.strategySource)
4061
+ strategySource: resolve5(configDir, value.technologyGovernance.strategySource)
2968
4062
  } : value.technologyGovernance,
2969
4063
  controlPlane: resolveEndpoint(value.controlPlane),
2970
4064
  executionEngine: resolveEndpoint(value.executionEngine),
@@ -3149,7 +4243,7 @@ function validateEndpoint(value, field, issues, technologyCatalog) {
3149
4243
  });
3150
4244
  return;
3151
4245
  }
3152
- if (!existsSync14(value.path)) {
4246
+ if (!existsSync15(value.path)) {
3153
4247
  issues.push({
3154
4248
  type: "missing-path",
3155
4249
  id: typeof value.id === "string" ? value.id : void 0,
@@ -3580,13 +4674,13 @@ function isExactVersion(value) {
3580
4674
  return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(value);
3581
4675
  }
3582
4676
  function isRepositoryRelativePath(value) {
3583
- if (value.length === 0 || isAbsolute3(value)) return false;
4677
+ if (value.length === 0 || isAbsolute4(value)) return false;
3584
4678
  const segments = value.replaceAll("\\", "/").split("/");
3585
4679
  return !segments.includes("..");
3586
4680
  }
3587
4681
  function isExactRepositoryRelativePath(value) {
3588
4682
  if (value.length === 0) return false;
3589
- if (isAbsolute3(value)) return false;
4683
+ if (isAbsolute4(value)) return false;
3590
4684
  if (/^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\") || value.startsWith("//"))
3591
4685
  return false;
3592
4686
  if (value.includes("\\")) return false;
@@ -3884,7 +4978,7 @@ function runScan(options) {
3884
4978
  const inspection = resolveInspectionOptions(options);
3885
4979
  if (!inspection.ok) return reportConfigError(inspection.error);
3886
4980
  const report = inspectHost(inspection.value);
3887
- const written = writeHostLensReport(report, resolve5(options.outPath));
4981
+ const written = writeHostLensReport(report, resolve6(options.outPath));
3888
4982
  if (options.json) {
3889
4983
  console.log(JSON.stringify({ ok: true, ...written, summary: report.summary }, null, 2));
3890
4984
  } else {
@@ -3904,9 +4998,9 @@ function runPlan(options) {
3904
4998
  if (!inspection.ok) return reportConfigError(inspection.error);
3905
4999
  const report = inspectHost(inspection.value);
3906
5000
  const plan = createHostLensCleanupPlan(report);
3907
- const outPath = resolve5(options.outPath);
3908
- mkdirSync6(dirname10(outPath), { recursive: true });
3909
- writeFileSync5(outPath, `${JSON.stringify(plan, null, 2)}
5001
+ const outPath = resolve6(options.outPath);
5002
+ mkdirSync7(dirname11(outPath), { recursive: true });
5003
+ writeFileSync6(outPath, `${JSON.stringify(plan, null, 2)}
3910
5004
  `);
3911
5005
  if (options.json)
3912
5006
  console.log(JSON.stringify({ ok: true, outPath, actions: plan.actions.length }, null, 2));
@@ -3979,7 +5073,7 @@ function parseOptions(args) {
3979
5073
  if (arg === "--home") {
3980
5074
  const value = args[index + 1];
3981
5075
  if (!value) return { ok: false, error: "Expected value after --home" };
3982
- options.homeDir = resolve5(value);
5076
+ options.homeDir = resolve6(value);
3983
5077
  index += 1;
3984
5078
  continue;
3985
5079
  }
@@ -3993,7 +5087,7 @@ function parseOptions(args) {
3993
5087
  if (arg === "--config") {
3994
5088
  const value = args[index + 1];
3995
5089
  if (!value) return { ok: false, error: "Expected value after --config" };
3996
- options.configPath = resolve5(value);
5090
+ options.configPath = resolve6(value);
3997
5091
  index += 1;
3998
5092
  continue;
3999
5093
  }
@@ -4019,8 +5113,8 @@ function printUsage2() {
4019
5113
  }
4020
5114
 
4021
5115
  // src/learning/recall.ts
4022
- import { existsSync as existsSync15, readdirSync as readdirSync8, readFileSync as readFileSync10 } from "node:fs";
4023
- import { basename as basename3, join as join15, relative as relative7 } from "node:path";
5116
+ import { existsSync as existsSync16, readdirSync as readdirSync9, readFileSync as readFileSync11 } from "node:fs";
5117
+ import { basename as basename4, join as join16, relative as relative8 } from "node:path";
4024
5118
  function recallLearnings(root, options) {
4025
5119
  const query = options.query.trim();
4026
5120
  const terms = tokenize(query);
@@ -4043,16 +5137,16 @@ function recallLearnings(root, options) {
4043
5137
  function loadLearningRecords(root) {
4044
5138
  const recordsByTitle = /* @__PURE__ */ new Map();
4045
5139
  for (const relativeDir of ["docs/reference/learnings", "docs/solutions"]) {
4046
- const learningDir = join15(root, relativeDir);
4047
- if (!existsSync15(learningDir)) continue;
5140
+ const learningDir = join16(root, relativeDir);
5141
+ if (!existsSync16(learningDir)) continue;
4048
5142
  for (const path of listMarkdownFiles(learningDir)) {
4049
5143
  const record = readLearningRecord(root, path);
4050
5144
  const key = record.title.trim().toLowerCase();
4051
5145
  if (!recordsByTitle.has(key)) recordsByTitle.set(key, record);
4052
5146
  }
4053
5147
  }
4054
- const conceptsPath = join15(root, "CONCEPTS.md");
4055
- if (existsSync15(conceptsPath)) {
5148
+ const conceptsPath = join16(root, "CONCEPTS.md");
5149
+ if (existsSync16(conceptsPath)) {
4056
5150
  const record = readLearningRecord(root, conceptsPath);
4057
5151
  recordsByTitle.set(`concepts:${record.title.toLowerCase()}`, record);
4058
5152
  }
@@ -4060,8 +5154,8 @@ function loadLearningRecords(root) {
4060
5154
  }
4061
5155
  function listMarkdownFiles(dir) {
4062
5156
  const files = [];
4063
- for (const entry of readdirSync8(dir, { withFileTypes: true })) {
4064
- const absolutePath = join15(dir, entry.name);
5157
+ for (const entry of readdirSync9(dir, { withFileTypes: true })) {
5158
+ const absolutePath = join16(dir, entry.name);
4065
5159
  if (entry.isDirectory()) {
4066
5160
  files.push(...listMarkdownFiles(absolutePath));
4067
5161
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -4071,11 +5165,11 @@ function listMarkdownFiles(dir) {
4071
5165
  return files.sort();
4072
5166
  }
4073
5167
  function readLearningRecord(root, absolutePath) {
4074
- const content = readFileSync10(absolutePath, "utf8");
5168
+ const content = readFileSync11(absolutePath, "utf8");
4075
5169
  const parsed = splitFrontmatter(content);
4076
5170
  const body = parsed.body;
4077
5171
  return {
4078
- relativePath: normalizePath(relative7(root, absolutePath)),
5172
+ relativePath: normalizePath(relative8(root, absolutePath)),
4079
5173
  title: findTitle(parsed.frontmatter, body) ?? titleFromPath(absolutePath),
4080
5174
  metadata: parsed.frontmatter,
4081
5175
  body
@@ -4103,7 +5197,7 @@ function findTitle(frontmatter, body) {
4103
5197
  return heading ? heading.slice(2).trim() : void 0;
4104
5198
  }
4105
5199
  function titleFromPath(path) {
4106
- return basename3(path, ".md").split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
5200
+ return basename4(path, ".md").split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
4107
5201
  }
4108
5202
  function scoreRecord(record, terms) {
4109
5203
  const title = record.title.toLowerCase();
@@ -4156,8 +5250,8 @@ function cleanMarkdownLine(input) {
4156
5250
  }
4157
5251
 
4158
5252
  // src/learning/capture.ts
4159
- import { existsSync as existsSync16, mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "node:fs";
4160
- import { basename as basename4, join as join16, relative as relative8 } from "node:path";
5253
+ import { existsSync as existsSync17, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7 } from "node:fs";
5254
+ import { basename as basename5, join as join17, relative as relative9 } from "node:path";
4161
5255
  function captureLearning(root, options) {
4162
5256
  const title = options.title.trim();
4163
5257
  const summary = options.summary.trim();
@@ -4165,13 +5259,13 @@ function captureLearning(root, options) {
4165
5259
  if (!summary) throw new Error("summary is required");
4166
5260
  const category = slugify(options.category ?? "workflow-issues") || "workflow-issues";
4167
5261
  const moduleName = options.module?.trim() || "PGS learning capture";
4168
- const dir = join16(root, "docs/reference/learnings", category);
4169
- mkdirSync7(dir, { recursive: true });
5262
+ const dir = join17(root, "docs/reference/learnings", category);
5263
+ mkdirSync8(dir, { recursive: true });
4170
5264
  const path = uniquePath(dir, slugify(title) || "learning");
4171
- const idSlug = basename4(path, ".md");
4172
- writeFileSync6(path, renderLearning({ title, summary, category, moduleName, idSlug }));
5265
+ const idSlug = basename5(path, ".md");
5266
+ writeFileSync7(path, renderLearning({ title, summary, category, moduleName, idSlug }));
4173
5267
  return {
4174
- relativePath: normalizePath2(relative8(root, path)),
5268
+ relativePath: normalizePath2(relative9(root, path)),
4175
5269
  title,
4176
5270
  captureMode: "pgs-native"
4177
5271
  };
@@ -4213,10 +5307,10 @@ function renderLearning(options) {
4213
5307
  }
4214
5308
  function uniquePath(dir, slug) {
4215
5309
  let index = 1;
4216
- let candidate = join16(dir, `${slug}.md`);
4217
- while (existsSync16(candidate)) {
5310
+ let candidate = join17(dir, `${slug}.md`);
5311
+ while (existsSync17(candidate)) {
4218
5312
  index += 1;
4219
- candidate = join16(dir, `${slug}-${index}.md`);
5313
+ candidate = join17(dir, `${slug}-${index}.md`);
4220
5314
  }
4221
5315
  return candidate;
4222
5316
  }
@@ -4401,12 +5495,12 @@ function printUsage3() {
4401
5495
  }
4402
5496
 
4403
5497
  // src/commands/lens.ts
4404
- import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "node:fs";
4405
- import { dirname as dirname13 } from "node:path";
5498
+ import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "node:fs";
5499
+ import { dirname as dirname14 } from "node:path";
4406
5500
 
4407
5501
  // src/lens/audit.ts
4408
- import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "node:fs";
4409
- import { basename as basename5, dirname as dirname11, join as join17 } from "node:path";
5502
+ import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
5503
+ import { basename as basename6, dirname as dirname12, join as join18 } from "node:path";
4410
5504
  var REQUIRED_ARTIFACTS = [
4411
5505
  "manifest.md",
4412
5506
  "raw/project-lens/architecture-lens.md",
@@ -4424,20 +5518,20 @@ function createProjectLensAuditPackage(targetDir, auditDir) {
4424
5518
  version: 1,
4425
5519
  target: {
4426
5520
  path: targetDir,
4427
- name: basename5(targetDir) || "target"
5521
+ name: basename6(targetDir) || "target"
4428
5522
  },
4429
5523
  requiredArtifacts: [...REQUIRED_ARTIFACTS]
4430
5524
  };
4431
- mkdirSync8(auditDir, { recursive: true });
4432
- writeJson(join17(auditDir, "audit.contract.json"), contract);
5525
+ mkdirSync9(auditDir, { recursive: true });
5526
+ writeJson(join18(auditDir, "audit.contract.json"), contract);
4433
5527
  for (const artifactPath of REQUIRED_ARTIFACTS) {
4434
- writeTemplate(join17(auditDir, artifactPath), renderArtifactTemplate(artifactPath, contract));
5528
+ writeTemplate(join18(auditDir, artifactPath), renderArtifactTemplate(artifactPath, contract));
4435
5529
  }
4436
5530
  return contract;
4437
5531
  }
4438
5532
  function checkProjectLensAuditPackage(auditDir, options = {}) {
4439
- const contractPath = join17(auditDir, "audit.contract.json");
4440
- if (!existsSync17(contractPath)) {
5533
+ const contractPath = join18(auditDir, "audit.contract.json");
5534
+ if (!existsSync18(contractPath)) {
4441
5535
  return {
4442
5536
  ok: false,
4443
5537
  auditDir,
@@ -4451,7 +5545,7 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
4451
5545
  }
4452
5546
  let contract;
4453
5547
  try {
4454
- contract = JSON.parse(readFileSync11(contractPath, "utf8"));
5548
+ contract = JSON.parse(readFileSync12(contractPath, "utf8"));
4455
5549
  } catch (error) {
4456
5550
  return {
4457
5551
  ok: false,
@@ -4484,12 +5578,12 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
4484
5578
  }
4485
5579
  }
4486
5580
  for (const artifactPath of REQUIRED_ARTIFACTS) {
4487
- const absolutePath = join17(auditDir, artifactPath);
4488
- if (!existsSync17(absolutePath)) {
5581
+ const absolutePath = join18(auditDir, artifactPath);
5582
+ if (!existsSync18(absolutePath)) {
4489
5583
  issues.push({ type: "missing-required-artifact", path: artifactPath });
4490
5584
  continue;
4491
5585
  }
4492
- const content = readFileSync11(absolutePath, "utf8");
5586
+ const content = readFileSync12(absolutePath, "utf8");
4493
5587
  if (isPendingArtifact(content)) {
4494
5588
  issues.push({ type: "artifact-not-complete", path: artifactPath });
4495
5589
  } else if (hasTemplateBody(content)) {
@@ -4505,13 +5599,13 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
4505
5599
  };
4506
5600
  }
4507
5601
  function writeJson(path, value) {
4508
- mkdirSync8(dirname11(path), { recursive: true });
4509
- writeFileSync7(path, `${JSON.stringify(value, null, 2)}
5602
+ mkdirSync9(dirname12(path), { recursive: true });
5603
+ writeFileSync8(path, `${JSON.stringify(value, null, 2)}
4510
5604
  `);
4511
5605
  }
4512
5606
  function writeTemplate(path, content) {
4513
- mkdirSync8(dirname11(path), { recursive: true });
4514
- writeFileSync7(path, content);
5607
+ mkdirSync9(dirname12(path), { recursive: true });
5608
+ writeFileSync8(path, content);
4515
5609
  }
4516
5610
  function renderArtifactTemplate(artifactPath, contract) {
4517
5611
  const title = artifactPath.replace(/\.md$/, "").split("/").map((part) => part.replaceAll("-", " ")).join(" / ");
@@ -4789,14 +5883,14 @@ function formatLink(link) {
4789
5883
  }
4790
5884
 
4791
5885
  // src/lens/scan.ts
4792
- import { spawnSync as spawnSync4 } from "node:child_process";
4793
- import { existsSync as existsSync21, readFileSync as readFileSync13, statSync as statSync6 } from "node:fs";
5886
+ import { spawnSync as spawnSync3 } from "node:child_process";
5887
+ import { existsSync as existsSync22, readFileSync as readFileSync14, statSync as statSync7 } from "node:fs";
4794
5888
  import { homedir as homedir3 } from "node:os";
4795
- import { join as join22 } from "node:path";
5889
+ import { join as join23 } from "node:path";
4796
5890
 
4797
5891
  // src/host-ssot.ts
4798
- import { lstatSync as lstatSync7, readlinkSync as readlinkSync3, realpathSync as realpathSync5 } from "node:fs";
4799
- import { dirname as dirname12, isAbsolute as isAbsolute4, join as join18, resolve as resolve6 } from "node:path";
5892
+ import { lstatSync as lstatSync8, readlinkSync as readlinkSync3, realpathSync as realpathSync5 } from "node:fs";
5893
+ import { dirname as dirname13, isAbsolute as isAbsolute5, join as join19, resolve as resolve7 } from "node:path";
4800
5894
  function inspectProjectHostSsot(root) {
4801
5895
  const agentsEntry = inspectCanonicalPath(root, "AGENTS.md");
4802
5896
  const canonicalSkills = inspectCanonicalPath(root, ".agents/skills");
@@ -4840,7 +5934,7 @@ function inspectUserSkillsSsot(homeDir) {
4840
5934
  };
4841
5935
  }
4842
5936
  function inspectCanonicalPath(root, path) {
4843
- const absolutePath = join18(root, path);
5937
+ const absolutePath = join19(root, path);
4844
5938
  const stat = safeLstat2(absolutePath);
4845
5939
  if (!stat) return { path, status: "missing" };
4846
5940
  if (stat.isSymbolicLink()) {
@@ -4856,7 +5950,7 @@ function inspectCanonicalPath(root, path) {
4856
5950
  return { path, status: "other" };
4857
5951
  }
4858
5952
  function inspectCompatibilityLink(root, path, expectedRawTarget) {
4859
- const absolutePath = join18(root, path);
5953
+ const absolutePath = join19(root, path);
4860
5954
  const stat = safeLstat2(absolutePath);
4861
5955
  const base = { path, expectedRawTarget, compliant: false };
4862
5956
  if (!stat) return { ...base, status: "missing" };
@@ -4865,9 +5959,9 @@ function inspectCompatibilityLink(root, path, expectedRawTarget) {
4865
5959
  if (stat.isDirectory()) return { ...base, status: "directory" };
4866
5960
  return { ...base, status: "other" };
4867
5961
  }
4868
- const rawTarget = readlinkSync3(absolutePath);
4869
- const resolvedTarget = resolve6(dirname12(absolutePath), rawTarget);
4870
- const expectedPath = resolve6(dirname12(absolutePath), expectedRawTarget);
5962
+ const rawTarget = normalizeSymlinkTarget(readlinkSync3(absolutePath));
5963
+ const resolvedTarget = resolve7(dirname13(absolutePath), rawTarget);
5964
+ const expectedPath = resolve7(dirname13(absolutePath), expectedRawTarget);
4871
5965
  const targetStat = safeLstat2(resolvedTarget);
4872
5966
  if (!targetStat) {
4873
5967
  return {
@@ -4886,7 +5980,7 @@ function inspectCompatibilityLink(root, path, expectedRawTarget) {
4886
5980
  if (!targetMatches) {
4887
5981
  return { ...base, rawTarget, resolvedTarget, status: "wrong-target" };
4888
5982
  }
4889
- if (isAbsolute4(rawTarget)) {
5983
+ if (isAbsolute5(rawTarget)) {
4890
5984
  return { ...base, rawTarget, resolvedTarget, status: "absolute-symlink" };
4891
5985
  }
4892
5986
  if (rawTarget !== expectedRawTarget) {
@@ -4907,16 +6001,16 @@ function inspectCompatibilityLink(root, path, expectedRawTarget) {
4907
6001
  }
4908
6002
  function safeLstat2(path) {
4909
6003
  try {
4910
- return lstatSync7(path);
6004
+ return lstatSync8(path);
4911
6005
  } catch {
4912
6006
  return void 0;
4913
6007
  }
4914
6008
  }
4915
6009
 
4916
6010
  // src/portfolio/redundancy.ts
4917
- import { existsSync as existsSync18, readdirSync as readdirSync9, statSync as statSync5 } from "node:fs";
6011
+ import { existsSync as existsSync19, readdirSync as readdirSync10, statSync as statSync6 } from "node:fs";
4918
6012
  import { homedir as homedir2 } from "node:os";
4919
- import { join as join19 } from "node:path";
6013
+ import { join as join20 } from "node:path";
4920
6014
  var DEFAULT_CACHE_THRESHOLD_BYTES = 1e9;
4921
6015
  var MAX_CACHE_ENTRIES = 2e4;
4922
6016
  function inspectHostRedundancy(options = {}) {
@@ -4947,8 +6041,8 @@ function inspectProjectRedundancy(root, options = {}) {
4947
6041
  }
4948
6042
  function inspectLegacyDirectories(root) {
4949
6043
  const relativePath = ".agent";
4950
- const path = join19(root, relativePath);
4951
- if (!existsSync18(path)) return [];
6044
+ const path = join20(root, relativePath);
6045
+ if (!existsSync19(path)) return [];
4952
6046
  const stats = collectDirectoryStats(path);
4953
6047
  return [
4954
6048
  {
@@ -4963,16 +6057,16 @@ function inspectLegacyDirectories(root) {
4963
6057
  function getPlaywrightCachePaths(homeDir, configuredPath) {
4964
6058
  const candidates = [
4965
6059
  configuredPath && configuredPath !== "0" ? configuredPath : void 0,
4966
- join19(homeDir, "Library/Caches/ms-playwright"),
4967
- join19(homeDir, ".cache/ms-playwright"),
4968
- join19(homeDir, "AppData/Local/ms-playwright")
6060
+ join20(homeDir, "Library/Caches/ms-playwright"),
6061
+ join20(homeDir, ".cache/ms-playwright"),
6062
+ join20(homeDir, "AppData/Local/ms-playwright")
4969
6063
  ].filter((path) => Boolean(path));
4970
6064
  return [...new Set(candidates)];
4971
6065
  }
4972
6066
  function inspectPlaywrightCache(path, cache) {
4973
6067
  const cached = cache?.get(path);
4974
6068
  if (cached) return cached;
4975
- if (!existsSync18(path)) {
6069
+ if (!existsSync19(path)) {
4976
6070
  const missing = {
4977
6071
  path,
4978
6072
  exists: false,
@@ -4987,7 +6081,7 @@ function inspectPlaywrightCache(path, cache) {
4987
6081
  const stats = collectDirectoryStats(path);
4988
6082
  let revisionCount = 0;
4989
6083
  try {
4990
- revisionCount = readdirSync9(path, { withFileTypes: true }).filter(
6084
+ revisionCount = readdirSync10(path, { withFileTypes: true }).filter(
4991
6085
  (entry) => entry.isDirectory()
4992
6086
  ).length;
4993
6087
  } catch {
@@ -5014,7 +6108,7 @@ function collectDirectoryStats(root) {
5014
6108
  if (!current) continue;
5015
6109
  let entries;
5016
6110
  try {
5017
- entries = readdirSync9(current, { withFileTypes: true });
6111
+ entries = readdirSync10(current, { withFileTypes: true });
5018
6112
  } catch {
5019
6113
  continue;
5020
6114
  }
@@ -5023,13 +6117,13 @@ function collectDirectoryStats(root) {
5023
6117
  truncated = true;
5024
6118
  break;
5025
6119
  }
5026
- const path = join19(current, entry.name);
6120
+ const path = join20(current, entry.name);
5027
6121
  if (entry.isDirectory()) {
5028
6122
  pending.push(path);
5029
6123
  } else if (entry.isFile()) {
5030
6124
  fileCount += 1;
5031
6125
  try {
5032
- bytes += statSync5(path).size;
6126
+ bytes += statSync6(path).size;
5033
6127
  } catch {
5034
6128
  }
5035
6129
  }
@@ -5040,11 +6134,11 @@ function collectDirectoryStats(root) {
5040
6134
  }
5041
6135
 
5042
6136
  // src/portfolio/verification.ts
5043
- import { existsSync as existsSync19, readFileSync as readFileSync12 } from "node:fs";
5044
- import { join as join20 } from "node:path";
6137
+ import { existsSync as existsSync20, readFileSync as readFileSync13 } from "node:fs";
6138
+ import { join as join21 } from "node:path";
5045
6139
  var REQUIRED_PROJECT_SCRIPTS = ["typecheck", "lint", "format:check", "verify"];
5046
6140
  function inspectProjectVerification(root) {
5047
- const packageJson = readPackageJson(join20(root, "package.json"));
6141
+ const packageJson = readPackageJson(join21(root, "package.json"));
5048
6142
  const scripts = Object.fromEntries(
5049
6143
  REQUIRED_PROJECT_SCRIPTS.map((name) => [
5050
6144
  name,
@@ -5062,18 +6156,18 @@ function inspectProjectVerification(root) {
5062
6156
  };
5063
6157
  }
5064
6158
  function readPackageJson(path) {
5065
- if (!existsSync19(path)) return void 0;
6159
+ if (!existsSync20(path)) return void 0;
5066
6160
  try {
5067
- return JSON.parse(readFileSync12(path, "utf8"));
6161
+ return JSON.parse(readFileSync13(path, "utf8"));
5068
6162
  } catch {
5069
6163
  return void 0;
5070
6164
  }
5071
6165
  }
5072
6166
 
5073
6167
  // src/repository-files.ts
5074
- import { spawnSync as spawnSync3 } from "node:child_process";
5075
- import { existsSync as existsSync20, readdirSync as readdirSync10 } from "node:fs";
5076
- import { isAbsolute as isAbsolute5, join as join21, posix as posix3, relative as relative9 } from "node:path";
6168
+ import { spawnSync as spawnSync2 } from "node:child_process";
6169
+ import { existsSync as existsSync21, readdirSync as readdirSync11 } from "node:fs";
6170
+ import { isAbsolute as isAbsolute6, join as join22, posix as posix3, relative as relative10 } from "node:path";
5077
6171
  var gitMaxBufferBytes = 64 * 1024 * 1024;
5078
6172
  var RepositoryFileDiscoveryError = class extends Error {
5079
6173
  constructor(message) {
@@ -5084,7 +6178,7 @@ var RepositoryFileDiscoveryError = class extends Error {
5084
6178
  function discoverRepositoryFiles(root, options = {}) {
5085
6179
  const probe = runGit(root, ["rev-parse", "--is-inside-work-tree"]);
5086
6180
  if (!probe.ok) {
5087
- if (!probe.notRepository || existsSync20(join21(root, ".git"))) {
6181
+ if (!probe.notRepository || existsSync21(join22(root, ".git"))) {
5088
6182
  throw new RepositoryFileDiscoveryError(probe.message);
5089
6183
  }
5090
6184
  return {
@@ -5115,7 +6209,7 @@ function discoverRepositoryFiles(root, options = {}) {
5115
6209
  `Git returned a path outside the repository boundary: ${path}`
5116
6210
  );
5117
6211
  }
5118
- if (existsSync20(join21(root, normalized))) files.add(normalized);
6212
+ if (existsSync21(join22(root, normalized))) files.add(normalized);
5119
6213
  }
5120
6214
  return { source: "git", files: [...files].sort() };
5121
6215
  }
@@ -5127,21 +6221,21 @@ function discoverFilesystemFiles(root, options) {
5127
6221
  const maxDepth = options.fallbackMaxDepth ?? Number.POSITIVE_INFINITY;
5128
6222
  const ignoredDirectories2 = options.fallbackIgnoredDirectories ?? /* @__PURE__ */ new Set();
5129
6223
  const visit = (directory, depth) => {
5130
- if (depth > maxDepth || !existsSync20(directory)) return;
6224
+ if (depth > maxDepth || !existsSync21(directory)) return;
5131
6225
  let entries;
5132
6226
  try {
5133
- entries = readdirSync10(directory, { withFileTypes: true });
6227
+ entries = readdirSync11(directory, { withFileTypes: true });
5134
6228
  } catch {
5135
6229
  return;
5136
6230
  }
5137
6231
  for (const entry of entries) {
5138
- const absolutePath = join21(directory, entry.name);
6232
+ const absolutePath = join22(directory, entry.name);
5139
6233
  if (entry.isDirectory()) {
5140
6234
  if (!ignoredDirectories2.has(entry.name)) visit(absolutePath, depth + 1);
5141
6235
  continue;
5142
6236
  }
5143
6237
  if (!entry.isFile()) continue;
5144
- const relativePath = normalizeRepositoryRelativePath(relative9(root, absolutePath));
6238
+ const relativePath = normalizeRepositoryRelativePath(relative10(root, absolutePath));
5145
6239
  if (isSafeRepositoryRelativePath(relativePath) && (options.fallbackIncludeFile?.(relativePath) ?? true)) {
5146
6240
  files.add(relativePath);
5147
6241
  }
@@ -5151,10 +6245,10 @@ function discoverFilesystemFiles(root, options) {
5151
6245
  return [...files].sort();
5152
6246
  }
5153
6247
  function isSafeRepositoryRelativePath(path) {
5154
- return path !== "" && path !== "." && !isAbsolute5(path) && !/^[a-zA-Z]:\//.test(path) && path !== ".." && !path.startsWith("../");
6248
+ return path !== "" && path !== "." && !isAbsolute6(path) && !/^[a-zA-Z]:\//.test(path) && path !== ".." && !path.startsWith("../");
5155
6249
  }
5156
6250
  function runGit(root, args) {
5157
- const result = spawnSync3("git", ["-C", root, ...args], {
6251
+ const result = spawnSync2("git", ["-C", root, ...args], {
5158
6252
  encoding: "utf8",
5159
6253
  maxBuffer: gitMaxBufferBytes,
5160
6254
  env: { ...process.env, LANG: "C", LC_ALL: "C" }
@@ -5191,7 +6285,7 @@ function scanProjectLensTarget(targetDir, options = {}) {
5191
6285
  includedFileCount: files.length,
5192
6286
  excludedFileCount: candidateFiles.length - files.length
5193
6287
  },
5194
- aiEntryFiles: ["AGENTS.md", "CLAUDE.md"].filter((file) => existsSync21(join22(targetDir, file))),
6288
+ aiEntryFiles: ["AGENTS.md", "CLAUDE.md"].filter((file) => existsSync22(join23(targetDir, file))),
5195
6289
  aiConfigFiles: [],
5196
6290
  hostSsot: inspectProjectHostSsot(targetDir),
5197
6291
  userHostSsot: inspectUserSkillsSsot(options.homeDir ?? process.env.HOME ?? homedir3()),
@@ -5202,19 +6296,19 @@ function scanProjectLensTarget(targetDir, options = {}) {
5202
6296
  }),
5203
6297
  packageJson,
5204
6298
  docs: {
5205
- hasDocsDirectory: existsSync21(join22(targetDir, "docs")),
6299
+ hasDocsDirectory: existsSync22(join23(targetDir, "docs")),
5206
6300
  markdownFileCount: markdownFiles.length,
5207
6301
  governanceFiles: markdownFiles.filter((file) => file.startsWith("docs/governance/") || file.startsWith("docs/policy/")).sort()
5208
6302
  },
5209
6303
  git: readGitState(targetDir),
5210
- largeFiles: files.map((file) => ({ path: file, bytes: statSync6(join22(targetDir, file)).size })).filter((file) => file.bytes >= largeFileBytes).sort((a, b) => b.bytes - a.bytes || a.path.localeCompare(b.path)).slice(0, 25)
6304
+ largeFiles: files.map((file) => ({ path: file, bytes: statSync7(join23(targetDir, file)).size })).filter((file) => file.bytes >= largeFileBytes).sort((a, b) => b.bytes - a.bytes || a.path.localeCompare(b.path)).slice(0, 25)
5211
6305
  };
5212
6306
  }
5213
6307
  function readPackageJson2(targetDir) {
5214
- const packageJsonPath = join22(targetDir, "package.json");
5215
- if (!existsSync21(packageJsonPath)) return void 0;
6308
+ const packageJsonPath = join23(targetDir, "package.json");
6309
+ if (!existsSync22(packageJsonPath)) return void 0;
5216
6310
  try {
5217
- const packageJson = JSON.parse(readFileSync13(packageJsonPath, "utf8"));
6311
+ const packageJson = JSON.parse(readFileSync14(packageJsonPath, "utf8"));
5218
6312
  return {
5219
6313
  scripts: Object.keys(packageJson.scripts ?? {}).sort(),
5220
6314
  dependencies: Object.keys(packageJson.dependencies ?? {}).sort(),
@@ -5237,7 +6331,7 @@ function readGitState(targetDir) {
5237
6331
  };
5238
6332
  }
5239
6333
  function runGit2(targetDir, args) {
5240
- const result = spawnSync4("git", ["-C", targetDir, ...args], {
6334
+ const result = spawnSync3("git", ["-C", targetDir, ...args], {
5241
6335
  encoding: "utf8",
5242
6336
  maxBuffer: 64 * 1024 * 1024
5243
6337
  });
@@ -5305,8 +6399,8 @@ function runLensReport(args) {
5305
6399
  }
5306
6400
  const report = scanProjectLensTarget(options.value.targetDir);
5307
6401
  const markdown = renderProjectLensMarkdownReport(report);
5308
- mkdirSync9(dirname13(options.value.outPath), { recursive: true });
5309
- writeFileSync8(options.value.outPath, markdown);
6402
+ mkdirSync10(dirname14(options.value.outPath), { recursive: true });
6403
+ writeFileSync9(options.value.outPath, markdown);
5310
6404
  console.log(`report: ${options.value.outPath}`);
5311
6405
  return 0;
5312
6406
  }
@@ -5411,19 +6505,18 @@ function printUsage4() {
5411
6505
  }
5412
6506
 
5413
6507
  // src/commands/portfolio.ts
5414
- import { existsSync as existsSync33, readFileSync as readFileSync18 } from "node:fs";
5415
- import { join as join34 } from "node:path";
6508
+ import { existsSync as existsSync34, readFileSync as readFileSync19 } from "node:fs";
6509
+ import { join as join35 } from "node:path";
5416
6510
 
5417
6511
  // src/portfolio/doctor.ts
5418
- import { spawnSync as spawnSync7 } from "node:child_process";
5419
- import { existsSync as existsSync24, readFileSync as readFileSync16 } from "node:fs";
6512
+ import { spawnSync as spawnSync5 } from "node:child_process";
6513
+ import { existsSync as existsSync25, readFileSync as readFileSync17 } from "node:fs";
5420
6514
  import { createRequire as createRequire2 } from "node:module";
5421
6515
  import { homedir as homedir4 } from "node:os";
5422
- import { dirname as dirname15, join as join25 } from "node:path";
6516
+ import { dirname as dirname16, join as join26 } from "node:path";
5423
6517
  import { fileURLToPath as fileURLToPath4 } from "node:url";
5424
6518
 
5425
6519
  // src/host-tooling/inventory.ts
5426
- import { spawnSync as spawnSync5 } from "node:child_process";
5427
6520
  function inspectHostTooling(requirements, runner = defaultRunner2) {
5428
6521
  const hosts = [];
5429
6522
  const issues = [];
@@ -5497,7 +6590,7 @@ function parseHostPlugins(host, value) {
5497
6590
  });
5498
6591
  }
5499
6592
  function defaultRunner2({ command: command2 }) {
5500
- const result = spawnSync5(command2[0] ?? "", command2.slice(1), {
6593
+ const result = spawnPlatformSync(command2[0] ?? "", command2.slice(1), {
5501
6594
  encoding: "utf8",
5502
6595
  timeout: 1e4
5503
6596
  });
@@ -5512,8 +6605,8 @@ function isRecord2(value) {
5512
6605
  }
5513
6606
 
5514
6607
  // src/portfolio/asset-state.ts
5515
- import { existsSync as existsSync22, lstatSync as lstatSync8, readFileSync as readFileSync14 } from "node:fs";
5516
- import { join as join23 } from "node:path";
6608
+ import { existsSync as existsSync23, lstatSync as lstatSync9, readFileSync as readFileSync15 } from "node:fs";
6609
+ import { join as join24 } from "node:path";
5517
6610
  function comparePortfolioAssetState(options) {
5518
6611
  const expectedManifest = readPlanDocument(
5519
6612
  options.expectedPlan,
@@ -5524,10 +6617,10 @@ function comparePortfolioAssetState(options) {
5524
6617
  ".pro-gov/assets.lock.json"
5525
6618
  );
5526
6619
  const currentManifest = readJsonFile(
5527
- join23(options.targetDir, ".pro-gov/assets.json")
6620
+ join24(options.targetDir, ".pro-gov/assets.json")
5528
6621
  );
5529
6622
  const currentLock = readJsonFile(
5530
- join23(options.targetDir, ".pro-gov/assets.lock.json")
6623
+ join24(options.targetDir, ".pro-gov/assets.lock.json")
5531
6624
  );
5532
6625
  const issues = [];
5533
6626
  if (!sameStrings(currentManifest?.bundleIds, expectedManifest?.bundleIds)) {
@@ -5555,7 +6648,7 @@ function comparePortfolioAssetState(options) {
5555
6648
  (action) => action.type === "adopt-symlink" && action.assetId === entry.id && action.legacyTargetPath === entry.targetPath
5556
6649
  ))
5557
6650
  continue;
5558
- const targetAbsolutePath = join23(options.targetDir, entry.targetPath);
6651
+ const targetAbsolutePath = join24(options.targetDir, entry.targetPath);
5559
6652
  if (!pathIsSymlink(targetAbsolutePath)) continue;
5560
6653
  issues.push({
5561
6654
  type: "orphaned-managed-symlink",
@@ -5577,9 +6670,9 @@ function readPlanDocument(plan, targetPath) {
5577
6670
  }
5578
6671
  }
5579
6672
  function readJsonFile(path) {
5580
- if (!existsSync22(path)) return void 0;
6673
+ if (!existsSync23(path)) return void 0;
5581
6674
  try {
5582
- return JSON.parse(readFileSync14(path, "utf8"));
6675
+ return JSON.parse(readFileSync15(path, "utf8"));
5583
6676
  } catch {
5584
6677
  return void 0;
5585
6678
  }
@@ -5605,16 +6698,16 @@ function normalizeLock(lock) {
5605
6698
  }
5606
6699
  function pathIsSymlink(path) {
5607
6700
  try {
5608
- return lstatSync8(path).isSymbolicLink();
6701
+ return lstatSync9(path).isSymbolicLink();
5609
6702
  } catch {
5610
6703
  return false;
5611
6704
  }
5612
6705
  }
5613
6706
 
5614
6707
  // src/portfolio/version-policy.ts
5615
- import { spawnSync as spawnSync6 } from "node:child_process";
5616
- import { existsSync as existsSync23, lstatSync as lstatSync9, readFileSync as readFileSync15 } from "node:fs";
5617
- import { dirname as dirname14, join as join24 } from "node:path";
6708
+ import { spawnSync as spawnSync4 } from "node:child_process";
6709
+ import { existsSync as existsSync24, lstatSync as lstatSync10, readFileSync as readFileSync16 } from "node:fs";
6710
+ import { dirname as dirname15, join as join25 } from "node:path";
5618
6711
  function inspectVersionPolicy(root, policy, projectType) {
5619
6712
  if (!policy) return { status: "compliant", packages: [], runtimes: [], attentionCount: 0 };
5620
6713
  const packageManifests = collectPackageManifests(root);
@@ -5704,7 +6797,7 @@ function inspectRuntime(expectedName, expectedVersion) {
5704
6797
  function readRuntimeVersion(name) {
5705
6798
  if (name === "node") return process.versions.node;
5706
6799
  if (name !== "deno") return void 0;
5707
- const result = spawnSync6("deno", ["--version"], { encoding: "utf8" });
6800
+ const result = spawnSync4("deno", ["--version"], { encoding: "utf8" });
5708
6801
  if (result.status !== 0) return void 0;
5709
6802
  return /^deno\s+(\d+\.\d+\.\d+)/m.exec(result.stdout)?.[1];
5710
6803
  }
@@ -5723,10 +6816,10 @@ function findDeclaredVersion(packageJson, name) {
5723
6816
  function readInstalledVersion(root, name, fromDirectory = root) {
5724
6817
  let current = fromDirectory;
5725
6818
  while (true) {
5726
- const packageJson = readJson2(join24(current, "node_modules", name, "package.json"));
6819
+ const packageJson = readJson2(join25(current, "node_modules", name, "package.json"));
5727
6820
  if (typeof packageJson?.version === "string") return packageJson.version;
5728
6821
  if (current === root) return void 0;
5729
- const parent = dirname14(current);
6822
+ const parent = dirname15(current);
5730
6823
  if (parent === current) return void 0;
5731
6824
  current = parent;
5732
6825
  }
@@ -5761,15 +6854,15 @@ function collectPackageManifests(root) {
5761
6854
  if (relativePath.split("/").at(-1) !== "package.json" || directorySegments.length > 6 || directorySegments.some((segment) => ignored.has(segment))) {
5762
6855
  return;
5763
6856
  }
5764
- const path = join24(root, relativePath);
6857
+ const path = join25(root, relativePath);
5765
6858
  try {
5766
- if (!lstatSync9(path).isFile()) return;
6859
+ if (!lstatSync10(path).isFile()) return;
5767
6860
  } catch {
5768
6861
  return;
5769
6862
  }
5770
6863
  const packageJson = readJson2(path);
5771
6864
  if (!packageJson) return;
5772
- manifests.set(relativePath, { path: relativePath, directory: dirname14(path), packageJson });
6865
+ manifests.set(relativePath, { path: relativePath, directory: dirname15(path), packageJson });
5773
6866
  };
5774
6867
  const discovery = discoverRepositoryFiles(root, {
5775
6868
  gitPathspecs: ["package.json", ":(glob)**/package.json"],
@@ -5784,9 +6877,9 @@ function unique(values) {
5784
6877
  return [...new Set(values)];
5785
6878
  }
5786
6879
  function readJson2(path) {
5787
- if (!existsSync23(path)) return void 0;
6880
+ if (!existsSync24(path)) return void 0;
5788
6881
  try {
5789
- return JSON.parse(readFileSync15(path, "utf8"));
6882
+ return JSON.parse(readFileSync16(path, "utf8"));
5790
6883
  } catch {
5791
6884
  return void 0;
5792
6885
  }
@@ -5819,12 +6912,12 @@ function inspectTarget(options) {
5819
6912
  const { target } = options;
5820
6913
  const hostSsot = inspectProjectHostSsot(target.path);
5821
6914
  const issues = [];
5822
- const packageJson = readJson3(join25(target.path, "package.json"));
6915
+ const packageJson = readJson3(join26(target.path, "package.json"));
5823
6916
  const packages = {};
5824
6917
  for (const packageName of ["@pieai/pro-gov", "@pieai/doc-gov"]) {
5825
6918
  const declared = packageJson?.devDependencies?.[packageName] ?? packageJson?.dependencies?.[packageName];
5826
6919
  const installedPackage = readJson3(
5827
- join25(target.path, "node_modules", packageName, "package.json")
6920
+ join26(target.path, "node_modules", packageName, "package.json")
5828
6921
  );
5829
6922
  const installed = installedPackage?.version;
5830
6923
  const expected = options.expectedPackageVersions[packageName];
@@ -5884,7 +6977,7 @@ function inspectTarget(options) {
5884
6977
  type: "asset-lock-drift",
5885
6978
  message: error instanceof Error ? error.message : String(error)
5886
6979
  });
5887
- if (!existsSync24(join25(target.path, ".pro-gov/assets.json"))) {
6980
+ if (!existsSync25(join26(target.path, ".pro-gov/assets.json"))) {
5888
6981
  issues.push({ type: "bundle-drift", message: "Target asset manifest is missing." });
5889
6982
  }
5890
6983
  }
@@ -5902,15 +6995,15 @@ function inspectTarget(options) {
5902
6995
  };
5903
6996
  }
5904
6997
  function readTargetAssetHost(targetDir) {
5905
- const lockfile = readJson3(join25(targetDir, ".pro-gov/assets.lock.json"));
6998
+ const lockfile = readJson3(join26(targetDir, ".pro-gov/assets.lock.json"));
5906
6999
  return isAssetRegistryHost(lockfile?.host) ? lockfile.host : void 0;
5907
7000
  }
5908
7001
  function isAssetRegistryHost(value) {
5909
7002
  return value === "codex" || value === "claude-code" || value === "gemini-cli" || value === "antigravity";
5910
7003
  }
5911
7004
  function runTargetChecks(target) {
5912
- const proGovCli = join25(target.path, "node_modules/@pieai/pro-gov/dist/cli.js");
5913
- const docGovCli = join25(target.path, "node_modules/@pieai/doc-gov/dist/cli.js");
7005
+ const proGovCli = join26(target.path, "node_modules/@pieai/pro-gov/dist/cli.js");
7006
+ const docGovCli = join26(target.path, "node_modules/@pieai/doc-gov/dist/cli.js");
5914
7007
  const commands = [
5915
7008
  {
5916
7009
  name: "pro-gov doctor",
@@ -5921,8 +7014,8 @@ function runTargetChecks(target) {
5921
7014
  { name: "doc-gov scan --check", cli: docGovCli, args: ["scan", "--check"] }
5922
7015
  ];
5923
7016
  return commands.map((command2) => {
5924
- if (!existsSync24(command2.cli)) return { name: command2.name, status: null };
5925
- const result = spawnSync7(process.execPath, [command2.cli, ...command2.args], {
7017
+ if (!existsSync25(command2.cli)) return { name: command2.name, status: null };
7018
+ const result = spawnSync5(process.execPath, [command2.cli, ...command2.args], {
5926
7019
  cwd: target.path,
5927
7020
  encoding: "utf8",
5928
7021
  timeout: 3e4
@@ -5931,13 +7024,13 @@ function runTargetChecks(target) {
5931
7024
  });
5932
7025
  }
5933
7026
  function inspectGit(path) {
5934
- const inside = spawnSync7("git", ["rev-parse", "--is-inside-work-tree"], {
7027
+ const inside = spawnSync5("git", ["rev-parse", "--is-inside-work-tree"], {
5935
7028
  cwd: path,
5936
7029
  encoding: "utf8"
5937
7030
  });
5938
7031
  if (inside.status !== 0) return { isRepository: false, dirty: false };
5939
- const status = spawnSync7("git", ["status", "--porcelain"], { cwd: path, encoding: "utf8" });
5940
- const branch = spawnSync7("git", ["branch", "--show-current"], { cwd: path, encoding: "utf8" });
7032
+ const status = spawnSync5("git", ["status", "--porcelain"], { cwd: path, encoding: "utf8" });
7033
+ const branch = spawnSync5("git", ["branch", "--show-current"], { cwd: path, encoding: "utf8" });
5941
7034
  return {
5942
7035
  isRepository: true,
5943
7036
  dirty: status.stdout.trim().length > 0,
@@ -5960,18 +7053,18 @@ function getExpectedPackageVersions() {
5960
7053
  };
5961
7054
  }
5962
7055
  function findOwnPackageJson() {
5963
- let current = dirname15(fileURLToPath4(import.meta.url));
7056
+ let current = dirname16(fileURLToPath4(import.meta.url));
5964
7057
  for (let depth = 0; depth < 5; depth += 1) {
5965
- const candidate = join25(current, "package.json");
5966
- if (existsSync24(candidate)) return candidate;
5967
- current = dirname15(current);
7058
+ const candidate = join26(current, "package.json");
7059
+ if (existsSync25(candidate)) return candidate;
7060
+ current = dirname16(current);
5968
7061
  }
5969
7062
  return "";
5970
7063
  }
5971
7064
  function readJson3(path) {
5972
- if (!path || !existsSync24(path)) return void 0;
7065
+ if (!path || !existsSync25(path)) return void 0;
5973
7066
  try {
5974
- return JSON.parse(readFileSync16(path, "utf8"));
7067
+ return JSON.parse(readFileSync17(path, "utf8"));
5975
7068
  } catch {
5976
7069
  return void 0;
5977
7070
  }
@@ -5988,15 +7081,15 @@ function deduplicateIssues(issues) {
5988
7081
 
5989
7082
  // src/portfolio/ai-health/index.ts
5990
7083
  import { homedir as homedir5 } from "node:os";
5991
- import { dirname as dirname17, join as join33, resolve as resolve9 } from "node:path";
7084
+ import { dirname as dirname18, join as join34, resolve as resolve10 } from "node:path";
5992
7085
 
5993
7086
  // src/portfolio/ai-health/entries.ts
5994
- import { existsSync as existsSync26, lstatSync as lstatSync11, realpathSync as realpathSync7 } from "node:fs";
5995
- import { join as join26 } from "node:path";
7087
+ import { existsSync as existsSync27, lstatSync as lstatSync12, realpathSync as realpathSync7 } from "node:fs";
7088
+ import { join as join27 } from "node:path";
5996
7089
 
5997
7090
  // src/portfolio/ai-health/shared.ts
5998
7091
  import { execFileSync as execFileSync2 } from "node:child_process";
5999
- import { existsSync as existsSync25, lstatSync as lstatSync10, readFileSync as readFileSync17, readdirSync as readdirSync11, realpathSync as realpathSync6, statSync as statSync7 } from "node:fs";
7092
+ import { existsSync as existsSync26, lstatSync as lstatSync11, readFileSync as readFileSync18, readdirSync as readdirSync12, realpathSync as realpathSync6, statSync as statSync8 } from "node:fs";
6000
7093
  function safeRealpath(path) {
6001
7094
  try {
6002
7095
  return realpathSync6(path);
@@ -6006,12 +7099,13 @@ function safeRealpath(path) {
6006
7099
  }
6007
7100
  function commandVersion(command2) {
6008
7101
  try {
6009
- const output = execFileSync2(command2, ["version"], {
7102
+ const result = spawnPlatformSync(command2, ["version"], {
6010
7103
  encoding: "utf8",
6011
7104
  stdio: ["ignore", "pipe", "ignore"],
6012
7105
  timeout: 3e3
6013
7106
  });
6014
- return output.match(/\b\d+\.\d+\.\d+\b/)?.[0];
7107
+ if (result.error || result.status !== 0) return void 0;
7108
+ return result.stdout.match(/\b\d+\.\d+\.\d+\b/)?.[0];
6015
7109
  } catch {
6016
7110
  return void 0;
6017
7111
  }
@@ -6026,7 +7120,7 @@ function jsonObjectKeys(path, key) {
6026
7120
  return Object.keys(value[key]).sort();
6027
7121
  }
6028
7122
  function tomlMcpNames(path) {
6029
- if (!existsSync25(path)) return [];
7123
+ if (!existsSync26(path)) return [];
6030
7124
  const names = /* @__PURE__ */ new Set();
6031
7125
  for (const line of safeRead(path).split(/\r?\n/)) {
6032
7126
  const match = line.match(/^\s*\[mcp_servers\.(?:"([^"]+)"|([^.\]]+))\]\s*$/);
@@ -6037,35 +7131,35 @@ function tomlMcpNames(path) {
6037
7131
  }
6038
7132
  function readJson4(path) {
6039
7133
  try {
6040
- return JSON.parse(readFileSync17(path, "utf8"));
7134
+ return JSON.parse(readFileSync18(path, "utf8"));
6041
7135
  } catch {
6042
7136
  return void 0;
6043
7137
  }
6044
7138
  }
6045
7139
  function safeRead(path) {
6046
7140
  try {
6047
- return readFileSync17(path, "utf8");
7141
+ return readFileSync18(path, "utf8");
6048
7142
  } catch {
6049
7143
  return "";
6050
7144
  }
6051
7145
  }
6052
7146
  function safeReadDir(path) {
6053
7147
  try {
6054
- return readdirSync11(path).sort();
7148
+ return readdirSync12(path).sort();
6055
7149
  } catch {
6056
7150
  return [];
6057
7151
  }
6058
7152
  }
6059
7153
  function safeIsDirectory(path) {
6060
7154
  try {
6061
- return statSync7(path).isDirectory();
7155
+ return statSync8(path).isDirectory();
6062
7156
  } catch {
6063
7157
  return false;
6064
7158
  }
6065
7159
  }
6066
7160
  function pathLexists(path) {
6067
7161
  try {
6068
- lstatSync10(path);
7162
+ lstatSync11(path);
6069
7163
  return true;
6070
7164
  } catch {
6071
7165
  return false;
@@ -6103,12 +7197,12 @@ function hasWorkflowReminderHooks(hooks) {
6103
7197
  );
6104
7198
  }
6105
7199
  function inspectEntries(root) {
6106
- const agentsPath = join26(root, "AGENTS.md");
6107
- const agents = !existsSync26(agentsPath) ? "missing" : safeRead(agentsPath).includes("PGS-ROUTER:BEGIN") ? "pgs-router" : "custom";
6108
- const claudePath = join26(root, "CLAUDE.md");
7200
+ const agentsPath = join27(root, "AGENTS.md");
7201
+ const agents = !existsSync27(agentsPath) ? "missing" : safeRead(agentsPath).includes("PGS-ROUTER:BEGIN") ? "pgs-router" : "custom";
7202
+ const claudePath = join27(root, "CLAUDE.md");
6109
7203
  let claude = "missing";
6110
7204
  if (pathLexists(claudePath)) {
6111
- const info = lstatSync11(claudePath);
7205
+ const info = lstatSync12(claudePath);
6112
7206
  if (info.isSymbolicLink()) {
6113
7207
  try {
6114
7208
  claude = realpathSync7(claudePath) === realpathSync7(agentsPath) ? "agents-symlink" : "custom";
@@ -6125,19 +7219,19 @@ function inspectEntries(root) {
6125
7219
  var AGENT_LINK_ROOTS = [".agents/workflows", ".agents/commands", ".claude/commands"];
6126
7220
  function inspectAgentLinks(root) {
6127
7221
  const entries = AGENT_LINK_ROOTS.flatMap((directory) => {
6128
- const directoryPath = join26(root, directory);
7222
+ const directoryPath = join27(root, directory);
6129
7223
  if (!pathLexists(directoryPath)) return [];
6130
7224
  try {
6131
- if (!lstatSync11(directoryPath).isDirectory()) return [];
7225
+ if (!lstatSync12(directoryPath).isDirectory()) return [];
6132
7226
  } catch {
6133
7227
  return [];
6134
7228
  }
6135
7229
  return safeReadDir(directoryPath).filter((name) => !name.startsWith(".")).flatMap((name) => {
6136
7230
  const relativePath = `${directory}/${name}`;
6137
- const path = join26(root, relativePath);
7231
+ const path = join27(root, relativePath);
6138
7232
  let stat;
6139
7233
  try {
6140
- stat = lstatSync11(path);
7234
+ stat = lstatSync12(path);
6141
7235
  } catch {
6142
7236
  return [];
6143
7237
  }
@@ -6158,9 +7252,9 @@ function inspectAgentLinks(root) {
6158
7252
  };
6159
7253
  }
6160
7254
  function inspectOptionalEntry(root, filename, agentsPath) {
6161
- const path = join26(root, filename);
7255
+ const path = join27(root, filename);
6162
7256
  if (!pathLexists(path)) return "missing";
6163
- const info = lstatSync11(path);
7257
+ const info = lstatSync12(path);
6164
7258
  if (info.isSymbolicLink()) {
6165
7259
  try {
6166
7260
  return realpathSync7(path) === realpathSync7(agentsPath) ? "agents-symlink" : "custom";
@@ -6198,7 +7292,7 @@ function inspectHooks(root) {
6198
7292
  { host: "codex", path: ".codex/hooks.json" }
6199
7293
  ];
6200
7294
  return configs.map((config) => {
6201
- const value = readJson4(join26(root, config.path));
7295
+ const value = readJson4(join27(root, config.path));
6202
7296
  const counts = /* @__PURE__ */ new Map();
6203
7297
  collectHookEvents(value, counts);
6204
7298
  return {
@@ -6219,18 +7313,18 @@ function collectHookEvents(value, counts) {
6219
7313
  }
6220
7314
  }
6221
7315
  function inspectDocs(root, expected) {
6222
- const packageJson = readJson4(join26(root, "package.json"));
7316
+ const packageJson = readJson4(join27(root, "package.json"));
6223
7317
  const dependencies = isRecord3(packageJson) ? { ...recordOrEmpty(packageJson.dependencies), ...recordOrEmpty(packageJson.devDependencies) } : {};
6224
7318
  const docGov = dependencyVersion(dependencies["@pieai/doc-gov"]);
6225
7319
  const proGov = dependencyVersion(dependencies["@pieai/pro-gov"]);
6226
- const routerMatch = safeRead(join26(root, "AGENTS.md")).match(/PGS-ROUTER:BEGIN\s+v([0-9.]+)/);
7320
+ const routerMatch = safeRead(join27(root, "AGENTS.md")).match(/PGS-ROUTER:BEGIN\s+v([0-9.]+)/);
6227
7321
  const declared = [docGov, proGov].filter((value) => Boolean(value));
6228
7322
  return {
6229
7323
  routerVersion: routerMatch?.[1],
6230
7324
  expectedRouterVersion: CURRENT_ROUTER_VERSION,
6231
7325
  routerAligned: routerMatch?.[1] === CURRENT_ROUTER_VERSION,
6232
- manifest: existsSync26(join26(root, "docs/governance/MANIFEST.yml")),
6233
- currentWork: existsSync26(join26(root, "docs/reference/execution/current-work.md")),
7326
+ manifest: existsSync27(join27(root, "docs/governance/MANIFEST.yml")),
7327
+ currentWork: existsSync27(join27(root, "docs/reference/execution/current-work.md")),
6234
7328
  packages: {
6235
7329
  expected,
6236
7330
  docGov,
@@ -6292,19 +7386,17 @@ function inspectGit2(root) {
6292
7386
  }
6293
7387
 
6294
7388
  // src/portfolio/ai-health/hosts.ts
6295
- import { execFileSync as execFileSync5 } from "node:child_process";
6296
- import { existsSync as existsSync28 } from "node:fs";
6297
- import { join as join28, resolve as resolve8, sep as sep3 } from "node:path";
7389
+ import { existsSync as existsSync29 } from "node:fs";
7390
+ import { join as join29, resolve as resolve9, sep as sep3 } from "node:path";
6298
7391
 
6299
7392
  // src/portfolio/ai-health/devspace.ts
6300
- import { execFileSync as execFileSync4 } from "node:child_process";
6301
- import { existsSync as existsSync27, statSync as statSync8 } from "node:fs";
6302
- import { join as join27, relative as relative10, resolve as resolve7, sep as sep2 } from "node:path";
7393
+ import { existsSync as existsSync28, statSync as statSync9 } from "node:fs";
7394
+ import { join as join28, relative as relative11, resolve as resolve8, sep as sep2 } from "node:path";
6303
7395
  function inspectDevSpaceHealth(options) {
6304
7396
  const run = options.run ?? runDevSpaceCommand;
6305
- const configDirectory = join27(options.homeDir, ".devspace");
6306
- const configPath = join27(configDirectory, "config.json");
6307
- const authPath = join27(configDirectory, "auth.json");
7397
+ const configDirectory = join28(options.homeDir, ".devspace");
7398
+ const configPath = join28(configDirectory, "config.json");
7399
+ const authPath = join28(configDirectory, "auth.json");
6308
7400
  const installedResult = run("devspace", ["--version"], 3e3);
6309
7401
  const installedVersion = installedResult.ok ? installedResult.stdout.match(/\b\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?\b/)?.[0] : void 0;
6310
7402
  const latestResult = run(
@@ -6313,9 +7405,9 @@ function inspectDevSpaceHealth(options) {
6313
7405
  5e3
6314
7406
  );
6315
7407
  const latestVersion = latestResult.ok ? latestResult.stdout.match(/\b\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?\b/)?.[0] : void 0;
6316
- const processResult = run("pgrep", ["-f", "devspace serve"], 3e3);
6317
- const pid = processResult.ok ? processResult.stdout.match(/\b\d+\b/)?.[0] : void 0;
6318
- const processEnvironment = pid ? run("ps", ["eww", "-p", pid, "-o", "command="], 3e3) : void 0;
7408
+ const processResult = process.platform === "win32" ? run("tasklist", ["/FI", "IMAGENAME eq devspace.exe", "/FO", "CSV", "/NH"], 3e3) : run("pgrep", ["-f", "devspace serve"], 3e3);
7409
+ const pid = processResult.ok ? process.platform === "win32" ? processResult.stdout.match(/"devspace\.exe","(\d+)"/i)?.[1] : processResult.stdout.match(/\b\d+\b/)?.[0] : void 0;
7410
+ const processEnvironment = process.platform !== "win32" && pid ? run("ps", ["eww", "-p", pid, "-o", "command="], 3e3) : void 0;
6319
7411
  const processToolMode = processEnvironment?.ok ? processEnvironment.stdout.match(
6320
7412
  /(?:^|\s)DEVSPACE_TOOL_MODE=(minimal|full|codex)(?:\s|$)/
6321
7413
  )?.[1] : void 0;
@@ -6327,9 +7419,9 @@ function inspectDevSpaceHealth(options) {
6327
7419
  (repositoryPath) => allowedRoots.some((root) => isPathInside(repositoryPath, root))
6328
7420
  ) ? "complete" : "partial";
6329
7421
  const bind = configExists && typeof configValue.host === "string" ? isLoopbackHost(configValue.host) ? "loopback" : "non-loopback" : "unknown";
6330
- const directoryMode = existsSync27(configDirectory) ? modeString(statSync8(configDirectory).mode) : void 0;
6331
- const fileMode = existsSync27(configPath) ? modeString(statSync8(configPath).mode) : void 0;
6332
- const authMode = existsSync27(authPath) ? modeString(statSync8(authPath).mode) : void 0;
7422
+ const directoryMode = process.platform !== "win32" && existsSync28(configDirectory) ? modeString(statSync9(configDirectory).mode) : void 0;
7423
+ const fileMode = process.platform !== "win32" && existsSync28(configPath) ? modeString(statSync9(configPath).mode) : void 0;
7424
+ const authMode = process.platform !== "win32" && existsSync28(authPath) ? modeString(statSync9(authPath).mode) : void 0;
6333
7425
  const update = installedVersion && latestVersion ? installedVersion === latestVersion ? "current" : "available" : "unknown";
6334
7426
  const recommendations = [];
6335
7427
  let status = "healthy";
@@ -6343,7 +7435,7 @@ function inspectDevSpaceHealth(options) {
6343
7435
  };
6344
7436
  if (!installedResult.ok) unhealthy("\u672C\u673A\u672A\u53D1\u73B0 DevSpace\uFF1B\u65E0\u6CD5\u4F7F\u7528\u5BBF\u4E3B\u5DE5\u4F5C\u533A\u670D\u52A1\u3002");
6345
7437
  if (!configExists) unhealthy("\u7F3A\u5C11 ~/.devspace/config.json\u3002");
6346
- if (!existsSync27(authPath)) unhealthy("\u7F3A\u5C11 ~/.devspace/auth.json\u3002");
7438
+ if (!existsSync28(authPath)) unhealthy("\u7F3A\u5C11 ~/.devspace/auth.json\u3002");
6347
7439
  if (directoryMode && directoryMode !== "700")
6348
7440
  unhealthy(`~/.devspace \u76EE\u5F55\u6743\u9650\u4E3A ${directoryMode}\uFF0C\u5E94\u6536\u7D27\u4E3A 700\u3002`);
6349
7441
  if (fileMode && fileMode !== "600") unhealthy(`DevSpace \u914D\u7F6E\u6587\u4EF6\u6743\u9650\u4E3A ${fileMode}\uFF0C\u5E94\u4E3A 600\u3002`);
@@ -6353,7 +7445,7 @@ function inspectDevSpaceHealth(options) {
6353
7445
  if (doctor === "failed") unhealthy("devspace doctor \u672A\u901A\u8FC7\u3002");
6354
7446
  if (portfolioCoverage === "partial") attention("DevSpace allowedRoots \u6CA1\u6709\u8986\u76D6\u5168\u90E8\u5DF2\u767B\u8BB0\u4ED3\u5E93\u3002");
6355
7447
  if (installedResult.ok && !pid) attention("DevSpace \u5DF2\u5B89\u88C5\u4F46\u5F53\u524D\u6CA1\u6709\u8FD0\u884C\u3002");
6356
- if (options.expectedToolMode && pid && processToolMode !== options.expectedToolMode) {
7448
+ if (options.expectedToolMode && pid && processToolMode !== void 0 && processToolMode !== options.expectedToolMode) {
6357
7449
  attention(
6358
7450
  `\u8FD0\u884C\u4E2D\u7684 DevSpace \u5DE5\u5177\u6A21\u5F0F\u4E3A ${processToolMode ?? "unknown"}\uFF0C\u671F\u671B ${options.expectedToolMode}\u3002`
6359
7451
  );
@@ -6376,7 +7468,7 @@ function inspectDevSpaceHealth(options) {
6376
7468
  exists: configExists,
6377
7469
  ...directoryMode ? { directoryMode } : {},
6378
7470
  ...fileMode ? { fileMode } : {},
6379
- authExists: existsSync27(authPath),
7471
+ authExists: existsSync28(authPath),
6380
7472
  ...authMode ? { authMode } : {},
6381
7473
  bind,
6382
7474
  portValid: configExists && typeof configValue.port === "number" && Number.isInteger(configValue.port) && configValue.port > 0 && configValue.port <= 65535,
@@ -6388,24 +7480,18 @@ function inspectDevSpaceHealth(options) {
6388
7480
  };
6389
7481
  }
6390
7482
  function runDevSpaceCommand(command2, args, timeout) {
6391
- try {
6392
- return {
6393
- ok: true,
6394
- stdout: execFileSync4(command2, args, {
6395
- encoding: "utf8",
6396
- stdio: ["ignore", "pipe", "ignore"],
6397
- timeout
6398
- })
6399
- };
6400
- } catch {
6401
- return { ok: false, stdout: "" };
6402
- }
7483
+ const result = spawnPlatformSync(command2, args, {
7484
+ encoding: "utf8",
7485
+ stdio: ["ignore", "pipe", "ignore"],
7486
+ timeout
7487
+ });
7488
+ return result.error || result.status !== 0 ? { ok: false, stdout: "" } : { ok: true, stdout: result.stdout };
6403
7489
  }
6404
7490
  function isLoopbackHost(host) {
6405
7491
  return ["127.0.0.1", "localhost", "::1"].includes(host.trim().toLowerCase());
6406
7492
  }
6407
7493
  function isPathInside(path, root) {
6408
- const fromRoot = relative10(resolve7(root), resolve7(path));
7494
+ const fromRoot = relative11(resolve8(root), resolve8(path));
6409
7495
  return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep2}`);
6410
7496
  }
6411
7497
 
@@ -6423,9 +7509,9 @@ var MCP_DISCOVERY_PATHS = {
6423
7509
  }
6424
7510
  };
6425
7511
  function inspectHostEnvironment(homeDir, grokVersion, repositoryPaths, devspaceSettings) {
6426
- const codexConfig = join28(homeDir, MCP_DISCOVERY_PATHS.user.codex);
6427
- const claudeConfig = join28(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode);
6428
- const grokConfig = join28(homeDir, MCP_DISCOVERY_PATHS.user.grok);
7512
+ const codexConfig = join29(homeDir, MCP_DISCOVERY_PATHS.user.codex);
7513
+ const claudeConfig = join29(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode);
7514
+ const grokConfig = join29(homeDir, MCP_DISCOVERY_PATHS.user.grok);
6429
7515
  const hostEnvironment = {
6430
7516
  mcp: {
6431
7517
  codexUser: { path: codexConfig, names: tomlMcpNames(codexConfig) },
@@ -6433,11 +7519,11 @@ function inspectHostEnvironment(homeDir, grokVersion, repositoryPaths, devspaceS
6433
7519
  grokUser: { path: grokConfig, names: tomlMcpNames(grokConfig) }
6434
7520
  },
6435
7521
  skills: {
6436
- codexUser: inspectSkillRoot(join28(homeDir, ".agents/skills")),
6437
- claudeCodeUser: inspectSkillRoot(join28(homeDir, ".claude/skills")),
6438
- grokUser: inspectSkillRoot(join28(homeDir, ".grok/skills")),
6439
- grokAgentsCompatibility: inspectSkillRoot(join28(homeDir, ".agents/skills")),
6440
- grokClaudeCompatibility: inspectSkillRoot(join28(homeDir, ".claude/skills")),
7522
+ codexUser: inspectSkillRoot(join29(homeDir, ".agents/skills")),
7523
+ claudeCodeUser: inspectSkillRoot(join29(homeDir, ".claude/skills")),
7524
+ grokUser: inspectSkillRoot(join29(homeDir, ".grok/skills")),
7525
+ grokAgentsCompatibility: inspectSkillRoot(join29(homeDir, ".agents/skills")),
7526
+ grokClaudeCompatibility: inspectSkillRoot(join29(homeDir, ".claude/skills")),
6441
7527
  ssot: inspectUserSkillsSsot(homeDir)
6442
7528
  },
6443
7529
  grok: {
@@ -6458,20 +7544,20 @@ function inspectHostEnvironment(homeDir, grokVersion, repositoryPaths, devspaceS
6458
7544
  function inspectSkillRoot(path) {
6459
7545
  const exists = pathLexists(path) && safeIsDirectory(path);
6460
7546
  const names = exists ? safeReadDir(path).filter(
6461
- (name) => !name.startsWith(".") && existsSync28(join28(path, name, "SKILL.md"))
7547
+ (name) => !name.startsWith(".") && existsSync29(join29(path, name, "SKILL.md"))
6462
7548
  ) : [];
6463
7549
  return { path, exists, names };
6464
7550
  }
6465
7551
  function claudeProjectLocalMcpNames(homeDir, root) {
6466
7552
  if (!homeDir) return [];
6467
- const value = readJson4(join28(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode));
7553
+ const value = readJson4(join29(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode));
6468
7554
  if (!isRecord3(value) || !isRecord3(value.projects)) return [];
6469
7555
  const candidates = new Set(
6470
- [resolve8(root), safeRealpath(root)].filter((path) => Boolean(path))
7556
+ [resolve9(root), safeRealpath(root)].filter((path) => Boolean(path))
6471
7557
  );
6472
7558
  const names = /* @__PURE__ */ new Set();
6473
7559
  for (const [path, project] of Object.entries(value.projects)) {
6474
- const projectPaths = [resolve8(path), safeRealpath(path)].filter(
7560
+ const projectPaths = [resolve9(path), safeRealpath(path)].filter(
6475
7561
  (candidate) => Boolean(candidate)
6476
7562
  );
6477
7563
  if (!projectPaths.some((candidate) => candidates.has(candidate)) || !isRecord3(project) || !isRecord3(project.mcpServers))
@@ -6490,19 +7576,19 @@ function inspectGrokProject(root, homeDir, grokVersion) {
6490
7576
  });
6491
7577
  if (!grokVersion) return empty("unavailable");
6492
7578
  try {
6493
- const value = JSON.parse(
6494
- execFileSync5("grok", ["inspect", "--json"], {
6495
- cwd: root,
6496
- encoding: "utf8",
6497
- env: { ...process.env, HOME: homeDir, GROK_HOME: join28(homeDir, ".grok") },
6498
- maxBuffer: 10 * 1024 * 1024,
6499
- stdio: ["ignore", "pipe", "ignore"],
6500
- timeout: 8e3
6501
- })
6502
- );
7579
+ const result = spawnPlatformSync("grok", ["inspect", "--json"], {
7580
+ cwd: root,
7581
+ encoding: "utf8",
7582
+ env: { ...process.env, HOME: homeDir, GROK_HOME: join29(homeDir, ".grok") },
7583
+ maxBuffer: 10 * 1024 * 1024,
7584
+ stdio: ["ignore", "pipe", "ignore"],
7585
+ timeout: 8e3
7586
+ });
7587
+ if (result.error || result.status !== 0) return empty("failed");
7588
+ const value = JSON.parse(result.stdout);
6503
7589
  if (!isRecord3(value)) return empty("failed");
6504
7590
  const userClaudeNames = new Set(
6505
- jsonObjectKeys(join28(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode), "mcpServers")
7591
+ jsonObjectKeys(join29(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode), "mcpServers")
6506
7592
  );
6507
7593
  const localClaudeNames = new Set(claudeProjectLocalMcpNames(homeDir, root));
6508
7594
  const effectiveMcp = Array.isArray(value.mcpServers) ? value.mcpServers.flatMap((item) => {
@@ -6570,25 +7656,26 @@ function inferGrokMcpScope(name, sourceType, sourcePath, root, homeDir, userClau
6570
7656
  if (userClaudeNames.has(name)) return "user";
6571
7657
  return "unknown";
6572
7658
  }
6573
- const resolvedSource = safeRealpath(sourcePath) ?? resolve8(sourcePath);
6574
- const resolvedRoot = safeRealpath(root) ?? resolve8(root);
6575
- if (resolvedSource === join28(resolvedRoot, MCP_DISCOVERY_PATHS.project.claudeCodeShared))
7659
+ const resolvedSource = safeRealpath(sourcePath) ?? resolve9(sourcePath);
7660
+ const resolvedRoot = safeRealpath(root) ?? resolve9(root);
7661
+ if (resolvedSource === join29(resolvedRoot, MCP_DISCOVERY_PATHS.project.claudeCodeShared))
6576
7662
  return "project-shared";
6577
7663
  if (resolvedSource.startsWith(resolvedRoot + sep3)) return "project";
6578
- if (homeDir && resolvedSource === join28(resolve8(homeDir), MCP_DISCOVERY_PATHS.user.claudeCode)) {
7664
+ if (homeDir && resolvedSource === join29(resolve9(homeDir), MCP_DISCOVERY_PATHS.user.claudeCode)) {
6579
7665
  if (localClaudeNames.has(name)) return "project-local";
6580
7666
  if (userClaudeNames.has(name)) return "user";
6581
7667
  }
6582
- if (homeDir && resolvedSource.startsWith(resolve8(homeDir) + sep3)) return "user";
7668
+ if (homeDir && resolvedSource.startsWith(resolve9(homeDir) + sep3)) return "user";
6583
7669
  if (sourceType === "project") return "project";
6584
7670
  return "unknown";
6585
7671
  }
6586
7672
 
6587
7673
  // src/portfolio/ai-health/secrets.ts
6588
- import { existsSync as existsSync29, lstatSync as lstatSync12, readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
6589
- import { join as join29, relative as relative11, sep as sep4 } from "node:path";
7674
+ import { existsSync as existsSync30, lstatSync as lstatSync13, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
7675
+ import { join as join30, relative as relative12, sep as sep4 } from "node:path";
7676
+ var supportsPosixModes = process.platform !== "win32";
6590
7677
  function inspectRepositorySecrets(root, id, secretsRoot, isRepository, environmentPolicy) {
6591
- const centralPath = join29(secretsRoot, id);
7678
+ const centralPath = join30(secretsRoot, id);
6592
7679
  const centralRealPath = safeRealpath(centralPath);
6593
7680
  const localOnlyReasons = new Map(
6594
7681
  (environmentPolicy?.localOnly ?? []).map((entry) => [entry.path, entry.reason])
@@ -6600,16 +7687,16 @@ function inspectRepositorySecrets(root, id, secretsRoot, isRepository, environme
6600
7687
  tracked: isRepository ? gitTracks(root, path) : false,
6601
7688
  template: isEnvironmentTemplate(path),
6602
7689
  fixture: isEnvironmentFixture(path),
6603
- symlink: lstatSync12(join29(root, path)).isSymbolicLink(),
6604
- centralized: pointsInside(join29(root, path), centralRealPath),
7690
+ symlink: lstatSync13(join30(root, path)).isSymbolicLink(),
7691
+ centralized: pointsInside(join30(root, path), centralRealPath),
6605
7692
  localOnly: localOnlyReason !== void 0,
6606
7693
  ...localOnlyReason !== void 0 ? { localOnlyReason } : {}
6607
7694
  };
6608
7695
  });
6609
7696
  return {
6610
- centralDirectory: existsSync29(centralPath) ? "present" : "absent",
6611
- centralMode: existsSync29(centralPath) ? modeString(statSync9(centralPath).mode) : void 0,
6612
- centralFiles: existsSync29(centralPath) ? collectCentralSecretFiles(centralPath) : [],
7697
+ centralDirectory: existsSync30(centralPath) ? "present" : "absent",
7698
+ centralMode: supportsPosixModes && existsSync30(centralPath) ? modeString(statSync10(centralPath).mode) : void 0,
7699
+ centralFiles: existsSync30(centralPath) ? collectCentralSecretFiles(centralPath) : [],
6613
7700
  repositoryEnvFiles: envFiles
6614
7701
  };
6615
7702
  }
@@ -6632,12 +7719,12 @@ function collectEnvironmentFiles(root, current = root, depth = 0) {
6632
7719
  if (depth > 5) return [];
6633
7720
  const found = [];
6634
7721
  try {
6635
- for (const entry of readdirSync12(current, { withFileTypes: true })) {
7722
+ for (const entry of readdirSync13(current, { withFileTypes: true })) {
6636
7723
  if (entry.isDirectory()) {
6637
7724
  if (!SKIP_ENV_DIRECTORIES.has(entry.name))
6638
- found.push(...collectEnvironmentFiles(root, join29(current, entry.name), depth + 1));
7725
+ found.push(...collectEnvironmentFiles(root, join30(current, entry.name), depth + 1));
6639
7726
  } else if (isEnvironmentFilename(entry.name) && !isProviderGeneratedEnvironmentFile(entry.name)) {
6640
- found.push(relative11(root, join29(current, entry.name)));
7727
+ found.push(relative12(root, join30(current, entry.name)).replaceAll("\\", "/"));
6641
7728
  }
6642
7729
  }
6643
7730
  } catch {
@@ -6649,10 +7736,14 @@ function collectCentralSecretFiles(root, current = root, depth = 0) {
6649
7736
  if (depth > 3) return [];
6650
7737
  const found = [];
6651
7738
  try {
6652
- for (const entry of readdirSync12(current, { withFileTypes: true })) {
6653
- const path = join29(current, entry.name);
7739
+ for (const entry of readdirSync13(current, { withFileTypes: true })) {
7740
+ const path = join30(current, entry.name);
6654
7741
  if (entry.isDirectory()) found.push(...collectCentralSecretFiles(root, path, depth + 1));
6655
- else found.push({ path: relative11(root, path), mode: modeString(lstatSync12(path).mode) });
7742
+ else
7743
+ found.push({
7744
+ path: relative12(root, path).replaceAll("\\", "/"),
7745
+ mode: supportsPosixModes ? modeString(lstatSync13(path).mode) : "unknown"
7746
+ });
6656
7747
  }
6657
7748
  } catch {
6658
7749
  return found;
@@ -6676,30 +7767,35 @@ function isEnvironmentFixture(path) {
6676
7767
  return /(^|[\\/])(?:tests?|__tests__)[\\/]fixtures?[\\/]/i.test(path) || /(^|[\\/])__fixtures__[\\/]/i.test(path);
6677
7768
  }
6678
7769
  function pointsInside(path, expectedRoot) {
6679
- if (!expectedRoot || !lstatSync12(path).isSymbolicLink()) return false;
7770
+ if (!expectedRoot || !lstatSync13(path).isSymbolicLink()) return false;
6680
7771
  const target = safeRealpath(path);
6681
7772
  if (!target) return false;
6682
- const fromRoot = relative11(expectedRoot, target);
7773
+ const fromRoot = relative12(expectedRoot, target);
6683
7774
  return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep4}`);
6684
7775
  }
6685
7776
  function hasUnsafeCentralSecretPermissions(secrets) {
7777
+ if (!supportsPosixModes) return false;
6686
7778
  return secrets.centralDirectory === "present" && (secrets.centralMode !== "700" || secrets.centralFiles.some((file) => file.mode !== "600"));
6687
7779
  }
6688
7780
  function inspectSecretsRoot(path) {
6689
- return existsSync29(path) ? { path, exists: true, mode: modeString(statSync9(path).mode) } : { path, exists: false };
7781
+ return existsSync30(path) ? {
7782
+ path,
7783
+ exists: true,
7784
+ ...supportsPosixModes ? { mode: modeString(statSync10(path).mode) } : {}
7785
+ } : { path, exists: false };
6690
7786
  }
6691
7787
 
6692
7788
  // src/portfolio/ai-health/skills.ts
6693
- import { existsSync as existsSync30, lstatSync as lstatSync13, realpathSync as realpathSync8 } from "node:fs";
6694
- import { join as join30 } from "node:path";
7789
+ import { existsSync as existsSync31, lstatSync as lstatSync14, realpathSync as realpathSync8 } from "node:fs";
7790
+ import { join as join31 } from "node:path";
6695
7791
  function countAutomaticSkillsNeedingReview(skills) {
6696
7792
  return skills.automatic.filter(
6697
7793
  (item) => !item.managed || !item.registryId || item.expectedPlacement !== "auto" || item.expectedScope === "user"
6698
7794
  ).length;
6699
7795
  }
6700
7796
  function inspectSkills(root, grokInspection, registeredSkills, userSkills) {
6701
- const lock = readJson4(join30(root, ".pro-gov/assets.lock.json"));
6702
- const assetManifest = readJson4(join30(root, ".pro-gov/assets.json"));
7797
+ const lock = readJson4(join31(root, ".pro-gov/assets.lock.json"));
7798
+ const assetManifest = readJson4(join31(root, ".pro-gov/assets.json"));
6703
7799
  const managed = /* @__PURE__ */ new Set();
6704
7800
  const bundleIds = stringArray(isRecord3(lock) ? lock.bundleIds : void 0);
6705
7801
  if (isRecord3(lock) && Array.isArray(lock.assets)) {
@@ -6711,7 +7807,7 @@ function inspectSkills(root, grokInspection, registeredSkills, userSkills) {
6711
7807
  }
6712
7808
  const inspectPlacement = (placement) => {
6713
7809
  const directory = placement === "auto" ? "skills" : "manual-skills";
6714
- const skillRoot = join30(root, ".agents", directory);
7810
+ const skillRoot = join31(root, ".agents", directory);
6715
7811
  if (!pathLexists(skillRoot) || !safeIsDirectory(skillRoot)) return [];
6716
7812
  return safeReadDir(skillRoot).filter((name) => !name.startsWith(".")).map((name) => inspectSkillItem(skillRoot, directory, name, managed, registeredSkills));
6717
7813
  };
@@ -6766,15 +7862,15 @@ function inspectSkills(root, grokInspection, registeredSkills, userSkills) {
6766
7862
  },
6767
7863
  hosts: {
6768
7864
  codexProject: automatic.filter((item) => item.kind !== "dangling-symlink").length,
6769
- claudeCodeProject: inspectSkillRoot(join30(root, ".claude/skills")).names.length,
6770
- grokNativeProject: inspectSkillRoot(join30(root, ".grok/skills")).names.length,
7865
+ claudeCodeProject: inspectSkillRoot(join31(root, ".claude/skills")).names.length,
7866
+ grokNativeProject: inspectSkillRoot(join31(root, ".grok/skills")).names.length,
6771
7867
  grokEffective: grokInspection.skills
6772
7868
  }
6773
7869
  };
6774
7870
  }
6775
7871
  function inspectSkillItem(skillRoot, directory, name, managed, registeredSkills) {
6776
- const path = join30(skillRoot, name);
6777
- const stat = lstatSync13(path);
7872
+ const path = join31(skillRoot, name);
7873
+ const stat = lstatSync14(path);
6778
7874
  let kind = stat.isSymbolicLink() ? "symlink" : stat.isDirectory() ? "directory" : "file";
6779
7875
  let realPath;
6780
7876
  try {
@@ -6783,7 +7879,7 @@ function inspectSkillItem(skillRoot, directory, name, managed, registeredSkills)
6783
7879
  if (stat.isSymbolicLink()) kind = "dangling-symlink";
6784
7880
  }
6785
7881
  const registered = realPath ? registeredSkills.find((skill) => skill.sourceRealPath === realPath) : void 0;
6786
- const classification = registered ? void 0 : realPath && isPluginPack(realPath) ? "plugin-pack" : kind === "directory" && existsSync30(join30(path, "SKILL.md")) ? "project-local" : void 0;
7882
+ const classification = registered ? void 0 : realPath && isPluginPack(realPath) ? "plugin-pack" : kind === "directory" && existsSync31(join31(path, "SKILL.md")) ? "project-local" : void 0;
6787
7883
  return {
6788
7884
  name,
6789
7885
  kind,
@@ -6795,11 +7891,11 @@ function inspectSkillItem(skillRoot, directory, name, managed, registeredSkills)
6795
7891
  };
6796
7892
  }
6797
7893
  function isPluginPack(path) {
6798
- const skillsRoot = join30(path, "skills");
6799
- return existsSync30(join30(path, ".codex-plugin/plugin.json")) && safeIsDirectory(skillsRoot) && safeReadDir(skillsRoot).some((name) => existsSync30(join30(skillsRoot, name, "SKILL.md")));
7894
+ const skillsRoot = join31(path, "skills");
7895
+ return existsSync31(join31(path, ".codex-plugin/plugin.json")) && safeIsDirectory(skillsRoot) && safeReadDir(skillsRoot).some((name) => existsSync31(join31(skillsRoot, name, "SKILL.md")));
6800
7896
  }
6801
7897
  function inspectInvalidSkillEntries(root, directory) {
6802
- const skillRoot = join30(root, ".agents", directory);
7898
+ const skillRoot = join31(root, ".agents", directory);
6803
7899
  if (!pathLexists(skillRoot) || !safeIsDirectory(skillRoot)) return [];
6804
7900
  return safeReadDir(skillRoot).flatMap((name) => {
6805
7901
  if (name === ".gitkeep") return [];
@@ -6807,14 +7903,14 @@ function inspectInvalidSkillEntries(root, directory) {
6807
7903
  return [{ path: `.agents/${directory}/${name}`, reason: "metadata-junk" }];
6808
7904
  if (name.startsWith("."))
6809
7905
  return [{ path: `.agents/${directory}/${name}`, reason: "unexpected-file" }];
6810
- const path = join30(skillRoot, name);
6811
- return !lstatSync13(path).isDirectory() && !lstatSync13(path).isSymbolicLink() ? [{ path: `.agents/${directory}/${name}`, reason: "unexpected-file" }] : [];
7906
+ const path = join31(skillRoot, name);
7907
+ return !lstatSync14(path).isDirectory() && !lstatSync14(path).isSymbolicLink() ? [{ path: `.agents/${directory}/${name}`, reason: "unexpected-file" }] : [];
6812
7908
  });
6813
7909
  }
6814
7910
  function skillDuplicatesUser(item, root, userSkills) {
6815
7911
  if (userSkills.names.has(item.name)) return true;
6816
- const automatic = join30(root, ".agents/skills", item.name);
6817
- const manual = join30(root, ".agents/manual-skills", item.name);
7912
+ const automatic = join31(root, ".agents/skills", item.name);
7913
+ const manual = join31(root, ".agents/manual-skills", item.name);
6818
7914
  const realPath = safeRealpath(pathLexists(automatic) ? automatic : manual);
6819
7915
  return realPath ? userSkills.realPaths.has(realPath) : false;
6820
7916
  }
@@ -6834,13 +7930,13 @@ function skillPlacementDrift(item, actualPlacement) {
6834
7930
  return [];
6835
7931
  }
6836
7932
  function inspectClaudeSkillRoot(root) {
6837
- const path = join30(root, ".claude/skills");
7933
+ const path = join31(root, ".claude/skills");
6838
7934
  if (!pathLexists(path)) return "missing";
6839
- const stat = lstatSync13(path);
7935
+ const stat = lstatSync14(path);
6840
7936
  if (stat.isSymbolicLink()) {
6841
7937
  try {
6842
7938
  const target = realpathSync8(path);
6843
- return target === realpathSync8(join30(root, ".agents/skills")) ? "shared-root" : "other";
7939
+ return target === realpathSync8(join31(root, ".agents/skills")) ? "shared-root" : "other";
6844
7940
  } catch {
6845
7941
  return "dangling-symlink";
6846
7942
  }
@@ -6853,27 +7949,27 @@ function inspectSkillRegistry(executionEngineRoot) {
6853
7949
  health: { source: 0, registered: 0, bundled: 0, bundles: 0 },
6854
7950
  skills: []
6855
7951
  };
6856
- const agentAssetsRoot = join30(executionEngineRoot, "agent-assets");
6857
- const registry = readJson4(join30(agentAssetsRoot, "registry.json"));
7952
+ const agentAssetsRoot = join31(executionEngineRoot, "agent-assets");
7953
+ const registry = readJson4(join31(agentAssetsRoot, "registry.json"));
6858
7954
  const assets = isRecord3(registry) && Array.isArray(registry.assets) ? registry.assets : [];
6859
7955
  const registeredSkills = assets.filter((asset) => isRecord3(asset) && asset.kind === "skill");
6860
- const bundleRoot = join30(agentAssetsRoot, "bundles");
7956
+ const bundleRoot = join31(agentAssetsRoot, "bundles");
6861
7957
  const bundleFiles = safeReadDir(bundleRoot).filter((file) => file.endsWith(".json"));
6862
7958
  const bundledIds = /* @__PURE__ */ new Set();
6863
7959
  for (const file of bundleFiles) {
6864
- const bundle = readJson4(join30(bundleRoot, file));
7960
+ const bundle = readJson4(join31(bundleRoot, file));
6865
7961
  if (!isRecord3(bundle) || !Array.isArray(bundle.assets)) continue;
6866
7962
  for (const id of bundle.assets) if (typeof id === "string") bundledIds.add(id);
6867
7963
  }
6868
7964
  const sourceRoots = [
6869
- join30(agentAssetsRoot, "skills/pie-skills"),
6870
- join30(agentAssetsRoot, "skills/npx-skills/.agents/skills")
7965
+ join31(agentAssetsRoot, "skills/pie-skills"),
7966
+ join31(agentAssetsRoot, "skills/npx-skills/.agents/skills")
6871
7967
  ];
6872
7968
  const source = sourceRoots.reduce(
6873
- (count, root) => count + safeReadDir(root).filter((name) => existsSync30(join30(root, name, "SKILL.md"))).length,
7969
+ (count, root) => count + safeReadDir(root).filter((name) => existsSync31(join31(root, name, "SKILL.md"))).length,
6874
7970
  0
6875
7971
  ) + registeredSkills.filter(
6876
- (asset) => isRecord3(asset) && asset.sourceKind === "local-pack" && typeof asset.sourcePath === "string" && isPluginPack(join30(agentAssetsRoot, asset.sourcePath))
7972
+ (asset) => isRecord3(asset) && asset.sourceKind === "local-pack" && typeof asset.sourcePath === "string" && isPluginPack(join31(agentAssetsRoot, asset.sourcePath))
6877
7973
  ).length;
6878
7974
  return {
6879
7975
  health: {
@@ -6890,7 +7986,7 @@ function inspectSkillRegistry(executionEngineRoot) {
6890
7986
  return [
6891
7987
  {
6892
7988
  id: asset.id,
6893
- sourceRealPath: safeRealpath(join30(agentAssetsRoot, asset.sourcePath)),
7989
+ sourceRealPath: safeRealpath(join31(agentAssetsRoot, asset.sourcePath)),
6894
7990
  defaultPlacement: asset.defaultPlacement,
6895
7991
  defaultScope: asset.defaultScope === "user" ? "user" : "project"
6896
7992
  }
@@ -6905,15 +8001,15 @@ function inspectUserSkillEvidence(root) {
6905
8001
  for (const name of safeReadDir(root)) {
6906
8002
  if (name.startsWith(".")) continue;
6907
8003
  names.add(name);
6908
- const realPath = safeRealpath(join30(root, name));
8004
+ const realPath = safeRealpath(join31(root, name));
6909
8005
  if (realPath) realPaths.add(realPath);
6910
8006
  }
6911
8007
  return { names, realPaths };
6912
8008
  }
6913
8009
 
6914
8010
  // src/portfolio/ai-health/technology.ts
6915
- import { existsSync as existsSync31, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
6916
- import { join as join31 } from "node:path";
8011
+ import { existsSync as existsSync32, readdirSync as readdirSync14, statSync as statSync11 } from "node:fs";
8012
+ import { join as join32 } from "node:path";
6917
8013
  function buildTechnologyMatrix(governance, repositories) {
6918
8014
  if (!governance || governance.technologies.length === 0) return [];
6919
8015
  const policy = governance.versionPolicy;
@@ -6940,8 +8036,8 @@ function buildTechnologyMatrix(governance, repositories) {
6940
8036
  }).filter((item) => item !== void 0);
6941
8037
  }).flat();
6942
8038
  const fileSignal = (technology.files ?? []).some(
6943
- (path) => hasUsableTechnologyFile(join31(repository.path, path)) || packageManifests.some(
6944
- (manifest) => hasUsableTechnologyFile(join31(manifest.directory, path))
8039
+ (path) => hasUsableTechnologyFile(join32(repository.path, path)) || packageManifests.some(
8040
+ (manifest) => hasUsableTechnologyFile(join32(manifest.directory, path))
6945
8041
  )
6946
8042
  );
6947
8043
  const modelSignal = [
@@ -7032,14 +8128,14 @@ function buildTechnologyMatrix(governance, repositories) {
7032
8128
  }).filter((technology) => technology.projectCount > 0);
7033
8129
  }
7034
8130
  function hasUsableTechnologyFile(path) {
7035
- if (!existsSync31(path)) return false;
8131
+ if (!existsSync32(path)) return false;
7036
8132
  try {
7037
- const info = statSync10(path);
8133
+ const info = statSync11(path);
7038
8134
  if (info.isFile()) return true;
7039
8135
  if (!info.isDirectory()) return false;
7040
- return readdirSync13(path, { withFileTypes: true }).some((entry) => {
8136
+ return readdirSync14(path, { withFileTypes: true }).some((entry) => {
7041
8137
  if (entry.name.startsWith(".")) return false;
7042
- const child = join31(path, entry.name);
8138
+ const child = join32(path, entry.name);
7043
8139
  if (entry.isDirectory()) return hasUsableTechnologyFile(child);
7044
8140
  return entry.name.toLowerCase() !== "readme.md";
7045
8141
  });
@@ -7051,7 +8147,7 @@ function inspectExclusiveOwnership(root, endpoint, governance) {
7051
8147
  const projectType = endpoint.projectType;
7052
8148
  return (governance?.exclusiveOwnership ?? []).flatMap((rule) => {
7053
8149
  if (projectType && rule.allowedProjectTypes.includes(projectType)) return [];
7054
- const paths = rule.paths.filter((path) => existsSync31(join31(root, path)));
8150
+ const paths = rule.paths.filter((path) => existsSync32(join32(root, path)));
7055
8151
  return paths.length > 0 ? [{ rule, paths }] : [];
7056
8152
  });
7057
8153
  }
@@ -7066,7 +8162,7 @@ function inspectProjectModel(root, endpoint, governance) {
7066
8162
  const detection = (id) => {
7067
8163
  const technology = technologyById.get(id);
7068
8164
  const packageMatch = technology?.packages?.some((name) => packages.has(name)) ?? false;
7069
- const fileMatch = technology?.files?.some((path) => existsSync31(join31(root, path))) ?? false;
8165
+ const fileMatch = technology?.files?.some((path) => existsSync32(join32(root, path))) ?? false;
7070
8166
  return { id, label: technology?.label ?? id, detected: packageMatch || fileMatch };
7071
8167
  };
7072
8168
  const selected = new Set(endpoint.capabilities ?? []);
@@ -7105,8 +8201,8 @@ function collectPackageNames(root) {
7105
8201
  }
7106
8202
 
7107
8203
  // src/portfolio/ai-health/report.ts
7108
- import { cpSync as cpSync3, existsSync as existsSync32, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "node:fs";
7109
- import { dirname as dirname16, join as join32 } from "node:path";
8204
+ import { cpSync as cpSync3, existsSync as existsSync33, mkdirSync as mkdirSync11, writeFileSync as writeFileSync10 } from "node:fs";
8205
+ import { dirname as dirname17, join as join33 } from "node:path";
7110
8206
  import { fileURLToPath as fileURLToPath5 } from "node:url";
7111
8207
  function mergePortfolioAiHealthReport(existing, latest, allRepositoryIds) {
7112
8208
  const repositoriesById = /* @__PURE__ */ new Map();
@@ -7147,36 +8243,36 @@ function mergePortfolioAiHealthReport(existing, latest, allRepositoryIds) {
7147
8243
  };
7148
8244
  }
7149
8245
  function writePortfolioAiHealthReport(report, outDir) {
7150
- mkdirSync10(outDir, { recursive: true });
8246
+ mkdirSync11(outDir, { recursive: true });
7151
8247
  const dashboardAssets = findDashboardAssets();
7152
8248
  for (const file of ["index.html", "app.js", "app.css"]) {
7153
- const source = join32(dashboardAssets, file);
7154
- if (!existsSync32(source)) throw new Error(`Portfolio dashboard asset is missing: ${source}`);
7155
- cpSync3(source, join32(outDir, file));
8249
+ const source = join33(dashboardAssets, file);
8250
+ if (!existsSync33(source)) throw new Error(`Portfolio dashboard asset is missing: ${source}`);
8251
+ cpSync3(source, join33(outDir, file));
7156
8252
  }
7157
- const jsonPath = join32(outDir, "portfolio-ai-health.json");
7158
- const htmlPath = join32(outDir, "index.html");
7159
- writeFileSync9(jsonPath, `${JSON.stringify(report, null, 2)}
8253
+ const jsonPath = join33(outDir, "portfolio-ai-health.json");
8254
+ const htmlPath = join33(outDir, "index.html");
8255
+ writeFileSync10(jsonPath, `${JSON.stringify(report, null, 2)}
7160
8256
  `);
7161
- writeFileSync9(
7162
- join32(outDir, "data.js"),
8257
+ writeFileSync10(
8258
+ join33(outDir, "data.js"),
7163
8259
  `window.__PORTFOLIO_AI_HEALTH__ = ${safeJavaScriptJson2(report)};
7164
8260
  `
7165
8261
  );
7166
8262
  return { jsonPath, htmlPath };
7167
8263
  }
7168
8264
  function findDashboardAssets() {
7169
- const packageRoot2 = dirname16(dirname16(fileURLToPath5(import.meta.url)));
8265
+ const packageRoot2 = dirname17(dirname17(fileURLToPath5(import.meta.url)));
7170
8266
  const candidates = [
7171
8267
  process.env.PGS_DASHBOARD_ASSETS_DIR,
7172
- join32(packageRoot2, ".dashboard-build"),
7173
- join32(packageRoot2, "assets/portfolio-dashboard"),
7174
- join32(process.cwd(), ".dashboard-build"),
7175
- join32(process.cwd(), "assets/portfolio-dashboard"),
7176
- join32(process.cwd(), "packages/pro-gov/.dashboard-build"),
7177
- join32(process.cwd(), "packages/pro-gov/assets/portfolio-dashboard")
8268
+ join33(packageRoot2, ".dashboard-build"),
8269
+ join33(packageRoot2, "assets/portfolio-dashboard"),
8270
+ join33(process.cwd(), ".dashboard-build"),
8271
+ join33(process.cwd(), "assets/portfolio-dashboard"),
8272
+ join33(process.cwd(), "packages/pro-gov/.dashboard-build"),
8273
+ join33(process.cwd(), "packages/pro-gov/assets/portfolio-dashboard")
7178
8274
  ].filter((value) => Boolean(value));
7179
- const match = candidates.find((path) => existsSync32(join32(path, "index.html")));
8275
+ const match = candidates.find((path) => existsSync33(join33(path, "index.html")));
7180
8276
  if (!match)
7181
8277
  throw new Error(
7182
8278
  "Portfolio dashboard assets were not built. Run pnpm --filter @pieai/pro-gov build."
@@ -7194,8 +8290,8 @@ function inspectPortfolioAiHealth(options) {
7194
8290
  if (options.targetId && options.targetId !== "all" && endpoints.length === 0) {
7195
8291
  throw new Error(`Unknown portfolio target: ${options.targetId}`);
7196
8292
  }
7197
- const secretsRoot = options.secretsRoot ?? join33(
7198
- dirname17(
8293
+ const secretsRoot = options.secretsRoot ?? join34(
8294
+ dirname18(
7199
8295
  options.manifest.controlPlane?.path ?? allEndpoints[0]?.endpoint.path ?? process.cwd()
7200
8296
  ),
7201
8297
  ".secrets"
@@ -7204,9 +8300,9 @@ function inspectPortfolioAiHealth(options) {
7204
8300
  const grokVersion = commandVersion("grok");
7205
8301
  const executionEngineRoot = options.manifest.executionEngine?.path;
7206
8302
  const skillRegistry = inspectSkillRegistry(executionEngineRoot);
7207
- const userSkills = inspectUserSkillEvidence(join33(homeDir, ".agents/skills"));
8303
+ const userSkills = inspectUserSkillEvidence(join34(homeDir, ".agents/skills"));
7208
8304
  const expectedPackageVersion = packageVersion(
7209
- join33(executionEngineRoot ?? "", "packages/pro-gov/package.json")
8305
+ join34(executionEngineRoot ?? "", "packages/pro-gov/package.json")
7210
8306
  );
7211
8307
  const repositories = endpoints.map(
7212
8308
  ({ endpoint, role }) => inspectRepository(
@@ -7263,7 +8359,7 @@ function collectEndpoints(manifest) {
7263
8359
  for (const target of manifest.targets) result.push({ endpoint: target, role: "target" });
7264
8360
  const seen = /* @__PURE__ */ new Set();
7265
8361
  return result.filter(({ endpoint }) => {
7266
- const key = resolve9(endpoint.path);
8362
+ const key = resolve10(endpoint.path);
7267
8363
  if (seen.has(key)) return false;
7268
8364
  seen.add(key);
7269
8365
  return true;
@@ -7280,13 +8376,13 @@ function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackage
7280
8376
  const hooks = inspectHooks(root);
7281
8377
  const docs = inspectDocs(root, role === "execution-engine" ? void 0 : expectedPackageVersion);
7282
8378
  const mcp = {
7283
- codexProject: tomlMcpNames(join33(root, MCP_DISCOVERY_PATHS.project.codex)),
8379
+ codexProject: tomlMcpNames(join34(root, MCP_DISCOVERY_PATHS.project.codex)),
7284
8380
  claudeCodeProjectShared: jsonObjectKeys(
7285
- join33(root, MCP_DISCOVERY_PATHS.project.claudeCodeShared),
8381
+ join34(root, MCP_DISCOVERY_PATHS.project.claudeCodeShared),
7286
8382
  "mcpServers"
7287
8383
  ),
7288
8384
  claudeCodeProjectLocal: claudeProjectLocalMcpNames(homeDir, root),
7289
- grokProject: tomlMcpNames(join33(root, MCP_DISCOVERY_PATHS.project.grok)),
8385
+ grokProject: tomlMcpNames(join34(root, MCP_DISCOVERY_PATHS.project.grok)),
7290
8386
  grokEffective: grokInspection.effectiveMcp,
7291
8387
  grokInspection: grokInspection.inspection
7292
8388
  };
@@ -7920,14 +9016,15 @@ function isHost2(value) {
7920
9016
  return value === "codex" || value === "claude-code" || value === "gemini-cli" || value === "antigravity";
7921
9017
  }
7922
9018
  function findPortfolioAgentAssetsDir(manifest) {
7923
- const agentAssetsDir = manifest?.executionEngine?.path ? join34(manifest.executionEngine.path, "agent-assets") : void 0;
7924
- return agentAssetsDir && existsSync33(join34(agentAssetsDir, "registry.json")) ? agentAssetsDir : void 0;
9019
+ const agentAssetsDir = manifest?.executionEngine?.path ? join35(manifest.executionEngine.path, "agent-assets") : void 0;
9020
+ return agentAssetsDir && existsSync34(join35(agentAssetsDir, "registry.json")) ? agentAssetsDir : void 0;
7925
9021
  }
7926
9022
  function reportMissingPortfolioRegistry(loaded, json) {
7927
- const expectedPath = loaded.manifest?.executionEngine?.path ? join34(loaded.manifest.executionEngine.path, "agent-assets/registry.json") : "executionEngine.path/agent-assets/registry.json";
9023
+ const expectedPath = loaded.manifest?.executionEngine?.path ? join35(loaded.manifest.executionEngine.path, "agent-assets/registry.json") : "executionEngine.path/agent-assets/registry.json";
9024
+ const displayExpectedPath = expectedPath.replaceAll("\\", "/");
7928
9025
  const issue = {
7929
9026
  type: "missing-control-plane-registry",
7930
- message: `Portfolio control-plane registry is required at ${expectedPath}; refusing the package fallback registry.`
9027
+ message: `Portfolio control-plane registry is required at ${displayExpectedPath}; refusing the package fallback registry.`
7931
9028
  };
7932
9029
  if (json) {
7933
9030
  console.log(
@@ -7962,10 +9059,10 @@ function printUsage5() {
7962
9059
  }
7963
9060
  function readExistingAiHealthReport(outDir, portfolioId) {
7964
9061
  if (!outDir) return void 0;
7965
- const path = join34(outDir, "portfolio-ai-health.json");
7966
- if (!existsSync33(path)) return void 0;
9062
+ const path = join35(outDir, "portfolio-ai-health.json");
9063
+ if (!existsSync34(path)) return void 0;
7967
9064
  try {
7968
- const value = JSON.parse(readFileSync18(path, "utf8"));
9065
+ const value = JSON.parse(readFileSync19(path, "utf8"));
7969
9066
  if (!value || typeof value !== "object" || value.portfolioId !== portfolioId || !Array.isArray(value.repositories))
7970
9067
  return void 0;
7971
9068
  return value;
@@ -7975,8 +9072,8 @@ function readExistingAiHealthReport(outDir, portfolioId) {
7975
9072
  }
7976
9073
 
7977
9074
  // src/commands/sync.ts
7978
- import { existsSync as existsSync34, lstatSync as lstatSync14, readFileSync as readFileSync19, readlinkSync as readlinkSync4 } from "node:fs";
7979
- import { join as join35 } from "node:path";
9075
+ import { existsSync as existsSync35, lstatSync as lstatSync15, readFileSync as readFileSync20, readlinkSync as readlinkSync4 } from "node:fs";
9076
+ import { join as join36 } from "node:path";
7980
9077
  function runSync(args) {
7981
9078
  const check = args.includes("--check");
7982
9079
  if (!check) {
@@ -8004,7 +9101,7 @@ function runSync(args) {
8004
9101
  console.log("pro-gov sync check");
8005
9102
  console.log(`profile: ${profile}`);
8006
9103
  for (const file of planStarterFiles(profile)) {
8007
- const targetPath = join35(process.cwd(), file.targetPath);
9104
+ const targetPath = join36(process.cwd(), file.targetPath);
8008
9105
  const stat = safeLstat3(targetPath);
8009
9106
  if (!stat) {
8010
9107
  if (file.ownership === "optional-guardrail") continue;
@@ -8022,14 +9119,14 @@ function runSync(args) {
8022
9119
  continue;
8023
9120
  }
8024
9121
  if (file.kind === "symlink") {
8025
- if (!stat.isSymbolicLink() || readlinkSync4(targetPath) !== file.linkTarget) {
9122
+ if (!stat.isSymbolicLink() || normalizeSymlinkTarget(readlinkSync4(targetPath)) !== file.linkTarget) {
8026
9123
  console.log(`different: ${file.targetPath}`);
8027
9124
  differences += 1;
8028
9125
  }
8029
9126
  continue;
8030
9127
  }
8031
- const source = readFileSync19(file.absoluteSourcePath, "utf8");
8032
- const target = readFileSync19(targetPath, "utf8");
9128
+ const source = readFileSync20(file.absoluteSourcePath, "utf8");
9129
+ const target = readFileSync20(targetPath, "utf8");
8033
9130
  if (!matchesExpectedContent(file.targetPath, source, target)) {
8034
9131
  console.log(`different: ${file.targetPath}`);
8035
9132
  differences += 1;
@@ -8063,13 +9160,13 @@ function normalizeMarkdownTableCell(cell) {
8063
9160
  }
8064
9161
  function inferInstalledProfile(root) {
8065
9162
  const installed = ["engineering-runtime", "doc-only"].filter(
8066
- (profile) => existsSync34(join35(root, `docs/governance/agents-routing/${profile}-v1.1.md`))
9163
+ (profile) => existsSync35(join36(root, `docs/governance/agents-routing/${profile}-v1.1.md`))
8067
9164
  );
8068
9165
  return installed.length === 1 ? installed[0] : void 0;
8069
9166
  }
8070
9167
  function safeLstat3(path) {
8071
9168
  try {
8072
- return lstatSync14(path);
9169
+ return lstatSync15(path);
8073
9170
  } catch {
8074
9171
  return void 0;
8075
9172
  }
@@ -8090,6 +9187,7 @@ var COMMANDS = [
8090
9187
  "assets check [--target <path>] [--strict-registry] [--json]",
8091
9188
  "assets public-check [--public-root <path>] [--private-root <path>] [--json]",
8092
9189
  "assets npx add|update ... --plan",
9190
+ "assets catalog build|check [--native-links] [--json]",
8093
9191
  "portfolio check --config <path> [--json]",
8094
9192
  "portfolio plan --config <path> [--target <id|all>] [--json]",
8095
9193
  "portfolio assets-check --config <path> [--target <id|all>] [--json]",