@skillsmith/cli 0.8.2 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1446,7 +1446,7 @@ import { Command as Command32 } from "commander";
1446
1446
  import { Command as Command2 } from "commander";
1447
1447
 
1448
1448
  // src/config.ts
1449
- import { join as join15 } from "path";
1449
+ import { join as join16 } from "path";
1450
1450
  import { homedir as homedir8 } from "os";
1451
1451
 
1452
1452
  // ../core/dist/src/install/paths.js
@@ -1460,7 +1460,8 @@ var CLIENT_NATIVE_PATHS = {
1460
1460
  windsurf: join(homedir(), ".codeium", "windsurf", "skills"),
1461
1461
  agents: join(homedir(), ".agents", "skills"),
1462
1462
  opencode: join(homedir(), ".config", "opencode", "skills"),
1463
- hermes: join(homedir(), ".hermes", "skills")
1463
+ hermes: join(homedir(), ".hermes", "skills"),
1464
+ grok: join(homedir(), ".grok", "skills")
1464
1465
  };
1465
1466
  var CANONICAL_CLIENT = "claude-code";
1466
1467
  var CLIENT_IDS = Object.freeze([
@@ -1470,7 +1471,8 @@ var CLIENT_IDS = Object.freeze([
1470
1471
  "windsurf",
1471
1472
  "agents",
1472
1473
  "opencode",
1473
- "hermes"
1474
+ "hermes",
1475
+ "grok"
1474
1476
  ]);
1475
1477
  function getCanonicalInstallPath() {
1476
1478
  return CLIENT_NATIVE_PATHS[CANONICAL_CLIENT];
@@ -1648,12 +1650,76 @@ async function removeLinks(skillId) {
1648
1650
  // ../core/dist/src/install/agent-pack-installer.js
1649
1651
  import { existsSync as existsSync9 } from "node:fs";
1650
1652
  import { homedir as homedir6 } from "node:os";
1651
- import { join as join13 } from "node:path";
1653
+ import { join as join14 } from "node:path";
1652
1654
 
1653
1655
  // ../core/dist/src/config/index.js
1654
1656
  import { homedir as homedir3 } from "os";
1655
- import { join as join3 } from "path";
1656
- import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync, chmodSync } from "fs";
1657
+ import { join as join4 } from "path";
1658
+ import { existsSync as existsSync2, readFileSync, mkdirSync, chmodSync as chmodSync2 } from "fs";
1659
+
1660
+ // ../core/dist/src/config/config-atomic-write.js
1661
+ import { randomBytes } from "node:crypto";
1662
+ import { openSync, closeSync, unlinkSync, writeFileSync, renameSync, statSync, chmodSync } from "node:fs";
1663
+ import { dirname as dirname2, join as join3 } from "node:path";
1664
+ var LOCK_ACQUIRE_TIMEOUT_MS = 5e3;
1665
+ var LOCK_RETRY_DELAY_MS = 20;
1666
+ var STALE_LOCK_AGE_MS = 1e4;
1667
+ function sleepSync(ms) {
1668
+ const view = new Int32Array(new SharedArrayBuffer(4));
1669
+ Atomics.wait(view, 0, 0, ms);
1670
+ }
1671
+ function acquireConfigLock(configPath2, timeoutMs = LOCK_ACQUIRE_TIMEOUT_MS) {
1672
+ const lockPath = `${configPath2}.lock`;
1673
+ const deadline = Date.now() + timeoutMs;
1674
+ let staleClearAttempted = false;
1675
+ for (; ; ) {
1676
+ try {
1677
+ const fd = openSync(lockPath, "wx", 384);
1678
+ try {
1679
+ writeFileSync(fd, String(process.pid));
1680
+ } finally {
1681
+ closeSync(fd);
1682
+ }
1683
+ return () => {
1684
+ try {
1685
+ unlinkSync(lockPath);
1686
+ } catch {
1687
+ }
1688
+ };
1689
+ } catch (err) {
1690
+ if (err.code !== "EEXIST")
1691
+ throw err;
1692
+ if (!staleClearAttempted) {
1693
+ staleClearAttempted = true;
1694
+ try {
1695
+ const age = Date.now() - statSync(lockPath).mtimeMs;
1696
+ if (age > STALE_LOCK_AGE_MS) {
1697
+ unlinkSync(lockPath);
1698
+ continue;
1699
+ }
1700
+ } catch {
1701
+ continue;
1702
+ }
1703
+ }
1704
+ if (Date.now() >= deadline) {
1705
+ throw new Error(`[skillsmith] Timed out waiting for config lock at ${lockPath} after ${timeoutMs}ms. If this persists, a crashed process may have left a stale lock \u2014 verify no other skillsmith process is running, then remove ${lockPath} manually.`);
1706
+ }
1707
+ sleepSync(LOCK_RETRY_DELAY_MS);
1708
+ }
1709
+ }
1710
+ }
1711
+ function atomicWriteFile(filePath, content, mode) {
1712
+ const dir = dirname2(filePath);
1713
+ const tmpPath = join3(dir, `.${randomBytes(6).toString("hex")}.tmp`);
1714
+ writeFileSync(tmpPath, content, { encoding: "utf-8", mode });
1715
+ renameSync(tmpPath, filePath);
1716
+ try {
1717
+ chmodSync(filePath, mode);
1718
+ } catch {
1719
+ }
1720
+ }
1721
+
1722
+ // ../core/dist/src/config/index.js
1657
1723
  var CONFIG_DIR = ".skillsmith";
1658
1724
  var CONFIG_FILE = "config.json";
1659
1725
  var CACHE_SUBDIR = "cache";
@@ -1672,10 +1738,10 @@ async function getKeytar() {
1672
1738
  var KEYTAR_SERVICE = "skillsmith-cli";
1673
1739
  var KEYTAR_ACCOUNT = "api-key";
1674
1740
  function getConfigDir() {
1675
- return join3(homedir3(), CONFIG_DIR);
1741
+ return join4(homedir3(), CONFIG_DIR);
1676
1742
  }
1677
1743
  function getConfigPath() {
1678
- return join3(getConfigDir(), CONFIG_FILE);
1744
+ return join4(getConfigDir(), CONFIG_FILE);
1679
1745
  }
1680
1746
  function ensureConfigDir() {
1681
1747
  const configDir = getConfigDir();
@@ -1685,7 +1751,7 @@ function ensureConfigDir() {
1685
1751
  }
1686
1752
  function getCacheDir() {
1687
1753
  const override = process.env.SKILLSMITH_CACHE_DIR_OVERRIDE;
1688
- const cacheDir = override && override.length > 0 ? override : join3(homedir3(), CONFIG_DIR, CACHE_SUBDIR);
1754
+ const cacheDir = override && override.length > 0 ? override : join4(homedir3(), CONFIG_DIR, CACHE_SUBDIR);
1689
1755
  if (!existsSync2(cacheDir)) {
1690
1756
  mkdirSync(cacheDir, { recursive: true, mode: 448 });
1691
1757
  }
@@ -1706,21 +1772,26 @@ function loadConfig() {
1706
1772
  function saveConfig(config2, options = { merge: true }) {
1707
1773
  ensureConfigDir();
1708
1774
  const configPath2 = getConfigPath();
1709
- let existingConfig = {};
1710
- if (options.merge && existsSync2(configPath2)) {
1711
- existingConfig = loadConfig();
1712
- }
1713
- const updates = Object.fromEntries(Object.entries(config2).filter(([, v]) => v !== void 0));
1714
- const deletions = Object.keys(config2).filter((k) => config2[k] === void 0);
1715
- const cleaned = { ...existingConfig };
1716
- for (const key of deletions) {
1717
- delete cleaned[key];
1718
- }
1719
- const mergedConfig = { ...cleaned, ...updates };
1720
- const configJson = JSON.stringify(mergedConfig, null, 2);
1721
- writeFileSync(configPath2, configJson, { encoding: "utf-8", mode: 384 });
1775
+ const release = acquireConfigLock(configPath2);
1722
1776
  try {
1723
- chmodSync(configPath2, 384);
1777
+ let existingConfig = {};
1778
+ if (options.merge && existsSync2(configPath2)) {
1779
+ existingConfig = loadConfig();
1780
+ }
1781
+ const updates = Object.fromEntries(Object.entries(config2).filter(([, v]) => v !== void 0));
1782
+ const deletions = Object.keys(config2).filter((k) => config2[k] === void 0);
1783
+ const cleaned = { ...existingConfig };
1784
+ for (const key of deletions) {
1785
+ delete cleaned[key];
1786
+ }
1787
+ const mergedConfig = { ...cleaned, ...updates };
1788
+ const configJson = JSON.stringify(mergedConfig, null, 2);
1789
+ atomicWriteFile(configPath2, configJson, 384);
1790
+ } finally {
1791
+ release();
1792
+ }
1793
+ try {
1794
+ chmodSync2(configPath2, 384);
1724
1795
  } catch {
1725
1796
  }
1726
1797
  }
@@ -2331,19 +2402,19 @@ var AGENT_TOOL_PROFILE_NAMES = [
2331
2402
 
2332
2403
  // ../core/dist/src/install/agent-home-relocate.js
2333
2404
  import { homedir as homedir4 } from "node:os";
2334
- import { isAbsolute, join as join4, relative as relative2 } from "node:path";
2405
+ import { isAbsolute, join as join5, relative as relative2 } from "node:path";
2335
2406
  function relocateUnderHome(absolutePath, homeDir) {
2336
2407
  if (!homeDir)
2337
2408
  return absolutePath;
2338
2409
  const rel = relative2(homedir4(), absolutePath);
2339
2410
  if (rel.startsWith("..") || isAbsolute(rel))
2340
2411
  return absolutePath;
2341
- return join4(homeDir, rel);
2412
+ return join5(homeDir, rel);
2342
2413
  }
2343
2414
 
2344
2415
  // ../core/dist/src/install/agent-pack-installer.fs-helpers.js
2345
- import { chmodSync as chmodSync2, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync2 } from "node:fs";
2346
- import { dirname as dirname2, join as join5 } from "node:path";
2416
+ import { chmodSync as chmodSync3, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
2417
+ import { dirname as dirname3, join as join6 } from "node:path";
2347
2418
  function writeOwnedArtifactFile(opts) {
2348
2419
  const { path: path24, content, executable, backupDir } = opts;
2349
2420
  if (existsSync3(path24)) {
@@ -2353,21 +2424,21 @@ function writeOwnedArtifactFile(opts) {
2353
2424
  return { changed: false, backupPath: null };
2354
2425
  }
2355
2426
  const backupPath = writeBackup(path24, backupDir);
2356
- mkdirSync2(dirname2(path24), { recursive: true });
2427
+ mkdirSync2(dirname3(path24), { recursive: true });
2357
2428
  writeFileSync2(path24, content, "utf-8");
2358
2429
  if (executable)
2359
- chmodSync2(path24, 493);
2430
+ chmodSync3(path24, 493);
2360
2431
  return { changed: true, backupPath };
2361
2432
  }
2362
- mkdirSync2(dirname2(path24), { recursive: true });
2433
+ mkdirSync2(dirname3(path24), { recursive: true });
2363
2434
  writeFileSync2(path24, content, "utf-8");
2364
2435
  if (executable)
2365
- chmodSync2(path24, 493);
2436
+ chmodSync3(path24, 493);
2366
2437
  return { changed: true, backupPath: null };
2367
2438
  }
2368
2439
  function isExecutable(path24) {
2369
2440
  try {
2370
- return (statSync(path24).mode & 73) !== 0;
2441
+ return (statSync2(path24).mode & 73) !== 0;
2371
2442
  } catch {
2372
2443
  return false;
2373
2444
  }
@@ -2376,25 +2447,25 @@ function writeBackup(sourcePath, backupDir) {
2376
2447
  mkdirSync2(backupDir, { recursive: true, mode: 448 });
2377
2448
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2378
2449
  const baseName = sourcePath.split("/").pop() ?? "file";
2379
- const backupPath = join5(backupDir, `${stamp}-${baseName}.bak`);
2450
+ const backupPath = join6(backupDir, `${stamp}-${baseName}.bak`);
2380
2451
  writeFileSync2(backupPath, readFileSync2(sourcePath, "utf-8"), { mode: 384 });
2381
2452
  return backupPath;
2382
2453
  }
2383
2454
 
2384
2455
  // ../core/dist/src/install/agent-manifest.js
2385
2456
  import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
2386
- import { join as join6 } from "node:path";
2457
+ import { join as join7 } from "node:path";
2387
2458
  var AGENT_INSTALL_DIR_ENV_VAR = "SKILLSMITH_AGENT_INSTALL_DIR";
2388
2459
  var AGENT_MANIFEST_SCHEMA_VERSION = 1;
2389
2460
  function getAgentInstallDir() {
2390
2461
  const override = process.env[AGENT_INSTALL_DIR_ENV_VAR];
2391
- return override && override.length > 0 ? override : join6(getConfigDir(), "agent-install");
2462
+ return override && override.length > 0 ? override : join7(getConfigDir(), "agent-install");
2392
2463
  }
2393
2464
  function getAgentManifestPath() {
2394
- return join6(getAgentInstallDir(), "manifest.json");
2465
+ return join7(getAgentInstallDir(), "manifest.json");
2395
2466
  }
2396
2467
  function getAgentInstallBackupsDir() {
2397
- return join6(getAgentInstallDir(), "backups");
2468
+ return join7(getAgentInstallDir(), "backups");
2398
2469
  }
2399
2470
  function loadAgentManifest() {
2400
2471
  const path24 = getAgentManifestPath();
@@ -2440,52 +2511,52 @@ function dedupeEntriesByPath(entries) {
2440
2511
  }
2441
2512
 
2442
2513
  // ../core/dist/src/install/agent-pack-installer.harness.js
2443
- import { join as join12 } from "node:path";
2514
+ import { join as join13 } from "node:path";
2444
2515
 
2445
2516
  // ../core/dist/src/install/agent-harness-targets.js
2446
2517
  import { homedir as homedir5 } from "node:os";
2447
- import { join as join7 } from "node:path";
2518
+ import { join as join8 } from "node:path";
2448
2519
  var home = homedir5();
2449
2520
  var AGENT_MCP_TARGETS = {
2450
2521
  "claude-code": {
2451
2522
  harness: "claude-code",
2452
- path: join7(home, ".claude", "settings.json"),
2523
+ path: join8(home, ".claude", "settings.json"),
2453
2524
  format: "json",
2454
2525
  keyPath: ["mcpServers"]
2455
2526
  },
2456
2527
  cursor: {
2457
2528
  harness: "cursor",
2458
- path: join7(home, ".cursor", "mcp.json"),
2529
+ path: join8(home, ".cursor", "mcp.json"),
2459
2530
  format: "json",
2460
2531
  keyPath: ["mcpServers"]
2461
2532
  },
2462
2533
  copilot: {
2463
2534
  harness: "copilot",
2464
- path: join7(home, ".copilot", "mcp-config.json"),
2535
+ path: join8(home, ".copilot", "mcp-config.json"),
2465
2536
  format: "json",
2466
2537
  keyPath: ["mcpServers"]
2467
2538
  },
2468
2539
  windsurf: {
2469
2540
  harness: "windsurf",
2470
- path: join7(home, ".codeium", "windsurf", "mcp_config.json"),
2541
+ path: join8(home, ".codeium", "windsurf", "mcp_config.json"),
2471
2542
  format: "json",
2472
2543
  keyPath: ["mcpServers"]
2473
2544
  },
2474
2545
  opencode: {
2475
2546
  harness: "opencode",
2476
- path: join7(home, ".config", "opencode", "opencode.json"),
2547
+ path: join8(home, ".config", "opencode", "opencode.json"),
2477
2548
  format: "json",
2478
2549
  keyPath: ["mcp"]
2479
2550
  },
2480
2551
  codex: {
2481
2552
  harness: "codex",
2482
- path: join7(home, ".codex", "config.toml"),
2553
+ path: join8(home, ".codex", "config.toml"),
2483
2554
  format: "toml-block",
2484
2555
  keyPath: []
2485
2556
  },
2486
2557
  hermes: {
2487
2558
  harness: "hermes",
2488
- path: join7(home, ".hermes", "config.yaml"),
2559
+ path: join8(home, ".hermes", "config.yaml"),
2489
2560
  format: "yaml",
2490
2561
  keyPath: ["mcp_servers"]
2491
2562
  }
@@ -2493,19 +2564,19 @@ var AGENT_MCP_TARGETS = {
2493
2564
  var AGENT_SHIM_TARGETS = {
2494
2565
  "claude-code": {
2495
2566
  harness: "claude-code",
2496
- path: join7(home, ".claude", "agents", "skillsmith-agent.md")
2567
+ path: join8(home, ".claude", "agents", "skillsmith-agent.md")
2497
2568
  },
2498
2569
  // Cursor 2.4+ reads `.claude/agents/` natively — no separate shim file.
2499
2570
  cursor: null,
2500
2571
  copilot: {
2501
2572
  harness: "copilot",
2502
- path: join7(home, ".copilot", "agents", "skillsmith-agent.agent.md")
2573
+ path: join8(home, ".copilot", "agents", "skillsmith-agent.agent.md")
2503
2574
  },
2504
2575
  opencode: {
2505
2576
  harness: "opencode",
2506
2577
  // Step-6 verified (opencode.ai/docs/agents/): global agent markdown
2507
2578
  // lives at ~/.config/opencode/agents/ — plural.
2508
- path: join7(home, ".config", "opencode", "agents", "skillsmith-agent.md")
2579
+ path: join8(home, ".config", "opencode", "agents", "skillsmith-agent.md")
2509
2580
  },
2510
2581
  // Codex's shim is a TOML `[agents.*]` table entry merged into
2511
2582
  // ~/.codex/config.toml, not a standalone file — see AGENT_MCP_TARGETS.codex
@@ -2515,16 +2586,16 @@ var AGENT_SHIM_TARGETS = {
2515
2586
  var AGENT_HOOK_TARGETS = {
2516
2587
  "claude-code": {
2517
2588
  harness: "claude-code",
2518
- scriptDir: join7(home, ".claude", "hooks"),
2519
- configPath: join7(home, ".claude", "settings.json"),
2589
+ scriptDir: join8(home, ".claude", "hooks"),
2590
+ configPath: join8(home, ".claude", "settings.json"),
2520
2591
  configFormat: "json",
2521
2592
  sessionStartKeyPath: ["hooks", "SessionStart"],
2522
2593
  sessionEndKeyPath: ["hooks", "SessionEnd"]
2523
2594
  },
2524
2595
  cursor: {
2525
2596
  harness: "cursor",
2526
- scriptDir: join7(home, ".cursor", "hooks"),
2527
- configPath: join7(home, ".cursor", "hooks.json"),
2597
+ scriptDir: join8(home, ".cursor", "hooks"),
2598
+ configPath: join8(home, ".cursor", "hooks.json"),
2528
2599
  configFormat: "json",
2529
2600
  // Cursor's hooks.json is Claude-compatible (PRD §3.1) but is itself the
2530
2601
  // hooks map (no wrapping "hooks" key) — see module header confidence note.
@@ -2533,8 +2604,8 @@ var AGENT_HOOK_TARGETS = {
2533
2604
  },
2534
2605
  codex: {
2535
2606
  harness: "codex",
2536
- scriptDir: join7(home, ".codex", "hooks"),
2537
- configPath: join7(home, ".codex", "config.toml"),
2607
+ scriptDir: join8(home, ".codex", "hooks"),
2608
+ configPath: join8(home, ".codex", "config.toml"),
2538
2609
  configFormat: "toml-block",
2539
2610
  // Unused for toml-block wiring (the block text carries its own
2540
2611
  // `[[hooks.SessionStart]]` headers); SessionEnd does not exist as a
@@ -2547,7 +2618,7 @@ var CODEX_CONFIG_TOML_PATH = AGENT_MCP_TARGETS.codex.path;
2547
2618
 
2548
2619
  // ../core/dist/src/install/agent-config-merge.json.js
2549
2620
  import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
2550
- import { dirname as dirname3, join as join8 } from "node:path";
2621
+ import { dirname as dirname4, join as join9 } from "node:path";
2551
2622
 
2552
2623
  // ../core/dist/src/install/agent-config-merge.types.js
2553
2624
  function shouldBackup(path24, alreadyBackedUpPaths) {
@@ -2624,7 +2695,7 @@ function writeBackup2(sourcePath, backupDir) {
2624
2695
  mkdirSync4(backupDir, { recursive: true, mode: 448 });
2625
2696
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2626
2697
  const baseName = sourcePath.split("/").pop() ?? "config";
2627
- const backupPath = join8(backupDir, `${stamp}-${baseName}.bak`);
2698
+ const backupPath = join9(backupDir, `${stamp}-${baseName}.bak`);
2628
2699
  writeFileSync4(backupPath, readFileSync4(sourcePath, "utf-8"), { mode: 384 });
2629
2700
  return backupPath;
2630
2701
  }
@@ -2662,7 +2733,7 @@ function mergeJsonMcpEntry(opts) {
2662
2733
  const backupPath2 = existed && shouldBackup(path24, alreadyBackedUpPaths) ? writeBackup2(path24, backupDir) : null;
2663
2734
  markBackedUp(path24, alreadyBackedUpPaths);
2664
2735
  setAtPath(doc, keyPath, { ...container, skillsmith: entryValue });
2665
- mkdirSync4(dirname3(path24), { recursive: true, mode: 448 });
2736
+ mkdirSync4(dirname4(path24), { recursive: true, mode: 448 });
2666
2737
  writeFileSync4(path24, JSON.stringify(doc, null, 2) + "\n", { mode: 384 });
2667
2738
  return { status: "updated", path: path24, backupPath: backupPath2 };
2668
2739
  }
@@ -2670,20 +2741,20 @@ function mergeJsonMcpEntry(opts) {
2670
2741
  markBackedUp(path24, alreadyBackedUpPaths);
2671
2742
  const currentContainer = container && typeof container === "object" && !Array.isArray(container) ? container : {};
2672
2743
  setAtPath(doc, keyPath, { ...currentContainer, skillsmith: entryValue });
2673
- mkdirSync4(dirname3(path24), { recursive: true, mode: 448 });
2744
+ mkdirSync4(dirname4(path24), { recursive: true, mode: 448 });
2674
2745
  writeFileSync4(path24, JSON.stringify(doc, null, 2) + "\n", { mode: 384 });
2675
2746
  return { status: "created", path: path24, backupPath };
2676
2747
  }
2677
2748
 
2678
2749
  // ../core/dist/src/install/agent-config-merge.yaml.js
2679
2750
  import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
2680
- import { dirname as dirname4, join as join9 } from "node:path";
2751
+ import { dirname as dirname5, join as join10 } from "node:path";
2681
2752
  import { Document, isMap, isScalar, parseDocument } from "yaml";
2682
2753
  function writeBackup3(sourcePath, backupDir) {
2683
2754
  mkdirSync5(backupDir, { recursive: true, mode: 448 });
2684
2755
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2685
2756
  const baseName = sourcePath.split("/").pop() ?? "config";
2686
- const backupPath = join9(backupDir, `${stamp}-${baseName}.bak`);
2757
+ const backupPath = join10(backupDir, `${stamp}-${baseName}.bak`);
2687
2758
  writeFileSync5(backupPath, readFileSync5(sourcePath, "utf-8"), { mode: 384 });
2688
2759
  return backupPath;
2689
2760
  }
@@ -2738,21 +2809,21 @@ function mergeYamlMcpEntry(opts) {
2738
2809
  const backupPath2 = existed && shouldBackup(path24, alreadyBackedUpPaths) ? writeBackup3(path24, backupDir) : null;
2739
2810
  markBackedUp(path24, alreadyBackedUpPaths);
2740
2811
  doc.setIn([mcpServersKey, "skillsmith"], entryValue);
2741
- mkdirSync5(dirname4(path24), { recursive: true, mode: 448 });
2812
+ mkdirSync5(dirname5(path24), { recursive: true, mode: 448 });
2742
2813
  writeFileSync5(path24, doc.toString(), { mode: 384 });
2743
2814
  return { status: "updated", path: path24, backupPath: backupPath2 };
2744
2815
  }
2745
2816
  const backupPath = existed && shouldBackup(path24, alreadyBackedUpPaths) ? writeBackup3(path24, backupDir) : null;
2746
2817
  markBackedUp(path24, alreadyBackedUpPaths);
2747
2818
  doc.setIn([mcpServersKey, "skillsmith"], entryValue);
2748
- mkdirSync5(dirname4(path24), { recursive: true, mode: 448 });
2819
+ mkdirSync5(dirname5(path24), { recursive: true, mode: 448 });
2749
2820
  writeFileSync5(path24, doc.toString(), { mode: 384 });
2750
2821
  return { status: "created", path: path24, backupPath };
2751
2822
  }
2752
2823
 
2753
2824
  // ../core/dist/src/install/agent-config-merge.toml-block.js
2754
2825
  import { existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "node:fs";
2755
- import { dirname as dirname5, join as join10 } from "node:path";
2826
+ import { dirname as dirname6, join as join11 } from "node:path";
2756
2827
  function markerStart(markerId) {
2757
2828
  return `# >>> skillsmith:${markerId} >>>`;
2758
2829
  }
@@ -2763,7 +2834,7 @@ function writeBackup4(sourcePath, backupDir) {
2763
2834
  mkdirSync6(backupDir, { recursive: true, mode: 448 });
2764
2835
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2765
2836
  const baseName = sourcePath.split("/").pop() ?? "config";
2766
- const backupPath = join10(backupDir, `${stamp}-${baseName}.bak`);
2837
+ const backupPath = join11(backupDir, `${stamp}-${baseName}.bak`);
2767
2838
  writeFileSync6(backupPath, readFileSync6(sourcePath, "utf-8"), { mode: 384 });
2768
2839
  return backupPath;
2769
2840
  }
@@ -2808,7 +2879,7 @@ ${end}`;
2808
2879
  ${trimmedBlock}
2809
2880
  ${end}
2810
2881
  `;
2811
- mkdirSync6(dirname5(path24), { recursive: true, mode: 448 });
2882
+ mkdirSync6(dirname6(path24), { recursive: true, mode: 448 });
2812
2883
  writeFileSync6(path24, appended, { mode: 384 });
2813
2884
  return { status: "created", path: path24, backupPath };
2814
2885
  }
@@ -2818,7 +2889,7 @@ function escapeRegExp(s) {
2818
2889
 
2819
2890
  // ../core/dist/src/install/agent-config-merge.json-array.js
2820
2891
  import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "node:fs";
2821
- import { dirname as dirname6, join as join11 } from "node:path";
2892
+ import { dirname as dirname7, join as join12 } from "node:path";
2822
2893
  function getAtPath2(root, keyPath) {
2823
2894
  let cur = root;
2824
2895
  for (const key of keyPath) {
@@ -2845,7 +2916,7 @@ function writeBackup5(sourcePath, backupDir) {
2845
2916
  mkdirSync7(backupDir, { recursive: true, mode: 448 });
2846
2917
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2847
2918
  const baseName = sourcePath.split("/").pop() ?? "config";
2848
- const backupPath = join11(backupDir, `${stamp}-${baseName}.bak`);
2919
+ const backupPath = join12(backupDir, `${stamp}-${baseName}.bak`);
2849
2920
  writeFileSync7(backupPath, readFileSync7(sourcePath, "utf-8"), { mode: 384 });
2850
2921
  return backupPath;
2851
2922
  }
@@ -2881,7 +2952,7 @@ function mergeJsonArrayEntry(opts) {
2881
2952
  markBackedUp(path24, alreadyBackedUpPaths);
2882
2953
  array2[existingIndex] = entry;
2883
2954
  setAtPath2(doc, keyPath, array2);
2884
- mkdirSync7(dirname6(path24), { recursive: true, mode: 448 });
2955
+ mkdirSync7(dirname7(path24), { recursive: true, mode: 448 });
2885
2956
  writeFileSync7(path24, JSON.stringify(doc, null, 2) + "\n", { mode: 384 });
2886
2957
  return { status: "updated", path: path24, backupPath: backupPath2 };
2887
2958
  }
@@ -2889,7 +2960,7 @@ function mergeJsonArrayEntry(opts) {
2889
2960
  markBackedUp(path24, alreadyBackedUpPaths);
2890
2961
  array2.push(entry);
2891
2962
  setAtPath2(doc, keyPath, array2);
2892
- mkdirSync7(dirname6(path24), { recursive: true, mode: 448 });
2963
+ mkdirSync7(dirname7(path24), { recursive: true, mode: 448 });
2893
2964
  writeFileSync7(path24, JSON.stringify(doc, null, 2) + "\n", { mode: 384 });
2894
2965
  return { status: "created", path: path24, backupPath };
2895
2966
  }
@@ -2968,8 +3039,8 @@ function installJsonHooks(harness, startArtifact, endArtifact, ctx, report) {
2968
3039
  if (!target || !startArtifact || !endArtifact)
2969
3040
  return;
2970
3041
  const scriptDir = relocateUnderHome(target.scriptDir, ctx.homeDir);
2971
- const startPath = join12(scriptDir, "session-start.sh");
2972
- const endPath = join12(scriptDir, "session-end.sh");
3042
+ const startPath = join13(scriptDir, "session-start.sh");
3043
+ const endPath = join13(scriptDir, "session-end.sh");
2973
3044
  const startResult = writeOwnedArtifactFile({
2974
3045
  path: startPath,
2975
3046
  content: startArtifact.content,
@@ -3029,8 +3100,8 @@ function installCodexHooks(startArtifact, endArtifact, ctx, report) {
3029
3100
  if (!target || !startArtifact || !endArtifact)
3030
3101
  return;
3031
3102
  const scriptDir = relocateUnderHome(target.scriptDir, ctx.homeDir);
3032
- const startPath = join12(scriptDir, "session-start.sh");
3033
- const endPath = join12(scriptDir, "session-end.sh");
3103
+ const startPath = join13(scriptDir, "session-start.sh");
3104
+ const endPath = join13(scriptDir, "session-end.sh");
3034
3105
  const startResult = writeOwnedArtifactFile({
3035
3106
  path: startPath,
3036
3107
  content: startArtifact.content,
@@ -3193,10 +3264,10 @@ function isPresent(nativePath, homeDir) {
3193
3264
  return existsSync9(relocateUnderHome(nativePath, homeDir));
3194
3265
  }
3195
3266
  function isCodexPresent(homeDir) {
3196
- return existsSync9(relocateUnderHome(join13(homedir6(), ".codex"), homeDir));
3267
+ return existsSync9(relocateUnderHome(join14(homedir6(), ".codex"), homeDir));
3197
3268
  }
3198
3269
  function writeSkillPackFor(clientNativePath, content, ctx, harness) {
3199
- const path24 = join13(relocateUnderHome(clientNativePath, ctx.homeDir), AGENT_PACK_SKILL_NAME, "SKILL.md");
3270
+ const path24 = join14(relocateUnderHome(clientNativePath, ctx.homeDir), AGENT_PACK_SKILL_NAME, "SKILL.md");
3200
3271
  const result = writeOwnedArtifactFile({
3201
3272
  path: path24,
3202
3273
  content,
@@ -3320,12 +3391,12 @@ function installAgentPack(opts = {}) {
3320
3391
  }
3321
3392
 
3322
3393
  // ../core/dist/src/install/agent-pack-uninstaller.js
3323
- import { dirname as dirname7 } from "node:path";
3324
- import { existsSync as existsSync10, readFileSync as readFileSync8, rmdirSync, unlinkSync, writeFileSync as writeFileSync8 } from "node:fs";
3394
+ import { dirname as dirname8 } from "node:path";
3395
+ import { existsSync as existsSync10, readFileSync as readFileSync8, rmdirSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync8 } from "node:fs";
3325
3396
 
3326
3397
  // ../core/dist/src/install/agent-manifest-path-guard.js
3327
3398
  import { homedir as homedir7 } from "node:os";
3328
- import { join as join14, relative as relative3, resolve as resolve2, sep } from "node:path";
3399
+ import { join as join15, relative as relative3, resolve as resolve2, sep } from "node:path";
3329
3400
  function computeAllowedPathSuffixes() {
3330
3401
  const suffixes = /* @__PURE__ */ new Set();
3331
3402
  const home2 = homedir7();
@@ -3336,15 +3407,15 @@ function computeAllowedPathSuffixes() {
3336
3407
  suffixes.add(rel);
3337
3408
  };
3338
3409
  for (const nativePath of Object.values(CLIENT_NATIVE_PATHS)) {
3339
- addSuffix(join14(nativePath, AGENT_PACK_SKILL_NAME, "SKILL.md"));
3410
+ addSuffix(join15(nativePath, AGENT_PACK_SKILL_NAME, "SKILL.md"));
3340
3411
  }
3341
3412
  for (const target of Object.values(AGENT_SHIM_TARGETS)) {
3342
3413
  if (target)
3343
3414
  addSuffix(target.path);
3344
3415
  }
3345
3416
  for (const target of Object.values(AGENT_HOOK_TARGETS)) {
3346
- addSuffix(join14(target.scriptDir, "session-start.sh"));
3347
- addSuffix(join14(target.scriptDir, "session-end.sh"));
3417
+ addSuffix(join15(target.scriptDir, "session-start.sh"));
3418
+ addSuffix(join15(target.scriptDir, "session-end.sh"));
3348
3419
  addSuffix(target.configPath);
3349
3420
  }
3350
3421
  for (const target of Object.values(AGENT_MCP_TARGETS)) {
@@ -3393,13 +3464,13 @@ function uninstallAgentPack(_opts = {}) {
3393
3464
  alreadyGone.push(entry.path);
3394
3465
  continue;
3395
3466
  }
3396
- touchedDirs.add(dirname7(entry.path));
3467
+ touchedDirs.add(dirname8(entry.path));
3397
3468
  if (entry.backupPath && existsSync10(entry.backupPath)) {
3398
3469
  const content = readFileSync8(entry.backupPath, "utf-8");
3399
3470
  writeFileSync8(entry.path, content, "utf-8");
3400
3471
  restored.push(entry.path);
3401
3472
  } else {
3402
- unlinkSync(entry.path);
3473
+ unlinkSync2(entry.path);
3403
3474
  removed.push(entry.path);
3404
3475
  }
3405
3476
  }
@@ -3422,7 +3493,7 @@ function cleanupEmptyDirs(dirs) {
3422
3493
  } catch {
3423
3494
  break;
3424
3495
  }
3425
- const parent = dirname7(current);
3496
+ const parent = dirname8(current);
3426
3497
  if (parent === current)
3427
3498
  break;
3428
3499
  current = parent;
@@ -3431,9 +3502,9 @@ function cleanupEmptyDirs(dirs) {
3431
3502
  }
3432
3503
 
3433
3504
  // src/config.ts
3434
- var DEFAULT_DB_PATH = join15(homedir8(), ".skillsmith", "skills.db");
3505
+ var DEFAULT_DB_PATH = join16(homedir8(), ".skillsmith", "skills.db");
3435
3506
  var DEFAULT_SKILLS_DIR = getCanonicalInstallPath();
3436
- var DEFAULT_MANIFEST_PATH = join15(homedir8(), ".skillsmith", "manifest.json");
3507
+ var DEFAULT_MANIFEST_PATH = join16(homedir8(), ".skillsmith", "manifest.json");
3437
3508
  function getDefaultDbPath() {
3438
3509
  return DEFAULT_DB_PATH;
3439
3510
  }
@@ -20014,7 +20085,17 @@ var CATEGORY_WEIGHTS = {
20014
20085
  // CRITICAL finding in either category reaches exactly the 40 quarantine threshold
20015
20086
  // on its own (50 * 2.0 * 1.0 = 100 -> capped 100 -> * 0.40 = 40).
20016
20087
  code_execution: 2,
20017
- obfuscated_directive: 2
20088
+ obfuscated_directive: 2,
20089
+ // SMI-595: a naming-similarity heuristic is advisory, not damning on its own —
20090
+ // deliberately the same tier as sensitive_path/url, NOT the 1.7-2.0 tier used
20091
+ // for jailbreak/exfiltration-class findings. Paired with the 0.04 coefficient
20092
+ // in calculateRiskScore (the same coefficient already used for
20093
+ // sensitivePaths/externalUrls/ssrf): a single medium-severity, high-confidence
20094
+ // finding scores 15 * 1.2 * 1.0 = 18 -> contributes 18 * 0.04 = 0.72 (rounds to
20095
+ // ~1) to the total; a saturated breakdown (capped at 100) contributes 4 —
20096
+ // comfortably under the riskThreshold: 40 quarantine cutoff on its own. See the
20097
+ // stacked-risk test in SecurityScanner.scoring.test.ts.
20098
+ typosquat: 1.2
20018
20099
  };
20019
20100
 
20020
20101
  // ../core/dist/src/security/scanner/regex-utils.js
@@ -20171,7 +20252,8 @@ function calculateRiskScore(findings) {
20171
20252
  ssrf: 0,
20172
20253
  pii: 0,
20173
20254
  codeExecution: 0,
20174
- obfuscatedDirective: 0
20255
+ obfuscatedDirective: 0,
20256
+ typosquat: 0
20175
20257
  };
20176
20258
  const confidenceWeights = {
20177
20259
  high: 1,
@@ -20223,6 +20305,9 @@ function calculateRiskScore(findings) {
20223
20305
  case "obfuscated_directive":
20224
20306
  breakdown.obfuscatedDirective += score;
20225
20307
  break;
20308
+ case "typosquat":
20309
+ breakdown.typosquat += score;
20310
+ break;
20226
20311
  }
20227
20312
  }
20228
20313
  breakdown.jailbreak = Math.min(100, breakdown.jailbreak);
@@ -20238,10 +20323,269 @@ function calculateRiskScore(findings) {
20238
20323
  breakdown.pii = Math.min(100, breakdown.pii);
20239
20324
  breakdown.codeExecution = Math.min(100, breakdown.codeExecution);
20240
20325
  breakdown.obfuscatedDirective = Math.min(100, breakdown.obfuscatedDirective);
20241
- const total = Math.min(100, Math.round(breakdown.jailbreak * 0.2 + breakdown.socialEngineering * 0.11 + breakdown.promptLeaking * 0.11 + breakdown.dataExfiltration * 0.08 + breakdown.privilegeEscalation * 0.11 + breakdown.suspiciousCode * 0.07 + breakdown.sensitivePaths * 0.04 + breakdown.externalUrls * 0.04 + breakdown.aiDefence * 0.12 + breakdown.ssrf * 0.04 + breakdown.pii * 0.08 + breakdown.codeExecution * 0.4 + breakdown.obfuscatedDirective * 0.4));
20326
+ breakdown.typosquat = Math.min(100, breakdown.typosquat);
20327
+ const total = Math.min(100, Math.round(breakdown.jailbreak * 0.2 + breakdown.socialEngineering * 0.11 + breakdown.promptLeaking * 0.11 + breakdown.dataExfiltration * 0.08 + breakdown.privilegeEscalation * 0.11 + breakdown.suspiciousCode * 0.07 + breakdown.sensitivePaths * 0.04 + breakdown.externalUrls * 0.04 + breakdown.aiDefence * 0.12 + breakdown.ssrf * 0.04 + breakdown.pii * 0.08 + breakdown.codeExecution * 0.4 + breakdown.obfuscatedDirective * 0.4 + breakdown.typosquat * 0.04));
20242
20328
  return { total, breakdown };
20243
20329
  }
20244
20330
 
20331
+ // ../core/dist/src/security/scanner/confusables.js
20332
+ var CONFUSABLES = {
20333
+ // Cyrillic -> Latin
20334
+ \u0430: "a",
20335
+ \u0435: "e",
20336
+ \u043E: "o",
20337
+ \u0440: "p",
20338
+ \u0441: "c",
20339
+ \u0443: "y",
20340
+ \u0445: "x",
20341
+ \u0456: "i",
20342
+ \u0458: "j",
20343
+ \u0455: "s",
20344
+ "\u0501": "d",
20345
+ \u04BB: "h",
20346
+ \u043A: "k",
20347
+ \u043C: "m",
20348
+ \u0442: "t",
20349
+ \u0432: "b",
20350
+ \u043D: "h",
20351
+ // Greek -> Latin
20352
+ \u03BF: "o",
20353
+ \u03B1: "a",
20354
+ \u03C1: "p",
20355
+ \u03B5: "e",
20356
+ \u03C4: "t",
20357
+ \u03B9: "i",
20358
+ \u03BA: "k",
20359
+ \u03C5: "u",
20360
+ \u03C7: "x",
20361
+ \u03BD: "v",
20362
+ \u03F2: "c",
20363
+ \u03B2: "b"
20364
+ };
20365
+ function isFullwidthLatin(cp) {
20366
+ return cp >= 65313 && cp <= 65338 || cp >= 65345 && cp <= 65370;
20367
+ }
20368
+ function isMathAlphanumeric(cp) {
20369
+ return cp >= 119808 && cp <= 120831;
20370
+ }
20371
+ function confusableSkeleton(s) {
20372
+ let out = "";
20373
+ for (const ch of s) {
20374
+ const cp = ch.codePointAt(0) ?? 0;
20375
+ if (isFullwidthLatin(cp)) {
20376
+ out += String.fromCodePoint(cp - 65248);
20377
+ } else if (isMathAlphanumeric(cp)) {
20378
+ const folded = ch.normalize("NFKC");
20379
+ out += CONFUSABLES[folded] ?? folded;
20380
+ } else if (CONFUSABLES[ch]) {
20381
+ out += CONFUSABLES[ch];
20382
+ } else {
20383
+ out += ch;
20384
+ }
20385
+ }
20386
+ return out;
20387
+ }
20388
+
20389
+ // ../core/dist/src/security/scanner/SecurityScanner.exec.js
20390
+ var INVISIBLE_RANGE = "\\u0300-\\u036F\\u00AD\\u061C\\u180E\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u206F\\uFEFF";
20391
+ var INVISIBLE_TEST = new RegExp("[" + INVISIBLE_RANGE + "]|[\\u{E0000}-\\u{E007F}]", "u");
20392
+ var INVISIBLE_STRIP = new RegExp("[" + INVISIBLE_RANGE + "]|[\\u{E0000}-\\u{E007F}]", "gu");
20393
+ function stripInvisible(s) {
20394
+ return s.replace(INVISIBLE_STRIP, "");
20395
+ }
20396
+ function hasConfusable(s) {
20397
+ for (const ch of s) {
20398
+ const cp = ch.codePointAt(0) ?? 0;
20399
+ if (isFullwidthLatin(cp) || isMathAlphanumeric(cp) || CONFUSABLES[ch])
20400
+ return true;
20401
+ }
20402
+ return false;
20403
+ }
20404
+ var OBFUSCATION_DIRECTIVE_PATTERN = /(?:ignore|disregard|forget)\s+(?:all\s+|the\s+)?(?:previous|prior|above|earlier)\s+(?:instruction|prompt|rule|direction)|bypass\s+(?:all\s+)?(?:restriction|filter|safety|guard|security)|(?:reveal|show|print|dump|leak)\s+(?:me\s+)?(?:your\s+|the\s+)?(?:system\s+)?(?:prompt|instruction)|(?:curl|wget)\b[^\n|]{0,120}?(?:https?:\/\/|\d{1,3}(?:\.\d{1,3}){3}|[\w-]{2,}\.[a-z]{2,})[^\n|]{0,120}?\|\s*(?:ba|z)?sh\b/i;
20405
+ function scanCodeExecution(content, lineContexts) {
20406
+ const lines = content.split("\n");
20407
+ const contexts = lineContexts ?? analyzeMarkdownContext(content);
20408
+ for (let i = 0; i < lines.length; i++) {
20409
+ const line = lines[i];
20410
+ for (const pattern of CODE_EXECUTION_PATTERNS) {
20411
+ const match = safeRegexTest(pattern, line);
20412
+ if (match) {
20413
+ const ctx = contexts[i];
20414
+ const inDocContext = ctx ? isDocumentationContext(ctx) : false;
20415
+ return [
20416
+ {
20417
+ type: "code_execution",
20418
+ severity: "medium",
20419
+ message: `Remote fetch piped to an interpreter: "${match[0].slice(0, 60)}${match[0].length > 60 ? "..." : ""}"`,
20420
+ location: line.trim().slice(0, 100),
20421
+ lineNumber: i + 1,
20422
+ category: "code_execution",
20423
+ inDocumentationContext: inDocContext,
20424
+ confidence: "high"
20425
+ }
20426
+ ];
20427
+ }
20428
+ }
20429
+ }
20430
+ return [];
20431
+ }
20432
+ function scanObfuscatedDirective(content) {
20433
+ const lines = content.split("\n");
20434
+ for (let i = 0; i < lines.length; i++) {
20435
+ const raw = lines[i];
20436
+ const hasInvisible = INVISIBLE_TEST.test(raw);
20437
+ const hasConf = hasConfusable(raw);
20438
+ if (!hasInvisible && !hasConf)
20439
+ continue;
20440
+ if (safeRegexCheck(OBFUSCATION_DIRECTIVE_PATTERN, raw))
20441
+ continue;
20442
+ const transforms = [];
20443
+ if (hasInvisible)
20444
+ transforms.push(stripInvisible(raw));
20445
+ if (hasConf)
20446
+ transforms.push(confusableSkeleton(raw));
20447
+ if (hasInvisible && hasConf)
20448
+ transforms.push(confusableSkeleton(stripInvisible(raw)));
20449
+ for (const transformed of transforms) {
20450
+ if (transformed === raw)
20451
+ continue;
20452
+ const match = safeRegexTest(OBFUSCATION_DIRECTIVE_PATTERN, transformed);
20453
+ if (match) {
20454
+ return [
20455
+ {
20456
+ type: "obfuscated_directive",
20457
+ severity: "critical",
20458
+ message: `Security directive concealed via Unicode obfuscation, revealed after de-obfuscation: "${match[0].slice(0, 60)}${match[0].length > 60 ? "..." : ""}"`,
20459
+ location: raw.trim().slice(0, 100),
20460
+ lineNumber: i + 1,
20461
+ category: "obfuscated_directive",
20462
+ inDocumentationContext: false,
20463
+ confidence: "high"
20464
+ }
20465
+ ];
20466
+ }
20467
+ }
20468
+ }
20469
+ return [];
20470
+ }
20471
+ var CODE_EXECUTION_CO_OCCURRENCE = /* @__PURE__ */ new Set([
20472
+ "data_exfiltration",
20473
+ "privilege_escalation",
20474
+ "sensitive_path",
20475
+ "obfuscated_directive"
20476
+ ]);
20477
+ function escalateCodeExecution(findings) {
20478
+ const codeExec = findings.find((f) => f.type === "code_execution");
20479
+ if (!codeExec)
20480
+ return;
20481
+ const hasDangerousCoSignal = findings.some((f) => f !== codeExec && CODE_EXECUTION_CO_OCCURRENCE.has(f.type) && f.inDocumentationContext !== true && (f.severity === "high" || f.severity === "critical"));
20482
+ if (hasDangerousCoSignal) {
20483
+ codeExec.severity = "critical";
20484
+ codeExec.message = `Remote fetch piped to an interpreter, co-occurring with exfiltration/privilege/credential signals \u2014 likely supply-chain execution. ${codeExec.message}`;
20485
+ }
20486
+ }
20487
+
20488
+ // ../core/dist/src/security/scanner/SecurityScanner.formatters.js
20489
+ function toMinimalRefs(report) {
20490
+ return report.findings.map((finding) => {
20491
+ const line = finding.lineNumber ?? 0;
20492
+ const severity = finding.severity.toUpperCase();
20493
+ const message = finding.message.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
20494
+ return `${report.skillId}:${line}:${severity}:${finding.type}:${message}`;
20495
+ });
20496
+ }
20497
+ function toSARIF(report) {
20498
+ const rules = [
20499
+ { id: "jailbreak", name: "Jailbreak Attempt", severity: "error" },
20500
+ { id: "social_engineering", name: "Social Engineering", severity: "warning" },
20501
+ { id: "prompt_leaking", name: "Prompt Leaking", severity: "error" },
20502
+ { id: "data_exfiltration", name: "Data Exfiltration", severity: "warning" },
20503
+ { id: "privilege_escalation", name: "Privilege Escalation", severity: "error" },
20504
+ { id: "suspicious_pattern", name: "Suspicious Pattern", severity: "warning" },
20505
+ { id: "sensitive_path", name: "Sensitive Path", severity: "warning" },
20506
+ { id: "url", name: "External URL", severity: "note" },
20507
+ { id: "ai_defence", name: "AI Injection", severity: "error" }
20508
+ ];
20509
+ const severityToLevel = {
20510
+ critical: "error",
20511
+ high: "error",
20512
+ medium: "warning",
20513
+ low: "note"
20514
+ };
20515
+ return {
20516
+ $schema: "https://json.schemastore.org/sarif-2.1.0.json",
20517
+ version: "2.1.0",
20518
+ runs: [
20519
+ {
20520
+ tool: {
20521
+ driver: {
20522
+ name: "Skillsmith Security Scanner",
20523
+ version: "1.0.0",
20524
+ informationUri: "https://github.com/smith-horn/skillsmith",
20525
+ rules: rules.map((rule) => ({
20526
+ id: rule.id,
20527
+ name: rule.name,
20528
+ shortDescription: { text: rule.name },
20529
+ defaultConfiguration: { level: rule.severity }
20530
+ }))
20531
+ }
20532
+ },
20533
+ results: report.findings.map((finding) => ({
20534
+ ruleId: finding.type,
20535
+ level: severityToLevel[finding.severity] ?? "warning",
20536
+ message: { text: finding.message },
20537
+ locations: [
20538
+ {
20539
+ physicalLocation: {
20540
+ artifactLocation: { uri: report.skillId },
20541
+ region: {
20542
+ startLine: finding.lineNumber ?? 1,
20543
+ snippet: finding.location ? { text: finding.location } : void 0
20544
+ }
20545
+ }
20546
+ }
20547
+ ],
20548
+ properties: {
20549
+ confidence: finding.confidence ?? "high",
20550
+ inDocumentationContext: finding.inDocumentationContext ?? false
20551
+ }
20552
+ })),
20553
+ invocations: [
20554
+ {
20555
+ executionSuccessful: true,
20556
+ endTimeUtc: report.scannedAt.toISOString()
20557
+ }
20558
+ ]
20559
+ }
20560
+ ]
20561
+ };
20562
+ }
20563
+ function toGitHubAnnotations(report) {
20564
+ return report.findings.map((finding) => {
20565
+ const severity = finding.severity === "critical" || finding.severity === "high" ? "error" : "warning";
20566
+ const line = finding.lineNumber ?? 1;
20567
+ const message = finding.message.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A");
20568
+ return `::${severity} file=${report.skillId},line=${line}::${message}`;
20569
+ });
20570
+ }
20571
+ function toSummary(report) {
20572
+ const bySeverity = { critical: 0, high: 0, medium: 0, low: 0 };
20573
+ const byType = {};
20574
+ for (const finding of report.findings) {
20575
+ bySeverity[finding.severity] = (bySeverity[finding.severity] || 0) + 1;
20576
+ byType[finding.type] = (byType[finding.type] || 0) + 1;
20577
+ }
20578
+ return {
20579
+ skillId: report.skillId,
20580
+ passed: report.passed,
20581
+ riskScore: report.riskScore,
20582
+ totalFindings: report.findings.length,
20583
+ bySeverity,
20584
+ byType,
20585
+ scanDurationMs: report.scanDurationMs
20586
+ };
20587
+ }
20588
+
20245
20589
  // ../core/dist/src/security/scanner/SecurityScanner.ssrf.js
20246
20590
  function scanSsrfPatterns(content, lineContexts) {
20247
20591
  const findings = [];
@@ -20621,262 +20965,6 @@ function scanPrivilegeEscalation(content, lineContexts) {
20621
20965
  return findings;
20622
20966
  }
20623
20967
 
20624
- // ../core/dist/src/security/scanner/SecurityScanner.exec.js
20625
- var INVISIBLE_RANGE = "\\u0300-\\u036F\\u00AD\\u061C\\u180E\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u206F\\uFEFF";
20626
- var INVISIBLE_TEST = new RegExp("[" + INVISIBLE_RANGE + "]|[\\u{E0000}-\\u{E007F}]", "u");
20627
- var INVISIBLE_STRIP = new RegExp("[" + INVISIBLE_RANGE + "]|[\\u{E0000}-\\u{E007F}]", "gu");
20628
- var CONFUSABLES = {
20629
- // Cyrillic -> Latin
20630
- \u0430: "a",
20631
- \u0435: "e",
20632
- \u043E: "o",
20633
- \u0440: "p",
20634
- \u0441: "c",
20635
- \u0443: "y",
20636
- \u0445: "x",
20637
- \u0456: "i",
20638
- \u0458: "j",
20639
- \u0455: "s",
20640
- "\u0501": "d",
20641
- \u04BB: "h",
20642
- \u043A: "k",
20643
- \u043C: "m",
20644
- \u0442: "t",
20645
- \u0432: "b",
20646
- \u043D: "h",
20647
- // Greek -> Latin
20648
- \u03BF: "o",
20649
- \u03B1: "a",
20650
- \u03C1: "p",
20651
- \u03B5: "e",
20652
- \u03C4: "t",
20653
- \u03B9: "i",
20654
- \u03BA: "k",
20655
- \u03C5: "u",
20656
- \u03C7: "x",
20657
- \u03BD: "v",
20658
- \u03F2: "c",
20659
- \u03B2: "b"
20660
- };
20661
- function isFullwidthLatin(cp) {
20662
- return cp >= 65313 && cp <= 65338 || cp >= 65345 && cp <= 65370;
20663
- }
20664
- function isMathAlphanumeric(cp) {
20665
- return cp >= 119808 && cp <= 120831;
20666
- }
20667
- function stripInvisible(s) {
20668
- return s.replace(INVISIBLE_STRIP, "");
20669
- }
20670
- function confusableSkeleton(s) {
20671
- let out = "";
20672
- for (const ch of s) {
20673
- const cp = ch.codePointAt(0) ?? 0;
20674
- if (isFullwidthLatin(cp)) {
20675
- out += String.fromCodePoint(cp - 65248);
20676
- } else if (isMathAlphanumeric(cp)) {
20677
- const folded = ch.normalize("NFKC");
20678
- out += CONFUSABLES[folded] ?? folded;
20679
- } else if (CONFUSABLES[ch]) {
20680
- out += CONFUSABLES[ch];
20681
- } else {
20682
- out += ch;
20683
- }
20684
- }
20685
- return out;
20686
- }
20687
- function hasConfusable(s) {
20688
- for (const ch of s) {
20689
- const cp = ch.codePointAt(0) ?? 0;
20690
- if (isFullwidthLatin(cp) || isMathAlphanumeric(cp) || CONFUSABLES[ch])
20691
- return true;
20692
- }
20693
- return false;
20694
- }
20695
- var OBFUSCATION_DIRECTIVE_PATTERN = /(?:ignore|disregard|forget)\s+(?:all\s+|the\s+)?(?:previous|prior|above|earlier)\s+(?:instruction|prompt|rule|direction)|bypass\s+(?:all\s+)?(?:restriction|filter|safety|guard|security)|(?:reveal|show|print|dump|leak)\s+(?:me\s+)?(?:your\s+|the\s+)?(?:system\s+)?(?:prompt|instruction)|(?:curl|wget)\b[^\n|]{0,120}?(?:https?:\/\/|\d{1,3}(?:\.\d{1,3}){3}|[\w-]{2,}\.[a-z]{2,})[^\n|]{0,120}?\|\s*(?:ba|z)?sh\b/i;
20696
- function scanCodeExecution(content, lineContexts) {
20697
- const lines = content.split("\n");
20698
- const contexts = lineContexts ?? analyzeMarkdownContext(content);
20699
- for (let i = 0; i < lines.length; i++) {
20700
- const line = lines[i];
20701
- for (const pattern of CODE_EXECUTION_PATTERNS) {
20702
- const match = safeRegexTest(pattern, line);
20703
- if (match) {
20704
- const ctx = contexts[i];
20705
- const inDocContext = ctx ? isDocumentationContext(ctx) : false;
20706
- return [
20707
- {
20708
- type: "code_execution",
20709
- severity: "medium",
20710
- message: `Remote fetch piped to an interpreter: "${match[0].slice(0, 60)}${match[0].length > 60 ? "..." : ""}"`,
20711
- location: line.trim().slice(0, 100),
20712
- lineNumber: i + 1,
20713
- category: "code_execution",
20714
- inDocumentationContext: inDocContext,
20715
- confidence: "high"
20716
- }
20717
- ];
20718
- }
20719
- }
20720
- }
20721
- return [];
20722
- }
20723
- function scanObfuscatedDirective(content) {
20724
- const lines = content.split("\n");
20725
- for (let i = 0; i < lines.length; i++) {
20726
- const raw = lines[i];
20727
- const hasInvisible = INVISIBLE_TEST.test(raw);
20728
- const hasConf = hasConfusable(raw);
20729
- if (!hasInvisible && !hasConf)
20730
- continue;
20731
- if (safeRegexCheck(OBFUSCATION_DIRECTIVE_PATTERN, raw))
20732
- continue;
20733
- const transforms = [];
20734
- if (hasInvisible)
20735
- transforms.push(stripInvisible(raw));
20736
- if (hasConf)
20737
- transforms.push(confusableSkeleton(raw));
20738
- if (hasInvisible && hasConf)
20739
- transforms.push(confusableSkeleton(stripInvisible(raw)));
20740
- for (const transformed of transforms) {
20741
- if (transformed === raw)
20742
- continue;
20743
- const match = safeRegexTest(OBFUSCATION_DIRECTIVE_PATTERN, transformed);
20744
- if (match) {
20745
- return [
20746
- {
20747
- type: "obfuscated_directive",
20748
- severity: "critical",
20749
- message: `Security directive concealed via Unicode obfuscation, revealed after de-obfuscation: "${match[0].slice(0, 60)}${match[0].length > 60 ? "..." : ""}"`,
20750
- location: raw.trim().slice(0, 100),
20751
- lineNumber: i + 1,
20752
- category: "obfuscated_directive",
20753
- inDocumentationContext: false,
20754
- confidence: "high"
20755
- }
20756
- ];
20757
- }
20758
- }
20759
- }
20760
- return [];
20761
- }
20762
- var CODE_EXECUTION_CO_OCCURRENCE = /* @__PURE__ */ new Set([
20763
- "data_exfiltration",
20764
- "privilege_escalation",
20765
- "sensitive_path",
20766
- "obfuscated_directive"
20767
- ]);
20768
- function escalateCodeExecution(findings) {
20769
- const codeExec = findings.find((f) => f.type === "code_execution");
20770
- if (!codeExec)
20771
- return;
20772
- const hasDangerousCoSignal = findings.some((f) => f !== codeExec && CODE_EXECUTION_CO_OCCURRENCE.has(f.type) && f.inDocumentationContext !== true && (f.severity === "high" || f.severity === "critical"));
20773
- if (hasDangerousCoSignal) {
20774
- codeExec.severity = "critical";
20775
- codeExec.message = `Remote fetch piped to an interpreter, co-occurring with exfiltration/privilege/credential signals \u2014 likely supply-chain execution. ${codeExec.message}`;
20776
- }
20777
- }
20778
-
20779
- // ../core/dist/src/security/scanner/SecurityScanner.formatters.js
20780
- function toMinimalRefs(report) {
20781
- return report.findings.map((finding) => {
20782
- const line = finding.lineNumber ?? 0;
20783
- const severity = finding.severity.toUpperCase();
20784
- const message = finding.message.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
20785
- return `${report.skillId}:${line}:${severity}:${finding.type}:${message}`;
20786
- });
20787
- }
20788
- function toSARIF(report) {
20789
- const rules = [
20790
- { id: "jailbreak", name: "Jailbreak Attempt", severity: "error" },
20791
- { id: "social_engineering", name: "Social Engineering", severity: "warning" },
20792
- { id: "prompt_leaking", name: "Prompt Leaking", severity: "error" },
20793
- { id: "data_exfiltration", name: "Data Exfiltration", severity: "warning" },
20794
- { id: "privilege_escalation", name: "Privilege Escalation", severity: "error" },
20795
- { id: "suspicious_pattern", name: "Suspicious Pattern", severity: "warning" },
20796
- { id: "sensitive_path", name: "Sensitive Path", severity: "warning" },
20797
- { id: "url", name: "External URL", severity: "note" },
20798
- { id: "ai_defence", name: "AI Injection", severity: "error" }
20799
- ];
20800
- const severityToLevel = {
20801
- critical: "error",
20802
- high: "error",
20803
- medium: "warning",
20804
- low: "note"
20805
- };
20806
- return {
20807
- $schema: "https://json.schemastore.org/sarif-2.1.0.json",
20808
- version: "2.1.0",
20809
- runs: [
20810
- {
20811
- tool: {
20812
- driver: {
20813
- name: "Skillsmith Security Scanner",
20814
- version: "1.0.0",
20815
- informationUri: "https://github.com/smith-horn/skillsmith",
20816
- rules: rules.map((rule) => ({
20817
- id: rule.id,
20818
- name: rule.name,
20819
- shortDescription: { text: rule.name },
20820
- defaultConfiguration: { level: rule.severity }
20821
- }))
20822
- }
20823
- },
20824
- results: report.findings.map((finding) => ({
20825
- ruleId: finding.type,
20826
- level: severityToLevel[finding.severity] ?? "warning",
20827
- message: { text: finding.message },
20828
- locations: [
20829
- {
20830
- physicalLocation: {
20831
- artifactLocation: { uri: report.skillId },
20832
- region: {
20833
- startLine: finding.lineNumber ?? 1,
20834
- snippet: finding.location ? { text: finding.location } : void 0
20835
- }
20836
- }
20837
- }
20838
- ],
20839
- properties: {
20840
- confidence: finding.confidence ?? "high",
20841
- inDocumentationContext: finding.inDocumentationContext ?? false
20842
- }
20843
- })),
20844
- invocations: [
20845
- {
20846
- executionSuccessful: true,
20847
- endTimeUtc: report.scannedAt.toISOString()
20848
- }
20849
- ]
20850
- }
20851
- ]
20852
- };
20853
- }
20854
- function toGitHubAnnotations(report) {
20855
- return report.findings.map((finding) => {
20856
- const severity = finding.severity === "critical" || finding.severity === "high" ? "error" : "warning";
20857
- const line = finding.lineNumber ?? 1;
20858
- const message = finding.message.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A");
20859
- return `::${severity} file=${report.skillId},line=${line}::${message}`;
20860
- });
20861
- }
20862
- function toSummary(report) {
20863
- const bySeverity = { critical: 0, high: 0, medium: 0, low: 0 };
20864
- const byType = {};
20865
- for (const finding of report.findings) {
20866
- bySeverity[finding.severity] = (bySeverity[finding.severity] || 0) + 1;
20867
- byType[finding.type] = (byType[finding.type] || 0) + 1;
20868
- }
20869
- return {
20870
- skillId: report.skillId,
20871
- passed: report.passed,
20872
- riskScore: report.riskScore,
20873
- totalFindings: report.findings.length,
20874
- bySeverity,
20875
- byType,
20876
- scanDurationMs: report.scanDurationMs
20877
- };
20878
- }
20879
-
20880
20968
  // ../core/dist/src/security/scanner/SecurityScanner.js
20881
20969
  var SecurityScanner = class {
20882
20970
  allowedDomains;
@@ -21165,7 +21253,7 @@ var logger3 = createLogger("Sanitization");
21165
21253
 
21166
21254
  // ../core/dist/src/security/pathValidation.js
21167
21255
  init_logger();
21168
- import { resolve as resolve4, normalize, dirname as dirname8, isAbsolute as isAbsolute2 } from "path";
21256
+ import { resolve as resolve4, normalize, dirname as dirname9, isAbsolute as isAbsolute2 } from "path";
21169
21257
  import { homedir as homedir9 } from "os";
21170
21258
  var logger4 = createLogger("PathValidation");
21171
21259
  var DEFAULT_ALLOWED_DIRS = [
@@ -22337,7 +22425,7 @@ var log4 = createLogger("RawUrlAdapter");
22337
22425
  // ../core/dist/src/sources/LocalFilesystemAdapter.js
22338
22426
  init_logger();
22339
22427
  import { createHash as createHash2 } from "crypto";
22340
- import { basename, dirname as dirname10, resolve as resolve5, join as join17 } from "path";
22428
+ import { basename, dirname as dirname11, resolve as resolve5, join as join18 } from "path";
22341
22429
 
22342
22430
  // ../core/dist/src/sources/LocalFilesystemAdapter.helpers.js
22343
22431
  import { promises as fs2 } from "fs";
@@ -22427,7 +22515,7 @@ async function resolveSafeRealpath(candidate, root, opts = {}) {
22427
22515
  }
22428
22516
 
22429
22517
  // ../core/dist/src/sources/LocalFilesystemAdapter.scan.js
22430
- import { join as join16, relative as relative4, dirname as dirname9 } from "path";
22518
+ import { join as join17, relative as relative4, dirname as dirname10 } from "path";
22431
22519
  var SKILL_FILE_NAMES = ["SKILL.md", "skill.md"];
22432
22520
  async function scanDirectoryRecursive(dirPath, depth, options) {
22433
22521
  if (depth > options.maxDepth)
@@ -22458,7 +22546,7 @@ async function scanDirectoryRecursive(dirPath, depth, options) {
22458
22546
  return;
22459
22547
  }
22460
22548
  for (const entry of dirResult.value) {
22461
- const fullPath = join16(dirPath, entry.name);
22549
+ const fullPath = join17(dirPath, entry.name);
22462
22550
  if (options.isExcluded(entry.name))
22463
22551
  continue;
22464
22552
  let isDirectory = entry.isDirectory();
@@ -22493,7 +22581,7 @@ async function scanDirectoryRecursive(dirPath, depth, options) {
22493
22581
  options.discovered.push({
22494
22582
  path: fullPath,
22495
22583
  relativePath: relative4(options.rootDir, fullPath),
22496
- directory: dirname9(fullPath),
22584
+ directory: dirname10(fullPath),
22497
22585
  stats: {
22498
22586
  size: stats.size,
22499
22587
  mtime: stats.mtime,
@@ -22606,7 +22694,7 @@ var LocalFilesystemAdapter = class extends BaseSourceAdapter {
22606
22694
  const stats = statResult.value;
22607
22695
  return {
22608
22696
  id: this.generateId(skillPath),
22609
- name: basename(dirname10(skillPath)),
22697
+ name: basename(dirname11(skillPath)),
22610
22698
  url: `file://${skillPath}`,
22611
22699
  description: null,
22612
22700
  owner: "local",
@@ -22730,11 +22818,11 @@ var LocalFilesystemAdapter = class extends BaseSourceAdapter {
22730
22818
  if (location.path?.startsWith("/")) {
22731
22819
  resolvedPath = location.path;
22732
22820
  } else if (location.path) {
22733
- resolvedPath = join17(this.rootDir, location.path);
22821
+ resolvedPath = join18(this.rootDir, location.path);
22734
22822
  } else if (location.owner && location.repo) {
22735
- resolvedPath = join17(this.rootDir, location.owner, location.repo, "SKILL.md");
22823
+ resolvedPath = join18(this.rootDir, location.owner, location.repo, "SKILL.md");
22736
22824
  } else if (location.repo) {
22737
- resolvedPath = join17(this.rootDir, location.repo, "SKILL.md");
22825
+ resolvedPath = join18(this.rootDir, location.repo, "SKILL.md");
22738
22826
  } else {
22739
22827
  throw new Error("Invalid location: must specify path or repo");
22740
22828
  }
@@ -23350,7 +23438,7 @@ import { createRequire as createRequire2 } from "node:module";
23350
23438
  import { existsSync as existsSync13, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "node:fs";
23351
23439
 
23352
23440
  // ../core/dist/src/db/drivers/corruption.js
23353
- import { existsSync as existsSync12, renameSync } from "node:fs";
23441
+ import { existsSync as existsSync12, renameSync as renameSync2 } from "node:fs";
23354
23442
  var CORRUPTION_MARKERS = [
23355
23443
  "sqlite_corrupt",
23356
23444
  "malformed",
@@ -23371,7 +23459,7 @@ function backupCorruptDbFile(path24) {
23371
23459
  }
23372
23460
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
23373
23461
  const backupPath = `${path24}.corrupt-${timestamp}`;
23374
- renameSync(path24, backupPath);
23462
+ renameSync2(path24, backupPath);
23375
23463
  return backupPath;
23376
23464
  }
23377
23465
 
@@ -23781,8 +23869,8 @@ function findSimilarBruteForceFromMap(embeddings, queryEmbedding, topK) {
23781
23869
  }
23782
23870
 
23783
23871
  // ../core/dist/src/embeddings/hnsw-search.js
23784
- import { existsSync as existsSync14, mkdirSync as mkdirSync8, readFileSync as readFileSync11, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "fs";
23785
- import { dirname as dirname11, join as join19 } from "path";
23872
+ import { existsSync as existsSync14, mkdirSync as mkdirSync8, readFileSync as readFileSync11, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync10 } from "fs";
23873
+ import { dirname as dirname12, join as join20 } from "path";
23786
23874
  var cachedCtor = null;
23787
23875
  async function loadHnswCtor() {
23788
23876
  if (cachedCtor === "unavailable")
@@ -23806,7 +23894,7 @@ async function loadHnswCtor() {
23806
23894
  function cachePaths(modelName) {
23807
23895
  const safeName = modelName.replace(/[/\\]/g, "__");
23808
23896
  const dir = getCacheDir();
23809
- const base = join19(dir, `hnsw-${safeName}`);
23897
+ const base = join20(dir, `hnsw-${safeName}`);
23810
23898
  return {
23811
23899
  bin: `${base}.bin`,
23812
23900
  meta: `${base}.meta.json`,
@@ -23841,9 +23929,9 @@ function readLabels(labelsPath) {
23841
23929
  }
23842
23930
  }
23843
23931
  function writeAtomic(tmp, final, contents) {
23844
- mkdirSync8(dirname11(tmp), { recursive: true });
23932
+ mkdirSync8(dirname12(tmp), { recursive: true });
23845
23933
  writeFileSync10(tmp, contents, typeof contents === "string" ? { encoding: "utf-8" } : void 0);
23846
- renameSync2(tmp, final);
23934
+ renameSync3(tmp, final);
23847
23935
  }
23848
23936
  async function loadOrBuildHnsw(args) {
23849
23937
  const Ctor = await loadHnswCtor();
@@ -23877,11 +23965,11 @@ async function loadOrBuildHnsw(args) {
23877
23965
  } catch (err) {
23878
23966
  try {
23879
23967
  if (existsSync14(paths.bin))
23880
- unlinkSync2(paths.bin);
23968
+ unlinkSync3(paths.bin);
23881
23969
  if (existsSync14(paths.meta))
23882
- unlinkSync2(paths.meta);
23970
+ unlinkSync3(paths.meta);
23883
23971
  if (existsSync14(paths.labels))
23884
- unlinkSync2(paths.labels);
23972
+ unlinkSync3(paths.labels);
23885
23973
  } catch {
23886
23974
  }
23887
23975
  try {
@@ -23944,7 +24032,7 @@ function createHandle(args) {
23944
24032
  return;
23945
24033
  }
23946
24034
  args.index.writeIndexSync(args.paths.binTmp);
23947
- renameSync2(args.paths.binTmp, args.paths.bin);
24035
+ renameSync3(args.paths.binTmp, args.paths.bin);
23948
24036
  const labelsArr = Array.from(args.labelToId.entries());
23949
24037
  writeAtomic(args.paths.labelsTmp, args.paths.labels, JSON.stringify(labelsArr));
23950
24038
  const meta3 = {
@@ -25384,17 +25472,17 @@ function redactSensitiveObject(obj, seen = /* @__PURE__ */ new WeakSet()) {
25384
25472
  }
25385
25473
 
25386
25474
  // ../core/dist/src/logging/rotation.js
25387
- import { createWriteStream, existsSync as existsSync15, statSync as statSync2 } from "node:fs";
25475
+ import { createWriteStream, existsSync as existsSync15, statSync as statSync3 } from "node:fs";
25388
25476
  import { mkdir as mkdir2, readdir as readdir2, stat, unlink as unlink2 } from "node:fs/promises";
25389
25477
  import { homedir as homedir10 } from "node:os";
25390
- import { join as join20 } from "node:path";
25478
+ import { join as join21 } from "node:path";
25391
25479
  var SIZE_CAP_BYTES = 10 * 1024 * 1024;
25392
25480
  var RETENTION_DAYS = 14;
25393
25481
  function getLogDir() {
25394
- return process.env.SKILLSMITH_LOG_DIR || join20(homedir10(), ".skillsmith", "logs");
25482
+ return process.env.SKILLSMITH_LOG_DIR || join21(homedir10(), ".skillsmith", "logs");
25395
25483
  }
25396
25484
  function dailyFilePath(surface, date5) {
25397
- return join20(getLogDir(), `skillsmith-${surface}-${date5}.jsonl`);
25485
+ return join21(getLogDir(), `skillsmith-${surface}-${date5}.jsonl`);
25398
25486
  }
25399
25487
  function nextRolledFilePath(surface, date5) {
25400
25488
  const base = dailyFilePath(surface, date5);
@@ -25405,7 +25493,7 @@ function nextRolledFilePath(surface, date5) {
25405
25493
  }
25406
25494
  function statSizeOrZero(filePath) {
25407
25495
  try {
25408
- return statSync2(filePath).size;
25496
+ return statSync3(filePath).size;
25409
25497
  } catch {
25410
25498
  return 0;
25411
25499
  }
@@ -25505,7 +25593,7 @@ async function pruneExpiredLogs() {
25505
25593
  const entries = await readdir2(dir);
25506
25594
  const cutoff = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
25507
25595
  await Promise.all(entries.map(async (name) => {
25508
- const full = join20(dir, name);
25596
+ const full = join21(dir, name);
25509
25597
  try {
25510
25598
  const info = await stat(full);
25511
25599
  if (info.isFile() && info.mtimeMs < cutoff) {
@@ -25686,7 +25774,7 @@ var INVENTORY_LIMITS = {
25686
25774
  // ../core/dist/src/sync/inventory-collector.js
25687
25775
  import { readdir as readdir3, readFile as readFile2, realpath, stat as stat2 } from "node:fs/promises";
25688
25776
  import { createHash as createHash4 } from "node:crypto";
25689
- import { join as join21 } from "node:path";
25777
+ import { join as join22 } from "node:path";
25690
25778
  async function safeRealpath(path24) {
25691
25779
  try {
25692
25780
  return await realpath(path24);
@@ -25705,14 +25793,14 @@ async function resolvesToDirectory(entryPath, isDirectory, isSymbolicLink) {
25705
25793
  return false;
25706
25794
  }
25707
25795
  }
25708
- async function readSkillFields(skillDir, dirName) {
25796
+ async function readSkillFields(skillDir) {
25709
25797
  try {
25710
- const content = await readFile2(join21(skillDir, "SKILL.md"), "utf-8");
25798
+ const content = await readFile2(join22(skillDir, "SKILL.md"), "utf-8");
25711
25799
  const contentHash = createHash4("sha256").update(content, "utf8").digest("hex");
25712
25800
  const parsed = new SkillParser().parse(content);
25713
25801
  if (!parsed) {
25714
25802
  return {
25715
- skillId: dirName,
25803
+ skillId: null,
25716
25804
  version: null,
25717
25805
  contentHash,
25718
25806
  author: null,
@@ -25722,7 +25810,7 @@ async function readSkillFields(skillDir, dirName) {
25722
25810
  }
25723
25811
  const parsedId = parsed["id"];
25724
25812
  return {
25725
- skillId: parsedId ?? parsed.name ?? dirName,
25813
+ skillId: parsedId ?? parsed.name ?? null,
25726
25814
  version: parsed.version ?? null,
25727
25815
  contentHash,
25728
25816
  author: parsed.author ?? null,
@@ -25731,7 +25819,7 @@ async function readSkillFields(skillDir, dirName) {
25731
25819
  };
25732
25820
  } catch {
25733
25821
  return {
25734
- skillId: dirName,
25822
+ skillId: null,
25735
25823
  version: null,
25736
25824
  contentHash: null,
25737
25825
  author: null,
@@ -25740,7 +25828,7 @@ async function readSkillFields(skillDir, dirName) {
25740
25828
  };
25741
25829
  }
25742
25830
  }
25743
- async function collectHarness(harness, entries, seenRealpaths) {
25831
+ async function collectHarness(harness, entries, fieldsCache, emitted) {
25744
25832
  const harnessDir = CLIENT_NATIVE_PATHS[harness];
25745
25833
  let dirents;
25746
25834
  try {
@@ -25754,18 +25842,27 @@ async function collectHarness(harness, entries, seenRealpaths) {
25754
25842
  for (const dirent of dirents) {
25755
25843
  if (dirent.name.startsWith("."))
25756
25844
  continue;
25757
- const entryPath = join21(harnessDir, dirent.name);
25845
+ const entryPath = join22(harnessDir, dirent.name);
25758
25846
  if (!await resolvesToDirectory(entryPath, dirent.isDirectory(), dirent.isSymbolicLink())) {
25759
25847
  continue;
25760
25848
  }
25761
25849
  const realDir = await safeRealpath(entryPath);
25762
- if (seenRealpaths.has(realDir))
25850
+ const emittedKey = `${harness}:${realDir}`;
25851
+ if (emitted.has(emittedKey))
25763
25852
  continue;
25764
- seenRealpaths.add(realDir);
25765
- const { skillId, version: version2, contentHash, author, license, repository } = await readSkillFields(entryPath, dirent.name);
25853
+ emitted.add(emittedKey);
25854
+ let fields = fieldsCache.get(realDir);
25855
+ if (!fields) {
25856
+ fields = await readSkillFields(entryPath);
25857
+ fieldsCache.set(realDir, fields);
25858
+ }
25859
+ const { skillId, version: version2, contentHash, author, license, repository } = fields;
25766
25860
  entries.push({
25767
25861
  harness,
25768
- skill_id: skillId,
25862
+ // skillId falls back to THIS dirent's own name — never cached (see
25863
+ // readSkillFields()'s docstring) — so a shared realpath with a
25864
+ // different directory name under another harness doesn't leak in.
25865
+ skill_id: skillId ?? dirent.name,
25769
25866
  version: version2,
25770
25867
  content_hash: contentHash,
25771
25868
  source: null,
@@ -25779,35 +25876,52 @@ async function collectHarness(harness, entries, seenRealpaths) {
25779
25876
  }
25780
25877
  async function collectDeviceSkills() {
25781
25878
  const entries = [];
25782
- const seenRealpaths = /* @__PURE__ */ new Set();
25879
+ const fieldsCache = /* @__PURE__ */ new Map();
25880
+ const emitted = /* @__PURE__ */ new Set();
25783
25881
  for (const harness of CLIENT_IDS) {
25784
- await collectHarness(harness, entries, seenRealpaths);
25882
+ await collectHarness(harness, entries, fieldsCache, emitted);
25785
25883
  }
25786
25884
  return entries;
25787
25885
  }
25788
25886
 
25789
25887
  // ../core/dist/src/sync/inventory-device.js
25790
25888
  import { hostname as hostname3 } from "node:os";
25791
- import { createHash as createHash5 } from "node:crypto";
25889
+ import { createHash as createHash6 } from "node:crypto";
25792
25890
 
25793
25891
  // ../core/dist/src/config/device-identity.js
25794
- import { randomUUID as randomUUID2 } from "node:crypto";
25892
+ import { randomUUID as randomUUID2, createHash as createHash5 } from "node:crypto";
25893
+ import { chmodSync as chmodSync4 } from "node:fs";
25894
+ function getOrCreatePersistedId(getExisting, generate, merge2) {
25895
+ const fastPathExisting = getExisting(loadConfig());
25896
+ if (fastPathExisting)
25897
+ return fastPathExisting;
25898
+ ensureConfigDir();
25899
+ const configPath2 = getConfigPath();
25900
+ const release = acquireConfigLock(configPath2);
25901
+ try {
25902
+ const latest = loadConfig();
25903
+ const alreadyCreated = getExisting(latest);
25904
+ if (alreadyCreated)
25905
+ return alreadyCreated;
25906
+ const id = generate();
25907
+ atomicWriteFile(configPath2, JSON.stringify(merge2(latest, id), null, 2), 384);
25908
+ try {
25909
+ chmodSync4(configPath2, 384);
25910
+ } catch {
25911
+ }
25912
+ return id;
25913
+ } finally {
25914
+ release();
25915
+ }
25916
+ }
25795
25917
  function getDeviceId() {
25796
25918
  return loadConfig().inventory?.deviceId;
25797
25919
  }
25798
25920
  function getOrCreateDeviceId() {
25799
- const config2 = loadConfig();
25800
- const existing = config2.inventory?.deviceId;
25801
- if (existing)
25802
- return existing;
25803
- const deviceId = randomUUID2();
25804
- saveConfig({
25805
- inventory: {
25806
- ...config2.inventory,
25807
- deviceId
25808
- }
25809
- });
25810
- return deviceId;
25921
+ return getOrCreatePersistedId((config2) => config2.inventory?.deviceId, () => randomUUID2(), (latest, deviceId) => ({
25922
+ ...latest,
25923
+ inventory: { ...latest.inventory, deviceId }
25924
+ }));
25811
25925
  }
25812
25926
  function forgetDevice() {
25813
25927
  saveConfig({ inventory: void 0 });
@@ -25840,7 +25954,7 @@ function capped(value, max) {
25840
25954
  function buildInventoryDevice(opts) {
25841
25955
  const deviceId = getOrCreateDeviceId();
25842
25956
  const deviceLabel = loadConfig().inventory?.deviceLabel;
25843
- const hostnameHash = createHash5("sha256").update(hostname3(), "utf8").digest("hex");
25957
+ const hostnameHash = createHash6("sha256").update(hostname3(), "utf8").digest("hex");
25844
25958
  return {
25845
25959
  device_id: deviceId,
25846
25960
  label: capped(deviceLabel, INVENTORY_LIMITS.LABEL_MAX),
@@ -25862,8 +25976,8 @@ async function buildInventoryPayload(opts) {
25862
25976
 
25863
25977
  // ../core/dist/src/config/token-credentials.js
25864
25978
  import { homedir as homedir11 } from "os";
25865
- import { join as join22 } from "path";
25866
- import { existsSync as existsSync16, readFileSync as readFileSync12, writeFileSync as writeFileSync11, chmodSync as chmodSync3 } from "fs";
25979
+ import { join as join23 } from "path";
25980
+ import { existsSync as existsSync16, readFileSync as readFileSync12, writeFileSync as writeFileSync11, chmodSync as chmodSync5 } from "fs";
25867
25981
 
25868
25982
  // ../core/dist/src/api/utils.js
25869
25983
  function calculateBackoff(attempt, baseDelay = 1e3) {
@@ -25892,7 +26006,7 @@ var KEYTAR_SERVICE2 = "skillsmith-cli";
25892
26006
  var KEYTAR_ACCOUNT_REFRESH = "refresh-token";
25893
26007
  var SUPABASE_AUTH_URL = (process.env.SUPABASE_URL ?? "https://vrcnzpmndtroqxxoqkzy.supabase.co") + "/auth/v1";
25894
26008
  function getConfigPath2() {
25895
- return join22(homedir11(), CONFIG_DIR2, CONFIG_FILE2);
26009
+ return join23(homedir11(), CONFIG_DIR2, CONFIG_FILE2);
25896
26010
  }
25897
26011
  function readConfigFile() {
25898
26012
  const p = getConfigPath2();
@@ -25909,7 +26023,7 @@ function writeConfigFile(data) {
25909
26023
  const p = getConfigPath2();
25910
26024
  writeFileSync11(p, JSON.stringify(data, null, 2), { encoding: "utf-8", mode: 384 });
25911
26025
  try {
25912
- chmodSync3(p, 384);
26026
+ chmodSync5(p, 384);
25913
26027
  } catch {
25914
26028
  }
25915
26029
  }
@@ -26200,10 +26314,16 @@ async function sendAuditDigest(payload) {
26200
26314
  }
26201
26315
 
26202
26316
  // ../core/dist/src/analysis/McpReferenceExtractor.js
26317
+ import { LineCounter, isScalar as isScalar2, isSeq, parseDocument as parseDocument2 } from "yaml";
26203
26318
  var MAX_INPUT_BYTES = 100 * 1024;
26204
26319
  var MCP_PATTERN = /mcp__([a-z][a-z0-9-]*)__([a-z][a-z0-9_]*)/g;
26205
26320
  var FENCE_PATTERN = /^(`{3,}|~{3,})/;
26206
- function extractMcpReferences(content) {
26321
+ var FRONTMATTER_DELIMITER = /^---\s*$/;
26322
+ var TOOL_LIST_FIELDS = ["allowed-tools", "tools"];
26323
+ var FRONTMATTER_MCP_TOKEN = /^mcp__([a-z][a-z0-9-]*)(?:__([a-z][a-z0-9_]*|\*))?$/;
26324
+ var JSON_SERVER_NAME = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
26325
+ var MAX_MCP_SERVERS_MARKERS = 20;
26326
+ function extractMcpReferences(content, registeredServers) {
26207
26327
  let truncated;
26208
26328
  if (new TextEncoder().encode(content).byteLength > MAX_INPUT_BYTES) {
26209
26329
  content = content.slice(0, MAX_INPUT_BYTES);
@@ -26213,12 +26333,16 @@ function extractMcpReferences(content) {
26213
26333
  const references = [];
26214
26334
  const serverSet = /* @__PURE__ */ new Set();
26215
26335
  const highConfidenceSet = /* @__PURE__ */ new Set();
26336
+ const lineInCodeBlock = [];
26337
+ const frontmatterBlock = extractFrontmatterBlock(lines);
26338
+ const frontmatterEndLine = frontmatterBlock ? frontmatterBlock.endLine : 0;
26216
26339
  let inCodeBlock = false;
26217
26340
  let fenceChar = null;
26218
26341
  let fenceLength = 0;
26219
26342
  for (let i = 0; i < lines.length; i++) {
26220
26343
  const line = lines[i];
26221
26344
  const lineNumber = i + 1;
26345
+ const inFrontmatter = lineNumber <= frontmatterEndLine;
26222
26346
  const fenceMatch = FENCE_PATTERN.exec(line);
26223
26347
  if (fenceMatch) {
26224
26348
  const matchChar = fenceMatch[1][0];
@@ -26233,33 +26357,176 @@ function extractMcpReferences(content) {
26233
26357
  fenceLength = 0;
26234
26358
  }
26235
26359
  }
26236
- let match;
26237
- MCP_PATTERN.lastIndex = 0;
26238
- while ((match = MCP_PATTERN.exec(line)) !== null) {
26239
- const server = match[1];
26240
- const tool = match[2];
26241
- references.push({
26242
- server,
26243
- tool,
26244
- line: lineNumber,
26245
- inCodeBlock
26246
- });
26247
- serverSet.add(server);
26248
- if (!inCodeBlock) {
26249
- highConfidenceSet.add(server);
26360
+ if (!inFrontmatter) {
26361
+ let match;
26362
+ MCP_PATTERN.lastIndex = 0;
26363
+ while ((match = MCP_PATTERN.exec(line)) !== null) {
26364
+ const server = match[1];
26365
+ const tool = match[2];
26366
+ references.push({
26367
+ server,
26368
+ tool,
26369
+ line: lineNumber,
26370
+ inCodeBlock
26371
+ });
26372
+ serverSet.add(server);
26373
+ if (!inCodeBlock) {
26374
+ highConfidenceSet.add(server);
26375
+ }
26250
26376
  }
26251
26377
  }
26378
+ lineInCodeBlock.push(inCodeBlock);
26379
+ }
26380
+ for (const ref of extractFrontmatterMcpRefs(frontmatterBlock)) {
26381
+ references.push({ server: ref.server, tool: ref.tool, line: ref.line, inCodeBlock: false });
26382
+ serverSet.add(ref.server);
26383
+ highConfidenceSet.add(ref.server);
26384
+ }
26385
+ for (const ref of extractMcpServersJsonRefs(content)) {
26386
+ const refInCodeBlock = lineInCodeBlock[ref.line - 1] ?? false;
26387
+ references.push({ server: ref.server, tool: "*", line: ref.line, inCodeBlock: refInCodeBlock });
26388
+ serverSet.add(ref.server);
26389
+ if (!refInCodeBlock) {
26390
+ highConfidenceSet.add(ref.server);
26391
+ }
26392
+ }
26393
+ references.sort((a, b) => a.line - b.line);
26394
+ const serverResolutions = {};
26395
+ for (const server of serverSet) {
26396
+ serverResolutions[server] = registeredServers === void 0 ? "unknown" : registeredServers.includes(server) ? "registered" : "unregistered";
26252
26397
  }
26253
26398
  const result = {
26254
26399
  references,
26255
26400
  servers: [...serverSet].sort(),
26256
- highConfidenceServers: [...highConfidenceSet].sort()
26401
+ highConfidenceServers: [...highConfidenceSet].sort(),
26402
+ serverResolutions
26257
26403
  };
26258
26404
  if (truncated) {
26259
26405
  result.truncated = true;
26260
26406
  }
26261
26407
  return result;
26262
26408
  }
26409
+ function extractFrontmatterBlock(lines) {
26410
+ if (lines.length === 0 || !FRONTMATTER_DELIMITER.test(lines[0]))
26411
+ return null;
26412
+ for (let i = 1; i < lines.length; i++) {
26413
+ if (FRONTMATTER_DELIMITER.test(lines[i])) {
26414
+ return { yamlLines: lines.slice(1, i), startLine: 2, endLine: i + 1 };
26415
+ }
26416
+ }
26417
+ return null;
26418
+ }
26419
+ function extractFrontmatterMcpRefs(block) {
26420
+ if (!block || block.yamlLines.length === 0)
26421
+ return [];
26422
+ const yamlSource = block.yamlLines.join("\n");
26423
+ const lineCounter = new LineCounter();
26424
+ let doc;
26425
+ try {
26426
+ doc = parseDocument2(yamlSource, { lineCounter });
26427
+ } catch {
26428
+ return [];
26429
+ }
26430
+ if (doc.errors.length > 0)
26431
+ return [];
26432
+ const refs = [];
26433
+ for (const field of TOOL_LIST_FIELDS) {
26434
+ let node;
26435
+ try {
26436
+ node = doc.get(field, true);
26437
+ } catch {
26438
+ continue;
26439
+ }
26440
+ if (node === void 0 || node === null)
26441
+ continue;
26442
+ const scalarNodes = isSeq(node) ? node.items : isScalar2(node) ? [node] : [];
26443
+ for (const item of scalarNodes) {
26444
+ if (!isScalar2(item) || typeof item.value !== "string")
26445
+ continue;
26446
+ const tokenMatch = FRONTMATTER_MCP_TOKEN.exec(item.value.trim());
26447
+ if (!tokenMatch)
26448
+ continue;
26449
+ const server = tokenMatch[1];
26450
+ const tool = tokenMatch[2] && tokenMatch[2] !== "*" ? tokenMatch[2] : "*";
26451
+ const range = item.range;
26452
+ const relativeLine = range ? lineCounter.linePos(range[0]).line : 1;
26453
+ refs.push({ server, tool, line: block.startLine + relativeLine - 1 });
26454
+ }
26455
+ }
26456
+ return refs;
26457
+ }
26458
+ function findMatchingBrace(text, openIdx) {
26459
+ let depth = 0;
26460
+ let inString = false;
26461
+ let escapeNext = false;
26462
+ for (let i = openIdx; i < text.length; i++) {
26463
+ const ch = text[i];
26464
+ if (escapeNext) {
26465
+ escapeNext = false;
26466
+ continue;
26467
+ }
26468
+ if (inString) {
26469
+ if (ch === "\\")
26470
+ escapeNext = true;
26471
+ else if (ch === '"')
26472
+ inString = false;
26473
+ continue;
26474
+ }
26475
+ if (ch === '"') {
26476
+ inString = true;
26477
+ continue;
26478
+ }
26479
+ if (ch === "{") {
26480
+ depth++;
26481
+ } else if (ch === "}") {
26482
+ depth--;
26483
+ if (depth === 0)
26484
+ return i;
26485
+ }
26486
+ }
26487
+ return -1;
26488
+ }
26489
+ function extractMcpServersJsonRefs(content) {
26490
+ const refs = [];
26491
+ const marker = '"mcpServers"';
26492
+ let fromIdx = 0;
26493
+ let markersProcessed = 0;
26494
+ while (markersProcessed < MAX_MCP_SERVERS_MARKERS) {
26495
+ const markerIdx = content.indexOf(marker, fromIdx);
26496
+ if (markerIdx === -1)
26497
+ break;
26498
+ fromIdx = markerIdx + marker.length;
26499
+ markersProcessed++;
26500
+ let i = markerIdx + marker.length;
26501
+ while (i < content.length && /\s/.test(content[i]))
26502
+ i++;
26503
+ if (content[i] !== ":")
26504
+ continue;
26505
+ i++;
26506
+ while (i < content.length && /\s/.test(content[i]))
26507
+ i++;
26508
+ if (content[i] !== "{")
26509
+ continue;
26510
+ const closeIdx = findMatchingBrace(content, i);
26511
+ if (closeIdx === -1)
26512
+ continue;
26513
+ let parsed;
26514
+ try {
26515
+ parsed = JSON.parse(content.slice(i, closeIdx + 1));
26516
+ } catch {
26517
+ continue;
26518
+ }
26519
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
26520
+ const lineNumber = content.slice(0, markerIdx).split("\n").length;
26521
+ for (const server of Object.keys(parsed)) {
26522
+ if (JSON_SERVER_NAME.test(server)) {
26523
+ refs.push({ server, line: lineNumber });
26524
+ }
26525
+ }
26526
+ }
26527
+ }
26528
+ return refs;
26529
+ }
26263
26530
 
26264
26531
  // ../core/dist/src/analysis/DependencyMerger.js
26265
26532
  function mergeDependencies(declared, inferred) {
@@ -26419,153 +26686,15 @@ function addInferredMcp(inferred, declaredMcpServers, result) {
26419
26686
  }
26420
26687
  }
26421
26688
 
26422
- // ../core/dist/src/services/skill-installation.service.js
26423
- import * as path6 from "path";
26424
- import * as os3 from "os";
26425
-
26426
- // ../core/dist/src/services/skill-installation.types.js
26427
- var TRUST_TIER_SCANNER_OPTIONS = {
26428
- verified: {
26429
- riskThreshold: 70,
26430
- maxContentLength: 2e6
26431
- },
26432
- curated: {
26433
- riskThreshold: 60,
26434
- maxContentLength: 2e6
26435
- },
26436
- community: {
26437
- riskThreshold: 40,
26438
- maxContentLength: 1e6
26439
- },
26440
- local: {
26441
- riskThreshold: 100,
26442
- maxContentLength: 1e7
26443
- },
26444
- experimental: {
26445
- riskThreshold: 25,
26446
- maxContentLength: 5e5
26447
- },
26448
- unknown: {
26449
- riskThreshold: 20,
26450
- maxContentLength: 25e4
26451
- },
26452
- // SMI-5205: new public tiers
26453
- official: {
26454
- riskThreshold: 80,
26455
- maxContentLength: 2e6
26456
- },
26457
- unverified: {
26458
- riskThreshold: 20,
26459
- // Same as unknown — unverified is the public alias for unknown
26460
- maxContentLength: 25e4
26461
- }
26462
- };
26463
-
26464
- // ../core/dist/src/services/skill-installation.feedback.js
26465
- function recordAiDefenceFeedback(params) {
26466
- if (!params.feedback || !params.scanReport)
26467
- return;
26468
- const report = params.scanReport;
26469
- params.feedback.recordFeedback({
26470
- input: params.skillMdContent.slice(0, 1e3),
26471
- wasAccurate: true,
26472
- verdict: params.blocked ? "true_positive" : report.passed ? "true_negative" : "true_positive",
26473
- threatType: !report.passed ? report.findings[0]?.type : void 0,
26474
- mitigation: params.blocked ? "block" : report.passed ? "log" : "block",
26475
- mitigationSuccess: true
26476
- }).catch(() => {
26477
- });
26478
- }
26479
- function collectTrendWarnings(params) {
26480
- if (!params.historyRepo)
26481
- return [];
26482
- try {
26483
- const history = params.historyRepo.getHistory(params.skillId, 5);
26484
- const trend = detectRiskTrend(params.scanReport.riskScore, history);
26485
- return trend.anomaly ? [trend.message] : [];
26486
- } catch {
26487
- return [];
26488
- }
26489
- }
26490
-
26491
- // ../core/dist/src/services/skill-manifest.js
26492
- import * as fs4 from "fs/promises";
26493
- import * as path3 from "path";
26494
- var MANIFEST_LOCK_TIMEOUT_MS = 3e4;
26495
- var MANIFEST_LOCK_RETRY_MS = 100;
26496
- var ManifestManager = class {
26497
- manifestPath;
26498
- constructor(manifestPath) {
26499
- this.manifestPath = manifestPath;
26500
- }
26501
- async load() {
26502
- try {
26503
- const content = await fs4.readFile(this.manifestPath, "utf-8");
26504
- return JSON.parse(content);
26505
- } catch {
26506
- return { version: "1.0.0", installedSkills: {} };
26507
- }
26508
- }
26509
- async save(manifest) {
26510
- await fs4.mkdir(path3.dirname(this.manifestPath), { recursive: true });
26511
- const tempPath = this.manifestPath + ".tmp." + process.pid;
26512
- await fs4.writeFile(tempPath, JSON.stringify(manifest, null, 2));
26513
- await fs4.rename(tempPath, this.manifestPath);
26514
- }
26515
- async acquireLock() {
26516
- const lockPath = this.manifestPath + ".lock";
26517
- const startTime = Date.now();
26518
- await fs4.mkdir(path3.dirname(this.manifestPath), { recursive: true });
26519
- while (Date.now() - startTime < MANIFEST_LOCK_TIMEOUT_MS) {
26520
- try {
26521
- await fs4.writeFile(lockPath, String(process.pid), { flag: "wx" });
26522
- return;
26523
- } catch (error46) {
26524
- if (error46.code === "EEXIST") {
26525
- try {
26526
- const stats = await fs4.stat(lockPath);
26527
- if (Date.now() - stats.mtimeMs > MANIFEST_LOCK_TIMEOUT_MS) {
26528
- await fs4.unlink(lockPath).catch(() => {
26529
- });
26530
- continue;
26531
- }
26532
- } catch {
26533
- continue;
26534
- }
26535
- await new Promise((resolve18) => setTimeout(resolve18, MANIFEST_LOCK_RETRY_MS));
26536
- } else {
26537
- throw error46;
26538
- }
26539
- }
26540
- }
26541
- throw new Error("Failed to acquire manifest lock after " + MANIFEST_LOCK_TIMEOUT_MS + "ms");
26542
- }
26543
- async releaseLock() {
26544
- try {
26545
- await fs4.unlink(this.manifestPath + ".lock");
26546
- } catch {
26547
- }
26548
- }
26549
- async updateSafely(updateFn) {
26550
- await this.acquireLock();
26551
- try {
26552
- const manifest = await this.load();
26553
- const updated = updateFn(manifest);
26554
- await this.save(updated);
26555
- } finally {
26556
- await this.releaseLock();
26557
- }
26558
- }
26559
- };
26560
-
26561
26689
  // ../core/dist/src/services/skill-installation.helpers.js
26562
- import * as fs6 from "fs/promises";
26563
- import * as path5 from "path";
26564
- import { createHash as createHash6 } from "crypto";
26565
-
26566
- // ../core/dist/src/services/skill-installation.io.js
26567
26690
  import * as fs5 from "fs/promises";
26691
+ import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
26568
26692
  import * as path4 from "path";
26693
+ import { createHash as createHash7 } from "crypto";
26694
+
26695
+ // ../core/dist/src/services/skill-installation.io.js
26696
+ import * as fs4 from "fs/promises";
26697
+ import * as path3 from "path";
26569
26698
  import * as os2 from "os";
26570
26699
 
26571
26700
  // ../core/dist/src/utils/safe-fs.js
@@ -26946,11 +27075,11 @@ async function fetchFromGitHub(owner, repo, filePath, branch = "main") {
26946
27075
  async function checkForModifications(skillPath, installedAt) {
26947
27076
  try {
26948
27077
  const installDate = new Date(installedAt);
26949
- const files = await fs5.readdir(skillPath, { withFileTypes: true });
27078
+ const files = await fs4.readdir(skillPath, { withFileTypes: true });
26950
27079
  for (const file2 of files) {
26951
27080
  if (file2.isFile()) {
26952
- const filePath = path4.join(skillPath, file2.name);
26953
- const stats = await fs5.stat(filePath);
27081
+ const filePath = path3.join(skillPath, file2.name);
27082
+ const stats = await fs4.stat(filePath);
26954
27083
  if (stats.mtime > installDate) {
26955
27084
  return true;
26956
27085
  }
@@ -26964,47 +27093,47 @@ async function checkForModifications(skillPath, installedAt) {
26964
27093
  async function writeInstallFiles(installPath, skillsDir, skillName, finalSkillContent, subSkillFiles, subagentContent) {
26965
27094
  const writtenFiles = [];
26966
27095
  let subagentPath;
26967
- const resolvedInstall = path4.resolve(installPath);
26968
- const resolvedSkillsDir = path4.resolve(skillsDir);
26969
- if (resolvedInstall !== resolvedSkillsDir && !resolvedInstall.startsWith(resolvedSkillsDir + path4.sep)) {
27096
+ const resolvedInstall = path3.resolve(installPath);
27097
+ const resolvedSkillsDir = path3.resolve(skillsDir);
27098
+ if (resolvedInstall !== resolvedSkillsDir && !resolvedInstall.startsWith(resolvedSkillsDir + path3.sep)) {
26970
27099
  throw new Error("Install path escapes skills directory (lexical): " + installPath);
26971
27100
  }
26972
27101
  let pathValidated = false;
26973
27102
  try {
26974
- await fs5.mkdir(installPath, { recursive: true });
26975
- const realInstallPath = await fs5.realpath(installPath);
26976
- const expectedPrefix = await fs5.realpath(skillsDir).catch(() => path4.resolve(skillsDir));
26977
- if (!realInstallPath.startsWith(expectedPrefix + path4.sep) && realInstallPath !== expectedPrefix) {
27103
+ await fs4.mkdir(installPath, { recursive: true });
27104
+ const realInstallPath = await fs4.realpath(installPath);
27105
+ const expectedPrefix = await fs4.realpath(skillsDir).catch(() => path3.resolve(skillsDir));
27106
+ if (!realInstallPath.startsWith(expectedPrefix + path3.sep) && realInstallPath !== expectedPrefix) {
26978
27107
  throw new Error("Install path escapes skills directory (realpath): " + installPath);
26979
27108
  }
26980
27109
  pathValidated = true;
26981
- const mainSkillPath = path4.join(installPath, "SKILL.md");
27110
+ const mainSkillPath = path3.join(installPath, "SKILL.md");
26982
27111
  await safeWriteFile(mainSkillPath, finalSkillContent);
26983
27112
  writtenFiles.push(mainSkillPath);
26984
27113
  if (subSkillFiles.length > 0) {
26985
27114
  await Promise.all(subSkillFiles.map(async (subSkill) => {
26986
- const subPath = path4.join(installPath, subSkill.filename);
27115
+ const subPath = path3.join(installPath, subSkill.filename);
26987
27116
  await safeWriteFile(subPath, subSkill.content);
26988
27117
  writtenFiles.push(subPath);
26989
27118
  }));
26990
27119
  }
26991
27120
  if (subagentContent) {
26992
- const agentsDir = path4.join(os2.homedir(), ".claude", "agents");
26993
- await fs5.mkdir(agentsDir, { recursive: true });
26994
- subagentPath = path4.join(agentsDir, skillName + "-specialist.md");
27121
+ const agentsDir = path3.join(os2.homedir(), ".claude", "agents");
27122
+ await fs4.mkdir(agentsDir, { recursive: true });
27123
+ subagentPath = path3.join(agentsDir, skillName + "-specialist.md");
26995
27124
  await safeWriteFile(subagentPath, subagentContent);
26996
27125
  writtenFiles.push(subagentPath);
26997
27126
  }
26998
27127
  } catch (writeError) {
26999
27128
  for (const filePath of writtenFiles) {
27000
- await fs5.unlink(filePath).catch(() => {
27129
+ await fs4.unlink(filePath).catch(() => {
27001
27130
  });
27002
27131
  }
27003
27132
  if (pathValidated) {
27004
- await fs5.rm(installPath, { recursive: true, force: true }).catch(() => {
27133
+ await fs4.rm(installPath, { recursive: true, force: true }).catch(() => {
27005
27134
  });
27006
27135
  } else {
27007
- await fs5.rmdir(installPath).catch(() => {
27136
+ await fs4.rmdir(installPath).catch(() => {
27008
27137
  });
27009
27138
  }
27010
27139
  throw writeError;
@@ -27055,7 +27184,7 @@ async function fetchAndScanOptionalFiles(owner, repo, basePath, branch, skillId,
27055
27184
 
27056
27185
  // ../core/dist/src/services/skill-installation.helpers.js
27057
27186
  function hashContent2(content) {
27058
- return createHash6("sha256").update(content).digest("hex");
27187
+ return createHash7("sha256").update(content).digest("hex");
27059
27188
  }
27060
27189
  function generateTips(skillName, optimizationInfo) {
27061
27190
  const tips = [
@@ -27078,10 +27207,30 @@ function generateTips(skillName, optimizationInfo) {
27078
27207
  tips.push("", "To uninstall: use the uninstall_skill tool");
27079
27208
  return tips;
27080
27209
  }
27210
+ function getRegisteredMcpServers(projectRoot = process.cwd()) {
27211
+ const mcpJsonPath = path4.join(projectRoot, ".mcp.json");
27212
+ if (!existsSync17(mcpJsonPath))
27213
+ return void 0;
27214
+ try {
27215
+ const raw = readFileSync13(mcpJsonPath, "utf-8");
27216
+ const parsed = JSON.parse(raw);
27217
+ if (!parsed || typeof parsed !== "object")
27218
+ return void 0;
27219
+ const mcpServers = parsed.mcpServers;
27220
+ if (!mcpServers || typeof mcpServers !== "object" || Array.isArray(mcpServers)) {
27221
+ return void 0;
27222
+ }
27223
+ return Object.keys(mcpServers);
27224
+ } catch {
27225
+ return void 0;
27226
+ }
27227
+ }
27081
27228
  function extractDepIntel(skillMdContent) {
27082
- const mcpResult = extractMcpReferences(skillMdContent);
27229
+ const mcpResult = extractMcpReferences(skillMdContent, getRegisteredMcpServers());
27083
27230
  const warnings = [];
27084
27231
  for (const server of mcpResult.highConfidenceServers) {
27232
+ if (mcpResult.serverResolutions?.[server] === "registered")
27233
+ continue;
27085
27234
  warnings.push("MCP server '" + server + "' is referenced but may not be configured");
27086
27235
  }
27087
27236
  return {
@@ -27091,7 +27240,7 @@ function extractDepIntel(skillMdContent) {
27091
27240
  };
27092
27241
  }
27093
27242
  function persistDependencies(repo, skillId, content, declared) {
27094
- const mcpResult = extractMcpReferences(content);
27243
+ const mcpResult = extractMcpReferences(content, getRegisteredMcpServers());
27095
27244
  const merged = mergeDependencies(declared, mcpResult);
27096
27245
  if (merged.length === 0)
27097
27246
  return 0;
@@ -27122,9 +27271,9 @@ async function performUninstall(params) {
27122
27271
  const manifestData = await manifest.load();
27123
27272
  const skillEntry = manifestData.installedSkills[skillName];
27124
27273
  if (!skillEntry) {
27125
- const potentialPath = path5.join(skillsDir, skillName);
27274
+ const potentialPath = path4.join(skillsDir, skillName);
27126
27275
  try {
27127
- await fs6.access(potentialPath);
27276
+ await fs5.access(potentialPath);
27128
27277
  if (!force) {
27129
27278
  return {
27130
27279
  success: false,
@@ -27134,7 +27283,7 @@ async function performUninstall(params) {
27134
27283
  };
27135
27284
  }
27136
27285
  onProgress("remove", "Removing orphan skill from disk");
27137
- await fs6.rm(potentialPath, { recursive: true, force: true });
27286
+ await fs5.rm(potentialPath, { recursive: true, force: true });
27138
27287
  return {
27139
27288
  success: true,
27140
27289
  skillName,
@@ -27161,7 +27310,7 @@ async function performUninstall(params) {
27161
27310
  }
27162
27311
  onProgress("remove", "Removing skill directory");
27163
27312
  try {
27164
- await fs6.rm(installPath, { recursive: true, force: true });
27313
+ await fs5.rm(installPath, { recursive: true, force: true });
27165
27314
  } catch (error46) {
27166
27315
  if (error46.code !== "ENOENT")
27167
27316
  throw error46;
@@ -27247,6 +27396,145 @@ function sanitizeInstallError(error46) {
27247
27396
  return "Installation failed due to an internal error";
27248
27397
  }
27249
27398
 
27399
+ // ../core/dist/src/services/skill-installation.service.js
27400
+ import * as path6 from "path";
27401
+ import * as os3 from "os";
27402
+
27403
+ // ../core/dist/src/services/skill-installation.types.js
27404
+ var TRUST_TIER_SCANNER_OPTIONS = {
27405
+ verified: {
27406
+ riskThreshold: 70,
27407
+ maxContentLength: 2e6
27408
+ },
27409
+ curated: {
27410
+ riskThreshold: 60,
27411
+ maxContentLength: 2e6
27412
+ },
27413
+ community: {
27414
+ riskThreshold: 40,
27415
+ maxContentLength: 1e6
27416
+ },
27417
+ local: {
27418
+ riskThreshold: 100,
27419
+ maxContentLength: 1e7
27420
+ },
27421
+ experimental: {
27422
+ riskThreshold: 25,
27423
+ maxContentLength: 5e5
27424
+ },
27425
+ unknown: {
27426
+ riskThreshold: 20,
27427
+ maxContentLength: 25e4
27428
+ },
27429
+ // SMI-5205: new public tiers
27430
+ official: {
27431
+ riskThreshold: 80,
27432
+ maxContentLength: 2e6
27433
+ },
27434
+ unverified: {
27435
+ riskThreshold: 20,
27436
+ // Same as unknown — unverified is the public alias for unknown
27437
+ maxContentLength: 25e4
27438
+ }
27439
+ };
27440
+
27441
+ // ../core/dist/src/services/skill-installation.feedback.js
27442
+ function recordAiDefenceFeedback(params) {
27443
+ if (!params.feedback || !params.scanReport)
27444
+ return;
27445
+ const report = params.scanReport;
27446
+ params.feedback.recordFeedback({
27447
+ input: params.skillMdContent.slice(0, 1e3),
27448
+ wasAccurate: true,
27449
+ verdict: params.blocked ? "true_positive" : report.passed ? "true_negative" : "true_positive",
27450
+ threatType: !report.passed ? report.findings[0]?.type : void 0,
27451
+ mitigation: params.blocked ? "block" : report.passed ? "log" : "block",
27452
+ mitigationSuccess: true
27453
+ }).catch(() => {
27454
+ });
27455
+ }
27456
+ function collectTrendWarnings(params) {
27457
+ if (!params.historyRepo)
27458
+ return [];
27459
+ try {
27460
+ const history = params.historyRepo.getHistory(params.skillId, 5);
27461
+ const trend = detectRiskTrend(params.scanReport.riskScore, history);
27462
+ return trend.anomaly ? [trend.message] : [];
27463
+ } catch {
27464
+ return [];
27465
+ }
27466
+ }
27467
+
27468
+ // ../core/dist/src/services/skill-manifest.js
27469
+ import * as fs6 from "fs/promises";
27470
+ import * as path5 from "path";
27471
+ var MANIFEST_LOCK_TIMEOUT_MS = 3e4;
27472
+ var MANIFEST_LOCK_RETRY_MS = 100;
27473
+ var ManifestManager = class {
27474
+ manifestPath;
27475
+ constructor(manifestPath) {
27476
+ this.manifestPath = manifestPath;
27477
+ }
27478
+ async load() {
27479
+ try {
27480
+ const content = await fs6.readFile(this.manifestPath, "utf-8");
27481
+ return JSON.parse(content);
27482
+ } catch {
27483
+ return { version: "1.0.0", installedSkills: {} };
27484
+ }
27485
+ }
27486
+ async save(manifest) {
27487
+ await fs6.mkdir(path5.dirname(this.manifestPath), { recursive: true });
27488
+ const tempPath = this.manifestPath + ".tmp." + process.pid;
27489
+ await fs6.writeFile(tempPath, JSON.stringify(manifest, null, 2));
27490
+ await fs6.rename(tempPath, this.manifestPath);
27491
+ }
27492
+ async acquireLock() {
27493
+ const lockPath = this.manifestPath + ".lock";
27494
+ const startTime = Date.now();
27495
+ await fs6.mkdir(path5.dirname(this.manifestPath), { recursive: true });
27496
+ while (Date.now() - startTime < MANIFEST_LOCK_TIMEOUT_MS) {
27497
+ try {
27498
+ await fs6.writeFile(lockPath, String(process.pid), { flag: "wx" });
27499
+ return;
27500
+ } catch (error46) {
27501
+ if (error46.code === "EEXIST") {
27502
+ try {
27503
+ const stats = await fs6.stat(lockPath);
27504
+ if (Date.now() - stats.mtimeMs > MANIFEST_LOCK_TIMEOUT_MS) {
27505
+ await fs6.unlink(lockPath).catch(() => {
27506
+ });
27507
+ continue;
27508
+ }
27509
+ } catch {
27510
+ continue;
27511
+ }
27512
+ await new Promise((resolve18) => setTimeout(resolve18, MANIFEST_LOCK_RETRY_MS));
27513
+ } else {
27514
+ throw error46;
27515
+ }
27516
+ }
27517
+ }
27518
+ throw new Error("Failed to acquire manifest lock after " + MANIFEST_LOCK_TIMEOUT_MS + "ms");
27519
+ }
27520
+ async releaseLock() {
27521
+ try {
27522
+ await fs6.unlink(this.manifestPath + ".lock");
27523
+ } catch {
27524
+ }
27525
+ }
27526
+ async updateSafely(updateFn) {
27527
+ await this.acquireLock();
27528
+ try {
27529
+ const manifest = await this.load();
27530
+ const updated = updateFn(manifest);
27531
+ await this.save(updated);
27532
+ } finally {
27533
+ await this.releaseLock();
27534
+ }
27535
+ }
27536
+ };
27537
+
27250
27538
  // ../core/dist/src/services/skill-installation.service.js
27251
27539
  var DEFAULT_SKILLS_DIR2 = path6.join(os3.homedir(), ".claude", "skills");
27252
27540
  var DEFAULT_MANIFEST_PATH2 = path6.join(os3.homedir(), ".skillsmith", "manifest.json");
@@ -29166,18 +29454,18 @@ var SUGGESTION_COOLDOWN_MS = 5 * 60 * 1e3;
29166
29454
  var MS_PER_DAY = 24 * 60 * 60 * 1e3;
29167
29455
 
29168
29456
  // ../core/dist/src/analytics/storage.js
29169
- import { join as join26, dirname as dirname13 } from "path";
29457
+ import { join as join27, dirname as dirname14 } from "path";
29170
29458
  import { homedir as homedir14 } from "os";
29171
- var ANALYTICS_DIR = join26(homedir14(), ".skillsmith");
29172
- var ANALYTICS_DB = join26(ANALYTICS_DIR, "analytics.db");
29459
+ var ANALYTICS_DIR = join27(homedir14(), ".skillsmith");
29460
+ var ANALYTICS_DB = join27(ANALYTICS_DIR, "analytics.db");
29173
29461
 
29174
29462
  // ../core/dist/src/analytics/usage-tracker.js
29175
29463
  var SESSION_TIMEOUT_MS = 60 * 60 * 1e3;
29176
29464
 
29177
29465
  // ../core/dist/src/analytics/metrics-exporter.js
29178
- import { join as join27, resolve as resolve8, isAbsolute as isAbsolute3 } from "path";
29466
+ import { join as join28, resolve as resolve8, isAbsolute as isAbsolute3 } from "path";
29179
29467
  import { homedir as homedir15 } from "os";
29180
- var DEFAULT_EXPORT_DIR = join27(homedir15(), ".skillsmith", "exports");
29468
+ var DEFAULT_EXPORT_DIR = join28(homedir15(), ".skillsmith", "exports");
29181
29469
 
29182
29470
  // ../core/dist/src/repositories/SkillVersionRepository.js
29183
29471
  var SkillVersionRepository = class {
@@ -31741,7 +32029,7 @@ var SourceRecoveryService = class {
31741
32029
  };
31742
32030
 
31743
32031
  // ../core/dist/src/provenance/backfill.js
31744
- import { existsSync as existsSync18 } from "fs";
32032
+ import { existsSync as existsSync19 } from "fs";
31745
32033
  import * as fs11 from "fs/promises";
31746
32034
  import * as os5 from "os";
31747
32035
  import * as path12 from "path";
@@ -31840,7 +32128,7 @@ function mergeEntry(existing, planned) {
31840
32128
  };
31841
32129
  }
31842
32130
  async function maybeWriteFrontmatter(dir, sourceUrl) {
31843
- if (existsSync18(path12.join(dir, ".git", "config")))
32131
+ if (existsSync19(path12.join(dir, ".git", "config")))
31844
32132
  return false;
31845
32133
  const skillMdPath = path12.join(dir, "SKILL.md");
31846
32134
  let content;
@@ -32069,21 +32357,21 @@ async function probeEmbeddingCapability(opts = {}) {
32069
32357
  }
32070
32358
 
32071
32359
  // src/version.ts
32072
- import { readFileSync as readFileSync16 } from "node:fs";
32073
- import { join as join35 } from "node:path";
32360
+ import { readFileSync as readFileSync17 } from "node:fs";
32361
+ import { join as join36 } from "node:path";
32074
32362
 
32075
32363
  // src/utils/package-root.ts
32076
- import { dirname as dirname15, join as join34 } from "node:path";
32364
+ import { dirname as dirname16, join as join35 } from "node:path";
32077
32365
  import { fileURLToPath } from "node:url";
32078
32366
  function packageRoot() {
32079
- return join34(dirname15(fileURLToPath(import.meta.url)), "..");
32367
+ return join35(dirname16(fileURLToPath(import.meta.url)), "..");
32080
32368
  }
32081
32369
 
32082
32370
  // src/version.ts
32083
32371
  function readVersion() {
32084
32372
  try {
32085
- const pkgPath = join35(packageRoot(), "package.json");
32086
- const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
32373
+ const pkgPath = join36(packageRoot(), "package.json");
32374
+ const pkg = JSON.parse(readFileSync17(pkgPath, "utf-8"));
32087
32375
  return pkg.version ?? "0.0.0";
32088
32376
  } catch {
32089
32377
  return "0.0.0";
@@ -32101,7 +32389,7 @@ function getCliLogger() {
32101
32389
  }
32102
32390
 
32103
32391
  // src/utils/open-database.ts
32104
- import { existsSync as existsSync19 } from "node:fs";
32392
+ import { existsSync as existsSync20 } from "node:fs";
32105
32393
  var logger8 = getCliLogger();
32106
32394
  async function openCliDatabase(path24, options) {
32107
32395
  if (options?.readonly) {
@@ -32113,7 +32401,7 @@ async function openCliDatabase(path24, options) {
32113
32401
  initializeSchema(db);
32114
32402
  return db;
32115
32403
  } catch (err) {
32116
- if (!isCorruptionError(err) || path24 === ":memory:" || !existsSync19(path24)) {
32404
+ if (!isCorruptionError(err) || path24 === ":memory:" || !existsSync20(path24)) {
32117
32405
  throw err;
32118
32406
  }
32119
32407
  if (db) {
@@ -32907,14 +33195,23 @@ import { confirm as confirm2 } from "@inquirer/prompts";
32907
33195
  import Table2 from "cli-table3";
32908
33196
  import ora4 from "ora";
32909
33197
  import { mkdir as mkdir5 } from "fs/promises";
32910
- import { dirname as dirname16 } from "path";
33198
+ import { dirname as dirname17 } from "path";
32911
33199
 
32912
33200
  // src/utils/skills-directory.ts
32913
33201
  import { readdir as readdir6, readFile as readFile6, realpath as realpath3, stat as stat6 } from "fs/promises";
32914
- import { createHash as createHash7 } from "crypto";
32915
- import { join as join36 } from "path";
33202
+ import { createHash as createHash8 } from "crypto";
33203
+ import { join as join37 } from "path";
32916
33204
  function getLocalSkillsDir() {
32917
- return join36(process.cwd(), ".claude", "skills");
33205
+ return join37(process.cwd(), ".claude", "skills");
33206
+ }
33207
+ async function resolvesToDirectory2(entryPath, isDirectory, isSymbolicLink) {
33208
+ if (isDirectory) return true;
33209
+ if (!isSymbolicLink) return false;
33210
+ try {
33211
+ return (await stat6(entryPath)).isDirectory();
33212
+ } catch {
33213
+ return false;
33214
+ }
32918
33215
  }
32919
33216
  async function getSkillsFromDirectory(skillsDir, dbPath, installedVia = CANONICAL_CLIENT) {
32920
33217
  const skills = [];
@@ -32932,10 +33229,15 @@ async function getSkillsFromDirectory(skillsDir, dbPath, installedVia = CANONICA
32932
33229
  try {
32933
33230
  const entries = await readdir6(skillsDir, { withFileTypes: true });
32934
33231
  for (const entry of entries) {
32935
- if (entry.isDirectory()) {
32936
- if (entry.name.startsWith(".")) continue;
32937
- const skillPath = join36(skillsDir, entry.name);
32938
- const skillMdPath = join36(skillPath, "SKILL.md");
33232
+ if (entry.name.startsWith(".")) continue;
33233
+ const skillPath = join37(skillsDir, entry.name);
33234
+ const isSkillDir = await resolvesToDirectory2(
33235
+ skillPath,
33236
+ entry.isDirectory(),
33237
+ entry.isSymbolicLink?.() ?? false
33238
+ );
33239
+ if (isSkillDir) {
33240
+ const skillMdPath = join37(skillPath, "SKILL.md");
32939
33241
  try {
32940
33242
  const skillMdStat = await stat6(skillMdPath);
32941
33243
  const content = await readFile6(skillMdPath, "utf-8");
@@ -32948,7 +33250,7 @@ async function getSkillsFromDirectory(skillsDir, dbPath, installedVia = CANONICA
32948
33250
  const skillId = parsedAny["id"] ?? entry.name;
32949
33251
  const latestVersion = await versionRepo.getLatestVersion(skillId);
32950
33252
  if (latestVersion) {
32951
- const currentHash = createHash7("sha256").update(content, "utf8").digest("hex");
33253
+ const currentHash = createHash8("sha256").update(content, "utf8").digest("hex");
32952
33254
  const storedHash = parsedAny["contentHash"] ?? parsedAny["originalContentHash"] ?? "";
32953
33255
  hasUpdates = storedHash !== "" && latestVersion.content_hash !== storedHash;
32954
33256
  if (!storedHash) {
@@ -33004,8 +33306,8 @@ async function safeRealpath2(p) {
33004
33306
  }
33005
33307
  async function readSkillMd(skillPath) {
33006
33308
  try {
33007
- const content = await readFile6(join36(skillPath, "SKILL.md"), "utf-8");
33008
- const contentHash = createHash7("sha256").update(content, "utf8").digest("hex");
33309
+ const content = await readFile6(join37(skillPath, "SKILL.md"), "utf-8");
33310
+ const contentHash = createHash8("sha256").update(content, "utf8").digest("hex");
33009
33311
  const parser2 = new SkillParser();
33010
33312
  const parsed = parser2.parse(content);
33011
33313
  const parsedAny = parsed;
@@ -33037,19 +33339,20 @@ async function getInstalledSkillsPerHarness() {
33037
33339
  const list = clientSkillsLists[i];
33038
33340
  if (list) ordered.push(...list);
33039
33341
  }
33040
- const seenPaths = /* @__PURE__ */ new Set();
33342
+ const fieldsCache = /* @__PURE__ */ new Map();
33343
+ const emitted = /* @__PURE__ */ new Set();
33041
33344
  const out = [];
33042
33345
  for (const skill of ordered) {
33043
33346
  const rp = await safeRealpath2(skill.path);
33044
- if (seenPaths.has(rp)) continue;
33045
- seenPaths.add(rp);
33046
- const {
33047
- contentHash,
33048
- skillId: parsedId,
33049
- author,
33050
- license,
33051
- repository
33052
- } = await readSkillMd(skill.path);
33347
+ const emittedKey = `${skill.installedVia}:${rp}`;
33348
+ if (emitted.has(emittedKey)) continue;
33349
+ emitted.add(emittedKey);
33350
+ let fields = fieldsCache.get(rp);
33351
+ if (!fields) {
33352
+ fields = await readSkillMd(skill.path);
33353
+ fieldsCache.set(rp, fields);
33354
+ }
33355
+ const { contentHash, skillId: parsedId, author, license, repository } = fields;
33053
33356
  out.push({
33054
33357
  harness: skill.installedVia,
33055
33358
  skillId: parsedId ?? skill.name,
@@ -33098,10 +33401,10 @@ async function getInstalledSkills(dbPath) {
33098
33401
  import { confirm } from "@inquirer/prompts";
33099
33402
  import ora3 from "ora";
33100
33403
  import { readFile as readFile7 } from "fs/promises";
33101
- import { join as join37 } from "path";
33404
+ import { join as join38 } from "path";
33102
33405
  async function resolveInstalledSkillId(installed) {
33103
33406
  try {
33104
- const content = await readFile7(join37(installed.path, "SKILL.md"), "utf-8");
33407
+ const content = await readFile7(join38(installed.path, "SKILL.md"), "utf-8");
33105
33408
  const parsed = new SkillParser().parse(content);
33106
33409
  const id = parsed?.["id"];
33107
33410
  return typeof id === "string" && id.includes("/") ? id : null;
@@ -33341,7 +33644,7 @@ Skill to remove:`));
33341
33644
  }
33342
33645
  }
33343
33646
  const spinner = ora4(`Removing ${skillName}...`).start();
33344
- await mkdir5(dirname16(dbPath), { recursive: true });
33647
+ await mkdir5(dirname17(dbPath), { recursive: true });
33345
33648
  const db = await openCliDatabase(dbPath);
33346
33649
  try {
33347
33650
  const skillRepo = new SkillRepository(db);
@@ -33495,8 +33798,8 @@ var InitSkillError = class _InitSkillError extends Error {
33495
33798
  import { input as input2, confirm as confirm3, select as select2 } from "@inquirer/prompts";
33496
33799
  import ora5 from "ora";
33497
33800
  import { mkdir as mkdir8, writeFile as writeFile5, readFile as readFile8, stat as stat7, readdir as readdir7 } from "fs/promises";
33498
- import { dirname as dirname17, join as join40, resolve as resolve10 } from "path";
33499
- import { createHash as createHash8 } from "crypto";
33801
+ import { dirname as dirname18, join as join41, resolve as resolve10 } from "path";
33802
+ import { createHash as createHash9 } from "crypto";
33500
33803
 
33501
33804
  // src/utils/skill-name.ts
33502
33805
  var VALID_SKILL_NAME_RE = /^[a-z][a-z0-9-]*$/;
@@ -33511,7 +33814,7 @@ function validateSkillName(name) {
33511
33814
  // src/commands/author/utils.ts
33512
33815
  import { access as access3 } from "fs/promises";
33513
33816
  import { mkdir as mkdir6 } from "fs/promises";
33514
- import { join as join38, resolve as resolve9 } from "path";
33817
+ import { join as join39, resolve as resolve9 } from "path";
33515
33818
  import { homedir as homedir19 } from "os";
33516
33819
  function printValidationResult(result, filePath) {
33517
33820
  console.log(source_default.bold(`
@@ -33545,7 +33848,7 @@ async function fileExists(path24) {
33545
33848
  }
33546
33849
  }
33547
33850
  async function ensureAgentsDirectory(customPath) {
33548
- const agentsDir = customPath ? resolve9(customPath.replace(/^~/, homedir19())) : join38(homedir19(), ".claude", "agents");
33851
+ const agentsDir = customPath ? resolve9(customPath.replace(/^~/, homedir19())) : join39(homedir19(), ".claude", "agents");
33549
33852
  await mkdir6(agentsDir, { recursive: true });
33550
33853
  return agentsDir;
33551
33854
  }
@@ -33600,7 +33903,7 @@ function validateSubagentDefinition(content) {
33600
33903
 
33601
33904
  // src/commands/author/init.helpers.ts
33602
33905
  import { mkdir as mkdir7, writeFile as writeFile4, rm as rm4 } from "fs/promises";
33603
- import { join as join39 } from "path";
33906
+ import { join as join40 } from "path";
33604
33907
 
33605
33908
  // src/templates/skill.md.template.ts
33606
33909
  var SKILL_MD_TEMPLATE = `---
@@ -34097,6 +34400,21 @@ SKILLSMITH_API_KEY = "sk_live_..."`,
34097
34400
  env:
34098
34401
  SKILLSMITH_API_KEY: "sk_live_..."`,
34099
34402
  notes: "Hermes config is YAML. Hermes has no SessionStart hook equivalent \u2014 nudge/attribution is unsupported on this harness."
34403
+ },
34404
+ // SMI-5697: grok added to ClientId (paths.ts); this Record<SnippetClientId,
34405
+ // ClientSnippet> is exhaustive over ClientId, so this entry is required for
34406
+ // the type to compile.
34407
+ grok: {
34408
+ label: "Grok Build (xAI)",
34409
+ configPath: "~/.grok/config.toml",
34410
+ format: "toml",
34411
+ body: `[mcp_servers.{{name}}]
34412
+ command = "npx"
34413
+ args = ["-y", "{{name}}"]
34414
+
34415
+ [mcp_servers.{{name}}.env]
34416
+ SKILLSMITH_API_KEY = "sk_live_..."`,
34417
+ notes: "Grok Build uses TOML under the [mcp_servers.<name>] table, mirroring the Codex CLI convention above \u2014 confirmed against docs.x.ai for the command/args shape; the env sub-table follows the same pattern as the Codex entry."
34100
34418
  }
34101
34419
  };
34102
34420
  function renderSnippet(client, packageName) {
@@ -34131,7 +34449,8 @@ var SNIPPET_DISPLAY_ORDER = Object.freeze([
34131
34449
  "codex",
34132
34450
  "agents",
34133
34451
  "opencode",
34134
- "hermes"
34452
+ "hermes",
34453
+ "grok"
34135
34454
  ]);
34136
34455
 
34137
34456
  // src/templates/mcp-server.template.ts
@@ -34477,15 +34796,15 @@ function renderMcpServerTemplates(data) {
34477
34796
  async function scaffoldSkillDirectory(input7) {
34478
34797
  const { skillDir, skillName, description, author, category, createdFresh } = input7;
34479
34798
  try {
34480
- await mkdir7(join39(skillDir, "scripts"), { recursive: true });
34481
- await mkdir7(join39(skillDir, "resources"), { recursive: true });
34799
+ await mkdir7(join40(skillDir, "scripts"), { recursive: true });
34800
+ await mkdir7(join40(skillDir, "resources"), { recursive: true });
34482
34801
  const skillMdContent = SKILL_MD_TEMPLATE.replace(/\{\{name\}\}/g, skillName).replace(/\{\{description\}\}/g, description).replace(/\{\{author\}\}/g, author).replace(/\{\{category\}\}/g, category).replace(/\{\{date\}\}/g, (/* @__PURE__ */ new Date()).toISOString().split("T")[0] || "").replace(/\{\{behavioralClassification\}\}/g, "");
34483
- await writeFile4(join39(skillDir, "SKILL.md"), skillMdContent, "utf-8");
34802
+ await writeFile4(join40(skillDir, "SKILL.md"), skillMdContent, "utf-8");
34484
34803
  const readmeContent = README_MD_TEMPLATE.replace(/\{\{name\}\}/g, skillName).replace(
34485
34804
  /\{\{description\}\}/g,
34486
34805
  description
34487
34806
  );
34488
- await writeFile4(join39(skillDir, "README.md"), readmeContent, "utf-8");
34807
+ await writeFile4(join40(skillDir, "README.md"), readmeContent, "utf-8");
34489
34808
  const placeholderScript = `#!/usr/bin/env node
34490
34809
  /**
34491
34810
  * ${skillName} - Example Script
@@ -34495,7 +34814,7 @@ async function scaffoldSkillDirectory(input7) {
34495
34814
 
34496
34815
  console.log('${skillName} script executed');
34497
34816
  `;
34498
- await writeFile4(join39(skillDir, "scripts", "example.js"), placeholderScript, "utf-8");
34817
+ await writeFile4(join40(skillDir, "scripts", "example.js"), placeholderScript, "utf-8");
34499
34818
  const gitignore = `# Dependencies
34500
34819
  node_modules/
34501
34820
 
@@ -34510,7 +34829,7 @@ dist/
34510
34829
  .DS_Store
34511
34830
  Thumbs.db
34512
34831
  `;
34513
- await writeFile4(join39(skillDir, ".gitignore"), gitignore, "utf-8");
34832
+ await writeFile4(join40(skillDir, ".gitignore"), gitignore, "utf-8");
34514
34833
  return { ok: true };
34515
34834
  } catch (error46) {
34516
34835
  await rollbackPartialScaffold(skillDir, createdFresh);
@@ -34626,11 +34945,11 @@ async function validateSkill(skillPath) {
34626
34945
  try {
34627
34946
  const stats = await stat7(filePath);
34628
34947
  if (stats.isDirectory()) {
34629
- filePath = join40(filePath, "SKILL.md");
34948
+ filePath = join41(filePath, "SKILL.md");
34630
34949
  }
34631
34950
  } catch {
34632
34951
  if (!filePath.endsWith(".md")) {
34633
- filePath = join40(filePath, "SKILL.md");
34952
+ filePath = join41(filePath, "SKILL.md");
34634
34953
  }
34635
34954
  }
34636
34955
  const content = await readFile8(filePath, "utf-8");
@@ -34671,13 +34990,13 @@ async function publishSkill(skillPath, options = {}) {
34671
34990
  try {
34672
34991
  const stats = await stat7(dirPath);
34673
34992
  if (!stats.isDirectory()) {
34674
- dirPath = dirname17(dirPath);
34993
+ dirPath = dirname18(dirPath);
34675
34994
  }
34676
34995
  } catch {
34677
34996
  spinner.fail(`Directory not found: ${dirPath}`);
34678
34997
  return false;
34679
34998
  }
34680
- const skillMdPath = join40(dirPath, "SKILL.md");
34999
+ const skillMdPath = join41(dirPath, "SKILL.md");
34681
35000
  spinner.text = "Validating skill...";
34682
35001
  const content = await readFile8(skillMdPath, "utf-8");
34683
35002
  const parser2 = new SkillParser({ requireName: true });
@@ -34692,7 +35011,7 @@ async function publishSkill(skillPath, options = {}) {
34692
35011
  return false;
34693
35012
  }
34694
35013
  spinner.text = "Generating checksum...";
34695
- const checksum = createHash8("sha256").update(content).digest("hex");
35014
+ const checksum = createHash9("sha256").update(content).digest("hex");
34696
35015
  const publishInfo = {
34697
35016
  name: metadata.name,
34698
35017
  version: metadata.version || "1.0.0",
@@ -34723,7 +35042,7 @@ async function publishSkill(skillPath, options = {}) {
34723
35042
  }).filter((p) => p !== null);
34724
35043
  let totalWarnings = 0;
34725
35044
  for (const mdFile of mdFiles) {
34726
- const filePath = join40(dirPath, mdFile);
35045
+ const filePath = join41(dirPath, mdFile);
34727
35046
  const fileContent = await readFile8(filePath, "utf-8");
34728
35047
  const result = SkillParser.checkReferences(fileContent, customPatterns);
34729
35048
  if (result.matches.length > 0) {
@@ -34752,7 +35071,7 @@ async function publishSkill(skillPath, options = {}) {
34752
35071
  spinner.start();
34753
35072
  }
34754
35073
  }
34755
- const manifestPath = join40(dirPath, ".skillsmith-publish.json");
35074
+ const manifestPath = join41(dirPath, ".skillsmith-publish.json");
34756
35075
  await writeFile5(manifestPath, JSON.stringify(publishInfo, null, 2), "utf-8");
34757
35076
  spinner.succeed("Skill prepared for publishing");
34758
35077
  console.log(source_default.bold("\nPublish Information:"));
@@ -34859,7 +35178,7 @@ function createPublishCommand() {
34859
35178
  import { Command as Command5 } from "commander";
34860
35179
  import ora6 from "ora";
34861
35180
  import { readFile as readFile9, writeFile as writeFile6, stat as stat8 } from "fs/promises";
34862
- import { basename as basename5, dirname as dirname18, join as join41, resolve as resolve11 } from "path";
35181
+ import { basename as basename5, dirname as dirname19, join as join42, resolve as resolve11 } from "path";
34863
35182
 
34864
35183
  // src/utils/tool-analyzer.ts
34865
35184
  var TOOL_PATTERNS3 = {
@@ -34988,13 +35307,13 @@ async function generateSubagent2(skillPath, options) {
34988
35307
  try {
34989
35308
  const stats = await stat8(dirPath);
34990
35309
  if (stats.isDirectory()) {
34991
- skillMdPath = join41(dirPath, "SKILL.md");
35310
+ skillMdPath = join42(dirPath, "SKILL.md");
34992
35311
  } else {
34993
35312
  skillMdPath = dirPath;
34994
- dirPath = dirname18(dirPath);
35313
+ dirPath = dirname19(dirPath);
34995
35314
  }
34996
35315
  } catch {
34997
- skillMdPath = dirPath.endsWith(".md") ? dirPath : join41(dirPath, "SKILL.md");
35316
+ skillMdPath = dirPath.endsWith(".md") ? dirPath : join42(dirPath, "SKILL.md");
34998
35317
  }
34999
35318
  spinner.text = "Reading SKILL.md...";
35000
35319
  const content = await readFile9(skillMdPath, "utf-8");
@@ -35041,7 +35360,7 @@ async function generateSubagent2(skillPath, options) {
35041
35360
  return;
35042
35361
  }
35043
35362
  const agentsDir = await ensureAgentsDirectory(options.output);
35044
- const subagentPath = join41(agentsDir, `${basename5(metadata.name)}-specialist.md`);
35363
+ const subagentPath = join42(agentsDir, `${basename5(metadata.name)}-specialist.md`);
35045
35364
  if (await fileExists(subagentPath)) {
35046
35365
  if (!options.force) {
35047
35366
  spinner.warn(`Subagent already exists: ${subagentPath}`);
@@ -35108,7 +35427,7 @@ function createSubagentCommand() {
35108
35427
  import { Command as Command6 } from "commander";
35109
35428
  import ora7 from "ora";
35110
35429
  import { readFile as readFile10, readdir as readdir8 } from "fs/promises";
35111
- import { join as join42, resolve as resolve12 } from "path";
35430
+ import { join as join43, resolve as resolve12 } from "path";
35112
35431
  import { homedir as homedir20 } from "os";
35113
35432
  var logger15 = getCliLogger();
35114
35433
  async function transformSkill2(skillPath, options) {
@@ -35124,9 +35443,9 @@ async function transformSkill2(skillPath, options) {
35124
35443
  const subdirs = await readdir8(dirPath, { withFileTypes: true });
35125
35444
  for (const entry of subdirs) {
35126
35445
  if (entry.isDirectory()) {
35127
- const skillMdPath2 = join42(dirPath, entry.name, "SKILL.md");
35446
+ const skillMdPath2 = join43(dirPath, entry.name, "SKILL.md");
35128
35447
  if (await fileExists(skillMdPath2)) {
35129
- skillDirs.push(join42(dirPath, entry.name));
35448
+ skillDirs.push(join43(dirPath, entry.name));
35130
35449
  }
35131
35450
  }
35132
35451
  }
@@ -35148,7 +35467,7 @@ Processing: ${skillDir}`));
35148
35467
  }
35149
35468
  return;
35150
35469
  }
35151
- const skillMdPath = join42(dirPath, "SKILL.md");
35470
+ const skillMdPath = join43(dirPath, "SKILL.md");
35152
35471
  if (!await fileExists(skillMdPath)) {
35153
35472
  spinner.fail(`No SKILL.md found at: ${skillMdPath}`);
35154
35473
  throw new Error(`No SKILL.md found at: ${skillMdPath}`);
@@ -35162,8 +35481,8 @@ Processing: ${skillDir}`));
35162
35481
  printValidationResult(validation, skillMdPath);
35163
35482
  return;
35164
35483
  }
35165
- const agentsDir = join42(homedir20(), ".claude", "agents");
35166
- const subagentPath = join42(agentsDir, `${metadata.name}-specialist.md`);
35484
+ const agentsDir = join43(homedir20(), ".claude", "agents");
35485
+ const subagentPath = join43(agentsDir, `${metadata.name}-specialist.md`);
35167
35486
  if (await fileExists(subagentPath)) {
35168
35487
  if (!options.force) {
35169
35488
  spinner.warn(`Subagent already exists: ${subagentPath}`);
@@ -35221,7 +35540,7 @@ import { Command as Command7 } from "commander";
35221
35540
  import { input as input3, confirm as confirm4 } from "@inquirer/prompts";
35222
35541
  import ora8 from "ora";
35223
35542
  import { mkdir as mkdir9, writeFile as writeFile7, stat as stat9 } from "fs/promises";
35224
- import { dirname as dirname19, join as join43, resolve as resolve13 } from "path";
35543
+ import { dirname as dirname20, join as join44, resolve as resolve13 } from "path";
35225
35544
  var logger16 = getCliLogger();
35226
35545
  async function initMcpServer(name, options) {
35227
35546
  const serverName = name || await input3({
@@ -35334,11 +35653,11 @@ async function initMcpServer(name, options) {
35334
35653
  author
35335
35654
  });
35336
35655
  await mkdir9(targetDir, { recursive: true });
35337
- await mkdir9(join43(targetDir, "src"), { recursive: true });
35338
- await mkdir9(join43(targetDir, "src", "tools"), { recursive: true });
35656
+ await mkdir9(join44(targetDir, "src"), { recursive: true });
35657
+ await mkdir9(join44(targetDir, "src", "tools"), { recursive: true });
35339
35658
  for (const [filePath, content] of files) {
35340
- const fullPath = join43(targetDir, filePath);
35341
- const dir = dirname19(fullPath);
35659
+ const fullPath = join44(targetDir, filePath);
35660
+ const dir = dirname20(fullPath);
35342
35661
  await mkdir9(dir, { recursive: true });
35343
35662
  await writeFile7(fullPath, content, "utf-8");
35344
35663
  }
@@ -35357,7 +35676,7 @@ async function initMcpServer(name, options) {
35357
35676
  "mcpServers": {
35358
35677
  "${serverName}": {
35359
35678
  "command": "npx",
35360
- "args": ["tsx", "${join43(targetDir, "src", "index.ts")}"]
35679
+ "args": ["tsx", "${join44(targetDir, "src", "index.ts")}"]
35361
35680
  }
35362
35681
  }
35363
35682
  }`)
@@ -35538,8 +35857,8 @@ import { Command as Command9 } from "commander";
35538
35857
  import ora9 from "ora";
35539
35858
 
35540
35859
  // src/commands/recommend.helpers.ts
35541
- import { existsSync as existsSync20, readdirSync as readdirSync2, readFileSync as readFileSync17, statSync as statSync4 } from "node:fs";
35542
- import { join as join44 } from "node:path";
35860
+ import { existsSync as existsSync21, readdirSync as readdirSync2, readFileSync as readFileSync18, statSync as statSync5 } from "node:fs";
35861
+ import { join as join45 } from "node:path";
35543
35862
 
35544
35863
  // src/commands/recommend.types.ts
35545
35864
  var VALID_TRUST_TIERS = [
@@ -35851,15 +36170,15 @@ function buildStackFromAnalysis(context) {
35851
36170
  }
35852
36171
  function getInstalledSkills2() {
35853
36172
  const skillsDir = getCanonicalInstallPath();
35854
- if (!existsSync20(skillsDir)) {
36173
+ if (!existsSync21(skillsDir)) {
35855
36174
  return [];
35856
36175
  }
35857
36176
  const installedSkills = [];
35858
36177
  try {
35859
36178
  const entries = readdirSync2(skillsDir);
35860
36179
  for (const entry of entries) {
35861
- const skillPath = join44(skillsDir, entry);
35862
- const stat13 = statSync4(skillPath);
36180
+ const skillPath = join45(skillsDir, entry);
36181
+ const stat13 = statSync5(skillPath);
35863
36182
  if (!stat13.isDirectory()) continue;
35864
36183
  const skill = {
35865
36184
  name: entry.toLowerCase(),
@@ -35867,10 +36186,10 @@ function getInstalledSkills2() {
35867
36186
  tags: [],
35868
36187
  category: null
35869
36188
  };
35870
- const skillMdPath = join44(skillPath, "SKILL.md");
35871
- if (existsSync20(skillMdPath)) {
36189
+ const skillMdPath = join45(skillPath, "SKILL.md");
36190
+ if (existsSync21(skillMdPath)) {
35872
36191
  try {
35873
- const content = readFileSync17(skillMdPath, "utf-8");
36192
+ const content = readFileSync18(skillMdPath, "utf-8");
35874
36193
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
35875
36194
  const frontmatter = frontmatterMatch?.[1];
35876
36195
  if (frontmatter) {
@@ -36483,7 +36802,7 @@ function createSyncCommand() {
36483
36802
  // src/commands/merge.ts
36484
36803
  import { Command as Command11 } from "commander";
36485
36804
  import { resolve as resolve14 } from "path";
36486
- import { existsSync as existsSync21 } from "fs";
36805
+ import { existsSync as existsSync22 } from "fs";
36487
36806
  var logger20 = getCliLogger();
36488
36807
  function formatMergeResult(result) {
36489
36808
  const lines = [
@@ -36516,11 +36835,11 @@ async function mergeActionImpl(sourcePath, targetPath, options) {
36516
36835
  }
36517
36836
  const resolvedSource = resolve14(sourcePath);
36518
36837
  const resolvedTarget = targetPath ? resolve14(targetPath) : getDefaultDbPath();
36519
- if (!existsSync21(resolvedSource)) {
36838
+ if (!existsSync22(resolvedSource)) {
36520
36839
  logger20.error(`Source database not found: ${resolvedSource}`);
36521
36840
  process.exit(1);
36522
36841
  }
36523
- if (!existsSync21(resolvedTarget)) {
36842
+ if (!existsSync22(resolvedTarget)) {
36524
36843
  logger20.error(`Target database not found: ${resolvedTarget}`);
36525
36844
  logger20.error("Create a new database first with: skillsmith init");
36526
36845
  process.exit(1);
@@ -36607,13 +36926,13 @@ var mergeAction = withTelemetry(mergeActionImpl, {
36607
36926
  import { Command as Command12 } from "commander";
36608
36927
  import ora11 from "ora";
36609
36928
  import { mkdir as mkdir10, copyFile as copyFile2, stat as stat10, readdir as readdir9 } from "fs/promises";
36610
- import { join as join45, dirname as dirname20 } from "path";
36929
+ import { join as join46, dirname as dirname21 } from "path";
36611
36930
  var logger21 = getCliLogger();
36612
36931
  function getAssetsPath() {
36613
- return join45(packageRoot(), "assets", "skillsmith-skill");
36932
+ return join46(packageRoot(), "assets", "skillsmith-skill");
36614
36933
  }
36615
36934
  function getTargetPath() {
36616
- return join45(getCanonicalInstallPath(), "skillsmith");
36935
+ return join46(getCanonicalInstallPath(), "skillsmith");
36617
36936
  }
36618
36937
  async function directoryExists(path24) {
36619
36938
  try {
@@ -36630,8 +36949,8 @@ async function copyDirectory(src, dest) {
36630
36949
  if (entry.isSymbolicLink()) {
36631
36950
  continue;
36632
36951
  }
36633
- const srcPath = join45(src, entry.name);
36634
- const destPath = join45(dest, entry.name);
36952
+ const srcPath = join46(src, entry.name);
36953
+ const destPath = join46(dest, entry.name);
36635
36954
  if (entry.isDirectory()) {
36636
36955
  await mkdir10(destPath, { recursive: true });
36637
36956
  filesCopied += await copyDirectory(srcPath, destPath);
@@ -36659,7 +36978,7 @@ async function installSkillsmithSkill(force) {
36659
36978
  }
36660
36979
  const spinner = ora11("Installing skillsmith skill...").start();
36661
36980
  try {
36662
- await mkdir10(dirname20(targetPath), { recursive: true });
36981
+ await mkdir10(dirname21(targetPath), { recursive: true });
36663
36982
  await mkdir10(targetPath, { recursive: true });
36664
36983
  const filesCopied = await copyDirectory(assetsPath, targetPath);
36665
36984
  if (filesCopied === 0) {
@@ -37075,7 +37394,7 @@ function createWhoamiCommand() {
37075
37394
  // src/commands/diff.ts
37076
37395
  import { Command as Command16 } from "commander";
37077
37396
  import { readFile as readFile12 } from "fs/promises";
37078
- import { join as join47 } from "path";
37397
+ import { join as join48 } from "path";
37079
37398
 
37080
37399
  // src/utils/license-types.ts
37081
37400
  var TIER_FEATURES = {
@@ -37089,7 +37408,9 @@ var TIER_FEATURES = {
37089
37408
  "team_workspaces",
37090
37409
  "private_skills",
37091
37410
  "usage_analytics",
37092
- "priority_support"
37411
+ "priority_support",
37412
+ // SMI-3140: expanded to Team + Enterprise (2026-07-14)
37413
+ "compliance_reports"
37093
37414
  ],
37094
37415
  enterprise: [
37095
37416
  // Individual features (inherited)
@@ -37100,12 +37421,12 @@ var TIER_FEATURES = {
37100
37421
  "private_skills",
37101
37422
  "usage_analytics",
37102
37423
  "priority_support",
37424
+ "compliance_reports",
37103
37425
  // Enterprise-only features (canonical names from enterprise package)
37104
37426
  "sso_saml",
37105
37427
  "rbac",
37106
37428
  "audit_logging",
37107
37429
  "siem_export",
37108
- "compliance_reports",
37109
37430
  "private_registry",
37110
37431
  "custom_integrations",
37111
37432
  "advanced_analytics"
@@ -37119,7 +37440,7 @@ async function tryLoadEnterpriseValidator() {
37119
37440
  return enterpriseValidatorCache;
37120
37441
  }
37121
37442
  try {
37122
- const packageName = "@skillsmith/enterprise";
37443
+ const packageName = "@smith-horn/enterprise";
37123
37444
  const enterprise = await import(
37124
37445
  /* webpackIgnore: true */
37125
37446
  packageName
@@ -37210,12 +37531,12 @@ async function requireTier(minimumTier) {
37210
37531
  }
37211
37532
 
37212
37533
  // src/utils/manifest.ts
37213
- import { createHash as createHash9, randomUUID as randomUUID6 } from "crypto";
37534
+ import { createHash as createHash10, randomUUID as randomUUID6 } from "crypto";
37214
37535
  import { readFile as readFile11, writeFile as writeFile8, mkdir as mkdir11, rename as rename3 } from "fs/promises";
37215
- import { join as join46, dirname as dirname21 } from "path";
37536
+ import { join as join47, dirname as dirname22 } from "path";
37216
37537
  import { homedir as homedir21 } from "os";
37217
- var SKILLSMITH_DIR = join46(homedir21(), ".skillsmith");
37218
- var MANIFEST_PATH = join46(SKILLSMITH_DIR, "manifest.json");
37538
+ var SKILLSMITH_DIR = join47(homedir21(), ".skillsmith");
37539
+ var MANIFEST_PATH = join47(SKILLSMITH_DIR, "manifest.json");
37219
37540
  async function loadManifest2() {
37220
37541
  try {
37221
37542
  const content = await readFile11(MANIFEST_PATH, "utf-8");
@@ -37225,7 +37546,7 @@ async function loadManifest2() {
37225
37546
  }
37226
37547
  }
37227
37548
  async function saveManifest2(manifest) {
37228
- await mkdir11(dirname21(MANIFEST_PATH), { recursive: true });
37549
+ await mkdir11(dirname22(MANIFEST_PATH), { recursive: true });
37229
37550
  const tmpPath = `${MANIFEST_PATH}.tmp.${process.pid}`;
37230
37551
  await writeFile8(tmpPath, JSON.stringify(manifest, null, 2));
37231
37552
  await rename3(tmpPath, MANIFEST_PATH);
@@ -37239,7 +37560,7 @@ var ROTATION_DAYS = 365;
37239
37560
  var OVERLAP_DAYS = 7;
37240
37561
  var MS_PER_DAY2 = 864e5;
37241
37562
  function generateAnonymousId2() {
37242
- return createHash9("sha256").update(randomUUID6()).digest("hex");
37563
+ return createHash10("sha256").update(randomUUID6()).digest("hex");
37243
37564
  }
37244
37565
  function shouldRotateAnonymousId(manifest) {
37245
37566
  const createdAt = manifest.telemetry?.anonymousIdCreatedAt;
@@ -37325,7 +37646,7 @@ function diffSections(oldContent, newContent) {
37325
37646
  return { added, removed, modified };
37326
37647
  }
37327
37648
  async function readInstalledSkillContent(skillName) {
37328
- const skillPath = join47(getCanonicalInstallPath(), skillName, "SKILL.md");
37649
+ const skillPath = join48(getCanonicalInstallPath(), skillName, "SKILL.md");
37329
37650
  try {
37330
37651
  return await readFile12(skillPath, "utf-8");
37331
37652
  } catch {
@@ -37537,7 +37858,7 @@ import { Command as Command21 } from "commander";
37537
37858
  import * as crypto11 from "node:crypto";
37538
37859
  import * as fs33 from "node:fs";
37539
37860
  import { homedir as homedir29 } from "node:os";
37540
- import { join as join58 } from "node:path";
37861
+ import { join as join59 } from "node:path";
37541
37862
  import { Command as Command18 } from "commander";
37542
37863
  import { input as input4, select as select3 } from "@inquirer/prompts";
37543
37864
 
@@ -44129,11 +44450,11 @@ import * as os14 from "node:os";
44129
44450
 
44130
44451
  // ../core/dist/src/audit/exclusions.js
44131
44452
  import { promises as fs29 } from "node:fs";
44132
- import { join as join56 } from "node:path";
44453
+ import { join as join57 } from "node:path";
44133
44454
  var EXCLUSIONS_FILE = "audit-exclusions.json";
44134
44455
  var EMPTY_CONFIG = { version: 1, exclusions: [] };
44135
44456
  function getExclusionsPath(opts) {
44136
- return join56(opts?.configDir ?? getConfigDir(), EXCLUSIONS_FILE);
44457
+ return join57(opts?.configDir ?? getConfigDir(), EXCLUSIONS_FILE);
44137
44458
  }
44138
44459
  async function loadExclusions(opts = {}) {
44139
44460
  const path24 = opts.configPath ?? getExclusionsPath();
@@ -44595,10 +44916,10 @@ async function requireConfirmationPhrase(expected, prompt) {
44595
44916
  }
44596
44917
  }
44597
44918
  function ledgerPath() {
44598
- return join58(homedir29(), ".skillsmith", "namespace-overrides.json");
44919
+ return join59(homedir29(), ".skillsmith", "namespace-overrides.json");
44599
44920
  }
44600
44921
  function backupsDir() {
44601
- return join58(homedir29(), ".skillsmith", "backups");
44922
+ return join59(homedir29(), ".skillsmith", "backups");
44602
44923
  }
44603
44924
  function backupLedgerForReset() {
44604
44925
  const src = ledgerPath();
@@ -44607,7 +44928,7 @@ function backupLedgerForReset() {
44607
44928
  fs33.mkdirSync(dir, { recursive: true, mode: 448 });
44608
44929
  const ts2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
44609
44930
  const suffix = crypto11.randomBytes(4).toString("hex");
44610
- const backupFile = join58(dir, `ledger-${ts2}-${suffix}.json`);
44931
+ const backupFile = join59(dir, `ledger-${ts2}-${suffix}.json`);
44611
44932
  fs33.copyFileSync(src, backupFile);
44612
44933
  return backupFile;
44613
44934
  }
@@ -45356,7 +45677,7 @@ import { Command as Command22 } from "commander";
45356
45677
  import { input as input6, confirm as confirm6, select as select4 } from "@inquirer/prompts";
45357
45678
  import ora12 from "ora";
45358
45679
  import { mkdir as mkdir17, writeFile as writeFile15, stat as stat12 } from "fs/promises";
45359
- import { join as join59 } from "path";
45680
+ import { join as join60 } from "path";
45360
45681
  var logger29 = getCliLogger();
45361
45682
  var VALID_TYPES = ["basic", "intermediate", "advanced"];
45362
45683
  var VALID_BEHAVIORS = ["autonomous", "guided", "interactive", "configurable"];
@@ -45492,7 +45813,7 @@ async function createSkill(name, options = {}) {
45492
45813
  default: false
45493
45814
  });
45494
45815
  const outputDir = options.output ?? getCanonicalInstallPath();
45495
- const skillDir = join59(outputDir, skillName);
45816
+ const skillDir = join60(outputDir, skillName);
45496
45817
  let exists = false;
45497
45818
  try {
45498
45819
  await stat12(skillDir);
@@ -45566,16 +45887,16 @@ Thumbs.db
45566
45887
  const spinner = ora12("Scaffolding skill...").start();
45567
45888
  try {
45568
45889
  await mkdir17(skillDir, { recursive: true });
45569
- await mkdir17(join59(skillDir, "resources"), { recursive: true });
45890
+ await mkdir17(join60(skillDir, "resources"), { recursive: true });
45570
45891
  if (includeScripts) {
45571
- await mkdir17(join59(skillDir, "scripts"), { recursive: true });
45892
+ await mkdir17(join60(skillDir, "scripts"), { recursive: true });
45572
45893
  }
45573
- await writeFile15(join59(skillDir, "SKILL.md"), skillMdContent, "utf-8");
45574
- await writeFile15(join59(skillDir, "README.md"), readmeContent, "utf-8");
45575
- await writeFile15(join59(skillDir, "CHANGELOG.md"), changelogContent, "utf-8");
45576
- await writeFile15(join59(skillDir, ".gitignore"), gitignoreContent, "utf-8");
45894
+ await writeFile15(join60(skillDir, "SKILL.md"), skillMdContent, "utf-8");
45895
+ await writeFile15(join60(skillDir, "README.md"), readmeContent, "utf-8");
45896
+ await writeFile15(join60(skillDir, "CHANGELOG.md"), changelogContent, "utf-8");
45897
+ await writeFile15(join60(skillDir, ".gitignore"), gitignoreContent, "utf-8");
45577
45898
  if (includeScripts) {
45578
- await writeFile15(join59(skillDir, "scripts", "example.js"), scriptContent, "utf-8");
45899
+ await writeFile15(join60(skillDir, "scripts", "example.js"), scriptContent, "utf-8");
45579
45900
  }
45580
45901
  spinner.succeed(`Skill scaffolded at ${skillDir}`);
45581
45902
  } catch (error46) {
@@ -45952,14 +46273,14 @@ import { resolve as resolve16 } from "node:path";
45952
46273
  import { promises as fs35 } from "node:fs";
45953
46274
 
45954
46275
  // src/commands/import-local.helpers.ts
45955
- import { createHash as createHash17 } from "node:crypto";
46276
+ import { createHash as createHash18 } from "node:crypto";
45956
46277
  import { promises as fs34 } from "node:fs";
45957
- import { join as join60, resolve as resolve15, dirname as dirname25, basename as basename8, sep as sep4, relative as relative6 } from "node:path";
46278
+ import { join as join61, resolve as resolve15, dirname as dirname26, basename as basename8, sep as sep4, relative as relative6 } from "node:path";
45958
46279
  import matter from "gray-matter";
45959
46280
  var SKILL_FILENAME = "SKILL.md";
45960
46281
  var MAX_DEPTH = 8;
45961
46282
  function localSkillId(canonicalPath) {
45962
- return createHash17("sha256").update(canonicalPath).digest("hex").slice(0, 32);
46283
+ return createHash18("sha256").update(canonicalPath).digest("hex").slice(0, 32);
45963
46284
  }
45964
46285
  async function walkSkillFiles(rootDir) {
45965
46286
  const canonicalRoot = await fs34.realpath(rootDir).catch(() => resolve15(rootDir));
@@ -45975,7 +46296,7 @@ async function walkSkillFiles(rootDir) {
45975
46296
  return;
45976
46297
  }
45977
46298
  for (const entry of entries) {
45978
- const entryPath = join60(dir, entry.name);
46299
+ const entryPath = join61(dir, entry.name);
45979
46300
  if (entry.isSymbolicLink()) {
45980
46301
  let realPath;
45981
46302
  try {
@@ -46016,7 +46337,7 @@ async function walkSkillFiles(rootDir) {
46016
46337
  }
46017
46338
  async function parseSkillFile(filePath) {
46018
46339
  const id = localSkillId(filePath);
46019
- const fallbackName = basename8(dirname25(filePath));
46340
+ const fallbackName = basename8(dirname26(filePath));
46020
46341
  let content;
46021
46342
  try {
46022
46343
  content = await fs34.readFile(filePath, "utf8");
@@ -46284,7 +46605,7 @@ function printHumanSummary(result) {
46284
46605
  import * as crypto12 from "node:crypto";
46285
46606
  import * as fs36 from "node:fs";
46286
46607
  import { homedir as homedir30 } from "node:os";
46287
- import { join as join61, dirname as dirname26 } from "node:path";
46608
+ import { join as join62, dirname as dirname27 } from "node:path";
46288
46609
  import { Command as Command26 } from "commander";
46289
46610
  var logger33 = getCliLogger();
46290
46611
  var CONFIG_DIR3 = ".skillsmith";
@@ -46308,7 +46629,7 @@ function isSupportedKey(key) {
46308
46629
  return SUPPORTED_KEYS.includes(key);
46309
46630
  }
46310
46631
  function configPath() {
46311
- return join61(homedir30(), CONFIG_DIR3, CONFIG_FILE3);
46632
+ return join62(homedir30(), CONFIG_DIR3, CONFIG_FILE3);
46312
46633
  }
46313
46634
  function readConfigFile2() {
46314
46635
  const path24 = configPath();
@@ -46324,7 +46645,7 @@ function readConfigFile2() {
46324
46645
  }
46325
46646
  function writeConfigFileAtomic(config2) {
46326
46647
  const path24 = configPath();
46327
- const dir = dirname26(path24);
46648
+ const dir = dirname27(path24);
46328
46649
  fs36.mkdirSync(dir, { recursive: true, mode: 448 });
46329
46650
  const tmpSuffix = crypto12.randomBytes(6).toString("hex");
46330
46651
  const tmpPath = `${path24}.${tmpSuffix}.tmp`;
@@ -46425,15 +46746,15 @@ function createConfigCommand2() {
46425
46746
  import { Command as Command27 } from "commander";
46426
46747
 
46427
46748
  // src/commands/telemetry.action.ts
46428
- import { existsSync as existsSync27, copyFileSync as copyFileSync2, chmodSync as chmodSync6, mkdirSync as mkdirSync13 } from "node:fs";
46749
+ import { existsSync as existsSync28, copyFileSync as copyFileSync2, chmodSync as chmodSync8, mkdirSync as mkdirSync13 } from "node:fs";
46429
46750
  import { homedir as homedir32 } from "node:os";
46430
- import { join as join63, dirname as dirname28 } from "node:path";
46431
- import { readdirSync as readdirSync4, unlinkSync as unlinkSync3, statSync as statSync6 } from "node:fs";
46751
+ import { join as join64, dirname as dirname29 } from "node:path";
46752
+ import { readdirSync as readdirSync4, unlinkSync as unlinkSync4, statSync as statSync7 } from "node:fs";
46432
46753
 
46433
46754
  // src/commands/telemetry.helpers.ts
46434
46755
  import * as crypto13 from "node:crypto";
46435
46756
  import * as fs37 from "node:fs";
46436
- import { join as join62, dirname as dirname27 } from "node:path";
46757
+ import { join as join63, dirname as dirname28 } from "node:path";
46437
46758
  import { homedir as homedir31 } from "node:os";
46438
46759
  var TelemetryHookError = class extends Error {
46439
46760
  constructor(code, message) {
@@ -46445,9 +46766,9 @@ var TelemetryHookError = class extends Error {
46445
46766
  };
46446
46767
  function resolveSettingsPath(scope) {
46447
46768
  if (scope === "user") {
46448
- return join62(homedir31(), ".claude", "settings.json");
46769
+ return join63(homedir31(), ".claude", "settings.json");
46449
46770
  }
46450
- return join62(process.cwd(), ".claude", "settings.json");
46771
+ return join63(process.cwd(), ".claude", "settings.json");
46451
46772
  }
46452
46773
  function loadClaudeSettings(scope) {
46453
46774
  const path24 = resolveSettingsPath(scope);
@@ -46520,7 +46841,7 @@ function removeSkillHookEntries(settings, hookPath) {
46520
46841
  }
46521
46842
  function writeClaudeSettings(scope, settings) {
46522
46843
  const path24 = resolveSettingsPath(scope);
46523
- const dir = dirname27(path24);
46844
+ const dir = dirname28(path24);
46524
46845
  fs37.mkdirSync(dir, { recursive: true, mode: 448 });
46525
46846
  const tmpSuffix = crypto13.randomBytes(6).toString("hex");
46526
46847
  const tmpPath = `${path24}.${tmpSuffix}.tmp`;
@@ -46539,10 +46860,10 @@ var PRIVACY_URL = "https://skillsmith.app/privacy#telemetry";
46539
46860
  var DEFAULT_ENDPOINT = "https://vrcnzpmndtroqxxoqkzy.supabase.co/functions/v1/events";
46540
46861
  var ORPHAN_TTL_MS = 60 * 60 * 1e3;
46541
46862
  function hookScriptPath() {
46542
- return join63(homedir32(), ".skillsmith", "hooks", "skill-telemetry.sh");
46863
+ return join64(homedir32(), ".skillsmith", "hooks", "skill-telemetry.sh");
46543
46864
  }
46544
46865
  function runDir() {
46545
- return join63(homedir32(), ".skillsmith", "run");
46866
+ return join64(homedir32(), ".skillsmith", "run");
46546
46867
  }
46547
46868
  function idTail(id) {
46548
46869
  if (!id) return "(none)";
@@ -46551,14 +46872,14 @@ function idTail(id) {
46551
46872
  function gcOrphanRunFiles() {
46552
46873
  try {
46553
46874
  const dir = runDir();
46554
- if (!existsSync27(dir)) return;
46875
+ if (!existsSync28(dir)) return;
46555
46876
  const now = Date.now();
46556
46877
  for (const f of readdirSync4(dir)) {
46557
46878
  if (!f.startsWith("skill-")) continue;
46558
- const fp = join63(dir, f);
46879
+ const fp = join64(dir, f);
46559
46880
  try {
46560
- const st = statSync6(fp);
46561
- if (now - st.mtimeMs > ORPHAN_TTL_MS) unlinkSync3(fp);
46881
+ const st = statSync7(fp);
46882
+ if (now - st.mtimeMs > ORPHAN_TTL_MS) unlinkSync4(fp);
46562
46883
  } catch {
46563
46884
  }
46564
46885
  }
@@ -46655,8 +46976,8 @@ async function runStatus() {
46655
46976
  }
46656
46977
  }
46657
46978
  async function runInstallHook(options) {
46658
- const templateSrc = join63(packageRoot(), "templates", "skill-telemetry.sh");
46659
- if (!existsSync27(templateSrc)) {
46979
+ const templateSrc = join64(packageRoot(), "templates", "skill-telemetry.sh");
46980
+ if (!existsSync28(templateSrc)) {
46660
46981
  throw new Error(
46661
46982
  "skill-telemetry.sh template not found. Ensure the CLI package is fully built: npm run build"
46662
46983
  );
@@ -46666,11 +46987,11 @@ async function runInstallHook(options) {
46666
46987
  const hookPath = hookScriptPath();
46667
46988
  const updated = addSkillHookEntries(settings, hookPath);
46668
46989
  const destPath = hookScriptPath();
46669
- const hooksDir = dirname28(destPath);
46990
+ const hooksDir = dirname29(destPath);
46670
46991
  mkdirSync13(hooksDir, { recursive: true, mode: 448 });
46671
46992
  copyFileSync2(templateSrc, destPath);
46672
46993
  try {
46673
- chmodSync6(destPath, 493);
46994
+ chmodSync8(destPath, 493);
46674
46995
  } catch {
46675
46996
  }
46676
46997
  writeClaudeSettings(scope, updated);
@@ -46699,7 +47020,7 @@ async function runUninstallHook(options) {
46699
47020
  writeClaudeSettings(scope, updated);
46700
47021
  try {
46701
47022
  const scriptPath = hookScriptPath();
46702
- if (existsSync27(scriptPath)) unlinkSync3(scriptPath);
47023
+ if (existsSync28(scriptPath)) unlinkSync4(scriptPath);
46703
47024
  } catch {
46704
47025
  }
46705
47026
  const scopeLabel = scope === "user" ? "~/.claude/settings.json" : "./.claude/settings.json";
@@ -47118,14 +47439,14 @@ function createAgentCommand() {
47118
47439
  }
47119
47440
 
47120
47441
  // src/commands/diagnose.ts
47121
- import { mkdirSync as mkdirSync14, readFileSync as readFileSync25, writeFileSync as writeFileSync15 } from "node:fs";
47122
- import { basename as basename9, dirname as dirname29, resolve as resolve17 } from "node:path";
47442
+ import { mkdirSync as mkdirSync14, readFileSync as readFileSync26, writeFileSync as writeFileSync15 } from "node:fs";
47443
+ import { basename as basename9, dirname as dirname30, resolve as resolve17 } from "node:path";
47123
47444
  import { Command as Command30 } from "commander";
47124
47445
 
47125
47446
  // src/commands/log-records.helpers.ts
47126
- import { existsSync as existsSync28, readFileSync as readFileSync24, readdirSync as readdirSync5, statSync as statSync7 } from "node:fs";
47447
+ import { existsSync as existsSync29, readFileSync as readFileSync25, readdirSync as readdirSync5, statSync as statSync8 } from "node:fs";
47127
47448
  import { homedir as homedir33 } from "node:os";
47128
- import { join as join64 } from "node:path";
47449
+ import { join as join65 } from "node:path";
47129
47450
  var LOG_FILE_PATTERN = /^skillsmith-[a-z]+-\d{4}-\d{2}-\d{2}\.jsonl(\.\d+)?$/;
47130
47451
  var LOG_LEVEL_ORDER = {
47131
47452
  debug: 0,
@@ -47138,12 +47459,12 @@ function isLogLevel(value) {
47138
47459
  return VALID_LEVELS.has(value);
47139
47460
  }
47140
47461
  function resolveLogDir() {
47141
- return process.env["SKILLSMITH_LOG_DIR"] || join64(homedir33(), ".skillsmith", "logs");
47462
+ return process.env["SKILLSMITH_LOG_DIR"] || join65(homedir33(), ".skillsmith", "logs");
47142
47463
  }
47143
47464
  function listLogFiles(dir) {
47144
- if (!existsSync28(dir)) return [];
47465
+ if (!existsSync29(dir)) return [];
47145
47466
  try {
47146
- return readdirSync5(dir).filter((name) => LOG_FILE_PATTERN.test(name)).sort().map((name) => join64(dir, name));
47467
+ return readdirSync5(dir).filter((name) => LOG_FILE_PATTERN.test(name)).sort().map((name) => join65(dir, name));
47147
47468
  } catch {
47148
47469
  return [];
47149
47470
  }
@@ -47151,7 +47472,7 @@ function listLogFiles(dir) {
47151
47472
  function readLogRecords(filePath) {
47152
47473
  let content;
47153
47474
  try {
47154
- content = readFileSync24(filePath, "utf8");
47475
+ content = readFileSync25(filePath, "utf8");
47155
47476
  } catch {
47156
47477
  return [];
47157
47478
  }
@@ -47192,7 +47513,7 @@ function formatRecordLine(record2) {
47192
47513
  }
47193
47514
  function fileSizeBytes(filePath) {
47194
47515
  try {
47195
- return statSync7(filePath).size;
47516
+ return statSync8(filePath).size;
47196
47517
  } catch {
47197
47518
  return 0;
47198
47519
  }
@@ -47257,7 +47578,7 @@ function buildBundleContent(summary, files) {
47257
47578
  parts.push("");
47258
47579
  parts.push(`===== ${basename9(file2)} (${fileSizeBytes(file2)} bytes) =====`);
47259
47580
  try {
47260
- parts.push(readFileSync25(file2, "utf8"));
47581
+ parts.push(readFileSync26(file2, "utf8"));
47261
47582
  } catch (error46) {
47262
47583
  parts.push(`[failed to read: ${sanitizeError(error46)}]`);
47263
47584
  }
@@ -47269,7 +47590,7 @@ function writeBundle(bundleOption, summary, files) {
47269
47590
  const rawPath = typeof bundleOption === "string" && bundleOption.length > 0 ? bundleOption : defaultBundlePath();
47270
47591
  const targetPath = resolve17(rawPath);
47271
47592
  const content = buildBundleContent(summary, files);
47272
- mkdirSync14(dirname29(targetPath), { recursive: true });
47593
+ mkdirSync14(dirname30(targetPath), { recursive: true });
47273
47594
  writeFileSync15(targetPath, content, "utf8");
47274
47595
  return targetPath;
47275
47596
  }
@@ -47328,8 +47649,8 @@ function createDiagnoseCommand() {
47328
47649
  }
47329
47650
 
47330
47651
  // src/commands/logs.ts
47331
- import { existsSync as existsSync29, readFileSync as readFileSync26, statSync as statSync8 } from "node:fs";
47332
- import { join as join65 } from "node:path";
47652
+ import { existsSync as existsSync30, readFileSync as readFileSync27, statSync as statSync9 } from "node:fs";
47653
+ import { join as join66 } from "node:path";
47333
47654
  import { Command as Command31 } from "commander";
47334
47655
  var logger38 = getCliLogger();
47335
47656
  var TAIL_SURFACES = ["cli", "mcp", "vscode"];
@@ -47338,7 +47659,7 @@ function todayDateString2() {
47338
47659
  }
47339
47660
  function todaysFilePaths(dir) {
47340
47661
  const date5 = todayDateString2();
47341
- return TAIL_SURFACES.map((surface) => join65(dir, `skillsmith-${surface}-${date5}.jsonl`));
47662
+ return TAIL_SURFACES.map((surface) => join66(dir, `skillsmith-${surface}-${date5}.jsonl`));
47342
47663
  }
47343
47664
  function resolveLevel(raw) {
47344
47665
  if (raw === void 0) return void 0;
@@ -47371,7 +47692,7 @@ async function startTail(dir, level, opts = {}) {
47371
47692
  const offsets = /* @__PURE__ */ new Map();
47372
47693
  let printedAny = false;
47373
47694
  for (const path24 of paths) {
47374
- if (!existsSync29(path24)) {
47695
+ if (!existsSync30(path24)) {
47375
47696
  offsets.set(path24, 0);
47376
47697
  continue;
47377
47698
  }
@@ -47381,7 +47702,7 @@ async function startTail(dir, level, opts = {}) {
47381
47702
  printRecords(sortByTsAsc(records));
47382
47703
  printedAny = true;
47383
47704
  }
47384
- offsets.set(path24, statSync8(path24).size);
47705
+ offsets.set(path24, statSync9(path24).size);
47385
47706
  }
47386
47707
  if (!printedAny) {
47387
47708
  console.log(noLogsFoundMessage(dir));
@@ -47399,7 +47720,7 @@ async function startTail(dir, level, opts = {}) {
47399
47720
  const handleEvent = (path24) => {
47400
47721
  let size;
47401
47722
  try {
47402
- size = statSync8(path24).size;
47723
+ size = statSync9(path24).size;
47403
47724
  } catch {
47404
47725
  return;
47405
47726
  }
@@ -47410,7 +47731,7 @@ async function startTail(dir, level, opts = {}) {
47410
47731
  }
47411
47732
  let content;
47412
47733
  try {
47413
- content = readFileSync26(path24).subarray(previousOffset).toString("utf8");
47734
+ content = readFileSync27(path24).subarray(previousOffset).toString("utf8");
47414
47735
  } catch {
47415
47736
  return;
47416
47737
  }
@@ -47480,12 +47801,12 @@ function shouldShowStartupHeader(commandPath, isTTY) {
47480
47801
  }
47481
47802
 
47482
47803
  // src/utils/node-version.ts
47483
- import { readFileSync as readFileSync27 } from "fs";
47484
- import { join as join66 } from "path";
47804
+ import { readFileSync as readFileSync28 } from "fs";
47805
+ import { join as join67 } from "path";
47485
47806
  function loadMinNodeVersion() {
47486
47807
  try {
47487
- const packageJsonPath2 = join66(packageRoot(), "package.json");
47488
- const packageJson2 = JSON.parse(readFileSync27(packageJsonPath2, "utf-8"));
47808
+ const packageJsonPath2 = join67(packageRoot(), "package.json");
47809
+ const packageJson2 = JSON.parse(readFileSync28(packageJsonPath2, "utf-8"));
47489
47810
  const engineConstraint = packageJson2.engines?.node ?? ">=22.22.0";
47490
47811
  return engineConstraint.replace(/[>=<^~\s]/g, "");
47491
47812
  } catch {
@@ -47556,16 +47877,16 @@ function checkNodeVersion() {
47556
47877
  }
47557
47878
 
47558
47879
  // src/index.ts
47559
- import { readFileSync as readFileSync28 } from "fs";
47560
- import { join as join67 } from "path";
47880
+ import { readFileSync as readFileSync29 } from "fs";
47881
+ import { join as join68 } from "path";
47561
47882
  var logger39 = getCliLogger();
47562
47883
  var versionError = checkNodeVersion();
47563
47884
  if (versionError) {
47564
47885
  logger39.error(versionError);
47565
47886
  process.exit(1);
47566
47887
  }
47567
- var packageJsonPath = join67(packageRoot(), "package.json");
47568
- var packageJson = JSON.parse(readFileSync28(packageJsonPath, "utf-8"));
47888
+ var packageJsonPath = join68(packageRoot(), "package.json");
47889
+ var packageJson = JSON.parse(readFileSync29(packageJsonPath, "utf-8"));
47569
47890
  var CLI_VERSION = packageJson.version;
47570
47891
  var program = new Command32();
47571
47892
  var commandName = process.argv[1]?.endsWith("sklx") ? "sklx" : "skillsmith";