@pieai/pro-gov 0.9.3 → 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 +1582 -533
  4. package/package.json +2 -2
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,18 +58,970 @@ 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
63
  import {
64
- cpSync,
65
64
  existsSync as existsSync3,
65
+ lstatSync,
66
66
  mkdirSync,
67
67
  mkdtempSync,
68
- readdirSync as readdirSync3,
69
68
  readFileSync as readFileSync2,
70
- statSync
69
+ readdirSync as readdirSync3,
70
+ renameSync,
71
+ rmSync,
72
+ statSync,
73
+ symlinkSync,
74
+ writeFileSync
75
+ } from "node:fs";
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
71
1023
  } from "node:fs";
72
- import { join as join3, relative as relative2 } from "node:path";
1024
+ import { join as join4, relative as relative3 } from "node:path";
73
1025
  import { tmpdir } from "node:os";
74
1026
 
75
1027
  // src/command-runner.ts
@@ -100,7 +1052,7 @@ function createNpxSkillsMaintenancePlan(options) {
100
1052
  throw new Error("npx skills add requires a source.");
101
1053
  }
102
1054
  const before = snapshotFiles(options.npxRoot);
103
- const tempRoot = mkdtempSync(join3(tmpdir(), "pro-gov-npx-skills-"));
1055
+ const tempRoot = mkdtempSync2(join4(tmpdir(), "pro-gov-npx-skills-"));
104
1056
  cpSync(options.npxRoot, tempRoot, { recursive: true, dereference: false });
105
1057
  const command2 = buildNpxCommand(options);
106
1058
  const runner = options.runner ?? defaultRunner;
@@ -144,10 +1096,10 @@ ${stderr}`.replace(ansiEscape, "");
144
1096
  }
145
1097
  }
146
1098
  function assertNativeNpxRoot(npxRoot) {
147
- if (!existsSync3(join3(npxRoot, "skills-lock.json"))) {
1099
+ if (!existsSync4(join4(npxRoot, "skills-lock.json"))) {
148
1100
  throw new Error(`npx skills root is missing skills-lock.json: ${npxRoot}`);
149
1101
  }
150
- if (!existsSync3(join3(npxRoot, ".agents/skills"))) {
1102
+ if (!existsSync4(join4(npxRoot, ".agents/skills"))) {
151
1103
  throw new Error(`npx skills root is missing .agents/skills: ${npxRoot}`);
152
1104
  }
153
1105
  }
@@ -178,21 +1130,21 @@ function defaultRunner({ command: command2, cwd, timeoutMs }) {
178
1130
  }
179
1131
  function snapshotFiles(root) {
180
1132
  const snapshot = /* @__PURE__ */ new Map();
181
- for (const filePath of listFiles2(root)) {
182
- const relativePath = toUnixPath2(relative2(root, filePath));
1133
+ for (const filePath of listFiles3(root)) {
1134
+ const relativePath = toUnixPath2(relative3(root, filePath));
183
1135
  snapshot.set(relativePath, hashFile(filePath));
184
1136
  }
185
1137
  return snapshot;
186
1138
  }
187
- function listFiles2(root) {
1139
+ function listFiles3(root) {
188
1140
  const files = [];
189
1141
  collectFiles(root, root, files);
190
1142
  return files.sort();
191
1143
  }
192
1144
  function collectFiles(root, current, files) {
193
- mkdirSync(root, { recursive: true });
194
- for (const entry of readdirSync3(current, { withFileTypes: true })) {
195
- 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);
196
1148
  if (entry.isDirectory()) {
197
1149
  collectFiles(root, entryPath, files);
198
1150
  } else if (entry.isFile()) {
@@ -201,11 +1153,11 @@ function collectFiles(root, current, files) {
201
1153
  }
202
1154
  }
203
1155
  function hashFile(path) {
204
- const hash = createHash("sha256");
205
- const stats = statSync(path);
1156
+ const hash = createHash2("sha256");
1157
+ const stats = statSync2(path);
206
1158
  hash.update(String(stats.size));
207
1159
  hash.update("\0");
208
- hash.update(readFileSync2(path));
1160
+ hash.update(readFileSync3(path));
209
1161
  return hash.digest("hex");
210
1162
  }
211
1163
  function diffSnapshots(before, after) {
@@ -232,14 +1184,14 @@ function toUnixPath2(path) {
232
1184
  }
233
1185
 
234
1186
  // src/asset-registry/loader.ts
235
- import { createHash as createHash2 } from "node:crypto";
236
- import { existsSync as existsSync5, readdirSync as readdirSync5, readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
237
- 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";
238
1190
  import { fileURLToPath as fileURLToPath2 } from "node:url";
239
1191
 
240
1192
  // src/asset-registry/registry.ts
241
- import { existsSync as existsSync4, lstatSync, readdirSync as readdirSync4, realpathSync } from "node:fs";
242
- 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";
243
1195
  var supportedFamilies = /* @__PURE__ */ new Set([
244
1196
  "pie-skills",
245
1197
  "npx-skills",
@@ -283,7 +1235,7 @@ function assetSkillInstallName(asset) {
283
1235
  return asset.installName ?? posix.basename(asset.sourcePath);
284
1236
  }
285
1237
  function isValidAssetProjectTargetPath(kind, projectTargetPath) {
286
- if (typeof projectTargetPath !== "string" || projectTargetPath.length === 0 || isAbsolute(projectTargetPath) || projectTargetPath.includes("\\")) {
1238
+ if (typeof projectTargetPath !== "string" || projectTargetPath.length === 0 || isAbsolute2(projectTargetPath) || projectTargetPath.includes("\\")) {
287
1239
  return false;
288
1240
  }
289
1241
  const segments = projectTargetPath.split("/");
@@ -439,11 +1391,11 @@ function validateAssetRegistry(registry, options = {}) {
439
1391
  });
440
1392
  }
441
1393
  if (options.agentAssetsDir && isSafeRegistrySourcePath(asset.sourcePath)) {
442
- const sourceAbsolutePath = join4(
1394
+ const sourceAbsolutePath = join5(
443
1395
  options.agentAssetsDir,
444
1396
  normalizeRegistrySourcePath(asset.sourcePath)
445
1397
  );
446
- if (!existsSync4(sourceAbsolutePath)) {
1398
+ if (!existsSync5(sourceAbsolutePath)) {
447
1399
  issues.push({
448
1400
  type: "missing-source-path",
449
1401
  id: asset.id,
@@ -467,7 +1419,7 @@ function validateAssetRegistry(registry, options = {}) {
467
1419
  message: `Local skill pack must contain .codex-plugin/plugin.json and at least one skills/*/SKILL.md: ${asset.sourcePath}`
468
1420
  });
469
1421
  }
470
- } else if (!existsSync4(join4(sourceAbsolutePath, "SKILL.md"))) {
1422
+ } else if (!existsSync5(join5(sourceAbsolutePath, "SKILL.md"))) {
471
1423
  issues.push({
472
1424
  type: "missing-skill-file",
473
1425
  id: asset.id,
@@ -479,7 +1431,7 @@ function validateAssetRegistry(registry, options = {}) {
479
1431
  }
480
1432
  }
481
1433
  if (options.agentAssetsDir) {
482
- const npxCompatibilityLayer = join4(options.agentAssetsDir, "skills/npx-skills/skills");
1434
+ const npxCompatibilityLayer = join5(options.agentAssetsDir, "skills/npx-skills/skills");
483
1435
  if (pathExistsEvenIfDanglingSymlink(npxCompatibilityLayer)) {
484
1436
  issues.push({
485
1437
  type: "internal-npx-compatibility-layer",
@@ -492,20 +1444,20 @@ function validateAssetRegistry(registry, options = {}) {
492
1444
  return issues;
493
1445
  }
494
1446
  function isLocalSkillPack(sourceAbsolutePath) {
495
- const skillsRoot = join4(sourceAbsolutePath, "skills");
496
- 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)) {
497
1449
  return false;
498
1450
  }
499
1451
  try {
500
- return lstatSync(skillsRoot).isDirectory() && readdirSync4(skillsRoot, { withFileTypes: true }).some(
501
- (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"))
502
1454
  );
503
1455
  } catch {
504
1456
  return false;
505
1457
  }
506
1458
  }
507
1459
  function isSafeRegistrySourcePath(sourcePath) {
508
- if (!sourcePath || isAbsolute(sourcePath) || sourcePath.startsWith("/")) return false;
1460
+ if (!sourcePath || isAbsolute2(sourcePath) || sourcePath.startsWith("/")) return false;
509
1461
  const normalized = normalizeRegistrySourcePath(sourcePath);
510
1462
  if (normalized === "." || normalized.startsWith("../") || normalized === "..") return false;
511
1463
  return !normalized.split("/").includes("..");
@@ -517,15 +1469,15 @@ function isWithinAgentAssetsDir(agentAssetsDir, sourceAbsolutePath) {
517
1469
  try {
518
1470
  const agentAssetsRealPath = realpathSync(agentAssetsDir);
519
1471
  const sourceRealPath = realpathSync(sourceAbsolutePath);
520
- const relativePath = relative3(agentAssetsRealPath, sourceRealPath);
521
- return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath);
1472
+ const relativePath = relative4(agentAssetsRealPath, sourceRealPath);
1473
+ return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute2(relativePath);
522
1474
  } catch {
523
1475
  return false;
524
1476
  }
525
1477
  }
526
1478
  function pathExistsEvenIfDanglingSymlink(path) {
527
1479
  try {
528
- lstatSync(path);
1480
+ lstatSync2(path);
529
1481
  return true;
530
1482
  } catch {
531
1483
  return false;
@@ -542,7 +1494,7 @@ function createAgentAssetRegistryProvenance(registry, selectedAssetIds) {
542
1494
  (left, right) => left.id < right.id ? -1 : left.id > right.id ? 1 : 0
543
1495
  )
544
1496
  });
545
- const hash = createHash2("sha256").update(JSON.stringify(canonicalRegistry)).digest("hex");
1497
+ const hash = createHash3("sha256").update(JSON.stringify(canonicalRegistry)).digest("hex");
546
1498
  return {
547
1499
  schema: "agent-assets-registry",
548
1500
  version: registry.schemaVersion,
@@ -552,8 +1504,8 @@ function createAgentAssetRegistryProvenance(registry, selectedAssetIds) {
552
1504
  }
553
1505
  function loadAgentAssetRegistry(options = {}) {
554
1506
  const agentAssetsDir = options.agentAssetsDir ?? findDefaultAgentAssetsDir();
555
- const registryPath = join5(agentAssetsDir, "registry.json");
556
- if (!existsSync5(registryPath)) {
1507
+ const registryPath = join6(agentAssetsDir, "registry.json");
1508
+ if (!existsSync6(registryPath)) {
557
1509
  return {
558
1510
  registry: { schemaVersion: 1, assets: [] },
559
1511
  agentAssetsDir,
@@ -561,7 +1513,7 @@ function loadAgentAssetRegistry(options = {}) {
561
1513
  issues: []
562
1514
  };
563
1515
  }
564
- const registry = JSON.parse(readFileSync3(registryPath, "utf8"));
1516
+ const registry = JSON.parse(readFileSync4(registryPath, "utf8"));
565
1517
  return {
566
1518
  registry,
567
1519
  agentAssetsDir,
@@ -579,15 +1531,15 @@ function createAgentAssetLockEntries(registry, agentAssetsDir, assetIds) {
579
1531
  })).sort((a, b) => a.id.localeCompare(b.id));
580
1532
  }
581
1533
  function hashAgentAssetContent(asset, agentAssetsDir) {
582
- return hashAssetPathContent(join5(agentAssetsDir, asset.sourcePath));
1534
+ return hashAssetPathContent(join6(agentAssetsDir, asset.sourcePath));
583
1535
  }
584
1536
  function hashAssetPathContent(sourceAbsolutePath) {
585
- const hash = createHash2("sha256");
586
- for (const filePath of listFiles3(sourceAbsolutePath)) {
587
- 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));
588
1540
  hash.update(relativePath);
589
1541
  hash.update("\0");
590
- hash.update(readFileSync3(filePath));
1542
+ hash.update(readFileSync4(filePath));
591
1543
  hash.update("\0");
592
1544
  }
593
1545
  return `sha256:${hash.digest("hex")}`;
@@ -602,40 +1554,40 @@ function canonicalizeValue(value) {
602
1554
  return value;
603
1555
  }
604
1556
  function findDefaultAgentAssetsDir() {
605
- const packageRoot2 = findPackageRoot(dirname2(fileURLToPath2(import.meta.url)));
606
- const repoRoot = join5(packageRoot2, "..", "..");
1557
+ const packageRoot2 = findPackageRoot(dirname3(fileURLToPath2(import.meta.url)));
1558
+ const repoRoot = join6(packageRoot2, "..", "..");
607
1559
  const candidates = [
608
- join5(packageRoot2, "assets/agent-assets"),
609
- join5(repoRoot, "agent-assets"),
610
- join5(packageRoot2, "assets/public-agent-assets"),
611
- 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")
612
1564
  ];
613
- return candidates.find((candidate) => existsSync5(join5(candidate, "registry.json"))) ?? candidates[0];
1565
+ return candidates.find((candidate) => existsSync6(join6(candidate, "registry.json"))) ?? candidates[0];
614
1566
  }
615
1567
  function findPackageRoot(startDir) {
616
1568
  let current = startDir;
617
- while (current !== dirname2(current)) {
618
- const packageJsonPath = join5(current, "package.json");
619
- if (existsSync5(packageJsonPath)) {
1569
+ while (current !== dirname3(current)) {
1570
+ const packageJsonPath = join6(current, "package.json");
1571
+ if (existsSync6(packageJsonPath)) {
620
1572
  try {
621
- const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
1573
+ const packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
622
1574
  if (packageJson.name === "@pieai/pro-gov") return current;
623
1575
  } catch {
624
1576
  }
625
1577
  }
626
- current = dirname2(current);
1578
+ current = dirname3(current);
627
1579
  }
628
1580
  return startDir;
629
1581
  }
630
- function listFiles3(absolutePath) {
631
- const stats = statSync2(absolutePath);
1582
+ function listFiles4(absolutePath) {
1583
+ const stats = statSync3(absolutePath);
632
1584
  if (stats.isFile()) return [absolutePath];
633
1585
  const files = [];
634
- for (const entry of readdirSync5(absolutePath, { withFileTypes: true })) {
1586
+ for (const entry of readdirSync6(absolutePath, { withFileTypes: true })) {
635
1587
  if (shouldIgnoreAssetHashEntry(entry.name)) continue;
636
- const entryPath = join5(absolutePath, entry.name);
1588
+ const entryPath = join6(absolutePath, entry.name);
637
1589
  if (entry.isDirectory()) {
638
- files.push(...listFiles3(entryPath));
1590
+ files.push(...listFiles4(entryPath));
639
1591
  } else if (entry.isFile()) {
640
1592
  files.push(entryPath);
641
1593
  }
@@ -650,8 +1602,8 @@ function toUnixPath3(path) {
650
1602
  }
651
1603
 
652
1604
  // src/asset-registry/public-promotion.ts
653
- import { existsSync as existsSync6 } from "node:fs";
654
- 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";
655
1607
  function checkPublicAssetPromotions(options) {
656
1608
  const issues = [];
657
1609
  let checked = 0;
@@ -689,7 +1641,7 @@ function checkPublicAssetPromotions(options) {
689
1641
  });
690
1642
  continue;
691
1643
  }
692
- if (!existsSync6(privatePathResult.path)) {
1644
+ if (!existsSync7(privatePathResult.path)) {
693
1645
  issues.push({
694
1646
  type: "missing-private-source",
695
1647
  id: asset.id,
@@ -709,7 +1661,7 @@ function checkPublicAssetPromotions(options) {
709
1661
  });
710
1662
  }
711
1663
  }
712
- if (!existsSync6(publicPathResult.path)) {
1664
+ if (!existsSync7(publicPathResult.path)) {
713
1665
  issues.push({
714
1666
  type: "missing-public-source",
715
1667
  id: asset.id,
@@ -736,32 +1688,32 @@ function needsPromotionCheck(asset) {
736
1688
  return asset.visibility === "public" && asset.publishable;
737
1689
  }
738
1690
  function resolveSafePath(root, sourcePath) {
739
- if (!sourcePath || isAbsolute2(sourcePath) || sourcePath.startsWith("/")) return { ok: false };
1691
+ if (!sourcePath || isAbsolute3(sourcePath) || sourcePath.startsWith("/")) return { ok: false };
740
1692
  const normalized = posix2.normalize(sourcePath.replaceAll("\\", "/"));
741
1693
  if (normalized === "." || normalized === ".." || normalized.startsWith("../")) {
742
1694
  return { ok: false };
743
1695
  }
744
1696
  if (normalized.split("/").includes("..")) return { ok: false };
745
- return { ok: true, path: join6(root, normalized) };
1697
+ return { ok: true, path: join7(root, normalized) };
746
1698
  }
747
1699
 
748
1700
  // src/asset-targets/apply.ts
749
- import { createHash as createHash3 } from "node:crypto";
1701
+ import { createHash as createHash4 } from "node:crypto";
750
1702
  import {
751
- existsSync as existsSync8,
752
- lstatSync as lstatSync3,
753
- mkdirSync as mkdirSync2,
1703
+ existsSync as existsSync9,
1704
+ lstatSync as lstatSync4,
1705
+ mkdirSync as mkdirSync3,
754
1706
  readlinkSync as readlinkSync2,
755
1707
  realpathSync as realpathSync3,
756
- symlinkSync,
1708
+ symlinkSync as symlinkSync2,
757
1709
  unlinkSync,
758
- writeFileSync
1710
+ writeFileSync as writeFileSync2
759
1711
  } from "node:fs";
760
- 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";
761
1713
 
762
1714
  // src/asset-targets/install-plan.ts
763
- import { existsSync as existsSync7, lstatSync as lstatSync2, readFileSync as readFileSync4, readlinkSync, realpathSync as realpathSync2, statSync as statSync3 } from "node:fs";
764
- import { basename, dirname as dirname3, join as join7, resolve } from "node:path";
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";
765
1717
 
766
1718
  // src/symlinks.ts
767
1719
  function normalizeSymlinkTarget(target) {
@@ -888,9 +1840,9 @@ function createAssetAction(asset, agentAssetsDir, targetDir, host, placement, ma
888
1840
  `User-scoped asset ${asset.id} must be linked at the user level, not installed into a project target.`
889
1841
  );
890
1842
  }
891
- const sourcePath = join7(agentAssetsDir, asset.sourcePath);
1843
+ const sourcePath = join8(agentAssetsDir, asset.sourcePath);
892
1844
  const targetPath = resolveHostTargetPath(asset, host, placement);
893
- const targetAbsolutePath = join7(targetDir, targetPath);
1845
+ const targetAbsolutePath = join8(targetDir, targetPath);
894
1846
  const targetExists = pathExistsEvenIfDanglingSymlink2(targetAbsolutePath);
895
1847
  const managedEntry = managedEntries.find(
896
1848
  (entry) => entry.id === asset.id && entry.targetPath === targetPath
@@ -907,7 +1859,7 @@ function createAssetAction(asset, agentAssetsDir, targetDir, host, placement, ma
907
1859
  });
908
1860
  }
909
1861
  if (targetExists) {
910
- const stats = lstatSync2(targetAbsolutePath);
1862
+ const stats = lstatSync3(targetAbsolutePath);
911
1863
  if (stats.isSymbolicLink() && managedEntry && (managedEntry.delivery ?? "symlink") === "symlink") {
912
1864
  return {
913
1865
  type: "update-symlink",
@@ -919,7 +1871,7 @@ function createAssetAction(asset, agentAssetsDir, targetDir, host, placement, ma
919
1871
  if (managedEntry && managedEntry.delivery === "snapshot") {
920
1872
  throw new Error(`Refusing to replace managed snapshot with a symlink: ${targetPath}`);
921
1873
  }
922
- if (stats.isSymbolicLink() && existsSync7(targetAbsolutePath) && realpathSync2(targetAbsolutePath) === realpathSync2(sourcePath)) {
1874
+ if (stats.isSymbolicLink() && existsSync8(targetAbsolutePath) && realpathSync2(targetAbsolutePath) === realpathSync2(sourcePath)) {
923
1875
  return {
924
1876
  type: "adopt-existing-symlink",
925
1877
  assetId: asset.id,
@@ -942,10 +1894,10 @@ function createSnapshotAction(options) {
942
1894
  `Snapshot delivery requires a rule target under docs/policy/shared-rules/: ${options.asset.id}`
943
1895
  );
944
1896
  }
945
- if (!statSync3(options.sourcePath).isFile()) {
1897
+ if (!statSync4(options.sourcePath).isFile()) {
946
1898
  throw new Error(`Snapshot source must be a regular file: ${options.asset.sourcePath}`);
947
1899
  }
948
- const content = readFileSync4(options.sourcePath);
1900
+ const content = readFileSync5(options.sourcePath);
949
1901
  const contentBase64 = content.toString("base64");
950
1902
  const contentHash = hashAgentAssetContent(options.asset, options.agentAssetsDir);
951
1903
  const managedDelivery = options.managedEntry?.delivery ?? "symlink";
@@ -958,9 +1910,9 @@ function createSnapshotAction(options) {
958
1910
  contentHash
959
1911
  };
960
1912
  }
961
- const stats = lstatSync2(options.targetAbsolutePath);
1913
+ const stats = lstatSync3(options.targetAbsolutePath);
962
1914
  if (stats.isSymbolicLink()) {
963
- 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) {
964
1916
  return {
965
1917
  type: "migrate-symlink-to-snapshot",
966
1918
  assetId: options.asset.id,
@@ -1016,9 +1968,9 @@ function resolveHostTargetPath(asset, _host, placement) {
1016
1968
  return `.agents/skills/${assetSkillInstallName(asset)}`;
1017
1969
  }
1018
1970
  if (asset.kind === "rule") {
1019
- return `.pro-gov/agent-assets/rules/${basename(asset.sourcePath)}`;
1971
+ return `.pro-gov/agent-assets/rules/${basename2(asset.sourcePath)}`;
1020
1972
  }
1021
- return `.pro-gov/agent-assets/commands/${basename(asset.sourcePath)}`;
1973
+ return `.pro-gov/agent-assets/commands/${basename2(asset.sourcePath)}`;
1022
1974
  }
1023
1975
  function resolveSkillPlacement(asset, placement) {
1024
1976
  if (placement !== "registry") return placement;
@@ -1028,16 +1980,16 @@ function createDirectoryActions(actions) {
1028
1980
  const directories = /* @__PURE__ */ new Set();
