@pieai/pro-gov 0.9.3 → 0.9.5

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 +1589 -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,977 @@ 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 < b ? -1 : a > b ? 1 : 0)) {
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(
629
+ (left, right) => compareStableStrings(left.relativePath, right.relativePath)
630
+ );
631
+ for (let index = 1; index < sorted.length; index += 1) {
632
+ if (sorted[index - 1].relativePath === sorted[index].relativePath) {
633
+ throw new Error(`Duplicate native catalog link path: ${sorted[index].relativePath}`);
634
+ }
635
+ }
636
+ return sorted;
637
+ }
638
+ function writePortableCatalog(catalogRoot, files) {
639
+ mkdirSync(catalogRoot, { recursive: true });
640
+ const warnings = [];
641
+ const previous = readPortableManifest(catalogRoot);
642
+ const previousFiles = new Set(previous?.portableFiles ?? []);
643
+ const expectedFiles = [...files.keys()].sort();
644
+ for (const relativePath of previousFiles) {
645
+ assertManagedPortablePath(relativePath);
646
+ const targetPath = resolveCatalogPath(catalogRoot, relativePath);
647
+ if (pathEntryExists(targetPath)) {
648
+ const stats = lstatSync(targetPath);
649
+ if (!stats.isFile() || stats.isSymbolicLink()) {
650
+ throw new Error(
651
+ `Refusing to replace a managed file that is no longer regular: ${relativePath}`
652
+ );
653
+ }
654
+ }
655
+ }
656
+ for (const relativePath of expectedFiles) {
657
+ assertManagedPortablePath(relativePath);
658
+ assertCatalogPathParents(catalogRoot, relativePath);
659
+ const targetPath = resolveCatalogPath(catalogRoot, relativePath);
660
+ if (pathEntryExists(targetPath) && !previousFiles.has(relativePath)) {
661
+ throw new Error(`Refusing to replace unmanaged catalog path: ${relativePath}`);
662
+ }
663
+ }
664
+ const legacyDirectories = findLegacyManagedDirectories(catalogRoot);
665
+ const stageRoot = mkdtempSync(join3(dirname2(catalogRoot), ".asset-catalog-stage-"));
666
+ const backupRoot = mkdtempSync(join3(dirname2(catalogRoot), ".asset-catalog-backup-"));
667
+ const installed = [];
668
+ const backedUp = [];
669
+ const legacyBackedUp = [];
670
+ try {
671
+ for (const [relativePath, content] of files) {
672
+ const stagedPath = resolveCatalogPath(stageRoot, relativePath);
673
+ mkdirSync(dirname2(stagedPath), { recursive: true });
674
+ writeFileSync(stagedPath, content);
675
+ }
676
+ writeFileSync(join3(stageRoot, portableManifestName), renderPortableManifest(files));
677
+ for (const relativePath of [...previousFiles, portableManifestName]) {
678
+ const sourcePath = resolveCatalogPath(catalogRoot, relativePath);
679
+ if (!pathEntryExists(sourcePath)) continue;
680
+ const backupPath = resolveCatalogPath(backupRoot, `portable/${relativePath}`);
681
+ mkdirSync(dirname2(backupPath), { recursive: true });
682
+ renameSync(sourcePath, backupPath);
683
+ backedUp.push(relativePath);
684
+ }
685
+ for (const name of legacyDirectories) {
686
+ const sourcePath = join3(catalogRoot, name);
687
+ const backupPath = resolveCatalogPath(backupRoot, `legacy/${name}`);
688
+ mkdirSync(dirname2(backupPath), { recursive: true });
689
+ renameSync(sourcePath, backupPath);
690
+ legacyBackedUp.push(name);
691
+ }
692
+ for (const relativePath of [...expectedFiles, portableManifestName]) {
693
+ const sourcePath = resolveCatalogPath(stageRoot, relativePath);
694
+ const targetPath = resolveCatalogPath(catalogRoot, relativePath);
695
+ mkdirSync(dirname2(targetPath), { recursive: true });
696
+ renameSync(sourcePath, targetPath);
697
+ installed.push(relativePath);
698
+ }
699
+ for (const relativePath of previousFiles) {
700
+ pruneEmptyCatalogParents(catalogRoot, dirname2(relativePath));
701
+ }
702
+ } catch (error) {
703
+ for (const relativePath of installed.reverse()) {
704
+ const targetPath = resolveCatalogPath(catalogRoot, relativePath);
705
+ if (pathEntryExists(targetPath)) rmSync(targetPath, { recursive: true, force: true });
706
+ }
707
+ for (const relativePath of backedUp.reverse()) {
708
+ const backupPath = resolveCatalogPath(backupRoot, `portable/${relativePath}`);
709
+ const targetPath = resolveCatalogPath(catalogRoot, relativePath);
710
+ if (!pathEntryExists(backupPath)) continue;
711
+ mkdirSync(dirname2(targetPath), { recursive: true });
712
+ renameSync(backupPath, targetPath);
713
+ }
714
+ for (const name of legacyBackedUp.reverse()) {
715
+ const backupPath = resolveCatalogPath(backupRoot, `legacy/${name}`);
716
+ const targetPath = join3(catalogRoot, name);
717
+ if (pathEntryExists(backupPath)) renameSync(backupPath, targetPath);
718
+ }
719
+ throw error;
720
+ } finally {
721
+ removeTemporaryTree(stageRoot, warnings);
722
+ removeTemporaryTree(backupRoot, warnings);
723
+ }
724
+ return warnings;
725
+ }
726
+ function renderPortableManifest(files) {
727
+ const manifest = {
728
+ schemaVersion: 1,
729
+ generator: manifestGenerator,
730
+ portableFiles: [...files.keys()].sort()
731
+ };
732
+ return `${JSON.stringify(manifest, null, 2)}
733
+ `;
734
+ }
735
+ function renderLocalLinksManifest(links) {
736
+ const manifest = {
737
+ schemaVersion: 1,
738
+ generator: manifestGenerator,
739
+ links: [...links].sort()
740
+ };
741
+ return `${JSON.stringify(manifest, null, 2)}
742
+ `;
743
+ }
744
+ function readPortableManifest(catalogRoot) {
745
+ const path = join3(catalogRoot, portableManifestName);
746
+ if (!existsSync3(path)) return void 0;
747
+ const value = readManifest(path, "portableFiles");
748
+ for (const relativePath of value.items) assertManagedPortablePath(relativePath);
749
+ return {
750
+ schemaVersion: 1,
751
+ generator: manifestGenerator,
752
+ portableFiles: value.items
753
+ };
754
+ }
755
+ function readLocalLinksManifest(catalogRoot) {
756
+ const path = join3(catalogRoot, localLinksManifestName);
757
+ if (!existsSync3(path)) return void 0;
758
+ const value = readManifest(path, "links");
759
+ for (const relativePath of value.items) assertManagedLocalLinkPath(relativePath);
760
+ return { schemaVersion: 1, generator: manifestGenerator, links: value.items };
761
+ }
762
+ function readManifest(path, listKey) {
763
+ const stats = lstatSync(path);
764
+ if (!stats.isFile() || stats.isSymbolicLink()) {
765
+ throw new Error(`Invalid asset catalog manifest file: ${path}`);
766
+ }
767
+ let parsed;
768
+ try {
769
+ parsed = JSON.parse(readFileSync2(path, "utf8"));
770
+ } catch {
771
+ throw new Error(`Invalid asset catalog manifest JSON: ${path}`);
772
+ }
773
+ const items = parsed[listKey];
774
+ if (parsed.schemaVersion !== 1 || parsed.generator !== manifestGenerator || !Array.isArray(items) || items.some((item) => typeof item !== "string") || new Set(items).size !== items.length) {
775
+ throw new Error(`Invalid asset catalog manifest shape: ${path}`);
776
+ }
777
+ return { items: [...items].sort() };
778
+ }
779
+ function findLegacyManagedDirectories(catalogRoot) {
780
+ const result = [];
781
+ for (const name of legacyManagedDirectoryNames) {
782
+ const root = join3(catalogRoot, name);
783
+ if (!existsSync3(root)) continue;
784
+ const stats = lstatSync(root);
785
+ if (!stats.isDirectory() || stats.isSymbolicLink()) {
786
+ throw new Error(`Refusing to migrate non-directory legacy catalog output: ${root}`);
787
+ }
788
+ const sentinel = join3(root, legacyManagedSentinel);
789
+ if (!existsSync3(sentinel) || !lstatSync(sentinel).isFile()) {
790
+ throw new Error(`Refusing to migrate unmanaged legacy catalog directory: ${root}`);
791
+ }
792
+ result.push(name);
793
+ }
794
+ return result;
795
+ }
796
+ function assertManagedPortablePath(relativePath) {
797
+ if (relativePath !== "INDEX.md" && relativePath !== "index.html" && relativePath !== "catalog.json" && !/^by-topic\/[a-z0-9]+(?:-[a-z0-9]+)*\/README\.md$/u.test(relativePath)) {
798
+ throw new Error(`Unsafe managed portable catalog path: ${relativePath}`);
799
+ }
800
+ }
801
+ function assertManagedLocalLinkPath(relativePath) {
802
+ if (!/^by-topic\/[a-z0-9]+(?:-[a-z0-9]+)*\/[a-z0-9][a-z0-9._-]*\.asset-link$/u.test(relativePath)) {
803
+ throw new Error(`Unsafe managed local catalog link path: ${relativePath}`);
804
+ }
805
+ }
806
+ function resolveCatalogPath(root, relativePath) {
807
+ const absoluteRoot = resolve(root);
808
+ const absolutePath = resolve(absoluteRoot, ...normalizeRelativePath(relativePath).split("/"));
809
+ const back = normalizeRelativePath(relative2(absoluteRoot, absolutePath));
810
+ if (back === ".." || back.startsWith("../") || isAbsolute(back)) {
811
+ throw new Error(`Catalog path escapes its managed root: ${relativePath}`);
812
+ }
813
+ return absolutePath;
814
+ }
815
+ function assertCatalogPathParents(catalogRoot, relativePath) {
816
+ const absoluteRoot = resolve(catalogRoot);
817
+ let current = dirname2(resolveCatalogPath(catalogRoot, relativePath));
818
+ while (current !== absoluteRoot) {
819
+ if (existsSync3(current)) {
820
+ const stats = lstatSync(current);
821
+ if (!stats.isDirectory() || stats.isSymbolicLink()) {
822
+ throw new Error(
823
+ `Refusing to write through a non-directory catalog parent: ${normalizeRelativePath(relative2(absoluteRoot, current))}`
824
+ );
825
+ }
826
+ }
827
+ const parent = dirname2(current);
828
+ if (parent === current) throw new Error(`Catalog parent escapes its root: ${relativePath}`);
829
+ current = parent;
830
+ }
831
+ }
832
+ function pruneEmptyCatalogParents(catalogRoot, relativeDirectory) {
833
+ const absoluteRoot = resolve(catalogRoot);
834
+ let current = resolveCatalogPath(catalogRoot, relativeDirectory);
835
+ while (current !== absoluteRoot && existsSync3(current)) {
836
+ const stats = lstatSync(current);
837
+ if (!stats.isDirectory() || stats.isSymbolicLink() || readdirSync3(current).length > 0) return;
838
+ rmSync(current, { recursive: true, force: true });
839
+ current = dirname2(current);
840
+ }
841
+ }
842
+ function pathEntryExists(path) {
843
+ try {
844
+ lstatSync(path);
845
+ return true;
846
+ } catch (error) {
847
+ if (error.code === "ENOENT") return false;
848
+ throw error;
849
+ }
850
+ }
851
+ function removeTemporaryTree(path, warnings) {
852
+ try {
853
+ rmSync(path, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
854
+ } catch (error) {
855
+ const reason = error instanceof Error ? error.message : String(error);
856
+ warnings.push(`temporary catalog cleanup pending (${path}): ${reason}`);
857
+ }
858
+ }
859
+ function findUncoveredSourcePaths(agentAssetsDir, entries, ignoredSourcePaths) {
860
+ const candidates = /* @__PURE__ */ new Set();
861
+ for (const filePath of listFiles2(join3(agentAssetsDir, "skills"))) {
862
+ if (basename(filePath) === "SKILL.md") {
863
+ candidates.add(normalizeRelativePath(relative2(agentAssetsDir, dirname2(filePath))));
864
+ }
865
+ }
866
+ for (const root of ["rules", "commands", "prompts"]) {
867
+ for (const filePath of listFiles2(join3(agentAssetsDir, root))) {
868
+ if (extname(filePath).toLowerCase() === ".md") {
869
+ candidates.add(normalizeRelativePath(relative2(agentAssetsDir, filePath)));
870
+ }
871
+ }
872
+ }
873
+ for (const filePath of listFiles2(join3(agentAssetsDir, "tooling"))) {
874
+ candidates.add(normalizeRelativePath(relative2(agentAssetsDir, filePath)));
875
+ }
876
+ return [...candidates].filter((candidate) => !isCoveredByAnySource(candidate, entries, ignoredSourcePaths)).sort();
877
+ }
878
+ function isCoveredByAnySource(candidate, entries, ignoredSourcePaths) {
879
+ const covers = (sourcePath) => candidate === sourcePath || candidate.startsWith(`${sourcePath}/`);
880
+ return entries.some((entry) => covers(entry.sourcePath)) || ignoredSourcePaths.some(covers);
881
+ }
882
+ function listFiles2(root) {
883
+ if (!existsSync3(root)) return [];
884
+ const stats = lstatSync(root);
885
+ if (stats.isSymbolicLink()) return [];
886
+ if (stats.isFile()) return [root];
887
+ const files = [];
888
+ for (const entry of readdirSync3(root, { withFileTypes: true })) {
889
+ if (ignoredTraversalNames.has(entry.name) || entry.name.startsWith("._")) continue;
890
+ const path = join3(root, entry.name);
891
+ if (entry.isDirectory()) files.push(...listFiles2(path));
892
+ else if (entry.isFile()) files.push(path);
893
+ }
894
+ return files.sort();
895
+ }
896
+ function readAssetDescription(sourceAbsolutePath, sourceType, fallback) {
897
+ const documentPath = sourceType === "directory" ? join3(sourceAbsolutePath, "SKILL.md") : sourceAbsolutePath;
898
+ if (!existsSync3(documentPath)) return fallback;
899
+ const text = readFileSync2(documentPath, "utf8");
900
+ const frontmatter = text.match(/^---\r?\n([\s\S]*?)\r?\n---/u)?.[1] ?? "";
901
+ const description = extractFrontmatterDescription(frontmatter);
902
+ if (description) return compactDescription(description, fallback);
903
+ const withoutFrontmatter = text.replace(/^---\r?\n[\s\S]*?\r?\n---\s*/u, "");
904
+ const paragraph = withoutFrontmatter.split(/\r?\n\r?\n/u).map((part) => part.replace(/\r?\n/g, " ").trim()).find((part) => part && !part.startsWith("#") && !part.startsWith("```"));
905
+ return compactDescription(paragraph ?? fallback, fallback);
906
+ }
907
+ function extractFrontmatterDescription(frontmatter) {
908
+ const lines = frontmatter.split(/\r?\n/u);
909
+ const index = lines.findIndex((line) => /^description\s*:/u.test(line));
910
+ if (index < 0) return void 0;
911
+ const inlineValue = lines[index].replace(/^description\s*:\s*/u, "").trim();
912
+ if (!/^[|>][+-]?$/u.test(inlineValue)) {
913
+ return inlineValue ? stripWrappingQuotes(inlineValue) : void 0;
914
+ }
915
+ const block = [];
916
+ for (const line of lines.slice(index + 1)) {
917
+ if (line.trim() && !/^\s/u.test(line)) break;
918
+ block.push(line.replace(/^\s+/u, "").trim());
919
+ }
920
+ const separator = inlineValue.startsWith("|") ? "\n" : " ";
921
+ const value = block.join(separator).trim();
922
+ return value || void 0;
923
+ }
924
+ function compactDescription(value, fallback) {
925
+ const compact = value.replace(/\[([^\]]+)]\([^)]*\)/g, "$1").replace(/[`*_>#]/g, "").replace(/\s+/g, " ").trim();
926
+ if (!compact) return fallback;
927
+ return compact.length > 220 ? `${compact.slice(0, 217)}...` : compact;
928
+ }
929
+ function stripWrappingQuotes(value) {
930
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
931
+ return value.slice(1, -1);
932
+ }
933
+ return value;
934
+ }
935
+ function resolveSafeSourcePath(agentAssetsDir, sourcePath, id) {
936
+ if (!isSafeSourcePath(sourcePath)) throw new Error(`Unsafe sourcePath for ${id}: ${sourcePath}`);
937
+ const absolutePath = resolve(agentAssetsDir, ...normalizeRelativePath(sourcePath).split("/"));
938
+ const relativePath = normalizeRelativePath(relative2(resolve(agentAssetsDir), absolutePath));
939
+ if (relativePath === ".." || relativePath.startsWith("../") || isAbsolute(relativePath)) {
940
+ throw new Error(`Source path escapes agent-assets for ${id}: ${sourcePath}`);
941
+ }
942
+ if (!existsSync3(absolutePath)) throw new Error(`Missing catalog source for ${id}: ${sourcePath}`);
943
+ return absolutePath;
944
+ }
945
+ function isSafeSourcePath(sourcePath) {
946
+ if (!sourcePath || isAbsolute(sourcePath) || sourcePath.includes("\\")) return false;
947
+ const normalized = normalizeRelativePath(sourcePath);
948
+ return normalized !== "." && normalized !== ".." && !normalized.startsWith("../");
949
+ }
950
+ function createBundleMembership(bundles) {
951
+ const membership = /* @__PURE__ */ new Map();
952
+ for (const bundle of bundles) {
953
+ for (const assetId of bundle.assets) {
954
+ membership.set(assetId, [...membership.get(assetId) ?? [], bundle.id]);
955
+ }
956
+ }
957
+ for (const [id, bundleIds] of membership) membership.set(id, uniqueSorted(bundleIds));
958
+ return membership;
959
+ }
960
+ function assertUniqueEntries(entries) {
961
+ const ids = /* @__PURE__ */ new Set();
962
+ for (const entry of entries) {
963
+ if (ids.has(entry.id)) throw new Error(`Duplicate catalog entry id: ${entry.id}`);
964
+ ids.add(entry.id);
965
+ }
966
+ }
967
+ function assertKnownTopics(id, topics, topicIds) {
968
+ for (const topic of topics) {
969
+ if (!topicIds.has(topic)) throw new Error(`Unknown topic ${topic} on catalog entry ${id}`);
970
+ }
971
+ }
972
+ function nativeLinkName(entry) {
973
+ const base = safePathSegment(entry.id.replace("/", "--"));
974
+ return `${base}.asset-link`;
975
+ }
976
+ function safePathSegment(value) {
977
+ const safe = value.normalize("NFKD").replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase();
978
+ if (!safe || safe === "." || safe === "..")
979
+ throw new Error(`Unsafe catalog path segment: ${value}`);
980
+ return safe;
981
+ }
982
+ function normalizeRelativePath(path) {
983
+ return path.replaceAll("\\", "/");
984
+ }
985
+ function encodeRelativeHref(path) {
986
+ return normalizeRelativePath(path).split("/").map((part) => part === "." || part === ".." ? part : encodeURIComponent(part)).join("/");
987
+ }
988
+ function uniqueSorted(values) {
989
+ return [...new Set(values)].sort(compareStableStrings);
990
+ }
991
+ function compareCatalogEntries(left, right) {
992
+ return compareStableStrings(
993
+ `${left.family}:${left.title}:${left.id}`,
994
+ `${right.family}:${right.title}:${right.id}`
995
+ );
996
+ }
997
+ function compareStableStrings(left, right) {
998
+ if (left === right) return 0;
999
+ return left < right ? -1 : 1;
1000
+ }
1001
+ function escapeTable(value) {
1002
+ return value.replaceAll("|", "\\|").replace(/\r?\n/g, " ");
1003
+ }
1004
+ function escapeHtml(value) {
1005
+ return value.replace(/[&<>"']/g, (character) => {
1006
+ const entities = {
1007
+ "&": "&amp;",
1008
+ "<": "&lt;",
1009
+ ">": "&gt;",
1010
+ '"': "&quot;",
1011
+ "'": "&#39;"
1012
+ };
1013
+ return entities[character] ?? character;
1014
+ });
1015
+ }
1016
+ function sha256(value) {
1017
+ return `sha256:${createHash("sha256").update(value).digest("hex")}`;
1018
+ }
1019
+
1020
+ // src/asset-npx/maintenance.ts
1021
+ import { createHash as createHash2 } from "node:crypto";
1022
+ import {
1023
+ cpSync,
1024
+ existsSync as existsSync4,
1025
+ mkdirSync as mkdirSync2,
1026
+ mkdtempSync as mkdtempSync2,
1027
+ readdirSync as readdirSync4,
1028
+ readFileSync as readFileSync3,
1029
+ statSync as statSync2
71
1030
  } from "node:fs";
72
- import { join as join3, relative as relative2 } from "node:path";
1031
+ import { join as join4, relative as relative3 } from "node:path";
73
1032
  import { tmpdir } from "node:os";
74
1033
 
75
1034
  // src/command-runner.ts
@@ -100,7 +1059,7 @@ function createNpxSkillsMaintenancePlan(options) {
100
1059
  throw new Error("npx skills add requires a source.");
101
1060
  }
102
1061
  const before = snapshotFiles(options.npxRoot);
103
- const tempRoot = mkdtempSync(join3(tmpdir(), "pro-gov-npx-skills-"));
1062
+ const tempRoot = mkdtempSync2(join4(tmpdir(), "pro-gov-npx-skills-"));
104
1063
  cpSync(options.npxRoot, tempRoot, { recursive: true, dereference: false });
105
1064
  const command2 = buildNpxCommand(options);
106
1065
  const runner = options.runner ?? defaultRunner;
@@ -144,10 +1103,10 @@ ${stderr}`.replace(ansiEscape, "");
144
1103
  }
145
1104
  }
