@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/CHANGELOG.md +8 -0
- package/README.md +5 -6
- package/dist/.skillsmith-dist-hash +1 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/cli.js +1081 -760
- package/package.json +12 -12
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
|
|
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
|
|
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
|
|
1656
|
-
import { existsSync as existsSync2, readFileSync,
|
|
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
|
|
1741
|
+
return join4(homedir3(), CONFIG_DIR);
|
|
1676
1742
|
}
|
|
1677
1743
|
function getConfigPath() {
|
|
1678
|
-
return
|
|
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 :
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
2346
|
-
import { dirname as
|
|
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(
|
|
2427
|
+
mkdirSync2(dirname3(path24), { recursive: true });
|
|
2357
2428
|
writeFileSync2(path24, content, "utf-8");
|
|
2358
2429
|
if (executable)
|
|
2359
|
-
|
|
2430
|
+
chmodSync3(path24, 493);
|
|
2360
2431
|
return { changed: true, backupPath };
|
|
2361
2432
|
}
|
|
2362
|
-
mkdirSync2(
|
|
2433
|
+
mkdirSync2(dirname3(path24), { recursive: true });
|
|
2363
2434
|
writeFileSync2(path24, content, "utf-8");
|
|
2364
2435
|
if (executable)
|
|
2365
|
-
|
|
2436
|
+
chmodSync3(path24, 493);
|
|
2366
2437
|
return { changed: true, backupPath: null };
|
|
2367
2438
|
}
|
|
2368
2439
|
function isExecutable(path24) {
|
|
2369
2440
|
try {
|
|
2370
|
-
return (
|
|
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 =
|
|
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
|
|
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 :
|
|
2462
|
+
return override && override.length > 0 ? override : join7(getConfigDir(), "agent-install");
|
|
2392
2463
|
}
|
|
2393
2464
|
function getAgentManifestPath() {
|
|
2394
|
-
return
|
|
2465
|
+
return join7(getAgentInstallDir(), "manifest.json");
|
|
2395
2466
|
}
|
|
2396
2467
|
function getAgentInstallBackupsDir() {
|
|
2397
|
-
return
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
2519
|
-
configPath:
|
|
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:
|
|
2527
|
-
configPath:
|
|
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:
|
|
2537
|
-
configPath:
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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 =
|
|
2972
|
-
const endPath =
|
|
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 =
|
|
3033
|
-
const endPath =
|
|
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(
|
|
3267
|
+
return existsSync9(relocateUnderHome(join14(homedir6(), ".codex"), homeDir));
|
|
3197
3268
|
}
|
|
3198
3269
|
function writeSkillPackFor(clientNativePath, content, ctx, harness) {
|
|
3199
|
-
const path24 =
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
3347
|
-
addSuffix(
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
3505
|
+
var DEFAULT_DB_PATH = join16(homedir8(), ".skillsmith", "skills.db");
|
|
3435
3506
|
var DEFAULT_SKILLS_DIR = getCanonicalInstallPath();
|
|
3436
|
-
var DEFAULT_MANIFEST_PATH =
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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:
|
|
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(
|
|
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 =
|
|
22821
|
+
resolvedPath = join18(this.rootDir, location.path);
|
|
22734
22822
|
} else if (location.owner && location.repo) {
|
|
22735
|
-
resolvedPath =
|
|
22823
|
+
resolvedPath = join18(this.rootDir, location.owner, location.repo, "SKILL.md");
|
|
22736
22824
|
} else if (location.repo) {
|
|
22737
|
-
resolvedPath =
|
|
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
|
-
|
|
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
|
|
23785
|
-
import { dirname as
|
|
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 =
|
|
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(
|
|
23932
|
+
mkdirSync8(dirname12(tmp), { recursive: true });
|
|
23845
23933
|
writeFileSync10(tmp, contents, typeof contents === "string" ? { encoding: "utf-8" } : void 0);
|
|
23846
|
-
|
|
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
|
-
|
|
23968
|
+
unlinkSync3(paths.bin);
|
|
23881
23969
|
if (existsSync14(paths.meta))
|
|
23882
|
-
|
|
23970
|
+
unlinkSync3(paths.meta);
|
|
23883
23971
|
if (existsSync14(paths.labels))
|
|
23884
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 ||
|
|
25482
|
+
return process.env.SKILLSMITH_LOG_DIR || join21(homedir10(), ".skillsmith", "logs");
|
|
25395
25483
|
}
|
|
25396
25484
|
function dailyFilePath(surface, date5) {
|
|
25397
|
-
return
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
25796
|
+
async function readSkillFields(skillDir) {
|
|
25709
25797
|
try {
|
|
25710
|
-
const content = await readFile2(
|
|
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:
|
|
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 ??
|
|
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:
|
|
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,
|
|
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 =
|
|
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
|
-
|
|
25850
|
+
const emittedKey = `${harness}:${realDir}`;
|
|
25851
|
+
if (emitted.has(emittedKey))
|
|
25763
25852
|
continue;
|
|
25764
|
-
|
|
25765
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
|
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
|
-
|
|
25800
|
-
|
|
25801
|
-
|
|
25802
|
-
|
|
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 =
|
|
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
|
|
25866
|
-
import { existsSync as existsSync16, readFileSync as readFileSync12, writeFileSync as writeFileSync11, chmodSync as
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
26237
|
-
|
|
26238
|
-
|
|
26239
|
-
|
|
26240
|
-
|
|
26241
|
-
|
|
26242
|
-
|
|
26243
|
-
|
|
26244
|
-
|
|
26245
|
-
|
|
26246
|
-
|
|
26247
|
-
|
|
26248
|
-
|
|
26249
|
-
|
|
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
|
|
27078
|
+
const files = await fs4.readdir(skillPath, { withFileTypes: true });
|
|
26950
27079
|
for (const file2 of files) {
|
|
26951
27080
|
if (file2.isFile()) {
|
|
26952
|
-
const filePath =
|
|
26953
|
-
const stats = await
|
|
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 =
|
|
26968
|
-
const resolvedSkillsDir =
|
|
26969
|
-
if (resolvedInstall !== resolvedSkillsDir && !resolvedInstall.startsWith(resolvedSkillsDir +
|
|
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
|
|
26975
|
-
const realInstallPath = await
|
|
26976
|
-
const expectedPrefix = await
|
|
26977
|
-
if (!realInstallPath.startsWith(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 =
|
|
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 =
|
|
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 =
|
|
26993
|
-
await
|
|
26994
|
-
subagentPath =
|
|
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
|
|
27129
|
+
await fs4.unlink(filePath).catch(() => {
|
|
27001
27130
|
});
|
|
27002
27131
|
}
|
|
27003
27132
|
if (pathValidated) {
|
|
27004
|
-
await
|
|
27133
|
+
await fs4.rm(installPath, { recursive: true, force: true }).catch(() => {
|
|
27005
27134
|
});
|
|
27006
27135
|
} else {
|
|
27007
|
-
await
|
|
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
|
|
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 =
|
|
27274
|
+
const potentialPath = path4.join(skillsDir, skillName);
|
|
27126
27275
|
try {
|
|
27127
|
-
await
|
|
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
|
|
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
|
|
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
|
|
29457
|
+
import { join as join27, dirname as dirname14 } from "path";
|
|
29170
29458
|
import { homedir as homedir14 } from "os";
|
|
29171
|
-
var ANALYTICS_DIR =
|
|
29172
|
-
var 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
|
|
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 =
|
|
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
|
|
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 (
|
|
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
|
|
32073
|
-
import { join as
|
|
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
|
|
32364
|
+
import { dirname as dirname16, join as join35 } from "node:path";
|
|
32077
32365
|
import { fileURLToPath } from "node:url";
|
|
32078
32366
|
function packageRoot() {
|
|
32079
|
-
return
|
|
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 =
|
|
32086
|
-
const pkg = JSON.parse(
|
|
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
|
|
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:" || !
|
|
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
|
|
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
|
|
32915
|
-
import { join as
|
|
33202
|
+
import { createHash as createHash8 } from "crypto";
|
|
33203
|
+
import { join as join37 } from "path";
|
|
32916
33204
|
function getLocalSkillsDir() {
|
|
32917
|
-
return
|
|
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.
|
|
32936
|
-
|
|
32937
|
-
|
|
32938
|
-
|
|
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 =
|
|
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(
|
|
33008
|
-
const contentHash =
|
|
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
|
|
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
|
-
|
|
33045
|
-
|
|
33046
|
-
|
|
33047
|
-
|
|
33048
|
-
|
|
33049
|
-
|
|
33050
|
-
|
|
33051
|
-
|
|
33052
|
-
} =
|
|
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
|
|
33404
|
+
import { join as join38 } from "path";
|
|
33102
33405
|
async function resolveInstalledSkillId(installed) {
|
|
33103
33406
|
try {
|
|
33104
|
-
const content = await readFile7(
|
|
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(
|
|
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
|
|
33499
|
-
import { createHash as
|
|
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
|
|
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())) :
|
|
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
|
|
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(
|
|
34481
|
-
await mkdir7(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
34948
|
+
filePath = join41(filePath, "SKILL.md");
|
|
34630
34949
|
}
|
|
34631
34950
|
} catch {
|
|
34632
34951
|
if (!filePath.endsWith(".md")) {
|
|
34633
|
-
filePath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
35310
|
+
skillMdPath = join42(dirPath, "SKILL.md");
|
|
34992
35311
|
} else {
|
|
34993
35312
|
skillMdPath = dirPath;
|
|
34994
|
-
dirPath =
|
|
35313
|
+
dirPath = dirname19(dirPath);
|
|
34995
35314
|
}
|
|
34996
35315
|
} catch {
|
|
34997
|
-
skillMdPath = dirPath.endsWith(".md") ? dirPath :
|
|
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 =
|
|
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
|
|
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 =
|
|
35446
|
+
const skillMdPath2 = join43(dirPath, entry.name, "SKILL.md");
|
|
35128
35447
|
if (await fileExists(skillMdPath2)) {
|
|
35129
|
-
skillDirs.push(
|
|
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 =
|
|
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 =
|
|
35166
|
-
const subagentPath =
|
|
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
|
|
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(
|
|
35338
|
-
await mkdir9(
|
|
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 =
|
|
35341
|
-
const dir =
|
|
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", "${
|
|
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
|
|
35542
|
-
import { join as
|
|
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 (!
|
|
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 =
|
|
35862
|
-
const stat13 =
|
|
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 =
|
|
35871
|
-
if (
|
|
36189
|
+
const skillMdPath = join45(skillPath, "SKILL.md");
|
|
36190
|
+
if (existsSync21(skillMdPath)) {
|
|
35872
36191
|
try {
|
|
35873
|
-
const content =
|
|
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
|
|
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 (!
|
|
36838
|
+
if (!existsSync22(resolvedSource)) {
|
|
36520
36839
|
logger20.error(`Source database not found: ${resolvedSource}`);
|
|
36521
36840
|
process.exit(1);
|
|
36522
36841
|
}
|
|
36523
|
-
if (!
|
|
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
|
|
36929
|
+
import { join as join46, dirname as dirname21 } from "path";
|
|
36611
36930
|
var logger21 = getCliLogger();
|
|
36612
36931
|
function getAssetsPath() {
|
|
36613
|
-
return
|
|
36932
|
+
return join46(packageRoot(), "assets", "skillsmith-skill");
|
|
36614
36933
|
}
|
|
36615
36934
|
function getTargetPath() {
|
|
36616
|
-
return
|
|
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 =
|
|
36634
|
-
const destPath =
|
|
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(
|
|
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
|
|
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 = "@
|
|
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
|
|
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
|
|
37536
|
+
import { join as join47, dirname as dirname22 } from "path";
|
|
37216
37537
|
import { homedir as homedir21 } from "os";
|
|
37217
|
-
var SKILLSMITH_DIR =
|
|
37218
|
-
var MANIFEST_PATH =
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
44919
|
+
return join59(homedir29(), ".skillsmith", "namespace-overrides.json");
|
|
44599
44920
|
}
|
|
44600
44921
|
function backupsDir() {
|
|
44601
|
-
return
|
|
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 =
|
|
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
|
|
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 =
|
|
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(
|
|
45890
|
+
await mkdir17(join60(skillDir, "resources"), { recursive: true });
|
|
45570
45891
|
if (includeScripts) {
|
|
45571
|
-
await mkdir17(
|
|
45892
|
+
await mkdir17(join60(skillDir, "scripts"), { recursive: true });
|
|
45572
45893
|
}
|
|
45573
|
-
await writeFile15(
|
|
45574
|
-
await writeFile15(
|
|
45575
|
-
await writeFile15(
|
|
45576
|
-
await writeFile15(
|
|
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(
|
|
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
|
|
46276
|
+
import { createHash as createHash18 } from "node:crypto";
|
|
45956
46277
|
import { promises as fs34 } from "node:fs";
|
|
45957
|
-
import { join as
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
46431
|
-
import { readdirSync as readdirSync4, unlinkSync as
|
|
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
|
|
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
|
|
46769
|
+
return join63(homedir31(), ".claude", "settings.json");
|
|
46449
46770
|
}
|
|
46450
|
-
return
|
|
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 =
|
|
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
|
|
46863
|
+
return join64(homedir32(), ".skillsmith", "hooks", "skill-telemetry.sh");
|
|
46543
46864
|
}
|
|
46544
46865
|
function runDir() {
|
|
46545
|
-
return
|
|
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 (!
|
|
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 =
|
|
46879
|
+
const fp = join64(dir, f);
|
|
46559
46880
|
try {
|
|
46560
|
-
const st =
|
|
46561
|
-
if (now - st.mtimeMs > ORPHAN_TTL_MS)
|
|
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 =
|
|
46659
|
-
if (!
|
|
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 =
|
|
46990
|
+
const hooksDir = dirname29(destPath);
|
|
46670
46991
|
mkdirSync13(hooksDir, { recursive: true, mode: 448 });
|
|
46671
46992
|
copyFileSync2(templateSrc, destPath);
|
|
46672
46993
|
try {
|
|
46673
|
-
|
|
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 (
|
|
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
|
|
47122
|
-
import { basename as basename9, dirname as
|
|
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
|
|
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
|
|
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"] ||
|
|
47462
|
+
return process.env["SKILLSMITH_LOG_DIR"] || join65(homedir33(), ".skillsmith", "logs");
|
|
47142
47463
|
}
|
|
47143
47464
|
function listLogFiles(dir) {
|
|
47144
|
-
if (!
|
|
47465
|
+
if (!existsSync29(dir)) return [];
|
|
47145
47466
|
try {
|
|
47146
|
-
return readdirSync5(dir).filter((name) => LOG_FILE_PATTERN.test(name)).sort().map((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 =
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
47332
|
-
import { join as
|
|
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) =>
|
|
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 (!
|
|
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,
|
|
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 =
|
|
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 =
|
|
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
|
|
47484
|
-
import { join as
|
|
47804
|
+
import { readFileSync as readFileSync28 } from "fs";
|
|
47805
|
+
import { join as join67 } from "path";
|
|
47485
47806
|
function loadMinNodeVersion() {
|
|
47486
47807
|
try {
|
|
47487
|
-
const packageJsonPath2 =
|
|
47488
|
-
const packageJson2 = JSON.parse(
|
|
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
|
|
47560
|
-
import { join as
|
|
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 =
|
|
47568
|
-
var packageJson = JSON.parse(
|
|
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";
|