1029
1981
  for (const action of actions) {
1030
1982
  if (action.type === "create-dir") continue;
1031
- const directory = dirname3(action.targetPath);
1983
+ const directory = dirname4(action.targetPath);
1032
1984
  if (directory !== ".") directories.add(directory);
1033
1985
  }
1034
1986
  return [...directories].sort().map((targetPath) => ({ type: "create-dir", targetPath }));
1035
1987
  }
1036
1988
  function readManagedLock(targetDir) {
1037
- const lockfilePath = join7(targetDir, ".pro-gov/assets.lock.json");
1038
- if (!existsSync7(lockfilePath)) return { entries: [] };
1989
+ const lockfilePath = join8(targetDir, ".pro-gov/assets.lock.json");
1990
+ if (!existsSync8(lockfilePath)) return { entries: [] };
1039
1991
  try {
1040
- const lockfile = JSON.parse(readFileSync4(lockfilePath, "utf8"));
1992
+ const lockfile = JSON.parse(readFileSync5(lockfilePath, "utf8"));
1041
1993
  return {
1042
1994
  host: typeof lockfile.host === "string" ? lockfile.host : void 0,
1043
1995
  entries: (lockfile.assets ?? []).filter(
@@ -1075,10 +2027,10 @@ function createLegacyClaudeAdoptions(options) {
1075
2027
  throw new Error(`Legacy Claude lock entry cannot be safely normalized: ${entry.targetPath}`);
1076
2028
  }
1077
2029
  const targetPath = `.agents/skills/${skillName}`;
1078
- const targetAbsolutePath = join7(options.targetDir, targetPath);
1079
- const compatibilityRootPath = join7(options.targetDir, ".claude/skills");
1080
- const canonicalRootPath = join7(options.targetDir, ".agents/skills");
1081
- if (!lstatSync2(canonicalRootPath).isDirectory() || !lstatSync2(compatibilityRootPath).isSymbolicLink() || normalizeSymlinkTarget(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)) {
1082
2034
  throw new Error(
1083
2035
  `Legacy Claude compatibility root is not the exact canonical alias for ${entry.id}.`
1084
2036
  );
@@ -1087,10 +2039,10 @@ function createLegacyClaudeAdoptions(options) {
1087
2039
  consumedTargetPaths.add(entry.targetPath);
1088
2040
  continue;
1089
2041
  }
1090
- const targetStat = lstatSync2(targetAbsolutePath);
1091
- const legacyAbsolutePath = join7(options.targetDir, entry.targetPath);
1092
- const legacyStat = lstatSync2(legacyAbsolutePath);
1093
- 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);
1094
2046
  if (!targetStat.isSymbolicLink() || !legacyStat.isSymbolicLink() || targetStat.dev !== legacyStat.dev || targetStat.ino !== legacyStat.ino || realpathSync2(targetAbsolutePath) !== realpathSync2(expectedSourcePath)) {
1095
2047
  throw new Error(`Legacy Claude skill target cannot be safely adopted: ${entry.targetPath}`);
1096
2048
  }
@@ -1120,9 +2072,9 @@ function createRemovalActions(targetDir, agentAssetsDir, managedEntries, expecte
1120
2072
  `Refusing to remove managed asset outside supported roots: ${entry.targetPath}`
1121
2073
  );
1122
2074
  }
1123
- const targetAbsolutePath = join7(targetDir, entry.targetPath);
2075
+ const targetAbsolutePath = join8(targetDir, entry.targetPath);
1124
2076
  if (!pathExistsEvenIfDanglingSymlink2(targetAbsolutePath)) continue;
1125
- const stats = lstatSync2(targetAbsolutePath);
2077
+ const stats = lstatSync3(targetAbsolutePath);
1126
2078
  if ((entry.delivery ?? "symlink") === "snapshot") {
1127
2079
  if (!isValidSnapshotProjectTargetPath(entry.targetPath)) {
1128
2080
  throw new Error(
@@ -1151,9 +2103,9 @@ function createRemovalActions(targetDir, agentAssetsDir, managedEntries, expecte
1151
2103
  `Refusing to remove path that is no longer a managed symlink: ${entry.targetPath}`
1152
2104
  );
1153
2105
  }
1154
- const expectedSourcePath = join7(agentAssetsDir, entry.sourcePath);
1155
- const actualSourcePath = resolve(dirname3(targetAbsolutePath), readlinkSync(targetAbsolutePath));
1156
- if (actualSourcePath !== resolve(expectedSourcePath)) {
2106
+ const expectedSourcePath = join8(agentAssetsDir, entry.sourcePath);
2107
+ const actualSourcePath = resolve2(dirname4(targetAbsolutePath), readlinkSync(targetAbsolutePath));
2108
+ if (actualSourcePath !== resolve2(expectedSourcePath)) {
1157
2109
  throw new Error(
1158
2110
  `Refusing to remove managed symlink with changed target: ${entry.targetPath}`
1159
2111
  );
@@ -1177,7 +2129,7 @@ function isLegacyClaudeSkillTargetPath(path) {
1177
2129
  }
1178
2130
  function pathExistsEvenIfDanglingSymlink2(path) {
1179
2131
  try {
1180
- lstatSync2(path);
2132
+ lstatSync3(path);
1181
2133
  return true;
1182
2134
  } catch {
1183
2135
  return false;
@@ -1201,7 +2153,7 @@ function applyAssetInstallPlan(plan) {
1201
2153
  return { appliedActions };
1202
2154
  }
1203
2155
  function applyAction(targetDir, action) {
1204
- const targetAbsolutePath = join8(targetDir, action.targetPath);
2156
+ const targetAbsolutePath = join9(targetDir, action.targetPath);
1205
2157
  if (action.type === "remove-symlink") {
1206
2158
  removeManagedSymlink(targetAbsolutePath, action);
1207
2159
  return;
@@ -1212,41 +2164,41 @@ function applyAction(targetDir, action) {
1212
2164
  return;
1213
2165
  }
1214
2166
  if (action.type === "create-dir") {
1215
- mkdirSync2(targetAbsolutePath, { recursive: true });
2167
+ mkdirSync3(targetAbsolutePath, { recursive: true });
1216
2168
  return;
1217
2169
  }
1218
2170
  if (action.type === "write-file") {
1219
- mkdirSync2(dirname4(targetAbsolutePath), { recursive: true });
1220
- writeFileSync(targetAbsolutePath, action.content);
2171
+ mkdirSync3(dirname5(targetAbsolutePath), { recursive: true });
2172
+ writeFileSync2(targetAbsolutePath, action.content);
1221
2173
  return;
1222
2174
  }
1223
- mkdirSync2(dirname4(targetAbsolutePath), { recursive: true });
1224
- const sourceAbsolutePath = resolve2(action.sourcePath);
1225
- 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)) || ".";
1226
2178
  if (action.type === "symlink") {
1227
2179
  if (pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1228
2180
  throw new Error(`Refusing to overwrite unmanaged target: ${action.targetPath}`);
1229
2181
  }
1230
- symlinkSync(symlinkTarget, targetAbsolutePath);
2182
+ symlinkSync2(symlinkTarget, targetAbsolutePath);
1231
2183
  return;
1232
2184
  }
1233
2185
  if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1234
- symlinkSync(symlinkTarget, targetAbsolutePath);
2186
+ symlinkSync2(symlinkTarget, targetAbsolutePath);
1235
2187
  return;
1236
2188
  }
1237
- const stats = lstatSync3(targetAbsolutePath);
2189
+ const stats = lstatSync4(targetAbsolutePath);
1238
2190
  if (!stats.isSymbolicLink()) {
1239
2191
  throw new Error(`Refusing to overwrite unmanaged target: ${action.targetPath}`);
1240
2192
  }
1241
2193
  unlinkSync(targetAbsolutePath);
1242
- symlinkSync(symlinkTarget, targetAbsolutePath);
2194
+ symlinkSync2(symlinkTarget, targetAbsolutePath);
1243
2195
  }
1244
2196
  function validateExistingSymlink(targetDir, action) {
1245
2197
  if (!isManagedAssetTargetPath(action.targetPath)) {
1246
2198
  throw new Error(`Refusing unsafe existing symlink adoption: ${action.targetPath}`);
1247
2199
  }
1248
- const targetAbsolutePath = join8(targetDir, action.targetPath);
1249
- 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)) {
1250
2202
  throw new Error(`Existing skill symlink changed before apply: ${action.targetPath}`);
1251
2203
  }
1252
2204
  }
@@ -1257,7 +2209,7 @@ function validateSnapshotAction(targetDir, action) {
1257
2209
  if ("contentBase64" in action && hashSnapshotBytes(Buffer.from(action.contentBase64, "base64")) !== action.contentHash) {
1258
2210
  throw new Error(`Snapshot content hash is invalid: ${action.targetPath}`);
1259
2211
  }
1260
- const targetAbsolutePath = join8(targetDir, action.targetPath);
2212
+ const targetAbsolutePath = join9(targetDir, action.targetPath);
1261
2213
  if (action.type === "snapshot") {
1262
2214
  if (pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1263
2215
  throw new Error(`Snapshot target changed before apply: ${action.targetPath}`);
@@ -1273,7 +2225,7 @@ function validateSnapshotAction(targetDir, action) {
1273
2225
  return;
1274
2226
  }
1275
2227
  if (action.type === "migrate-symlink-to-snapshot") {
1276
- 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) {
1277
2229
  throw new Error(`Snapshot symlink changed before apply: ${action.targetPath}`);
1278
2230
  }
1279
2231
  return;
@@ -1281,30 +2233,30 @@ function validateSnapshotAction(targetDir, action) {
1281
2233
  assertRegularSnapshotHash(targetAbsolutePath, action.expectedContentHash, action.targetPath);
1282
2234
  }
1283
2235
  function applySnapshotAction(targetDir, action) {
1284
- const targetAbsolutePath = join8(targetDir, action.targetPath);
2236
+ const targetAbsolutePath = join9(targetDir, action.targetPath);
1285
2237
  if (action.type === "adopt-snapshot") return;
1286
2238
  if (action.type === "remove-snapshot") {
1287
2239
  unlinkSync(targetAbsolutePath);
1288
2240
  return;
1289
2241
  }
1290
2242
  if (action.type === "migrate-symlink-to-snapshot") unlinkSync(targetAbsolutePath);
1291
- mkdirSync2(dirname4(targetAbsolutePath), { recursive: true });
2243
+ mkdirSync3(dirname5(targetAbsolutePath), { recursive: true });
1292
2244
  if (action.type === "snapshot" && pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1293
2245
  throw new Error(`Refusing to overwrite unmanaged target: ${action.targetPath}`);
1294
2246
  }
1295
- writeFileSync(targetAbsolutePath, Buffer.from(action.contentBase64, "base64"));
2247
+ writeFileSync2(targetAbsolutePath, Buffer.from(action.contentBase64, "base64"));
1296
2248
  }
1297
2249
  function assertRegularSnapshotHash(targetAbsolutePath, expectedHash, targetPath) {
1298
2250
  if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1299
2251
  throw new Error(`Snapshot target changed before apply: ${targetPath}`);
1300
2252
  }
1301
- const stats = lstatSync3(targetAbsolutePath);
2253
+ const stats = lstatSync4(targetAbsolutePath);
1302
2254
  if (!stats.isFile() || hashAssetPathContent(targetAbsolutePath) !== expectedHash) {
1303
2255
  throw new Error(`Snapshot target hash changed before apply: ${targetPath}`);
1304
2256
  }
1305
2257
  }
1306
2258
  function hashSnapshotBytes(content) {
1307
- const hash = createHash3("sha256");
2259
+ const hash = createHash4("sha256");
1308
2260
  hash.update("");
1309
2261
  hash.update("\0");
1310
2262
  hash.update(content);
@@ -1315,17 +2267,17 @@ function validateAdoptedSymlink(targetDir, action) {
1315
2267
  if (!isManagedAssetTargetPath(action.targetPath) || !isLegacyClaudeSkillTargetPath(action.legacyTargetPath) || action.compatibilityRootPath !== ".claude/skills" || action.expectedCompatibilityRawTarget !== "../.agents/skills") {
1316
2268
  throw new Error(`Refusing unsafe legacy Claude adoption: ${action.legacyTargetPath}`);
1317
2269
  }
1318
- const canonicalRootPath = join8(targetDir, ".agents/skills");
1319
- const compatibilityRootPath = join8(targetDir, action.compatibilityRootPath);
1320
- const targetAbsolutePath = join8(targetDir, action.targetPath);
1321
- const legacyAbsolutePath = join8(targetDir, action.legacyTargetPath);
1322
- if (!lstatSync3(canonicalRootPath).isDirectory() || !lstatSync3(compatibilityRootPath).isSymbolicLink() || normalizeSymlinkTarget(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)) {
1323
2275
  throw new Error(
1324
2276
  `Legacy Claude compatibility alias changed before apply: ${action.compatibilityRootPath}`
1325
2277
  );
1326
2278
  }
1327
- const targetStat = lstatSync3(targetAbsolutePath);
1328
- const legacyStat = lstatSync3(legacyAbsolutePath);
2279
+ const targetStat = lstatSync4(targetAbsolutePath);
2280
+ const legacyStat = lstatSync4(legacyAbsolutePath);
1329
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)) {
1330
2282
  throw new Error(`Legacy Claude skill target changed before apply: ${action.targetPath}`);
1331
2283
  }
@@ -1337,33 +2289,33 @@ function removeManagedSymlink(targetAbsolutePath, action) {
1337
2289
  );
1338
2290
  }
1339
2291
  if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) return;
1340
- const stats = lstatSync3(targetAbsolutePath);
2292
+ const stats = lstatSync4(targetAbsolutePath);
1341
2293
  if (!stats.isSymbolicLink()) {
1342
2294
  throw new Error(
1343
2295
  `Refusing to remove path that is no longer a managed symlink: ${action.targetPath}`
1344
2296
  );
1345
2297
  }
1346
- const actualSourcePath = resolve2(dirname4(targetAbsolutePath), readlinkSync2(targetAbsolutePath));
1347
- if (actualSourcePath !== resolve2(action.expectedSourcePath)) {
2298
+ const actualSourcePath = resolve3(dirname5(targetAbsolutePath), readlinkSync2(targetAbsolutePath));
2299
+ if (actualSourcePath !== resolve3(action.expectedSourcePath)) {
1348
2300
  throw new Error(`Refusing to remove managed symlink with changed target: ${action.targetPath}`);
1349
2301
  }
1350
2302
  unlinkSync(targetAbsolutePath);
1351
2303
  }
1352
2304
  function pathExistsEvenIfDanglingSymlink3(path) {
1353
2305
  try {
1354
- lstatSync3(path);
2306
+ lstatSync4(path);
1355
2307
  return true;
1356
2308
  } catch {
1357
- return existsSync8(path);
2309
+ return existsSync9(path);
1358
2310
  }
1359
2311
  }
1360
2312
 
1361
2313
  // src/asset-targets/check.ts
1362
- import { existsSync as existsSync9, lstatSync as lstatSync4, readFileSync as readFileSync5, realpathSync as realpathSync4 } from "node:fs";
1363
- 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";
1364
2316
  function checkInstalledAssets(options) {
1365
- const lockfilePath = join9(options.targetDir, ".pro-gov/assets.lock.json");
1366
- if (!existsSync9(lockfilePath)) {
2317
+ const lockfilePath = join10(options.targetDir, ".pro-gov/assets.lock.json");
2318
+ if (!existsSync10(lockfilePath)) {
1367
2319
  return {
1368
2320
  targetDir: options.targetDir,
1369
2321
  issues: [
@@ -1375,7 +2327,7 @@ function checkInstalledAssets(options) {
1375
2327
  };
1376
2328
  }
1377
2329
  const registryById = new Map(options.registry.assets.map((asset) => [asset.id, asset]));
1378
- const lockfile = JSON.parse(readFileSync5(lockfilePath, "utf8"));
2330
+ const lockfile = JSON.parse(readFileSync6(lockfilePath, "utf8"));
1379
2331
  const issues = [];
1380
2332
  const strictRegistry = options.strictRegistry ?? false;
1381
2333
  const selectedAssetIds = (lockfile.assets ?? []).map((entry) => entry.id);
@@ -1404,7 +2356,7 @@ function checkInstalledAssets(options) {
1404
2356
  continue;
1405
2357
  }
1406
2358
  const asset = registryById.get(entry.id);
1407
- const targetAbsolutePath = join9(options.targetDir, entry.targetPath);
2359
+ const targetAbsolutePath = join10(options.targetDir, entry.targetPath);
1408
2360
  const delivery = entry.delivery ?? "symlink";
1409
2361
  const portableDeferredSkill = delivery === "symlink" && !strictRegistry && isProjectSkillTarget(entry.targetPath);
1410
2362
  if (delivery !== "symlink" && delivery !== "snapshot") {
@@ -1466,7 +2418,7 @@ function checkInstalledAssets(options) {
1466
2418
  });
1467
2419
  continue;
1468
2420
  }
1469
- const targetStats = lstatSync4(targetAbsolutePath);
2421
+ const targetStats = lstatSync5(targetAbsolutePath);
1470
2422
  if (delivery === "snapshot" && !targetStats.isFile()) {
1471
2423
  issues.push({
1472
2424
  type: "snapshot-not-regular-file",
@@ -1485,7 +2437,7 @@ function checkInstalledAssets(options) {
1485
2437
  });
1486
2438
  continue;
1487
2439
  }
1488
- if (delivery === "symlink" && !existsSync9(targetAbsolutePath)) {
2440
+ if (delivery === "symlink" && !existsSync10(targetAbsolutePath)) {
1489
2441
  if (portableDeferredSkill) continue;
1490
2442
  issues.push({
1491
2443
  type: "dangling-symlink",
@@ -1506,8 +2458,8 @@ function checkInstalledAssets(options) {
1506
2458
  });
1507
2459
  }
1508
2460
  if (!asset || !strictRegistry || registryProvenanceMismatch) continue;
1509
- const sourceAbsolutePath = join9(options.agentAssetsDir, asset.sourcePath);
1510
- if (!existsSync9(sourceAbsolutePath)) {
2461
+ const sourceAbsolutePath = join10(options.agentAssetsDir, asset.sourcePath);
2462
+ if (!existsSync10(sourceAbsolutePath)) {
1511
2463
  issues.push({
1512
2464
  type: "missing-source",
1513
2465
  id: entry.id,
@@ -1571,7 +2523,7 @@ function checkDuplicateSkillPlacements(targetDir, registry) {
1571
2523
  const skillName = assetSkillInstallName(asset);
1572
2524
  const autoPath = `.agents/skills/${skillName}`;
1573
2525
  const manualPath = `.agents/manual-skills/${skillName}`;
1574
- if (pathExistsEvenIfDanglingSymlink4(join9(targetDir, autoPath)) && pathExistsEvenIfDanglingSymlink4(join9(targetDir, manualPath))) {
2526
+ if (pathExistsEvenIfDanglingSymlink4(join10(targetDir, autoPath)) && pathExistsEvenIfDanglingSymlink4(join10(targetDir, manualPath))) {
1575
2527
  issues.push({
1576
2528
  type: "duplicate-skill-placement",
1577
2529
  id: asset.id,
@@ -1617,7 +2569,7 @@ function expectedRegistrySkillTargetPath(host, skillName, placement) {
1617
2569
  }
1618
2570
  function pathExistsEvenIfDanglingSymlink4(path) {
1619
2571
  try {
1620
- lstatSync4(path);
2572
+ lstatSync5(path);
1621
2573
  return true;
1622
2574
  } catch {
1623
2575
  return false;
@@ -1628,8 +2580,8 @@ function isProjectSkillTarget(targetPath) {
1628
2580
  }
1629
2581
 
1630
2582
  // src/asset-targets/recommend.ts
1631
- import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync6 } from "node:fs";
1632
- 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";
1633
2585
  var frontendPackages = /* @__PURE__ */ new Set([
1634
2586
  "@vitejs/plugin-react",
1635
2587
  "astro",
@@ -1643,21 +2595,21 @@ var frontendPackages = /* @__PURE__ */ new Set([
1643
2595
  ]);
1644
2596
  var agentEntryCandidates = ["AGENTS.md", "CLAUDE.md"];
1645
2597
  function discoverTargetSignals(targetDir) {
1646
- const packageJson = readJson(join10(targetDir, "package.json"));
2598
+ const packageJson = readJson(join11(targetDir, "package.json"));
1647
2599
  const dependencyNames = packageJson ? Object.keys({ ...packageJson.dependencies, ...packageJson.devDependencies }) : [];
1648
2600
  const frontendSignals = dependencyNames.filter((name) => frontendPackages.has(name)).sort();
1649
- const hasAgentEntry = agentEntryCandidates.some((file) => existsSync10(join10(targetDir, file)));
2601
+ const hasAgentEntry = agentEntryCandidates.some((file) => existsSync11(join11(targetDir, file)));
1650
2602
  const researchSignals = [
1651
- existsSync10(join10(targetDir, "docs/research")) ? "docs/research" : "",
1652
- existsSync10(join10(targetDir, "research")) ? "research" : "",
2603
+ existsSync11(join11(targetDir, "docs/research")) ? "docs/research" : "",
2604
+ existsSync11(join11(targetDir, "research")) ? "research" : "",
1653
2605
  hasBookChildDirectory(targetDir, "research") ? "books/*/research" : "",
1654
- textFileIncludes(join10(targetDir, "README.md"), ["research", "\u8C03\u7814"]) ? "README research" : ""
2606
+ textFileIncludes(join11(targetDir, "README.md"), ["research", "\u8C03\u7814"]) ? "README research" : ""
1655
2607
  ].filter(Boolean);
1656
2608
  const writingSignals = [
1657
- existsSync10(join10(targetDir, "chapters")) ? "chapters" : "",
1658
- existsSync10(join10(targetDir, "src/chapters")) ? "src/chapters" : "",
2609
+ existsSync11(join11(targetDir, "chapters")) ? "chapters" : "",
2610
+ existsSync11(join11(targetDir, "src/chapters")) ? "src/chapters" : "",
1659
2611
  hasBookChildDirectory(targetDir, "chapters") ? "books/*/chapters" : "",
1660
- textFileIncludes(join10(targetDir, "AGENTS.md"), [
2612
+ textFileIncludes(join11(targetDir, "AGENTS.md"), [
1661
2613
  "writing mode",
1662
2614
  "novel chapter",
1663
2615
  "book content"
@@ -1702,24 +2654,24 @@ function recommendBundlesForTarget(targetDir) {
1702
2654
  return recommendations;
1703
2655
  }
1704
2656
  function readJson(path) {
1705
- if (!existsSync10(path)) return void 0;
2657
+ if (!existsSync11(path)) return void 0;
1706
2658
  try {
1707
- return JSON.parse(readFileSync6(path, "utf8"));
2659
+ return JSON.parse(readFileSync7(path, "utf8"));
1708
2660
  } catch {
1709
2661
  return void 0;
1710
2662
  }
1711
2663
  }
1712
2664
  function textFileIncludes(path, needles) {
1713
- if (!existsSync10(path)) return false;
1714
- const contents = readFileSync6(path, "utf8").toLowerCase();
2665
+ if (!existsSync11(path)) return false;
2666
+ const contents = readFileSync7(path, "utf8").toLowerCase();
1715
2667
  return needles.some((needle) => contents.includes(needle.toLowerCase()));
1716
2668
  }
1717
2669
  function hasBookChildDirectory(targetDir, childName) {
1718
- const booksDir = join10(targetDir, "books");
1719
- if (!existsSync10(booksDir)) return false;
2670
+ const booksDir = join11(targetDir, "books");
2671
+ if (!existsSync11(booksDir)) return false;
1720
2672
  try {
1721
- return readdirSync6(booksDir, { withFileTypes: true }).some(
1722
- (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))
1723
2675
  );
1724
2676
  } catch {
1725
2677
  return false;
@@ -1753,9 +2705,67 @@ function runAssets(args) {
1753
2705
  if (subcommand2 === "npx") {
1754
2706
  return runAssetsNpx(rest);
1755
2707
  }
2708
+ if (subcommand2 === "catalog") {
2709
+ return runAssetsCatalog(rest);
2710
+ }
1756
2711
  printUsage();
1757
2712
  return 1;
1758
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
+ }
1759
2769
  function runAssetsPublicCheck(args) {
1760
2770
  const options = parsePublicCheckOptions(args);
1761
2771
  if (!options.ok) {
@@ -1835,7 +2845,7 @@ function runAssetsApply(args) {
1835
2845
  return 1;
1836
2846
  }
1837
2847
  try {
1838
- const plan = JSON.parse(readFileSync7(options.value.planPath, "utf8"));
2848
+ const plan = JSON.parse(readFileSync8(options.value.planPath, "utf8"));
1839
2849
  const result = applyAssetInstallPlan(plan);
1840
2850
  console.log(`applied-actions: ${result.appliedActions.length}`);
1841
2851
  return 0;
@@ -1960,8 +2970,8 @@ function runAssetsPlan(args) {
1960
2970
  console.log("dry-run: true");
1961
2971
  }
1962
2972
  if (options.value.outPath) {
1963
- mkdirSync3(dirname5(options.value.outPath), { recursive: true });
1964
- 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)}
1965
2975
  `);
1966
2976
  if (!options.value.json) {
1967
2977
  console.log(`plan: ${options.value.outPath}`);
@@ -1973,6 +2983,33 @@ function runAssetsPlan(args) {
1973
2983
  return 1;
1974
2984
  }
1975
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
+ }
1976
3013
  function parseListOptions(args) {
1977
3014
  const options = {
1978
3015
  json: false,
@@ -2159,10 +3196,10 @@ function parsePublicCheckOptions(args) {
2159
3196
  return { ok: false, error: `Unknown assets public-check option: ${arg}` };
2160
3197
  }
2161
3198
  }
2162
- if (!existsSync11(options.publicRoot)) {
3199
+ if (!existsSync12(options.publicRoot)) {
2163
3200
  return { ok: false, error: `Public agent assets root does not exist: ${options.publicRoot}` };
2164
3201
  }
2165
- if (!existsSync11(options.privateRoot)) {
3202
+ if (!existsSync12(options.privateRoot)) {
2166
3203
  return { ok: false, error: `Private agent assets root does not exist: ${options.privateRoot}` };
2167
3204
  }
2168
3205
  return { ok: true, value: options };
@@ -2171,13 +3208,13 @@ function getDefaultPublicCheckRoots() {
2171
3208
  const defaultRegistryRoot = loadAgentAssetRegistry().agentAssetsDir;
2172
3209
  if (defaultRegistryRoot.endsWith("public-agent-assets")) {
2173
3210
  return {
2174
- privateRoot: join11(dirname5(defaultRegistryRoot), "agent-assets"),
3211
+ privateRoot: join12(dirname6(defaultRegistryRoot), "agent-assets"),
2175
3212
  publicRoot: defaultRegistryRoot
2176
3213
  };
2177
3214
  }
2178
3215
  return {
2179
3216
  privateRoot: defaultRegistryRoot,
2180
- publicRoot: join11(dirname5(defaultRegistryRoot), "public-agent-assets")
3217
+ publicRoot: join12(dirname6(defaultRegistryRoot), "public-agent-assets")
2181
3218
  };
2182
3219
  }
2183
3220
  function listRegistryAssets(options) {
@@ -2238,6 +3275,9 @@ function printUsage() {
2238
3275
  );
2239
3276
  console.error(" pro-gov assets npx add <source> [--skill <name>] --plan [--root <path>]");
2240
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
+ );
2241
3281
  }
2242
3282
  function printNpxUsage() {
2243
3283
  console.log("Usage: pro-gov assets npx add <source> [--skill <name>] --plan [--root <path>]");
@@ -2245,11 +3285,19 @@ function printNpxUsage() {
2245
3285
  console.log("");
2246
3286
  console.log("Runs npx skills only in a temporary copy and prints a reviewable plan.");
2247
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
+ }
2248
3296
 
2249
3297
  // src/commands/doctor.ts
2250
- import { existsSync as existsSync12 } from "node:fs";
3298
+ import { existsSync as existsSync13 } from "node:fs";
2251
3299
  import { createRequire } from "node:module";
2252
- import { dirname as dirname6, join as join12 } from "node:path";
3300
+ import { dirname as dirname7, join as join13 } from "node:path";
2253
3301
  var REQUIRED_ASSETS = [
2254
3302
  "starter/.agents/skills/.gitkeep",
2255
3303
  "starter/AGENTS.template.md",
@@ -2314,16 +3362,16 @@ function resolveDocGovDependencyCli() {
2314
3362
  try {
2315
3363
  const require2 = createRequire(import.meta.url);
2316
3364
  const packageJsonPath = require2.resolve("@pieai/doc-gov/package.json");
2317
- const cliPath = join12(dirname6(packageJsonPath), "dist/cli.js");
2318
- return existsSync12(cliPath) ? cliPath : null;
3365
+ const cliPath = join13(dirname7(packageJsonPath), "dist/cli.js");
3366
+ return existsSync13(cliPath) ? cliPath : null;
2319
3367
  } catch {
2320
3368
  return null;
2321
3369
  }
2322
3370
  }
2323
3371
 
2324
3372
  // src/commands/init.ts
2325
- import { lstatSync as lstatSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync8, symlinkSync as symlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
2326
- 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";
2327
3375
 
2328
3376
  // src/commands/shared.ts
2329
3377
  function planStarterFiles(profile) {
@@ -2410,7 +3458,7 @@ function runInit(args) {
2410
3458
  function applyStarterFiles(files, profile) {
2411
3459
  const root = process.cwd();
2412
3460
  const conflicts = files.filter((file) => {
2413
- const targetPath = join13(root, file.targetPath);
3461
+ const targetPath = join14(root, file.targetPath);
2414
3462
  const stat = safeLstat(targetPath);
2415
3463
  if (!stat) return false;
2416
3464
  return file.kind !== "directory" || !stat.isDirectory();
@@ -2422,19 +3470,19 @@ function applyStarterFiles(files, profile) {
2422
3470
  return 1;
2423
3471
  }
2424
3472
  for (const file of files) {
2425
- const targetPath = join13(root, file.targetPath);
2426
- mkdirSync4(dirname7(targetPath), { recursive: true });
3473
+ const targetPath = join14(root, file.targetPath);
3474
+ mkdirSync5(dirname8(targetPath), { recursive: true });
2427
3475
  if (file.kind === "directory") {
2428
- mkdirSync4(targetPath, { recursive: true });
3476
+ mkdirSync5(targetPath, { recursive: true });
2429
3477
  continue;
2430
3478
  }
2431
3479
  if (file.kind === "symlink") {
2432
- symlinkSync2(file.linkTarget, targetPath);
3480
+ symlinkSync3(file.linkTarget, targetPath);
2433
3481
  continue;
2434
3482
  }
2435
- const source = readFileSync8(file.absoluteSourcePath);
2436
- const content = file.targetPath === "AGENTS.md" ? renderAgentsTemplate(source.toString("utf8"), basename2(root), profile) : source;
2437
- 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);
2438
3486
  }
2439
3487
  console.log("pro-gov init APPLIED");
2440
3488
  console.log(`profile: ${profile}`);
@@ -2451,7 +3499,7 @@ function renderAgentsTemplate(template, projectName, profile) {
2451
3499
  }
2452
3500
  function safeLstat(path) {
2453
3501
  try {
2454
- return lstatSync5(path);
3502
+ return lstatSync6(path);
2455
3503
  } catch {
2456
3504
  return void 0;
2457
3505
  }
@@ -2465,22 +3513,22 @@ function readFlag(args, flag) {
2465
3513
  }
2466
3514
 
2467
3515
  // src/commands/host-lens.ts
2468
- import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "node:fs";
2469
- 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";
2470
3518
 
2471
3519
  // src/host-lens.ts
2472
3520
  import { execFileSync } from "node:child_process";
2473
3521
  import {
2474
3522
  cpSync as cpSync2,
2475
- existsSync as existsSync13,
2476
- lstatSync as lstatSync6,
2477
- mkdirSync as mkdirSync5,
2478
- readdirSync as readdirSync7,
2479
- statSync as statSync4,
2480
- 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
2481
3529
  } from "node:fs";
2482
3530
  import { homedir } from "node:os";
2483
- 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";
2484
3532
  import { fileURLToPath as fileURLToPath3 } from "node:url";
2485
3533
  var ROOT_DEFINITIONS = [
2486
3534
  {
@@ -2562,9 +3610,9 @@ var ROOT_DEFINITIONS = [
2562
3610
  }
2563
3611
  ];
2564
3612
  function inspectHost(options = {}) {
2565
- const homePath = resolve3(options.homeDir ?? homedir());
3613
+ const homePath = resolve4(options.homeDir ?? homedir());
2566
3614
  const now = options.now ?? /* @__PURE__ */ new Date();
2567
- const rootPaths = ROOT_DEFINITIONS.map((definition) => join14(homePath, definition.relativePath));
3615
+ const rootPaths = ROOT_DEFINITIONS.map((definition) => join15(homePath, definition.relativePath));
2568
3616
  const rootSizes = measurePaths(rootPaths);
2569
3617
  const findings = inspectFindings(homePath);
2570
3618
  const rootFindingIds = /* @__PURE__ */ new Map();
@@ -2581,7 +3629,7 @@ function inspectHost(options = {}) {
2581
3629
  label: definition.label,
2582
3630
  path: displayPath(path, homePath),
2583
3631
  kind: definition.kind,
2584
- exists: existsSync13(path),
3632
+ exists: existsSync14(path),
2585
3633
  bytes: rootSizes.get(path) ?? 0,
2586
3634
  status: rootFindings.length > 0 ? "attention" : "healthy",
2587
3635
  note: definition.note
@@ -2688,25 +3736,25 @@ function createHostLensCleanupPlan(report) {
2688
3736
  };
2689
3737
  }
2690
3738
  function writeHostLensReport(report, outDir) {
2691
- mkdirSync5(outDir, { recursive: true });
3739
+ mkdirSync6(outDir, { recursive: true });
2692
3740
  const assets = findHostDashboardAssets();
2693
3741
  for (const file of ["index.html", "app.js", "app.css"]) {
2694
- const source = join14(assets, file);
2695
- if (!existsSync13(source)) throw new Error(`HostLens dashboard asset is missing: ${source}`);
2696
- 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));
2697
3745
  }
2698
- const jsonPath = join14(outDir, "host-lens.json");
2699
- const htmlPath = join14(outDir, "index.html");
2700
- 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)}
2701
3749
  `);
2702
- writeFileSync4(join14(outDir, "data.js"), `window.__HOST_LENS__ = ${safeJavaScriptJson(report)};
3750
+ writeFileSync5(join15(outDir, "data.js"), `window.__HOST_LENS__ = ${safeJavaScriptJson(report)};
2703
3751
  `);
2704
3752
  return { jsonPath, htmlPath };
2705
3753
  }
2706
3754
  function inspectFindings(homePath) {
2707
3755
  const candidates = [];
2708
3756
  const addGroup = (value) => {
2709
- const paths = value.paths.filter((path) => existsSync13(path));
3757
+ const paths = value.paths.filter((path) => existsSync14(path));
2710
3758
  if (paths.length === 0) return;
2711
3759
  candidates.push({ ...value, paths: paths.map((path) => displayPath(path, homePath)) });
2712
3760
  };
@@ -2714,7 +3762,7 @@ function inspectFindings(homePath) {
2714
3762
  id: "codex-backups",
2715
3763
  rootId: "codex",
2716
3764
  label: "Codex \u5386\u53F2\u5907\u4EFD",
2717
- paths: matchingChildren(join14(homePath, ".codex"), (name) => /^backup-/i.test(name)),
3765
+ paths: matchingChildren(join15(homePath, ".codex"), (name) => /^backup-/i.test(name)),
2718
3766
  category: "backup",
2719
3767
  confidence: "medium",
2720
3768
  disposition: "manual-review",
@@ -2725,7 +3773,7 @@ function inspectFindings(homePath) {
2725
3773
  id: "codex-cache",
2726
3774
  rootId: "codex",
2727
3775
  label: "Codex \u53EF\u518D\u751F\u6210\u7F13\u5B58",
2728
- paths: [join14(homePath, ".codex/cache")],
3776
+ paths: [join15(homePath, ".codex/cache")],
2729
3777
  category: "cache",
2730
3778
  confidence: "high",
2731
3779
  disposition: "report-only",
@@ -2736,7 +3784,7 @@ function inspectFindings(homePath) {
2736
3784
  id: "claude-temporary",
2737
3785
  rootId: "claude-code",
2738
3786
  label: "Claude Code \u4E34\u65F6\u76EE\u5F55",
2739
- paths: matchingChildren(join14(homePath, ".claude"), (name) => /^(temp|tmp)[-_]/i.test(name)),
3787
+ paths: matchingChildren(join15(homePath, ".claude"), (name) => /^(temp|tmp)[-_]/i.test(name)),
2740
3788
  category: "temporary",
2741
3789
  confidence: "high",
2742
3790
  disposition: "manual-review",
@@ -2747,7 +3795,7 @@ function inspectFindings(homePath) {
2747
3795
  id: "claude-cache",
2748
3796
  rootId: "claude-code",
2749
3797
  label: "Claude Code \u7F13\u5B58",
2750
- paths: [join14(homePath, ".claude/cache")],
3798
+ paths: [join15(homePath, ".claude/cache")],
2751
3799
  category: "cache",
2752
3800
  confidence: "high",
2753
3801
  disposition: "report-only",
@@ -2758,7 +3806,7 @@ function inspectFindings(homePath) {
2758
3806
  id: "copilot-session-history",
2759
3807
  rootId: "copilot",
2760
3808
  label: "Copilot \u4F1A\u8BDD\u72B6\u6001",
2761
- paths: [join14(homePath, ".copilot/session-state")],
3809
+ paths: [join15(homePath, ".copilot/session-state")],
2762
3810
  category: "session-history",
2763
3811
  confidence: "low",
2764
3812
  disposition: "manual-review",
@@ -2769,7 +3817,7 @@ function inspectFindings(homePath) {
2769
3817
  id: "npm-npx-cache",
2770
3818
  rootId: "npm-cache",
2771
3819
  label: "npx \u4E34\u65F6\u5B89\u88C5\u7F13\u5B58",
2772
- paths: [join14(homePath, ".npm/_npx")],
3820
+ paths: [join15(homePath, ".npm/_npx")],
2773
3821
  category: "cache",
2774
3822
  confidence: "high",
2775
3823
  disposition: "native-tool",
@@ -2781,7 +3829,7 @@ function inspectFindings(homePath) {
2781
3829
  id: "npm-content-cache",
2782
3830
  rootId: "npm-cache",
2783
3831
  label: "npm \u5185\u5BB9\u7F13\u5B58",
2784
- paths: [join14(homePath, ".npm/_cacache")],
3832
+ paths: [join15(homePath, ".npm/_cacache")],
2785
3833
  category: "cache",
2786
3834
  confidence: "high",
2787
3835
  disposition: "native-tool",
@@ -2794,7 +3842,7 @@ function inspectFindings(homePath) {
2794
3842
  rootId: "pnpm-store",
2795
3843
  label: "pnpm \u672A\u5F15\u7528\u5305\u5019\u9009",
2796
3844
  paths: [
2797
- join14(
3845
+ join15(
2798
3846
  homePath,
2799
3847
  process.platform === "win32" ? "AppData/Local/pnpm/store" : "Library/pnpm/store"
2800
3848
  )
@@ -2811,11 +3859,11 @@ function inspectFindings(homePath) {
2811
3859
  rootId: "playwright",
2812
3860
  label: "Playwright \u6D4F\u89C8\u5668\u7248\u672C",
2813
3861
  paths: [
2814
- join14(
3862
+ join15(
2815
3863
  homePath,
2816
3864
  process.platform === "win32" ? "AppData/Local/ms-playwright" : "Library/Caches/ms-playwright"
2817
3865
  ),
2818
- join14(homePath, ".cache/ms-playwright")
3866
+ join15(homePath, ".cache/ms-playwright")
2819
3867
  ],
2820
3868
  category: "duplicate-runtime",
2821
3869
  confidence: "medium",
@@ -2867,16 +3915,16 @@ function buildProtections(homePath) {
2867
3915
  }));
2868
3916
  }
2869
3917
  function matchingChildren(root, predicate) {
2870
- if (!existsSync13(root)) return [];
3918
+ if (!existsSync14(root)) return [];
2871
3919
  try {
2872
- 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));
2873
3921
  } catch {
2874
3922
  return [];
2875
3923
  }
2876
3924
  }
2877
3925
  function measurePaths(paths) {
2878
- const uniquePaths = [...new Set(paths.map((path) => resolve3(path)))].filter(
2879
- (path) => existsSync13(path)
3926
+ const uniquePaths = [...new Set(paths.map((path) => resolve4(path)))].filter(
3927
+ (path) => existsSync14(path)
2880
3928
  );
2881
3929
  const result = /* @__PURE__ */ new Map();
2882
3930
  if (uniquePaths.length === 0) return result;
@@ -2889,7 +3937,7 @@ function measurePaths(paths) {
2889
3937
  for (const line of output.split(/\r?\n/)) {
2890
3938
  const match = line.match(/^(\d+)\s+(.+)$/);
2891
3939
  if (!match) continue;
2892
- result.set(resolve3(match[2]), Number(match[1]) * 1024);
3940
+ result.set(resolve4(match[2]), Number(match[1]) * 1024);
2893
3941
  }
2894
3942
  return result;
2895
3943
  } catch {
@@ -2899,7 +3947,7 @@ function measurePaths(paths) {
2899
3947
  }
2900
3948
  function fallbackMeasure(root) {
2901
3949
  try {
2902
- const rootStats = lstatSync6(root);
3950
+ const rootStats = lstatSync7(root);
2903
3951
  if (!rootStats.isDirectory()) return rootStats.size;
2904
3952
  } catch {
2905
3953
  return 0;
@@ -2912,17 +3960,17 @@ function fallbackMeasure(root) {
2912
3960
  if (!current) continue;
2913
3961
  let entries;
2914
3962
  try {
2915
- entries = readdirSync7(current, { withFileTypes: true });
3963
+ entries = readdirSync8(current, { withFileTypes: true });
2916
3964
  } catch {
2917
3965
  continue;
2918
3966
  }
2919
3967
  for (const entry of entries) {
2920
3968
  visited += 1;
2921
- const path = join14(current, entry.name);
3969
+ const path = join15(current, entry.name);
2922
3970
  if (entry.isDirectory()) pending.push(path);
2923
3971
  else if (entry.isFile()) {
2924
3972
  try {
2925
- bytes += statSync4(path).size;
3973
+ bytes += statSync5(path).size;
2926
3974
  } catch {
2927
3975
  }
2928
3976
  }
@@ -2933,35 +3981,35 @@ function fallbackMeasure(root) {
2933
3981
  }
2934
3982
  function countImmediateEntries(path) {
2935
3983
  try {
2936
- return lstatSync6(path).isDirectory() ? readdirSync7(path).length : 1;
3984
+ return lstatSync7(path).isDirectory() ? readdirSync8(path).length : 1;
2937
3985
  } catch {
2938
3986
  return 0;
2939
3987
  }
2940
3988
  }
2941
3989
  function displayPath(path, homePath) {
2942
- const absolute = resolve3(path);
2943
- const withinHome = relative6(homePath, absolute);
3990
+ const absolute = resolve4(path);
3991
+ const withinHome = relative7(homePath, absolute);
2944
3992
  if (withinHome === "") return "~";
2945
3993
  if (!withinHome.startsWith("..")) return `~/${withinHome.replaceAll("\\", "/")}`;
2946
3994
  return absolute;
2947
3995
  }
2948
3996
  function undisplayPath(path, homePath) {
2949
3997
  if (path === "~") return homePath;
2950
- if (path.startsWith("~/")) return join14(homePath, path.slice(2));
2951
- return resolve3(path);
3998
+ if (path.startsWith("~/")) return join15(homePath, path.slice(2));
3999
+ return resolve4(path);
2952
4000
  }
2953
4001
  function findHostDashboardAssets() {
2954
- const packageRoot2 = dirname8(dirname8(fileURLToPath3(import.meta.url)));
4002
+ const packageRoot2 = dirname9(dirname9(fileURLToPath3(import.meta.url)));
2955
4003
  const candidates = [
2956
4004
  process.env.PGS_HOST_DASHBOARD_ASSETS_DIR,
2957
- join14(packageRoot2, ".host-dashboard-build"),
2958
- join14(packageRoot2, "assets/host-dashboard"),
2959
- join14(process.cwd(), ".host-dashboard-build"),
2960
- join14(process.cwd(), "assets/host-dashboard"),
2961
- join14(process.cwd(), "packages/pro-gov/.host-dashboard-build"),
2962
- 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")
2963
4011
  ].filter((value) => Boolean(value));
2964
- const match = candidates.find((path) => existsSync13(join14(path, "index.html")));
4012
+ const match = candidates.find((path) => existsSync14(join15(path, "index.html")));
2965
4013
  if (!match)
2966
4014
  throw new Error(
2967
4015
  "HostLens dashboard assets were not built. Run pnpm --filter @pieai/pro-gov build."
@@ -2973,12 +4021,12 @@ function safeJavaScriptJson(value) {
2973
4021
  }
2974
4022
 
2975
4023
  // src/portfolio/manifest.ts
2976
- import { existsSync as existsSync14, readFileSync as readFileSync9 } from "node:fs";
2977
- 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";
2978
4026
  function loadPortfolioManifest(configPath) {
2979
4027
  let parsed;
2980
4028
  try {
2981
- parsed = JSON.parse(readFileSync9(configPath, "utf8"));
4029
+ parsed = JSON.parse(readFileSync10(configPath, "utf8"));
2982
4030
  } catch (error) {
2983
4031
  return {
2984
4032
  configPath,
@@ -2990,7 +4038,7 @@ function loadPortfolioManifest(configPath) {
2990
4038
  ]
2991
4039
  };
2992
4040
  }
2993
- const normalized = resolveManifestPaths(parsed, dirname9(resolve4(configPath)));
4041
+ const normalized = resolveManifestPaths(parsed, dirname10(resolve5(configPath)));
2994
4042
  const issues = validatePortfolioManifest(normalized);
2995
4043
  return {
2996
4044
  configPath,
@@ -3001,16 +4049,16 @@ function loadPortfolioManifest(configPath) {
3001
4049
  function resolveManifestPaths(value, configDir) {
3002
4050
  if (!isRecord(value)) return value;
3003
4051
  const resolveEndpoint = (endpoint) => {
3004
- if (!isRecord(endpoint) || typeof endpoint.path !== "string" || isAbsolute3(endpoint.path)) {
4052
+ if (!isRecord(endpoint) || typeof endpoint.path !== "string" || isAbsolute4(endpoint.path)) {
3005
4053
  return endpoint;
3006
4054
  }
3007
- return { ...endpoint, path: resolve4(configDir, endpoint.path) };
4055
+ return { ...endpoint, path: resolve5(configDir, endpoint.path) };
3008
4056
  };
3009
4057
  return {
3010
4058
  ...value,
3011
- 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) ? {
3012
4060
  ...value.technologyGovernance,
3013
- strategySource: resolve4(configDir, value.technologyGovernance.strategySource)
4061
+ strategySource: resolve5(configDir, value.technologyGovernance.strategySource)
3014
4062
  } : value.technologyGovernance,
3015
4063
  controlPlane: resolveEndpoint(value.controlPlane),
3016
4064
  executionEngine: resolveEndpoint(value.executionEngine),
@@ -3195,7 +4243,7 @@ function validateEndpoint(value, field, issues, technologyCatalog) {
3195
4243
  });
3196
4244
  return;
3197
4245
  }
3198
- if (!existsSync14(value.path)) {
4246
+ if (!existsSync15(value.path)) {
3199
4247
  issues.push({
3200
4248
  type: "missing-path",
3201
4249
  id: typeof value.id === "string" ? value.id : void 0,
@@ -3626,13 +4674,13 @@ function isExactVersion(value) {
3626
4674
  return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(value);
3627
4675
  }
3628
4676
  function isRepositoryRelativePath(value) {
3629
- if (value.length === 0 || isAbsolute3(value)) return false;
4677
+ if (value.length === 0 || isAbsolute4(value)) return false;
3630
4678
  const segments = value.replaceAll("\\", "/").split("/");
3631
4679
  return !segments.includes("..");
3632
4680
  }
3633
4681
  function isExactRepositoryRelativePath(value) {
3634
4682
  if (value.length === 0) return false;
3635
- if (isAbsolute3(value)) return false;
4683
+ if (isAbsolute4(value)) return false;
3636
4684
  if (/^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\") || value.startsWith("//"))
3637
4685
  return false;
3638
4686
  if (value.includes("\\")) return false;
@@ -3930,7 +4978,7 @@ function runScan(options) {
3930
4978
  const inspection = resolveInspectionOptions(options);
3931
4979
  if (!inspection.ok) return reportConfigError(inspection.error);
3932
4980
  const report = inspectHost(inspection.value);
3933
- const written = writeHostLensReport(report, resolve5(options.outPath));
4981
+ const written = writeHostLensReport(report, resolve6(options.outPath));
3934
4982
  if (options.json) {
3935
4983
  console.log(JSON.stringify({ ok: true, ...written, summary: report.summary }, null, 2));
3936
4984
  } else {
@@ -3950,9 +4998,9 @@ function runPlan(options) {
3950
4998
  if (!inspection.ok) return reportConfigError(inspection.error);
3951
4999
  const report = inspectHost(inspection.value);
3952
5000
  const plan = createHostLensCleanupPlan(report);
3953
- const outPath = resolve5(options.outPath);
3954
- mkdirSync6(dirname10(outPath), { recursive: true });
3955
- 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)}
3956
5004
  `);
3957
5005
  if (options.json)
3958
5006
  console.log(JSON.stringify({ ok: true, outPath, actions: plan.actions.length }, null, 2));
@@ -4025,7 +5073,7 @@ function parseOptions(args) {
4025
5073
  if (arg === "--home") {
4026
5074
  const value = args[index + 1];
4027
5075
  if (!value) return { ok: false, error: "Expected value after --home" };
4028
- options.homeDir = resolve5(value);
5076
+ options.homeDir = resolve6(value);
4029
5077
  index += 1;
4030
5078
  continue;
4031
5079
  }
@@ -4039,7 +5087,7 @@ function parseOptions(args) {
4039
5087
  if (arg === "--config") {
4040
5088
  const value = args[index + 1];
4041
5089
  if (!value) return { ok: false, error: "Expected value after --config" };
4042
- options.configPath = resolve5(value);
5090
+ options.configPath = resolve6(value);
4043
5091
  index += 1;
4044
5092
  continue;
4045
5093
  }
@@ -4065,8 +5113,8 @@ function printUsage2() {
4065
5113
  }
4066
5114
 
4067
5115
  // src/learning/recall.ts
4068
- import { existsSync as existsSync15, readdirSync as readdirSync8, readFileSync as readFileSync10 } from "node:fs";
4069
- 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";
4070
5118
  function recallLearnings(root, options) {
4071
5119
  const query = options.query.trim();
4072
5120
  const terms = tokenize(query);
@@ -4089,16 +5137,16 @@ function recallLearnings(root, options) {
4089
5137
  function loadLearningRecords(root) {
4090
5138
  const recordsByTitle = /* @__PURE__ */ new Map();
4091
5139
  for (const relativeDir of ["docs/reference/learnings", "docs/solutions"]) {
4092
- const learningDir = join15(root, relativeDir);
4093
- if (!existsSync15(learningDir)) continue;
5140
+ const learningDir = join16(root, relativeDir);
5141
+ if (!existsSync16(learningDir)) continue;
4094
5142
  for (const path of listMarkdownFiles(learningDir)) {
4095
5143
  const record = readLearningRecord(root, path);
4096
5144
  const key = record.title.trim().toLowerCase();
4097
5145
  if (!recordsByTitle.has(key)) recordsByTitle.set(key, record);
4098
5146
  }
4099
5147
  }
4100
- const conceptsPath = join15(root, "CONCEPTS.md");
4101
- if (existsSync15(conceptsPath)) {
5148
+ const conceptsPath = join16(root, "CONCEPTS.md");
5149
+ if (existsSync16(conceptsPath)) {
4102
5150
  const record = readLearningRecord(root, conceptsPath);
4103
5151
  recordsByTitle.set(`concepts:${record.title.toLowerCase()}`, record);
4104
5152
  }
@@ -4106,8 +5154,8 @@ function loadLearningRecords(root) {
4106
5154
  }
4107
5155
  function listMarkdownFiles(dir) {
4108
5156
  const files = [];
4109
- for (const entry of readdirSync8(dir, { withFileTypes: true })) {
4110
- const absolutePath = join15(dir, entry.name);
5157
+ for (const entry of readdirSync9(dir, { withFileTypes: true })) {
5158
+ const absolutePath = join16(dir, entry.name);
4111
5159
  if (entry.isDirectory()) {
4112
5160
  files.push(...listMarkdownFiles(absolutePath));
4113
5161
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -4117,11 +5165,11 @@ function listMarkdownFiles(dir) {
4117
5165
  return files.sort();
4118
5166
  }
4119
5167
  function readLearningRecord(root, absolutePath) {
4120
- const content = readFileSync10(absolutePath, "utf8");
5168
+ const content = readFileSync11(absolutePath, "utf8");
4121
5169
  const parsed = splitFrontmatter(content);
4122
5170
  const body = parsed.body;
4123
5171
  return {
4124
- relativePath: normalizePath(relative7(root, absolutePath)),
5172
+ relativePath: normalizePath(relative8(root, absolutePath)),
4125
5173
  title: findTitle(parsed.frontmatter, body) ?? titleFromPath(absolutePath),
4126
5174
  metadata: parsed.frontmatter,
4127
5175
  body
@@ -4149,7 +5197,7 @@ function findTitle(frontmatter, body) {
4149
5197
  return heading ? heading.slice(2).trim() : void 0;
4150
5198
  }
4151
5199
  function titleFromPath(path) {
4152
- 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(" ");
4153
5201
  }
4154
5202
  function scoreRecord(record, terms) {
4155
5203
  const title = record.title.toLowerCase();
@@ -4202,8 +5250,8 @@ function cleanMarkdownLine(input) {
4202
5250
  }
4203
5251
 
4204
5252
  // src/learning/capture.ts
4205
- import { existsSync as existsSync16, mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "node:fs";
4206
- 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";
4207
5255
  function captureLearning(root, options) {
4208
5256
  const title = options.title.trim();
4209
5257
  const summary = options.summary.trim();
@@ -4211,13 +5259,13 @@ function captureLearning(root, options) {
4211
5259
  if (!summary) throw new Error("summary is required");
4212
5260
  const category = slugify(options.category ?? "workflow-issues") || "workflow-issues";
4213
5261
  const moduleName = options.module?.trim() || "PGS learning capture";
4214
- const dir = join16(root, "docs/reference/learnings", category);
4215
- mkdirSync7(dir, { recursive: true });
5262
+ const dir = join17(root, "docs/reference/learnings", category);
5263
+ mkdirSync8(dir, { recursive: true });
4216
5264
  const path = uniquePath(dir, slugify(title) || "learning");
4217
- const idSlug = basename4(path, ".md");
4218
- writeFileSync6(path, renderLearning({ title, summary, category, moduleName, idSlug }));
5265
+ const idSlug = basename5(path, ".md");
5266
+ writeFileSync7(path, renderLearning({ title, summary, category, moduleName, idSlug }));
4219
5267
  return {
4220
- relativePath: normalizePath2(relative8(root, path)),
5268
+ relativePath: normalizePath2(relative9(root, path)),
4221
5269
  title,
4222
5270
  captureMode: "pgs-native"
4223
5271
  };
@@ -4259,10 +5307,10 @@ function renderLearning(options) {
4259
5307
  }
4260
5308
  function uniquePath(dir, slug) {
4261
5309
  let index = 1;
4262
- let candidate = join16(dir, `${slug}.md`);
4263
- while (existsSync16(candidate)) {
5310
+ let candidate = join17(dir, `${slug}.md`);
5311
+ while (existsSync17(candidate)) {
4264
5312
  index += 1;
4265
- candidate = join16(dir, `${slug}-${index}.md`);
5313
+ candidate = join17(dir, `${slug}-${index}.md`);
4266
5314
  }
4267
5315
  return candidate;
4268
5316
  }
@@ -4447,12 +5495,12 @@ function printUsage3() {
4447
5495
  }
4448
5496
 
4449
5497
  // src/commands/lens.ts
4450
- import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "node:fs";
4451
- 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";
4452
5500
 
4453
5501
  // src/lens/audit.ts
4454
- import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "node:fs";
4455
- 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";
4456
5504
  var REQUIRED_ARTIFACTS = [
4457
5505
  "manifest.md",
4458
5506
  "raw/project-lens/architecture-lens.md",
@@ -4470,20 +5518,20 @@ function createProjectLensAuditPackage(targetDir, auditDir) {
4470
5518
  version: 1,
4471
5519
  target: {
4472
5520
  path: targetDir,
4473
- name: basename5(targetDir) || "target"
5521
+ name: basename6(targetDir) || "target"
4474
5522
  },
4475
5523
  requiredArtifacts: [...REQUIRED_ARTIFACTS]
4476
5524
  };
4477
- mkdirSync8(auditDir, { recursive: true });
4478
- writeJson(join17(auditDir, "audit.contract.json"), contract);
5525
+ mkdirSync9(auditDir, { recursive: true });
5526
+ writeJson(join18(auditDir, "audit.contract.json"), contract);
4479
5527
  for (const artifactPath of REQUIRED_ARTIFACTS) {
4480
- writeTemplate(join17(auditDir, artifactPath), renderArtifactTemplate(artifactPath, contract));
5528
+ writeTemplate(join18(auditDir, artifactPath), renderArtifactTemplate(artifactPath, contract));
4481
5529
  }
4482
5530
  return contract;
4483
5531
  }
4484
5532
  function checkProjectLensAuditPackage(auditDir, options = {}) {
4485
- const contractPath = join17(auditDir, "audit.contract.json");
4486
- if (!existsSync17(contractPath)) {
5533
+ const contractPath = join18(auditDir, "audit.contract.json");
5534
+ if (!existsSync18(contractPath)) {
4487
5535
  return {
4488
5536
  ok: false,
4489
5537
  auditDir,
@@ -4497,7 +5545,7 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
4497
5545
  }
4498
5546
  let contract;
4499
5547
  try {
4500
- contract = JSON.parse(readFileSync11(contractPath, "utf8"));
5548
+ contract = JSON.parse(readFileSync12(contractPath, "utf8"));
4501
5549
  } catch (error) {
4502
5550
  return {
4503
5551
  ok: false,
@@ -4530,12 +5578,12 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
4530
5578
  }
4531
5579
  }
4532
5580
  for (const artifactPath of REQUIRED_ARTIFACTS) {
4533
- const absolutePath = join17(auditDir, artifactPath);
4534
- if (!existsSync17(absolutePath)) {
5581
+ const absolutePath = join18(auditDir, artifactPath);
5582
+ if (!existsSync18(absolutePath)) {
4535
5583
  issues.push({ type: "missing-required-artifact", path: artifactPath });
4536
5584
  continue;
4537
5585
  }
4538
- const content = readFileSync11(absolutePath, "utf8");
5586
+ const content = readFileSync12(absolutePath, "utf8");
4539
5587
  if (isPendingArtifact(content)) {
4540
5588
  issues.push({ type: "artifact-not-complete", path: artifactPath });
4541
5589
  } else if (hasTemplateBody(content)) {
@@ -4551,13 +5599,13 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
4551
5599
  };
4552
5600
  }
4553
5601
  function writeJson(path, value) {
4554
- mkdirSync8(dirname11(path), { recursive: true });
4555
- writeFileSync7(path, `${JSON.stringify(value, null, 2)}
5602
+ mkdirSync9(dirname12(path), { recursive: true });
5603
+ writeFileSync8(path, `${JSON.stringify(value, null, 2)}
4556
5604
  `);
4557
5605
  }
4558
5606
  function writeTemplate(path, content) {
4559
- mkdirSync8(dirname11(path), { recursive: true });
4560
- writeFileSync7(path, content);
5607
+ mkdirSync9(dirname12(path), { recursive: true });
5608
+ writeFileSync8(path, content);
4561
5609
  }
4562
5610
  function renderArtifactTemplate(artifactPath, contract) {
4563
5611
  const title = artifactPath.replace(/\.md$/, "").split("/").map((part) => part.replaceAll("-", " ")).join(" / ");
@@ -4836,13 +5884,13 @@ function formatLink(link) {
4836
5884
 
4837
5885
  // src/lens/scan.ts
4838
5886
  import { spawnSync as spawnSync3 } from "node:child_process";
4839
- import { existsSync as existsSync21, readFileSync as readFileSync13, statSync as statSync6 } from "node:fs";
5887
+ import { existsSync as existsSync22, readFileSync as readFileSync14, statSync as statSync7 } from "node:fs";
4840
5888
  import { homedir as homedir3 } from "node:os";
4841
- import { join as join22 } from "node:path";
5889
+ import { join as join23 } from "node:path";
4842
5890
 
4843
5891
  // src/host-ssot.ts
4844
- import { lstatSync as lstatSync7, readlinkSync as readlinkSync3, realpathSync as realpathSync5 } from "node:fs";
4845
- 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";
4846
5894
  function inspectProjectHostSsot(root) {
4847
5895
  const agentsEntry = inspectCanonicalPath(root, "AGENTS.md");
4848
5896
  const canonicalSkills = inspectCanonicalPath(root, ".agents/skills");
@@ -4886,7 +5934,7 @@ function inspectUserSkillsSsot(homeDir) {
4886
5934
  };
4887
5935
  }
4888
5936
  function inspectCanonicalPath(root, path) {
4889
- const absolutePath = join18(root, path);
5937
+ const absolutePath = join19(root, path);
4890
5938
  const stat = safeLstat2(absolutePath);
4891
5939
  if (!stat) return { path, status: "missing" };
4892
5940
  if (stat.isSymbolicLink()) {
@@ -4902,7 +5950,7 @@ function inspectCanonicalPath(root, path) {
4902
5950
  return { path, status: "other" };
4903
5951
  }
4904
5952
  function inspectCompatibilityLink(root, path, expectedRawTarget) {
4905
- const absolutePath = join18(root, path);
5953
+ const absolutePath = join19(root, path);
4906
5954
  const stat = safeLstat2(absolutePath);
4907
5955
  const base = { path, expectedRawTarget, compliant: false };
4908
5956
  if (!stat) return { ...base, status: "missing" };
@@ -4912,8 +5960,8 @@ function inspectCompatibilityLink(root, path, expectedRawTarget) {
4912
5960
  return { ...base, status: "other" };
4913
5961
  }
4914
5962
  const rawTarget = normalizeSymlinkTarget(readlinkSync3(absolutePath));
4915
- const resolvedTarget = resolve6(dirname12(absolutePath), rawTarget);
4916
- const expectedPath = resolve6(dirname12(absolutePath), expectedRawTarget);
5963
+ const resolvedTarget = resolve7(dirname13(absolutePath), rawTarget);
5964
+ const expectedPath = resolve7(dirname13(absolutePath), expectedRawTarget);
4917
5965
  const targetStat = safeLstat2(resolvedTarget);
4918
5966
  if (!targetStat) {
4919
5967
  return {
@@ -4932,7 +5980,7 @@ function inspectCompatibilityLink(root, path, expectedRawTarget) {
4932
5980
  if (!targetMatches) {
4933
5981
  return { ...base, rawTarget, resolvedTarget, status: "wrong-target" };
4934
5982
  }
4935
- if (isAbsolute4(rawTarget)) {
5983
+ if (isAbsolute5(rawTarget)) {
4936
5984
  return { ...base, rawTarget, resolvedTarget, status: "absolute-symlink" };
4937
5985
  }
4938
5986
  if (rawTarget !== expectedRawTarget) {
@@ -4953,16 +6001,16 @@ function inspectCompatibilityLink(root, path, expectedRawTarget) {
4953
6001
  }
4954
6002
  function safeLstat2(path) {
4955
6003
  try {
4956
- return lstatSync7(path);
6004
+ return lstatSync8(path);
4957
6005
  } catch {
4958
6006
  return void 0;
4959
6007
  }
4960
6008
  }
4961
6009
 
4962
6010
  // src/portfolio/redundancy.ts
4963
- 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";
4964
6012
  import { homedir as homedir2 } from "node:os";
4965
- import { join as join19 } from "node:path";
6013
+ import { join as join20 } from "node:path";
4966
6014
  var DEFAULT_CACHE_THRESHOLD_BYTES = 1e9;
4967
6015
  var MAX_CACHE_ENTRIES = 2e4;
4968
6016
  function inspectHostRedundancy(options = {}) {
@@ -4993,8 +6041,8 @@ function inspectProjectRedundancy(root, options = {}) {
4993
6041
  }
4994
6042
  function inspectLegacyDirectories(root) {
4995
6043
  const relativePath = ".agent";
4996
- const path = join19(root, relativePath);
4997
- if (!existsSync18(path)) return [];
6044
+ const path = join20(root, relativePath);
6045
+ if (!existsSync19(path)) return [];
4998
6046
  const stats = collectDirectoryStats(path);
4999
6047
  return [
5000
6048
  {
@@ -5009,16 +6057,16 @@ function inspectLegacyDirectories(root) {
5009
6057
  function getPlaywrightCachePaths(homeDir, configuredPath) {
5010
6058
  const candidates = [
5011
6059
  configuredPath && configuredPath !== "0" ? configuredPath : void 0,
5012
- join19(homeDir, "Library/Caches/ms-playwright"),
5013
- join19(homeDir, ".cache/ms-playwright"),
5014
- 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")
5015
6063
  ].filter((path) => Boolean(path));
5016
6064
  return [...new Set(candidates)];
5017
6065
  }
5018
6066
  function inspectPlaywrightCache(path, cache) {
5019
6067
  const cached = cache?.get(path);
5020
6068
  if (cached) return cached;
5021
- if (!existsSync18(path)) {
6069
+ if (!existsSync19(path)) {
5022
6070
  const missing = {
5023
6071
  path,
5024
6072
  exists: false,
@@ -5033,7 +6081,7 @@ function inspectPlaywrightCache(path, cache) {
5033
6081
  const stats = collectDirectoryStats(path);
5034
6082
  let revisionCount = 0;
5035
6083
  try {
5036
- revisionCount = readdirSync9(path, { withFileTypes: true }).filter(
6084
+ revisionCount = readdirSync10(path, { withFileTypes: true }).filter(
5037
6085
  (entry) => entry.isDirectory()
5038
6086
  ).length;
5039
6087
  } catch {
@@ -5060,7 +6108,7 @@ function collectDirectoryStats(root) {
5060
6108
  if (!current) continue;
5061
6109
  let entries;
5062
6110
  try {
5063
- entries = readdirSync9(current, { withFileTypes: true });
6111
+ entries = readdirSync10(current, { withFileTypes: true });
5064
6112
  } catch {
5065
6113
  continue;
5066
6114
  }
@@ -5069,13 +6117,13 @@ function collectDirectoryStats(root) {
5069
6117
  truncated = true;
5070
6118
  break;
5071
6119
  }
5072
- const path = join19(current, entry.name);
6120
+ const path = join20(current, entry.name);
5073
6121
  if (entry.isDirectory()) {
5074
6122
  pending.push(path);
5075
6123
  } else if (entry.isFile()) {
5076
6124
  fileCount += 1;
5077
6125
  try {
5078
- bytes += statSync5(path).size;
6126
+ bytes += statSync6(path).size;
5079
6127
  } catch {
5080
6128
  }
5081
6129
  }
@@ -5086,11 +6134,11 @@ function collectDirectoryStats(root) {
5086
6134
  }
5087
6135
 
5088
6136
  // src/portfolio/verification.ts
5089
- import { existsSync as existsSync19, readFileSync as readFileSync12 } from "node:fs";
5090
- 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";
5091
6139
  var REQUIRED_PROJECT_SCRIPTS = ["typecheck", "lint", "format:check", "verify"];
5092
6140
  function inspectProjectVerification(root) {
5093
- const packageJson = readPackageJson(join20(root, "package.json"));
6141
+ const packageJson = readPackageJson(join21(root, "package.json"));
5094
6142
  const scripts = Object.fromEntries(
5095
6143
  REQUIRED_PROJECT_SCRIPTS.map((name) => [
5096
6144
  name,
@@ -5108,9 +6156,9 @@ function inspectProjectVerification(root) {
5108
6156
  };
5109
6157
  }
5110
6158
  function readPackageJson(path) {
5111
- if (!existsSync19(path)) return void 0;
6159
+ if (!existsSync20(path)) return void 0;
5112
6160
  try {
5113
- return JSON.parse(readFileSync12(path, "utf8"));
6161
+ return JSON.parse(readFileSync13(path, "utf8"));
5114
6162
  } catch {
5115
6163
  return void 0;
5116
6164
  }
@@ -5118,8 +6166,8 @@ function readPackageJson(path) {
5118
6166
 
5119
6167
  // src/repository-files.ts
5120
6168
  import { spawnSync as spawnSync2 } from "node:child_process";
5121
- import { existsSync as existsSync20, readdirSync as readdirSync10 } from "node:fs";
5122
- import { isAbsolute as isAbsolute5, join as join21, posix as posix3, relative as relative9 } from "node:path";
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";
5123
6171
  var gitMaxBufferBytes = 64 * 1024 * 1024;
5124
6172
  var RepositoryFileDiscoveryError = class extends Error {
5125
6173
  constructor(message) {
@@ -5130,7 +6178,7 @@ var RepositoryFileDiscoveryError = class extends Error {
5130
6178
  function discoverRepositoryFiles(root, options = {}) {
5131
6179
  const probe = runGit(root, ["rev-parse", "--is-inside-work-tree"]);
5132
6180
  if (!probe.ok) {
5133
- if (!probe.notRepository || existsSync20(join21(root, ".git"))) {
6181
+ if (!probe.notRepository || existsSync21(join22(root, ".git"))) {
5134
6182
  throw new RepositoryFileDiscoveryError(probe.message);
5135
6183
  }
5136
6184
  return {
@@ -5161,7 +6209,7 @@ function discoverRepositoryFiles(root, options = {}) {
5161
6209
  `Git returned a path outside the repository boundary: ${path}`
5162
6210
  );
5163
6211
  }
5164
- if (existsSync20(join21(root, normalized))) files.add(normalized);
6212
+ if (existsSync21(join22(root, normalized))) files.add(normalized);
5165
6213
  }
5166
6214
  return { source: "git", files: [...files].sort() };
5167
6215
  }
@@ -5173,21 +6221,21 @@ function discoverFilesystemFiles(root, options) {
5173
6221
  const maxDepth = options.fallbackMaxDepth ?? Number.POSITIVE_INFINITY;
5174
6222
  const ignoredDirectories2 = options.fallbackIgnoredDirectories ?? /* @__PURE__ */ new Set();
5175
6223
  const visit = (directory, depth) => {
5176
- if (depth > maxDepth || !existsSync20(directory)) return;
6224
+ if (depth > maxDepth || !existsSync21(directory)) return;
5177
6225
  let entries;
5178
6226
  try {
5179
- entries = readdirSync10(directory, { withFileTypes: true });
6227
+ entries = readdirSync11(directory, { withFileTypes: true });
5180
6228
  } catch {
5181
6229
  return;
5182
6230
  }
5183
6231
  for (const entry of entries) {
5184
- const absolutePath = join21(directory, entry.name);
6232
+ const absolutePath = join22(directory, entry.name);
5185
6233
  if (entry.isDirectory()) {
5186
6234
  if (!ignoredDirectories2.has(entry.name)) visit(absolutePath, depth + 1);
5187
6235
  continue;
5188
6236
  }
5189
6237
  if (!entry.isFile()) continue;
5190
- const relativePath = normalizeRepositoryRelativePath(relative9(root, absolutePath));
6238
+ const relativePath = normalizeRepositoryRelativePath(relative10(root, absolutePath));
5191
6239
  if (isSafeRepositoryRelativePath(relativePath) && (options.fallbackIncludeFile?.(relativePath) ?? true)) {
5192
6240
  files.add(relativePath);
5193
6241
  }
@@ -5197,7 +6245,7 @@ function discoverFilesystemFiles(root, options) {
5197
6245
  return [...files].sort();
5198
6246
  }
5199
6247
  function isSafeRepositoryRelativePath(path) {
5200
- 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("../");
5201
6249
  }
5202
6250
  function runGit(root, args) {
5203
6251
  const result = spawnSync2("git", ["-C", root, ...args], {
@@ -5237,7 +6285,7 @@ function scanProjectLensTarget(targetDir, options = {}) {
5237
6285
  includedFileCount: files.length,
5238
6286
  excludedFileCount: candidateFiles.length - files.length
5239
6287
  },
5240
- aiEntryFiles: ["AGENTS.md", "CLAUDE.md"].filter((file) => existsSync21(join22(targetDir, file))),
6288
+ aiEntryFiles: ["AGENTS.md", "CLAUDE.md"].filter((file) => existsSync22(join23(targetDir, file))),
5241
6289
  aiConfigFiles: [],
5242
6290
  hostSsot: inspectProjectHostSsot(targetDir),
5243
6291
  userHostSsot: inspectUserSkillsSsot(options.homeDir ?? process.env.HOME ?? homedir3()),
@@ -5248,19 +6296,19 @@ function scanProjectLensTarget(targetDir, options = {}) {
5248
6296
  }),
5249
6297
  packageJson,
5250
6298
  docs: {
5251
- hasDocsDirectory: existsSync21(join22(targetDir, "docs")),
6299
+ hasDocsDirectory: existsSync22(join23(targetDir, "docs")),
5252
6300
  markdownFileCount: markdownFiles.length,
5253
6301
  governanceFiles: markdownFiles.filter((file) => file.startsWith("docs/governance/") || file.startsWith("docs/policy/")).sort()
5254
6302
  },
5255
6303
  git: readGitState(targetDir),
5256
- 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)
5257
6305
  };
5258
6306
  }
5259
6307
  function readPackageJson2(targetDir) {
5260
- const packageJsonPath = join22(targetDir, "package.json");
5261
- if (!existsSync21(packageJsonPath)) return void 0;
6308
+ const packageJsonPath = join23(targetDir, "package.json");
6309
+ if (!existsSync22(packageJsonPath)) return void 0;
5262
6310
  try {
5263
- const packageJson = JSON.parse(readFileSync13(packageJsonPath, "utf8"));
6311
+ const packageJson = JSON.parse(readFileSync14(packageJsonPath, "utf8"));
5264
6312
  return {
5265
6313
  scripts: Object.keys(packageJson.scripts ?? {}).sort(),
5266
6314
  dependencies: Object.keys(packageJson.dependencies ?? {}).sort(),
@@ -5351,8 +6399,8 @@ function runLensReport(args) {
5351
6399
  }
5352
6400
  const report = scanProjectLensTarget(options.value.targetDir);
5353
6401
  const markdown = renderProjectLensMarkdownReport(report);
5354
- mkdirSync9(dirname13(options.value.outPath), { recursive: true });
5355
- writeFileSync8(options.value.outPath, markdown);
6402
+ mkdirSync10(dirname14(options.value.outPath), { recursive: true });
6403
+ writeFileSync9(options.value.outPath, markdown);
5356
6404
  console.log(`report: ${options.value.outPath}`);
5357
6405
  return 0;
5358
6406
  }
@@ -5457,15 +6505,15 @@ function printUsage4() {
5457
6505
  }
5458
6506
 
5459
6507
  // src/commands/portfolio.ts
5460
- import { existsSync as existsSync33, readFileSync as readFileSync18 } from "node:fs";
5461
- 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";
5462
6510
 
5463
6511
  // src/portfolio/doctor.ts
5464
6512
  import { spawnSync as spawnSync5 } from "node:child_process";
5465
- import { existsSync as existsSync24, readFileSync as readFileSync16 } from "node:fs";
6513
+ import { existsSync as existsSync25, readFileSync as readFileSync17 } from "node:fs";
5466
6514
  import { createRequire as createRequire2 } from "node:module";
5467
6515
  import { homedir as homedir4 } from "node:os";
5468
- import { dirname as dirname15, join as join25 } from "node:path";
6516
+ import { dirname as dirname16, join as join26 } from "node:path";
5469
6517
  import { fileURLToPath as fileURLToPath4 } from "node:url";
5470
6518
 
5471
6519
  // src/host-tooling/inventory.ts
@@ -5557,8 +6605,8 @@ function isRecord2(value) {
5557
6605
  }
5558
6606
 
5559
6607
  // src/portfolio/asset-state.ts
5560
- import { existsSync as existsSync22, lstatSync as lstatSync8, readFileSync as readFileSync14 } from "node:fs";
5561
- 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";
5562
6610
  function comparePortfolioAssetState(options) {
5563
6611
  const expectedManifest = readPlanDocument(
5564
6612
  options.expectedPlan,
@@ -5569,10 +6617,10 @@ function comparePortfolioAssetState(options) {
5569
6617
  ".pro-gov/assets.lock.json"
5570
6618
  );
5571
6619
  const currentManifest = readJsonFile(
5572
- join23(options.targetDir, ".pro-gov/assets.json")
6620
+ join24(options.targetDir, ".pro-gov/assets.json")
5573
6621
  );
5574
6622
  const currentLock = readJsonFile(
5575
- join23(options.targetDir, ".pro-gov/assets.lock.json")
6623
+ join24(options.targetDir, ".pro-gov/assets.lock.json")
5576
6624
  );
5577
6625
  const issues = [];
5578
6626
  if (!sameStrings(currentManifest?.bundleIds, expectedManifest?.bundleIds)) {
@@ -5600,7 +6648,7 @@ function comparePortfolioAssetState(options) {
5600
6648
  (action) => action.type === "adopt-symlink" && action.assetId === entry.id && action.legacyTargetPath === entry.targetPath
5601
6649
  ))
5602
6650
  continue;
5603
- const targetAbsolutePath = join23(options.targetDir, entry.targetPath);
6651
+ const targetAbsolutePath = join24(options.targetDir, entry.targetPath);
5604
6652
  if (!pathIsSymlink(targetAbsolutePath)) continue;
5605
6653
  issues.push({
5606
6654
  type: "orphaned-managed-symlink",
@@ -5622,9 +6670,9 @@ function readPlanDocument(plan, targetPath) {
5622
6670
  }
5623
6671
  }
5624
6672
  function readJsonFile(path) {
5625
- if (!existsSync22(path)) return void 0;
6673
+ if (!existsSync23(path)) return void 0;
5626
6674
  try {
5627
- return JSON.parse(readFileSync14(path, "utf8"));
6675
+ return JSON.parse(readFileSync15(path, "utf8"));
5628
6676
  } catch {
5629
6677
  return void 0;
5630
6678
  }
@@ -5650,7 +6698,7 @@ function normalizeLock(lock) {
5650
6698
  }
5651
6699
  function pathIsSymlink(path) {
5652
6700
  try {
5653
- return lstatSync8(path).isSymbolicLink();
6701
+ return lstatSync9(path).isSymbolicLink();
5654
6702
  } catch {
5655
6703
  return false;
5656
6704
  }
@@ -5658,8 +6706,8 @@ function pathIsSymlink(path) {
5658
6706
 
5659
6707
  // src/portfolio/version-policy.ts
5660
6708
  import { spawnSync as spawnSync4 } from "node:child_process";
5661
- import { existsSync as existsSync23, lstatSync as lstatSync9, readFileSync as readFileSync15 } from "node:fs";
5662
- import { dirname as dirname14, join as join24 } from "node:path";
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";
5663
6711
  function inspectVersionPolicy(root, policy, projectType) {
5664
6712
  if (!policy) return { status: "compliant", packages: [], runtimes: [], attentionCount: 0 };
5665
6713
  const packageManifests = collectPackageManifests(root);
@@ -5768,10 +6816,10 @@ function findDeclaredVersion(packageJson, name) {
5768
6816
  function readInstalledVersion(root, name, fromDirectory = root) {
5769
6817
  let current = fromDirectory;
5770
6818
  while (true) {
5771
- const packageJson = readJson2(join24(current, "node_modules", name, "package.json"));
6819
+ const packageJson = readJson2(join25(current, "node_modules", name, "package.json"));
5772
6820
  if (typeof packageJson?.version === "string") return packageJson.version;
5773
6821
  if (current === root) return void 0;
5774
- const parent = dirname14(current);
6822
+ const parent = dirname15(current);
5775
6823
  if (parent === current) return void 0;
5776
6824
  current = parent;
5777
6825
  }
@@ -5806,15 +6854,15 @@ function collectPackageManifests(root) {
5806
6854
  if (relativePath.split("/").at(-1) !== "package.json" || directorySegments.length > 6 || directorySegments.some((segment) => ignored.has(segment))) {
5807
6855
  return;
5808
6856
  }
5809
- const path = join24(root, relativePath);
6857
+ const path = join25(root, relativePath);
5810
6858
  try {
5811
- if (!lstatSync9(path).isFile()) return;
6859
+ if (!lstatSync10(path).isFile()) return;
5812
6860
  } catch {
5813
6861
  return;
5814
6862
  }
5815
6863
  const packageJson = readJson2(path);
5816
6864
  if (!packageJson) return;
5817
- manifests.set(relativePath, { path: relativePath, directory: dirname14(path), packageJson });
6865
+ manifests.set(relativePath, { path: relativePath, directory: dirname15(path), packageJson });
5818
6866
  };
5819
6867
  const discovery = discoverRepositoryFiles(root, {
5820
6868
  gitPathspecs: ["package.json", ":(glob)**/package.json"],
@@ -5829,9 +6877,9 @@ function unique(values) {
5829
6877
  return [...new Set(values)];
5830
6878
  }
5831
6879
  function readJson2(path) {
5832
- if (!existsSync23(path)) return void 0;
6880
+ if (!existsSync24(path)) return void 0;
5833
6881
  try {
5834
- return JSON.parse(readFileSync15(path, "utf8"));
6882
+ return JSON.parse(readFileSync16(path, "utf8"));
5835
6883
  } catch {
5836
6884
  return void 0;
5837
6885
  }
@@ -5864,12 +6912,12 @@ function inspectTarget(options) {
5864
6912
  const { target } = options;
5865
6913
  const hostSsot = inspectProjectHostSsot(target.path);
5866
6914
  const issues = [];
5867
- const packageJson = readJson3(join25(target.path, "package.json"));
6915
+ const packageJson = readJson3(join26(target.path, "package.json"));
5868
6916
  const packages = {};
5869
6917
  for (const packageName of ["@pieai/pro-gov", "@pieai/doc-gov"]) {
5870
6918
  const declared = packageJson?.devDependencies?.[packageName] ?? packageJson?.dependencies?.[packageName];
5871
6919
  const installedPackage = readJson3(
5872
- join25(target.path, "node_modules", packageName, "package.json")
6920
+ join26(target.path, "node_modules", packageName, "package.json")
5873
6921
  );
5874
6922
  const installed = installedPackage?.version;
5875
6923
  const expected = options.expectedPackageVersions[packageName];
@@ -5929,7 +6977,7 @@ function inspectTarget(options) {
5929
6977
  type: "asset-lock-drift",
5930
6978
  message: error instanceof Error ? error.message : String(error)
5931
6979
  });
5932
- if (!existsSync24(join25(target.path, ".pro-gov/assets.json"))) {
6980
+ if (!existsSync25(join26(target.path, ".pro-gov/assets.json"))) {
5933
6981
  issues.push({ type: "bundle-drift", message: "Target asset manifest is missing." });
5934
6982
  }
5935
6983
  }
@@ -5947,15 +6995,15 @@ function inspectTarget(options) {
5947
6995
  };
5948
6996
  }
5949
6997
  function readTargetAssetHost(targetDir) {
5950
- const lockfile = readJson3(join25(targetDir, ".pro-gov/assets.lock.json"));
6998
+ const lockfile = readJson3(join26(targetDir, ".pro-gov/assets.lock.json"));
5951
6999
  return isAssetRegistryHost(lockfile?.host) ? lockfile.host : void 0;
5952
7000
  }
5953
7001
  function isAssetRegistryHost(value) {
5954
7002
  return value === "codex" || value === "claude-code" || value === "gemini-cli" || value === "antigravity";
5955
7003
  }
5956
7004
  function runTargetChecks(target) {
5957
- const proGovCli = join25(target.path, "node_modules/@pieai/pro-gov/dist/cli.js");
5958
- 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");
5959
7007
  const commands = [
5960
7008
  {
5961
7009
  name: "pro-gov doctor",
@@ -5966,7 +7014,7 @@ function runTargetChecks(target) {
5966
7014
  { name: "doc-gov scan --check", cli: docGovCli, args: ["scan", "--check"] }
5967
7015
  ];
5968
7016
  return commands.map((command2) => {
5969
- if (!existsSync24(command2.cli)) return { name: command2.name, status: null };
7017
+ if (!existsSync25(command2.cli)) return { name: command2.name, status: null };
5970
7018
  const result = spawnSync5(process.execPath, [command2.cli, ...command2.args], {
5971
7019
  cwd: target.path,
5972
7020
  encoding: "utf8",
@@ -6005,18 +7053,18 @@ function getExpectedPackageVersions() {
6005
7053
  };
6006
7054
  }
6007
7055
  function findOwnPackageJson() {
6008
- let current = dirname15(fileURLToPath4(import.meta.url));
7056
+ let current = dirname16(fileURLToPath4(import.meta.url));
6009
7057
  for (let depth = 0; depth < 5; depth += 1) {
6010
- const candidate = join25(current, "package.json");
6011
- if (existsSync24(candidate)) return candidate;
6012
- current = dirname15(current);
7058
+ const candidate = join26(current, "package.json");
7059
+ if (existsSync25(candidate)) return candidate;
7060
+ current = dirname16(current);
6013
7061
  }
6014
7062
  return "";
6015
7063
  }
6016
7064
  function readJson3(path) {
6017
- if (!path || !existsSync24(path)) return void 0;
7065
+ if (!path || !existsSync25(path)) return void 0;
6018
7066
  try {
6019
- return JSON.parse(readFileSync16(path, "utf8"));
7067
+ return JSON.parse(readFileSync17(path, "utf8"));
6020
7068
  } catch {
6021
7069
  return void 0;
6022
7070
  }
@@ -6033,15 +7081,15 @@ function deduplicateIssues(issues) {
6033
7081
 
6034
7082
  // src/portfolio/ai-health/index.ts
6035
7083
  import { homedir as homedir5 } from "node:os";
6036
- 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";
6037
7085
 
6038
7086
  // src/portfolio/ai-health/entries.ts
6039
- import { existsSync as existsSync26, lstatSync as lstatSync11, realpathSync as realpathSync7 } from "node:fs";
6040
- 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";
6041
7089
 
6042
7090
  // src/portfolio/ai-health/shared.ts
6043
7091
  import { execFileSync as execFileSync2 } from "node:child_process";
6044
- 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";
6045
7093
  function safeRealpath(path) {
6046
7094
  try {
6047
7095
  return realpathSync6(path);
@@ -6072,7 +7120,7 @@ function jsonObjectKeys(path, key) {
6072
7120
  return Object.keys(value[key]).sort();
6073
7121
  }
6074
7122
  function tomlMcpNames(path) {
6075
- if (!existsSync25(path)) return [];
7123
+ if (!existsSync26(path)) return [];
6076
7124
  const names = /* @__PURE__ */ new Set();
6077
7125
  for (const line of safeRead(path).split(/\r?\n/)) {
6078
7126
  const match = line.match(/^\s*\[mcp_servers\.(?:"([^"]+)"|([^.\]]+))\]\s*$/);
@@ -6083,35 +7131,35 @@ function tomlMcpNames(path) {
6083
7131
  }
6084
7132
  function readJson4(path) {
6085
7133
  try {
6086
- return JSON.parse(readFileSync17(path, "utf8"));
7134
+ return JSON.parse(readFileSync18(path, "utf8"));
6087
7135
  } catch {
6088
7136
  return void 0;
6089
7137
  }
6090
7138
  }
6091
7139
  function safeRead(path) {
6092
7140
  try {
6093
- return readFileSync17(path, "utf8");
7141
+ return readFileSync18(path, "utf8");
6094
7142
  } catch {
6095
7143
  return "";
6096
7144
  }
6097
7145
  }
6098
7146
  function safeReadDir(path) {
6099
7147
  try {
6100
- return readdirSync11(path).sort();
7148
+ return readdirSync12(path).sort();
6101
7149
  } catch {
6102
7150
  return [];
6103
7151
  }
6104
7152
  }
6105
7153
  function safeIsDirectory(path) {
6106
7154
  try {
6107
- return statSync7(path).isDirectory();
7155
+ return statSync8(path).isDirectory();
6108
7156
  } catch {
6109
7157
  return false;
6110
7158
  }
6111
7159
  }
6112
7160
  function pathLexists(path) {
6113
7161
  try {
6114
- lstatSync10(path);
7162
+ lstatSync11(path);
6115
7163
  return true;
6116
7164
  } catch {
6117
7165
  return false;
@@ -6149,12 +7197,12 @@ function hasWorkflowReminderHooks(hooks) {
6149
7197
  );
6150
7198
  }
6151
7199
  function inspectEntries(root) {
6152
- const agentsPath = join26(root, "AGENTS.md");
6153
- const agents = !existsSync26(agentsPath) ? "missing" : safeRead(agentsPath).includes("PGS-ROUTER:BEGIN") ? "pgs-router" : "custom";
6154
- 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");
6155
7203
  let claude = "missing";
6156
7204
  if (pathLexists(claudePath)) {
6157
- const info = lstatSync11(claudePath);
7205
+ const info = lstatSync12(claudePath);
6158
7206
  if (info.isSymbolicLink()) {
6159
7207
  try {
6160
7208
  claude = realpathSync7(claudePath) === realpathSync7(agentsPath) ? "agents-symlink" : "custom";
@@ -6171,19 +7219,19 @@ function inspectEntries(root) {
6171
7219
  var AGENT_LINK_ROOTS = [".agents/workflows", ".agents/commands", ".claude/commands"];
6172
7220
  function inspectAgentLinks(root) {
6173
7221
  const entries = AGENT_LINK_ROOTS.flatMap((directory) => {
6174
- const directoryPath = join26(root, directory);
7222
+ const directoryPath = join27(root, directory);
6175
7223
  if (!pathLexists(directoryPath)) return [];
6176
7224
  try {
6177
- if (!lstatSync11(directoryPath).isDirectory()) return [];
7225
+ if (!lstatSync12(directoryPath).isDirectory()) return [];
6178
7226
  } catch {
6179
7227
  return [];
6180
7228
  }
6181
7229
  return safeReadDir(directoryPath).filter((name) => !name.startsWith(".")).flatMap((name) => {
6182
7230
  const relativePath = `${directory}/${name}`;
6183
- const path = join26(root, relativePath);
7231
+ const path = join27(root, relativePath);
6184
7232
  let stat;
6185
7233
  try {
6186
- stat = lstatSync11(path);
7234
+ stat = lstatSync12(path);
6187
7235
  } catch {
6188
7236
  return [];
6189
7237
  }
@@ -6204,9 +7252,9 @@ function inspectAgentLinks(root) {
6204
7252
  };
6205
7253
  }
6206
7254
  function inspectOptionalEntry(root, filename, agentsPath) {
6207
- const path = join26(root, filename);
7255
+ const path = join27(root, filename);
6208
7256
  if (!pathLexists(path)) return "missing";
6209
- const info = lstatSync11(path);
7257
+ const info = lstatSync12(path);
6210
7258
  if (info.isSymbolicLink()) {
6211
7259
  try {
6212
7260
  return realpathSync7(path) === realpathSync7(agentsPath) ? "agents-symlink" : "custom";
@@ -6244,7 +7292,7 @@ function inspectHooks(root) {
6244
7292
  { host: "codex", path: ".codex/hooks.json" }
6245
7293
  ];
6246
7294
  return configs.map((config) => {
6247
- const value = readJson4(join26(root, config.path));
7295
+ const value = readJson4(join27(root, config.path));
6248
7296
  const counts = /* @__PURE__ */ new Map();
6249
7297
  collectHookEvents(value, counts);
6250
7298
  return {
@@ -6265,18 +7313,18 @@ function collectHookEvents(value, counts) {
6265
7313
  }
6266
7314
  }
6267
7315
  function inspectDocs(root, expected) {
6268
- const packageJson = readJson4(join26(root, "package.json"));
7316
+ const packageJson = readJson4(join27(root, "package.json"));
6269
7317
  const dependencies = isRecord3(packageJson) ? { ...recordOrEmpty(packageJson.dependencies), ...recordOrEmpty(packageJson.devDependencies) } : {};
6270
7318
  const docGov = dependencyVersion(dependencies["@pieai/doc-gov"]);
6271
7319
  const proGov = dependencyVersion(dependencies["@pieai/pro-gov"]);
6272
- 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.]+)/);
6273
7321
  const declared = [docGov, proGov].filter((value) => Boolean(value));
6274
7322
  return {
6275
7323
  routerVersion: routerMatch?.[1],
6276
7324
  expectedRouterVersion: CURRENT_ROUTER_VERSION,
6277
7325
  routerAligned: routerMatch?.[1] === CURRENT_ROUTER_VERSION,
6278
- manifest: existsSync26(join26(root, "docs/governance/MANIFEST.yml")),
6279
- 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")),
6280
7328
  packages: {
6281
7329
  expected,
6282
7330
  docGov,
@@ -6338,17 +7386,17 @@ function inspectGit2(root) {
6338
7386
  }
6339
7387
 
6340
7388
  // src/portfolio/ai-health/hosts.ts
6341
- import { existsSync as existsSync28 } from "node:fs";
6342
- 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";
6343
7391
 
6344
7392
  // src/portfolio/ai-health/devspace.ts
6345
- import { existsSync as existsSync27, statSync as statSync8 } from "node:fs";
6346
- 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";
6347
7395
  function inspectDevSpaceHealth(options) {
6348
7396
  const run = options.run ?? runDevSpaceCommand;
6349
- const configDirectory = join27(options.homeDir, ".devspace");
6350
- const configPath = join27(configDirectory, "config.json");
6351
- 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");
6352
7400
  const installedResult = run("devspace", ["--version"], 3e3);
6353
7401
  const installedVersion = installedResult.ok ? installedResult.stdout.match(/\b\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?\b/)?.[0] : void 0;
6354
7402
  const latestResult = run(
@@ -6371,9 +7419,9 @@ function inspectDevSpaceHealth(options) {
6371
7419
  (repositoryPath) => allowedRoots.some((root) => isPathInside(repositoryPath, root))
6372
7420
  ) ? "complete" : "partial";
6373
7421
  const bind = configExists && typeof configValue.host === "string" ? isLoopbackHost(configValue.host) ? "loopback" : "non-loopback" : "unknown";
6374
- const directoryMode = process.platform !== "win32" && existsSync27(configDirectory) ? modeString(statSync8(configDirectory).mode) : void 0;
6375
- const fileMode = process.platform !== "win32" && existsSync27(configPath) ? modeString(statSync8(configPath).mode) : void 0;
6376
- const authMode = process.platform !== "win32" && 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;
6377
7425
  const update = installedVersion && latestVersion ? installedVersion === latestVersion ? "current" : "available" : "unknown";
6378
7426
  const recommendations = [];
6379
7427
  let status = "healthy";
@@ -6387,7 +7435,7 @@ function inspectDevSpaceHealth(options) {
6387
7435
  };
6388
7436
  if (!installedResult.ok) unhealthy("\u672C\u673A\u672A\u53D1\u73B0 DevSpace\uFF1B\u65E0\u6CD5\u4F7F\u7528\u5BBF\u4E3B\u5DE5\u4F5C\u533A\u670D\u52A1\u3002");
6389
7437
  if (!configExists) unhealthy("\u7F3A\u5C11 ~/.devspace/config.json\u3002");
6390
- if (!existsSync27(authPath)) unhealthy("\u7F3A\u5C11 ~/.devspace/auth.json\u3002");
7438
+ if (!existsSync28(authPath)) unhealthy("\u7F3A\u5C11 ~/.devspace/auth.json\u3002");
6391
7439
  if (directoryMode && directoryMode !== "700")
6392
7440
  unhealthy(`~/.devspace \u76EE\u5F55\u6743\u9650\u4E3A ${directoryMode}\uFF0C\u5E94\u6536\u7D27\u4E3A 700\u3002`);
6393
7441
  if (fileMode && fileMode !== "600") unhealthy(`DevSpace \u914D\u7F6E\u6587\u4EF6\u6743\u9650\u4E3A ${fileMode}\uFF0C\u5E94\u4E3A 600\u3002`);
@@ -6420,7 +7468,7 @@ function inspectDevSpaceHealth(options) {
6420
7468
  exists: configExists,
6421
7469
  ...directoryMode ? { directoryMode } : {},
6422
7470
  ...fileMode ? { fileMode } : {},
6423
- authExists: existsSync27(authPath),
7471
+ authExists: existsSync28(authPath),
6424
7472
  ...authMode ? { authMode } : {},
6425
7473
  bind,
6426
7474
  portValid: configExists && typeof configValue.port === "number" && Number.isInteger(configValue.port) && configValue.port > 0 && configValue.port <= 65535,
@@ -6443,7 +7491,7 @@ function isLoopbackHost(host) {
6443
7491
  return ["127.0.0.1", "localhost", "::1"].includes(host.trim().toLowerCase());
6444
7492
  }
6445
7493
  function isPathInside(path, root) {
6446
- const fromRoot = relative10(resolve7(root), resolve7(path));
7494
+ const fromRoot = relative11(resolve8(root), resolve8(path));
6447
7495
  return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep2}`);
6448
7496
  }
6449
7497
 
@@ -6461,9 +7509,9 @@ var MCP_DISCOVERY_PATHS = {
6461
7509
  }
6462
7510
  };
6463
7511
  function inspectHostEnvironment(homeDir, grokVersion, repositoryPaths, devspaceSettings) {
6464
- const codexConfig = join28(homeDir, MCP_DISCOVERY_PATHS.user.codex);
6465
- const claudeConfig = join28(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode);
6466
- 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);
6467
7515
  const hostEnvironment = {
6468
7516
  mcp: {
6469
7517
  codexUser: { path: codexConfig, names: tomlMcpNames(codexConfig) },
@@ -6471,11 +7519,11 @@ function inspectHostEnvironment(homeDir, grokVersion, repositoryPaths, devspaceS
6471
7519
  grokUser: { path: grokConfig, names: tomlMcpNames(grokConfig) }
6472
7520
  },
6473
7521
  skills: {
6474
- codexUser: inspectSkillRoot(join28(homeDir, ".agents/skills")),
6475
- claudeCodeUser: inspectSkillRoot(join28(homeDir, ".claude/skills")),
6476
- grokUser: inspectSkillRoot(join28(homeDir, ".grok/skills")),
6477
- grokAgentsCompatibility: inspectSkillRoot(join28(homeDir, ".agents/skills")),
6478
- 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")),
6479
7527
  ssot: inspectUserSkillsSsot(homeDir)
6480
7528
  },
6481
7529
  grok: {
@@ -6496,20 +7544,20 @@ function inspectHostEnvironment(homeDir, grokVersion, repositoryPaths, devspaceS
6496
7544
  function inspectSkillRoot(path) {
6497
7545
  const exists = pathLexists(path) && safeIsDirectory(path);
6498
7546
  const names = exists ? safeReadDir(path).filter(
6499
- (name) => !name.startsWith(".") && existsSync28(join28(path, name, "SKILL.md"))
7547
+ (name) => !name.startsWith(".") && existsSync29(join29(path, name, "SKILL.md"))
6500
7548
  ) : [];
6501
7549
  return { path, exists, names };
6502
7550
  }
6503
7551
  function claudeProjectLocalMcpNames(homeDir, root) {
6504
7552
  if (!homeDir) return [];
6505
- const value = readJson4(join28(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode));
7553
+ const value = readJson4(join29(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode));
6506
7554
  if (!isRecord3(value) || !isRecord3(value.projects)) return [];
6507
7555
  const candidates = new Set(
6508
- [resolve8(root), safeRealpath(root)].filter((path) => Boolean(path))
7556
+ [resolve9(root), safeRealpath(root)].filter((path) => Boolean(path))
6509
7557
  );
6510
7558
  const names = /* @__PURE__ */ new Set();
6511
7559
  for (const [path, project] of Object.entries(value.projects)) {
6512
- const projectPaths = [resolve8(path), safeRealpath(path)].filter(
7560
+ const projectPaths = [resolve9(path), safeRealpath(path)].filter(
6513
7561
  (candidate) => Boolean(candidate)
6514
7562
  );
6515
7563
  if (!projectPaths.some((candidate) => candidates.has(candidate)) || !isRecord3(project) || !isRecord3(project.mcpServers))
@@ -6531,7 +7579,7 @@ function inspectGrokProject(root, homeDir, grokVersion) {
6531
7579
  const result = spawnPlatformSync("grok", ["inspect", "--json"], {
6532
7580
  cwd: root,
6533
7581
  encoding: "utf8",
6534
- env: { ...process.env, HOME: homeDir, GROK_HOME: join28(homeDir, ".grok") },
7582
+ env: { ...process.env, HOME: homeDir, GROK_HOME: join29(homeDir, ".grok") },
6535
7583
  maxBuffer: 10 * 1024 * 1024,
6536
7584
  stdio: ["ignore", "pipe", "ignore"],
6537
7585
  timeout: 8e3
@@ -6540,7 +7588,7 @@ function inspectGrokProject(root, homeDir, grokVersion) {
6540
7588
  const value = JSON.parse(result.stdout);
6541
7589
  if (!isRecord3(value)) return empty("failed");
6542
7590
  const userClaudeNames = new Set(
6543
- jsonObjectKeys(join28(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode), "mcpServers")
7591
+ jsonObjectKeys(join29(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode), "mcpServers")
6544
7592
  );
6545
7593
  const localClaudeNames = new Set(claudeProjectLocalMcpNames(homeDir, root));
6546
7594
  const effectiveMcp = Array.isArray(value.mcpServers) ? value.mcpServers.flatMap((item) => {
@@ -6608,26 +7656,26 @@ function inferGrokMcpScope(name, sourceType, sourcePath, root, homeDir, userClau
6608
7656
  if (userClaudeNames.has(name)) return "user";
6609
7657
  return "unknown";
6610
7658
  }
6611
- const resolvedSource = safeRealpath(sourcePath) ?? resolve8(sourcePath);
6612
- const resolvedRoot = safeRealpath(root) ?? resolve8(root);
6613
- 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))
6614
7662
  return "project-shared";
6615
7663
  if (resolvedSource.startsWith(resolvedRoot + sep3)) return "project";
6616
- if (homeDir && resolvedSource === join28(resolve8(homeDir), MCP_DISCOVERY_PATHS.user.claudeCode)) {
7664
+ if (homeDir && resolvedSource === join29(resolve9(homeDir), MCP_DISCOVERY_PATHS.user.claudeCode)) {
6617
7665
  if (localClaudeNames.has(name)) return "project-local";
6618
7666
  if (userClaudeNames.has(name)) return "user";
6619
7667
  }
6620
- if (homeDir && resolvedSource.startsWith(resolve8(homeDir) + sep3)) return "user";
7668
+ if (homeDir && resolvedSource.startsWith(resolve9(homeDir) + sep3)) return "user";
6621
7669
  if (sourceType === "project") return "project";
6622
7670
  return "unknown";
6623
7671
  }
6624
7672
 
6625
7673
  // src/portfolio/ai-health/secrets.ts
6626
- import { existsSync as existsSync29, lstatSync as lstatSync12, readdirSync as readdirSync12, statSync as statSync9 } from "node:fs";
6627
- 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";
6628
7676
  var supportsPosixModes = process.platform !== "win32";
6629
7677
  function inspectRepositorySecrets(root, id, secretsRoot, isRepository, environmentPolicy) {
6630
- const centralPath = join29(secretsRoot, id);
7678
+ const centralPath = join30(secretsRoot, id);
6631
7679
  const centralRealPath = safeRealpath(centralPath);
6632
7680
  const localOnlyReasons = new Map(
6633
7681
  (environmentPolicy?.localOnly ?? []).map((entry) => [entry.path, entry.reason])
@@ -6639,16 +7687,16 @@ function inspectRepositorySecrets(root, id, secretsRoot, isRepository, environme
6639
7687
  tracked: isRepository ? gitTracks(root, path) : false,
6640
7688
  template: isEnvironmentTemplate(path),
6641
7689
  fixture: isEnvironmentFixture(path),
6642
- symlink: lstatSync12(join29(root, path)).isSymbolicLink(),
6643
- centralized: pointsInside(join29(root, path), centralRealPath),
7690
+ symlink: lstatSync13(join30(root, path)).isSymbolicLink(),
7691
+ centralized: pointsInside(join30(root, path), centralRealPath),
6644
7692
  localOnly: localOnlyReason !== void 0,
6645
7693
  ...localOnlyReason !== void 0 ? { localOnlyReason } : {}
6646
7694
  };
6647
7695
  });
6648
7696
  return {
6649
- centralDirectory: existsSync29(centralPath) ? "present" : "absent",
6650
- centralMode: supportsPosixModes && existsSync29(centralPath) ? modeString(statSync9(centralPath).mode) : void 0,
6651
- 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) : [],
6652
7700
  repositoryEnvFiles: envFiles
6653
7701
  };
6654
7702
  }
@@ -6671,12 +7719,12 @@ function collectEnvironmentFiles(root, current = root, depth = 0) {
6671
7719
  if (depth > 5) return [];
6672
7720
  const found = [];
6673
7721
  try {
6674
- for (const entry of readdirSync12(current, { withFileTypes: true })) {
7722
+ for (const entry of readdirSync13(current, { withFileTypes: true })) {
6675
7723
  if (entry.isDirectory()) {
6676
7724
  if (!SKIP_ENV_DIRECTORIES.has(entry.name))
6677
- found.push(...collectEnvironmentFiles(root, join29(current, entry.name), depth + 1));
7725
+ found.push(...collectEnvironmentFiles(root, join30(current, entry.name), depth + 1));
6678
7726
  } else if (isEnvironmentFilename(entry.name) && !isProviderGeneratedEnvironmentFile(entry.name)) {
6679
- found.push(relative11(root, join29(current, entry.name)).replaceAll("\\", "/"));
7727
+ found.push(relative12(root, join30(current, entry.name)).replaceAll("\\", "/"));
6680
7728
  }
6681
7729
  }
6682
7730
  } catch {
@@ -6688,13 +7736,13 @@ function collectCentralSecretFiles(root, current = root, depth = 0) {
6688
7736
  if (depth > 3) return [];
6689
7737
  const found = [];
6690
7738
  try {
6691
- for (const entry of readdirSync12(current, { withFileTypes: true })) {
6692
- const path = join29(current, entry.name);
7739
+ for (const entry of readdirSync13(current, { withFileTypes: true })) {
7740
+ const path = join30(current, entry.name);
6693
7741
  if (entry.isDirectory()) found.push(...collectCentralSecretFiles(root, path, depth + 1));
6694
7742
  else
6695
7743
  found.push({
6696
- path: relative11(root, path).replaceAll("\\", "/"),
6697
- mode: supportsPosixModes ? modeString(lstatSync12(path).mode) : "unknown"
7744
+ path: relative12(root, path).replaceAll("\\", "/"),
7745
+ mode: supportsPosixModes ? modeString(lstatSync13(path).mode) : "unknown"
6698
7746
  });
6699
7747
  }
6700
7748
  } catch {
@@ -6719,10 +7767,10 @@ function isEnvironmentFixture(path) {
6719
7767
  return /(^|[\\/])(?:tests?|__tests__)[\\/]fixtures?[\\/]/i.test(path) || /(^|[\\/])__fixtures__[\\/]/i.test(path);
6720
7768
  }
6721
7769
  function pointsInside(path, expectedRoot) {
6722
- if (!expectedRoot || !lstatSync12(path).isSymbolicLink()) return false;
7770
+ if (!expectedRoot || !lstatSync13(path).isSymbolicLink()) return false;
6723
7771
  const target = safeRealpath(path);
6724
7772
  if (!target) return false;
6725
- const fromRoot = relative11(expectedRoot, target);
7773
+ const fromRoot = relative12(expectedRoot, target);
6726
7774
  return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep4}`);
6727
7775
  }
6728
7776
  function hasUnsafeCentralSecretPermissions(secrets) {
@@ -6730,24 +7778,24 @@ function hasUnsafeCentralSecretPermissions(secrets) {
6730
7778
  return secrets.centralDirectory === "present" && (secrets.centralMode !== "700" || secrets.centralFiles.some((file) => file.mode !== "600"));
6731
7779
  }
6732
7780
  function inspectSecretsRoot(path) {
6733
- return existsSync29(path) ? {
7781
+ return existsSync30(path) ? {
6734
7782
  path,
6735
7783
  exists: true,
6736
- ...supportsPosixModes ? { mode: modeString(statSync9(path).mode) } : {}
7784
+ ...supportsPosixModes ? { mode: modeString(statSync10(path).mode) } : {}
6737
7785
  } : { path, exists: false };
6738
7786
  }
6739
7787
 
6740
7788
  // src/portfolio/ai-health/skills.ts
6741
- import { existsSync as existsSync30, lstatSync as lstatSync13, realpathSync as realpathSync8 } from "node:fs";
6742
- 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";
6743
7791
  function countAutomaticSkillsNeedingReview(skills) {
6744
7792
  return skills.automatic.filter(
6745
7793
  (item) => !item.managed || !item.registryId || item.expectedPlacement !== "auto" || item.expectedScope === "user"
6746
7794
  ).length;
6747
7795
  }
6748
7796
  function inspectSkills(root, grokInspection, registeredSkills, userSkills) {
6749
- const lock = readJson4(join30(root, ".pro-gov/assets.lock.json"));
6750
- 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"));
6751
7799
  const managed = /* @__PURE__ */ new Set();
6752
7800
  const bundleIds = stringArray(isRecord3(lock) ? lock.bundleIds : void 0);
6753
7801
  if (isRecord3(lock) && Array.isArray(lock.assets)) {
@@ -6759,7 +7807,7 @@ function inspectSkills(root, grokInspection, registeredSkills, userSkills) {
6759
7807
  }
6760
7808
  const inspectPlacement = (placement) => {
6761
7809
  const directory = placement === "auto" ? "skills" : "manual-skills";
6762
- const skillRoot = join30(root, ".agents", directory);
7810
+ const skillRoot = join31(root, ".agents", directory);
6763
7811
  if (!pathLexists(skillRoot) || !safeIsDirectory(skillRoot)) return [];
6764
7812
  return safeReadDir(skillRoot).filter((name) => !name.startsWith(".")).map((name) => inspectSkillItem(skillRoot, directory, name, managed, registeredSkills));
6765
7813
  };
@@ -6814,15 +7862,15 @@ function inspectSkills(root, grokInspection, registeredSkills, userSkills) {
6814
7862
  },
6815
7863
  hosts: {
6816
7864
  codexProject: automatic.filter((item) => item.kind !== "dangling-symlink").length,
6817
- claudeCodeProject: inspectSkillRoot(join30(root, ".claude/skills")).names.length,
6818
- 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,
6819
7867
  grokEffective: grokInspection.skills
6820
7868
  }
6821
7869
  };
6822
7870
  }
6823
7871
  function inspectSkillItem(skillRoot, directory, name, managed, registeredSkills) {
6824
- const path = join30(skillRoot, name);
6825
- const stat = lstatSync13(path);
7872
+ const path = join31(skillRoot, name);
7873
+ const stat = lstatSync14(path);
6826
7874
  let kind = stat.isSymbolicLink() ? "symlink" : stat.isDirectory() ? "directory" : "file";
6827
7875
  let realPath;
6828
7876
  try {
@@ -6831,7 +7879,7 @@ function inspectSkillItem(skillRoot, directory, name, managed, registeredSkills)
6831
7879
  if (stat.isSymbolicLink()) kind = "dangling-symlink";
6832
7880
  }
6833
7881
  const registered = realPath ? registeredSkills.find((skill) => skill.sourceRealPath === realPath) : void 0;
6834
- 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;
6835
7883
  return {
6836
7884
  name,
6837
7885
  kind,
@@ -6843,11 +7891,11 @@ function inspectSkillItem(skillRoot, directory, name, managed, registeredSkills)
6843
7891
  };
6844
7892
  }
6845
7893
  function isPluginPack(path) {
6846
- const skillsRoot = join30(path, "skills");
6847
- 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")));
6848
7896
  }
6849
7897
  function inspectInvalidSkillEntries(root, directory) {
6850
- const skillRoot = join30(root, ".agents", directory);
7898
+ const skillRoot = join31(root, ".agents", directory);
6851
7899
  if (!pathLexists(skillRoot) || !safeIsDirectory(skillRoot)) return [];
6852
7900
  return safeReadDir(skillRoot).flatMap((name) => {
6853
7901
  if (name === ".gitkeep") return [];
@@ -6855,14 +7903,14 @@ function inspectInvalidSkillEntries(root, directory) {
6855
7903
  return [{ path: `.agents/${directory}/${name}`, reason: "metadata-junk" }];
6856
7904
  if (name.startsWith("."))
6857
7905
  return [{ path: `.agents/${directory}/${name}`, reason: "unexpected-file" }];
6858
- const path = join30(skillRoot, name);
6859
- 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" }] : [];
6860
7908
  });
6861
7909
  }
6862
7910
  function skillDuplicatesUser(item, root, userSkills) {
6863
7911
  if (userSkills.names.has(item.name)) return true;
6864
- const automatic = join30(root, ".agents/skills", item.name);
6865
- 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);
6866
7914
  const realPath = safeRealpath(pathLexists(automatic) ? automatic : manual);
6867
7915
  return realPath ? userSkills.realPaths.has(realPath) : false;
6868
7916
  }
@@ -6882,13 +7930,13 @@ function skillPlacementDrift(item, actualPlacement) {
6882
7930
  return [];
6883
7931
  }
6884
7932
  function inspectClaudeSkillRoot(root) {
6885
- const path = join30(root, ".claude/skills");
7933
+ const path = join31(root, ".claude/skills");
6886
7934
  if (!pathLexists(path)) return "missing";
6887
- const stat = lstatSync13(path);
7935
+ const stat = lstatSync14(path);
6888
7936
  if (stat.isSymbolicLink()) {
6889
7937
  try {
6890
7938
  const target = realpathSync8(path);
6891
- return target === realpathSync8(join30(root, ".agents/skills")) ? "shared-root" : "other";
7939
+ return target === realpathSync8(join31(root, ".agents/skills")) ? "shared-root" : "other";
6892
7940
  } catch {
6893
7941
  return "dangling-symlink";
6894
7942
  }
@@ -6901,27 +7949,27 @@ function inspectSkillRegistry(executionEngineRoot) {
6901
7949
  health: { source: 0, registered: 0, bundled: 0, bundles: 0 },
6902
7950
  skills: []
6903
7951
  };
6904
- const agentAssetsRoot = join30(executionEngineRoot, "agent-assets");
6905
- const registry = readJson4(join30(agentAssetsRoot, "registry.json"));
7952
+ const agentAssetsRoot = join31(executionEngineRoot, "agent-assets");
7953
+ const registry = readJson4(join31(agentAssetsRoot, "registry.json"));
6906
7954
  const assets = isRecord3(registry) && Array.isArray(registry.assets) ? registry.assets : [];
6907
7955
  const registeredSkills = assets.filter((asset) => isRecord3(asset) && asset.kind === "skill");
6908
- const bundleRoot = join30(agentAssetsRoot, "bundles");
7956
+ const bundleRoot = join31(agentAssetsRoot, "bundles");
6909
7957
  const bundleFiles = safeReadDir(bundleRoot).filter((file) => file.endsWith(".json"));
6910
7958
  const bundledIds = /* @__PURE__ */ new Set();
6911
7959
  for (const file of bundleFiles) {
6912
- const bundle = readJson4(join30(bundleRoot, file));
7960
+ const bundle = readJson4(join31(bundleRoot, file));
6913
7961
  if (!isRecord3(bundle) || !Array.isArray(bundle.assets)) continue;
6914
7962
  for (const id of bundle.assets) if (typeof id === "string") bundledIds.add(id);
6915
7963
  }
6916
7964
  const sourceRoots = [
6917
- join30(agentAssetsRoot, "skills/pie-skills"),
6918
- join30(agentAssetsRoot, "skills/npx-skills/.agents/skills")
7965
+ join31(agentAssetsRoot, "skills/pie-skills"),
7966
+ join31(agentAssetsRoot, "skills/npx-skills/.agents/skills")
6919
7967
  ];
6920
7968
  const source = sourceRoots.reduce(
6921
- (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,
6922
7970
  0
6923
7971
  ) + registeredSkills.filter(
6924
- (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))
6925
7973
  ).length;
6926
7974
  return {
6927
7975
  health: {
@@ -6938,7 +7986,7 @@ function inspectSkillRegistry(executionEngineRoot) {
6938
7986
  return [
6939
7987
  {
6940
7988
  id: asset.id,
6941
- sourceRealPath: safeRealpath(join30(agentAssetsRoot, asset.sourcePath)),
7989
+ sourceRealPath: safeRealpath(join31(agentAssetsRoot, asset.sourcePath)),
6942
7990
  defaultPlacement: asset.defaultPlacement,
6943
7991
  defaultScope: asset.defaultScope === "user" ? "user" : "project"
6944
7992
  }
@@ -6953,15 +8001,15 @@ function inspectUserSkillEvidence(root) {
6953
8001
  for (const name of safeReadDir(root)) {
6954
8002
  if (name.startsWith(".")) continue;
6955
8003
  names.add(name);
6956
- const realPath = safeRealpath(join30(root, name));
8004
+ const realPath = safeRealpath(join31(root, name));
6957
8005
  if (realPath) realPaths.add(realPath);
6958
8006
  }
6959
8007
  return { names, realPaths };
6960
8008
  }
6961
8009
 
6962
8010
  // src/portfolio/ai-health/technology.ts
6963
- import { existsSync as existsSync31, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
6964
- 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";
6965
8013
  function buildTechnologyMatrix(governance, repositories) {
6966
8014
  if (!governance || governance.technologies.length === 0) return [];
6967
8015
  const policy = governance.versionPolicy;
@@ -6988,8 +8036,8 @@ function buildTechnologyMatrix(governance, repositories) {
6988
8036
  }).filter((item) => item !== void 0);
6989
8037
  }).flat();
6990
8038
  const fileSignal = (technology.files ?? []).some(
6991
- (path) => hasUsableTechnologyFile(join31(repository.path, path)) || packageManifests.some(
6992
- (manifest) => hasUsableTechnologyFile(join31(manifest.directory, path))
8039
+ (path) => hasUsableTechnologyFile(join32(repository.path, path)) || packageManifests.some(
8040
+ (manifest) => hasUsableTechnologyFile(join32(manifest.directory, path))
6993
8041
  )
6994
8042
  );
6995
8043
  const modelSignal = [
@@ -7080,14 +8128,14 @@ function buildTechnologyMatrix(governance, repositories) {
7080
8128
  }).filter((technology) => technology.projectCount > 0);
7081
8129
  }
7082
8130
  function hasUsableTechnologyFile(path) {
7083
- if (!existsSync31(path)) return false;
8131
+ if (!existsSync32(path)) return false;
7084
8132
  try {
7085
- const info = statSync10(path);
8133
+ const info = statSync11(path);
7086
8134
  if (info.isFile()) return true;
7087
8135
  if (!info.isDirectory()) return false;
7088
- return readdirSync13(path, { withFileTypes: true }).some((entry) => {
8136
+ return readdirSync14(path, { withFileTypes: true }).some((entry) => {
7089
8137
  if (entry.name.startsWith(".")) return false;
7090
- const child = join31(path, entry.name);
8138
+ const child = join32(path, entry.name);
7091
8139
  if (entry.isDirectory()) return hasUsableTechnologyFile(child);
7092
8140
  return entry.name.toLowerCase() !== "readme.md";
7093
8141
  });
@@ -7099,7 +8147,7 @@ function inspectExclusiveOwnership(root, endpoint, governance) {
7099
8147
  const projectType = endpoint.projectType;
7100
8148
  return (governance?.exclusiveOwnership ?? []).flatMap((rule) => {
7101
8149
  if (projectType && rule.allowedProjectTypes.includes(projectType)) return [];
7102
- const paths = rule.paths.filter((path) => existsSync31(join31(root, path)));
8150
+ const paths = rule.paths.filter((path) => existsSync32(join32(root, path)));
7103
8151
  return paths.length > 0 ? [{ rule, paths }] : [];
7104
8152
  });
7105
8153
  }
@@ -7114,7 +8162,7 @@ function inspectProjectModel(root, endpoint, governance) {
7114
8162
  const detection = (id) => {
7115
8163
  const technology = technologyById.get(id);
7116
8164
  const packageMatch = technology?.packages?.some((name) => packages.has(name)) ?? false;
7117
- const fileMatch = technology?.files?.some((path) => existsSync31(join31(root, path))) ?? false;
8165
+ const fileMatch = technology?.files?.some((path) => existsSync32(join32(root, path))) ?? false;
7118
8166
  return { id, label: technology?.label ?? id, detected: packageMatch || fileMatch };
7119
8167
  };
7120
8168
  const selected = new Set(endpoint.capabilities ?? []);
@@ -7153,8 +8201,8 @@ function collectPackageNames(root) {
7153
8201
  }
7154
8202
 
7155
8203
  // src/portfolio/ai-health/report.ts
7156
- import { cpSync as cpSync3, existsSync as existsSync32, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "node:fs";
7157
- 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";
7158
8206
  import { fileURLToPath as fileURLToPath5 } from "node:url";
7159
8207
  function mergePortfolioAiHealthReport(existing, latest, allRepositoryIds) {
7160
8208
  const repositoriesById = /* @__PURE__ */ new Map();
@@ -7195,36 +8243,36 @@ function mergePortfolioAiHealthReport(existing, latest, allRepositoryIds) {
7195
8243
  };
7196
8244
  }
7197
8245
  function writePortfolioAiHealthReport(report, outDir) {
7198
- mkdirSync10(outDir, { recursive: true });
8246
+ mkdirSync11(outDir, { recursive: true });
7199
8247
  const dashboardAssets = findDashboardAssets();
7200
8248
  for (const file of ["index.html", "app.js", "app.css"]) {
7201
- const source = join32(dashboardAssets, file);
7202
- if (!existsSync32(source)) throw new Error(`Portfolio dashboard asset is missing: ${source}`);
7203
- 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));
7204
8252
  }
7205
- const jsonPath = join32(outDir, "portfolio-ai-health.json");
7206
- const htmlPath = join32(outDir, "index.html");
7207
- 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)}
7208
8256
  `);
7209
- writeFileSync9(
7210
- join32(outDir, "data.js"),
8257
+ writeFileSync10(
8258
+ join33(outDir, "data.js"),
7211
8259
  `window.__PORTFOLIO_AI_HEALTH__ = ${safeJavaScriptJson2(report)};
7212
8260
  `
7213
8261
  );
7214
8262
  return { jsonPath, htmlPath };
7215
8263
  }
7216
8264
  function findDashboardAssets() {
7217
- const packageRoot2 = dirname16(dirname16(fileURLToPath5(import.meta.url)));
8265
+ const packageRoot2 = dirname17(dirname17(fileURLToPath5(import.meta.url)));
7218
8266
  const candidates = [
7219
8267
  process.env.PGS_DASHBOARD_ASSETS_DIR,
7220
- join32(packageRoot2, ".dashboard-build"),
7221
- join32(packageRoot2, "assets/portfolio-dashboard"),
7222
- join32(process.cwd(), ".dashboard-build"),
7223
- join32(process.cwd(), "assets/portfolio-dashboard"),
7224
- join32(process.cwd(), "packages/pro-gov/.dashboard-build"),
7225
- 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")
7226
8274
  ].filter((value) => Boolean(value));
7227
- const match = candidates.find((path) => existsSync32(join32(path, "index.html")));
8275
+ const match = candidates.find((path) => existsSync33(join33(path, "index.html")));
7228
8276
  if (!match)
7229
8277
  throw new Error(
7230
8278
  "Portfolio dashboard assets were not built. Run pnpm --filter @pieai/pro-gov build."
@@ -7242,8 +8290,8 @@ function inspectPortfolioAiHealth(options) {
7242
8290
  if (options.targetId && options.targetId !== "all" && endpoints.length === 0) {
7243
8291
  throw new Error(`Unknown portfolio target: ${options.targetId}`);
7244
8292
  }
7245
- const secretsRoot = options.secretsRoot ?? join33(
7246
- dirname17(
8293
+ const secretsRoot = options.secretsRoot ?? join34(
8294
+ dirname18(
7247
8295
  options.manifest.controlPlane?.path ?? allEndpoints[0]?.endpoint.path ?? process.cwd()
7248
8296
  ),
7249
8297
  ".secrets"
@@ -7252,9 +8300,9 @@ function inspectPortfolioAiHealth(options) {
7252
8300
  const grokVersion = commandVersion("grok");
7253
8301
  const executionEngineRoot = options.manifest.executionEngine?.path;
7254
8302
  const skillRegistry = inspectSkillRegistry(executionEngineRoot);
7255
- const userSkills = inspectUserSkillEvidence(join33(homeDir, ".agents/skills"));
8303
+ const userSkills = inspectUserSkillEvidence(join34(homeDir, ".agents/skills"));
7256
8304
  const expectedPackageVersion = packageVersion(
7257
- join33(executionEngineRoot ?? "", "packages/pro-gov/package.json")
8305
+ join34(executionEngineRoot ?? "", "packages/pro-gov/package.json")
7258
8306
  );
7259
8307
  const repositories = endpoints.map(
7260
8308
  ({ endpoint, role }) => inspectRepository(
@@ -7311,7 +8359,7 @@ function collectEndpoints(manifest) {
7311
8359
  for (const target of manifest.targets) result.push({ endpoint: target, role: "target" });
7312
8360
  const seen = /* @__PURE__ */ new Set();
7313
8361
  return result.filter(({ endpoint }) => {
7314
- const key = resolve9(endpoint.path);
8362
+ const key = resolve10(endpoint.path);
7315
8363
  if (seen.has(key)) return false;
7316
8364
  seen.add(key);
7317
8365
  return true;
@@ -7328,13 +8376,13 @@ function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackage
7328
8376
  const hooks = inspectHooks(root);
7329
8377
  const docs = inspectDocs(root, role === "execution-engine" ? void 0 : expectedPackageVersion);
7330
8378
  const mcp = {
7331
- codexProject: tomlMcpNames(join33(root, MCP_DISCOVERY_PATHS.project.codex)),
8379
+ codexProject: tomlMcpNames(join34(root, MCP_DISCOVERY_PATHS.project.codex)),
7332
8380
  claudeCodeProjectShared: jsonObjectKeys(
7333
- join33(root, MCP_DISCOVERY_PATHS.project.claudeCodeShared),
8381
+ join34(root, MCP_DISCOVERY_PATHS.project.claudeCodeShared),
7334
8382
  "mcpServers"
7335
8383
  ),
7336
8384
  claudeCodeProjectLocal: claudeProjectLocalMcpNames(homeDir, root),
7337
- grokProject: tomlMcpNames(join33(root, MCP_DISCOVERY_PATHS.project.grok)),
8385
+ grokProject: tomlMcpNames(join34(root, MCP_DISCOVERY_PATHS.project.grok)),
7338
8386
  grokEffective: grokInspection.effectiveMcp,
7339
8387
  grokInspection: grokInspection.inspection
7340
8388
  };
@@ -7968,11 +9016,11 @@ function isHost2(value) {
7968
9016
  return value === "codex" || value === "claude-code" || value === "gemini-cli" || value === "antigravity";
7969
9017
  }
7970
9018
  function findPortfolioAgentAssetsDir(manifest) {
7971
- const agentAssetsDir = manifest?.executionEngine?.path ? join34(manifest.executionEngine.path, "agent-assets") : void 0;
7972
- 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;
7973
9021
  }
7974
9022
  function reportMissingPortfolioRegistry(loaded, json) {
7975
- 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";
7976
9024
  const displayExpectedPath = expectedPath.replaceAll("\\", "/");
7977
9025
  const issue = {
7978
9026
  type: "missing-control-plane-registry",
@@ -8011,10 +9059,10 @@ function printUsage5() {
8011
9059
  }
8012
9060
  function readExistingAiHealthReport(outDir, portfolioId) {
8013
9061
  if (!outDir) return void 0;
8014
- const path = join34(outDir, "portfolio-ai-health.json");
8015
- if (!existsSync33(path)) return void 0;
9062
+ const path = join35(outDir, "portfolio-ai-health.json");
9063
+ if (!existsSync34(path)) return void 0;
8016
9064
  try {
8017
- const value = JSON.parse(readFileSync18(path, "utf8"));
9065
+ const value = JSON.parse(readFileSync19(path, "utf8"));
8018
9066
  if (!value || typeof value !== "object" || value.portfolioId !== portfolioId || !Array.isArray(value.repositories))
8019
9067
  return void 0;
8020
9068
  return value;
@@ -8024,8 +9072,8 @@ function readExistingAiHealthReport(outDir, portfolioId) {
8024
9072
  }
8025
9073
 
8026
9074
  // src/commands/sync.ts
8027
- import { existsSync as existsSync34, lstatSync as lstatSync14, readFileSync as readFileSync19, readlinkSync as readlinkSync4 } from "node:fs";
8028
- 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";
8029
9077
  function runSync(args) {
8030
9078
  const check = args.includes("--check");
8031
9079
  if (!check) {
@@ -8053,7 +9101,7 @@ function runSync(args) {
8053
9101
  console.log("pro-gov sync check");
8054
9102
  console.log(`profile: ${profile}`);
8055
9103
  for (const file of planStarterFiles(profile)) {
8056
- const targetPath = join35(process.cwd(), file.targetPath);
9104
+ const targetPath = join36(process.cwd(), file.targetPath);
8057
9105
  const stat = safeLstat3(targetPath);
8058
9106
  if (!stat) {
8059
9107
  if (file.ownership === "optional-guardrail") continue;
@@ -8077,8 +9125,8 @@ function runSync(args) {
8077
9125
  }
8078
9126
  continue;
8079
9127
  }
8080
- const source = readFileSync19(file.absoluteSourcePath, "utf8");
8081
- const target = readFileSync19(targetPath, "utf8");
9128
+ const source = readFileSync20(file.absoluteSourcePath, "utf8");
9129
+ const target = readFileSync20(targetPath, "utf8");
8082
9130
  if (!matchesExpectedContent(file.targetPath, source, target)) {
8083
9131
  console.log(`different: ${file.targetPath}`);
8084
9132
  differences += 1;
@@ -8112,13 +9160,13 @@ function normalizeMarkdownTableCell(cell) {
8112
9160
  }
8113
9161
  function inferInstalledProfile(root) {
8114
9162
  const installed = ["engineering-runtime", "doc-only"].filter(
8115
- (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`))
8116
9164
  );
8117
9165
  return installed.length === 1 ? installed[0] : void 0;
8118
9166
  }
8119
9167
  function safeLstat3(path) {
8120
9168
  try {
8121
- return lstatSync14(path);
9169
+ return lstatSync15(path);
8122
9170
  } catch {
8123
9171
  return void 0;
8124
9172
  }
@@ -8139,6 +9187,7 @@ var COMMANDS = [
8139
9187
  "assets check [--target <path>] [--strict-registry] [--json]",
8140
9188
  "assets public-check [--public-root <path>] [--private-root <path>] [--json]",
8141
9189
  "assets npx add|update ... --plan",
9190
+ "assets catalog build|check [--native-links] [--json]",
8142
9191
  "portfolio check --config <path> [--json]",
8143
9192
  "portfolio plan --config <path> [--target <id|all>] [--json]",
8144
9193
  "portfolio assets-check --config <path> [--target <id|all>] [--json]",