146
1105
  function assertNativeNpxRoot(npxRoot) {
147
- if (!existsSync3(join3(npxRoot, "skills-lock.json"))) {
1106
+ if (!existsSync4(join4(npxRoot, "skills-lock.json"))) {
148
1107
  throw new Error(`npx skills root is missing skills-lock.json: ${npxRoot}`);
149
1108
  }
150
- if (!existsSync3(join3(npxRoot, ".agents/skills"))) {
1109
+ if (!existsSync4(join4(npxRoot, ".agents/skills"))) {
151
1110
  throw new Error(`npx skills root is missing .agents/skills: ${npxRoot}`);
152
1111
  }
153
1112
  }
@@ -178,21 +1137,21 @@ function defaultRunner({ command: command2, cwd, timeoutMs }) {
178
1137
  }
179
1138
  function snapshotFiles(root) {
180
1139
  const snapshot = /* @__PURE__ */ new Map();
181
- for (const filePath of listFiles2(root)) {
182
- const relativePath = toUnixPath2(relative2(root, filePath));
1140
+ for (const filePath of listFiles3(root)) {
1141
+ const relativePath = toUnixPath2(relative3(root, filePath));
183
1142
  snapshot.set(relativePath, hashFile(filePath));
184
1143
  }
185
1144
  return snapshot;
186
1145
  }
187
- function listFiles2(root) {
1146
+ function listFiles3(root) {
188
1147
  const files = [];
189
1148
  collectFiles(root, root, files);
190
1149
  return files.sort();
191
1150
  }
192
1151
  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);
1152
+ mkdirSync2(root, { recursive: true });
1153
+ for (const entry of readdirSync4(current, { withFileTypes: true })) {
1154
+ const entryPath = join4(current, entry.name);
196
1155
  if (entry.isDirectory()) {
197
1156
  collectFiles(root, entryPath, files);
198
1157
  } else if (entry.isFile()) {
@@ -201,11 +1160,11 @@ function collectFiles(root, current, files) {
201
1160
  }
202
1161
  }
203
1162
  function hashFile(path) {
204
- const hash = createHash("sha256");
205
- const stats = statSync(path);
1163
+ const hash = createHash2("sha256");
1164
+ const stats = statSync2(path);
206
1165
  hash.update(String(stats.size));
207
1166
  hash.update("\0");
208
- hash.update(readFileSync2(path));
1167
+ hash.update(readFileSync3(path));
209
1168
  return hash.digest("hex");
210
1169
  }
211
1170
  function diffSnapshots(before, after) {
@@ -232,14 +1191,14 @@ function toUnixPath2(path) {
232
1191
  }
233
1192
 
234
1193
  // 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";
1194
+ import { createHash as createHash3 } from "node:crypto";
1195
+ import { existsSync as existsSync6, readdirSync as readdirSync6, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
1196
+ import { dirname as dirname3, join as join6, relative as relative5 } from "node:path";
238
1197
  import { fileURLToPath as fileURLToPath2 } from "node:url";
239
1198
 
240
1199
  // 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";
1200
+ import { existsSync as existsSync5, lstatSync as lstatSync2, readdirSync as readdirSync5, realpathSync } from "node:fs";
1201
+ import { isAbsolute as isAbsolute2, join as join5, posix, relative as relative4, sep } from "node:path";
243
1202
  var supportedFamilies = /* @__PURE__ */ new Set([
244
1203
  "pie-skills",
245
1204
  "npx-skills",
@@ -283,7 +1242,7 @@ function assetSkillInstallName(asset) {
283
1242
  return asset.installName ?? posix.basename(asset.sourcePath);
284
1243
  }
285
1244
  function isValidAssetProjectTargetPath(kind, projectTargetPath) {
286
- if (typeof projectTargetPath !== "string" || projectTargetPath.length === 0 || isAbsolute(projectTargetPath) || projectTargetPath.includes("\\")) {
1245
+ if (typeof projectTargetPath !== "string" || projectTargetPath.length === 0 || isAbsolute2(projectTargetPath) || projectTargetPath.includes("\\")) {
287
1246
  return false;
288
1247
  }
289
1248
  const segments = projectTargetPath.split("/");
@@ -439,11 +1398,11 @@ function validateAssetRegistry(registry, options = {}) {
439
1398
  });
440
1399
  }
441
1400
  if (options.agentAssetsDir && isSafeRegistrySourcePath(asset.sourcePath)) {
442
- const sourceAbsolutePath = join4(
1401
+ const sourceAbsolutePath = join5(
443
1402
  options.agentAssetsDir,
444
1403
  normalizeRegistrySourcePath(asset.sourcePath)
445
1404
  );
446
- if (!existsSync4(sourceAbsolutePath)) {
1405
+ if (!existsSync5(sourceAbsolutePath)) {
447
1406
  issues.push({
448
1407
  type: "missing-source-path",
449
1408
  id: asset.id,
@@ -467,7 +1426,7 @@ function validateAssetRegistry(registry, options = {}) {
467
1426
  message: `Local skill pack must contain .codex-plugin/plugin.json and at least one skills/*/SKILL.md: ${asset.sourcePath}`
468
1427
  });
469
1428
  }
470
- } else if (!existsSync4(join4(sourceAbsolutePath, "SKILL.md"))) {
1429
+ } else if (!existsSync5(join5(sourceAbsolutePath, "SKILL.md"))) {
471
1430
  issues.push({
472
1431
  type: "missing-skill-file",
473
1432
  id: asset.id,
@@ -479,7 +1438,7 @@ function validateAssetRegistry(registry, options = {}) {
479
1438
  }
480
1439
  }
481
1440
  if (options.agentAssetsDir) {
482
- const npxCompatibilityLayer = join4(options.agentAssetsDir, "skills/npx-skills/skills");
1441
+ const npxCompatibilityLayer = join5(options.agentAssetsDir, "skills/npx-skills/skills");
483
1442
  if (pathExistsEvenIfDanglingSymlink(npxCompatibilityLayer)) {
484
1443
  issues.push({
485
1444
  type: "internal-npx-compatibility-layer",
@@ -492,20 +1451,20 @@ function validateAssetRegistry(registry, options = {}) {
492
1451
  return issues;
493
1452
  }
494
1453
  function isLocalSkillPack(sourceAbsolutePath) {
495
- const skillsRoot = join4(sourceAbsolutePath, "skills");
496
- if (!existsSync4(join4(sourceAbsolutePath, ".codex-plugin/plugin.json")) || !existsSync4(skillsRoot)) {
1454
+ const skillsRoot = join5(sourceAbsolutePath, "skills");
1455
+ if (!existsSync5(join5(sourceAbsolutePath, ".codex-plugin/plugin.json")) || !existsSync5(skillsRoot)) {
497
1456
  return false;
498
1457
  }
499
1458
  try {
500
- return lstatSync(skillsRoot).isDirectory() && readdirSync4(skillsRoot, { withFileTypes: true }).some(
501
- (entry) => entry.isDirectory() && existsSync4(join4(skillsRoot, entry.name, "SKILL.md"))
1459
+ return lstatSync2(skillsRoot).isDirectory() && readdirSync5(skillsRoot, { withFileTypes: true }).some(
1460
+ (entry) => entry.isDirectory() && existsSync5(join5(skillsRoot, entry.name, "SKILL.md"))
502
1461
  );
503
1462
  } catch {
504
1463
  return false;
505
1464
  }
506
1465
  }
507
1466
  function isSafeRegistrySourcePath(sourcePath) {
508
- if (!sourcePath || isAbsolute(sourcePath) || sourcePath.startsWith("/")) return false;
1467
+ if (!sourcePath || isAbsolute2(sourcePath) || sourcePath.startsWith("/")) return false;
509
1468
  const normalized = normalizeRegistrySourcePath(sourcePath);
510
1469
  if (normalized === "." || normalized.startsWith("../") || normalized === "..") return false;
511
1470
  return !normalized.split("/").includes("..");
@@ -517,15 +1476,15 @@ function isWithinAgentAssetsDir(agentAssetsDir, sourceAbsolutePath) {
517
1476
  try {
518
1477
  const agentAssetsRealPath = realpathSync(agentAssetsDir);
519
1478
  const sourceRealPath = realpathSync(sourceAbsolutePath);
520
- const relativePath = relative3(agentAssetsRealPath, sourceRealPath);
521
- return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath);
1479
+ const relativePath = relative4(agentAssetsRealPath, sourceRealPath);
1480
+ return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute2(relativePath);
522
1481
  } catch {
523
1482
  return false;
524
1483
  }
525
1484
  }
526
1485
  function pathExistsEvenIfDanglingSymlink(path) {
527
1486
  try {
528
- lstatSync(path);
1487
+ lstatSync2(path);
529
1488
  return true;
530
1489
  } catch {
531
1490
  return false;
@@ -542,7 +1501,7 @@ function createAgentAssetRegistryProvenance(registry, selectedAssetIds) {
542
1501
  (left, right) => left.id < right.id ? -1 : left.id > right.id ? 1 : 0
543
1502
  )
544
1503
  });
545
- const hash = createHash2("sha256").update(JSON.stringify(canonicalRegistry)).digest("hex");
1504
+ const hash = createHash3("sha256").update(JSON.stringify(canonicalRegistry)).digest("hex");
546
1505
  return {
547
1506
  schema: "agent-assets-registry",
548
1507
  version: registry.schemaVersion,
@@ -552,8 +1511,8 @@ function createAgentAssetRegistryProvenance(registry, selectedAssetIds) {
552
1511
  }
553
1512
  function loadAgentAssetRegistry(options = {}) {
554
1513
  const agentAssetsDir = options.agentAssetsDir ?? findDefaultAgentAssetsDir();
555
- const registryPath = join5(agentAssetsDir, "registry.json");
556
- if (!existsSync5(registryPath)) {
1514
+ const registryPath = join6(agentAssetsDir, "registry.json");
1515
+ if (!existsSync6(registryPath)) {
557
1516
  return {
558
1517
  registry: { schemaVersion: 1, assets: [] },
559
1518
  agentAssetsDir,
@@ -561,7 +1520,7 @@ function loadAgentAssetRegistry(options = {}) {
561
1520
  issues: []
562
1521
  };
563
1522
  }
564
- const registry = JSON.parse(readFileSync3(registryPath, "utf8"));
1523
+ const registry = JSON.parse(readFileSync4(registryPath, "utf8"));
565
1524
  return {
566
1525
  registry,
567
1526
  agentAssetsDir,
@@ -579,15 +1538,15 @@ function createAgentAssetLockEntries(registry, agentAssetsDir, assetIds) {
579
1538
  })).sort((a, b) => a.id.localeCompare(b.id));
580
1539
  }
581
1540
  function hashAgentAssetContent(asset, agentAssetsDir) {
582
- return hashAssetPathContent(join5(agentAssetsDir, asset.sourcePath));
1541
+ return hashAssetPathContent(join6(agentAssetsDir, asset.sourcePath));
583
1542
  }
584
1543
  function hashAssetPathContent(sourceAbsolutePath) {
585
- const hash = createHash2("sha256");
586
- for (const filePath of listFiles3(sourceAbsolutePath)) {
587
- const relativePath = toUnixPath3(relative4(sourceAbsolutePath, filePath));
1544
+ const hash = createHash3("sha256");
1545
+ for (const filePath of listFiles4(sourceAbsolutePath)) {
1546
+ const relativePath = toUnixPath3(relative5(sourceAbsolutePath, filePath));
588
1547
  hash.update(relativePath);
589
1548
  hash.update("\0");
590
- hash.update(readFileSync3(filePath));
1549
+ hash.update(readFileSync4(filePath));
591
1550
  hash.update("\0");
592
1551
  }
593
1552
  return `sha256:${hash.digest("hex")}`;
@@ -602,40 +1561,40 @@ function canonicalizeValue(value) {
602
1561
  return value;
603
1562
  }
604
1563
  function findDefaultAgentAssetsDir() {
605
- const packageRoot2 = findPackageRoot(dirname2(fileURLToPath2(import.meta.url)));
606
- const repoRoot = join5(packageRoot2, "..", "..");
1564
+ const packageRoot2 = findPackageRoot(dirname3(fileURLToPath2(import.meta.url)));
1565
+ const repoRoot = join6(packageRoot2, "..", "..");
607
1566
  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")
1567
+ join6(packageRoot2, "assets/agent-assets"),
1568
+ join6(repoRoot, "agent-assets"),
1569
+ join6(packageRoot2, "assets/public-agent-assets"),
1570
+ join6(repoRoot, "public-agent-assets")
612
1571
  ];
613
- return candidates.find((candidate) => existsSync5(join5(candidate, "registry.json"))) ?? candidates[0];
1572
+ return candidates.find((candidate) => existsSync6(join6(candidate, "registry.json"))) ?? candidates[0];
614
1573
  }
615
1574
  function findPackageRoot(startDir) {
616
1575
  let current = startDir;
617
- while (current !== dirname2(current)) {
618
- const packageJsonPath = join5(current, "package.json");
619
- if (existsSync5(packageJsonPath)) {
1576
+ while (current !== dirname3(current)) {
1577
+ const packageJsonPath = join6(current, "package.json");
1578
+ if (existsSync6(packageJsonPath)) {
620
1579
  try {
621
- const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf8"));
1580
+ const packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
622
1581
  if (packageJson.name === "@pieai/pro-gov") return current;
623
1582
  } catch {
624
1583
  }
625
1584
  }
626
- current = dirname2(current);
1585
+ current = dirname3(current);
627
1586
  }
628
1587
  return startDir;
629
1588
  }
630
- function listFiles3(absolutePath) {
631
- const stats = statSync2(absolutePath);
1589
+ function listFiles4(absolutePath) {
1590
+ const stats = statSync3(absolutePath);
632
1591
  if (stats.isFile()) return [absolutePath];
633
1592
  const files = [];
634
- for (const entry of readdirSync5(absolutePath, { withFileTypes: true })) {
1593
+ for (const entry of readdirSync6(absolutePath, { withFileTypes: true })) {
635
1594
  if (shouldIgnoreAssetHashEntry(entry.name)) continue;
636
- const entryPath = join5(absolutePath, entry.name);
1595
+ const entryPath = join6(absolutePath, entry.name);
637
1596
  if (entry.isDirectory()) {
638
- files.push(...listFiles3(entryPath));
1597
+ files.push(...listFiles4(entryPath));
639
1598
  } else if (entry.isFile()) {
640
1599
  files.push(entryPath);
641
1600
  }
@@ -650,8 +1609,8 @@ function toUnixPath3(path) {
650
1609
  }
651
1610
 
652
1611
  // 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";
1612
+ import { existsSync as existsSync7 } from "node:fs";
1613
+ import { isAbsolute as isAbsolute3, join as join7, posix as posix2 } from "node:path";
655
1614
  function checkPublicAssetPromotions(options) {
656
1615
  const issues = [];
657
1616
  let checked = 0;
@@ -689,7 +1648,7 @@ function checkPublicAssetPromotions(options) {
689
1648
  });
690
1649
  continue;
691
1650
  }
692
- if (!existsSync6(privatePathResult.path)) {
1651
+ if (!existsSync7(privatePathResult.path)) {
693
1652
  issues.push({
694
1653
  type: "missing-private-source",
695
1654
  id: asset.id,
@@ -709,7 +1668,7 @@ function checkPublicAssetPromotions(options) {
709
1668
  });
710
1669
  }
711
1670
  }
712
- if (!existsSync6(publicPathResult.path)) {
1671
+ if (!existsSync7(publicPathResult.path)) {
713
1672
  issues.push({
714
1673
  type: "missing-public-source",
715
1674
  id: asset.id,
@@ -736,32 +1695,32 @@ function needsPromotionCheck(asset) {
736
1695
  return asset.visibility === "public" && asset.publishable;
737
1696
  }
738
1697
  function resolveSafePath(root, sourcePath) {
739
- if (!sourcePath || isAbsolute2(sourcePath) || sourcePath.startsWith("/")) return { ok: false };
1698
+ if (!sourcePath || isAbsolute3(sourcePath) || sourcePath.startsWith("/")) return { ok: false };
740
1699
  const normalized = posix2.normalize(sourcePath.replaceAll("\\", "/"));
741
1700
  if (normalized === "." || normalized === ".." || normalized.startsWith("../")) {
742
1701
  return { ok: false };
743
1702
  }
744
1703
  if (normalized.split("/").includes("..")) return { ok: false };
745
- return { ok: true, path: join6(root, normalized) };
1704
+ return { ok: true, path: join7(root, normalized) };
746
1705
  }
747
1706
 
748
1707
  // src/asset-targets/apply.ts
749
- import { createHash as createHash3 } from "node:crypto";
1708
+ import { createHash as createHash4 } from "node:crypto";
750
1709
  import {
751
- existsSync as existsSync8,
752
- lstatSync as lstatSync3,
753
- mkdirSync as mkdirSync2,
1710
+ existsSync as existsSync9,
1711
+ lstatSync as lstatSync4,
1712
+ mkdirSync as mkdirSync3,
754
1713
  readlinkSync as readlinkSync2,
755
1714
  realpathSync as realpathSync3,
756
- symlinkSync,
1715
+ symlinkSync as symlinkSync2,
757
1716
  unlinkSync,
758
- writeFileSync
1717
+ writeFileSync as writeFileSync2
759
1718
  } from "node:fs";
760
- import { dirname as dirname4, join as join8, relative as relative5, resolve as resolve2 } from "node:path";
1719
+ import { dirname as dirname5, join as join9, relative as relative6, resolve as resolve3 } from "node:path";
761
1720
 
762
1721
  // 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";
1722
+ import { existsSync as existsSync8, lstatSync as lstatSync3, readFileSync as readFileSync5, readlinkSync, realpathSync as realpathSync2, statSync as statSync4 } from "node:fs";
1723
+ import { basename as basename2, dirname as dirname4, join as join8, resolve as resolve2 } from "node:path";
765
1724
 
766
1725
  // src/symlinks.ts
767
1726
  function normalizeSymlinkTarget(target) {
@@ -888,9 +1847,9 @@ function createAssetAction(asset, agentAssetsDir, targetDir, host, placement, ma
888
1847
  `User-scoped asset ${asset.id} must be linked at the user level, not installed into a project target.`
889
1848
  );
890
1849
  }
891
- const sourcePath = join7(agentAssetsDir, asset.sourcePath);
1850
+ const sourcePath = join8(agentAssetsDir, asset.sourcePath);
892
1851
  const targetPath = resolveHostTargetPath(asset, host, placement);
893
- const targetAbsolutePath = join7(targetDir, targetPath);
1852
+ const targetAbsolutePath = join8(targetDir, targetPath);
894
1853
  const targetExists = pathExistsEvenIfDanglingSymlink2(targetAbsolutePath);
895
1854
  const managedEntry = managedEntries.find(
896
1855
  (entry) => entry.id === asset.id && entry.targetPath === targetPath
@@ -907,7 +1866,7 @@ function createAssetAction(asset, agentAssetsDir, targetDir, host, placement, ma
907
1866
  });
908
1867
  }
909
1868
  if (targetExists) {
910
- const stats = lstatSync2(targetAbsolutePath);
1869
+ const stats = lstatSync3(targetAbsolutePath);
911
1870
  if (stats.isSymbolicLink() && managedEntry && (managedEntry.delivery ?? "symlink") === "symlink") {
912
1871
  return {
913
1872
  type: "update-symlink",
@@ -919,7 +1878,7 @@ function createAssetAction(asset, agentAssetsDir, targetDir, host, placement, ma
919
1878
  if (managedEntry && managedEntry.delivery === "snapshot") {
920
1879
  throw new Error(`Refusing to replace managed snapshot with a symlink: ${targetPath}`);
921
1880
  }
922
- if (stats.isSymbolicLink() && existsSync7(targetAbsolutePath) && realpathSync2(targetAbsolutePath) === realpathSync2(sourcePath)) {
1881
+ if (stats.isSymbolicLink() && existsSync8(targetAbsolutePath) && realpathSync2(targetAbsolutePath) === realpathSync2(sourcePath)) {
923
1882
  return {
924
1883
  type: "adopt-existing-symlink",
925
1884
  assetId: asset.id,
@@ -942,10 +1901,10 @@ function createSnapshotAction(options) {
942
1901
  `Snapshot delivery requires a rule target under docs/policy/shared-rules/: ${options.asset.id}`
943
1902
  );
944
1903
  }
945
- if (!statSync3(options.sourcePath).isFile()) {
1904
+ if (!statSync4(options.sourcePath).isFile()) {
946
1905
  throw new Error(`Snapshot source must be a regular file: ${options.asset.sourcePath}`);
947
1906
  }
948
- const content = readFileSync4(options.sourcePath);
1907
+ const content = readFileSync5(options.sourcePath);
949
1908
  const contentBase64 = content.toString("base64");
950
1909
  const contentHash = hashAgentAssetContent(options.asset, options.agentAssetsDir);
951
1910
  const managedDelivery = options.managedEntry?.delivery ?? "symlink";
@@ -958,9 +1917,9 @@ function createSnapshotAction(options) {
958
1917
  contentHash
959
1918
  };
960
1919
  }
961
- const stats = lstatSync2(options.targetAbsolutePath);
1920
+ const stats = lstatSync3(options.targetAbsolutePath);
962
1921
  if (stats.isSymbolicLink()) {
963
- if (existsSync7(options.targetAbsolutePath) && realpathSync2(options.targetAbsolutePath) === realpathSync2(options.sourcePath) && hashAssetPathContent(options.targetAbsolutePath) === contentHash) {
1922
+ if (existsSync8(options.targetAbsolutePath) && realpathSync2(options.targetAbsolutePath) === realpathSync2(options.sourcePath) && hashAssetPathContent(options.targetAbsolutePath) === contentHash) {
964
1923
  return {
965
1924
  type: "migrate-symlink-to-snapshot",
966
1925
  assetId: options.asset.id,
@@ -1016,9 +1975,9 @@ function resolveHostTargetPath(asset, _host, placement) {
1016
1975
  return `.agents/skills/${assetSkillInstallName(asset)}`;
1017
1976
  }
1018
1977
  if (asset.kind === "rule") {
1019
- return `.pro-gov/agent-assets/rules/${basename(asset.sourcePath)}`;
1978
+ return `.pro-gov/agent-assets/rules/${basename2(asset.sourcePath)}`;
1020
1979
  }
1021
- return `.pro-gov/agent-assets/commands/${basename(asset.sourcePath)}`;
1980
+ return `.pro-gov/agent-assets/commands/${basename2(asset.sourcePath)}`;
1022
1981
  }
1023
1982
  function resolveSkillPlacement(asset, placement) {
1024
1983
  if (placement !== "registry") return placement;
@@ -1028,16 +1987,16 @@ function createDirectoryActions(actions) {
1028
1987
  const directories = /* @__PURE__ */ new Set();
1029
1988
  for (const action of actions) {
1030
1989
  if (action.type === "create-dir") continue;
1031
- const directory = dirname3(action.targetPath);
1990
+ const directory = dirname4(action.targetPath);
1032
1991
  if (directory !== ".") directories.add(directory);
1033
1992
  }
1034
1993
  return [...directories].sort().map((targetPath) => ({ type: "create-dir", targetPath }));
1035
1994
  }
1036
1995
  function readManagedLock(targetDir) {
1037
- const lockfilePath = join7(targetDir, ".pro-gov/assets.lock.json");
1038
- if (!existsSync7(lockfilePath)) return { entries: [] };
1996
+ const lockfilePath = join8(targetDir, ".pro-gov/assets.lock.json");
1997
+ if (!existsSync8(lockfilePath)) return { entries: [] };
1039
1998
  try {
1040
- const lockfile = JSON.parse(readFileSync4(lockfilePath, "utf8"));
1999
+ const lockfile = JSON.parse(readFileSync5(lockfilePath, "utf8"));
1041
2000
  return {
1042
2001
  host: typeof lockfile.host === "string" ? lockfile.host : void 0,
1043
2002
  entries: (lockfile.assets ?? []).filter(
@@ -1075,10 +2034,10 @@ function createLegacyClaudeAdoptions(options) {
1075
2034
  throw new Error(`Legacy Claude lock entry cannot be safely normalized: ${entry.targetPath}`);
1076
2035
  }
1077
2036
  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)) {
2037
+ const targetAbsolutePath = join8(options.targetDir, targetPath);
2038
+ const compatibilityRootPath = join8(options.targetDir, ".claude/skills");
2039
+ const canonicalRootPath = join8(options.targetDir, ".agents/skills");
2040
+ if (!lstatSync3(canonicalRootPath).isDirectory() || !lstatSync3(compatibilityRootPath).isSymbolicLink() || normalizeSymlinkTarget(readlinkSync(compatibilityRootPath)) !== "../.agents/skills" || realpathSync2(compatibilityRootPath) !== realpathSync2(canonicalRootPath)) {
1082
2041
  throw new Error(
1083
2042
  `Legacy Claude compatibility root is not the exact canonical alias for ${entry.id}.`
1084
2043
  );
@@ -1087,10 +2046,10 @@ function createLegacyClaudeAdoptions(options) {
1087
2046
  consumedTargetPaths.add(entry.targetPath);
1088
2047
  continue;
1089
2048
  }
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);
2049
+ const targetStat = lstatSync3(targetAbsolutePath);
2050
+ const legacyAbsolutePath = join8(options.targetDir, entry.targetPath);
2051
+ const legacyStat = lstatSync3(legacyAbsolutePath);
2052
+ const expectedSourcePath = join8(options.agentAssetsDir, entry.sourcePath);
1094
2053
  if (!targetStat.isSymbolicLink() || !legacyStat.isSymbolicLink() || targetStat.dev !== legacyStat.dev || targetStat.ino !== legacyStat.ino || realpathSync2(targetAbsolutePath) !== realpathSync2(expectedSourcePath)) {
1095
2054
  throw new Error(`Legacy Claude skill target cannot be safely adopted: ${entry.targetPath}`);
1096
2055
  }
@@ -1120,9 +2079,9 @@ function createRemovalActions(targetDir, agentAssetsDir, managedEntries, expecte
1120
2079
  `Refusing to remove managed asset outside supported roots: ${entry.targetPath}`
1121
2080
  );
1122
2081
  }
1123
- const targetAbsolutePath = join7(targetDir, entry.targetPath);
2082
+ const targetAbsolutePath = join8(targetDir, entry.targetPath);
1124
2083
  if (!pathExistsEvenIfDanglingSymlink2(targetAbsolutePath)) continue;
1125
- const stats = lstatSync2(targetAbsolutePath);
2084
+ const stats = lstatSync3(targetAbsolutePath);
1126
2085
  if ((entry.delivery ?? "symlink") === "snapshot") {
1127
2086
  if (!isValidSnapshotProjectTargetPath(entry.targetPath)) {
1128
2087
  throw new Error(
@@ -1151,9 +2110,9 @@ function createRemovalActions(targetDir, agentAssetsDir, managedEntries, expecte
1151
2110
  `Refusing to remove path that is no longer a managed symlink: ${entry.targetPath}`
1152
2111
  );
1153
2112
  }
1154
- const expectedSourcePath = join7(agentAssetsDir, entry.sourcePath);
1155
- const actualSourcePath = resolve(dirname3(targetAbsolutePath), readlinkSync(targetAbsolutePath));
1156
- if (actualSourcePath !== resolve(expectedSourcePath)) {
2113
+ const expectedSourcePath = join8(agentAssetsDir, entry.sourcePath);
2114
+ const actualSourcePath = resolve2(dirname4(targetAbsolutePath), readlinkSync(targetAbsolutePath));
2115
+ if (actualSourcePath !== resolve2(expectedSourcePath)) {
1157
2116
  throw new Error(
1158
2117
  `Refusing to remove managed symlink with changed target: ${entry.targetPath}`
1159
2118
  );
@@ -1177,7 +2136,7 @@ function isLegacyClaudeSkillTargetPath(path) {
1177
2136
  }
1178
2137
  function pathExistsEvenIfDanglingSymlink2(path) {
1179
2138
  try {
1180
- lstatSync2(path);
2139
+ lstatSync3(path);
1181
2140
  return true;
1182
2141
  } catch {
1183
2142
  return false;
@@ -1201,7 +2160,7 @@ function applyAssetInstallPlan(plan) {
1201
2160
  return { appliedActions };
1202
2161
  }
1203
2162
  function applyAction(targetDir, action) {
1204
- const targetAbsolutePath = join8(targetDir, action.targetPath);
2163
+ const targetAbsolutePath = join9(targetDir, action.targetPath);
1205
2164
  if (action.type === "remove-symlink") {
1206
2165
  removeManagedSymlink(targetAbsolutePath, action);
1207
2166
  return;
@@ -1212,41 +2171,41 @@ function applyAction(targetDir, action) {
1212
2171
  return;
1213
2172
  }
1214
2173
  if (action.type === "create-dir") {
1215
- mkdirSync2(targetAbsolutePath, { recursive: true });
2174
+ mkdirSync3(targetAbsolutePath, { recursive: true });
1216
2175
  return;
1217
2176
  }
1218
2177
  if (action.type === "write-file") {
1219
- mkdirSync2(dirname4(targetAbsolutePath), { recursive: true });
1220
- writeFileSync(targetAbsolutePath, action.content);
2178
+ mkdirSync3(dirname5(targetAbsolutePath), { recursive: true });
2179
+ writeFileSync2(targetAbsolutePath, action.content);
1221
2180
  return;
1222
2181
  }
1223
- mkdirSync2(dirname4(targetAbsolutePath), { recursive: true });
1224
- const sourceAbsolutePath = resolve2(action.sourcePath);
1225
- const symlinkTarget = relative5(realpathSync3(dirname4(targetAbsolutePath)), realpathSync3(sourceAbsolutePath)) || ".";
2182
+ mkdirSync3(dirname5(targetAbsolutePath), { recursive: true });
2183
+ const sourceAbsolutePath = resolve3(action.sourcePath);
2184
+ const symlinkTarget = relative6(realpathSync3(dirname5(targetAbsolutePath)), realpathSync3(sourceAbsolutePath)) || ".";
1226
2185
  if (action.type === "symlink") {
1227
2186
  if (pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1228
2187
  throw new Error(`Refusing to overwrite unmanaged target: ${action.targetPath}`);
1229
2188
  }
1230
- symlinkSync(symlinkTarget, targetAbsolutePath);
2189
+ symlinkSync2(symlinkTarget, targetAbsolutePath);
1231
2190
  return;
1232
2191
  }
1233
2192
  if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1234
- symlinkSync(symlinkTarget, targetAbsolutePath);
2193
+ symlinkSync2(symlinkTarget, targetAbsolutePath);
1235
2194
  return;
1236
2195
  }
1237
- const stats = lstatSync3(targetAbsolutePath);
2196
+ const stats = lstatSync4(targetAbsolutePath);
1238
2197
  if (!stats.isSymbolicLink()) {
1239
2198
  throw new Error(`Refusing to overwrite unmanaged target: ${action.targetPath}`);
1240
2199
  }
1241
2200
  unlinkSync(targetAbsolutePath);
1242
- symlinkSync(symlinkTarget, targetAbsolutePath);
2201
+ symlinkSync2(symlinkTarget, targetAbsolutePath);
1243
2202
  }
1244
2203
  function validateExistingSymlink(targetDir, action) {
1245
2204
  if (!isManagedAssetTargetPath(action.targetPath)) {
1246
2205
  throw new Error(`Refusing unsafe existing symlink adoption: ${action.targetPath}`);
1247
2206
  }
1248
- const targetAbsolutePath = join8(targetDir, action.targetPath);
1249
- if (!lstatSync3(targetAbsolutePath).isSymbolicLink() || realpathSync3(targetAbsolutePath) !== realpathSync3(action.sourcePath)) {
2207
+ const targetAbsolutePath = join9(targetDir, action.targetPath);
2208
+ if (!lstatSync4(targetAbsolutePath).isSymbolicLink() || realpathSync3(targetAbsolutePath) !== realpathSync3(action.sourcePath)) {
1250
2209
  throw new Error(`Existing skill symlink changed before apply: ${action.targetPath}`);
1251
2210
  }
1252
2211
  }
@@ -1257,7 +2216,7 @@ function validateSnapshotAction(targetDir, action) {
1257
2216
  if ("contentBase64" in action && hashSnapshotBytes(Buffer.from(action.contentBase64, "base64")) !== action.contentHash) {
1258
2217
  throw new Error(`Snapshot content hash is invalid: ${action.targetPath}`);
1259
2218
  }
1260
- const targetAbsolutePath = join8(targetDir, action.targetPath);
2219
+ const targetAbsolutePath = join9(targetDir, action.targetPath);
1261
2220
  if (action.type === "snapshot") {
1262
2221
  if (pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1263
2222
  throw new Error(`Snapshot target changed before apply: ${action.targetPath}`);
@@ -1273,7 +2232,7 @@ function validateSnapshotAction(targetDir, action) {
1273
2232
  return;
1274
2233
  }
1275
2234
  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) {
2235
+ if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath) || !lstatSync4(targetAbsolutePath).isSymbolicLink() || !existsSync9(targetAbsolutePath) || realpathSync3(targetAbsolutePath) !== realpathSync3(action.sourcePath) || hashAssetPathContent(targetAbsolutePath) !== action.contentHash) {
1277
2236
  throw new Error(`Snapshot symlink changed before apply: ${action.targetPath}`);
1278
2237
  }
1279
2238
  return;
@@ -1281,30 +2240,30 @@ function validateSnapshotAction(targetDir, action) {
1281
2240
  assertRegularSnapshotHash(targetAbsolutePath, action.expectedContentHash, action.targetPath);
1282
2241
  }
1283
2242
  function applySnapshotAction(targetDir, action) {
1284
- const targetAbsolutePath = join8(targetDir, action.targetPath);
2243
+ const targetAbsolutePath = join9(targetDir, action.targetPath);
1285
2244
  if (action.type === "adopt-snapshot") return;
1286
2245
  if (action.type === "remove-snapshot") {
1287
2246
  unlinkSync(targetAbsolutePath);
1288
2247
  return;
1289
2248
  }
1290
2249
  if (action.type === "migrate-symlink-to-snapshot") unlinkSync(targetAbsolutePath);
1291
- mkdirSync2(dirname4(targetAbsolutePath), { recursive: true });
2250
+ mkdirSync3(dirname5(targetAbsolutePath), { recursive: true });
1292
2251
  if (action.type === "snapshot" && pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1293
2252
  throw new Error(`Refusing to overwrite unmanaged target: ${action.targetPath}`);
1294
2253
  }
1295
- writeFileSync(targetAbsolutePath, Buffer.from(action.contentBase64, "base64"));
2254
+ writeFileSync2(targetAbsolutePath, Buffer.from(action.contentBase64, "base64"));
1296
2255
  }
1297
2256
  function assertRegularSnapshotHash(targetAbsolutePath, expectedHash, targetPath) {
1298
2257
  if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) {
1299
2258
  throw new Error(`Snapshot target changed before apply: ${targetPath}`);
1300
2259
  }
1301
- const stats = lstatSync3(targetAbsolutePath);
2260
+ const stats = lstatSync4(targetAbsolutePath);
1302
2261
  if (!stats.isFile() || hashAssetPathContent(targetAbsolutePath) !== expectedHash) {
1303
2262
  throw new Error(`Snapshot target hash changed before apply: ${targetPath}`);
1304
2263
  }
1305
2264
  }
1306
2265
  function hashSnapshotBytes(content) {
1307
- const hash = createHash3("sha256");
2266
+ const hash = createHash4("sha256");
1308
2267
  hash.update("");
1309
2268
  hash.update("\0");
1310
2269
  hash.update(content);
@@ -1315,17 +2274,17 @@ function validateAdoptedSymlink(targetDir, action) {
1315
2274
  if (!isManagedAssetTargetPath(action.targetPath) || !isLegacyClaudeSkillTargetPath(action.legacyTargetPath) || action.compatibilityRootPath !== ".claude/skills" || action.expectedCompatibilityRawTarget !== "../.agents/skills") {
1316
2275
  throw new Error(`Refusing unsafe legacy Claude adoption: ${action.legacyTargetPath}`);
1317
2276
  }
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)) {
2277
+ const canonicalRootPath = join9(targetDir, ".agents/skills");
2278
+ const compatibilityRootPath = join9(targetDir, action.compatibilityRootPath);
2279
+ const targetAbsolutePath = join9(targetDir, action.targetPath);
2280
+ const legacyAbsolutePath = join9(targetDir, action.legacyTargetPath);
2281
+ if (!lstatSync4(canonicalRootPath).isDirectory() || !lstatSync4(compatibilityRootPath).isSymbolicLink() || normalizeSymlinkTarget(readlinkSync2(compatibilityRootPath)) !== action.expectedCompatibilityRawTarget || realpathSync3(compatibilityRootPath) !== realpathSync3(canonicalRootPath)) {
1323
2282
  throw new Error(
1324
2283
  `Legacy Claude compatibility alias changed before apply: ${action.compatibilityRootPath}`
1325
2284
  );
1326
2285
  }
1327
- const targetStat = lstatSync3(targetAbsolutePath);
1328
- const legacyStat = lstatSync3(legacyAbsolutePath);
2286
+ const targetStat = lstatSync4(targetAbsolutePath);
2287
+ const legacyStat = lstatSync4(legacyAbsolutePath);
1329
2288
  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
2289
  throw new Error(`Legacy Claude skill target changed before apply: ${action.targetPath}`);
1331
2290
  }
@@ -1337,33 +2296,33 @@ function removeManagedSymlink(targetAbsolutePath, action) {
1337
2296
  );
1338
2297
  }
1339
2298
  if (!pathExistsEvenIfDanglingSymlink3(targetAbsolutePath)) return;
1340
- const stats = lstatSync3(targetAbsolutePath);
2299
+ const stats = lstatSync4(targetAbsolutePath);
1341
2300
  if (!stats.isSymbolicLink()) {
1342
2301
  throw new Error(
1343
2302
  `Refusing to remove path that is no longer a managed symlink: ${action.targetPath}`
1344
2303
  );
1345
2304
  }
1346
- const actualSourcePath = resolve2(dirname4(targetAbsolutePath), readlinkSync2(targetAbsolutePath));
1347
- if (actualSourcePath !== resolve2(action.expectedSourcePath)) {
2305
+ const actualSourcePath = resolve3(dirname5(targetAbsolutePath), readlinkSync2(targetAbsolutePath));
2306
+ if (actualSourcePath !== resolve3(action.expectedSourcePath)) {
1348
2307
  throw new Error(`Refusing to remove managed symlink with changed target: ${action.targetPath}`);
1349
2308
  }
1350
2309
  unlinkSync(targetAbsolutePath);
1351
2310
  }
1352
2311
  function pathExistsEvenIfDanglingSymlink3(path) {
1353
2312
  try {
1354
- lstatSync3(path);
2313
+ lstatSync4(path);
1355
2314
  return true;
1356
2315
  } catch {
1357
- return existsSync8(path);
2316
+ return existsSync9(path);
1358
2317
  }
1359
2318
  }
1360
2319
 
1361
2320
  // 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";
2321
+ import { existsSync as existsSync10, lstatSync as lstatSync5, readFileSync as readFileSync6, realpathSync as realpathSync4 } from "node:fs";
2322
+ import { join as join10 } from "node:path";
1364
2323
  function checkInstalledAssets(options) {
1365
- const lockfilePath = join9(options.targetDir, ".pro-gov/assets.lock.json");
1366
- if (!existsSync9(lockfilePath)) {
2324
+ const lockfilePath = join10(options.targetDir, ".pro-gov/assets.lock.json");
2325
+ if (!existsSync10(lockfilePath)) {
1367
2326
  return {
1368
2327
  targetDir: options.targetDir,
1369
2328
  issues: [
@@ -1375,7 +2334,7 @@ function checkInstalledAssets(options) {
1375
2334
  };
1376
2335
  }
1377
2336
  const registryById = new Map(options.registry.assets.map((asset) => [asset.id, asset]));
1378
- const lockfile = JSON.parse(readFileSync5(lockfilePath, "utf8"));
2337
+ const lockfile = JSON.parse(readFileSync6(lockfilePath, "utf8"));
1379
2338
  const issues = [];
1380
2339
  const strictRegistry = options.strictRegistry ?? false;
1381
2340
  const selectedAssetIds = (lockfile.assets ?? []).map((entry) => entry.id);
@@ -1404,7 +2363,7 @@ function checkInstalledAssets(options) {
1404
2363
  continue;
1405
2364
  }
1406
2365
  const asset = registryById.get(entry.id);
1407
- const targetAbsolutePath = join9(options.targetDir, entry.targetPath);
2366
+ const targetAbsolutePath = join10(options.targetDir, entry.targetPath);
1408
2367
  const delivery = entry.delivery ?? "symlink";
1409
2368
  const portableDeferredSkill = delivery === "symlink" && !strictRegistry && isProjectSkillTarget(entry.targetPath);
1410
2369
  if (delivery !== "symlink" && delivery !== "snapshot") {
@@ -1466,7 +2425,7 @@ function checkInstalledAssets(options) {
1466
2425
  });
1467
2426
  continue;
1468
2427
  }
1469
- const targetStats = lstatSync4(targetAbsolutePath);
2428
+ const targetStats = lstatSync5(targetAbsolutePath);
1470
2429
  if (delivery === "snapshot" && !targetStats.isFile()) {
1471
2430
  issues.push({
1472
2431
  type: "snapshot-not-regular-file",
@@ -1485,7 +2444,7 @@ function checkInstalledAssets(options) {
1485
2444
  });
1486
2445
  continue;
1487
2446
  }
1488
- if (delivery === "symlink" && !existsSync9(targetAbsolutePath)) {
2447
+ if (delivery === "symlink" && !existsSync10(targetAbsolutePath)) {
1489
2448
  if (portableDeferredSkill) continue;
1490
2449
  issues.push({
1491
2450
  type: "dangling-symlink",
@@ -1506,8 +2465,8 @@ function checkInstalledAssets(options) {
1506
2465
  });
1507
2466
  }
1508
2467
  if (!asset || !strictRegistry || registryProvenanceMismatch) continue;
1509
- const sourceAbsolutePath = join9(options.agentAssetsDir, asset.sourcePath);
1510
- if (!existsSync9(sourceAbsolutePath)) {
2468
+ const sourceAbsolutePath = join10(options.agentAssetsDir, asset.sourcePath);
2469
+ if (!existsSync10(sourceAbsolutePath)) {
1511
2470
  issues.push({
1512
2471
  type: "missing-source",
1513
2472
  id: entry.id,
@@ -1571,7 +2530,7 @@ function checkDuplicateSkillPlacements(targetDir, registry) {
1571
2530
  const skillName = assetSkillInstallName(asset);
1572
2531
  const autoPath = `.agents/skills/${skillName}`;
1573
2532
  const manualPath = `.agents/manual-skills/${skillName}`;
1574
- if (pathExistsEvenIfDanglingSymlink4(join9(targetDir, autoPath)) && pathExistsEvenIfDanglingSymlink4(join9(targetDir, manualPath))) {
2533
+ if (pathExistsEvenIfDanglingSymlink4(join10(targetDir, autoPath)) && pathExistsEvenIfDanglingSymlink4(join10(targetDir, manualPath))) {
1575
2534
  issues.push({
1576
2535
  type: "duplicate-skill-placement",
1577
2536
  id: asset.id,
@@ -1617,7 +2576,7 @@ function expectedRegistrySkillTargetPath(host, skillName, placement) {
1617
2576
  }
1618
2577
  function pathExistsEvenIfDanglingSymlink4(path) {
1619
2578
  try {
1620
- lstatSync4(path);
2579
+ lstatSync5(path);
1621
2580
  return true;
1622
2581
  } catch {
1623
2582
  return false;
@@ -1628,8 +2587,8 @@ function isProjectSkillTarget(targetPath) {
1628
2587
  }
1629
2588
 
1630
2589
  // 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";
2590
+ import { existsSync as existsSync11, readdirSync as readdirSync7, readFileSync as readFileSync7 } from "node:fs";
2591
+ import { join as join11 } from "node:path";
1633
2592
  var frontendPackages = /* @__PURE__ */ new Set([
1634
2593
  "@vitejs/plugin-react",
1635
2594
  "astro",
@@ -1643,21 +2602,21 @@ var frontendPackages = /* @__PURE__ */ new Set([
1643
2602
  ]);
1644
2603
  var agentEntryCandidates = ["AGENTS.md", "CLAUDE.md"];
1645
2604
  function discoverTargetSignals(targetDir) {
1646
- const packageJson = readJson(join10(targetDir, "package.json"));
2605
+ const packageJson = readJson(join11(targetDir, "package.json"));
1647
2606
  const dependencyNames = packageJson ? Object.keys({ ...packageJson.dependencies, ...packageJson.devDependencies }) : [];
1648
2607
  const frontendSignals = dependencyNames.filter((name) => frontendPackages.has(name)).sort();
1649
- const hasAgentEntry = agentEntryCandidates.some((file) => existsSync10(join10(targetDir, file)));
2608
+ const hasAgentEntry = agentEntryCandidates.some((file) => existsSync11(join11(targetDir, file)));
1650
2609
  const researchSignals = [
1651
- existsSync10(join10(targetDir, "docs/research")) ? "docs/research" : "",
1652
- existsSync10(join10(targetDir, "research")) ? "research" : "",
2610
+ existsSync11(join11(targetDir, "docs/research")) ? "docs/research" : "",
2611
+ existsSync11(join11(targetDir, "research")) ? "research" : "",
1653
2612
  hasBookChildDirectory(targetDir, "research") ? "books/*/research" : "",
1654
- textFileIncludes(join10(targetDir, "README.md"), ["research", "\u8C03\u7814"]) ? "README research" : ""
2613
+ textFileIncludes(join11(targetDir, "README.md"), ["research", "\u8C03\u7814"]) ? "README research" : ""
1655
2614
  ].filter(Boolean);
1656
2615
  const writingSignals = [
1657
- existsSync10(join10(targetDir, "chapters")) ? "chapters" : "",
1658
- existsSync10(join10(targetDir, "src/chapters")) ? "src/chapters" : "",
2616
+ existsSync11(join11(targetDir, "chapters")) ? "chapters" : "",
2617
+ existsSync11(join11(targetDir, "src/chapters")) ? "src/chapters" : "",
1659
2618
  hasBookChildDirectory(targetDir, "chapters") ? "books/*/chapters" : "",
1660
- textFileIncludes(join10(targetDir, "AGENTS.md"), [
2619
+ textFileIncludes(join11(targetDir, "AGENTS.md"), [
1661
2620
  "writing mode",
1662
2621
  "novel chapter",
1663
2622
  "book content"
@@ -1702,24 +2661,24 @@ function recommendBundlesForTarget(targetDir) {
1702
2661
  return recommendations;
1703
2662
  }
1704
2663
  function readJson(path) {
1705
- if (!existsSync10(path)) return void 0;
2664
+ if (!existsSync11(path)) return void 0;
1706
2665
  try {
1707
- return JSON.parse(readFileSync6(path, "utf8"));
2666
+ return JSON.parse(readFileSync7(path, "utf8"));
1708
2667
  } catch {
1709
2668
  return void 0;
1710
2669
  }
1711
2670
  }
1712
2671
  function textFileIncludes(path, needles) {
1713
- if (!existsSync10(path)) return false;
1714
- const contents = readFileSync6(path, "utf8").toLowerCase();
2672
+ if (!existsSync11(path)) return false;
2673
+ const contents = readFileSync7(path, "utf8").toLowerCase();
1715
2674
  return needles.some((needle) => contents.includes(needle.toLowerCase()));
1716
2675
  }
1717
2676
  function hasBookChildDirectory(targetDir, childName) {
1718
- const booksDir = join10(targetDir, "books");
1719
- if (!existsSync10(booksDir)) return false;
2677
+ const booksDir = join11(targetDir, "books");
2678
+ if (!existsSync11(booksDir)) return false;
1720
2679
  try {
1721
- return readdirSync6(booksDir, { withFileTypes: true }).some(
1722
- (entry) => entry.isDirectory() && existsSync10(join10(booksDir, entry.name, childName))
2680
+ return readdirSync7(booksDir, { withFileTypes: true }).some(
2681
+ (entry) => entry.isDirectory() && existsSync11(join11(booksDir, entry.name, childName))
1723
2682
  );
1724
2683
  } catch {
1725
2684
  return false;
@@ -1753,9 +2712,67 @@ function runAssets(args) {
1753
2712
  if (subcommand2 === "npx") {
1754
2713
  return runAssetsNpx(rest);
1755
2714
  }
2715
+ if (subcommand2 === "catalog") {
2716
+ return runAssetsCatalog(rest);
2717
+ }
1756
2718
  printUsage();
1757
2719
  return 1;
1758
2720
  }
2721
+ function runAssetsCatalog(args) {
2722
+ const [operation, ...rest] = args;
2723
+ if (!operation || operation === "--help" || operation === "-h") {
2724
+ printCatalogUsage();
2725
+ return operation ? 0 : 1;
2726
+ }
2727
+ if (operation !== "build" && operation !== "check") {
2728
+ printCatalogUsage();
2729
+ return 1;
2730
+ }
2731
+ const options = parseCatalogOptions(operation, rest);
2732
+ if (!options.ok) {
2733
+ console.error(options.error);
2734
+ printCatalogUsage();
2735
+ return 1;
2736
+ }
2737
+ try {
2738
+ const loaded = loadAgentAssetRegistry(
2739
+ options.value.agentAssetsDir ? { agentAssetsDir: options.value.agentAssetsDir } : void 0
2740
+ );
2741
+ if (loaded.issues.length > 0) {
2742
+ for (const issue of loaded.issues) console.error(`${issue.type}: ${issue.message}`);
2743
+ return 1;
2744
+ }
2745
+ const catalogRoot = options.value.catalogRoot ?? join12(loaded.agentAssetsDir, "#catalogs");
2746
+ const common = {
2747
+ agentAssetsDir: loaded.agentAssetsDir,
2748
+ catalogRoot,
2749
+ registry: loaded.registry,
2750
+ bundles: loadAgentAssetBundles(loaded.agentAssetsDir)
2751
+ };
2752
+ if (operation === "check") {
2753
+ const result2 = checkAssetCatalog(common);
2754
+ if (options.value.json) console.log(JSON.stringify(result2, null, 2));
2755
+ else if (result2.issues.length === 0)
2756
+ console.log(`asset catalog check passed (${result2.assetCount} assets)`);
2757
+ else for (const issue of result2.issues) console.log(issue);
2758
+ return result2.issues.length === 0 ? 0 : 1;
2759
+ }
2760
+ const result = buildAssetCatalog({ ...common, nativeLinks: options.value.nativeLinks });
2761
+ if (options.value.json) console.log(JSON.stringify(result, null, 2));
2762
+ else {
2763
+ console.log(`catalog: ${result.catalogRoot}`);
2764
+ console.log(`assets: ${result.assetCount}`);
2765
+ console.log(`topics: ${result.topicCount}`);
2766
+ console.log(`portable-files: ${result.portableFileCount}`);
2767
+ console.log(`native-links: ${result.nativeLinkCount}`);
2768
+ for (const warning of result.warnings) console.warn(`warning: ${warning}`);
2769
+ }
2770
+ return 0;
2771
+ } catch (error) {
2772
+ console.error(error instanceof Error ? error.message : String(error));
2773
+ return 1;
2774
+ }
2775
+ }
1759
2776
  function runAssetsPublicCheck(args) {
1760
2777
  const options = parsePublicCheckOptions(args);
1761
2778
  if (!options.ok) {
@@ -1835,7 +2852,7 @@ function runAssetsApply(args) {
1835
2852
  return 1;
1836
2853
  }
1837
2854
  try {
1838
- const plan = JSON.parse(readFileSync7(options.value.planPath, "utf8"));
2855
+ const plan = JSON.parse(readFileSync8(options.value.planPath, "utf8"));
1839
2856
  const result = applyAssetInstallPlan(plan);
1840
2857
  console.log(`applied-actions: ${result.appliedActions.length}`);
1841
2858
  return 0;
@@ -1960,8 +2977,8 @@ function runAssetsPlan(args) {
1960
2977
  console.log("dry-run: true");
1961
2978
  }
1962
2979
  if (options.value.outPath) {
1963
- mkdirSync3(dirname5(options.value.outPath), { recursive: true });
1964
- writeFileSync2(options.value.outPath, `${JSON.stringify(plan, null, 2)}
2980
+ mkdirSync4(dirname6(options.value.outPath), { recursive: true });
2981
+ writeFileSync3(options.value.outPath, `${JSON.stringify(plan, null, 2)}
1965
2982
  `);
1966
2983
  if (!options.value.json) {
1967
2984
  console.log(`plan: ${options.value.outPath}`);
@@ -1973,6 +2990,33 @@ function runAssetsPlan(args) {
1973
2990
  return 1;
1974
2991
  }
1975
2992
  }
2993
+ function parseCatalogOptions(operation, args) {
2994
+ const options = { nativeLinks: false, json: false };
2995
+ for (let index = 0; index < args.length; index += 1) {
2996
+ const arg = args[index];
2997
+ if (arg === "--agent-assets") {
2998
+ const value = args[index + 1];
2999
+ if (!value) return { ok: false, error: "Expected --agent-assets <path>" };
3000
+ options.agentAssetsDir = value;
3001
+ index += 1;
3002
+ } else if (arg === "--catalog-root") {
3003
+ const value = args[index + 1];
3004
+ if (!value) return { ok: false, error: "Expected --catalog-root <path>" };
3005
+ options.catalogRoot = value;
3006
+ index += 1;
3007
+ } else if (arg === "--native-links") {
3008
+ if (operation !== "build") {
3009
+ return { ok: false, error: "--native-links is only valid with assets catalog build" };
3010
+ }
3011
+ options.nativeLinks = true;
3012
+ } else if (arg === "--json") {
3013
+ options.json = true;
3014
+ } else {
3015
+ return { ok: false, error: `Unknown assets catalog option: ${arg}` };
3016
+ }
3017
+ }
3018
+ return { ok: true, value: options };
3019
+ }
1976
3020
  function parseListOptions(args) {
1977
3021
  const options = {
1978
3022
  json: false,
@@ -2159,10 +3203,10 @@ function parsePublicCheckOptions(args) {
2159
3203
  return { ok: false, error: `Unknown assets public-check option: ${arg}` };
2160
3204
  }
2161
3205
  }
2162
- if (!existsSync11(options.publicRoot)) {
3206
+ if (!existsSync12(options.publicRoot)) {
2163
3207
  return { ok: false, error: `Public agent assets root does not exist: ${options.publicRoot}` };
2164
3208
  }
2165
- if (!existsSync11(options.privateRoot)) {
3209
+ if (!existsSync12(options.privateRoot)) {
2166
3210
  return { ok: false, error: `Private agent assets root does not exist: ${options.privateRoot}` };
2167
3211
  }
2168
3212
  return { ok: true, value: options };
@@ -2171,13 +3215,13 @@ function getDefaultPublicCheckRoots() {
2171
3215
  const defaultRegistryRoot = loadAgentAssetRegistry().agentAssetsDir;
2172
3216
  if (defaultRegistryRoot.endsWith("public-agent-assets")) {
2173
3217
  return {
2174
- privateRoot: join11(dirname5(defaultRegistryRoot), "agent-assets"),
3218
+ privateRoot: join12(dirname6(defaultRegistryRoot), "agent-assets"),
2175
3219
  publicRoot: defaultRegistryRoot
2176
3220
  };
2177
3221
  }
2178
3222
  return {
2179
3223
  privateRoot: defaultRegistryRoot,
2180
- publicRoot: join11(dirname5(defaultRegistryRoot), "public-agent-assets")
3224
+ publicRoot: join12(dirname6(defaultRegistryRoot), "public-agent-assets")
2181
3225
  };
2182
3226
  }
2183
3227
  function listRegistryAssets(options) {
@@ -2238,6 +3282,9 @@ function printUsage() {
2238
3282
  );
2239
3283
  console.error(" pro-gov assets npx add <source> [--skill <name>] --plan [--root <path>]");
2240
3284
  console.error(" pro-gov assets npx update [--skill <name>] --plan [--root <path>]");
3285
+ console.error(
3286
+ " pro-gov assets catalog build|check [--agent-assets <path>] [--catalog-root <path>] [--native-links] [--json]"
3287
+ );
2241
3288
  }
2242
3289
  function printNpxUsage() {
2243
3290
  console.log("Usage: pro-gov assets npx add <source> [--skill <name>] --plan [--root <path>]");
@@ -2245,11 +3292,19 @@ function printNpxUsage() {
2245
3292
  console.log("");
2246
3293
  console.log("Runs npx skills only in a temporary copy and prints a reviewable plan.");
2247
3294
  }
3295
+ function printCatalogUsage() {
3296
+ console.log(
3297
+ "Usage: pro-gov assets catalog build [--agent-assets <path>] [--catalog-root <path>] [--native-links] [--json]"
3298
+ );
3299
+ console.log(
3300
+ "Usage: pro-gov assets catalog check [--agent-assets <path>] [--catalog-root <path>] [--json]"
3301
+ );
3302
+ }
2248
3303
 
2249
3304
  // src/commands/doctor.ts
2250
- import { existsSync as existsSync12 } from "node:fs";
3305
+ import { existsSync as existsSync13 } from "node:fs";
2251
3306
  import { createRequire } from "node:module";
2252
- import { dirname as dirname6, join as join12 } from "node:path";
3307
+ import { dirname as dirname7, join as join13 } from "node:path";
2253
3308
  var REQUIRED_ASSETS = [
2254
3309
  "starter/.agents/skills/.gitkeep",
2255
3310
  "starter/AGENTS.template.md",
@@ -2314,16 +3369,16 @@ function resolveDocGovDependencyCli() {
2314
3369
  try {
2315
3370
  const require2 = createRequire(import.meta.url);
2316
3371
  const packageJsonPath = require2.resolve("@pieai/doc-gov/package.json");
2317
- const cliPath = join12(dirname6(packageJsonPath), "dist/cli.js");
2318
- return existsSync12(cliPath) ? cliPath : null;
3372
+ const cliPath = join13(dirname7(packageJsonPath), "dist/cli.js");
3373
+ return existsSync13(cliPath) ? cliPath : null;
2319
3374
  } catch {
2320
3375
  return null;
2321
3376
  }
2322
3377
  }
2323
3378
 
2324
3379
  // 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";
3380
+ import { lstatSync as lstatSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync9, symlinkSync as symlinkSync3, writeFileSync as writeFileSync4 } from "node:fs";
3381
+ import { basename as basename3, dirname as dirname8, join as join14 } from "node:path";
2327
3382
 
2328
3383
  // src/commands/shared.ts
2329
3384
  function planStarterFiles(profile) {
@@ -2410,7 +3465,7 @@ function runInit(args) {
2410
3465
  function applyStarterFiles(files, profile) {
2411
3466
  const root = process.cwd();
2412
3467
  const conflicts = files.filter((file) => {
2413
- const targetPath = join13(root, file.targetPath);
3468
+ const targetPath = join14(root, file.targetPath);
2414
3469
  const stat = safeLstat(targetPath);
2415
3470
  if (!stat) return false;
2416
3471
  return file.kind !== "directory" || !stat.isDirectory();
@@ -2422,19 +3477,19 @@ function applyStarterFiles(files, profile) {
2422
3477
  return 1;
2423
3478
  }
2424
3479
  for (const file of files) {
2425
- const targetPath = join13(root, file.targetPath);
2426
- mkdirSync4(dirname7(targetPath), { recursive: true });
3480
+ const targetPath = join14(root, file.targetPath);
3481
+ mkdirSync5(dirname8(targetPath), { recursive: true });
2427
3482
  if (file.kind === "directory") {
2428
- mkdirSync4(targetPath, { recursive: true });
3483
+ mkdirSync5(targetPath, { recursive: true });
2429
3484
  continue;
2430
3485
  }
2431
3486
  if (file.kind === "symlink") {
2432
- symlinkSync2(file.linkTarget, targetPath);
3487
+ symlinkSync3(file.linkTarget, targetPath);
2433
3488
  continue;
2434
3489
  }
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);
3490
+ const source = readFileSync9(file.absoluteSourcePath);
3491
+ const content = file.targetPath === "AGENTS.md" ? renderAgentsTemplate(source.toString("utf8"), basename3(root), profile) : source;
3492
+ writeFileSync4(targetPath, content);
2438
3493
  }
2439
3494
  console.log("pro-gov init APPLIED");
2440
3495
  console.log(`profile: ${profile}`);
@@ -2451,7 +3506,7 @@ function renderAgentsTemplate(template, projectName, profile) {
2451
3506
  }
2452
3507
  function safeLstat(path) {
2453
3508
  try {
2454
- return lstatSync5(path);
3509
+ return lstatSync6(path);
2455
3510
  } catch {
2456
3511
  return void 0;
2457
3512
  }
@@ -2465,22 +3520,22 @@ function readFlag(args, flag) {
2465
3520
  }
2466
3521
 
2467
3522
  // 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";
3523
+ import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "node:fs";
3524
+ import { dirname as dirname11, resolve as resolve6 } from "node:path";
2470
3525
 
2471
3526
  // src/host-lens.ts
2472
3527
  import { execFileSync } from "node:child_process";
2473
3528
  import {
2474
3529
  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
3530
+ existsSync as existsSync14,
3531
+ lstatSync as lstatSync7,
3532
+ mkdirSync as mkdirSync6,
3533
+ readdirSync as readdirSync8,
3534
+ statSync as statSync5,
3535
+ writeFileSync as writeFileSync5
2481
3536
  } from "node:fs";
2482
3537
  import { homedir } from "node:os";
2483
- import { dirname as dirname8, join as join14, relative as relative6, resolve as resolve3 } from "node:path";
3538
+ import { dirname as dirname9, join as join15, relative as relative7, resolve as resolve4 } from "node:path";
2484
3539
  import { fileURLToPath as fileURLToPath3 } from "node:url";
2485
3540
  var ROOT_DEFINITIONS = [
2486
3541
  {
@@ -2562,9 +3617,9 @@ var ROOT_DEFINITIONS = [
2562
3617
  }
2563
3618
  ];
2564
3619
  function inspectHost(options = {}) {
2565
- const homePath = resolve3(options.homeDir ?? homedir());
3620
+ const homePath = resolve4(options.homeDir ?? homedir());
2566
3621
  const now = options.now ?? /* @__PURE__ */ new Date();
2567
- const rootPaths = ROOT_DEFINITIONS.map((definition) => join14(homePath, definition.relativePath));
3622
+ const rootPaths = ROOT_DEFINITIONS.map((definition) => join15(homePath, definition.relativePath));
2568
3623
  const rootSizes = measurePaths(rootPaths);
2569
3624
  const findings = inspectFindings(homePath);
2570
3625
  const rootFindingIds = /* @__PURE__ */ new Map();
@@ -2581,7 +3636,7 @@ function inspectHost(options = {}) {
2581
3636
  label: definition.label,
2582
3637
  path: displayPath(path, homePath),
2583
3638
  kind: definition.kind,
2584
- exists: existsSync13(path),
3639
+ exists: existsSync14(path),
2585
3640
  bytes: rootSizes.get(path) ?? 0,
2586
3641
  status: rootFindings.length > 0 ? "attention" : "healthy",
2587
3642
  note: definition.note
@@ -2688,25 +3743,25 @@ function createHostLensCleanupPlan(report) {
2688
3743
  };
2689
3744
  }
2690
3745
  function writeHostLensReport(report, outDir) {
2691
- mkdirSync5(outDir, { recursive: true });
3746
+ mkdirSync6(outDir, { recursive: true });
2692
3747
  const assets = findHostDashboardAssets();
2693
3748
  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));
3749
+ const source = join15(assets, file);
3750
+ if (!existsSync14(source)) throw new Error(`HostLens dashboard asset is missing: ${source}`);
3751
+ cpSync2(source, join15(outDir, file));
2697
3752
  }
2698
- const jsonPath = join14(outDir, "host-lens.json");
2699
- const htmlPath = join14(outDir, "index.html");
2700
- writeFileSync4(jsonPath, `${JSON.stringify(report, null, 2)}
3753
+ const jsonPath = join15(outDir, "host-lens.json");
3754
+ const htmlPath = join15(outDir, "index.html");
3755
+ writeFileSync5(jsonPath, `${JSON.stringify(report, null, 2)}
2701
3756
  `);
2702
- writeFileSync4(join14(outDir, "data.js"), `window.__HOST_LENS__ = ${safeJavaScriptJson(report)};
3757
+ writeFileSync5(join15(outDir, "data.js"), `window.__HOST_LENS__ = ${safeJavaScriptJson(report)};
2703
3758
  `);
2704
3759
  return { jsonPath, htmlPath };
2705
3760
  }
2706
3761
  function inspectFindings(homePath) {
2707
3762
  const candidates = [];
2708
3763
  const addGroup = (value) => {
2709
- const paths = value.paths.filter((path) => existsSync13(path));
3764
+ const paths = value.paths.filter((path) => existsSync14(path));
2710
3765
  if (paths.length === 0) return;
2711
3766
  candidates.push({ ...value, paths: paths.map((path) => displayPath(path, homePath)) });
2712
3767
  };
@@ -2714,7 +3769,7 @@ function inspectFindings(homePath) {
2714
3769
  id: "codex-backups",
2715
3770
  rootId: "codex",
2716
3771
  label: "Codex \u5386\u53F2\u5907\u4EFD",
2717
- paths: matchingChildren(join14(homePath, ".codex"), (name) => /^backup-/i.test(name)),
3772
+ paths: matchingChildren(join15(homePath, ".codex"), (name) => /^backup-/i.test(name)),
2718
3773
  category: "backup",
2719
3774
  confidence: "medium",
2720
3775
  disposition: "manual-review",
@@ -2725,7 +3780,7 @@ function inspectFindings(homePath) {
2725
3780
  id: "codex-cache",
2726
3781
  rootId: "codex",
2727
3782
  label: "Codex \u53EF\u518D\u751F\u6210\u7F13\u5B58",
2728
- paths: [join14(homePath, ".codex/cache")],
3783
+ paths: [join15(homePath, ".codex/cache")],
2729
3784
  category: "cache",
2730
3785
  confidence: "high",
2731
3786
  disposition: "report-only",
@@ -2736,7 +3791,7 @@ function inspectFindings(homePath) {
2736
3791
  id: "claude-temporary",
2737
3792
  rootId: "claude-code",
2738
3793
  label: "Claude Code \u4E34\u65F6\u76EE\u5F55",
2739
- paths: matchingChildren(join14(homePath, ".claude"), (name) => /^(temp|tmp)[-_]/i.test(name)),
3794
+ paths: matchingChildren(join15(homePath, ".claude"), (name) => /^(temp|tmp)[-_]/i.test(name)),
2740
3795
  category: "temporary",
2741
3796
  confidence: "high",
2742
3797
  disposition: "manual-review",
@@ -2747,7 +3802,7 @@ function inspectFindings(homePath) {
2747
3802
  id: "claude-cache",
2748
3803
  rootId: "claude-code",
2749
3804
  label: "Claude Code \u7F13\u5B58",
2750
- paths: [join14(homePath, ".claude/cache")],
3805
+ paths: [join15(homePath, ".claude/cache")],
2751
3806
  category: "cache",
2752
3807
  confidence: "high",
2753
3808
  disposition: "report-only",
@@ -2758,7 +3813,7 @@ function inspectFindings(homePath) {
2758
3813
  id: "copilot-session-history",
2759
3814
  rootId: "copilot",
2760
3815
  label: "Copilot \u4F1A\u8BDD\u72B6\u6001",
2761
- paths: [join14(homePath, ".copilot/session-state")],
3816
+ paths: [join15(homePath, ".copilot/session-state")],
2762
3817
  category: "session-history",
2763
3818
  confidence: "low",
2764
3819
  disposition: "manual-review",
@@ -2769,7 +3824,7 @@ function inspectFindings(homePath) {
2769
3824
  id: "npm-npx-cache",
2770
3825
  rootId: "npm-cache",
2771
3826
  label: "npx \u4E34\u65F6\u5B89\u88C5\u7F13\u5B58",
2772
- paths: [join14(homePath, ".npm/_npx")],
3827
+ paths: [join15(homePath, ".npm/_npx")],
2773
3828
  category: "cache",
2774
3829
  confidence: "high",
2775
3830
  disposition: "native-tool",
@@ -2781,7 +3836,7 @@ function inspectFindings(homePath) {
2781
3836
  id: "npm-content-cache",
2782
3837
  rootId: "npm-cache",
2783
3838
  label: "npm \u5185\u5BB9\u7F13\u5B58",
2784
- paths: [join14(homePath, ".npm/_cacache")],
3839
+ paths: [join15(homePath, ".npm/_cacache")],
2785
3840
  category: "cache",
2786
3841
  confidence: "high",
2787
3842
  disposition: "native-tool",
@@ -2794,7 +3849,7 @@ function inspectFindings(homePath) {
2794
3849
  rootId: "pnpm-store",
2795
3850
  label: "pnpm \u672A\u5F15\u7528\u5305\u5019\u9009",
2796
3851
  paths: [
2797
- join14(
3852
+ join15(
2798
3853
  homePath,
2799
3854
  process.platform === "win32" ? "AppData/Local/pnpm/store" : "Library/pnpm/store"
2800
3855
  )
@@ -2811,11 +3866,11 @@ function inspectFindings(homePath) {
2811
3866
  rootId: "playwright",
2812
3867
  label: "Playwright \u6D4F\u89C8\u5668\u7248\u672C",
2813
3868
  paths: [
2814
- join14(
3869
+ join15(
2815
3870
  homePath,
2816
3871
  process.platform === "win32" ? "AppData/Local/ms-playwright" : "Library/Caches/ms-playwright"
2817
3872
  ),
2818
- join14(homePath, ".cache/ms-playwright")
3873
+ join15(homePath, ".cache/ms-playwright")
2819
3874
  ],
2820
3875
  category: "duplicate-runtime",
2821
3876
  confidence: "medium",
@@ -2867,16 +3922,16 @@ function buildProtections(homePath) {
2867
3922
  }));
2868
3923
  }
2869
3924
  function matchingChildren(root, predicate) {
2870
- if (!existsSync13(root)) return [];
3925
+ if (!existsSync14(root)) return [];
2871
3926
  try {
2872
- return readdirSync7(root, { withFileTypes: true }).filter((entry) => predicate(entry.name)).map((entry) => join14(root, entry.name));
3927
+ return readdirSync8(root, { withFileTypes: true }).filter((entry) => predicate(entry.name)).map((entry) => join15(root, entry.name));
2873
3928
  } catch {
2874
3929
  return [];
2875
3930
  }
2876
3931
  }
2877
3932
  function measurePaths(paths) {
2878
- const uniquePaths = [...new Set(paths.map((path) => resolve3(path)))].filter(
2879
- (path) => existsSync13(path)
3933
+ const uniquePaths = [...new Set(paths.map((path) => resolve4(path)))].filter(
3934
+ (path) => existsSync14(path)
2880
3935
  );
2881
3936
  const result = /* @__PURE__ */ new Map();
2882
3937
  if (uniquePaths.length === 0) return result;
@@ -2889,7 +3944,7 @@ function measurePaths(paths) {
2889
3944
  for (const line of output.split(/\r?\n/)) {
2890
3945
  const match = line.match(/^(\d+)\s+(.+)$/);
2891
3946
  if (!match) continue;
2892
- result.set(resolve3(match[2]), Number(match[1]) * 1024);
3947
+ result.set(resolve4(match[2]), Number(match[1]) * 1024);
2893
3948
  }
2894
3949
  return result;
2895
3950
  } catch {
@@ -2899,7 +3954,7 @@ function measurePaths(paths) {
2899
3954
  }
2900
3955
  function fallbackMeasure(root) {
2901
3956
  try {
2902
- const rootStats = lstatSync6(root);
3957
+ const rootStats = lstatSync7(root);
2903
3958
  if (!rootStats.isDirectory()) return rootStats.size;
2904
3959
  } catch {
2905
3960
  return 0;
@@ -2912,17 +3967,17 @@ function fallbackMeasure(root) {
2912
3967
  if (!current) continue;
2913
3968
  let entries;
2914
3969
  try {
2915
- entries = readdirSync7(current, { withFileTypes: true });
3970
+ entries = readdirSync8(current, { withFileTypes: true });
2916
3971
  } catch {
2917
3972
  continue;
2918
3973
  }
2919
3974
  for (const entry of entries) {
2920
3975
  visited += 1;
2921
- const path = join14(current, entry.name);
3976
+ const path = join15(current, entry.name);
2922
3977
  if (entry.isDirectory()) pending.push(path);
2923
3978
  else if (entry.isFile()) {
2924
3979
  try {
2925
- bytes += statSync4(path).size;
3980
+ bytes += statSync5(path).size;
2926
3981
  } catch {
2927
3982
  }
2928
3983
  }
@@ -2933,35 +3988,35 @@ function fallbackMeasure(root) {
2933
3988
  }
2934
3989
  function countImmediateEntries(path) {
2935
3990
  try {
2936
- return lstatSync6(path).isDirectory() ? readdirSync7(path).length : 1;
3991
+ return lstatSync7(path).isDirectory() ? readdirSync8(path).length : 1;
2937
3992
  } catch {
2938
3993
  return 0;
2939
3994
  }
2940
3995
  }
2941
3996
  function displayPath(path, homePath) {
2942
- const absolute = resolve3(path);
2943
- const withinHome = relative6(homePath, absolute);
3997
+ const absolute = resolve4(path);
3998
+ const withinHome = relative7(homePath, absolute);
2944
3999
  if (withinHome === "") return "~";
2945
4000
  if (!withinHome.startsWith("..")) return `~/${withinHome.replaceAll("\\", "/")}`;
2946
4001
  return absolute;
2947
4002
  }
2948
4003
  function undisplayPath(path, homePath) {
2949
4004
  if (path === "~") return homePath;
2950
- if (path.startsWith("~/")) return join14(homePath, path.slice(2));
2951
- return resolve3(path);
4005
+ if (path.startsWith("~/")) return join15(homePath, path.slice(2));
4006
+ return resolve4(path);
2952
4007
  }
2953
4008
  function findHostDashboardAssets() {
2954
- const packageRoot2 = dirname8(dirname8(fileURLToPath3(import.meta.url)));
4009
+ const packageRoot2 = dirname9(dirname9(fileURLToPath3(import.meta.url)));
2955
4010
  const candidates = [
2956
4011
  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")
4012
+ join15(packageRoot2, ".host-dashboard-build"),
4013
+ join15(packageRoot2, "assets/host-dashboard"),
4014
+ join15(process.cwd(), ".host-dashboard-build"),
4015
+ join15(process.cwd(), "assets/host-dashboard"),
4016
+ join15(process.cwd(), "packages/pro-gov/.host-dashboard-build"),
4017
+ join15(process.cwd(), "packages/pro-gov/assets/host-dashboard")
2963
4018
  ].filter((value) => Boolean(value));
2964
- const match = candidates.find((path) => existsSync13(join14(path, "index.html")));
4019
+ const match = candidates.find((path) => existsSync14(join15(path, "index.html")));
2965
4020
  if (!match)
2966
4021
  throw new Error(
2967
4022
  "HostLens dashboard assets were not built. Run pnpm --filter @pieai/pro-gov build."
@@ -2973,12 +4028,12 @@ function safeJavaScriptJson(value) {
2973
4028
  }
2974
4029
 
2975
4030
  // 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";
4031
+ import { existsSync as existsSync15, readFileSync as readFileSync10 } from "node:fs";
4032
+ import { dirname as dirname10, isAbsolute as isAbsolute4, resolve as resolve5 } from "node:path";
2978
4033
  function loadPortfolioManifest(configPath) {
2979
4034
  let parsed;
2980
4035
  try {
2981
- parsed = JSON.parse(readFileSync9(configPath, "utf8"));
4036
+ parsed = JSON.parse(readFileSync10(configPath, "utf8"));
2982
4037
  } catch (error) {
2983
4038
  return {
2984
4039
  configPath,
@@ -2990,7 +4045,7 @@ function loadPortfolioManifest(configPath) {
2990
4045
  ]
2991
4046
  };
2992
4047
  }
2993
- const normalized = resolveManifestPaths(parsed, dirname9(resolve4(configPath)));
4048
+ const normalized = resolveManifestPaths(parsed, dirname10(resolve5(configPath)));
2994
4049
  const issues = validatePortfolioManifest(normalized);
2995
4050
  return {
2996
4051
  configPath,
@@ -3001,16 +4056,16 @@ function loadPortfolioManifest(configPath) {
3001
4056
  function resolveManifestPaths(value, configDir) {
3002
4057
  if (!isRecord(value)) return value;
3003
4058
  const resolveEndpoint = (endpoint) => {
3004
- if (!isRecord(endpoint) || typeof endpoint.path !== "string" || isAbsolute3(endpoint.path)) {
4059
+ if (!isRecord(endpoint) || typeof endpoint.path !== "string" || isAbsolute4(endpoint.path)) {
3005
4060
  return endpoint;
3006
4061
  }
3007
- return { ...endpoint, path: resolve4(configDir, endpoint.path) };
4062
+ return { ...endpoint, path: resolve5(configDir, endpoint.path) };
3008
4063
  };
3009
4064
  return {
3010
4065
  ...value,
3011
- technologyGovernance: isRecord(value.technologyGovernance) && typeof value.technologyGovernance.strategySource === "string" && !isAbsolute3(value.technologyGovernance.strategySource) ? {
4066
+ technologyGovernance: isRecord(value.technologyGovernance) && typeof value.technologyGovernance.strategySource === "string" && !isAbsolute4(value.technologyGovernance.strategySource) ? {
3012
4067
  ...value.technologyGovernance,
3013
- strategySource: resolve4(configDir, value.technologyGovernance.strategySource)
4068
+ strategySource: resolve5(configDir, value.technologyGovernance.strategySource)
3014
4069
  } : value.technologyGovernance,
3015
4070
  controlPlane: resolveEndpoint(value.controlPlane),
3016
4071
  executionEngine: resolveEndpoint(value.executionEngine),
@@ -3195,7 +4250,7 @@ function validateEndpoint(value, field, issues, technologyCatalog) {
3195
4250
  });
3196
4251
  return;
3197
4252
  }
3198
- if (!existsSync14(value.path)) {
4253
+ if (!existsSync15(value.path)) {
3199
4254
  issues.push({
3200
4255
  type: "missing-path",
3201
4256
  id: typeof value.id === "string" ? value.id : void 0,
@@ -3626,13 +4681,13 @@ function isExactVersion(value) {
3626
4681
  return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(value);
3627
4682
  }
3628
4683
  function isRepositoryRelativePath(value) {
3629
- if (value.length === 0 || isAbsolute3(value)) return false;
4684
+ if (value.length === 0 || isAbsolute4(value)) return false;
3630
4685
  const segments = value.replaceAll("\\", "/").split("/");
3631
4686
  return !segments.includes("..");
3632
4687
  }
3633
4688
  function isExactRepositoryRelativePath(value) {
3634
4689
  if (value.length === 0) return false;
3635
- if (isAbsolute3(value)) return false;
4690
+ if (isAbsolute4(value)) return false;
3636
4691
  if (/^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\") || value.startsWith("//"))
3637
4692
  return false;
3638
4693
  if (value.includes("\\")) return false;
@@ -3930,7 +4985,7 @@ function runScan(options) {
3930
4985
  const inspection = resolveInspectionOptions(options);
3931
4986
  if (!inspection.ok) return reportConfigError(inspection.error);
3932
4987
  const report = inspectHost(inspection.value);
3933
- const written = writeHostLensReport(report, resolve5(options.outPath));
4988
+ const written = writeHostLensReport(report, resolve6(options.outPath));
3934
4989
  if (options.json) {
3935
4990
  console.log(JSON.stringify({ ok: true, ...written, summary: report.summary }, null, 2));
3936
4991
  } else {
@@ -3950,9 +5005,9 @@ function runPlan(options) {
3950
5005
  if (!inspection.ok) return reportConfigError(inspection.error);
3951
5006
  const report = inspectHost(inspection.value);
3952
5007
  const plan = createHostLensCleanupPlan(report);
3953
- const outPath = resolve5(options.outPath);
3954
- mkdirSync6(dirname10(outPath), { recursive: true });
3955
- writeFileSync5(outPath, `${JSON.stringify(plan, null, 2)}
5008
+ const outPath = resolve6(options.outPath);
5009
+ mkdirSync7(dirname11(outPath), { recursive: true });
5010
+ writeFileSync6(outPath, `${JSON.stringify(plan, null, 2)}
3956
5011
  `);
3957
5012
  if (options.json)
3958
5013
  console.log(JSON.stringify({ ok: true, outPath, actions: plan.actions.length }, null, 2));
@@ -4025,7 +5080,7 @@ function parseOptions(args) {
4025
5080
  if (arg === "--home") {
4026
5081
  const value = args[index + 1];
4027
5082
  if (!value) return { ok: false, error: "Expected value after --home" };
4028
- options.homeDir = resolve5(value);
5083
+ options.homeDir = resolve6(value);
4029
5084
  index += 1;
4030
5085
  continue;
4031
5086
  }
@@ -4039,7 +5094,7 @@ function parseOptions(args) {
4039
5094
  if (arg === "--config") {
4040
5095
  const value = args[index + 1];
4041
5096
  if (!value) return { ok: false, error: "Expected value after --config" };
4042
- options.configPath = resolve5(value);
5097
+ options.configPath = resolve6(value);
4043
5098
  index += 1;
4044
5099
  continue;
4045
5100
  }
@@ -4065,8 +5120,8 @@ function printUsage2() {
4065
5120
  }
4066
5121
 
4067
5122
  // 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";
5123
+ import { existsSync as existsSync16, readdirSync as readdirSync9, readFileSync as readFileSync11 } from "node:fs";
5124
+ import { basename as basename4, join as join16, relative as relative8 } from "node:path";
4070
5125
  function recallLearnings(root, options) {
4071
5126
  const query = options.query.trim();
4072
5127
  const terms = tokenize(query);
@@ -4089,16 +5144,16 @@ function recallLearnings(root, options) {
4089
5144
  function loadLearningRecords(root) {
4090
5145
  const recordsByTitle = /* @__PURE__ */ new Map();
4091
5146
  for (const relativeDir of ["docs/reference/learnings", "docs/solutions"]) {
4092
- const learningDir = join15(root, relativeDir);
4093
- if (!existsSync15(learningDir)) continue;
5147
+ const learningDir = join16(root, relativeDir);
5148
+ if (!existsSync16(learningDir)) continue;
4094
5149
  for (const path of listMarkdownFiles(learningDir)) {
4095
5150
  const record = readLearningRecord(root, path);
4096
5151
  const key = record.title.trim().toLowerCase();
4097
5152
  if (!recordsByTitle.has(key)) recordsByTitle.set(key, record);
4098
5153
  }
4099
5154
  }
4100
- const conceptsPath = join15(root, "CONCEPTS.md");
4101
- if (existsSync15(conceptsPath)) {
5155
+ const conceptsPath = join16(root, "CONCEPTS.md");
5156
+ if (existsSync16(conceptsPath)) {
4102
5157
  const record = readLearningRecord(root, conceptsPath);
4103
5158
  recordsByTitle.set(`concepts:${record.title.toLowerCase()}`, record);
4104
5159
  }
@@ -4106,8 +5161,8 @@ function loadLearningRecords(root) {
4106
5161
  }
4107
5162
  function listMarkdownFiles(dir) {
4108
5163
  const files = [];
4109
- for (const entry of readdirSync8(dir, { withFileTypes: true })) {
4110
- const absolutePath = join15(dir, entry.name);
5164
+ for (const entry of readdirSync9(dir, { withFileTypes: true })) {
5165
+ const absolutePath = join16(dir, entry.name);
4111
5166
  if (entry.isDirectory()) {
4112
5167
  files.push(...listMarkdownFiles(absolutePath));
4113
5168
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -4117,11 +5172,11 @@ function listMarkdownFiles(dir) {
4117
5172
  return files.sort();
4118
5173
  }
4119
5174
  function readLearningRecord(root, absolutePath) {
4120
- const content = readFileSync10(absolutePath, "utf8");
5175
+ const content = readFileSync11(absolutePath, "utf8");
4121
5176
  const parsed = splitFrontmatter(content);
4122
5177
  const body = parsed.body;
4123
5178
  return {
4124
- relativePath: normalizePath(relative7(root, absolutePath)),
5179
+ relativePath: normalizePath(relative8(root, absolutePath)),
4125
5180
  title: findTitle(parsed.frontmatter, body) ?? titleFromPath(absolutePath),
4126
5181
  metadata: parsed.frontmatter,
4127
5182
  body
@@ -4149,7 +5204,7 @@ function findTitle(frontmatter, body) {
4149
5204
  return heading ? heading.slice(2).trim() : void 0;
4150
5205
  }
4151
5206
  function titleFromPath(path) {
4152
- return basename3(path, ".md").split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
5207
+ return basename4(path, ".md").split(/[-_]/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
4153
5208
  }
4154
5209
  function scoreRecord(record, terms) {
4155
5210
  const title = record.title.toLowerCase();
@@ -4202,8 +5257,8 @@ function cleanMarkdownLine(input) {
4202
5257
  }
4203
5258
 
4204
5259
  // 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";
5260
+ import { existsSync as existsSync17, mkdirSync as mkdirSync8, writeFileSync as writeFileSync7 } from "node:fs";
5261
+ import { basename as basename5, join as join17, relative as relative9 } from "node:path";
4207
5262
  function captureLearning(root, options) {
4208
5263
  const title = options.title.trim();
4209
5264
  const summary = options.summary.trim();
@@ -4211,13 +5266,13 @@ function captureLearning(root, options) {
4211
5266
  if (!summary) throw new Error("summary is required");
4212
5267
  const category = slugify(options.category ?? "workflow-issues") || "workflow-issues";
4213
5268
  const moduleName = options.module?.trim() || "PGS learning capture";
4214
- const dir = join16(root, "docs/reference/learnings", category);
4215
- mkdirSync7(dir, { recursive: true });
5269
+ const dir = join17(root, "docs/reference/learnings", category);
5270
+ mkdirSync8(dir, { recursive: true });
4216
5271
  const path = uniquePath(dir, slugify(title) || "learning");
4217
- const idSlug = basename4(path, ".md");
4218
- writeFileSync6(path, renderLearning({ title, summary, category, moduleName, idSlug }));
5272
+ const idSlug = basename5(path, ".md");
5273
+ writeFileSync7(path, renderLearning({ title, summary, category, moduleName, idSlug }));
4219
5274
  return {
4220
- relativePath: normalizePath2(relative8(root, path)),
5275
+ relativePath: normalizePath2(relative9(root, path)),
4221
5276
  title,
4222
5277
  captureMode: "pgs-native"
4223
5278
  };
@@ -4259,10 +5314,10 @@ function renderLearning(options) {
4259
5314
  }
4260
5315
  function uniquePath(dir, slug) {
4261
5316
  let index = 1;
4262
- let candidate = join16(dir, `${slug}.md`);
4263
- while (existsSync16(candidate)) {
5317
+ let candidate = join17(dir, `${slug}.md`);
5318
+ while (existsSync17(candidate)) {
4264
5319
  index += 1;
4265
- candidate = join16(dir, `${slug}-${index}.md`);
5320
+ candidate = join17(dir, `${slug}-${index}.md`);
4266
5321
  }
4267
5322
  return candidate;
4268
5323
  }
@@ -4447,12 +5502,12 @@ function printUsage3() {
4447
5502
  }
4448
5503
 
4449
5504
  // src/commands/lens.ts
4450
- import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "node:fs";
4451
- import { dirname as dirname13 } from "node:path";
5505
+ import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "node:fs";
5506
+ import { dirname as dirname14 } from "node:path";
4452
5507
 
4453
5508
  // 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";
5509
+ import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
5510
+ import { basename as basename6, dirname as dirname12, join as join18 } from "node:path";
4456
5511
  var REQUIRED_ARTIFACTS = [
4457
5512
  "manifest.md",
4458
5513
  "raw/project-lens/architecture-lens.md",
@@ -4470,20 +5525,20 @@ function createProjectLensAuditPackage(targetDir, auditDir) {
4470
5525
  version: 1,
4471
5526
  target: {
4472
5527
  path: targetDir,
4473
- name: basename5(targetDir) || "target"
5528
+ name: basename6(targetDir) || "target"
4474
5529
  },
4475
5530
  requiredArtifacts: [...REQUIRED_ARTIFACTS]
4476
5531
  };
4477
- mkdirSync8(auditDir, { recursive: true });
4478
- writeJson(join17(auditDir, "audit.contract.json"), contract);
5532
+ mkdirSync9(auditDir, { recursive: true });
5533
+ writeJson(join18(auditDir, "audit.contract.json"), contract);
4479
5534
  for (const artifactPath of REQUIRED_ARTIFACTS) {
4480
- writeTemplate(join17(auditDir, artifactPath), renderArtifactTemplate(artifactPath, contract));
5535
+ writeTemplate(join18(auditDir, artifactPath), renderArtifactTemplate(artifactPath, contract));
4481
5536
  }
4482
5537
  return contract;
4483
5538
  }
4484
5539
  function checkProjectLensAuditPackage(auditDir, options = {}) {
4485
- const contractPath = join17(auditDir, "audit.contract.json");
4486
- if (!existsSync17(contractPath)) {
5540
+ const contractPath = join18(auditDir, "audit.contract.json");
5541
+ if (!existsSync18(contractPath)) {
4487
5542
  return {
4488
5543
  ok: false,
4489
5544
  auditDir,
@@ -4497,7 +5552,7 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
4497
5552
  }
4498
5553
  let contract;
4499
5554
  try {
4500
- contract = JSON.parse(readFileSync11(contractPath, "utf8"));
5555
+ contract = JSON.parse(readFileSync12(contractPath, "utf8"));
4501
5556
  } catch (error) {
4502
5557
  return {
4503
5558
  ok: false,
@@ -4530,12 +5585,12 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
4530
5585
  }
4531
5586
  }
4532
5587
  for (const artifactPath of REQUIRED_ARTIFACTS) {
4533
- const absolutePath = join17(auditDir, artifactPath);
4534
- if (!existsSync17(absolutePath)) {
5588
+ const absolutePath = join18(auditDir, artifactPath);
5589
+ if (!existsSync18(absolutePath)) {
4535
5590
  issues.push({ type: "missing-required-artifact", path: artifactPath });
4536
5591
  continue;
4537
5592
  }
4538
- const content = readFileSync11(absolutePath, "utf8");
5593
+ const content = readFileSync12(absolutePath, "utf8");
4539
5594
  if (isPendingArtifact(content)) {
4540
5595
  issues.push({ type: "artifact-not-complete", path: artifactPath });
4541
5596
  } else if (hasTemplateBody(content)) {
@@ -4551,13 +5606,13 @@ function checkProjectLensAuditPackage(auditDir, options = {}) {
4551
5606
  };
4552
5607
  }
4553
5608
  function writeJson(path, value) {
4554
- mkdirSync8(dirname11(path), { recursive: true });
4555
- writeFileSync7(path, `${JSON.stringify(value, null, 2)}
5609
+ mkdirSync9(dirname12(path), { recursive: true });
5610
+ writeFileSync8(path, `${JSON.stringify(value, null, 2)}
4556
5611
  `);
4557
5612
  }
4558
5613
  function writeTemplate(path, content) {
4559
- mkdirSync8(dirname11(path), { recursive: true });
4560
- writeFileSync7(path, content);
5614
+ mkdirSync9(dirname12(path), { recursive: true });
5615
+ writeFileSync8(path, content);
4561
5616
  }
4562
5617
  function renderArtifactTemplate(artifactPath, contract) {
4563
5618
  const title = artifactPath.replace(/\.md$/, "").split("/").map((part) => part.replaceAll("-", " ")).join(" / ");
@@ -4836,13 +5891,13 @@ function formatLink(link) {
4836
5891
 
4837
5892
  // src/lens/scan.ts
4838
5893
  import { spawnSync as spawnSync3 } from "node:child_process";
4839
- import { existsSync as existsSync21, readFileSync as readFileSync13, statSync as statSync6 } from "node:fs";
5894
+ import { existsSync as existsSync22, readFileSync as readFileSync14, statSync as statSync7 } from "node:fs";
4840
5895
  import { homedir as homedir3 } from "node:os";
4841
- import { join as join22 } from "node:path";
5896
+ import { join as join23 } from "node:path";
4842
5897
 
4843
5898
  // 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";
5899
+ import { lstatSync as lstatSync8, readlinkSync as readlinkSync3, realpathSync as realpathSync5 } from "node:fs";
5900
+ import { dirname as dirname13, isAbsolute as isAbsolute5, join as join19, resolve as resolve7 } from "node:path";
4846
5901
  function inspectProjectHostSsot(root) {
4847
5902
  const agentsEntry = inspectCanonicalPath(root, "AGENTS.md");
4848
5903
  const canonicalSkills = inspectCanonicalPath(root, ".agents/skills");
@@ -4886,7 +5941,7 @@ function inspectUserSkillsSsot(homeDir) {
4886
5941
  };
4887
5942
  }
4888
5943
  function inspectCanonicalPath(root, path) {
4889
- const absolutePath = join18(root, path);
5944
+ const absolutePath = join19(root, path);
4890
5945
  const stat = safeLstat2(absolutePath);
4891
5946
  if (!stat) return { path, status: "missing" };
4892
5947
  if (stat.isSymbolicLink()) {
@@ -4902,7 +5957,7 @@ function inspectCanonicalPath(root, path) {
4902
5957
  return { path, status: "other" };
4903
5958
  }
4904
5959
  function inspectCompatibilityLink(root, path, expectedRawTarget) {
4905
- const absolutePath = join18(root, path);
5960
+ const absolutePath = join19(root, path);
4906
5961
  const stat = safeLstat2(absolutePath);
4907
5962
  const base = { path, expectedRawTarget, compliant: false };
4908
5963
  if (!stat) return { ...base, status: "missing" };
@@ -4912,8 +5967,8 @@ function inspectCompatibilityLink(root, path, expectedRawTarget) {
4912
5967
  return { ...base, status: "other" };
4913
5968
  }
4914
5969
  const rawTarget = normalizeSymlinkTarget(readlinkSync3(absolutePath));
4915
- const resolvedTarget = resolve6(dirname12(absolutePath), rawTarget);
4916
- const expectedPath = resolve6(dirname12(absolutePath), expectedRawTarget);
5970
+ const resolvedTarget = resolve7(dirname13(absolutePath), rawTarget);
5971
+ const expectedPath = resolve7(dirname13(absolutePath), expectedRawTarget);
4917
5972
  const targetStat = safeLstat2(resolvedTarget);
4918
5973
  if (!targetStat) {
4919
5974
  return {
@@ -4932,7 +5987,7 @@ function inspectCompatibilityLink(root, path, expectedRawTarget) {
4932
5987
  if (!targetMatches) {
4933
5988
  return { ...base, rawTarget, resolvedTarget, status: "wrong-target" };
4934
5989
  }
4935
- if (isAbsolute4(rawTarget)) {
5990
+ if (isAbsolute5(rawTarget)) {
4936
5991
  return { ...base, rawTarget, resolvedTarget, status: "absolute-symlink" };
4937
5992
  }
4938
5993
  if (rawTarget !== expectedRawTarget) {
@@ -4953,16 +6008,16 @@ function inspectCompatibilityLink(root, path, expectedRawTarget) {
4953
6008
  }
4954
6009
  function safeLstat2(path) {
4955
6010
  try {
4956
- return lstatSync7(path);
6011
+ return lstatSync8(path);
4957
6012
  } catch {
4958
6013
  return void 0;
4959
6014
  }
4960
6015
  }
4961
6016
 
4962
6017
  // src/portfolio/redundancy.ts
4963
- import { existsSync as existsSync18, readdirSync as readdirSync9, statSync as statSync5 } from "node:fs";
6018
+ import { existsSync as existsSync19, readdirSync as readdirSync10, statSync as statSync6 } from "node:fs";
4964
6019
  import { homedir as homedir2 } from "node:os";
4965
- import { join as join19 } from "node:path";
6020
+ import { join as join20 } from "node:path";
4966
6021
  var DEFAULT_CACHE_THRESHOLD_BYTES = 1e9;
4967
6022
  var MAX_CACHE_ENTRIES = 2e4;
4968
6023
  function inspectHostRedundancy(options = {}) {
@@ -4993,8 +6048,8 @@ function inspectProjectRedundancy(root, options = {}) {
4993
6048
  }
4994
6049
  function inspectLegacyDirectories(root) {
4995
6050
  const relativePath = ".agent";
4996
- const path = join19(root, relativePath);
4997
- if (!existsSync18(path)) return [];
6051
+ const path = join20(root, relativePath);
6052
+ if (!existsSync19(path)) return [];
4998
6053
  const stats = collectDirectoryStats(path);
4999
6054
  return [
5000
6055
  {
@@ -5009,16 +6064,16 @@ function inspectLegacyDirectories(root) {
5009
6064
  function getPlaywrightCachePaths(homeDir, configuredPath) {
5010
6065
  const candidates = [
5011
6066
  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")
6067
+ join20(homeDir, "Library/Caches/ms-playwright"),
6068
+ join20(homeDir, ".cache/ms-playwright"),
6069
+ join20(homeDir, "AppData/Local/ms-playwright")
5015
6070
  ].filter((path) => Boolean(path));
5016
6071
  return [...new Set(candidates)];
5017
6072
  }
5018
6073
  function inspectPlaywrightCache(path, cache) {
5019
6074
  const cached = cache?.get(path);
5020
6075
  if (cached) return cached;
5021
- if (!existsSync18(path)) {
6076
+ if (!existsSync19(path)) {
5022
6077
  const missing = {
5023
6078
  path,
5024
6079
  exists: false,
@@ -5033,7 +6088,7 @@ function inspectPlaywrightCache(path, cache) {
5033
6088
  const stats = collectDirectoryStats(path);
5034
6089
  let revisionCount = 0;
5035
6090
  try {
5036
- revisionCount = readdirSync9(path, { withFileTypes: true }).filter(
6091
+ revisionCount = readdirSync10(path, { withFileTypes: true }).filter(
5037
6092
  (entry) => entry.isDirectory()
5038
6093
  ).length;
5039
6094
  } catch {
@@ -5060,7 +6115,7 @@ function collectDirectoryStats(root) {
5060
6115
  if (!current) continue;
5061
6116
  let entries;
5062
6117
  try {
5063
- entries = readdirSync9(current, { withFileTypes: true });
6118
+ entries = readdirSync10(current, { withFileTypes: true });
5064
6119
  } catch {
5065
6120
  continue;
5066
6121
  }
@@ -5069,13 +6124,13 @@ function collectDirectoryStats(root) {
5069
6124
  truncated = true;
5070
6125
  break;
5071
6126
  }
5072
- const path = join19(current, entry.name);
6127
+ const path = join20(current, entry.name);
5073
6128
  if (entry.isDirectory()) {
5074
6129
  pending.push(path);
5075
6130
  } else if (entry.isFile()) {
5076
6131
  fileCount += 1;
5077
6132
  try {
5078
- bytes += statSync5(path).size;
6133
+ bytes += statSync6(path).size;
5079
6134
  } catch {
5080
6135
  }
5081
6136
  }
@@ -5086,11 +6141,11 @@ function collectDirectoryStats(root) {
5086
6141
  }
5087
6142
 
5088
6143
  // src/portfolio/verification.ts
5089
- import { existsSync as existsSync19, readFileSync as readFileSync12 } from "node:fs";
5090
- import { join as join20 } from "node:path";
6144
+ import { existsSync as existsSync20, readFileSync as readFileSync13 } from "node:fs";
6145
+ import { join as join21 } from "node:path";
5091
6146
  var REQUIRED_PROJECT_SCRIPTS = ["typecheck", "lint", "format:check", "verify"];
5092
6147
  function inspectProjectVerification(root) {
5093
- const packageJson = readPackageJson(join20(root, "package.json"));
6148
+ const packageJson = readPackageJson(join21(root, "package.json"));
5094
6149
  const scripts = Object.fromEntries(
5095
6150
  REQUIRED_PROJECT_SCRIPTS.map((name) => [
5096
6151
  name,
@@ -5108,9 +6163,9 @@ function inspectProjectVerification(root) {
5108
6163
  };
5109
6164
  }
5110
6165
  function readPackageJson(path) {
5111
- if (!existsSync19(path)) return void 0;
6166
+ if (!existsSync20(path)) return void 0;
5112
6167
  try {
5113
- return JSON.parse(readFileSync12(path, "utf8"));
6168
+ return JSON.parse(readFileSync13(path, "utf8"));
5114
6169
  } catch {
5115
6170
  return void 0;
5116
6171
  }
@@ -5118,8 +6173,8 @@ function readPackageJson(path) {
5118
6173
 
5119
6174
  // src/repository-files.ts
5120
6175
  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";
6176
+ import { existsSync as existsSync21, readdirSync as readdirSync11 } from "node:fs";
6177
+ import { isAbsolute as isAbsolute6, join as join22, posix as posix3, relative as relative10 } from "node:path";
5123
6178
  var gitMaxBufferBytes = 64 * 1024 * 1024;
5124
6179
  var RepositoryFileDiscoveryError = class extends Error {
5125
6180
  constructor(message) {
@@ -5130,7 +6185,7 @@ var RepositoryFileDiscoveryError = class extends Error {
5130
6185
  function discoverRepositoryFiles(root, options = {}) {
5131
6186
  const probe = runGit(root, ["rev-parse", "--is-inside-work-tree"]);
5132
6187
  if (!probe.ok) {
5133
- if (!probe.notRepository || existsSync20(join21(root, ".git"))) {
6188
+ if (!probe.notRepository || existsSync21(join22(root, ".git"))) {
5134
6189
  throw new RepositoryFileDiscoveryError(probe.message);
5135
6190
  }
5136
6191
  return {
@@ -5161,7 +6216,7 @@ function discoverRepositoryFiles(root, options = {}) {
5161
6216
  `Git returned a path outside the repository boundary: ${path}`
5162
6217
  );
5163
6218
  }
5164
- if (existsSync20(join21(root, normalized))) files.add(normalized);
6219
+ if (existsSync21(join22(root, normalized))) files.add(normalized);
5165
6220
  }
5166
6221
  return { source: "git", files: [...files].sort() };
5167
6222
  }
@@ -5173,21 +6228,21 @@ function discoverFilesystemFiles(root, options) {
5173
6228
  const maxDepth = options.fallbackMaxDepth ?? Number.POSITIVE_INFINITY;
5174
6229
  const ignoredDirectories2 = options.fallbackIgnoredDirectories ?? /* @__PURE__ */ new Set();
5175
6230
  const visit = (directory, depth) => {
5176
- if (depth > maxDepth || !existsSync20(directory)) return;
6231
+ if (depth > maxDepth || !existsSync21(directory)) return;
5177
6232
  let entries;
5178
6233
  try {
5179
- entries = readdirSync10(directory, { withFileTypes: true });
6234
+ entries = readdirSync11(directory, { withFileTypes: true });
5180
6235
  } catch {
5181
6236
  return;
5182
6237
  }
5183
6238
  for (const entry of entries) {
5184
- const absolutePath = join21(directory, entry.name);
6239
+ const absolutePath = join22(directory, entry.name);
5185
6240
  if (entry.isDirectory()) {
5186
6241
  if (!ignoredDirectories2.has(entry.name)) visit(absolutePath, depth + 1);
5187
6242
  continue;
5188
6243
  }
5189
6244
  if (!entry.isFile()) continue;
5190
- const relativePath = normalizeRepositoryRelativePath(relative9(root, absolutePath));
6245
+ const relativePath = normalizeRepositoryRelativePath(relative10(root, absolutePath));
5191
6246
  if (isSafeRepositoryRelativePath(relativePath) && (options.fallbackIncludeFile?.(relativePath) ?? true)) {
5192
6247
  files.add(relativePath);
5193
6248
  }
@@ -5197,7 +6252,7 @@ function discoverFilesystemFiles(root, options) {
5197
6252
  return [...files].sort();
5198
6253
  }
5199
6254
  function isSafeRepositoryRelativePath(path) {
5200
- return path !== "" && path !== "." && !isAbsolute5(path) && !/^[a-zA-Z]:\//.test(path) && path !== ".." && !path.startsWith("../");
6255
+ return path !== "" && path !== "." && !isAbsolute6(path) && !/^[a-zA-Z]:\//.test(path) && path !== ".." && !path.startsWith("../");
5201
6256
  }
5202
6257
  function runGit(root, args) {
5203
6258
  const result = spawnSync2("git", ["-C", root, ...args], {
@@ -5237,7 +6292,7 @@ function scanProjectLensTarget(targetDir, options = {}) {
5237
6292
  includedFileCount: files.length,
5238
6293
  excludedFileCount: candidateFiles.length - files.length
5239
6294
  },
5240
- aiEntryFiles: ["AGENTS.md", "CLAUDE.md"].filter((file) => existsSync21(join22(targetDir, file))),
6295
+ aiEntryFiles: ["AGENTS.md", "CLAUDE.md"].filter((file) => existsSync22(join23(targetDir, file))),
5241
6296
  aiConfigFiles: [],
5242
6297
  hostSsot: inspectProjectHostSsot(targetDir),
5243
6298
  userHostSsot: inspectUserSkillsSsot(options.homeDir ?? process.env.HOME ?? homedir3()),
@@ -5248,19 +6303,19 @@ function scanProjectLensTarget(targetDir, options = {}) {
5248
6303
  }),
5249
6304
  packageJson,
5250
6305
  docs: {
5251
- hasDocsDirectory: existsSync21(join22(targetDir, "docs")),
6306
+ hasDocsDirectory: existsSync22(join23(targetDir, "docs")),
5252
6307
  markdownFileCount: markdownFiles.length,
5253
6308
  governanceFiles: markdownFiles.filter((file) => file.startsWith("docs/governance/") || file.startsWith("docs/policy/")).sort()
5254
6309
  },
5255
6310
  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)
6311
+ 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
6312
  };
5258
6313
  }
5259
6314
  function readPackageJson2(targetDir) {
5260
- const packageJsonPath = join22(targetDir, "package.json");
5261
- if (!existsSync21(packageJsonPath)) return void 0;
6315
+ const packageJsonPath = join23(targetDir, "package.json");
6316
+ if (!existsSync22(packageJsonPath)) return void 0;
5262
6317
  try {
5263
- const packageJson = JSON.parse(readFileSync13(packageJsonPath, "utf8"));
6318
+ const packageJson = JSON.parse(readFileSync14(packageJsonPath, "utf8"));
5264
6319
  return {
5265
6320
  scripts: Object.keys(packageJson.scripts ?? {}).sort(),
5266
6321
  dependencies: Object.keys(packageJson.dependencies ?? {}).sort(),
@@ -5351,8 +6406,8 @@ function runLensReport(args) {
5351
6406
  }
5352
6407
  const report = scanProjectLensTarget(options.value.targetDir);
5353
6408
  const markdown = renderProjectLensMarkdownReport(report);
5354
- mkdirSync9(dirname13(options.value.outPath), { recursive: true });
5355
- writeFileSync8(options.value.outPath, markdown);
6409
+ mkdirSync10(dirname14(options.value.outPath), { recursive: true });
6410
+ writeFileSync9(options.value.outPath, markdown);
5356
6411
  console.log(`report: ${options.value.outPath}`);
5357
6412
  return 0;
5358
6413
  }
@@ -5457,15 +6512,15 @@ function printUsage4() {
5457
6512
  }
5458
6513
 
5459
6514
  // src/commands/portfolio.ts
5460
- import { existsSync as existsSync33, readFileSync as readFileSync18 } from "node:fs";
5461
- import { join as join34 } from "node:path";
6515
+ import { existsSync as existsSync34, readFileSync as readFileSync19 } from "node:fs";
6516
+ import { join as join35 } from "node:path";
5462
6517
 
5463
6518
  // src/portfolio/doctor.ts
5464
6519
  import { spawnSync as spawnSync5 } from "node:child_process";
5465
- import { existsSync as existsSync24, readFileSync as readFileSync16 } from "node:fs";
6520
+ import { existsSync as existsSync25, readFileSync as readFileSync17 } from "node:fs";
5466
6521
  import { createRequire as createRequire2 } from "node:module";
5467
6522
  import { homedir as homedir4 } from "node:os";
5468
- import { dirname as dirname15, join as join25 } from "node:path";
6523
+ import { dirname as dirname16, join as join26 } from "node:path";
5469
6524
  import { fileURLToPath as fileURLToPath4 } from "node:url";
5470
6525
 
5471
6526
  // src/host-tooling/inventory.ts
@@ -5557,8 +6612,8 @@ function isRecord2(value) {
5557
6612
  }
5558
6613
 
5559
6614
  // 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";
6615
+ import { existsSync as existsSync23, lstatSync as lstatSync9, readFileSync as readFileSync15 } from "node:fs";
6616
+ import { join as join24 } from "node:path";
5562
6617
  function comparePortfolioAssetState(options) {
5563
6618
  const expectedManifest = readPlanDocument(
5564
6619
  options.expectedPlan,
@@ -5569,10 +6624,10 @@ function comparePortfolioAssetState(options) {
5569
6624
  ".pro-gov/assets.lock.json"
5570
6625
  );
5571
6626
  const currentManifest = readJsonFile(
5572
- join23(options.targetDir, ".pro-gov/assets.json")
6627
+ join24(options.targetDir, ".pro-gov/assets.json")
5573
6628
  );
5574
6629
  const currentLock = readJsonFile(
5575
- join23(options.targetDir, ".pro-gov/assets.lock.json")
6630
+ join24(options.targetDir, ".pro-gov/assets.lock.json")
5576
6631
  );
5577
6632
  const issues = [];
5578
6633
  if (!sameStrings(currentManifest?.bundleIds, expectedManifest?.bundleIds)) {
@@ -5600,7 +6655,7 @@ function comparePortfolioAssetState(options) {
5600
6655
  (action) => action.type === "adopt-symlink" && action.assetId === entry.id && action.legacyTargetPath === entry.targetPath
5601
6656
  ))
5602
6657
  continue;
5603
- const targetAbsolutePath = join23(options.targetDir, entry.targetPath);
6658
+ const targetAbsolutePath = join24(options.targetDir, entry.targetPath);
5604
6659
  if (!pathIsSymlink(targetAbsolutePath)) continue;
5605
6660
  issues.push({
5606
6661
  type: "orphaned-managed-symlink",
@@ -5622,9 +6677,9 @@ function readPlanDocument(plan, targetPath) {
5622
6677
  }
5623
6678
  }
5624
6679
  function readJsonFile(path) {
5625
- if (!existsSync22(path)) return void 0;
6680
+ if (!existsSync23(path)) return void 0;
5626
6681
  try {
5627
- return JSON.parse(readFileSync14(path, "utf8"));
6682
+ return JSON.parse(readFileSync15(path, "utf8"));
5628
6683
  } catch {
5629
6684
  return void 0;
5630
6685
  }
@@ -5650,7 +6705,7 @@ function normalizeLock(lock) {
5650
6705
  }
5651
6706
  function pathIsSymlink(path) {
5652
6707
  try {
5653
- return lstatSync8(path).isSymbolicLink();
6708
+ return lstatSync9(path).isSymbolicLink();
5654
6709
  } catch {
5655
6710
  return false;
5656
6711
  }
@@ -5658,8 +6713,8 @@ function pathIsSymlink(path) {
5658
6713
 
5659
6714
  // src/portfolio/version-policy.ts
5660
6715
  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";
6716
+ import { existsSync as existsSync24, lstatSync as lstatSync10, readFileSync as readFileSync16 } from "node:fs";
6717
+ import { dirname as dirname15, join as join25 } from "node:path";
5663
6718
  function inspectVersionPolicy(root, policy, projectType) {
5664
6719
  if (!policy) return { status: "compliant", packages: [], runtimes: [], attentionCount: 0 };
5665
6720
  const packageManifests = collectPackageManifests(root);
@@ -5768,10 +6823,10 @@ function findDeclaredVersion(packageJson, name) {
5768
6823
  function readInstalledVersion(root, name, fromDirectory = root) {
5769
6824
  let current = fromDirectory;
5770
6825
  while (true) {
5771
- const packageJson = readJson2(join24(current, "node_modules", name, "package.json"));
6826
+ const packageJson = readJson2(join25(current, "node_modules", name, "package.json"));
5772
6827
  if (typeof packageJson?.version === "string") return packageJson.version;
5773
6828
  if (current === root) return void 0;
5774
- const parent = dirname14(current);
6829
+ const parent = dirname15(current);
5775
6830
  if (parent === current) return void 0;
5776
6831
  current = parent;
5777
6832
  }
@@ -5806,15 +6861,15 @@ function collectPackageManifests(root) {
5806
6861
  if (relativePath.split("/").at(-1) !== "package.json" || directorySegments.length > 6 || directorySegments.some((segment) => ignored.has(segment))) {
5807
6862
  return;
5808
6863
  }
5809
- const path = join24(root, relativePath);
6864
+ const path = join25(root, relativePath);
5810
6865
  try {
5811
- if (!lstatSync9(path).isFile()) return;
6866
+ if (!lstatSync10(path).isFile()) return;
5812
6867
  } catch {
5813
6868
  return;
5814
6869
  }
5815
6870
  const packageJson = readJson2(path);
5816
6871
  if (!packageJson) return;
5817
- manifests.set(relativePath, { path: relativePath, directory: dirname14(path), packageJson });
6872
+ manifests.set(relativePath, { path: relativePath, directory: dirname15(path), packageJson });
5818
6873
  };
5819
6874
  const discovery = discoverRepositoryFiles(root, {
5820
6875
  gitPathspecs: ["package.json", ":(glob)**/package.json"],
@@ -5829,9 +6884,9 @@ function unique(values) {
5829
6884
  return [...new Set(values)];
5830
6885
  }
5831
6886
  function readJson2(path) {
5832
- if (!existsSync23(path)) return void 0;
6887
+ if (!existsSync24(path)) return void 0;
5833
6888
  try {
5834
- return JSON.parse(readFileSync15(path, "utf8"));
6889
+ return JSON.parse(readFileSync16(path, "utf8"));
5835
6890
  } catch {
5836
6891
  return void 0;
5837
6892
  }
@@ -5864,12 +6919,12 @@ function inspectTarget(options) {
5864
6919
  const { target } = options;
5865
6920
  const hostSsot = inspectProjectHostSsot(target.path);
5866
6921
  const issues = [];
5867
- const packageJson = readJson3(join25(target.path, "package.json"));
6922
+ const packageJson = readJson3(join26(target.path, "package.json"));
5868
6923
  const packages = {};
5869
6924
  for (const packageName of ["@pieai/pro-gov", "@pieai/doc-gov"]) {
5870
6925
  const declared = packageJson?.devDependencies?.[packageName] ?? packageJson?.dependencies?.[packageName];
5871
6926
  const installedPackage = readJson3(
5872
- join25(target.path, "node_modules", packageName, "package.json")
6927
+ join26(target.path, "node_modules", packageName, "package.json")
5873
6928
  );
5874
6929
  const installed = installedPackage?.version;
5875
6930
  const expected = options.expectedPackageVersions[packageName];
@@ -5929,7 +6984,7 @@ function inspectTarget(options) {
5929
6984
  type: "asset-lock-drift",
5930
6985
  message: error instanceof Error ? error.message : String(error)
5931
6986
  });
5932
- if (!existsSync24(join25(target.path, ".pro-gov/assets.json"))) {
6987
+ if (!existsSync25(join26(target.path, ".pro-gov/assets.json"))) {
5933
6988
  issues.push({ type: "bundle-drift", message: "Target asset manifest is missing." });
5934
6989
  }
5935
6990
  }
@@ -5947,15 +7002,15 @@ function inspectTarget(options) {
5947
7002
  };
5948
7003
  }
5949
7004
  function readTargetAssetHost(targetDir) {
5950
- const lockfile = readJson3(join25(targetDir, ".pro-gov/assets.lock.json"));
7005
+ const lockfile = readJson3(join26(targetDir, ".pro-gov/assets.lock.json"));
5951
7006
  return isAssetRegistryHost(lockfile?.host) ? lockfile.host : void 0;
5952
7007
  }
5953
7008
  function isAssetRegistryHost(value) {
5954
7009
  return value === "codex" || value === "claude-code" || value === "gemini-cli" || value === "antigravity";
5955
7010
  }
5956
7011
  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");
7012
+ const proGovCli = join26(target.path, "node_modules/@pieai/pro-gov/dist/cli.js");
7013
+ const docGovCli = join26(target.path, "node_modules/@pieai/doc-gov/dist/cli.js");
5959
7014
  const commands = [
5960
7015
  {
5961
7016
  name: "pro-gov doctor",
@@ -5966,7 +7021,7 @@ function runTargetChecks(target) {
5966
7021
  { name: "doc-gov scan --check", cli: docGovCli, args: ["scan", "--check"] }
5967
7022
  ];
5968
7023
  return commands.map((command2) => {
5969
- if (!existsSync24(command2.cli)) return { name: command2.name, status: null };
7024
+ if (!existsSync25(command2.cli)) return { name: command2.name, status: null };
5970
7025
  const result = spawnSync5(process.execPath, [command2.cli, ...command2.args], {
5971
7026
  cwd: target.path,
5972
7027
  encoding: "utf8",
@@ -6005,18 +7060,18 @@ function getExpectedPackageVersions() {
6005
7060
  };
6006
7061
  }
6007
7062
  function findOwnPackageJson() {
6008
- let current = dirname15(fileURLToPath4(import.meta.url));
7063
+ let current = dirname16(fileURLToPath4(import.meta.url));
6009
7064
  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);
7065
+ const candidate = join26(current, "package.json");
7066
+ if (existsSync25(candidate)) return candidate;
7067
+ current = dirname16(current);
6013
7068
  }
6014
7069
  return "";
6015
7070
  }
6016
7071
  function readJson3(path) {
6017
- if (!path || !existsSync24(path)) return void 0;
7072
+ if (!path || !existsSync25(path)) return void 0;
6018
7073
  try {
6019
- return JSON.parse(readFileSync16(path, "utf8"));
7074
+ return JSON.parse(readFileSync17(path, "utf8"));
6020
7075
  } catch {
6021
7076
  return void 0;
6022
7077
  }
@@ -6033,15 +7088,15 @@ function deduplicateIssues(issues) {
6033
7088
 
6034
7089
  // src/portfolio/ai-health/index.ts
6035
7090
  import { homedir as homedir5 } from "node:os";
6036
- import { dirname as dirname17, join as join33, resolve as resolve9 } from "node:path";
7091
+ import { dirname as dirname18, join as join34, resolve as resolve10 } from "node:path";
6037
7092
 
6038
7093
  // 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";
7094
+ import { existsSync as existsSync27, lstatSync as lstatSync12, realpathSync as realpathSync7 } from "node:fs";
7095
+ import { join as join27 } from "node:path";
6041
7096
 
6042
7097
  // src/portfolio/ai-health/shared.ts
6043
7098
  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";
7099
+ import { existsSync as existsSync26, lstatSync as lstatSync11, readFileSync as readFileSync18, readdirSync as readdirSync12, realpathSync as realpathSync6, statSync as statSync8 } from "node:fs";
6045
7100
  function safeRealpath(path) {
6046
7101
  try {
6047
7102
  return realpathSync6(path);
@@ -6072,7 +7127,7 @@ function jsonObjectKeys(path, key) {
6072
7127
  return Object.keys(value[key]).sort();
6073
7128
  }
6074
7129
  function tomlMcpNames(path) {
6075
- if (!existsSync25(path)) return [];
7130
+ if (!existsSync26(path)) return [];
6076
7131
  const names = /* @__PURE__ */ new Set();
6077
7132
  for (const line of safeRead(path).split(/\r?\n/)) {
6078
7133
  const match = line.match(/^\s*\[mcp_servers\.(?:"([^"]+)"|([^.\]]+))\]\s*$/);
@@ -6083,35 +7138,35 @@ function tomlMcpNames(path) {
6083
7138
  }
6084
7139
  function readJson4(path) {
6085
7140
  try {
6086
- return JSON.parse(readFileSync17(path, "utf8"));
7141
+ return JSON.parse(readFileSync18(path, "utf8"));
6087
7142
  } catch {
6088
7143
  return void 0;
6089
7144
  }
6090
7145
  }
6091
7146
  function safeRead(path) {
6092
7147
  try {
6093
- return readFileSync17(path, "utf8");
7148
+ return readFileSync18(path, "utf8");
6094
7149
  } catch {
6095
7150
  return "";
6096
7151
  }
6097
7152
  }
6098
7153
  function safeReadDir(path) {
6099
7154
  try {
6100
- return readdirSync11(path).sort();
7155
+ return readdirSync12(path).sort();
6101
7156
  } catch {
6102
7157
  return [];
6103
7158
  }
6104
7159
  }
6105
7160
  function safeIsDirectory(path) {
6106
7161
  try {
6107
- return statSync7(path).isDirectory();
7162
+ return statSync8(path).isDirectory();
6108
7163
  } catch {
6109
7164
  return false;
6110
7165
  }
6111
7166
  }
6112
7167
  function pathLexists(path) {
6113
7168
  try {
6114
- lstatSync10(path);
7169
+ lstatSync11(path);
6115
7170
  return true;
6116
7171
  } catch {
6117
7172
  return false;
@@ -6149,12 +7204,12 @@ function hasWorkflowReminderHooks(hooks) {
6149
7204
  );
6150
7205
  }
6151
7206
  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");
7207
+ const agentsPath = join27(root, "AGENTS.md");
7208
+ const agents = !existsSync27(agentsPath) ? "missing" : safeRead(agentsPath).includes("PGS-ROUTER:BEGIN") ? "pgs-router" : "custom";
7209
+ const claudePath = join27(root, "CLAUDE.md");
6155
7210
  let claude = "missing";
6156
7211
  if (pathLexists(claudePath)) {
6157
- const info = lstatSync11(claudePath);
7212
+ const info = lstatSync12(claudePath);
6158
7213
  if (info.isSymbolicLink()) {
6159
7214
  try {
6160
7215
  claude = realpathSync7(claudePath) === realpathSync7(agentsPath) ? "agents-symlink" : "custom";
@@ -6171,19 +7226,19 @@ function inspectEntries(root) {
6171
7226
  var AGENT_LINK_ROOTS = [".agents/workflows", ".agents/commands", ".claude/commands"];
6172
7227
  function inspectAgentLinks(root) {
6173
7228
  const entries = AGENT_LINK_ROOTS.flatMap((directory) => {
6174
- const directoryPath = join26(root, directory);
7229
+ const directoryPath = join27(root, directory);
6175
7230
  if (!pathLexists(directoryPath)) return [];
6176
7231
  try {
6177
- if (!lstatSync11(directoryPath).isDirectory()) return [];
7232
+ if (!lstatSync12(directoryPath).isDirectory()) return [];
6178
7233
  } catch {
6179
7234
  return [];
6180
7235
  }
6181
7236
  return safeReadDir(directoryPath).filter((name) => !name.startsWith(".")).flatMap((name) => {
6182
7237
  const relativePath = `${directory}/${name}`;
6183
- const path = join26(root, relativePath);
7238
+ const path = join27(root, relativePath);
6184
7239
  let stat;
6185
7240
  try {
6186
- stat = lstatSync11(path);
7241
+ stat = lstatSync12(path);
6187
7242
  } catch {
6188
7243
  return [];
6189
7244
  }
@@ -6204,9 +7259,9 @@ function inspectAgentLinks(root) {
6204
7259
  };
6205
7260
  }
6206
7261
  function inspectOptionalEntry(root, filename, agentsPath) {
6207
- const path = join26(root, filename);
7262
+ const path = join27(root, filename);
6208
7263
  if (!pathLexists(path)) return "missing";
6209
- const info = lstatSync11(path);
7264
+ const info = lstatSync12(path);
6210
7265
  if (info.isSymbolicLink()) {
6211
7266
  try {
6212
7267
  return realpathSync7(path) === realpathSync7(agentsPath) ? "agents-symlink" : "custom";
@@ -6244,7 +7299,7 @@ function inspectHooks(root) {
6244
7299
  { host: "codex", path: ".codex/hooks.json" }
6245
7300
  ];
6246
7301
  return configs.map((config) => {
6247
- const value = readJson4(join26(root, config.path));
7302
+ const value = readJson4(join27(root, config.path));
6248
7303
  const counts = /* @__PURE__ */ new Map();
6249
7304
  collectHookEvents(value, counts);
6250
7305
  return {
@@ -6265,18 +7320,18 @@ function collectHookEvents(value, counts) {
6265
7320
  }
6266
7321
  }
6267
7322
  function inspectDocs(root, expected) {
6268
- const packageJson = readJson4(join26(root, "package.json"));
7323
+ const packageJson = readJson4(join27(root, "package.json"));
6269
7324
  const dependencies = isRecord3(packageJson) ? { ...recordOrEmpty(packageJson.dependencies), ...recordOrEmpty(packageJson.devDependencies) } : {};
6270
7325
  const docGov = dependencyVersion(dependencies["@pieai/doc-gov"]);
6271
7326
  const proGov = dependencyVersion(dependencies["@pieai/pro-gov"]);
6272
- const routerMatch = safeRead(join26(root, "AGENTS.md")).match(/PGS-ROUTER:BEGIN\s+v([0-9.]+)/);
7327
+ const routerMatch = safeRead(join27(root, "AGENTS.md")).match(/PGS-ROUTER:BEGIN\s+v([0-9.]+)/);
6273
7328
  const declared = [docGov, proGov].filter((value) => Boolean(value));
6274
7329
  return {
6275
7330
  routerVersion: routerMatch?.[1],
6276
7331
  expectedRouterVersion: CURRENT_ROUTER_VERSION,
6277
7332
  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")),
7333
+ manifest: existsSync27(join27(root, "docs/governance/MANIFEST.yml")),
7334
+ currentWork: existsSync27(join27(root, "docs/reference/execution/current-work.md")),
6280
7335
  packages: {
6281
7336
  expected,
6282
7337
  docGov,
@@ -6338,17 +7393,17 @@ function inspectGit2(root) {
6338
7393
  }
6339
7394
 
6340
7395
  // 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";
7396
+ import { existsSync as existsSync29 } from "node:fs";
7397
+ import { join as join29, resolve as resolve9, sep as sep3 } from "node:path";
6343
7398
 
6344
7399
  // 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";
7400
+ import { existsSync as existsSync28, statSync as statSync9 } from "node:fs";
7401
+ import { join as join28, relative as relative11, resolve as resolve8, sep as sep2 } from "node:path";
6347
7402
  function inspectDevSpaceHealth(options) {
6348
7403
  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");
7404
+ const configDirectory = join28(options.homeDir, ".devspace");
7405
+ const configPath = join28(configDirectory, "config.json");
7406
+ const authPath = join28(configDirectory, "auth.json");
6352
7407
  const installedResult = run("devspace", ["--version"], 3e3);
6353
7408
  const installedVersion = installedResult.ok ? installedResult.stdout.match(/\b\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?\b/)?.[0] : void 0;
6354
7409
  const latestResult = run(
@@ -6371,9 +7426,9 @@ function inspectDevSpaceHealth(options) {
6371
7426
  (repositoryPath) => allowedRoots.some((root) => isPathInside(repositoryPath, root))
6372
7427
  ) ? "complete" : "partial";
6373
7428
  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;
7429
+ const directoryMode = process.platform !== "win32" && existsSync28(configDirectory) ? modeString(statSync9(configDirectory).mode) : void 0;
7430
+ const fileMode = process.platform !== "win32" && existsSync28(configPath) ? modeString(statSync9(configPath).mode) : void 0;
7431
+ const authMode = process.platform !== "win32" && existsSync28(authPath) ? modeString(statSync9(authPath).mode) : void 0;
6377
7432
  const update = installedVersion && latestVersion ? installedVersion === latestVersion ? "current" : "available" : "unknown";
6378
7433
  const recommendations = [];
6379
7434
  let status = "healthy";
@@ -6387,7 +7442,7 @@ function inspectDevSpaceHealth(options) {
6387
7442
  };
6388
7443
  if (!installedResult.ok) unhealthy("\u672C\u673A\u672A\u53D1\u73B0 DevSpace\uFF1B\u65E0\u6CD5\u4F7F\u7528\u5BBF\u4E3B\u5DE5\u4F5C\u533A\u670D\u52A1\u3002");
6389
7444
  if (!configExists) unhealthy("\u7F3A\u5C11 ~/.devspace/config.json\u3002");
6390
- if (!existsSync27(authPath)) unhealthy("\u7F3A\u5C11 ~/.devspace/auth.json\u3002");
7445
+ if (!existsSync28(authPath)) unhealthy("\u7F3A\u5C11 ~/.devspace/auth.json\u3002");
6391
7446
  if (directoryMode && directoryMode !== "700")
6392
7447
  unhealthy(`~/.devspace \u76EE\u5F55\u6743\u9650\u4E3A ${directoryMode}\uFF0C\u5E94\u6536\u7D27\u4E3A 700\u3002`);
6393
7448
  if (fileMode && fileMode !== "600") unhealthy(`DevSpace \u914D\u7F6E\u6587\u4EF6\u6743\u9650\u4E3A ${fileMode}\uFF0C\u5E94\u4E3A 600\u3002`);
@@ -6420,7 +7475,7 @@ function inspectDevSpaceHealth(options) {
6420
7475
  exists: configExists,
6421
7476
  ...directoryMode ? { directoryMode } : {},
6422
7477
  ...fileMode ? { fileMode } : {},
6423
- authExists: existsSync27(authPath),
7478
+ authExists: existsSync28(authPath),
6424
7479
  ...authMode ? { authMode } : {},
6425
7480
  bind,
6426
7481
  portValid: configExists && typeof configValue.port === "number" && Number.isInteger(configValue.port) && configValue.port > 0 && configValue.port <= 65535,
@@ -6443,7 +7498,7 @@ function isLoopbackHost(host) {
6443
7498
  return ["127.0.0.1", "localhost", "::1"].includes(host.trim().toLowerCase());
6444
7499
  }
6445
7500
  function isPathInside(path, root) {
6446
- const fromRoot = relative10(resolve7(root), resolve7(path));
7501
+ const fromRoot = relative11(resolve8(root), resolve8(path));
6447
7502
  return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep2}`);
6448
7503
  }
6449
7504
 
@@ -6461,9 +7516,9 @@ var MCP_DISCOVERY_PATHS = {
6461
7516
  }
6462
7517
  };
6463
7518
  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);
7519
+ const codexConfig = join29(homeDir, MCP_DISCOVERY_PATHS.user.codex);
7520
+ const claudeConfig = join29(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode);
7521
+ const grokConfig = join29(homeDir, MCP_DISCOVERY_PATHS.user.grok);
6467
7522
  const hostEnvironment = {
6468
7523
  mcp: {
6469
7524
  codexUser: { path: codexConfig, names: tomlMcpNames(codexConfig) },
@@ -6471,11 +7526,11 @@ function inspectHostEnvironment(homeDir, grokVersion, repositoryPaths, devspaceS
6471
7526
  grokUser: { path: grokConfig, names: tomlMcpNames(grokConfig) }
6472
7527
  },
6473
7528
  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")),
7529
+ codexUser: inspectSkillRoot(join29(homeDir, ".agents/skills")),
7530
+ claudeCodeUser: inspectSkillRoot(join29(homeDir, ".claude/skills")),
7531
+ grokUser: inspectSkillRoot(join29(homeDir, ".grok/skills")),
7532
+ grokAgentsCompatibility: inspectSkillRoot(join29(homeDir, ".agents/skills")),
7533
+ grokClaudeCompatibility: inspectSkillRoot(join29(homeDir, ".claude/skills")),
6479
7534
  ssot: inspectUserSkillsSsot(homeDir)
6480
7535
  },
6481
7536
  grok: {
@@ -6496,20 +7551,20 @@ function inspectHostEnvironment(homeDir, grokVersion, repositoryPaths, devspaceS
6496
7551
  function inspectSkillRoot(path) {
6497
7552
  const exists = pathLexists(path) && safeIsDirectory(path);
6498
7553
  const names = exists ? safeReadDir(path).filter(
6499
- (name) => !name.startsWith(".") && existsSync28(join28(path, name, "SKILL.md"))
7554
+ (name) => !name.startsWith(".") && existsSync29(join29(path, name, "SKILL.md"))
6500
7555
  ) : [];
6501
7556
  return { path, exists, names };
6502
7557
  }
6503
7558
  function claudeProjectLocalMcpNames(homeDir, root) {
6504
7559
  if (!homeDir) return [];
6505
- const value = readJson4(join28(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode));
7560
+ const value = readJson4(join29(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode));
6506
7561
  if (!isRecord3(value) || !isRecord3(value.projects)) return [];
6507
7562
  const candidates = new Set(
6508
- [resolve8(root), safeRealpath(root)].filter((path) => Boolean(path))
7563
+ [resolve9(root), safeRealpath(root)].filter((path) => Boolean(path))
6509
7564
  );
6510
7565
  const names = /* @__PURE__ */ new Set();
6511
7566
  for (const [path, project] of Object.entries(value.projects)) {
6512
- const projectPaths = [resolve8(path), safeRealpath(path)].filter(
7567
+ const projectPaths = [resolve9(path), safeRealpath(path)].filter(
6513
7568
  (candidate) => Boolean(candidate)
6514
7569
  );
6515
7570
  if (!projectPaths.some((candidate) => candidates.has(candidate)) || !isRecord3(project) || !isRecord3(project.mcpServers))
@@ -6531,7 +7586,7 @@ function inspectGrokProject(root, homeDir, grokVersion) {
6531
7586
  const result = spawnPlatformSync("grok", ["inspect", "--json"], {
6532
7587
  cwd: root,
6533
7588
  encoding: "utf8",
6534
- env: { ...process.env, HOME: homeDir, GROK_HOME: join28(homeDir, ".grok") },
7589
+ env: { ...process.env, HOME: homeDir, GROK_HOME: join29(homeDir, ".grok") },
6535
7590
  maxBuffer: 10 * 1024 * 1024,
6536
7591
  stdio: ["ignore", "pipe", "ignore"],
6537
7592
  timeout: 8e3
@@ -6540,7 +7595,7 @@ function inspectGrokProject(root, homeDir, grokVersion) {
6540
7595
  const value = JSON.parse(result.stdout);
6541
7596
  if (!isRecord3(value)) return empty("failed");
6542
7597
  const userClaudeNames = new Set(
6543
- jsonObjectKeys(join28(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode), "mcpServers")
7598
+ jsonObjectKeys(join29(homeDir, MCP_DISCOVERY_PATHS.user.claudeCode), "mcpServers")
6544
7599
  );
6545
7600
  const localClaudeNames = new Set(claudeProjectLocalMcpNames(homeDir, root));
6546
7601
  const effectiveMcp = Array.isArray(value.mcpServers) ? value.mcpServers.flatMap((item) => {
@@ -6608,26 +7663,26 @@ function inferGrokMcpScope(name, sourceType, sourcePath, root, homeDir, userClau
6608
7663
  if (userClaudeNames.has(name)) return "user";
6609
7664
  return "unknown";
6610
7665
  }
6611
- const resolvedSource = safeRealpath(sourcePath) ?? resolve8(sourcePath);
6612
- const resolvedRoot = safeRealpath(root) ?? resolve8(root);
6613
- if (resolvedSource === join28(resolvedRoot, MCP_DISCOVERY_PATHS.project.claudeCodeShared))
7666
+ const resolvedSource = safeRealpath(sourcePath) ?? resolve9(sourcePath);
7667
+ const resolvedRoot = safeRealpath(root) ?? resolve9(root);
7668
+ if (resolvedSource === join29(resolvedRoot, MCP_DISCOVERY_PATHS.project.claudeCodeShared))
6614
7669
  return "project-shared";
6615
7670
  if (resolvedSource.startsWith(resolvedRoot + sep3)) return "project";
6616
- if (homeDir && resolvedSource === join28(resolve8(homeDir), MCP_DISCOVERY_PATHS.user.claudeCode)) {
7671
+ if (homeDir && resolvedSource === join29(resolve9(homeDir), MCP_DISCOVERY_PATHS.user.claudeCode)) {
6617
7672
  if (localClaudeNames.has(name)) return "project-local";
6618
7673
  if (userClaudeNames.has(name)) return "user";
6619
7674
  }
6620
- if (homeDir && resolvedSource.startsWith(resolve8(homeDir) + sep3)) return "user";
7675
+ if (homeDir && resolvedSource.startsWith(resolve9(homeDir) + sep3)) return "user";
6621
7676
  if (sourceType === "project") return "project";
6622
7677
  return "unknown";
6623
7678
  }
6624
7679
 
6625
7680
  // 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";
7681
+ import { existsSync as existsSync30, lstatSync as lstatSync13, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
7682
+ import { join as join30, relative as relative12, sep as sep4 } from "node:path";
6628
7683
  var supportsPosixModes = process.platform !== "win32";
6629
7684
  function inspectRepositorySecrets(root, id, secretsRoot, isRepository, environmentPolicy) {
6630
- const centralPath = join29(secretsRoot, id);
7685
+ const centralPath = join30(secretsRoot, id);
6631
7686
  const centralRealPath = safeRealpath(centralPath);
6632
7687
  const localOnlyReasons = new Map(
6633
7688
  (environmentPolicy?.localOnly ?? []).map((entry) => [entry.path, entry.reason])
@@ -6639,16 +7694,16 @@ function inspectRepositorySecrets(root, id, secretsRoot, isRepository, environme
6639
7694
  tracked: isRepository ? gitTracks(root, path) : false,
6640
7695
  template: isEnvironmentTemplate(path),
6641
7696
  fixture: isEnvironmentFixture(path),
6642
- symlink: lstatSync12(join29(root, path)).isSymbolicLink(),
6643
- centralized: pointsInside(join29(root, path), centralRealPath),
7697
+ symlink: lstatSync13(join30(root, path)).isSymbolicLink(),
7698
+ centralized: pointsInside(join30(root, path), centralRealPath),
6644
7699
  localOnly: localOnlyReason !== void 0,
6645
7700
  ...localOnlyReason !== void 0 ? { localOnlyReason } : {}
6646
7701
  };
6647
7702
  });
6648
7703
  return {
6649
- centralDirectory: existsSync29(centralPath) ? "present" : "absent",
6650
- centralMode: supportsPosixModes && existsSync29(centralPath) ? modeString(statSync9(centralPath).mode) : void 0,
6651
- centralFiles: existsSync29(centralPath) ? collectCentralSecretFiles(centralPath) : [],
7704
+ centralDirectory: existsSync30(centralPath) ? "present" : "absent",
7705
+ centralMode: supportsPosixModes && existsSync30(centralPath) ? modeString(statSync10(centralPath).mode) : void 0,
7706
+ centralFiles: existsSync30(centralPath) ? collectCentralSecretFiles(centralPath) : [],
6652
7707
  repositoryEnvFiles: envFiles
6653
7708
  };
6654
7709
  }
@@ -6671,12 +7726,12 @@ function collectEnvironmentFiles(root, current = root, depth = 0) {
6671
7726
  if (depth > 5) return [];
6672
7727
  const found = [];
6673
7728
  try {
6674
- for (const entry of readdirSync12(current, { withFileTypes: true })) {
7729
+ for (const entry of readdirSync13(current, { withFileTypes: true })) {
6675
7730
  if (entry.isDirectory()) {
6676
7731
  if (!SKIP_ENV_DIRECTORIES.has(entry.name))
6677
- found.push(...collectEnvironmentFiles(root, join29(current, entry.name), depth + 1));
7732
+ found.push(...collectEnvironmentFiles(root, join30(current, entry.name), depth + 1));
6678
7733
  } else if (isEnvironmentFilename(entry.name) && !isProviderGeneratedEnvironmentFile(entry.name)) {
6679
- found.push(relative11(root, join29(current, entry.name)).replaceAll("\\", "/"));
7734
+ found.push(relative12(root, join30(current, entry.name)).replaceAll("\\", "/"));
6680
7735
  }
6681
7736
  }
6682
7737
  } catch {
@@ -6688,13 +7743,13 @@ function collectCentralSecretFiles(root, current = root, depth = 0) {
6688
7743
  if (depth > 3) return [];
6689
7744
  const found = [];
6690
7745
  try {
6691
- for (const entry of readdirSync12(current, { withFileTypes: true })) {
6692
- const path = join29(current, entry.name);
7746
+ for (const entry of readdirSync13(current, { withFileTypes: true })) {
7747
+ const path = join30(current, entry.name);
6693
7748
  if (entry.isDirectory()) found.push(...collectCentralSecretFiles(root, path, depth + 1));
6694
7749
  else
6695
7750
  found.push({
6696
- path: relative11(root, path).replaceAll("\\", "/"),
6697
- mode: supportsPosixModes ? modeString(lstatSync12(path).mode) : "unknown"
7751
+ path: relative12(root, path).replaceAll("\\", "/"),
7752
+ mode: supportsPosixModes ? modeString(lstatSync13(path).mode) : "unknown"
6698
7753
  });
6699
7754
  }
6700
7755
  } catch {
@@ -6719,10 +7774,10 @@ function isEnvironmentFixture(path) {
6719
7774
  return /(^|[\\/])(?:tests?|__tests__)[\\/]fixtures?[\\/]/i.test(path) || /(^|[\\/])__fixtures__[\\/]/i.test(path);
6720
7775
  }
6721
7776
  function pointsInside(path, expectedRoot) {
6722
- if (!expectedRoot || !lstatSync12(path).isSymbolicLink()) return false;
7777
+ if (!expectedRoot || !lstatSync13(path).isSymbolicLink()) return false;
6723
7778
  const target = safeRealpath(path);
6724
7779
  if (!target) return false;
6725
- const fromRoot = relative11(expectedRoot, target);
7780
+ const fromRoot = relative12(expectedRoot, target);
6726
7781
  return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep4}`);
6727
7782
  }
6728
7783
  function hasUnsafeCentralSecretPermissions(secrets) {
@@ -6730,24 +7785,24 @@ function hasUnsafeCentralSecretPermissions(secrets) {
6730
7785
  return secrets.centralDirectory === "present" && (secrets.centralMode !== "700" || secrets.centralFiles.some((file) => file.mode !== "600"));
6731
7786
  }
6732
7787
  function inspectSecretsRoot(path) {
6733
- return existsSync29(path) ? {
7788
+ return existsSync30(path) ? {
6734
7789
  path,
6735
7790
  exists: true,
6736
- ...supportsPosixModes ? { mode: modeString(statSync9(path).mode) } : {}
7791
+ ...supportsPosixModes ? { mode: modeString(statSync10(path).mode) } : {}
6737
7792
  } : { path, exists: false };
6738
7793
  }
6739
7794
 
6740
7795
  // 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";
7796
+ import { existsSync as existsSync31, lstatSync as lstatSync14, realpathSync as realpathSync8 } from "node:fs";
7797
+ import { join as join31 } from "node:path";
6743
7798
  function countAutomaticSkillsNeedingReview(skills) {
6744
7799
  return skills.automatic.filter(
6745
7800
  (item) => !item.managed || !item.registryId || item.expectedPlacement !== "auto" || item.expectedScope === "user"
6746
7801
  ).length;
6747
7802
  }
6748
7803
  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"));
7804
+ const lock = readJson4(join31(root, ".pro-gov/assets.lock.json"));
7805
+ const assetManifest = readJson4(join31(root, ".pro-gov/assets.json"));
6751
7806
  const managed = /* @__PURE__ */ new Set();
6752
7807
  const bundleIds = stringArray(isRecord3(lock) ? lock.bundleIds : void 0);
6753
7808
  if (isRecord3(lock) && Array.isArray(lock.assets)) {
@@ -6759,7 +7814,7 @@ function inspectSkills(root, grokInspection, registeredSkills, userSkills) {
6759
7814
  }
6760
7815
  const inspectPlacement = (placement) => {
6761
7816
  const directory = placement === "auto" ? "skills" : "manual-skills";
6762
- const skillRoot = join30(root, ".agents", directory);
7817
+ const skillRoot = join31(root, ".agents", directory);
6763
7818
  if (!pathLexists(skillRoot) || !safeIsDirectory(skillRoot)) return [];
6764
7819
  return safeReadDir(skillRoot).filter((name) => !name.startsWith(".")).map((name) => inspectSkillItem(skillRoot, directory, name, managed, registeredSkills));
6765
7820
  };
@@ -6814,15 +7869,15 @@ function inspectSkills(root, grokInspection, registeredSkills, userSkills) {
6814
7869
  },
6815
7870
  hosts: {
6816
7871
  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,
7872
+ claudeCodeProject: inspectSkillRoot(join31(root, ".claude/skills")).names.length,
7873
+ grokNativeProject: inspectSkillRoot(join31(root, ".grok/skills")).names.length,
6819
7874
  grokEffective: grokInspection.skills
6820
7875
  }
6821
7876
  };
6822
7877
  }
6823
7878
  function inspectSkillItem(skillRoot, directory, name, managed, registeredSkills) {
6824
- const path = join30(skillRoot, name);
6825
- const stat = lstatSync13(path);
7879
+ const path = join31(skillRoot, name);
7880
+ const stat = lstatSync14(path);
6826
7881
  let kind = stat.isSymbolicLink() ? "symlink" : stat.isDirectory() ? "directory" : "file";
6827
7882
  let realPath;
6828
7883
  try {
@@ -6831,7 +7886,7 @@ function inspectSkillItem(skillRoot, directory, name, managed, registeredSkills)
6831
7886
  if (stat.isSymbolicLink()) kind = "dangling-symlink";
6832
7887
  }
6833
7888
  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;
7889
+ const classification = registered ? void 0 : realPath && isPluginPack(realPath) ? "plugin-pack" : kind === "directory" && existsSync31(join31(path, "SKILL.md")) ? "project-local" : void 0;
6835
7890
  return {
6836
7891
  name,
6837
7892
  kind,
@@ -6843,11 +7898,11 @@ function inspectSkillItem(skillRoot, directory, name, managed, registeredSkills)
6843
7898
  };
6844
7899
  }
6845
7900
  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")));
7901
+ const skillsRoot = join31(path, "skills");
7902
+ return existsSync31(join31(path, ".codex-plugin/plugin.json")) && safeIsDirectory(skillsRoot) && safeReadDir(skillsRoot).some((name) => existsSync31(join31(skillsRoot, name, "SKILL.md")));
6848
7903
  }
6849
7904
  function inspectInvalidSkillEntries(root, directory) {
6850
- const skillRoot = join30(root, ".agents", directory);
7905
+ const skillRoot = join31(root, ".agents", directory);
6851
7906
  if (!pathLexists(skillRoot) || !safeIsDirectory(skillRoot)) return [];
6852
7907
  return safeReadDir(skillRoot).flatMap((name) => {
6853
7908
  if (name === ".gitkeep") return [];
@@ -6855,14 +7910,14 @@ function inspectInvalidSkillEntries(root, directory) {
6855
7910
  return [{ path: `.agents/${directory}/${name}`, reason: "metadata-junk" }];
6856
7911
  if (name.startsWith("."))
6857
7912
  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" }] : [];
7913
+ const path = join31(skillRoot, name);
7914
+ return !lstatSync14(path).isDirectory() && !lstatSync14(path).isSymbolicLink() ? [{ path: `.agents/${directory}/${name}`, reason: "unexpected-file" }] : [];
6860
7915
  });
6861
7916
  }
6862
7917
  function skillDuplicatesUser(item, root, userSkills) {
6863
7918
  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);
7919
+ const automatic = join31(root, ".agents/skills", item.name);
7920
+ const manual = join31(root, ".agents/manual-skills", item.name);
6866
7921
  const realPath = safeRealpath(pathLexists(automatic) ? automatic : manual);
6867
7922
  return realPath ? userSkills.realPaths.has(realPath) : false;
6868
7923
  }
@@ -6882,13 +7937,13 @@ function skillPlacementDrift(item, actualPlacement) {
6882
7937
  return [];
6883
7938
  }
6884
7939
  function inspectClaudeSkillRoot(root) {
6885
- const path = join30(root, ".claude/skills");
7940
+ const path = join31(root, ".claude/skills");
6886
7941
  if (!pathLexists(path)) return "missing";
6887
- const stat = lstatSync13(path);
7942
+ const stat = lstatSync14(path);
6888
7943
  if (stat.isSymbolicLink()) {
6889
7944
  try {
6890
7945
  const target = realpathSync8(path);
6891
- return target === realpathSync8(join30(root, ".agents/skills")) ? "shared-root" : "other";
7946
+ return target === realpathSync8(join31(root, ".agents/skills")) ? "shared-root" : "other";
6892
7947
  } catch {
6893
7948
  return "dangling-symlink";
6894
7949
  }
@@ -6901,27 +7956,27 @@ function inspectSkillRegistry(executionEngineRoot) {
6901
7956
  health: { source: 0, registered: 0, bundled: 0, bundles: 0 },
6902
7957
  skills: []
6903
7958
  };
6904
- const agentAssetsRoot = join30(executionEngineRoot, "agent-assets");
6905
- const registry = readJson4(join30(agentAssetsRoot, "registry.json"));
7959
+ const agentAssetsRoot = join31(executionEngineRoot, "agent-assets");
7960
+ const registry = readJson4(join31(agentAssetsRoot, "registry.json"));
6906
7961
  const assets = isRecord3(registry) && Array.isArray(registry.assets) ? registry.assets : [];
6907
7962
  const registeredSkills = assets.filter((asset) => isRecord3(asset) && asset.kind === "skill");
6908
- const bundleRoot = join30(agentAssetsRoot, "bundles");
7963
+ const bundleRoot = join31(agentAssetsRoot, "bundles");
6909
7964
  const bundleFiles = safeReadDir(bundleRoot).filter((file) => file.endsWith(".json"));
6910
7965
  const bundledIds = /* @__PURE__ */ new Set();
6911
7966
  for (const file of bundleFiles) {
6912
- const bundle = readJson4(join30(bundleRoot, file));
7967
+ const bundle = readJson4(join31(bundleRoot, file));
6913
7968
  if (!isRecord3(bundle) || !Array.isArray(bundle.assets)) continue;
6914
7969
  for (const id of bundle.assets) if (typeof id === "string") bundledIds.add(id);
6915
7970
  }
6916
7971
  const sourceRoots = [
6917
- join30(agentAssetsRoot, "skills/pie-skills"),
6918
- join30(agentAssetsRoot, "skills/npx-skills/.agents/skills")
7972
+ join31(agentAssetsRoot, "skills/pie-skills"),
7973
+ join31(agentAssetsRoot, "skills/npx-skills/.agents/skills")
6919
7974
  ];
6920
7975
  const source = sourceRoots.reduce(
6921
- (count, root) => count + safeReadDir(root).filter((name) => existsSync30(join30(root, name, "SKILL.md"))).length,
7976
+ (count, root) => count + safeReadDir(root).filter((name) => existsSync31(join31(root, name, "SKILL.md"))).length,
6922
7977
  0
6923
7978
  ) + registeredSkills.filter(
6924
- (asset) => isRecord3(asset) && asset.sourceKind === "local-pack" && typeof asset.sourcePath === "string" && isPluginPack(join30(agentAssetsRoot, asset.sourcePath))
7979
+ (asset) => isRecord3(asset) && asset.sourceKind === "local-pack" && typeof asset.sourcePath === "string" && isPluginPack(join31(agentAssetsRoot, asset.sourcePath))
6925
7980
  ).length;
6926
7981
  return {
6927
7982
  health: {
@@ -6938,7 +7993,7 @@ function inspectSkillRegistry(executionEngineRoot) {
6938
7993
  return [
6939
7994
  {
6940
7995
  id: asset.id,
6941
- sourceRealPath: safeRealpath(join30(agentAssetsRoot, asset.sourcePath)),
7996
+ sourceRealPath: safeRealpath(join31(agentAssetsRoot, asset.sourcePath)),
6942
7997
  defaultPlacement: asset.defaultPlacement,
6943
7998
  defaultScope: asset.defaultScope === "user" ? "user" : "project"
6944
7999
  }
@@ -6953,15 +8008,15 @@ function inspectUserSkillEvidence(root) {
6953
8008
  for (const name of safeReadDir(root)) {
6954
8009
  if (name.startsWith(".")) continue;
6955
8010
  names.add(name);
6956
- const realPath = safeRealpath(join30(root, name));
8011
+ const realPath = safeRealpath(join31(root, name));
6957
8012
  if (realPath) realPaths.add(realPath);
6958
8013
  }
6959
8014
  return { names, realPaths };
6960
8015
  }
6961
8016
 
6962
8017
  // 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";
8018
+ import { existsSync as existsSync32, readdirSync as readdirSync14, statSync as statSync11 } from "node:fs";
8019
+ import { join as join32 } from "node:path";
6965
8020
  function buildTechnologyMatrix(governance, repositories) {
6966
8021
  if (!governance || governance.technologies.length === 0) return [];
6967
8022
  const policy = governance.versionPolicy;
@@ -6988,8 +8043,8 @@ function buildTechnologyMatrix(governance, repositories) {
6988
8043
  }).filter((item) => item !== void 0);
6989
8044
  }).flat();
6990
8045
  const fileSignal = (technology.files ?? []).some(
6991
- (path) => hasUsableTechnologyFile(join31(repository.path, path)) || packageManifests.some(
6992
- (manifest) => hasUsableTechnologyFile(join31(manifest.directory, path))
8046
+ (path) => hasUsableTechnologyFile(join32(repository.path, path)) || packageManifests.some(
8047
+ (manifest) => hasUsableTechnologyFile(join32(manifest.directory, path))
6993
8048
  )
6994
8049
  );
6995
8050
  const modelSignal = [
@@ -7080,14 +8135,14 @@ function buildTechnologyMatrix(governance, repositories) {
7080
8135
  }).filter((technology) => technology.projectCount > 0);
7081
8136
  }
7082
8137
  function hasUsableTechnologyFile(path) {
7083
- if (!existsSync31(path)) return false;
8138
+ if (!existsSync32(path)) return false;
7084
8139
  try {
7085
- const info = statSync10(path);
8140
+ const info = statSync11(path);
7086
8141
  if (info.isFile()) return true;
7087
8142
  if (!info.isDirectory()) return false;
7088
- return readdirSync13(path, { withFileTypes: true }).some((entry) => {
8143
+ return readdirSync14(path, { withFileTypes: true }).some((entry) => {
7089
8144
  if (entry.name.startsWith(".")) return false;
7090
- const child = join31(path, entry.name);
8145
+ const child = join32(path, entry.name);
7091
8146
  if (entry.isDirectory()) return hasUsableTechnologyFile(child);
7092
8147
  return entry.name.toLowerCase() !== "readme.md";
7093
8148
  });
@@ -7099,7 +8154,7 @@ function inspectExclusiveOwnership(root, endpoint, governance) {
7099
8154
  const projectType = endpoint.projectType;
7100
8155
  return (governance?.exclusiveOwnership ?? []).flatMap((rule) => {
7101
8156
  if (projectType && rule.allowedProjectTypes.includes(projectType)) return [];
7102
- const paths = rule.paths.filter((path) => existsSync31(join31(root, path)));
8157
+ const paths = rule.paths.filter((path) => existsSync32(join32(root, path)));
7103
8158
  return paths.length > 0 ? [{ rule, paths }] : [];
7104
8159
  });
7105
8160
  }
@@ -7114,7 +8169,7 @@ function inspectProjectModel(root, endpoint, governance) {
7114
8169
  const detection = (id) => {
7115
8170
  const technology = technologyById.get(id);
7116
8171
  const packageMatch = technology?.packages?.some((name) => packages.has(name)) ?? false;
7117
- const fileMatch = technology?.files?.some((path) => existsSync31(join31(root, path))) ?? false;
8172
+ const fileMatch = technology?.files?.some((path) => existsSync32(join32(root, path))) ?? false;
7118
8173
  return { id, label: technology?.label ?? id, detected: packageMatch || fileMatch };
7119
8174
  };
7120
8175
  const selected = new Set(endpoint.capabilities ?? []);
@@ -7153,8 +8208,8 @@ function collectPackageNames(root) {
7153
8208
  }
7154
8209
 
7155
8210
  // 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";
8211
+ import { cpSync as cpSync3, existsSync as existsSync33, mkdirSync as mkdirSync11, writeFileSync as writeFileSync10 } from "node:fs";
8212
+ import { dirname as dirname17, join as join33 } from "node:path";
7158
8213
  import { fileURLToPath as fileURLToPath5 } from "node:url";
7159
8214
  function mergePortfolioAiHealthReport(existing, latest, allRepositoryIds) {
7160
8215
  const repositoriesById = /* @__PURE__ */ new Map();
@@ -7195,36 +8250,36 @@ function mergePortfolioAiHealthReport(existing, latest, allRepositoryIds) {
7195
8250
  };
7196
8251
  }
7197
8252
  function writePortfolioAiHealthReport(report, outDir) {
7198
- mkdirSync10(outDir, { recursive: true });
8253
+ mkdirSync11(outDir, { recursive: true });
7199
8254
  const dashboardAssets = findDashboardAssets();
7200
8255
  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));
8256
+ const source = join33(dashboardAssets, file);
8257
+ if (!existsSync33(source)) throw new Error(`Portfolio dashboard asset is missing: ${source}`);
8258
+ cpSync3(source, join33(outDir, file));
7204
8259
  }
7205
- const jsonPath = join32(outDir, "portfolio-ai-health.json");
7206
- const htmlPath = join32(outDir, "index.html");
7207
- writeFileSync9(jsonPath, `${JSON.stringify(report, null, 2)}
8260
+ const jsonPath = join33(outDir, "portfolio-ai-health.json");
8261
+ const htmlPath = join33(outDir, "index.html");
8262
+ writeFileSync10(jsonPath, `${JSON.stringify(report, null, 2)}
7208
8263
  `);
7209
- writeFileSync9(
7210
- join32(outDir, "data.js"),
8264
+ writeFileSync10(
8265
+ join33(outDir, "data.js"),
7211
8266
  `window.__PORTFOLIO_AI_HEALTH__ = ${safeJavaScriptJson2(report)};
7212
8267
  `
7213
8268
  );
7214
8269
  return { jsonPath, htmlPath };
7215
8270
  }
7216
8271
  function findDashboardAssets() {
7217
- const packageRoot2 = dirname16(dirname16(fileURLToPath5(import.meta.url)));
8272
+ const packageRoot2 = dirname17(dirname17(fileURLToPath5(import.meta.url)));
7218
8273
  const candidates = [
7219
8274
  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")
8275
+ join33(packageRoot2, ".dashboard-build"),
8276
+ join33(packageRoot2, "assets/portfolio-dashboard"),
8277
+ join33(process.cwd(), ".dashboard-build"),
8278
+ join33(process.cwd(), "assets/portfolio-dashboard"),
8279
+ join33(process.cwd(), "packages/pro-gov/.dashboard-build"),
8280
+ join33(process.cwd(), "packages/pro-gov/assets/portfolio-dashboard")
7226
8281
  ].filter((value) => Boolean(value));
7227
- const match = candidates.find((path) => existsSync32(join32(path, "index.html")));
8282
+ const match = candidates.find((path) => existsSync33(join33(path, "index.html")));
7228
8283
  if (!match)
7229
8284
  throw new Error(
7230
8285
  "Portfolio dashboard assets were not built. Run pnpm --filter @pieai/pro-gov build."
@@ -7242,8 +8297,8 @@ function inspectPortfolioAiHealth(options) {
7242
8297
  if (options.targetId && options.targetId !== "all" && endpoints.length === 0) {
7243
8298
  throw new Error(`Unknown portfolio target: ${options.targetId}`);
7244
8299
  }
7245
- const secretsRoot = options.secretsRoot ?? join33(
7246
- dirname17(
8300
+ const secretsRoot = options.secretsRoot ?? join34(
8301
+ dirname18(
7247
8302
  options.manifest.controlPlane?.path ?? allEndpoints[0]?.endpoint.path ?? process.cwd()
7248
8303
  ),
7249
8304
  ".secrets"
@@ -7252,9 +8307,9 @@ function inspectPortfolioAiHealth(options) {
7252
8307
  const grokVersion = commandVersion("grok");
7253
8308
  const executionEngineRoot = options.manifest.executionEngine?.path;
7254
8309
  const skillRegistry = inspectSkillRegistry(executionEngineRoot);
7255
- const userSkills = inspectUserSkillEvidence(join33(homeDir, ".agents/skills"));
8310
+ const userSkills = inspectUserSkillEvidence(join34(homeDir, ".agents/skills"));
7256
8311
  const expectedPackageVersion = packageVersion(
7257
- join33(executionEngineRoot ?? "", "packages/pro-gov/package.json")
8312
+ join34(executionEngineRoot ?? "", "packages/pro-gov/package.json")
7258
8313
  );
7259
8314
  const repositories = endpoints.map(
7260
8315
  ({ endpoint, role }) => inspectRepository(
@@ -7311,7 +8366,7 @@ function collectEndpoints(manifest) {
7311
8366
  for (const target of manifest.targets) result.push({ endpoint: target, role: "target" });
7312
8367
  const seen = /* @__PURE__ */ new Set();
7313
8368
  return result.filter(({ endpoint }) => {
7314
- const key = resolve9(endpoint.path);
8369
+ const key = resolve10(endpoint.path);
7315
8370
  if (seen.has(key)) return false;
7316
8371
  seen.add(key);
7317
8372
  return true;
@@ -7328,13 +8383,13 @@ function inspectRepository(endpoint, role, secretsRoot, homeDir, expectedPackage
7328
8383
  const hooks = inspectHooks(root);
7329
8384
  const docs = inspectDocs(root, role === "execution-engine" ? void 0 : expectedPackageVersion);
7330
8385
  const mcp = {
7331
- codexProject: tomlMcpNames(join33(root, MCP_DISCOVERY_PATHS.project.codex)),
8386
+ codexProject: tomlMcpNames(join34(root, MCP_DISCOVERY_PATHS.project.codex)),
7332
8387
  claudeCodeProjectShared: jsonObjectKeys(
7333
- join33(root, MCP_DISCOVERY_PATHS.project.claudeCodeShared),
8388
+ join34(root, MCP_DISCOVERY_PATHS.project.claudeCodeShared),
7334
8389
  "mcpServers"
7335
8390
  ),
7336
8391
  claudeCodeProjectLocal: claudeProjectLocalMcpNames(homeDir, root),
7337
- grokProject: tomlMcpNames(join33(root, MCP_DISCOVERY_PATHS.project.grok)),
8392
+ grokProject: tomlMcpNames(join34(root, MCP_DISCOVERY_PATHS.project.grok)),
7338
8393
  grokEffective: grokInspection.effectiveMcp,
7339
8394
  grokInspection: grokInspection.inspection
7340
8395
  };
@@ -7968,11 +9023,11 @@ function isHost2(value) {
7968
9023
  return value === "codex" || value === "claude-code" || value === "gemini-cli" || value === "antigravity";
7969
9024
  }
7970
9025
  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;
9026
+ const agentAssetsDir = manifest?.executionEngine?.path ? join35(manifest.executionEngine.path, "agent-assets") : void 0;
9027
+ return agentAssetsDir && existsSync34(join35(agentAssetsDir, "registry.json")) ? agentAssetsDir : void 0;
7973
9028
  }
7974
9029
  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";
9030
+ const expectedPath = loaded.manifest?.executionEngine?.path ? join35(loaded.manifest.executionEngine.path, "agent-assets/registry.json") : "executionEngine.path/agent-assets/registry.json";
7976
9031
  const displayExpectedPath = expectedPath.replaceAll("\\", "/");
7977
9032
  const issue = {
7978
9033
  type: "missing-control-plane-registry",
@@ -8011,10 +9066,10 @@ function printUsage5() {
8011
9066
  }
8012
9067
  function readExistingAiHealthReport(outDir, portfolioId) {
8013
9068
  if (!outDir) return void 0;
8014
- const path = join34(outDir, "portfolio-ai-health.json");
8015
- if (!existsSync33(path)) return void 0;
9069
+ const path = join35(outDir, "portfolio-ai-health.json");
9070
+ if (!existsSync34(path)) return void 0;
8016
9071
  try {
8017
- const value = JSON.parse(readFileSync18(path, "utf8"));
9072
+ const value = JSON.parse(readFileSync19(path, "utf8"));
8018
9073
  if (!value || typeof value !== "object" || value.portfolioId !== portfolioId || !Array.isArray(value.repositories))
8019
9074
  return void 0;
8020
9075
  return value;
@@ -8024,8 +9079,8 @@ function readExistingAiHealthReport(outDir, portfolioId) {
8024
9079
  }
8025
9080
 
8026
9081
  // 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";
9082
+ import { existsSync as existsSync35, lstatSync as lstatSync15, readFileSync as readFileSync20, readlinkSync as readlinkSync4 } from "node:fs";
9083
+ import { join as join36 } from "node:path";
8029
9084
  function runSync(args) {
8030
9085
  const check = args.includes("--check");
8031
9086
  if (!check) {
@@ -8053,7 +9108,7 @@ function runSync(args) {
8053
9108
  console.log("pro-gov sync check");
8054
9109
  console.log(`profile: ${profile}`);
8055
9110
  for (const file of planStarterFiles(profile)) {
8056
- const targetPath = join35(process.cwd(), file.targetPath);
9111
+ const targetPath = join36(process.cwd(), file.targetPath);
8057
9112
  const stat = safeLstat3(targetPath);
8058
9113
  if (!stat) {
8059
9114
  if (file.ownership === "optional-guardrail") continue;
@@ -8077,8 +9132,8 @@ function runSync(args) {
8077
9132
  }
8078
9133
  continue;
8079
9134
  }
8080
- const source = readFileSync19(file.absoluteSourcePath, "utf8");
8081
- const target = readFileSync19(targetPath, "utf8");
9135
+ const source = readFileSync20(file.absoluteSourcePath, "utf8");
9136
+ const target = readFileSync20(targetPath, "utf8");
8082
9137
  if (!matchesExpectedContent(file.targetPath, source, target)) {
8083
9138
  console.log(`different: ${file.targetPath}`);
8084
9139
  differences += 1;
@@ -8112,13 +9167,13 @@ function normalizeMarkdownTableCell(cell) {
8112
9167
  }
8113
9168
  function inferInstalledProfile(root) {
8114
9169
  const installed = ["engineering-runtime", "doc-only"].filter(
8115
- (profile) => existsSync34(join35(root, `docs/governance/agents-routing/${profile}-v1.1.md`))
9170
+ (profile) => existsSync35(join36(root, `docs/governance/agents-routing/${profile}-v1.1.md`))
8116
9171
  );
8117
9172
  return installed.length === 1 ? installed[0] : void 0;
8118
9173
  }
8119
9174
  function safeLstat3(path) {
8120
9175
  try {
8121
- return lstatSync14(path);
9176
+ return lstatSync15(path);
8122
9177
  } catch {
8123
9178
  return void 0;
8124
9179
  }
@@ -8139,6 +9194,7 @@ var COMMANDS = [
8139
9194
  "assets check [--target <path>] [--strict-registry] [--json]",
8140
9195
  "assets public-check [--public-root <path>] [--private-root <path>] [--json]",
8141
9196
  "assets npx add|update ... --plan",
9197
+ "assets catalog build|check [--native-links] [--json]",
8142
9198
  "portfolio check --config <path> [--json]",
8143
9199
  "portfolio plan --config <path> [--target <id|all>] [--json]",
8144
9200
  "portfolio assets-check --config <path> [--target <id|all>] [--json]",