@rhize/skill-forge 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/commands/add.ts","../src/config.ts","../src/install/resolve.ts","../src/install/quarantine.ts","../src/install/promote.ts","../src/license.ts","../src/report/render.ts","../src/provenance.ts","../src/gate/profile.ts","../src/util.ts","../src/gate/safety.ts","../src/gate/overlap.ts","../src/commands/gatePipeline.ts","../src/queue.ts","../src/commands/scan.ts","../src/commands/list.ts","../src/commands/status.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { createRequire } from 'node:module';\nimport { add } from './commands/add.js';\nimport { scan } from './commands/scan.js';\nimport { list } from './commands/list.js';\nimport { status } from './commands/status.js';\n\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json') as { version: string };\n\nconst program = new Command();\n\nprogram\n .name('skill-forge')\n .description('The supply-chain gate for agent skills')\n .version(pkg.version);\n\nprogram\n .command('add <source>')\n .description('Quarantine-install a skill and run it through the safety gate')\n .option('-y, --yes', 'skip the interactive prompt; honor the gate verdict automatically')\n .option('-t, --target <dir>', 'skills root to promote into (defaults to config skillsRoots[0])')\n .option('--json', 'print the gate result as JSON instead of the terminal report')\n .option('--ingest', 'after a successful promote, hand off to Claude via /rhize-meta:forge-ingest')\n .action(add);\n\nprogram\n .command('scan <source>')\n .description('Run the safety gate against a skill without installing it')\n .action(scan);\n\nprogram\n .command('list')\n .description('List skills currently in quarantine')\n .action(list);\n\nprogram\n .command('status')\n .description('Show skill-forge configuration and quarantine status')\n .action(status);\n\nawait program.parseAsync();\n","import { createInterface } from 'node:readline/promises';\nimport { spawnSync } from 'node:child_process';\nimport { loadConfig } from '../config.js';\nimport { resolve as resolveSource } from '../install/resolve.js';\nimport { quarantine } from '../install/quarantine.js';\nimport { promote, reject, hold } from '../install/promote.js';\nimport { renderJson, renderReport } from '../report/render.js';\nimport { appendProvenance } from '../provenance.js';\nimport { runGate } from './gatePipeline.js';\nimport type { Verdict } from '../gate/safety.js';\nimport { suggestVerbCode } from '../gate/overlap.js';\nimport { appendQueueEntry } from '../queue.js';\nimport { upgradeNotice } from '../license.js';\n\nexport interface AddOptions {\n yes?: boolean;\n target?: string;\n json?: boolean;\n ingest?: boolean;\n}\n\ntype Decision = 'promote' | 'hold' | 'reject';\n\n/**\n * `skill-forge add <source>` — quarantine-install a skill and run it through\n * the safety gate (profile -> safety scan -> overlap analysis -> report ->\n * promote/hold/reject). `--yes` skips the interactive prompt and honors the\n * gate verdict; otherwise the decision is asked interactively.\n */\nexport async function add(source: string, options: AddOptions = {}): Promise<void> {\n const config = loadConfig();\n const targetRoot = options.target ?? config.skillsRoots[0];\n if (!targetRoot) {\n throw new Error('no target skills root configured; pass --target <dir> or set skillsRoots in the config');\n }\n\n const resolved = resolveSource(source);\n const entry = await quarantine(resolved, config.quarantineDir);\n\n if (entry.skillDirs.length === 0) {\n console.error(`[skill-forge] no SKILL.md found in installed source: ${source}`);\n reject(entry);\n process.exitCode = 1;\n return;\n }\n if (entry.skillDirs.length > 1) {\n console.error(\n `[skill-forge] ${entry.skillDirs.length} skills found in ${source}; gating the first — the whole ` +\n 'quarantine entry is promoted/rejected together:\\n' +\n entry.skillDirs.map((d) => ` - ${d}`).join('\\n')\n );\n }\n\n const { safety, gateResult, pro } = await runGate(entry.skillDirs[0], targetRoot);\n const { profile, overlap } = gateResult;\n\n console.log(options.json ? renderJson(gateResult) : renderReport(gateResult));\n\n const decision = options.yes ? yesDecision(safety.verdict) : await promptDecision(safety.verdict);\n\n let installedPath: string | null = null;\n\n if (decision === 'promote') {\n const result = promote(entry, targetRoot);\n console.log(`[skill-forge] promoted -> ${result.promoted.join(', ')}`);\n // Free-tier `add` makes no absorb/fork judgment — it either installs the\n // candidate as-is or it doesn't. DEFER is the matching verb from the\n // rhize-skill-forge five-verb matrix (\"adopt as-is; just install/keep it\").\n // Provenance recording is Pro — see the consolidated notice below.\n if (pro) {\n const ledgerPath = appendProvenance(\n {\n name: profile.name ?? entry.id,\n source,\n version: profile.version,\n license: profile.license,\n verb: 'DEFER',\n target: targetRoot,\n took: `installed as-is (${result.promoted.length} skill dir${result.promoted.length === 1 ? '' : 's'})`,\n notes:\n `gate: safety=${safety.verdict}` +\n (overlap ? `, overlap nearest=${overlap.nearest} (${overlap.nearestScore})` : ''),\n },\n targetRoot\n );\n console.log(`[skill-forge] provenance recorded -> ${ledgerPath}`);\n }\n installedPath = result.promoted[0] ?? null;\n } else if (decision === 'reject') {\n reject(entry);\n console.log('[skill-forge] rejected — quarantine sandbox removed');\n if (safety.verdict === 'block') process.exitCode = 1;\n return; // rejected candidates are gone — nothing to queue for ingestion\n } else {\n hold(entry);\n console.log(`[skill-forge] held in quarantine -> ${entry.quarantinePath}`);\n }\n\n // Pending-ingestion queue and --ingest handoff are Pro; free `add` stops\n // at promote/hold, which is the complete free-tier flow.\n if (pro) {\n // The queue entry records the gate result at decision time so a later\n // ingest pass doesn't need to re-run it.\n appendQueueEntry({\n source,\n sourceType: resolved.type,\n installedPath,\n quarantinePath: entry.quarantinePath,\n gate: {\n license: profile.license ?? null,\n safetyVerdict: safety.verdict,\n safetyFindings: safety.findings.map((f) => `${f.severity} [${f.rule}] ${f.file}: ${f.detail}`),\n overlapTop: overlap ? overlap.ranking.slice(0, 3).map((r) => ({ skill: r.skill, score: r.score })) : [],\n suggestedVerb: overlap ? suggestVerbCode(overlap.nearestScore) : null,\n },\n status: 'pending',\n });\n\n if (options.ingest && installedPath) {\n handoffToClaude(installedPath);\n }\n } else {\n console.log(upgradeNotice('provenance ledger, pending-ingestion queue, and --ingest handoff'));\n }\n}\n\n/**\n * If a `claude` binary is on PATH, spawns it with the forge-ingest slash\n * command, inheriting stdio so the handoff session runs interactively in the\n * user's terminal. Otherwise (the spawn fails with ENOENT) prints the exact\n * command for the user to run.\n */\nfunction handoffToClaude(installedPath: string): void {\n const command = `/rhize-meta:forge-ingest ${installedPath}`;\n const result = spawnSync('claude', ['-p', command], { stdio: 'inherit' });\n const error = result.error as NodeJS.ErrnoException | undefined;\n if (error?.code === 'ENOENT') {\n console.log('[skill-forge] claude not found on PATH — run this yourself:');\n console.log(` claude -p \"${command}\"`);\n } else if (error) {\n console.error(`[skill-forge] failed to launch claude: ${error.message}`);\n }\n}\n\n/** --yes honors the gate verdict: a block is rejected (nonzero exit); pass/warn is promoted. */\nfunction yesDecision(verdict: Verdict): Decision {\n return verdict === 'block' ? 'reject' : 'promote';\n}\n\nasync function promptDecision(verdict: Verdict): Promise<Decision> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n const defaultChoice: Decision = verdict === 'block' ? 'reject' : verdict === 'warn' ? 'hold' : 'promote';\n const prompt = `Promote, hold, or reject this skill? [promote/hold/reject] (default: ${defaultChoice}) `;\n try {\n // Iterate lines via the async-iterator protocol rather than looping on\n // rl.question(): question() attaches a one-shot 'line' listener per call,\n // and on piped (non-TTY) input where several answer lines arrive in a\n // single chunk, a line can be emitted — and silently dropped — between\n // one question() resolving and the next one attaching its listener. The\n // async iterator queues lines internally, so no answer is ever lost.\n process.stdout.write(prompt);\n for await (const raw of rl) {\n const answer = raw.trim().toLowerCase() || defaultChoice;\n if (answer === 'p' || answer === 'promote') return 'promote';\n if (answer === 'h' || answer === 'hold') return 'hold';\n if (answer === 'r' || answer === 'reject') return 'reject';\n process.stdout.write(`Please answer promote, hold, or reject (or p/h/r).\\n${prompt}`);\n }\n // stdin closed without a valid answer — fail safe to the verdict's default.\n return defaultChoice;\n } finally {\n rl.close();\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport type Strictness = 'block-high' | 'block-medium' | 'warn-only';\n\nexport interface Config {\n skillsRoots: string[];\n quarantineDir: string;\n strictness: Strictness;\n /** Pro license key (skf1.<payload>.<signature>). SKILL_FORGE_LICENSE env takes precedence — see src/license.ts. */\n licenseKey?: string;\n}\n\n/**\n * Resolves the skill-forge home directory. Honors SKILL_FORGE_HOME so tests\n * (and users) can redirect config/quarantine storage away from ~/.skill-forge.\n */\nexport function getHomeDir(): string {\n return process.env.SKILL_FORGE_HOME ?? join(homedir(), '.skill-forge');\n}\n\nexport function getConfigPath(): string {\n return join(getHomeDir(), 'config.json');\n}\n\nexport function defaultConfig(): Config {\n const home = getHomeDir();\n return {\n skillsRoots: [join(process.cwd(), '.claude', 'skills')],\n quarantineDir: join(home, 'quarantine'),\n strictness: 'block-high',\n };\n}\n\n/**\n * Loads config from disk, falling back to defaults for any missing keys.\n * Does not write anything to disk.\n */\nexport function loadConfig(): Config {\n const configPath = getConfigPath();\n const defaults = defaultConfig();\n\n if (!existsSync(configPath)) {\n return defaults;\n }\n\n const raw = readFileSync(configPath, 'utf-8');\n const parsed = JSON.parse(raw) as Partial<Config>;\n\n return {\n skillsRoots: parsed.skillsRoots ?? defaults.skillsRoots,\n quarantineDir: parsed.quarantineDir ?? defaults.quarantineDir,\n strictness: parsed.strictness ?? defaults.strictness,\n licenseKey: parsed.licenseKey ?? defaults.licenseKey,\n };\n}\n\nexport function saveConfig(config: Config): void {\n const home = getHomeDir();\n if (!existsSync(home)) {\n mkdirSync(home, { recursive: true });\n }\n writeFileSync(getConfigPath(), JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n","import { existsSync } from 'node:fs';\n\nexport type SourceType = 'skills.sh' | 'git' | 'local';\n\nexport interface ResolvedSource {\n type: SourceType;\n value: string;\n}\n\nconst GIT_URL_PATTERN = /^(git@|https?:\\/\\/|ssh:\\/\\/)/;\n\n/**\n * Classifies a skill source string so the installer knows which quarantine\n * strategy to use:\n * - an http(s)/git@/ssh:// URL, or one ending in `.git`, is a git remote\n * - an existing filesystem path is a local skill\n * - everything else is treated as a skills.sh `owner/name` slug\n */\nexport function resolve(source: string): ResolvedSource {\n const value = source.trim();\n\n if (GIT_URL_PATTERN.test(value) || value.endsWith('.git')) {\n return { type: 'git', value };\n }\n\n if (existsSync(value)) {\n return { type: 'local', value };\n }\n\n return { type: 'skills.sh', value };\n}\n","import { randomUUID } from 'node:crypto';\nimport { spawn } from 'node:child_process';\nimport { cpSync, existsSync, mkdirSync, readdirSync } from 'node:fs';\nimport type { Dirent } from 'node:fs';\nimport { basename, join, resolve as resolvePath } from 'node:path';\nimport type { ResolvedSource } from './resolve.js';\n\nexport interface ExecResult {\n stdout: string;\n stderr: string;\n code: number;\n}\n\nexport type ExecFn = (\n command: string,\n args: string[],\n options: { cwd: string }\n) => Promise<ExecResult>;\n\nexport interface QuarantineEntry {\n id: string;\n quarantinePath: string;\n skillDirs: string[];\n source: ResolvedSource;\n}\n\nexport interface QuarantineDeps {\n exec?: ExecFn;\n}\n\nconst SKILL_MANIFEST = 'SKILL.md';\nconst SKIP_DIRS = new Set(['.git', 'node_modules']);\n\n/**\n * Installs `source` into `<quarantineDir>/<id>/` — never into a target\n * skills root. skills.sh sources run `npx -y skills@latest add <slug>`\n * with cwd pinned to the sandbox; git sources run a shallow clone into a\n * named subdir of the sandbox; local sources are recursively copied in.\n * Returns the sandbox id/path plus every directory inside it that looks\n * like an installed skill (contains SKILL.md).\n */\nexport async function quarantine(\n source: ResolvedSource,\n quarantineDir: string,\n deps: QuarantineDeps = {}\n): Promise<QuarantineEntry> {\n const exec = deps.exec ?? defaultExec;\n const id = randomUUID();\n const quarantinePath = join(quarantineDir, id);\n mkdirSync(quarantinePath, { recursive: true });\n\n switch (source.type) {\n case 'skills.sh': {\n const result = await exec('npx', ['-y', 'skills@latest', 'add', source.value], {\n cwd: quarantinePath,\n });\n if (result.code !== 0) {\n throw new Error(\n `skills.sh install failed for \"${source.value}\" (exit ${result.code}): ${result.stderr || result.stdout}`\n );\n }\n break;\n }\n case 'git': {\n const clonePath = join(quarantinePath, deriveGitDirName(source.value));\n const result = await exec('git', ['clone', '--depth', '1', source.value, clonePath], {\n cwd: quarantinePath,\n });\n if (result.code !== 0) {\n throw new Error(\n `git clone failed for \"${source.value}\" (exit ${result.code}): ${result.stderr || result.stdout}`\n );\n }\n break;\n }\n case 'local': {\n const dirName = basename(resolvePath(source.value));\n cpSync(source.value, join(quarantinePath, dirName), { recursive: true });\n break;\n }\n }\n\n return { id, quarantinePath, skillDirs: findSkillDirs(quarantinePath), source };\n}\n\n/** IDs (sandbox dir names) of entries currently held in quarantine; [] when the dir doesn't exist. */\nexport function listQuarantineIds(quarantineDir: string): string[] {\n if (!existsSync(quarantineDir)) return [];\n return readdirSync(quarantineDir, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => e.name);\n}\n\n/** Recursively finds every directory under `root` that directly contains SKILL.md. */\nexport function findSkillDirs(root: string): string[] {\n const found: string[] = [];\n\n function walk(dir: string): void {\n let entries: Dirent[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n if (entries.some((entry) => entry.isFile() && entry.name === SKILL_MANIFEST)) {\n found.push(dir);\n }\n\n for (const entry of entries) {\n if (entry.isDirectory() && !SKIP_DIRS.has(entry.name)) {\n walk(join(dir, entry.name));\n }\n }\n }\n\n walk(root);\n return found;\n}\n\n/** Derives a filesystem-friendly dir name from a git URL (scp-like or http(s)). */\nfunction deriveGitDirName(url: string): string {\n const stripped = url.replace(/\\.git$/, '');\n const segments = stripped.split(/[/:]/).filter(Boolean);\n return segments[segments.length - 1] || 'repo';\n}\n\nfunction defaultExec(command: string, args: string[], options: { cwd: string }): Promise<ExecResult> {\n return new Promise((resolvePromise, reject) => {\n const child = spawn(command, args, { cwd: options.cwd });\n let stdout = '';\n let stderr = '';\n child.stdout?.on('data', (chunk: Buffer) => (stdout += chunk.toString()));\n child.stderr?.on('data', (chunk: Buffer) => (stderr += chunk.toString()));\n child.on('error', reject);\n child.on('close', (code) => resolvePromise({ stdout, stderr, code: code ?? 1 }));\n });\n}\n","import { cpSync, existsSync, mkdirSync, renameSync, rmSync } from 'node:fs';\nimport { basename, join } from 'node:path';\nimport type { QuarantineEntry } from './quarantine.js';\n\nexport interface PromoteOptions {\n force?: boolean;\n}\n\nexport interface PromoteResult {\n promoted: string[];\n}\n\n/**\n * Moves every skill dir found during quarantine into `targetSkillsRoot`.\n * Refuses to overwrite an existing dir of the same name unless `force` is\n * set — checked up front for every skill dir before anything is moved, so\n * a naming clash never leaves a partial promotion behind. Cleans up the\n * quarantine sandbox once every skill dir has been moved out.\n */\nexport function promote(\n entry: QuarantineEntry,\n targetSkillsRoot: string,\n options: PromoteOptions = {}\n): PromoteResult {\n const force = options.force ?? false;\n\n for (const skillDir of entry.skillDirs) {\n const dest = join(targetSkillsRoot, basename(skillDir));\n if (existsSync(dest) && !force) {\n throw new Error(`Refusing to overwrite existing skill dir: ${dest} (pass force to override)`);\n }\n }\n\n mkdirSync(targetSkillsRoot, { recursive: true });\n\n const promoted: string[] = [];\n for (const skillDir of entry.skillDirs) {\n const dest = join(targetSkillsRoot, basename(skillDir));\n if (existsSync(dest)) {\n rmSync(dest, { recursive: true, force: true });\n }\n moveDir(skillDir, dest);\n promoted.push(dest);\n }\n\n rmSync(entry.quarantinePath, { recursive: true, force: true });\n\n return { promoted };\n}\n\n/** Deletes the quarantine sandbox without promoting anything into a skills root. */\nexport function reject(entry: QuarantineEntry): void {\n rmSync(entry.quarantinePath, { recursive: true, force: true });\n}\n\n/** No-op: leaves the quarantine sandbox exactly as-is for later review. */\nexport function hold(_entry: QuarantineEntry): void {\n // intentionally does nothing — quarantine dir is left in place\n}\n\nfunction moveDir(src: string, dest: string): void {\n try {\n renameSync(src, dest);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'EXDEV') {\n cpSync(src, dest, { recursive: true });\n rmSync(src, { recursive: true, force: true });\n } else {\n throw err;\n }\n }\n}\n","import { verify as cryptoVerify } from 'node:crypto';\nimport { loadConfig } from './config.js';\n\n/**\n * Offline license-key verification for skill-forge Pro. No network calls —\n * the CLI must gate Pro features without phoning home.\n *\n * Key format: `skf1.<base64url payload>.<base64url signature>`\n * - `skf1` is a fixed prefix identifying the key scheme/version.\n * - payload is base64url-encoded JSON: `{ name, email, seats, exp }`, where\n * `exp` is an ISO date string (`YYYY-MM-DD`) the key is valid through.\n * - signature is an Ed25519 signature (via node:crypto) over the raw\n * decoded payload bytes, verified against the embedded PUBLIC_KEY_PEM.\n *\n * Keys are minted offline with `scripts/keygen.mjs sign` using the matching\n * private key, which is never committed to this repo (see keys/, gitignored).\n */\n\nconst LICENSE_PREFIX = 'skf1';\n\n// Ed25519 public key (SPKI/PEM), generated once via `scripts/keygen.mjs keypair`.\n// The matching private key lives outside this repo — see scripts/keygen.mjs.\nconst PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAWSVbcX9u70IsN1O5zS5z+AYtAUVz7ExT7h+1gT9x+wQ=\n-----END PUBLIC KEY-----\n`;\n\nexport interface LicensePayload {\n name: string;\n email: string;\n seats: number;\n exp: string;\n}\n\nexport type LicenseInvalidReason = 'missing' | 'malformed' | 'signature' | 'expired';\n\nexport type LicenseStatus =\n | { valid: true; payload: LicensePayload }\n | { valid: false; reason: LicenseInvalidReason };\n\n/** Reads the raw license key string. SKILL_FORGE_LICENSE (env) takes precedence over config.licenseKey. */\nexport function readLicenseKey(): string | undefined {\n const fromEnv = process.env.SKILL_FORGE_LICENSE?.trim();\n if (fromEnv) return fromEnv;\n return loadConfig().licenseKey?.trim() || undefined;\n}\n\nfunction isLicensePayload(value: unknown): value is LicensePayload {\n if (typeof value !== 'object' || value === null) return false;\n const p = value as Record<string, unknown>;\n return typeof p.name === 'string' && typeof p.email === 'string' && typeof p.seats === 'number' && typeof p.exp === 'string';\n}\n\n/**\n * Verifies a license key string. Pass no argument to check the default\n * sources (SKILL_FORGE_LICENSE env, then config.licenseKey). Verifies the\n * `skf1.<payload>.<signature>` shape, the Ed25519 signature against\n * PUBLIC_KEY_PEM, and that `exp` has not passed. Any tampering (payload or\n * signature) or expiry makes the key invalid.\n */\nexport function checkLicense(rawKey: string | undefined = readLicenseKey()): LicenseStatus {\n if (!rawKey) {\n return { valid: false, reason: 'missing' };\n }\n\n const parts = rawKey.trim().split('.');\n if (parts.length !== 3 || parts[0] !== LICENSE_PREFIX) {\n return { valid: false, reason: 'malformed' };\n }\n const [, payloadPart, signaturePart] = parts;\n\n const payloadBuffer = Buffer.from(payloadPart, 'base64url');\n const signature = Buffer.from(signaturePart, 'base64url');\n\n let payload: unknown;\n try {\n payload = JSON.parse(payloadBuffer.toString('utf-8'));\n } catch {\n return { valid: false, reason: 'malformed' };\n }\n if (!isLicensePayload(payload)) {\n return { valid: false, reason: 'malformed' };\n }\n\n // Ed25519 signs/verifies over raw bytes directly — algorithm must be null.\n let signatureValid: boolean;\n try {\n signatureValid = cryptoVerify(null, payloadBuffer, PUBLIC_KEY_PEM, signature);\n } catch {\n return { valid: false, reason: 'malformed' };\n }\n if (!signatureValid) {\n return { valid: false, reason: 'signature' };\n }\n\n const expMs = new Date(payload.exp).getTime();\n if (Number.isNaN(expMs) || expMs < Date.now()) {\n return { valid: false, reason: 'expired' };\n }\n\n return { valid: true, payload };\n}\n\n/** True if the default license sources resolve to a currently-valid license. */\nexport function hasProLicense(): boolean {\n return checkLicense().valid;\n}\n\n/** Short line replacing a locked Pro output — printed in place of the feature's normal output. */\nexport function upgradeNotice(feature: string): string {\n return `[skill-forge] Pro feature (${feature}) — set SKILL_FORGE_LICENSE or config.licenseKey to a valid license to unlock.`;\n}\n","import type { SkillProfile } from '../gate/profile.js';\nimport type { OverlapResult } from '../gate/overlap.js';\nimport { upgradeNotice } from '../license.js';\n\n/**\n * Report renderer — Step 3 of the gate pipeline (profile -> safety scan ->\n * overlap analysis -> report -> promote/reject). Renders the human-readable\n * decision box from `rhize-skill-forge/SKILL.md` (Step 3 report format) and a\n * `--json` equivalent.\n */\n\nexport type SafetyVerdict = 'ALLOW' | 'CAUTION' | 'BLOCK' | 'UNKNOWN';\n\nexport interface SafetyFinding {\n severity?: string;\n rule?: string;\n message?: string;\n}\n\n/** Mirrors the verdict shape emitted by skill_safety.py (SkillSpector wrapper). */\nexport interface SafetyResult {\n verdict: SafetyVerdict;\n severity?: string;\n score?: number;\n recommendation?: string;\n findings?: SafetyFinding[];\n}\n\n/**\n * Aggregate result of the quarantine gate pipeline for a single skill\n * candidate — the input to renderReport / renderJson. `worthTaking` and\n * `leaveBehind` are placeholders: they're only populated once a human (or a\n * later ABSORB step) has actually decided what to extract, so they render as\n * pending until then.\n */\nexport interface GateResult {\n profile: SkillProfile;\n safety: SafetyResult;\n overlap?: OverlapResult;\n /** True when overlap analysis was skipped for lack of a Pro license (as opposed to a missing skills root). */\n overlapLocked?: boolean;\n worthTaking?: string[];\n leaveBehind?: string[];\n}\n\nconst BORDER = '━'.repeat(55);\nconst LABEL_WIDTH = 20;\n\nfunction padLabel(text: string): string {\n return text.padEnd(LABEL_WIDTH);\n}\n\nfunction renderListField(labelText: string, items: string[] | undefined, placeholder: string): string {\n const head = `${padLabel(labelText)}: `;\n if (!items || items.length === 0) {\n return `${head}${placeholder}`;\n }\n const bullets = items.map((item) => ` - ${item}`).join('\\n');\n return `${head.trimEnd()}\\n${bullets}`;\n}\n\nfunction renderSafetyLine(safety: SafetyResult): string {\n const detail = [safety.severity, safety.score !== undefined ? `score ${safety.score}` : undefined]\n .filter((v): v is string => Boolean(v))\n .join(', ');\n const withDetail = detail ? `${safety.verdict} (${detail})` : safety.verdict;\n return safety.recommendation ? `${withDetail} — ${safety.recommendation}` : withDetail;\n}\n\n/** Renders the Step-3 terminal decision box for a single gate result. */\nexport function renderReport(result: GateResult): string {\n const { profile, overlap, overlapLocked, safety, worthTaking, leaveBehind } = result;\n const name = profile.name ?? 'unknown';\n const version = profile.version ?? 'unknown';\n const license = profile.license ?? 'UNKNOWN';\n\n // Locked (no Pro license): the two overlap lines collapse into one short\n // upgrade notice instead of \"n/a\" placeholders for data we never computed.\n const overlapLines = overlapLocked\n ? [`${padLabel('Overlap analysis')}: ${upgradeNotice('overlap analysis')}`]\n : overlap\n ? [\n `${padLabel('Nearest Rhize skill')}: ${overlap.nearest} (overlap ${overlap.nearestScore})`,\n `${padLabel('Recommended verb')}: ${overlap.suggestedVerb}`,\n ]\n : [`${padLabel('Nearest Rhize skill')}: n/a — no skills root configured`, `${padLabel('Recommended verb')}: n/a`];\n\n const lines: string[] = [\n BORDER,\n `🔨 Forge: ${name} (v${version}, ${license})`,\n BORDER,\n ...overlapLines,\n renderListField(\"What's worth taking\", worthTaking, '(pending — not yet determined)'),\n renderListField('What to leave behind', leaveBehind, '(pending — not yet determined)'),\n `${padLabel('Safety verdict')}: ${renderSafetyLine(safety)}`,\n BORDER,\n ];\n\n return lines.join('\\n');\n}\n\n/** Renders a gate result as JSON for `--json` output. */\nexport function renderJson(result: GateResult): string {\n return JSON.stringify(result, null, 2);\n}\n","import { appendFileSync, existsSync, statSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/**\n * Provenance ledger — direct TypeScript port of `record_provenance.py`'s\n * recording path (rhize-meta/skills/rhize-skill-forge/scripts). Appends a\n * structured entry to `<skillsRoot>/SOURCES.md`, the audit ledger of every\n * external-skill ingestion decision. Field names and formatting mirror the\n * Python source exactly so an existing SOURCES.md ledger stays consistent\n * regardless of which tool wrote which entry.\n */\n\nexport const FORGE_VERBS = ['DEFER', 'ABSORB', 'FORK', 'REJECT', 'WATCH'] as const;\nexport type ForgeVerb = (typeof FORGE_VERBS)[number];\n\nexport interface ProvenanceEntry {\n name: string;\n source: string;\n verb: ForgeVerb;\n version?: string;\n license?: string;\n target?: string;\n took?: string;\n verified?: string;\n driftCheck?: string;\n notes?: string;\n}\n\nconst LEDGER_HEADER = '# Rhize Skill Forge — Provenance Ledger\\n\\nOne entry per external-skill ingestion decision.\\n';\n\nfunction todayLocalIsoDate(): string {\n const d = new Date();\n const year = d.getFullYear();\n const month = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${year}-${month}-${day}`;\n}\n\n/**\n * Appends a provenance entry to `<skillsRoot>/SOURCES.md`, creating the\n * ledger with its header on first write. Throws if `verb` isn't a valid\n * forge verb or `skillsRoot` isn't an existing directory — mirrors the\n * Python script's fail-loud `fail()` calls. Returns the ledger path.\n */\nexport function appendProvenance(entry: ProvenanceEntry, skillsRoot: string): string {\n if (!FORGE_VERBS.includes(entry.verb)) {\n throw new Error(`verb must be one of ${FORGE_VERBS.join(', ')}`);\n }\n if (!existsSync(skillsRoot) || !statSync(skillsRoot).isDirectory()) {\n throw new Error(`skills-root not a directory: ${skillsRoot}`);\n }\n\n const today = todayLocalIsoDate();\n const driftCheck = entry.driftCheck ?? `# define how to detect upstream change for ${entry.name}`;\n\n const block =\n `\\n## ${entry.name} — ${today}\\n` +\n `- **Source:** ${entry.source}\\n` +\n `- **Upstream ref:** ${entry.version ?? 'n/a'}\\n` +\n `- **License:** ${entry.license ?? 'UNKNOWN'}\\n` +\n `- **Verb:** ${entry.verb}\\n` +\n `- **Target:** ${entry.target ?? 'n/a'}\\n` +\n `- **Took:** ${entry.took ?? 'nothing'}\\n` +\n `- **Verified:** ${entry.verified ?? 'n/a'}\\n` +\n `- **Drift check:** \\`${driftCheck}\\`\\n` +\n `- **Notes:** ${entry.notes ?? ''}\\n`;\n\n const ledgerPath = join(skillsRoot, 'SOURCES.md');\n if (!existsSync(ledgerPath)) {\n writeFileSync(ledgerPath, LEDGER_HEADER, 'utf-8');\n }\n appendFileSync(ledgerPath, block, 'utf-8');\n\n return ledgerPath;\n}\n","import { execFileSync } from 'node:child_process';\nimport {\n closeSync,\n existsSync,\n mkdtempSync,\n openSync,\n readFileSync,\n readdirSync,\n readSync,\n rmSync,\n statSync,\n} from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { basename, dirname, extname, join, resolve } from 'node:path';\nimport { expandHome, isDir, stripQuotes } from '../util.js';\n\n/**\n * Port of rhize-skill-forge's profile_skill.py. Stdlib-only structured profile of an\n * external skill candidate: a skill dir, a single SKILL.md, a plugin dir\n * (.claude-plugin/plugin.json), an MCP config dir (.mcp.json / mcp.json), or a .skill\n * (zip) bundle. Fails loudly (throws) rather than guessing.\n */\n\nconst SPDX_HINTS: Record<string, string> = {\n mit: 'MIT (permissive)',\n apache: 'Apache-2.0 (permissive)',\n bsd: 'BSD (permissive)',\n isc: 'ISC (permissive)',\n unlicense: 'Unlicense (public domain)',\n cc0: 'CC0 (public domain)',\n 'mozilla public license': 'MPL (weak copyleft)',\n 'gnu general public': 'GPL (copyleft)',\n 'gnu affero': 'AGPL (copyleft)',\n 'creative commons attribution-sharealike': 'CC-BY-SA (copyleft)',\n 'creative commons attribution': 'CC-BY (attribution)',\n 'all rights reserved': 'All rights reserved (restrictive)',\n};\n\nconst RESOURCE_DIRS = ['scripts', 'references', 'reference', 'commands', 'hooks', 'assets', 'templates', 'sub-skills'];\n\nconst PYTHON_STDLIB = new Set([\n 'os', 'sys', 're', 'json', 'argparse', 'pathlib', 'tempfile', 'zipfile',\n 'subprocess', 'collections', 'datetime', 'math', 'itertools', 'typing',\n 'shutil', 'glob', 'hashlib', 'urllib', 'csv', 'io', 'functools', '__future__',\n]);\n\nexport interface SkillProfile {\n kind: 'skill';\n inputPath: string;\n skillMd: string;\n name?: string;\n version?: string;\n validFrontmatter: boolean;\n description?: string;\n license: string;\n bodyLines: number;\n headerCount: number;\n topHeaders: string[];\n resources: Record<string, number>;\n mcpDependencies: string[];\n externalPythonDeps: string[];\n}\n\nexport interface PluginProfile {\n kind: 'plugin';\n inputPath: string;\n name?: string;\n version?: string;\n description?: string;\n skills: string[];\n skillCount: number;\n commands: number;\n mcpServers: string[];\n}\n\nexport interface McpProfile {\n kind: 'mcp';\n inputPath: string;\n config: string;\n servers: string[];\n serverCount: number;\n}\n\nexport type Profile = SkillProfile | PluginProfile | McpProfile;\n\n/** Profiles a skill dir, SKILL.md, plugin dir, MCP config dir, or .skill archive. */\nexport function profileSkill(inputPath: string): Profile {\n const p = resolve(expandHome(inputPath));\n if (!existsSync(p)) {\n throw new Error(`path does not exist: ${p}`);\n }\n\n const stat = statSync(p);\n const kind = stat.isDirectory() ? classifyInput(p) : 'skill';\n\n if (kind === 'plugin') {\n return profilePlugin(p);\n }\n if (kind === 'mcp') {\n return profileMcp(p);\n }\n return profileSkillDir(p);\n}\n\n/** skill | plugin | mcp — by manifest/config presence (directories only). */\nfunction classifyInput(p: string): 'skill' | 'plugin' | 'mcp' {\n if (existsSync(join(p, '.claude-plugin', 'plugin.json'))) return 'plugin';\n if (existsSync(join(p, '.mcp.json')) || existsSync(join(p, 'mcp.json'))) return 'mcp';\n return 'skill';\n}\n\ninterface ResolvedSkillMd {\n skillMd: string;\n root: string;\n tmpDir?: string;\n}\n\nfunction resolveSkillMd(inputPath: string): ResolvedSkillMd {\n let currentPath = inputPath;\n let tmpDir: string | undefined;\n\n const initialStat = statSync(currentPath);\n if (extname(currentPath) === '.skill' || (initialStat.isFile() && isZipFile(currentPath))) {\n tmpDir = mkdtempSync(join(tmpdir(), 'forge_'));\n extractZip(currentPath, tmpDir);\n currentPath = tmpDir;\n }\n\n const stat = statSync(currentPath);\n if (stat.isFile() && basename(currentPath) === 'SKILL.md') {\n return { skillMd: currentPath, root: dirname(currentPath), tmpDir };\n }\n if (stat.isDirectory()) {\n const direct = join(currentPath, 'SKILL.md');\n if (existsSync(direct)) {\n return { skillMd: direct, root: currentPath, tmpDir };\n }\n const matches = findSkillMdOneLevelDown(currentPath);\n if (matches.length === 1) {\n return { skillMd: matches[0], root: dirname(matches[0]), tmpDir };\n }\n if (matches.length > 1) {\n cleanupTmp(tmpDir);\n throw new Error(\n `multiple SKILL.md found under ${currentPath}; point at a single skill dir:\\n ${matches.join('\\n ')}`\n );\n }\n }\n cleanupTmp(tmpDir);\n throw new Error(`no SKILL.md found at ${currentPath}`);\n}\n\nfunction cleanupTmp(tmpDir?: string): void {\n if (tmpDir) rmSync(tmpDir, { recursive: true, force: true });\n}\n\n/** Mirrors path.glob(\"<dir>/SKILL.md\") + path.glob(\"skills/<dir>/SKILL.md\") (plugin layout support). */\nfunction findSkillMdOneLevelDown(dir: string): string[] {\n const matches = new Set<string>();\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n const candidate = join(dir, entry.name, 'SKILL.md');\n if (existsSync(candidate)) matches.add(candidate);\n }\n }\n const skillsDir = join(dir, 'skills');\n if (isDir(skillsDir)) {\n for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n const candidate = join(skillsDir, entry.name, 'SKILL.md');\n if (existsSync(candidate)) matches.add(candidate);\n }\n }\n }\n return Array.from(matches);\n}\n\nfunction isZipFile(p: string): boolean {\n try {\n const fd = openSync(p, 'r');\n const buf = Buffer.alloc(4);\n const bytesRead = readSync(fd, buf, 0, 4, 0);\n closeSync(fd);\n if (bytesRead < 4) return false;\n // ZIP local file header (PK\\x03\\x04), empty archive (PK\\x05\\x06), or spanned (PK\\x07\\x08)\n return buf[0] === 0x50 && buf[1] === 0x4b && (buf[2] === 0x03 || buf[2] === 0x05 || buf[2] === 0x07);\n } catch {\n return false;\n }\n}\n\nfunction extractZip(zipPath: string, destDir: string): void {\n try {\n execFileSync('unzip', ['-q', '-o', zipPath, '-d', destDir], { stdio: ['ignore', 'ignore', 'pipe'] });\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n if (e.code === 'ENOENT') {\n throw new Error(\n 'unsupported without unzip: .skill archives require the system `unzip` binary, which was not found on ' +\n 'PATH. Install unzip, or extract the archive yourself and point profileSkill at the resulting directory.'\n );\n }\n throw new Error(`failed to extract ${zipPath}: ${e.message ?? String(err)}`);\n }\n}\n\ninterface Frontmatter {\n validFrontmatter: boolean;\n name?: string;\n version?: string;\n license?: string;\n description?: string;\n}\n\n/** Mirrors text.split(\"---\", 2)[1] — the block between the first two \"---\" delimiters. */\nfunction frontmatterBlock(text: string): string {\n const first = text.indexOf('---');\n const rest = text.slice(first + 3);\n const second = rest.indexOf('---');\n return second === -1 ? rest : rest.slice(0, second);\n}\n\nfunction extractSimpleField(block: string, key: string): string | undefined {\n const re = new RegExp(`^${key}:\\\\s*(.+)$`, 'm');\n const m = block.match(re);\n if (!m) return undefined;\n return stripQuotes(m[1].trim());\n}\n\nfunction extractDescription(block: string): string | undefined {\n const m = block.match(/^description:\\s*(.*)$/m);\n if (!m) return undefined;\n const first = m[1].trim();\n if (['>', '>-', '|', '|-', ''].includes(first)) {\n const lines = block.split('\\n');\n const idx = lines.findIndex((l) => /^description:/.test(l));\n if (idx === -1) return undefined;\n const buf: string[] = [];\n for (let i = idx + 1; i < lines.length; i++) {\n const l = lines[i];\n if (/^\\s+\\S/.test(l)) {\n buf.push(l.trim());\n } else if (l.trim() === '') {\n continue;\n } else {\n break;\n }\n }\n return buf.join(' ').trim();\n }\n return stripQuotes(first);\n}\n\nfunction parseFrontmatter(text: string): Frontmatter {\n if (!text.startsWith('---\\n')) {\n return { validFrontmatter: false };\n }\n const block = frontmatterBlock(text);\n return {\n validFrontmatter: true,\n name: extractSimpleField(block, 'name'),\n version: extractSimpleField(block, 'version'),\n license: extractSimpleField(block, 'license'),\n description: extractDescription(block),\n };\n}\n\nfunction detectLicense(root: string, fm: Frontmatter): string {\n if (fm.license) return fm.license;\n const names = ['LICENSE', 'LICENSE.md', 'LICENSE.txt', 'COPYING', 'license'];\n for (const name of names) {\n const p = join(root, name);\n if (existsSync(p) && statSync(p).isFile()) {\n const head = readFileSync(p, 'utf-8').slice(0, 2000).toLowerCase();\n for (const [hint, label] of Object.entries(SPDX_HINTS)) {\n if (head.includes(hint)) return label;\n }\n return 'present but unrecognized';\n }\n }\n return 'NONE STATED';\n}\n\nfunction walkFiles(dir: string): string[] {\n const out: string[] = [];\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n out.push(...walkFiles(full));\n } else if (entry.isFile()) {\n out.push(full);\n }\n }\n return out;\n}\n\nfunction countResources(root: string): Record<string, number> {\n const resources: Record<string, number> = {};\n for (const d of RESOURCE_DIRS) {\n const dp = join(root, d);\n if (isDir(dp)) {\n resources[d] = walkFiles(dp).length;\n }\n }\n return resources;\n}\n\nfunction scanMcpAndImports(text: string, root: string): { mcpDeps: string[]; externalDeps: string[] } {\n const mcpRefRe = /mcp__[a-zA-Z0-9_]+/g;\n const mcpRefs = new Set(text.match(mcpRefRe) ?? []);\n const imports = new Set<string>();\n\n const scriptsDir = join(root, 'scripts');\n if (isDir(scriptsDir)) {\n for (const f of walkFiles(scriptsDir)) {\n if (!f.endsWith('.py')) continue;\n const ft = readFileSync(f, 'utf-8');\n for (const m of ft.match(mcpRefRe) ?? []) mcpRefs.add(m);\n const importRe = /^\\s*(?:import|from)\\s+([a-zA-Z0-9_]+)/gm;\n let m: RegExpExecArray | null;\n while ((m = importRe.exec(ft)) !== null) {\n imports.add(m[1]);\n }\n }\n }\n\n return {\n mcpDeps: Array.from(mcpRefs).sort(),\n externalDeps: Array.from(imports)\n .filter((i) => !PYTHON_STDLIB.has(i))\n .sort(),\n };\n}\n\nfunction profileSkillDir(inputPath: string): SkillProfile {\n const { skillMd, root, tmpDir } = resolveSkillMd(inputPath);\n try {\n const text = readFileSync(skillMd, 'utf-8');\n const fm = parseFrontmatter(text);\n const bodyLines = (text.match(/\\n/g)?.length ?? 0) + 1;\n const headers = Array.from(text.matchAll(/^#{1,3}\\s+(.+)$/gm)).map((m) => m[1]);\n const resources = countResources(root);\n const { mcpDeps, externalDeps } = scanMcpAndImports(text, root);\n\n return {\n kind: 'skill',\n inputPath,\n skillMd,\n name: fm.name,\n version: fm.version,\n validFrontmatter: fm.validFrontmatter,\n description: fm.description,\n license: detectLicense(root, fm),\n bodyLines,\n headerCount: headers.length,\n topHeaders: headers.slice(0, 12),\n resources,\n mcpDependencies: mcpDeps,\n externalPythonDeps: externalDeps,\n };\n } finally {\n cleanupTmp(tmpDir);\n }\n}\n\nfunction profilePlugin(p: string): PluginProfile {\n const manifestPath = join(p, '.claude-plugin', 'plugin.json');\n let manifest: Record<string, unknown>;\n try {\n manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));\n } catch (err) {\n throw new Error(`could not parse plugin.json under ${p}: ${(err as Error).message}`);\n }\n\n const skillsDir = join(p, 'skills');\n let skills: string[] = [];\n if (isDir(skillsDir)) {\n skills = readdirSync(skillsDir, { withFileTypes: true })\n .filter((d) => d.isDirectory() && existsSync(join(skillsDir, d.name, 'SKILL.md')))\n .map((d) => d.name)\n .sort();\n }\n\n const commandsDir = join(p, 'commands');\n const commands = isDir(commandsDir)\n ? readdirSync(commandsDir).filter((f) => f.endsWith('.md')).length\n : 0;\n\n const mc = (manifest.mcpServers ?? manifest.mcp_servers ?? {}) as Record<string, unknown>;\n const mcpServers = typeof mc === 'object' && mc !== null ? Object.keys(mc).sort() : [];\n\n return {\n kind: 'plugin',\n inputPath: p,\n name: manifest.name as string | undefined,\n version: manifest.version as string | undefined,\n description: manifest.description as string | undefined,\n skills,\n skillCount: skills.length,\n commands,\n mcpServers,\n };\n}\n\nfunction profileMcp(p: string): McpProfile {\n const cfgPath = existsSync(join(p, '.mcp.json')) ? join(p, '.mcp.json') : join(p, 'mcp.json');\n let cfg: Record<string, unknown>;\n try {\n cfg = JSON.parse(readFileSync(cfgPath, 'utf-8'));\n } catch (err) {\n throw new Error(`could not parse MCP config ${cfgPath}: ${(err as Error).message}`);\n }\n\n const servers = (cfg.mcpServers ?? cfg.servers ?? {}) as Record<string, unknown>;\n const names = typeof servers === 'object' && servers !== null ? Object.keys(servers).sort() : [];\n\n return {\n kind: 'mcp',\n inputPath: p,\n config: cfgPath,\n servers: names,\n serverCount: names.length,\n };\n}\n","import { statSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/** Expands a leading `~` / `~/` to the user's home directory. */\nexport function expandHome(p: string): string {\n if (p === '~') return homedir();\n if (p.startsWith('~/')) return join(homedir(), p.slice(2));\n return p;\n}\n\n/** Strips wrapping single/double quotes from a frontmatter value. */\nexport function stripQuotes(s: string): string {\n return s.replace(/^['\"]+/, '').replace(/['\"]+$/, '');\n}\n\n/** True if `p` exists and is a directory (false on any stat error). */\nexport function isDir(p: string): boolean {\n try {\n return statSync(p).isDirectory();\n } catch {\n return false;\n }\n}\n","import { readFileSync, readdirSync, statSync } from 'node:fs';\nimport { execFile } from 'node:child_process';\nimport { delimiter, extname, join } from 'node:path';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\nexport type Severity = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';\nexport type Verdict = 'pass' | 'warn' | 'block';\n\nexport interface SafetyFinding {\n severity: Severity;\n rule: string;\n file: string;\n detail: string;\n}\n\nexport interface SafetyResult {\n findings: SafetyFinding[];\n verdict: Verdict;\n}\n\n/**\n * Ported from skill_safety.py's SkillSpector severity->verdict mapping\n * (LOW allow, MEDIUM caution, HIGH/CRITICAL block). skill_safety.py itself\n * has no built-in static ruleset — it is a pure SkillSpector wrapper — so\n * this deny-pattern list is a new FREE-tier baseline (per docs/PRODUCT.md's\n * \"safety gate: built-in ruleset + safety scanner shell-out\") that gives a\n * verdict even when SkillSpector isn't installed.\n */\nconst BLOCK_SEVERITIES: ReadonlySet<Severity> = new Set(['HIGH', 'CRITICAL']);\n\ninterface DenyRule {\n rule: string;\n severity: Severity;\n pattern: RegExp;\n message: string;\n}\n\nconst DENY_RULES: DenyRule[] = [\n {\n rule: 'curl-pipe-shell',\n severity: 'CRITICAL',\n pattern: /\\b(curl|wget)\\b[^\\n|]*\\|\\s*(sudo\\s+)?(bash|sh|zsh)\\b/i,\n message: 'downloads a remote script and pipes it directly into a shell',\n },\n {\n rule: 'base64-obfuscated-exec',\n severity: 'CRITICAL',\n pattern:\n /(base64\\s+(-d|--decode)\\b[^\\n|]*\\|\\s*(bash|sh|zsh|python3?)\\b)|(\\bexec\\s*\\(\\s*(base64\\.)?b64decode\\()|(\\beval\\s*\\(\\s*Buffer\\.from\\([^)]*['\"]base64['\"])/i,\n message: 'decodes a base64 payload and executes it (obfuscated execution)',\n },\n {\n rule: 'reverse-shell',\n severity: 'CRITICAL',\n pattern: /(\\/dev\\/tcp\\/\\S+\\/\\d+)|(\\bnc\\s+-e\\s+\\/bin\\/(ba)?sh\\b)|(bash\\s+-i\\s*>&\\s*\\/dev\\/tcp)/i,\n message: 'opens a reverse shell back to a remote host',\n },\n {\n rule: 'destructive-rm-rf',\n severity: 'HIGH',\n pattern: /\\brm\\s+-[a-z]*r[a-z]*f[a-z]*\\b|\\brm\\s+-[a-z]*f[a-z]*r[a-z]*\\b/i,\n message: 'recursive force-delete (rm -rf) — verify the target path is scoped',\n },\n {\n rule: 'credential-file-access',\n severity: 'HIGH',\n pattern:\n /(~|\\$HOME)?\\/\\.(ssh\\/id_(rsa|ed25519|ecdsa)|aws\\/credentials|npmrc|netrc|gnupg|docker\\/config\\.json)\\b|\\bid_rsa\\b|\\.env\\b.*\\bcat\\b|\\bcat\\b.*\\.env\\b/i,\n message: 'reads a credential/secrets file (SSH keys, cloud credentials, .env)',\n },\n {\n rule: 'credential-exfil',\n severity: 'HIGH',\n pattern: /\\b(curl|wget|fetch)\\b[^\\n]*(--data|--data-binary|-F|-d)\\b[^\\n]*(\\.ssh|\\.aws|\\.env|credentials|id_rsa)/i,\n message: 'uploads credential/secret file contents to a remote endpoint',\n },\n {\n rule: 'dynamic-eval',\n severity: 'MEDIUM',\n pattern: /\\beval\\s*\\(|\\bexec\\s*\\(\\s*compile\\s*\\(|\\bnew\\s+Function\\s*\\(/,\n message: 'evaluates dynamically constructed code',\n },\n {\n rule: 'shell-true-subprocess',\n severity: 'MEDIUM',\n pattern: /subprocess\\.(Popen|run|call)\\([^)]*shell\\s*=\\s*True/,\n message: 'runs a subprocess with shell=True (shell-injection risk)',\n },\n {\n rule: 'persistence-cron-rc',\n severity: 'MEDIUM',\n pattern: />>\\s*~?\\/?\\.(bashrc|zshrc|bash_profile|profile)\\b|\\bcrontab\\s+-/i,\n message: 'modifies shell startup files or crontab (persistence)',\n },\n {\n rule: 'sudo-usage',\n severity: 'LOW',\n pattern: /\\bsudo\\s+/,\n message: 'invokes sudo — review for unnecessary privilege escalation',\n },\n];\n\nconst SKIP_DIRS = new Set(['.git', 'node_modules', 'dist', 'build', '__pycache__', '.venv', 'venv']);\nconst BINARY_EXT = new Set([\n '.png', '.jpg', '.jpeg', '.gif', '.webp', '.ico', '.pdf', '.zip', '.tar', '.gz',\n '.woff', '.woff2', '.ttf', '.eot', '.mp4', '.mp3', '.wasm', '.bin', '.exe',\n]);\nconst MAX_FILE_BYTES = 2 * 1024 * 1024; // 2MB — skills ship small text/scripts\n\nfunction* walk(dir: string): Generator<string> {\n let entries;\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue;\n yield* walk(full);\n } else if (entry.isFile()) {\n yield full;\n }\n }\n}\n\nfunction readTextFile(file: string): string | undefined {\n if (BINARY_EXT.has(extname(file).toLowerCase())) return undefined;\n try {\n if (statSync(file).size > MAX_FILE_BYTES) return undefined;\n const content = readFileSync(file, 'utf-8');\n if (content.slice(0, 512).includes('\\0')) return undefined; // crude binary sniff\n return content;\n } catch {\n return undefined;\n }\n}\n\nfunction scanStaticRules(targetPath: string): SafetyFinding[] {\n const findings: SafetyFinding[] = [];\n let root;\n try {\n root = statSync(targetPath);\n } catch {\n return findings;\n }\n const files = root.isDirectory() ? Array.from(walk(targetPath)) : [targetPath];\n\n for (const file of files) {\n const content = readTextFile(file);\n if (content === undefined) continue;\n const lines = content.split('\\n');\n for (const rule of DENY_RULES) {\n for (let i = 0; i < lines.length; i++) {\n if (rule.pattern.test(lines[i])) {\n findings.push({\n severity: rule.severity,\n rule: rule.rule,\n file,\n detail: `line ${i + 1}: ${rule.message}`,\n });\n }\n }\n }\n }\n return findings;\n}\n\n/** Locate an executable on PATH without shelling out (no `which`/`command -v` dependency). */\nfunction which(cmd: string): string | undefined {\n const pathEnv = process.env.PATH ?? '';\n const dirs = pathEnv.split(delimiter).filter(Boolean);\n const exts = process.platform === 'win32' ? (process.env.PATHEXT ?? '.EXE;.CMD;.BAT').split(';') : [''];\n for (const dir of dirs) {\n for (const ext of exts) {\n const candidate = join(dir, cmd + ext);\n try {\n if (statSync(candidate).isFile()) return candidate;\n } catch {\n // not found in this dir, keep looking\n }\n }\n }\n return undefined;\n}\n\ninterface SkillSpectorFinding {\n severity?: string;\n rule_id?: string;\n message?: string;\n}\n\ninterface SkillSpectorOutput {\n filtered_findings?: SkillSpectorFinding[];\n findings?: SkillSpectorFinding[];\n}\n\nfunction normalizeSeverity(sev: string | undefined): Severity {\n const s = (sev ?? '').toUpperCase();\n if (s === 'LOW' || s === 'MEDIUM' || s === 'HIGH' || s === 'CRITICAL') return s;\n return 'MEDIUM'; // unrecognized external severity: treat cautiously, don't silently trust as safe\n}\n\n/**\n * Mirrors skill_safety.py's `skillspector scan <target> --format json --no-llm`\n * invocation. Skipped cleanly (no findings, no error) when SkillSpector isn't\n * installed or its output can't be parsed — the built-in ruleset above is the\n * FREE-tier baseline; SkillSpector is additive when present.\n */\nasync function scanWithSkillSpector(targetPath: string): Promise<SafetyFinding[]> {\n const bin = which('skillspector');\n if (!bin) return [];\n\n try {\n const { stdout } = await execFileAsync(bin, ['scan', targetPath, '--format', 'json', '--no-llm'], {\n timeout: 60_000,\n maxBuffer: 10 * 1024 * 1024,\n });\n const parsed = JSON.parse(stdout) as SkillSpectorOutput;\n const raw = parsed.filtered_findings ?? parsed.findings ?? [];\n return raw.map((f) => ({\n severity: normalizeSeverity(f.severity),\n rule: f.rule_id ?? 'skillspector',\n file: targetPath,\n detail: f.message ?? '',\n }));\n } catch {\n return [];\n }\n}\n\nfunction verdictFor(findings: SafetyFinding[]): Verdict {\n if (findings.some((f) => BLOCK_SEVERITIES.has(f.severity))) return 'block';\n if (findings.length > 0) return 'warn';\n return 'pass';\n}\n\n/**\n * Scans a quarantined skill (file or directory) for unsafe patterns: the\n * built-in deny-pattern ruleset always runs, and an installed SkillSpector\n * (https://github.com/NVIDIA/SkillSpector) is shelled out to and merged in\n * when present on PATH. Blocks on any HIGH or CRITICAL finding.\n */\nexport async function scanSafety(targetPath: string): Promise<SafetyResult> {\n const findings = [...scanStaticRules(targetPath), ...(await scanWithSkillSpector(targetPath))];\n return { findings, verdict: verdictFor(findings) };\n}\n","import { existsSync, readFileSync, readdirSync, type Dirent } from 'node:fs';\nimport { basename, dirname, join, resolve } from 'node:path';\nimport { expandHome, isDir, stripQuotes } from '../util.js';\n\n/**\n * Overlap gate — ranks a candidate skill against an existing skill set.\n *\n * Direct TypeScript port of `overlap_scan.py` (rhize-meta/skills/rhize-skill-forge/scripts).\n * Heuristic, dependency-free. Blends Jaccard token overlap with keyword containment of the\n * candidate's distinctive phrases (weights, thresholds, tokenizer, and quoted-phrase bonus\n * all match the Python source exactly).\n */\n\nconst STOPWORDS = new Set(\n `\na an the and or of to for in on with use used using when whether this that these those is are be\nyour you it its as at by from into via per also any all not no than then so if up out via about\nskill skills user claude code based comprehensive enhanced practical including include includes\n`\n .split(/\\s+/)\n .filter(Boolean),\n);\n\nconst DEFAULT_SET_THRESHOLD = 0.45;\n\nexport interface SkillMeta {\n name: string;\n description: string;\n}\n\nexport interface SkillOverlapScore {\n skill: string;\n path: string;\n score: number;\n}\n\nexport interface OverlapResult {\n candidate: string;\n nearest: string;\n nearestScore: number;\n suggestedVerb: string;\n ranking: SkillOverlapScore[];\n}\n\nexport interface OverlapPair {\n a: string;\n b: string;\n score: number;\n}\n\n/** Mirrors Python's `str.split(sep, maxSplit)` — returns up to maxSplit+1 parts. */\nfunction splitLimited(text: string, sep: string, maxSplit: number): string[] {\n const parts: string[] = [];\n let rest = text;\n for (let i = 0; i < maxSplit; i++) {\n const idx = rest.indexOf(sep);\n if (idx === -1) break;\n parts.push(rest.slice(0, idx));\n rest = rest.slice(idx + sep.length);\n }\n parts.push(rest);\n return parts;\n}\n\n/** Reads a SKILL.md's `name`/`description` frontmatter, falling back to heading + body lead-in. */\nexport function readFrontmatter(skillMdPath: string): SkillMeta {\n const text = readFileSync(skillMdPath, 'utf-8');\n const meta: SkillMeta = { name: basename(dirname(skillMdPath)), description: '' };\n\n if (text.startsWith('---\\n')) {\n const block = splitLimited(text, '---', 2)[1] ?? '';\n\n const nameMatch = block.match(/^name:\\s*(.+)$/m);\n if (nameMatch) {\n meta.name = stripQuotes(nameMatch[1].trim());\n }\n\n const descMatch = block.match(/^description:\\s*(.*)$/m);\n if (descMatch) {\n const first = descMatch[1].trim();\n if (['>', '>-', '|', '|-', ''].includes(first)) {\n const lines = block.split(/\\r\\n|\\r|\\n/);\n const idx = lines.findIndex((l) => /^description:/.test(l));\n const buf: string[] = [];\n if (idx !== -1) {\n for (const l of lines.slice(idx + 1)) {\n if (/^\\s+\\S/.test(l)) {\n buf.push(l.trim());\n } else if (l.trim() === '') {\n continue;\n } else {\n break;\n }\n }\n }\n meta.description = buf.join(' ');\n } else {\n meta.description = stripQuotes(first);\n }\n }\n } else {\n const heads = Array.from(text.matchAll(/^#\\s+(.+)$/gm)).map((m) => m[1]);\n meta.description = `${heads[0] ?? ''} ${text.slice(0, 400)}`;\n }\n\n return meta;\n}\n\nfunction tokenize(text: string): Set<string> {\n const matches = text.toLowerCase().match(/[a-z][a-z0-9+.-]{2,}/g) ?? [];\n return new Set(matches.filter((t) => !STOPWORDS.has(t)));\n}\n\n/**\n * Distinctive quoted trigger phrases and salient words from a description.\n * Ported for parity with the source script; not used by score() (which extracts phrases inline).\n */\nexport function keyphrases(text: string): Set<string> {\n const quoted = new Set<string>();\n for (const m of text.matchAll(/\"([^\"]{2,40})\"/g)) quoted.add(m[1].toLowerCase().trim());\n for (const m of text.matchAll(/'([^']{2,40})'/g)) quoted.add(m[1].toLowerCase().trim());\n return quoted.size > 0 ? quoted : tokenize(text);\n}\n\nfunction intersectSize(a: Set<string>, b: Set<string>): number {\n let n = 0;\n for (const x of a) if (b.has(x)) n += 1;\n return n;\n}\n\nfunction unionSize(a: Set<string>, b: Set<string>): number {\n const u = new Set(a);\n for (const x of b) u.add(x);\n return u.size;\n}\n\nfunction isSubset(sub: Set<string>, sup: Set<string>): boolean {\n for (const x of sub) if (!sup.has(x)) return false;\n return true;\n}\n\nfunction round3(n: number): number {\n return Math.round(n * 1000) / 1000;\n}\n\nfunction score(cand: SkillMeta, other: SkillMeta): number {\n const a = tokenize(`${cand.name} ${cand.description}`);\n const b = tokenize(`${other.name} ${other.description}`);\n if (a.size === 0) return 0.0;\n\n const inter = intersectSize(a, b);\n const union = unionSize(a, b);\n const jaccard = union ? inter / union : 0.0;\n const containment = inter / a.size;\n\n const phrases = new Set<string>();\n for (const m of cand.description.matchAll(/\"([^\"]{2,40})\"/g)) phrases.add(m[1]);\n for (const m of cand.description.matchAll(/'([^']{2,40})'/g)) phrases.add(m[1]);\n\n let pscore = 0.0;\n if (phrases.size > 0) {\n let hits = 0;\n for (const p of phrases) {\n const pw = tokenize(p);\n if (pw.size > 0 && isSubset(pw, b)) hits += 1;\n }\n pscore = hits / phrases.size;\n }\n\n return round3(0.3 * jaccard + 0.5 * containment + 0.2 * pscore);\n}\n\n/** Score tiers behind suggestVerb/suggestVerbCode — one table so prose and bare verb can't drift. */\nconst VERB_TIERS = [\n { min: 0.45, verb: 'ABSORB', prose: 'ABSORB (or REJECT if ours is already better)' },\n { min: 0.2, verb: 'FORK', prose: 'FORK (re-skin), or ABSORB one piece' },\n { min: -Infinity, verb: 'DEFER', prose: 'DEFER / new FORK (little overlap)' },\n] as const;\n\nfunction verbTier(topScore: number): (typeof VERB_TIERS)[number] {\n return VERB_TIERS.find((t) => topScore >= t.min) ?? VERB_TIERS[VERB_TIERS.length - 1];\n}\n\nexport function suggestVerb(topScore: number): string {\n return verbTier(topScore).prose;\n}\n\n/** Bare verb of the tier suggestVerb would pick — for callers that store an enum, not prose (e.g. the pending queue). */\nexport function suggestVerbCode(topScore: number): (typeof VERB_TIERS)[number]['verb'] {\n return verbTier(topScore).verb;\n}\n\nfunction listSubdirs(dir: string): string[] {\n if (!existsSync(dir)) return [];\n let entries: Dirent[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n return entries.filter((e) => e.isDirectory()).map((e) => join(dir, e.name));\n}\n\n/**\n * Finds SKILL.md files matching the same three glob shapes as the Python script:\n * \"<dir>/SKILL.md\", \"skills/<dir>/SKILL.md\", \"<dir>/skills/<dir>/SKILL.md\" (one wildcard\n * <dir> level each) — deduped, .git excluded, sorted.\n */\nfunction findSkillMdFiles(root: string): string[] {\n const found = new Set<string>();\n\n for (const dir of listSubdirs(root)) {\n const candidate = join(dir, 'SKILL.md');\n if (existsSync(candidate)) found.add(resolve(candidate));\n }\n\n for (const dir of listSubdirs(join(root, 'skills'))) {\n const candidate = join(dir, 'SKILL.md');\n if (existsSync(candidate)) found.add(resolve(candidate));\n }\n\n for (const topDir of listSubdirs(root)) {\n for (const dir of listSubdirs(join(topDir, 'skills'))) {\n const candidate = join(dir, 'SKILL.md');\n if (existsSync(candidate)) found.add(resolve(candidate));\n }\n }\n\n return Array.from(found)\n .filter((p) => !p.includes('/.git/'))\n .sort();\n}\n\n/**\n * Ranks a single candidate skill against every skill found under `skillsRoot`.\n * Throws if `skillsRoot` isn't a directory, the candidate's SKILL.md can't be found,\n * or no existing skills are found under the root (mirrors the Python script's `fail()` exits).\n */\nexport function scanOverlap(candidatePath: string, skillsRoot: string): OverlapResult {\n const root = resolve(expandHome(skillsRoot));\n if (!isDir(root)) {\n throw new Error(`skills-root not a directory: ${root}`);\n }\n\n let cpath = resolve(expandHome(candidatePath));\n if (isDir(cpath)) {\n cpath = join(cpath, 'SKILL.md');\n }\n if (!existsSync(cpath)) {\n throw new Error(`candidate SKILL.md not found: ${cpath}`);\n }\n\n const cand = readFrontmatter(cpath);\n\n const existing = findSkillMdFiles(root).filter((p) => p !== resolve(cpath));\n if (existing.length === 0) {\n throw new Error(`no existing skills found under ${root}`);\n }\n\n const ranking: SkillOverlapScore[] = existing.map((p) => {\n const other = readFrontmatter(p);\n return { skill: other.name, path: p, score: score(cand, other) };\n });\n ranking.sort((a, b) => b.score - a.score);\n\n const top = ranking[0];\n return {\n candidate: cand.name,\n nearest: top.skill,\n nearestScore: top.score,\n suggestedVerb: suggestVerb(top.score),\n ranking,\n };\n}\n\n/**\n * N-way: pairwise overlap across the whole installed set; reports pairs scoring >= threshold.\n * Symmetric — the pair score is max(score(a,b), score(b,a)) since score() is candidate-centric.\n */\nexport function scanOverlapSet(\n skillsRoot: string,\n threshold: number = DEFAULT_SET_THRESHOLD,\n): OverlapPair[] {\n const root = resolve(expandHome(skillsRoot));\n if (!isDir(root)) {\n throw new Error(`skills-root not a directory: ${root}`);\n }\n\n const paths = findSkillMdFiles(root);\n const metas = paths.map(readFrontmatter);\n\n const pairs: OverlapPair[] = [];\n for (let i = 0; i < metas.length; i++) {\n for (let j = i + 1; j < metas.length; j++) {\n const sc = Math.max(score(metas[i], metas[j]), score(metas[j], metas[i]));\n if (sc >= threshold) {\n pairs.push({ a: metas[i].name, b: metas[j].name, score: round3(sc) });\n }\n }\n }\n pairs.sort((a, b) => b.score - a.score);\n return pairs;\n}\n","import { profileSkill, type SkillProfile } from '../gate/profile.js';\nimport { scanSafety, type SafetyResult as GateSafetyResult } from '../gate/safety.js';\nimport { scanOverlap, type OverlapResult } from '../gate/overlap.js';\nimport { hasProLicense } from '../license.js';\nimport type { GateResult, SafetyResult as ReportSafetyResult } from '../report/render.js';\n\n/**\n * Shared profile -> safety -> overlap pipeline used by both `add` and `scan`.\n * Bridges gate/safety.ts's SafetyResult (verdict: pass|warn|block, findings\n * with file/detail) into report/render.ts's SafetyResult shape (verdict:\n * ALLOW|CAUTION|BLOCK, findings with message) so both commands render through\n * the same Step-3 report box.\n */\n\nconst SAFETY_VERDICT_MAP: Record<GateSafetyResult['verdict'], ReportSafetyResult['verdict']> = {\n pass: 'ALLOW',\n warn: 'CAUTION',\n block: 'BLOCK',\n};\n\nexport interface GateRunResult {\n /** Gate-level safety result (verdict: pass|warn|block) — drives the promote/reject decision. */\n safety: GateSafetyResult;\n /** Report input; also carries the profile and overlap produced by the gate. */\n gateResult: GateResult;\n /** Whether a valid Pro license was found — checked once here so callers don't re-verify. */\n pro: boolean;\n}\n\nfunction asSkillProfile(candidatePath: string): SkillProfile {\n const profile = profileSkill(candidatePath);\n if (profile.kind !== 'skill') {\n throw new Error(`expected a skill candidate at ${candidatePath}, found a ${profile.kind}`);\n }\n return profile;\n}\n\n/** Best-effort overlap: a missing/empty skills root is reported to stderr, not fatal to the gate. */\nfunction tryOverlap(candidatePath: string, skillsRoot: string): OverlapResult | undefined {\n try {\n return scanOverlap(candidatePath, skillsRoot);\n } catch (err) {\n console.error(`[skill-forge] overlap scan skipped: ${(err as Error).message}`);\n return undefined;\n }\n}\n\n/**\n * Runs profile -> safety -> overlap for a single quarantined candidate and\n * builds the report input. Overlap analysis is a Pro feature: without a\n * valid license, it's skipped entirely (not just hidden), and the report\n * renders a short upgrade notice in its place (see `overlapLocked`).\n */\nexport async function runGate(candidatePath: string, skillsRoot: string): Promise<GateRunResult> {\n const profile = asSkillProfile(candidatePath);\n const safety = await scanSafety(candidatePath);\n const pro = hasProLicense();\n const overlap = pro ? tryOverlap(candidatePath, skillsRoot) : undefined;\n\n const gateResult: GateResult = {\n profile,\n overlap,\n overlapLocked: !pro,\n safety: {\n verdict: SAFETY_VERDICT_MAP[safety.verdict],\n findings: safety.findings.map((f) => ({\n severity: f.severity,\n rule: f.rule,\n message: `${f.file}: ${f.detail}`,\n })),\n },\n };\n\n return { safety, gateResult, pro };\n}\n","import { randomUUID } from 'node:crypto';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { getHomeDir } from './config.js';\nimport type { Verdict as SafetyVerdict } from './gate/safety.js';\nimport type { SourceType } from './install/resolve.js';\nimport type { ForgeVerb } from './provenance.js';\n\n/**\n * Pending-ingestion queue — persists every skill install decided by `add`\n * (promote or hold) to `<SKILL_FORGE_HOME>/queue.json`, so the `--ingest`\n * Claude handoff (or a future `queue` command) can pick the entry back up.\n * The entry shape here, `schemas/queue-entry.schema.json`, and\n * `docs/queue-schema.md` are the single source of truth for the pending-queue\n * entry spec — keep all three in sync if the shape ever changes.\n */\n\n/** Suggested forge verb on a queue entry — the five-verb matrix defined by provenance.ts's FORGE_VERBS. */\nexport type SuggestedVerb = ForgeVerb;\nexport type { SafetyVerdict, SourceType };\n\nexport type QueueStatus = 'pending' | 'ingested' | 'dismissed';\n\nexport interface OverlapTopEntry {\n skill: string;\n score: number;\n}\n\nexport interface QueueEntryGate {\n license: string | null;\n safetyVerdict: SafetyVerdict;\n safetyFindings: string[];\n /** Top overlap matches against the configured skill set; empty on the free tier. */\n overlapTop: OverlapTopEntry[];\n suggestedVerb: SuggestedVerb | null;\n}\n\nexport interface QueueEntry {\n id: string;\n source: string;\n sourceType: SourceType;\n /** Final path after promote; null while the candidate is only held in quarantine. */\n installedPath: string | null;\n quarantinePath: string;\n gate: QueueEntryGate;\n status: QueueStatus;\n createdAt: string;\n}\n\nexport interface Queue {\n version: 1;\n entries: QueueEntry[];\n}\n\n/** Input for a new entry — `id` and `createdAt` are generated by `appendQueueEntry`. */\nexport type NewQueueEntry = Omit<QueueEntry, 'id' | 'createdAt'>;\n\nexport function getQueuePath(): string {\n return join(getHomeDir(), 'queue.json');\n}\n\n/** Reads the queue from disk, defaulting to an empty queue when the file doesn't exist yet. */\nexport function readQueue(): Queue {\n const path = getQueuePath();\n if (!existsSync(path)) {\n return { version: 1, entries: [] };\n }\n const raw = readFileSync(path, 'utf-8');\n const parsed = JSON.parse(raw) as Partial<Queue>;\n return { version: 1, entries: parsed.entries ?? [] };\n}\n\nfunction writeQueue(queue: Queue): void {\n mkdirSync(getHomeDir(), { recursive: true }); // recursive mkdir is a no-op when the dir exists\n writeFileSync(getQueuePath(), JSON.stringify(queue, null, 2) + '\\n', 'utf-8');\n}\n\n/** Appends a new entry (generating `id`/`createdAt`) and persists the queue. Returns the full entry. */\nexport function appendQueueEntry(entry: NewQueueEntry): QueueEntry {\n const queue = readQueue();\n const existingIds = new Set(queue.entries.map((e) => e.id));\n const full: QueueEntry = {\n ...entry,\n id: generateId(existingIds),\n createdAt: new Date().toISOString(),\n };\n queue.entries.push(full);\n writeQueue(queue);\n return full;\n}\n\n/** Applies a partial update to the entry with `id` (e.g. a status transition) and persists the queue. */\nexport function updateQueueEntry(id: string, patch: Partial<Omit<QueueEntry, 'id' | 'createdAt'>>): QueueEntry {\n const queue = readQueue();\n const index = queue.entries.findIndex((e) => e.id === id);\n if (index === -1) {\n throw new Error(`no queue entry with id: ${id}`);\n }\n const updated: QueueEntry = { ...queue.entries[index], ...patch };\n queue.entries[index] = updated;\n writeQueue(queue);\n return updated;\n}\n\n/** Short unique slug (8 hex chars from a UUID), collision-checked against ids already in the queue. */\nfunction generateId(existingIds: Set<string>): string {\n let id: string;\n do {\n id = randomUUID().split('-')[0];\n } while (existingIds.has(id));\n return id;\n}\n","import { loadConfig } from '../config.js';\nimport { resolve as resolveSource } from '../install/resolve.js';\nimport { quarantine } from '../install/quarantine.js';\nimport { reject } from '../install/promote.js';\nimport { renderReport } from '../report/render.js';\nimport { runGate } from './gatePipeline.js';\n\n/**\n * `skill-forge scan <source>` — run the safety gate against a skill without\n * installing it into a target skills root: quarantine-install for\n * inspection, gate + report, then always clean up the quarantine sandbox\n * regardless of verdict. Exits nonzero when the safety verdict is \"block\".\n */\nexport async function scan(source: string): Promise<void> {\n const config = loadConfig();\n const skillsRoot = config.skillsRoots[0];\n if (!skillsRoot) {\n throw new Error('no skills root configured; set skillsRoots in the config');\n }\n\n const resolved = resolveSource(source);\n const entry = await quarantine(resolved, config.quarantineDir);\n\n try {\n if (entry.skillDirs.length === 0) {\n console.error(`[skill-forge] no SKILL.md found in installed source: ${source}`);\n process.exitCode = 1;\n return;\n }\n if (entry.skillDirs.length > 1) {\n console.error(\n `[skill-forge] ${entry.skillDirs.length} skills found in ${source}; scanning the first:\\n` +\n entry.skillDirs.map((d) => ` - ${d}`).join('\\n')\n );\n }\n\n const { safety, gateResult } = await runGate(entry.skillDirs[0], skillsRoot);\n console.log(renderReport(gateResult));\n\n if (safety.verdict === 'block') {\n process.exitCode = 1;\n }\n } finally {\n reject(entry);\n }\n}\n","import { join } from 'node:path';\nimport { loadConfig } from '../config.js';\nimport { findSkillDirs, listQuarantineIds } from '../install/quarantine.js';\nimport { readFrontmatter } from '../gate/overlap.js';\n\n/**\n * `skill-forge list` — list skills currently held in quarantine (installed\n * but not yet promoted or rejected via `add`).\n */\nexport async function list(): Promise<void> {\n const config = loadConfig();\n const { quarantineDir } = config;\n\n const ids = listQuarantineIds(quarantineDir);\n\n if (ids.length === 0) {\n console.log(`[skill-forge] quarantine dir: ${quarantineDir}`);\n console.log(' (empty — nothing held)');\n return;\n }\n\n console.log(`[skill-forge] ${ids.length} held in quarantine (${quarantineDir}):`);\n for (const id of ids) {\n const skillDirs = findSkillDirs(join(quarantineDir, id));\n if (skillDirs.length === 0) {\n console.log(` ${id} (no SKILL.md found)`);\n continue;\n }\n const names = skillDirs.map((dir) => {\n try {\n return readFrontmatter(join(dir, 'SKILL.md')).name;\n } catch {\n return dir;\n }\n });\n console.log(` ${id} -> ${names.join(', ')}`);\n }\n}\n","import { loadConfig } from '../config.js';\nimport { listQuarantineIds } from '../install/quarantine.js';\n\n/**\n * `skill-forge status` — show current configuration and a summary of what's\n * held in quarantine.\n */\nexport async function status(): Promise<void> {\n const config = loadConfig();\n const heldCount = listQuarantineIds(config.quarantineDir).length;\n\n console.log('[skill-forge] status');\n console.log(` skillsRoots: ${config.skillsRoots.join(', ')}`);\n console.log(` quarantineDir: ${config.quarantineDir}`);\n console.log(` strictness: ${config.strictness}`);\n console.log(` held in quarantine: ${heldCount}`);\n}\n"],"mappings":"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","names":["existsSync","existsSync","mkdirSync","join","walk","reject","cpSync","existsSync","mkdirSync","basename","join","existsSync","writeFileSync","join","existsSync","readFileSync","readdirSync","rmSync","statSync","basename","join","resolve","statSync","homedir","join","resolve","existsSync","statSync","join","basename","rmSync","readdirSync","readFileSync","m","readFileSync","readdirSync","statSync","extname","join","SKIP_DIRS","existsSync","readFileSync","readdirSync","basename","dirname","join","resolve","readFileSync","basename","dirname","existsSync","readdirSync","join","resolve","randomUUID","existsSync","mkdirSync","readFileSync","writeFileSync","join","join","existsSync","readFileSync","mkdirSync","writeFileSync","randomUUID","join","join","require"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/commands/add.ts","../src/config.ts","../src/install/resolve.ts","../src/install/quarantine.ts","../src/errors.ts","../src/install/promote.ts","../src/provenance.ts","../src/gate/profile.ts","../src/util.ts","../src/gate/safety.ts","../src/gate/overlap.ts","../src/license.ts","../src/report/render.ts","../src/commands/gatePipeline.ts","../src/queue.ts","../src/handoff.ts","../src/commands/init.ts","../src/agents.ts","../src/commands/scan.ts","../src/commands/list.ts","../src/commands/status.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { createRequire } from 'node:module';\nimport { add } from './commands/add.js';\nimport { scan } from './commands/scan.js';\nimport { list } from './commands/list.js';\nimport { status } from './commands/status.js';\nimport { init } from './commands/init.js';\nimport { UserError } from './errors.js';\n\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json') as { version: string };\n\nconst program = new Command();\n\nprogram\n .name('skill-forge')\n .description('The supply-chain gate for agent skills')\n .version(pkg.version);\n\nprogram\n .command('add <source>')\n .description('Quarantine-install a skill and run it through the safety gate')\n .option('-y, --yes', 'skip the interactive prompt; honor the gate verdict automatically')\n .option('-t, --target <dir>', 'skills root to promote into (defaults to config skillsRoots[0])')\n .option('--json', 'print the gate result as JSON instead of the terminal report')\n .option('--ingest', 'after a successful promote, hand off the bundled ingest prompt to a coding agent')\n .action(add);\n\nprogram\n .command('scan <source>')\n .description('Run the safety gate against a skill without installing it')\n .option('--json', 'print the gate result as JSON instead of the terminal report')\n .action(scan);\n\nprogram\n .command('list')\n .description('List skills currently in quarantine')\n .action(list);\n\nprogram\n .command('status')\n .description('Show skill-forge configuration and quarantine status')\n .action(status);\n\nprogram\n .command('init')\n .description('Detect installed agents and set gate targets, default promotion target, and handoff agent')\n .option('--defaults', 'non-interactive: use all detected agents as gate targets, the first as the default target')\n .option('--list', 'print detected agent skill roots and exit — no writes')\n .action(init);\n\ntry {\n await program.parseAsync();\n} catch (err) {\n if (err instanceof UserError) {\n console.error(`skill-forge: ${err.message}`);\n process.exit(1);\n }\n\n if (process.env.DEBUG === 'skill-forge') {\n console.error(err);\n } else {\n const message = err instanceof Error ? err.message : String(err);\n console.error(`skill-forge: unexpected error: ${message}`);\n console.error('Re-run with DEBUG=skill-forge to see the full stack trace.');\n }\n process.exit(1);\n}\n","import { createInterface } from 'node:readline/promises';\nimport { loadConfig } from '../config.js';\nimport { resolve as resolveSource } from '../install/resolve.js';\nimport { quarantine } from '../install/quarantine.js';\nimport { promote, reject, hold } from '../install/promote.js';\nimport { appendProvenance } from '../provenance.js';\nimport { printGateOutput, runGate } from './gatePipeline.js';\nimport type { Verdict } from '../gate/safety.js';\nimport { suggestVerbCode } from '../gate/overlap.js';\nimport { appendQueueEntry } from '../queue.js';\nimport { runHandoff } from '../handoff.js';\nimport { maybeOfferInit } from './init.js';\n\nconst PRO_FEATURE_LABEL = 'overlap analysis, the provenance ledger, and the pending-ingestion queue';\n\nexport interface AddOptions {\n yes?: boolean;\n target?: string;\n json?: boolean;\n ingest?: boolean;\n}\n\ntype Decision = 'promote' | 'hold' | 'reject';\n\n/** Prints a decision-outcome line: stdout normally, stderr in --json mode so stdout stays a single parseable JSON payload. */\nfunction logOutcome(message: string, json: boolean | undefined): void {\n if (json) console.error(message);\n else console.log(message);\n}\n\n/**\n * `skill-forge add <source>` — quarantine-install a skill and run it through\n * the safety gate (profile -> safety scan -> overlap analysis -> report ->\n * promote/hold/reject). `--yes` skips the interactive prompt and honors the\n * gate verdict; otherwise the decision is asked interactively.\n */\nexport async function add(source: string, options: AddOptions = {}): Promise<void> {\n await maybeOfferInit({ json: options.json, yes: options.yes });\n\n const config = loadConfig();\n const targetRoot = options.target ?? config.defaultTarget ?? config.skillsRoots[0];\n if (!targetRoot) {\n throw new Error('no target skills root configured; pass --target <dir> or set skillsRoots in the config');\n }\n\n const resolved = resolveSource(source);\n const entry = await quarantine(resolved, config.quarantineDir);\n\n if (entry.skillDirs.length === 0) {\n console.error(`[skill-forge] no SKILL.md found in installed source: ${source}`);\n reject(entry);\n process.exitCode = 1;\n return;\n }\n if (entry.skillDirs.length > 1) {\n console.error(\n `[skill-forge] ${entry.skillDirs.length} skills found in ${source}; gating the first — the whole ` +\n 'quarantine entry is promoted/rejected together:\\n' +\n entry.skillDirs.map((d) => ` - ${d}`).join('\\n')\n );\n }\n\n const { safety, gateResult, proEnabled } = await runGate(entry.skillDirs[0], targetRoot, PRO_FEATURE_LABEL);\n const { profile, overlap } = gateResult;\n\n printGateOutput(gateResult, options.json);\n\n // --json implies non-interactive: honor the gate verdict the same way --yes does, never prompt.\n const decision = options.yes || options.json ? yesDecision(safety.verdict) : await promptDecision(safety.verdict);\n\n let installedPath: string | null = null;\n\n if (decision === 'promote') {\n const result = promote(entry, targetRoot);\n logOutcome(`[skill-forge] promoted -> ${result.promoted.join(', ')}`, options.json);\n // Free-tier `add` makes no absorb/fork judgment — it either installs the\n // candidate as-is or it doesn't. DEFER is the matching verb from the\n // rhize-skill-forge five-verb matrix (\"adopt as-is; just install/keep it\").\n // Provenance recording is Pro — see the notice printed above the report.\n if (proEnabled) {\n const ledgerPath = appendProvenance(\n {\n name: profile.name ?? entry.id,\n source,\n version: profile.version,\n license: profile.license,\n verb: 'DEFER',\n target: targetRoot,\n took: `installed as-is (${result.promoted.length} skill dir${result.promoted.length === 1 ? '' : 's'})`,\n notes:\n `gate: safety=${safety.verdict}` +\n (overlap ? `, overlap nearest=${overlap.nearest} (${overlap.nearestScore})` : ''),\n },\n targetRoot\n );\n logOutcome(`[skill-forge] provenance recorded -> ${ledgerPath}`, options.json);\n }\n installedPath = result.promoted[0] ?? null;\n } else if (decision === 'reject') {\n reject(entry);\n logOutcome('[skill-forge] rejected — quarantine sandbox removed', options.json);\n if (safety.verdict === 'block') process.exitCode = 1;\n return; // rejected candidates are gone — nothing to queue for ingestion\n } else {\n hold(entry);\n logOutcome(`[skill-forge] held in quarantine -> ${entry.quarantinePath}`, options.json);\n }\n\n // Pending-ingestion queue and --ingest handoff are Pro; free `add` stops\n // at promote/hold, which is the complete free-tier flow. (Unlicensed/locked\n // case is covered by the notice printed above the report, not here.)\n if (proEnabled) {\n // The queue entry records the gate result at decision time so a later\n // ingest pass doesn't need to re-run it.\n appendQueueEntry({\n source,\n sourceType: resolved.type,\n installedPath,\n quarantinePath: entry.quarantinePath,\n gate: {\n license: profile.license ?? null,\n safetyVerdict: safety.verdict,\n safetyFindings: safety.findings.map((f) => `${f.severity} [${f.rule}] ${f.file}: ${f.detail}`),\n overlapTop: overlap ? overlap.ranking.slice(0, 3).map((r) => ({ skill: r.skill, score: r.score })) : [],\n suggestedVerb: overlap ? suggestVerbCode(overlap.nearestScore) : null,\n },\n status: 'pending',\n });\n\n if (options.ingest && installedPath) {\n // Runs the bundled agent-neutral ingest prompt (assets/ingest-prompt.md) via the\n // configured handoffCommand template, or a detected-agent fallback. See src/handoff.ts.\n runHandoff(config, installedPath);\n }\n }\n}\n\n/** --yes honors the gate verdict: a block is rejected (nonzero exit); pass/warn is promoted. */\nfunction yesDecision(verdict: Verdict): Decision {\n return verdict === 'block' ? 'reject' : 'promote';\n}\n\nasync function promptDecision(verdict: Verdict): Promise<Decision> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n const defaultChoice: Decision = verdict === 'block' ? 'reject' : verdict === 'warn' ? 'hold' : 'promote';\n const prompt = `Promote, hold, or reject this skill? [promote/hold/reject] (default: ${defaultChoice}) `;\n try {\n // Iterate lines via the async-iterator protocol rather than looping on\n // rl.question(): question() attaches a one-shot 'line' listener per call,\n // and on piped (non-TTY) input where several answer lines arrive in a\n // single chunk, a line can be emitted — and silently dropped — between\n // one question() resolving and the next one attaching its listener. The\n // async iterator queues lines internally, so no answer is ever lost.\n process.stdout.write(prompt);\n for await (const raw of rl) {\n const answer = raw.trim().toLowerCase() || defaultChoice;\n if (answer === 'p' || answer === 'promote') return 'promote';\n if (answer === 'h' || answer === 'hold') return 'hold';\n if (answer === 'r' || answer === 'reject') return 'reject';\n process.stdout.write(`Please answer promote, hold, or reject (or p/h/r).\\n${prompt}`);\n }\n // stdin closed without a valid answer — fail safe to the verdict's default.\n return defaultChoice;\n } finally {\n rl.close();\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport type Strictness = 'block-high' | 'block-medium' | 'warn-only';\n\nexport interface Config {\n skillsRoots: string[];\n quarantineDir: string;\n strictness: Strictness;\n /** Pro license key (skf1.<payload>.<signature>). SKILL_FORGE_LICENSE env takes precedence — see src/license.ts. */\n licenseKey?: string;\n /** Default skills root to promote into when `add`'s `--target` is omitted. Set by `skill-forge init`. */\n defaultTarget?: string;\n /** Agent-to-skillsRoot mapping recorded by `skill-forge init` (see src/agents.ts for the agent matrix). */\n agents?: { id: string; skillsRoot: string }[];\n /** Argv-style template for handing a prompt to the chosen handoff agent, e.g. `['claude', '-p', '{prompt}']`. Set by `skill-forge init`. See src/handoff.ts. */\n handoffCommand?: string[];\n}\n\n/**\n * Resolves the skill-forge home directory. Honors SKILL_FORGE_HOME so tests\n * (and users) can redirect config/quarantine storage away from ~/.skill-forge.\n */\nexport function getHomeDir(): string {\n return process.env.SKILL_FORGE_HOME ?? join(homedir(), '.skill-forge');\n}\n\nexport function getConfigPath(): string {\n return join(getHomeDir(), 'config.json');\n}\n\nexport function defaultConfig(): Config {\n const home = getHomeDir();\n return {\n skillsRoots: [join(process.cwd(), '.claude', 'skills')],\n quarantineDir: join(home, 'quarantine'),\n strictness: 'block-high',\n };\n}\n\n/**\n * Loads config from disk, falling back to defaults for any missing keys.\n * Does not write anything to disk.\n */\nexport function loadConfig(): Config {\n const configPath = getConfigPath();\n const defaults = defaultConfig();\n\n if (!existsSync(configPath)) {\n return defaults;\n }\n\n const raw = readFileSync(configPath, 'utf-8');\n const parsed = JSON.parse(raw) as Partial<Config>;\n\n return {\n skillsRoots: parsed.skillsRoots ?? defaults.skillsRoots,\n quarantineDir: parsed.quarantineDir ?? defaults.quarantineDir,\n strictness: parsed.strictness ?? defaults.strictness,\n licenseKey: parsed.licenseKey ?? defaults.licenseKey,\n defaultTarget: parsed.defaultTarget ?? defaults.defaultTarget,\n agents: parsed.agents ?? defaults.agents,\n handoffCommand: parsed.handoffCommand ?? defaults.handoffCommand,\n };\n}\n\nexport function saveConfig(config: Config): void {\n const home = getHomeDir();\n if (!existsSync(home)) {\n mkdirSync(home, { recursive: true });\n }\n writeFileSync(getConfigPath(), JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n","import { existsSync } from 'node:fs';\n\nexport type SourceType = 'skills.sh' | 'git' | 'local';\n\nexport interface ResolvedSource {\n type: SourceType;\n value: string;\n}\n\nconst GIT_URL_PATTERN = /^(git@|https?:\\/\\/|ssh:\\/\\/)/;\n// Unambiguous filesystem-path prefixes: no skills.sh `owner/name` slug starts\n// with any of these, so a missing path here is a friendly \"not found\" error\n// rather than a slow (network-dependent) skills.sh install attempt.\nconst LOCAL_PATH_PATTERN = /^(\\.{1,2}\\/|\\/|~\\/|~$)/;\n\n/**\n * Classifies a skill source string so the installer knows which quarantine\n * strategy to use:\n * - an http(s)/git@/ssh:// URL, or one ending in `.git`, is a git remote\n * - an existing filesystem path is a local skill\n * - a nonexistent path that's unambiguously path-shaped (./, ../, /, ~/) is\n * still local, so it fails fast with a friendly error instead of falling\n * through to skills.sh\n * - everything else is treated as a skills.sh `owner/name` slug\n */\nexport function resolve(source: string): ResolvedSource {\n const value = source.trim();\n\n if (GIT_URL_PATTERN.test(value) || value.endsWith('.git')) {\n return { type: 'git', value };\n }\n\n if (existsSync(value) || LOCAL_PATH_PATTERN.test(value)) {\n return { type: 'local', value };\n }\n\n return { type: 'skills.sh', value };\n}\n","import { randomUUID } from 'node:crypto';\nimport { spawn } from 'node:child_process';\nimport { cpSync, existsSync, mkdirSync, readdirSync } from 'node:fs';\nimport type { Dirent } from 'node:fs';\nimport { basename, join, resolve as resolvePath } from 'node:path';\nimport type { ResolvedSource } from './resolve.js';\nimport { UserError } from '../errors.js';\n\nexport interface ExecResult {\n stdout: string;\n stderr: string;\n code: number;\n}\n\nexport type ExecFn = (\n command: string,\n args: string[],\n options: { cwd: string }\n) => Promise<ExecResult>;\n\nexport interface QuarantineEntry {\n id: string;\n quarantinePath: string;\n skillDirs: string[];\n source: ResolvedSource;\n}\n\nexport interface QuarantineDeps {\n exec?: ExecFn;\n}\n\nconst SKILL_MANIFEST = 'SKILL.md';\nconst SKIP_DIRS = new Set(['.git', 'node_modules']);\n\n/**\n * Installs `source` into `<quarantineDir>/<id>/` — never into a target\n * skills root. skills.sh sources run `npx -y skills@latest add <slug>`\n * with cwd pinned to the sandbox; git sources run a shallow clone into a\n * named subdir of the sandbox; local sources are recursively copied in.\n * Returns the sandbox id/path plus every directory inside it that looks\n * like an installed skill (contains SKILL.md).\n */\nexport async function quarantine(\n source: ResolvedSource,\n quarantineDir: string,\n deps: QuarantineDeps = {}\n): Promise<QuarantineEntry> {\n const exec = deps.exec ?? defaultExec;\n const id = randomUUID();\n const quarantinePath = join(quarantineDir, id);\n mkdirSync(quarantinePath, { recursive: true });\n\n switch (source.type) {\n case 'skills.sh': {\n const result = await exec('npx', ['-y', 'skills@latest', 'add', source.value], {\n cwd: quarantinePath,\n });\n if (result.code !== 0) {\n throw new Error(\n `skills.sh install failed for \"${source.value}\" (exit ${result.code}): ${result.stderr || result.stdout}`\n );\n }\n break;\n }\n case 'git': {\n const clonePath = join(quarantinePath, deriveGitDirName(source.value));\n const result = await exec('git', ['clone', '--depth', '1', source.value, clonePath], {\n cwd: quarantinePath,\n });\n if (result.code !== 0) {\n throw new UserError(\n `git clone failed for \"${source.value}\" (exit ${result.code}): ${result.stderr || result.stdout}`\n );\n }\n break;\n }\n case 'local': {\n if (!existsSync(source.value)) {\n throw new UserError(`local path not found: ${source.value}`);\n }\n const dirName = basename(resolvePath(source.value));\n cpSync(source.value, join(quarantinePath, dirName), { recursive: true });\n break;\n }\n default: {\n throw new UserError(`unknown source type: ${(source as ResolvedSource).type}`);\n }\n }\n\n return { id, quarantinePath, skillDirs: findSkillDirs(quarantinePath), source };\n}\n\n/** IDs (sandbox dir names) of entries currently held in quarantine; [] when the dir doesn't exist. */\nexport function listQuarantineIds(quarantineDir: string): string[] {\n if (!existsSync(quarantineDir)) return [];\n return readdirSync(quarantineDir, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => e.name);\n}\n\n/** Recursively finds every directory under `root` that directly contains SKILL.md. */\nexport function findSkillDirs(root: string): string[] {\n const found: string[] = [];\n\n function walk(dir: string): void {\n let entries: Dirent[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n if (entries.some((entry) => entry.isFile() && entry.name === SKILL_MANIFEST)) {\n found.push(dir);\n }\n\n for (const entry of entries) {\n if (entry.isDirectory() && !SKIP_DIRS.has(entry.name)) {\n walk(join(dir, entry.name));\n }\n }\n }\n\n walk(root);\n return found;\n}\n\n/** Derives a filesystem-friendly dir name from a git URL (scp-like or http(s)). */\nfunction deriveGitDirName(url: string): string {\n const stripped = url.replace(/\\.git$/, '');\n const segments = stripped.split(/[/:]/).filter(Boolean);\n return segments[segments.length - 1] || 'repo';\n}\n\nfunction defaultExec(command: string, args: string[], options: { cwd: string }): Promise<ExecResult> {\n return new Promise((resolvePromise, reject) => {\n const child = spawn(command, args, { cwd: options.cwd });\n let stdout = '';\n let stderr = '';\n child.stdout?.on('data', (chunk: Buffer) => (stdout += chunk.toString()));\n child.stderr?.on('data', (chunk: Buffer) => (stderr += chunk.toString()));\n child.on('error', reject);\n child.on('close', (code) => resolvePromise({ stdout, stderr, code: code ?? 1 }));\n });\n}\n","/**\n * Friendly, expected-failure errors — bad input, unreachable sources, missing\n * config, etc. Thrown anywhere in the pipeline and caught once at the top\n * level (src/cli.ts), which prints just the message and exits 1 instead of a\n * raw stack trace. Anything that is NOT a UserError is treated as a bug: the\n * top-level handler prints a short message plus a hint to re-run with\n * DEBUG=skill-forge to see the full stack.\n */\nexport class UserError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'UserError';\n }\n}\n","import { cpSync, existsSync, mkdirSync, renameSync, rmSync } from 'node:fs';\nimport { basename, join } from 'node:path';\nimport type { QuarantineEntry } from './quarantine.js';\n\nexport interface PromoteOptions {\n force?: boolean;\n}\n\nexport interface PromoteResult {\n promoted: string[];\n}\n\n/**\n * Moves every skill dir found during quarantine into `targetSkillsRoot`.\n * Refuses to overwrite an existing dir of the same name unless `force` is\n * set — checked up front for every skill dir before anything is moved, so\n * a naming clash never leaves a partial promotion behind. Cleans up the\n * quarantine sandbox once every skill dir has been moved out.\n */\nexport function promote(\n entry: QuarantineEntry,\n targetSkillsRoot: string,\n options: PromoteOptions = {}\n): PromoteResult {\n const force = options.force ?? false;\n\n for (const skillDir of entry.skillDirs) {\n const dest = join(targetSkillsRoot, basename(skillDir));\n if (existsSync(dest) && !force) {\n throw new Error(`Refusing to overwrite existing skill dir: ${dest} (pass force to override)`);\n }\n }\n\n mkdirSync(targetSkillsRoot, { recursive: true });\n\n const promoted: string[] = [];\n for (const skillDir of entry.skillDirs) {\n const dest = join(targetSkillsRoot, basename(skillDir));\n if (existsSync(dest)) {\n rmSync(dest, { recursive: true, force: true });\n }\n moveDir(skillDir, dest);\n promoted.push(dest);\n }\n\n rmSync(entry.quarantinePath, { recursive: true, force: true });\n\n return { promoted };\n}\n\n/** Deletes the quarantine sandbox without promoting anything into a skills root. */\nexport function reject(entry: QuarantineEntry): void {\n rmSync(entry.quarantinePath, { recursive: true, force: true });\n}\n\n/** No-op: leaves the quarantine sandbox exactly as-is for later review. */\nexport function hold(_entry: QuarantineEntry): void {\n // intentionally does nothing — quarantine dir is left in place\n}\n\nfunction moveDir(src: string, dest: string): void {\n try {\n renameSync(src, dest);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'EXDEV') {\n cpSync(src, dest, { recursive: true });\n rmSync(src, { recursive: true, force: true });\n } else {\n throw err;\n }\n }\n}\n","/*\n * Rhize Commercial License — NOT covered by the MIT license that applies to\n * the rest of this repository. Production use of this module's functionality\n * requires a valid license key. See LICENSE and LICENSE-COMMERCIAL.\n * Copyright (c) 2026 Rhize Media\n */\nimport { appendFileSync, existsSync, statSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/**\n * Provenance ledger — direct TypeScript port of `record_provenance.py`'s\n * recording path (rhize-meta/skills/rhize-skill-forge/scripts). Appends a\n * structured entry to `<skillsRoot>/SOURCES.md`, the audit ledger of every\n * external-skill ingestion decision. Field names and formatting mirror the\n * Python source exactly so an existing SOURCES.md ledger stays consistent\n * regardless of which tool wrote which entry.\n */\n\nexport const FORGE_VERBS = ['DEFER', 'ABSORB', 'FORK', 'REJECT', 'WATCH'] as const;\nexport type ForgeVerb = (typeof FORGE_VERBS)[number];\n\nexport interface ProvenanceEntry {\n name: string;\n source: string;\n verb: ForgeVerb;\n version?: string;\n license?: string;\n target?: string;\n took?: string;\n verified?: string;\n driftCheck?: string;\n notes?: string;\n}\n\nconst LEDGER_HEADER = '# Skill Forge — Provenance Ledger\\n\\nOne entry per external-skill ingestion decision.\\n';\n\nfunction todayLocalIsoDate(): string {\n const d = new Date();\n const year = d.getFullYear();\n const month = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${year}-${month}-${day}`;\n}\n\n/**\n * Appends a provenance entry to `<skillsRoot>/SOURCES.md`, creating the\n * ledger with its header on first write. Throws if `verb` isn't a valid\n * forge verb or `skillsRoot` isn't an existing directory — mirrors the\n * Python script's fail-loud `fail()` calls. Returns the ledger path.\n */\nexport function appendProvenance(entry: ProvenanceEntry, skillsRoot: string): string {\n if (!FORGE_VERBS.includes(entry.verb)) {\n throw new Error(`verb must be one of ${FORGE_VERBS.join(', ')}`);\n }\n if (!existsSync(skillsRoot) || !statSync(skillsRoot).isDirectory()) {\n throw new Error(`skills-root not a directory: ${skillsRoot}`);\n }\n\n const today = todayLocalIsoDate();\n const driftCheck = entry.driftCheck ?? `# define how to detect upstream change for ${entry.name}`;\n\n const block =\n `\\n## ${entry.name} — ${today}\\n` +\n `- **Source:** ${entry.source}\\n` +\n `- **Upstream ref:** ${entry.version ?? 'n/a'}\\n` +\n `- **License:** ${entry.license ?? 'UNKNOWN'}\\n` +\n `- **Verb:** ${entry.verb}\\n` +\n `- **Target:** ${entry.target ?? 'n/a'}\\n` +\n `- **Took:** ${entry.took ?? 'nothing'}\\n` +\n `- **Verified:** ${entry.verified ?? 'n/a'}\\n` +\n `- **Drift check:** \\`${driftCheck}\\`\\n` +\n `- **Notes:** ${entry.notes ?? ''}\\n`;\n\n const ledgerPath = join(skillsRoot, 'SOURCES.md');\n if (!existsSync(ledgerPath)) {\n writeFileSync(ledgerPath, LEDGER_HEADER, 'utf-8');\n }\n appendFileSync(ledgerPath, block, 'utf-8');\n\n return ledgerPath;\n}\n","import { execFileSync } from 'node:child_process';\nimport {\n closeSync,\n existsSync,\n mkdtempSync,\n openSync,\n readFileSync,\n readdirSync,\n readSync,\n rmSync,\n statSync,\n} from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { basename, dirname, extname, join, resolve } from 'node:path';\nimport { expandHome, isDir, stripQuotes } from '../util.js';\n\n/**\n * Port of rhize-skill-forge's profile_skill.py. Stdlib-only structured profile of an\n * external skill candidate: a skill dir, a single SKILL.md, a plugin dir\n * (.claude-plugin/plugin.json), an MCP config dir (.mcp.json / mcp.json), or a .skill\n * (zip) bundle. Fails loudly (throws) rather than guessing.\n */\n\nconst SPDX_HINTS: Record<string, string> = {\n mit: 'MIT (permissive)',\n apache: 'Apache-2.0 (permissive)',\n bsd: 'BSD (permissive)',\n isc: 'ISC (permissive)',\n unlicense: 'Unlicense (public domain)',\n cc0: 'CC0 (public domain)',\n 'mozilla public license': 'MPL (weak copyleft)',\n 'gnu general public': 'GPL (copyleft)',\n 'gnu affero': 'AGPL (copyleft)',\n 'creative commons attribution-sharealike': 'CC-BY-SA (copyleft)',\n 'creative commons attribution': 'CC-BY (attribution)',\n 'all rights reserved': 'All rights reserved (restrictive)',\n};\n\nconst RESOURCE_DIRS = ['scripts', 'references', 'reference', 'commands', 'hooks', 'assets', 'templates', 'sub-skills'];\n\nconst PYTHON_STDLIB = new Set([\n 'os', 'sys', 're', 'json', 'argparse', 'pathlib', 'tempfile', 'zipfile',\n 'subprocess', 'collections', 'datetime', 'math', 'itertools', 'typing',\n 'shutil', 'glob', 'hashlib', 'urllib', 'csv', 'io', 'functools', '__future__',\n]);\n\nexport interface SkillProfile {\n kind: 'skill';\n inputPath: string;\n skillMd: string;\n name?: string;\n version?: string;\n validFrontmatter: boolean;\n description?: string;\n license: string;\n bodyLines: number;\n headerCount: number;\n topHeaders: string[];\n resources: Record<string, number>;\n mcpDependencies: string[];\n externalPythonDeps: string[];\n}\n\nexport interface PluginProfile {\n kind: 'plugin';\n inputPath: string;\n name?: string;\n version?: string;\n description?: string;\n skills: string[];\n skillCount: number;\n commands: number;\n mcpServers: string[];\n}\n\nexport interface McpProfile {\n kind: 'mcp';\n inputPath: string;\n config: string;\n servers: string[];\n serverCount: number;\n}\n\nexport type Profile = SkillProfile | PluginProfile | McpProfile;\n\n/** Profiles a skill dir, SKILL.md, plugin dir, MCP config dir, or .skill archive. */\nexport function profileSkill(inputPath: string): Profile {\n const p = resolve(expandHome(inputPath));\n if (!existsSync(p)) {\n throw new Error(`path does not exist: ${p}`);\n }\n\n const stat = statSync(p);\n const kind = stat.isDirectory() ? classifyInput(p) : 'skill';\n\n if (kind === 'plugin') {\n return profilePlugin(p);\n }\n if (kind === 'mcp') {\n return profileMcp(p);\n }\n return profileSkillDir(p);\n}\n\n/** skill | plugin | mcp — by manifest/config presence (directories only). */\nfunction classifyInput(p: string): 'skill' | 'plugin' | 'mcp' {\n if (existsSync(join(p, '.claude-plugin', 'plugin.json'))) return 'plugin';\n if (existsSync(join(p, '.mcp.json')) || existsSync(join(p, 'mcp.json'))) return 'mcp';\n return 'skill';\n}\n\ninterface ResolvedSkillMd {\n skillMd: string;\n root: string;\n tmpDir?: string;\n}\n\nfunction resolveSkillMd(inputPath: string): ResolvedSkillMd {\n let currentPath = inputPath;\n let tmpDir: string | undefined;\n\n const initialStat = statSync(currentPath);\n if (extname(currentPath) === '.skill' || (initialStat.isFile() && isZipFile(currentPath))) {\n tmpDir = mkdtempSync(join(tmpdir(), 'forge_'));\n extractZip(currentPath, tmpDir);\n currentPath = tmpDir;\n }\n\n const stat = statSync(currentPath);\n if (stat.isFile() && basename(currentPath) === 'SKILL.md') {\n return { skillMd: currentPath, root: dirname(currentPath), tmpDir };\n }\n if (stat.isDirectory()) {\n const direct = join(currentPath, 'SKILL.md');\n if (existsSync(direct)) {\n return { skillMd: direct, root: currentPath, tmpDir };\n }\n const matches = findSkillMdOneLevelDown(currentPath);\n if (matches.length === 1) {\n return { skillMd: matches[0], root: dirname(matches[0]), tmpDir };\n }\n if (matches.length > 1) {\n cleanupTmp(tmpDir);\n throw new Error(\n `multiple SKILL.md found under ${currentPath}; point at a single skill dir:\\n ${matches.join('\\n ')}`\n );\n }\n }\n cleanupTmp(tmpDir);\n throw new Error(`no SKILL.md found at ${currentPath}`);\n}\n\nfunction cleanupTmp(tmpDir?: string): void {\n if (tmpDir) rmSync(tmpDir, { recursive: true, force: true });\n}\n\n/** Mirrors path.glob(\"<dir>/SKILL.md\") + path.glob(\"skills/<dir>/SKILL.md\") (plugin layout support). */\nfunction findSkillMdOneLevelDown(dir: string): string[] {\n const matches = new Set<string>();\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n const candidate = join(dir, entry.name, 'SKILL.md');\n if (existsSync(candidate)) matches.add(candidate);\n }\n }\n const skillsDir = join(dir, 'skills');\n if (isDir(skillsDir)) {\n for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n const candidate = join(skillsDir, entry.name, 'SKILL.md');\n if (existsSync(candidate)) matches.add(candidate);\n }\n }\n }\n return Array.from(matches);\n}\n\nfunction isZipFile(p: string): boolean {\n try {\n const fd = openSync(p, 'r');\n const buf = Buffer.alloc(4);\n const bytesRead = readSync(fd, buf, 0, 4, 0);\n closeSync(fd);\n if (bytesRead < 4) return false;\n // ZIP local file header (PK\\x03\\x04), empty archive (PK\\x05\\x06), or spanned (PK\\x07\\x08)\n return buf[0] === 0x50 && buf[1] === 0x4b && (buf[2] === 0x03 || buf[2] === 0x05 || buf[2] === 0x07);\n } catch {\n return false;\n }\n}\n\nfunction extractZip(zipPath: string, destDir: string): void {\n try {\n execFileSync('unzip', ['-q', '-o', zipPath, '-d', destDir], { stdio: ['ignore', 'ignore', 'pipe'] });\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n if (e.code === 'ENOENT') {\n throw new Error(\n 'unsupported without unzip: .skill archives require the system `unzip` binary, which was not found on ' +\n 'PATH. Install unzip, or extract the archive yourself and point profileSkill at the resulting directory.'\n );\n }\n throw new Error(`failed to extract ${zipPath}: ${e.message ?? String(err)}`);\n }\n}\n\ninterface Frontmatter {\n validFrontmatter: boolean;\n name?: string;\n version?: string;\n license?: string;\n description?: string;\n}\n\n/** Mirrors text.split(\"---\", 2)[1] — the block between the first two \"---\" delimiters. */\nfunction frontmatterBlock(text: string): string {\n const first = text.indexOf('---');\n const rest = text.slice(first + 3);\n const second = rest.indexOf('---');\n return second === -1 ? rest : rest.slice(0, second);\n}\n\nfunction extractSimpleField(block: string, key: string): string | undefined {\n const re = new RegExp(`^${key}:\\\\s*(.+)$`, 'm');\n const m = block.match(re);\n if (!m) return undefined;\n return stripQuotes(m[1].trim());\n}\n\nfunction extractDescription(block: string): string | undefined {\n const m = block.match(/^description:\\s*(.*)$/m);\n if (!m) return undefined;\n const first = m[1].trim();\n if (['>', '>-', '|', '|-', ''].includes(first)) {\n const lines = block.split('\\n');\n const idx = lines.findIndex((l) => /^description:/.test(l));\n if (idx === -1) return undefined;\n const buf: string[] = [];\n for (let i = idx + 1; i < lines.length; i++) {\n const l = lines[i];\n if (/^\\s+\\S/.test(l)) {\n buf.push(l.trim());\n } else if (l.trim() === '') {\n continue;\n } else {\n break;\n }\n }\n return buf.join(' ').trim();\n }\n return stripQuotes(first);\n}\n\nfunction parseFrontmatter(text: string): Frontmatter {\n if (!text.startsWith('---\\n')) {\n return { validFrontmatter: false };\n }\n const block = frontmatterBlock(text);\n return {\n validFrontmatter: true,\n name: extractSimpleField(block, 'name'),\n version: extractSimpleField(block, 'version'),\n license: extractSimpleField(block, 'license'),\n description: extractDescription(block),\n };\n}\n\nfunction detectLicense(root: string, fm: Frontmatter): string {\n if (fm.license) return fm.license;\n const names = ['LICENSE', 'LICENSE.md', 'LICENSE.txt', 'COPYING', 'license'];\n for (const name of names) {\n const p = join(root, name);\n if (existsSync(p) && statSync(p).isFile()) {\n const head = readFileSync(p, 'utf-8').slice(0, 2000).toLowerCase();\n for (const [hint, label] of Object.entries(SPDX_HINTS)) {\n if (head.includes(hint)) return label;\n }\n return 'present but unrecognized';\n }\n }\n return 'NONE STATED';\n}\n\nfunction walkFiles(dir: string): string[] {\n const out: string[] = [];\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n out.push(...walkFiles(full));\n } else if (entry.isFile()) {\n out.push(full);\n }\n }\n return out;\n}\n\nfunction countResources(root: string): Record<string, number> {\n const resources: Record<string, number> = {};\n for (const d of RESOURCE_DIRS) {\n const dp = join(root, d);\n if (isDir(dp)) {\n resources[d] = walkFiles(dp).length;\n }\n }\n return resources;\n}\n\nfunction scanMcpAndImports(text: string, root: string): { mcpDeps: string[]; externalDeps: string[] } {\n const mcpRefRe = /mcp__[a-zA-Z0-9_]+/g;\n const mcpRefs = new Set(text.match(mcpRefRe) ?? []);\n const imports = new Set<string>();\n\n const scriptsDir = join(root, 'scripts');\n if (isDir(scriptsDir)) {\n for (const f of walkFiles(scriptsDir)) {\n if (!f.endsWith('.py')) continue;\n const ft = readFileSync(f, 'utf-8');\n for (const m of ft.match(mcpRefRe) ?? []) mcpRefs.add(m);\n const importRe = /^\\s*(?:import|from)\\s+([a-zA-Z0-9_]+)/gm;\n let m: RegExpExecArray | null;\n while ((m = importRe.exec(ft)) !== null) {\n imports.add(m[1]);\n }\n }\n }\n\n return {\n mcpDeps: Array.from(mcpRefs).sort(),\n externalDeps: Array.from(imports)\n .filter((i) => !PYTHON_STDLIB.has(i))\n .sort(),\n };\n}\n\nfunction profileSkillDir(inputPath: string): SkillProfile {\n const { skillMd, root, tmpDir } = resolveSkillMd(inputPath);\n try {\n const text = readFileSync(skillMd, 'utf-8');\n const fm = parseFrontmatter(text);\n const bodyLines = (text.match(/\\n/g)?.length ?? 0) + 1;\n const headers = Array.from(text.matchAll(/^#{1,3}\\s+(.+)$/gm)).map((m) => m[1]);\n const resources = countResources(root);\n const { mcpDeps, externalDeps } = scanMcpAndImports(text, root);\n\n return {\n kind: 'skill',\n inputPath,\n skillMd,\n name: fm.name,\n version: fm.version,\n validFrontmatter: fm.validFrontmatter,\n description: fm.description,\n license: detectLicense(root, fm),\n bodyLines,\n headerCount: headers.length,\n topHeaders: headers.slice(0, 12),\n resources,\n mcpDependencies: mcpDeps,\n externalPythonDeps: externalDeps,\n };\n } finally {\n cleanupTmp(tmpDir);\n }\n}\n\nfunction profilePlugin(p: string): PluginProfile {\n const manifestPath = join(p, '.claude-plugin', 'plugin.json');\n let manifest: Record<string, unknown>;\n try {\n manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));\n } catch (err) {\n throw new Error(`could not parse plugin.json under ${p}: ${(err as Error).message}`);\n }\n\n const skillsDir = join(p, 'skills');\n let skills: string[] = [];\n if (isDir(skillsDir)) {\n skills = readdirSync(skillsDir, { withFileTypes: true })\n .filter((d) => d.isDirectory() && existsSync(join(skillsDir, d.name, 'SKILL.md')))\n .map((d) => d.name)\n .sort();\n }\n\n const commandsDir = join(p, 'commands');\n const commands = isDir(commandsDir)\n ? readdirSync(commandsDir).filter((f) => f.endsWith('.md')).length\n : 0;\n\n const mc = (manifest.mcpServers ?? manifest.mcp_servers ?? {}) as Record<string, unknown>;\n const mcpServers = typeof mc === 'object' && mc !== null ? Object.keys(mc).sort() : [];\n\n return {\n kind: 'plugin',\n inputPath: p,\n name: manifest.name as string | undefined,\n version: manifest.version as string | undefined,\n description: manifest.description as string | undefined,\n skills,\n skillCount: skills.length,\n commands,\n mcpServers,\n };\n}\n\nfunction profileMcp(p: string): McpProfile {\n const cfgPath = existsSync(join(p, '.mcp.json')) ? join(p, '.mcp.json') : join(p, 'mcp.json');\n let cfg: Record<string, unknown>;\n try {\n cfg = JSON.parse(readFileSync(cfgPath, 'utf-8'));\n } catch (err) {\n throw new Error(`could not parse MCP config ${cfgPath}: ${(err as Error).message}`);\n }\n\n const servers = (cfg.mcpServers ?? cfg.servers ?? {}) as Record<string, unknown>;\n const names = typeof servers === 'object' && servers !== null ? Object.keys(servers).sort() : [];\n\n return {\n kind: 'mcp',\n inputPath: p,\n config: cfgPath,\n servers: names,\n serverCount: names.length,\n };\n}\n","import { statSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/** Expands a leading `~` / `~/` to the user's home directory. */\nexport function expandHome(p: string): string {\n if (p === '~') return homedir();\n if (p.startsWith('~/')) return join(homedir(), p.slice(2));\n return p;\n}\n\n/** Strips wrapping single/double quotes from a frontmatter value. */\nexport function stripQuotes(s: string): string {\n return s.replace(/^['\"]+/, '').replace(/['\"]+$/, '');\n}\n\n/** True if `p` exists and is a directory (false on any stat error). */\nexport function isDir(p: string): boolean {\n try {\n return statSync(p).isDirectory();\n } catch {\n return false;\n }\n}\n","import { readFileSync, readdirSync, statSync } from 'node:fs';\nimport { execFile } from 'node:child_process';\nimport { delimiter, extname, join } from 'node:path';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\nexport type Severity = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';\nexport type Verdict = 'pass' | 'warn' | 'block';\n\nexport interface SafetyFinding {\n severity: Severity;\n rule: string;\n file: string;\n detail: string;\n}\n\nexport interface SafetyResult {\n findings: SafetyFinding[];\n verdict: Verdict;\n}\n\n/**\n * Ported from skill_safety.py's SkillSpector severity->verdict mapping\n * (LOW allow, MEDIUM caution, HIGH/CRITICAL block). skill_safety.py itself\n * has no built-in static ruleset — it is a pure SkillSpector wrapper — so\n * this deny-pattern list is a new FREE-tier baseline (per docs/PRODUCT.md's\n * \"safety gate: built-in ruleset + safety scanner shell-out\") that gives a\n * verdict even when SkillSpector isn't installed.\n */\nconst BLOCK_SEVERITIES: ReadonlySet<Severity> = new Set(['HIGH', 'CRITICAL']);\n\ninterface DenyRule {\n rule: string;\n severity: Severity;\n pattern: RegExp;\n message: string;\n}\n\nconst DENY_RULES: DenyRule[] = [\n {\n rule: 'curl-pipe-shell',\n severity: 'CRITICAL',\n pattern: /\\b(curl|wget)\\b[^\\n|]*\\|\\s*(sudo\\s+)?(bash|sh|zsh)\\b/i,\n message: 'downloads a remote script and pipes it directly into a shell',\n },\n {\n rule: 'base64-obfuscated-exec',\n severity: 'CRITICAL',\n pattern:\n /(base64\\s+(-d|--decode)\\b[^\\n|]*\\|\\s*(bash|sh|zsh|python3?)\\b)|(\\bexec\\s*\\(\\s*(base64\\.)?b64decode\\()|(\\beval\\s*\\(\\s*Buffer\\.from\\([^)]*['\"]base64['\"])/i,\n message: 'decodes a base64 payload and executes it (obfuscated execution)',\n },\n {\n rule: 'reverse-shell',\n severity: 'CRITICAL',\n pattern: /(\\/dev\\/tcp\\/\\S+\\/\\d+)|(\\bnc\\s+-e\\s+\\/bin\\/(ba)?sh\\b)|(bash\\s+-i\\s*>&\\s*\\/dev\\/tcp)/i,\n message: 'opens a reverse shell back to a remote host',\n },\n {\n rule: 'destructive-rm-rf',\n severity: 'HIGH',\n pattern: /\\brm\\s+-[a-z]*r[a-z]*f[a-z]*\\b|\\brm\\s+-[a-z]*f[a-z]*r[a-z]*\\b/i,\n message: 'recursive force-delete (rm -rf) — verify the target path is scoped',\n },\n {\n rule: 'credential-file-access',\n severity: 'HIGH',\n pattern:\n /(~|\\$HOME)?\\/\\.(ssh\\/id_(rsa|ed25519|ecdsa)|aws\\/credentials|npmrc|netrc|gnupg|docker\\/config\\.json)\\b|\\bid_rsa\\b|\\.env\\b.*\\bcat\\b|\\bcat\\b.*\\.env\\b/i,\n message: 'reads a credential/secrets file (SSH keys, cloud credentials, .env)',\n },\n {\n rule: 'credential-exfil',\n severity: 'HIGH',\n pattern: /\\b(curl|wget|fetch)\\b[^\\n]*(--data|--data-binary|-F|-d)\\b[^\\n]*(\\.ssh|\\.aws|\\.env|credentials|id_rsa)/i,\n message: 'uploads credential/secret file contents to a remote endpoint',\n },\n {\n rule: 'dynamic-eval',\n severity: 'MEDIUM',\n pattern: /\\beval\\s*\\(|\\bexec\\s*\\(\\s*compile\\s*\\(|\\bnew\\s+Function\\s*\\(/,\n message: 'evaluates dynamically constructed code',\n },\n {\n rule: 'shell-true-subprocess',\n severity: 'MEDIUM',\n pattern: /subprocess\\.(Popen|run|call)\\([^)]*shell\\s*=\\s*True/,\n message: 'runs a subprocess with shell=True (shell-injection risk)',\n },\n {\n rule: 'persistence-cron-rc',\n severity: 'MEDIUM',\n pattern: />>\\s*~?\\/?\\.(bashrc|zshrc|bash_profile|profile)\\b|\\bcrontab\\s+-/i,\n message: 'modifies shell startup files or crontab (persistence)',\n },\n {\n rule: 'sudo-usage',\n severity: 'LOW',\n pattern: /\\bsudo\\s+/,\n message: 'invokes sudo — review for unnecessary privilege escalation',\n },\n];\n\nconst SKIP_DIRS = new Set(['.git', 'node_modules', 'dist', 'build', '__pycache__', '.venv', 'venv']);\nconst BINARY_EXT = new Set([\n '.png', '.jpg', '.jpeg', '.gif', '.webp', '.ico', '.pdf', '.zip', '.tar', '.gz',\n '.woff', '.woff2', '.ttf', '.eot', '.mp4', '.mp3', '.wasm', '.bin', '.exe',\n]);\nconst MAX_FILE_BYTES = 2 * 1024 * 1024; // 2MB — skills ship small text/scripts\n\nfunction* walk(dir: string): Generator<string> {\n let entries;\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue;\n yield* walk(full);\n } else if (entry.isFile()) {\n yield full;\n }\n }\n}\n\nfunction readTextFile(file: string): string | undefined {\n if (BINARY_EXT.has(extname(file).toLowerCase())) return undefined;\n try {\n if (statSync(file).size > MAX_FILE_BYTES) return undefined;\n const content = readFileSync(file, 'utf-8');\n if (content.slice(0, 512).includes('\\0')) return undefined; // crude binary sniff\n return content;\n } catch {\n return undefined;\n }\n}\n\nfunction scanStaticRules(targetPath: string): SafetyFinding[] {\n const findings: SafetyFinding[] = [];\n let root;\n try {\n root = statSync(targetPath);\n } catch {\n return findings;\n }\n const files = root.isDirectory() ? Array.from(walk(targetPath)) : [targetPath];\n\n for (const file of files) {\n const content = readTextFile(file);\n if (content === undefined) continue;\n const lines = content.split('\\n');\n for (const rule of DENY_RULES) {\n for (let i = 0; i < lines.length; i++) {\n if (rule.pattern.test(lines[i])) {\n findings.push({\n severity: rule.severity,\n rule: rule.rule,\n file,\n detail: `line ${i + 1}: ${rule.message}`,\n });\n }\n }\n }\n }\n return findings;\n}\n\n/** Locate an executable on PATH without shelling out (no `which`/`command -v` dependency). */\nfunction which(cmd: string): string | undefined {\n const pathEnv = process.env.PATH ?? '';\n const dirs = pathEnv.split(delimiter).filter(Boolean);\n const exts = process.platform === 'win32' ? (process.env.PATHEXT ?? '.EXE;.CMD;.BAT').split(';') : [''];\n for (const dir of dirs) {\n for (const ext of exts) {\n const candidate = join(dir, cmd + ext);\n try {\n if (statSync(candidate).isFile()) return candidate;\n } catch {\n // not found in this dir, keep looking\n }\n }\n }\n return undefined;\n}\n\ninterface SkillSpectorFinding {\n severity?: string;\n rule_id?: string;\n message?: string;\n}\n\ninterface SkillSpectorOutput {\n filtered_findings?: SkillSpectorFinding[];\n findings?: SkillSpectorFinding[];\n}\n\nfunction normalizeSeverity(sev: string | undefined): Severity {\n const s = (sev ?? '').toUpperCase();\n if (s === 'LOW' || s === 'MEDIUM' || s === 'HIGH' || s === 'CRITICAL') return s;\n return 'MEDIUM'; // unrecognized external severity: treat cautiously, don't silently trust as safe\n}\n\n/**\n * Mirrors skill_safety.py's `skillspector scan <target> --format json --no-llm`\n * invocation. Skipped cleanly (no findings, no error) when SkillSpector isn't\n * installed or its output can't be parsed — the built-in ruleset above is the\n * FREE-tier baseline; SkillSpector is additive when present.\n */\nasync function scanWithSkillSpector(targetPath: string): Promise<SafetyFinding[]> {\n const bin = which('skillspector');\n if (!bin) return [];\n\n try {\n const { stdout } = await execFileAsync(bin, ['scan', targetPath, '--format', 'json', '--no-llm'], {\n timeout: 60_000,\n maxBuffer: 10 * 1024 * 1024,\n });\n const parsed = JSON.parse(stdout) as SkillSpectorOutput;\n const raw = parsed.filtered_findings ?? parsed.findings ?? [];\n return raw.map((f) => ({\n severity: normalizeSeverity(f.severity),\n rule: f.rule_id ?? 'skillspector',\n file: targetPath,\n detail: f.message ?? '',\n }));\n } catch {\n return [];\n }\n}\n\nfunction verdictFor(findings: SafetyFinding[]): Verdict {\n if (findings.some((f) => BLOCK_SEVERITIES.has(f.severity))) return 'block';\n if (findings.length > 0) return 'warn';\n return 'pass';\n}\n\n/**\n * Scans a quarantined skill (file or directory) for unsafe patterns: the\n * built-in deny-pattern ruleset always runs, and an installed SkillSpector\n * (https://github.com/NVIDIA/SkillSpector) is shelled out to and merged in\n * when present on PATH. Blocks on any HIGH or CRITICAL finding.\n */\nexport async function scanSafety(targetPath: string): Promise<SafetyResult> {\n const findings = [...scanStaticRules(targetPath), ...(await scanWithSkillSpector(targetPath))];\n return { findings, verdict: verdictFor(findings) };\n}\n","/*\n * Rhize Commercial License — NOT covered by the MIT license that applies to\n * the rest of this repository. Production use of this module's functionality\n * requires a valid license key. See LICENSE and LICENSE-COMMERCIAL.\n * Copyright (c) 2026 Rhize Media\n */\nimport { existsSync, readFileSync, readdirSync, type Dirent } from 'node:fs';\nimport { basename, dirname, join, resolve } from 'node:path';\nimport { expandHome, isDir, stripQuotes } from '../util.js';\n\n/**\n * Overlap gate — ranks a candidate skill against an existing skill set.\n *\n * Direct TypeScript port of `overlap_scan.py` (rhize-meta/skills/rhize-skill-forge/scripts).\n * Heuristic, dependency-free. Blends Jaccard token overlap with keyword containment of the\n * candidate's distinctive phrases (weights, thresholds, tokenizer, and quoted-phrase bonus\n * all match the Python source exactly).\n */\n\nconst STOPWORDS = new Set(\n `\na an the and or of to for in on with use used using when whether this that these those is are be\nyour you it its as at by from into via per also any all not no than then so if up out via about\nskill skills user claude code based comprehensive enhanced practical including include includes\n`\n .split(/\\s+/)\n .filter(Boolean),\n);\n\nconst DEFAULT_SET_THRESHOLD = 0.45;\n\nexport interface SkillMeta {\n name: string;\n description: string;\n}\n\nexport interface SkillOverlapScore {\n skill: string;\n path: string;\n score: number;\n}\n\nexport interface OverlapResult {\n candidate: string;\n nearest: string;\n nearestScore: number;\n suggestedVerb: string;\n ranking: SkillOverlapScore[];\n}\n\nexport interface OverlapPair {\n a: string;\n b: string;\n score: number;\n}\n\n/** Mirrors Python's `str.split(sep, maxSplit)` — returns up to maxSplit+1 parts. */\nfunction splitLimited(text: string, sep: string, maxSplit: number): string[] {\n const parts: string[] = [];\n let rest = text;\n for (let i = 0; i < maxSplit; i++) {\n const idx = rest.indexOf(sep);\n if (idx === -1) break;\n parts.push(rest.slice(0, idx));\n rest = rest.slice(idx + sep.length);\n }\n parts.push(rest);\n return parts;\n}\n\n/** Reads a SKILL.md's `name`/`description` frontmatter, falling back to heading + body lead-in. */\nexport function readFrontmatter(skillMdPath: string): SkillMeta {\n const text = readFileSync(skillMdPath, 'utf-8');\n const meta: SkillMeta = { name: basename(dirname(skillMdPath)), description: '' };\n\n if (text.startsWith('---\\n')) {\n const block = splitLimited(text, '---', 2)[1] ?? '';\n\n const nameMatch = block.match(/^name:\\s*(.+)$/m);\n if (nameMatch) {\n meta.name = stripQuotes(nameMatch[1].trim());\n }\n\n const descMatch = block.match(/^description:\\s*(.*)$/m);\n if (descMatch) {\n const first = descMatch[1].trim();\n if (['>', '>-', '|', '|-', ''].includes(first)) {\n const lines = block.split(/\\r\\n|\\r|\\n/);\n const idx = lines.findIndex((l) => /^description:/.test(l));\n const buf: string[] = [];\n if (idx !== -1) {\n for (const l of lines.slice(idx + 1)) {\n if (/^\\s+\\S/.test(l)) {\n buf.push(l.trim());\n } else if (l.trim() === '') {\n continue;\n } else {\n break;\n }\n }\n }\n meta.description = buf.join(' ');\n } else {\n meta.description = stripQuotes(first);\n }\n }\n } else {\n const heads = Array.from(text.matchAll(/^#\\s+(.+)$/gm)).map((m) => m[1]);\n meta.description = `${heads[0] ?? ''} ${text.slice(0, 400)}`;\n }\n\n return meta;\n}\n\nfunction tokenize(text: string): Set<string> {\n const matches = text.toLowerCase().match(/[a-z][a-z0-9+.-]{2,}/g) ?? [];\n return new Set(matches.filter((t) => !STOPWORDS.has(t)));\n}\n\n/**\n * Distinctive quoted trigger phrases and salient words from a description.\n * Ported for parity with the source script; not used by score() (which extracts phrases inline).\n */\nexport function keyphrases(text: string): Set<string> {\n const quoted = new Set<string>();\n for (const m of text.matchAll(/\"([^\"]{2,40})\"/g)) quoted.add(m[1].toLowerCase().trim());\n for (const m of text.matchAll(/'([^']{2,40})'/g)) quoted.add(m[1].toLowerCase().trim());\n return quoted.size > 0 ? quoted : tokenize(text);\n}\n\nfunction intersectSize(a: Set<string>, b: Set<string>): number {\n let n = 0;\n for (const x of a) if (b.has(x)) n += 1;\n return n;\n}\n\nfunction unionSize(a: Set<string>, b: Set<string>): number {\n const u = new Set(a);\n for (const x of b) u.add(x);\n return u.size;\n}\n\nfunction isSubset(sub: Set<string>, sup: Set<string>): boolean {\n for (const x of sub) if (!sup.has(x)) return false;\n return true;\n}\n\nfunction round3(n: number): number {\n return Math.round(n * 1000) / 1000;\n}\n\nfunction score(cand: SkillMeta, other: SkillMeta): number {\n const a = tokenize(`${cand.name} ${cand.description}`);\n const b = tokenize(`${other.name} ${other.description}`);\n if (a.size === 0) return 0.0;\n\n const inter = intersectSize(a, b);\n const union = unionSize(a, b);\n const jaccard = union ? inter / union : 0.0;\n const containment = inter / a.size;\n\n const phrases = new Set<string>();\n for (const m of cand.description.matchAll(/\"([^\"]{2,40})\"/g)) phrases.add(m[1]);\n for (const m of cand.description.matchAll(/'([^']{2,40})'/g)) phrases.add(m[1]);\n\n let pscore = 0.0;\n if (phrases.size > 0) {\n let hits = 0;\n for (const p of phrases) {\n const pw = tokenize(p);\n if (pw.size > 0 && isSubset(pw, b)) hits += 1;\n }\n pscore = hits / phrases.size;\n }\n\n return round3(0.3 * jaccard + 0.5 * containment + 0.2 * pscore);\n}\n\n/** Score tiers behind suggestVerb/suggestVerbCode — one table so prose and bare verb can't drift. */\nconst VERB_TIERS = [\n { min: 0.45, verb: 'ABSORB', prose: 'ABSORB (or REJECT if ours is already better)' },\n { min: 0.2, verb: 'FORK', prose: 'FORK (re-skin), or ABSORB one piece' },\n { min: -Infinity, verb: 'DEFER', prose: 'DEFER / new FORK (little overlap)' },\n] as const;\n\nfunction verbTier(topScore: number): (typeof VERB_TIERS)[number] {\n return VERB_TIERS.find((t) => topScore >= t.min) ?? VERB_TIERS[VERB_TIERS.length - 1];\n}\n\nexport function suggestVerb(topScore: number): string {\n return verbTier(topScore).prose;\n}\n\n/** Bare verb of the tier suggestVerb would pick — for callers that store an enum, not prose (e.g. the pending queue). */\nexport function suggestVerbCode(topScore: number): (typeof VERB_TIERS)[number]['verb'] {\n return verbTier(topScore).verb;\n}\n\nfunction listSubdirs(dir: string): string[] {\n if (!existsSync(dir)) return [];\n let entries: Dirent[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n return entries.filter((e) => e.isDirectory()).map((e) => join(dir, e.name));\n}\n\n/**\n * Finds SKILL.md files matching the same three glob shapes as the Python script:\n * \"<dir>/SKILL.md\", \"skills/<dir>/SKILL.md\", \"<dir>/skills/<dir>/SKILL.md\" (one wildcard\n * <dir> level each) — deduped, .git excluded, sorted.\n */\nfunction findSkillMdFiles(root: string): string[] {\n const found = new Set<string>();\n\n for (const dir of listSubdirs(root)) {\n const candidate = join(dir, 'SKILL.md');\n if (existsSync(candidate)) found.add(resolve(candidate));\n }\n\n for (const dir of listSubdirs(join(root, 'skills'))) {\n const candidate = join(dir, 'SKILL.md');\n if (existsSync(candidate)) found.add(resolve(candidate));\n }\n\n for (const topDir of listSubdirs(root)) {\n for (const dir of listSubdirs(join(topDir, 'skills'))) {\n const candidate = join(dir, 'SKILL.md');\n if (existsSync(candidate)) found.add(resolve(candidate));\n }\n }\n\n return Array.from(found)\n .filter((p) => !p.includes('/.git/'))\n .sort();\n}\n\n/**\n * Ranks a single candidate skill against every skill found under `skillsRoot`.\n * Throws if `skillsRoot` isn't a directory, the candidate's SKILL.md can't be found,\n * or no existing skills are found under the root (mirrors the Python script's `fail()` exits).\n */\nexport function scanOverlap(candidatePath: string, skillsRoot: string): OverlapResult {\n const root = resolve(expandHome(skillsRoot));\n if (!isDir(root)) {\n throw new Error(`skills-root not a directory: ${root}`);\n }\n\n let cpath = resolve(expandHome(candidatePath));\n if (isDir(cpath)) {\n cpath = join(cpath, 'SKILL.md');\n }\n if (!existsSync(cpath)) {\n throw new Error(`candidate SKILL.md not found: ${cpath}`);\n }\n\n const cand = readFrontmatter(cpath);\n\n const existing = findSkillMdFiles(root).filter((p) => p !== resolve(cpath));\n if (existing.length === 0) {\n throw new Error(`no existing skills found under ${root}`);\n }\n\n const ranking: SkillOverlapScore[] = existing.map((p) => {\n const other = readFrontmatter(p);\n return { skill: other.name, path: p, score: score(cand, other) };\n });\n ranking.sort((a, b) => b.score - a.score);\n\n const top = ranking[0];\n return {\n candidate: cand.name,\n nearest: top.skill,\n nearestScore: top.score,\n suggestedVerb: suggestVerb(top.score),\n ranking,\n };\n}\n\n/**\n * N-way: pairwise overlap across the whole installed set; reports pairs scoring >= threshold.\n * Symmetric — the pair score is max(score(a,b), score(b,a)) since score() is candidate-centric.\n */\nexport function scanOverlapSet(\n skillsRoot: string,\n threshold: number = DEFAULT_SET_THRESHOLD,\n): OverlapPair[] {\n const root = resolve(expandHome(skillsRoot));\n if (!isDir(root)) {\n throw new Error(`skills-root not a directory: ${root}`);\n }\n\n const paths = findSkillMdFiles(root);\n const metas = paths.map(readFrontmatter);\n\n const pairs: OverlapPair[] = [];\n for (let i = 0; i < metas.length; i++) {\n for (let j = i + 1; j < metas.length; j++) {\n const sc = Math.max(score(metas[i], metas[j]), score(metas[j], metas[i]));\n if (sc >= threshold) {\n pairs.push({ a: metas[i].name, b: metas[j].name, score: round3(sc) });\n }\n }\n }\n pairs.sort((a, b) => b.score - a.score);\n return pairs;\n}\n","/*\n * Rhize Commercial License — NOT covered by the MIT license that applies to\n * the rest of this repository. Production use of this module's functionality\n * requires a valid license key. See LICENSE and LICENSE-COMMERCIAL.\n * Copyright (c) 2026 Rhize Media\n */\nimport { verify as cryptoVerify } from 'node:crypto';\nimport { loadConfig } from './config.js';\n\n/**\n * Offline license-key verification for skill-forge Pro. No network calls —\n * the CLI must gate Pro features without phoning home.\n *\n * Key format: `skf1.<base64url payload>.<base64url signature>`\n * - `skf1` is a fixed prefix identifying the key scheme/version.\n * - payload is base64url-encoded JSON: `{ name, email, seats, exp }`, where\n * `exp` is an ISO date string (`YYYY-MM-DD`) the key is valid through.\n * - signature is an Ed25519 signature (via node:crypto) over the raw\n * decoded payload bytes, verified against the embedded PUBLIC_KEY_PEM.\n *\n * Keys are minted offline with `scripts/keygen.mjs sign` using the matching\n * private key, which is never committed to this repo (see keys/, gitignored).\n *\n * Beta pricing (0.x): the commercial license still governs Pro modules, but\n * the runtime gate SLEEPS while BETA_FREE is true — every Pro feature runs\n * for everyone, with a one-line notice unless a valid key is present. Flip\n * BETA_FREE to false at 1.0 to re-arm the lock; nothing else needs to change.\n * `proFeatureStatus()` is the shape callers should gate on: it folds\n * \"do we have a valid key\" and \"does the beta make that not matter\" into a\n * single `{ enabled, notice? }` result. `hasProLicense()` / `upgradeNotice()`\n * remain as-is for existing call sites until they're migrated to it.\n */\n\nconst LICENSE_PREFIX = 'skf1';\n\n/** Single source of truth for beta pricing. Flip to false at v1.0. */\nexport const BETA_FREE = true;\n\n// Ed25519 public key (SPKI/PEM), generated once via `scripts/keygen.mjs keypair`.\n// The matching private key lives outside this repo — see scripts/keygen.mjs.\nconst PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAWSVbcX9u70IsN1O5zS5z+AYtAUVz7ExT7h+1gT9x+wQ=\n-----END PUBLIC KEY-----\n`;\n\nexport interface LicensePayload {\n name: string;\n email: string;\n seats: number;\n exp: string;\n}\n\nexport type LicenseInvalidReason = 'missing' | 'malformed' | 'signature' | 'expired';\n\nexport type LicenseStatus =\n | { valid: true; payload: LicensePayload }\n | { valid: false; reason: LicenseInvalidReason };\n\n/** Reads the raw license key string. SKILL_FORGE_LICENSE (env) takes precedence over config.licenseKey. */\nexport function readLicenseKey(): string | undefined {\n const fromEnv = process.env.SKILL_FORGE_LICENSE?.trim();\n if (fromEnv) return fromEnv;\n return loadConfig().licenseKey?.trim() || undefined;\n}\n\nfunction isLicensePayload(value: unknown): value is LicensePayload {\n if (typeof value !== 'object' || value === null) return false;\n const p = value as Record<string, unknown>;\n return typeof p.name === 'string' && typeof p.email === 'string' && typeof p.seats === 'number' && typeof p.exp === 'string';\n}\n\n/**\n * Verifies a license key string. Pass no argument to check the default\n * sources (SKILL_FORGE_LICENSE env, then config.licenseKey). Verifies the\n * `skf1.<payload>.<signature>` shape, the Ed25519 signature against\n * PUBLIC_KEY_PEM, and that `exp` has not passed. Any tampering (payload or\n * signature) or expiry makes the key invalid.\n */\nexport function checkLicense(rawKey: string | undefined = readLicenseKey()): LicenseStatus {\n if (!rawKey) {\n return { valid: false, reason: 'missing' };\n }\n\n const parts = rawKey.trim().split('.');\n if (parts.length !== 3 || parts[0] !== LICENSE_PREFIX) {\n return { valid: false, reason: 'malformed' };\n }\n const [, payloadPart, signaturePart] = parts;\n\n const payloadBuffer = Buffer.from(payloadPart, 'base64url');\n const signature = Buffer.from(signaturePart, 'base64url');\n\n let payload: unknown;\n try {\n payload = JSON.parse(payloadBuffer.toString('utf-8'));\n } catch {\n return { valid: false, reason: 'malformed' };\n }\n if (!isLicensePayload(payload)) {\n return { valid: false, reason: 'malformed' };\n }\n\n // Ed25519 signs/verifies over raw bytes directly — algorithm must be null.\n let signatureValid: boolean;\n try {\n signatureValid = cryptoVerify(null, payloadBuffer, PUBLIC_KEY_PEM, signature);\n } catch {\n return { valid: false, reason: 'malformed' };\n }\n if (!signatureValid) {\n return { valid: false, reason: 'signature' };\n }\n\n const expMs = new Date(payload.exp).getTime();\n if (Number.isNaN(expMs) || expMs < Date.now()) {\n return { valid: false, reason: 'expired' };\n }\n\n return { valid: true, payload };\n}\n\n/** True if the default license sources resolve to a currently-valid license. */\nexport function hasProLicense(): boolean {\n return checkLicense().valid;\n}\n\n/** Short line replacing a locked Pro output — printed in place of the feature's normal output. */\nexport function upgradeNotice(feature: string): string {\n return `[skill-forge] Pro feature (${feature}) — set SKILL_FORGE_LICENSE or config.licenseKey to a valid license to unlock.`;\n}\n\nfunction invalidLicenseReasonText(reason: LicenseInvalidReason): string {\n switch (reason) {\n case 'malformed':\n return 'is malformed';\n case 'signature':\n return 'has an invalid signature';\n case 'expired':\n return 'has expired';\n default:\n return 'is invalid';\n }\n}\n\n/** Result shape for {@link proFeatureStatus}: whether a Pro feature should run, and an optional line to print alongside it. */\nexport interface ProFeatureStatus {\n enabled: boolean;\n notice?: string;\n}\n\n/**\n * Resolves whether a Pro feature should run right now, folding in the 0.x\n * beta-free policy. This is the shape call sites should consume going\n * forward — see the module docstring for the resolution table.\n *\n * `betaFree` defaults to {@link BETA_FREE} and is only a parameter so tests\n * can exercise the post-1.0 (locked) branch without touching the constant.\n */\nexport function proFeatureStatus(feature: string, betaFree: boolean = BETA_FREE): ProFeatureStatus {\n const status = checkLicense();\n if (status.valid) {\n return { enabled: true };\n }\n\n if (!betaFree) {\n return { enabled: false, notice: upgradeNotice(feature) };\n }\n\n const betaNotice = `Pro feature (${feature}) — free during the 0.x beta; will require a license at 1.0.`;\n if (status.reason === 'missing') {\n return { enabled: true, notice: betaNotice };\n }\n\n return {\n enabled: true,\n notice: `${betaNotice} (Note: your license key ${invalidLicenseReasonText(status.reason)} — ignored while Pro is free.)`,\n };\n}\n","import type { SkillProfile } from '../gate/profile.js';\nimport type { OverlapResult } from '../gate/overlap.js';\nimport { upgradeNotice } from '../license.js';\n\n/**\n * Report renderer — Step 3 of the gate pipeline (profile -> safety scan ->\n * overlap analysis -> report -> promote/reject). Renders the human-readable\n * decision box from `rhize-skill-forge/SKILL.md` (Step 3 report format) and a\n * `--json` equivalent.\n */\n\nexport type SafetyVerdict = 'ALLOW' | 'CAUTION' | 'BLOCK' | 'UNKNOWN';\n\nexport interface SafetyFinding {\n severity?: string;\n rule?: string;\n message?: string;\n}\n\n/** Mirrors the verdict shape emitted by skill_safety.py (SkillSpector wrapper). */\nexport interface SafetyResult {\n verdict: SafetyVerdict;\n severity?: string;\n score?: number;\n recommendation?: string;\n findings?: SafetyFinding[];\n}\n\n/**\n * Aggregate result of the quarantine gate pipeline for a single skill\n * candidate — the input to renderReport / renderJson. `worthTaking` and\n * `leaveBehind` are placeholders: they're only populated once a human (or a\n * later ABSORB step) has actually decided what to extract, so they render as\n * pending until then.\n */\nexport interface GateResult {\n profile: SkillProfile;\n safety: SafetyResult;\n overlap?: OverlapResult;\n /** True when overlap analysis was skipped for lack of a Pro license (as opposed to a missing skills root). */\n overlapLocked?: boolean;\n worthTaking?: string[];\n leaveBehind?: string[];\n /** Pro-feature beta/upgrade notices (see src/license.ts's proFeatureStatus) — printed above the terminal report, or carried as a JSON field for --json. */\n notices?: string[];\n}\n\nconst BORDER = '━'.repeat(55);\nconst LABEL_WIDTH = 20;\n\nfunction padLabel(text: string): string {\n return text.padEnd(LABEL_WIDTH);\n}\n\nfunction renderListField(labelText: string, items: string[] | undefined, placeholder: string): string {\n const head = `${padLabel(labelText)}: `;\n if (!items || items.length === 0) {\n return `${head}${placeholder}`;\n }\n const bullets = items.map((item) => ` - ${item}`).join('\\n');\n return `${head.trimEnd()}\\n${bullets}`;\n}\n\nfunction renderSafetyLine(safety: SafetyResult): string {\n const detail = [safety.severity, safety.score !== undefined ? `score ${safety.score}` : undefined]\n .filter((v): v is string => Boolean(v))\n .join(', ');\n const withDetail = detail ? `${safety.verdict} (${detail})` : safety.verdict;\n return safety.recommendation ? `${withDetail} — ${safety.recommendation}` : withDetail;\n}\n\nfunction renderFindingLine(finding: SafetyFinding): string {\n const severity = finding.severity ?? 'UNKNOWN';\n const rule = finding.rule ?? 'unknown-rule';\n return ` - [${severity}] ${rule}${finding.message ? `: ${finding.message}` : ''}`;\n}\n\n/**\n * Renders the safety findings list — severity, rule, and a one-line detail\n * per finding — so a blocked/cautioned skill shows *why* without needing\n * `--json`. Omitted entirely when there are no findings.\n */\nfunction renderFindingsLines(findings: SafetyFinding[] | undefined): string[] {\n if (!findings?.length) return [];\n return [`${padLabel('Findings')}:`, ...findings.map(renderFindingLine)];\n}\n\n/** Renders the Step-3 terminal decision box for a single gate result. */\nexport function renderReport(result: GateResult): string {\n const { profile, overlap, overlapLocked, safety, worthTaking, leaveBehind } = result;\n const name = profile.name ?? 'unknown';\n const version = profile.version ?? 'unknown';\n const license = profile.license ?? 'UNKNOWN';\n\n // Locked (no Pro license): the two overlap lines collapse into one short\n // upgrade notice instead of \"n/a\" placeholders for data we never computed.\n const overlapLines = overlapLocked\n ? [`${padLabel('Overlap analysis')}: ${upgradeNotice('overlap analysis')}`]\n : overlap\n ? [\n `${padLabel('Nearest existing skill')}: ${overlap.nearest} (overlap ${overlap.nearestScore})`,\n `${padLabel('Recommended verb')}: ${overlap.suggestedVerb}`,\n ]\n : [`${padLabel('Nearest existing skill')}: n/a — no skills root configured`, `${padLabel('Recommended verb')}: n/a`];\n\n const lines: string[] = [\n BORDER,\n `🔨 Forge: ${name} (v${version}, ${license})`,\n BORDER,\n ...overlapLines,\n renderListField(\"What's worth taking\", worthTaking, '(pending — not yet determined)'),\n renderListField('What to leave behind', leaveBehind, '(pending — not yet determined)'),\n `${padLabel('Safety verdict')}: ${renderSafetyLine(safety)}`,\n ...renderFindingsLines(safety.findings),\n BORDER,\n ];\n\n return lines.join('\\n');\n}\n\n/** Renders a gate result as JSON for `--json` output. */\nexport function renderJson(result: GateResult): string {\n return JSON.stringify(result, null, 2);\n}\n","import { profileSkill, type SkillProfile } from '../gate/profile.js';\nimport { scanSafety, type SafetyResult as GateSafetyResult } from '../gate/safety.js';\nimport { scanOverlap, type OverlapResult } from '../gate/overlap.js';\nimport { proFeatureStatus } from '../license.js';\nimport { renderJson, renderReport, type GateResult, type SafetyResult as ReportSafetyResult } from '../report/render.js';\n\n/**\n * Shared profile -> safety -> overlap pipeline used by both `add` and `scan`.\n * Bridges gate/safety.ts's SafetyResult (verdict: pass|warn|block, findings\n * with file/detail) into report/render.ts's SafetyResult shape (verdict:\n * ALLOW|CAUTION|BLOCK, findings with message) so both commands render through\n * the same Step-3 report box.\n */\n\nconst SAFETY_VERDICT_MAP: Record<GateSafetyResult['verdict'], ReportSafetyResult['verdict']> = {\n pass: 'ALLOW',\n warn: 'CAUTION',\n block: 'BLOCK',\n};\n\nexport interface GateRunResult {\n /** Gate-level safety result (verdict: pass|warn|block) — drives the promote/reject decision. */\n safety: GateSafetyResult;\n /** Report input; also carries the profile, overlap, and any Pro-feature notice produced by the gate. */\n gateResult: GateResult;\n /** Whether Pro features should run right now (valid license, or the 0.x beta-free policy) — checked once here so callers don't re-verify. */\n proEnabled: boolean;\n}\n\n/**\n * Prints a gate result the same way for `add` and `scan`: any Pro-feature\n * notices then the terminal report box normally, or a single parseable JSON\n * payload with `--json` (notices ride along as the payload's `notices` field\n * instead of being printed above it).\n */\nexport function printGateOutput(gateResult: GateResult, json?: boolean): void {\n if (!json) {\n for (const notice of gateResult.notices ?? []) console.log(`[skill-forge] ${notice}`);\n }\n console.log(json ? renderJson(gateResult) : renderReport(gateResult));\n}\n\nfunction asSkillProfile(candidatePath: string): SkillProfile {\n const profile = profileSkill(candidatePath);\n if (profile.kind !== 'skill') {\n throw new Error(`expected a skill candidate at ${candidatePath}, found a ${profile.kind}`);\n }\n return profile;\n}\n\n/** Best-effort overlap: a missing/empty skills root is reported to stderr, not fatal to the gate. */\nfunction tryOverlap(candidatePath: string, skillsRoot: string): OverlapResult | undefined {\n try {\n return scanOverlap(candidatePath, skillsRoot);\n } catch (err) {\n console.error(`[skill-forge] overlap scan skipped: ${(err as Error).message}`);\n return undefined;\n }\n}\n\n/**\n * Runs profile -> safety -> overlap for a single quarantined candidate and\n * builds the report input. Overlap analysis (and any other Pro feature the\n * caller gates on `proEnabled`) is checked exactly once via `proFeatureStatus`\n * — during the 0.x beta this is `enabled: true` for everyone, with an\n * optional notice surfaced through `gateResult.notices`; post-1.0 (or with an\n * invalid/expired key once the beta flag flips) it's `enabled: false` and\n * `overlapLocked` is set so the report shows the upgrade notice in place of\n * the overlap lines. `proFeatureLabel` names the Pro feature(s) this\n * particular caller exercises, for the printed/JSON notice text — callers\n * that also gate other Pro features off the same `proEnabled` result (e.g.\n * `add`'s provenance ledger and pending-queue write) should pass a label\n * covering all of them so only one notice is produced per invocation.\n */\nexport async function runGate(\n candidatePath: string,\n skillsRoot: string,\n proFeatureLabel: string = 'overlap analysis'\n): Promise<GateRunResult> {\n const profile = asSkillProfile(candidatePath);\n const safety = await scanSafety(candidatePath);\n const proStatus = proFeatureStatus(proFeatureLabel);\n const overlap = proStatus.enabled ? tryOverlap(candidatePath, skillsRoot) : undefined;\n\n const gateResult: GateResult = {\n profile,\n overlap,\n overlapLocked: !proStatus.enabled,\n notices: proStatus.notice ? [proStatus.notice] : undefined,\n safety: {\n verdict: SAFETY_VERDICT_MAP[safety.verdict],\n findings: safety.findings.map((f) => ({\n severity: f.severity,\n rule: f.rule,\n message: `${f.file}: ${f.detail}`,\n })),\n },\n };\n\n return { safety, gateResult, proEnabled: proStatus.enabled };\n}\n","/*\n * Rhize Commercial License — NOT covered by the MIT license that applies to\n * the rest of this repository. Production use of this module's functionality\n * requires a valid license key. See LICENSE and LICENSE-COMMERCIAL.\n * Copyright (c) 2026 Rhize Media\n */\nimport { randomUUID } from 'node:crypto';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { getHomeDir } from './config.js';\nimport type { Verdict as SafetyVerdict } from './gate/safety.js';\nimport type { SourceType } from './install/resolve.js';\nimport type { ForgeVerb } from './provenance.js';\n\n/**\n * Pending-ingestion queue — persists every skill install decided by `add`\n * (promote or hold) to `<SKILL_FORGE_HOME>/queue.json`, so the `--ingest`\n * Claude handoff (or a future `queue` command) can pick the entry back up.\n * The entry shape here, `schemas/queue-entry.schema.json`, and\n * `docs/queue-schema.md` are the single source of truth for the pending-queue\n * entry spec — keep all three in sync if the shape ever changes.\n */\n\n/** Suggested forge verb on a queue entry — the five-verb matrix defined by provenance.ts's FORGE_VERBS. */\nexport type SuggestedVerb = ForgeVerb;\nexport type { SafetyVerdict, SourceType };\n\nexport type QueueStatus = 'pending' | 'ingested' | 'dismissed';\n\nexport interface OverlapTopEntry {\n skill: string;\n score: number;\n}\n\nexport interface QueueEntryGate {\n license: string | null;\n safetyVerdict: SafetyVerdict;\n safetyFindings: string[];\n /** Top overlap matches against the configured skill set; empty on the free tier. */\n overlapTop: OverlapTopEntry[];\n suggestedVerb: SuggestedVerb | null;\n}\n\nexport interface QueueEntry {\n id: string;\n source: string;\n sourceType: SourceType;\n /** Final path after promote; null while the candidate is only held in quarantine. */\n installedPath: string | null;\n quarantinePath: string;\n gate: QueueEntryGate;\n status: QueueStatus;\n createdAt: string;\n}\n\nexport interface Queue {\n version: 1;\n entries: QueueEntry[];\n}\n\n/** Input for a new entry — `id` and `createdAt` are generated by `appendQueueEntry`. */\nexport type NewQueueEntry = Omit<QueueEntry, 'id' | 'createdAt'>;\n\nexport function getQueuePath(): string {\n return join(getHomeDir(), 'queue.json');\n}\n\n/** Reads the queue from disk, defaulting to an empty queue when the file doesn't exist yet. */\nexport function readQueue(): Queue {\n const path = getQueuePath();\n if (!existsSync(path)) {\n return { version: 1, entries: [] };\n }\n const raw = readFileSync(path, 'utf-8');\n const parsed = JSON.parse(raw) as Partial<Queue>;\n return { version: 1, entries: parsed.entries ?? [] };\n}\n\nfunction writeQueue(queue: Queue): void {\n mkdirSync(getHomeDir(), { recursive: true }); // recursive mkdir is a no-op when the dir exists\n writeFileSync(getQueuePath(), JSON.stringify(queue, null, 2) + '\\n', 'utf-8');\n}\n\n/** Appends a new entry (generating `id`/`createdAt`) and persists the queue. Returns the full entry. */\nexport function appendQueueEntry(entry: NewQueueEntry): QueueEntry {\n const queue = readQueue();\n const existingIds = new Set(queue.entries.map((e) => e.id));\n const full: QueueEntry = {\n ...entry,\n id: generateId(existingIds),\n createdAt: new Date().toISOString(),\n };\n queue.entries.push(full);\n writeQueue(queue);\n return full;\n}\n\n/** Applies a partial update to the entry with `id` (e.g. a status transition) and persists the queue. */\nexport function updateQueueEntry(id: string, patch: Partial<Omit<QueueEntry, 'id' | 'createdAt'>>): QueueEntry {\n const queue = readQueue();\n const index = queue.entries.findIndex((e) => e.id === id);\n if (index === -1) {\n throw new Error(`no queue entry with id: ${id}`);\n }\n const updated: QueueEntry = { ...queue.entries[index], ...patch };\n queue.entries[index] = updated;\n writeQueue(queue);\n return updated;\n}\n\n/** Short unique slug (8 hex chars from a UUID), collision-checked against ids already in the queue. */\nfunction generateId(existingIds: Set<string>): string {\n let id: string;\n do {\n id = randomUUID().split('-')[0];\n } while (existingIds.has(id));\n return id;\n}\n","import { spawnSync, type StdioOptions } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Generic ingestion handoff — after a skill is promoted (or held), optionally\n * launches a coding agent to run the deep decide/absorb pass described in the\n * bundled `assets/ingest-prompt.md`. Deliberately agent-neutral: nothing here\n * assumes Claude specifically (see `docs/queue-schema.md` for the pending\n * queue this hands off into).\n *\n * SECURITY: `config.handoffCommand` (and the built-in agent-detection\n * fallback) are **argv-style arrays**, not shell strings. `runHandoff` always\n * calls `spawnSync(bin, args, ...)` with no `shell` option, so a token like\n * `\"; rm -rf /\"` is passed to the child process as a single literal argv\n * element — it is never parsed by a shell. Do not add `shell: true` here, and\n * do not build the command by joining tokens into one string.\n */\n\n/** Minimal config shape this module needs — kept structural so it doesn't require editing src/config.ts's Config type. */\nexport interface HandoffConfig {\n /**\n * Optional argv-style command template, e.g.\n * `[\"claude\", \"-p\", \"read {prompt} and process the skill at {path}\"]`.\n * `{path}` is replaced with the absolute installed-skill path; `{prompt}`\n * is replaced with the absolute path to the bundled ingest-prompt.md.\n * An empty/absent array falls through to the detection chain below.\n */\n handoffCommand?: string[];\n}\n\nexport interface HandoffCommand {\n /** argv (index 0 is the binary) to spawn; empty when nothing runnable was found. */\n argv: string[];\n /** How this command was resolved. */\n source: 'template' | 'detected' | 'none';\n}\n\nexport interface HandoffDeps {\n /** Checks whether a binary is available on PATH. Overridable for tests; defaults to a real `which`/`where` check. */\n isOnPath?: (bin: string) => boolean;\n}\n\nexport interface RunHandoffOptions extends HandoffDeps {\n /** stdio for the spawned process. Defaults to 'inherit' (the handoff is an interactive agent session). Tests can override to capture output. */\n stdio?: StdioOptions;\n /** Overrides argv[0]-execution for tests; defaults to node:child_process spawnSync. */\n spawn?: typeof spawnSync;\n}\n\nexport interface RunHandoffResult {\n /** Whether a process was actually spawned (successfully or not). */\n ran: boolean;\n /** The resolved command, for callers that want to log/inspect it. */\n command: HandoffCommand;\n /** The message printed to the user in print-only mode or when the resolved binary was missing. */\n printed?: string;\n /** Set when spawning failed (e.g. ENOENT). */\n error?: string;\n}\n\n/** Binaries checked, in order, when no `handoffCommand` template is configured. */\nconst KNOWN_AGENT_BINARIES = ['claude', 'codex', 'cursor-agent', 'windsurf', 'opencode', 'gemini'];\n\n/**\n * The generic ingest instruction used in handoff argv templates — `{prompt}` and `{path}` are\n * replaced by `substitute()`. Shared by the detected-binary fallback below, the per-agent\n * templates in src/agents.ts, and `skill-forge init`'s best-effort fallback.\n */\nexport const HANDOFF_INSTRUCTION = 'Read {prompt} and follow its instructions for the skill installed at {path}';\n\n/**\n * Absolute path to the bundled ingest-prompt.md, resolved relative to this\n * module so it works both from source (tests run against `src/`) and from\n * the built package (`dist/`).\n *\n * tsup's `publicDir` copies the *contents* of `assets/` into `dist/`\n * (flattening one level), so the built layout is `dist/ingest-prompt.md`\n * sitting next to the bundled `dist/cli.js` — see tsup.config.ts. The dev/\n * test layout is the repo's own `assets/ingest-prompt.md`, one level up from\n * `src/`.\n */\nexport function resolvePromptPath(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n const builtCandidate = join(here, 'ingest-prompt.md');\n if (existsSync(builtCandidate)) return builtCandidate;\n return join(here, '..', 'assets', 'ingest-prompt.md');\n}\n\nfunction defaultIsOnPath(bin: string): boolean {\n const finder = process.platform === 'win32' ? 'where' : 'which';\n const result = spawnSync(finder, [bin], { stdio: 'ignore' });\n return result.status === 0;\n}\n\n/** Replaces `{path}` and `{prompt}` tokens in each argv element; unknown `{word}` tokens are left as-is. */\nfunction substitute(template: string[], vars: Record<string, string>): string[] {\n return template.map((token) => token.replace(/\\{(\\w+)\\}/g, (match, key: string) => vars[key] ?? match));\n}\n\n/**\n * Resolves the handoff command via a fallback chain:\n * 1. `config.handoffCommand` template, with `{path}`/`{prompt}` substituted.\n * 2. The first known agent binary found on PATH, invoked generically with\n * the ingest prompt.\n * 3. Nothing runnable (`source: 'none'`) — the caller should print\n * instructions instead of spawning anything.\n */\nexport function buildHandoffCommand(config: HandoffConfig, installedPath: string, deps: HandoffDeps = {}): HandoffCommand {\n const isOnPath = deps.isOnPath ?? defaultIsOnPath;\n const promptPath = resolvePromptPath();\n const vars = { path: installedPath, prompt: promptPath };\n\n if (config.handoffCommand && config.handoffCommand.length > 0) {\n return { argv: substitute(config.handoffCommand, vars), source: 'template' };\n }\n\n for (const bin of KNOWN_AGENT_BINARIES) {\n if (isOnPath(bin)) {\n return { argv: substitute([bin, '-p', HANDOFF_INSTRUCTION], vars), source: 'detected' };\n }\n }\n\n return { argv: [], source: 'none' };\n}\n\n/** The message shown when there's nothing runnable to spawn (print-only mode, or a resolved binary that turned out missing). */\nfunction printOnlyMessage(installedPath: string): string {\n const promptPath = resolvePromptPath();\n return (\n '[skill-forge] no agent handoff available — run one yourself:\\n' +\n ` Prompt: ${promptPath}\\n` +\n ` Skill path: ${installedPath}`\n );\n}\n\n/**\n * Executes the resolved handoff command WITHOUT a shell (`spawnSync(bin,\n * args, ...)` — no `shell: true`), so argv-style templates can never be\n * shell-interpreted. Falls back to printing the command for the user to run\n * manually in print-only mode, or if the resolved binary isn't actually on\n * PATH at spawn time (ENOENT).\n */\nexport function runHandoff(config: HandoffConfig, installedPath: string, options: RunHandoffOptions = {}): RunHandoffResult {\n const command = buildHandoffCommand(config, installedPath, options);\n const spawn = options.spawn ?? spawnSync;\n\n if (command.source === 'none' || command.argv.length === 0) {\n const printed = printOnlyMessage(installedPath);\n console.log(printed);\n return { ran: false, command, printed };\n }\n\n const [bin, ...args] = command.argv;\n const result = spawn(bin, args, { stdio: options.stdio ?? 'inherit' });\n const error = result.error as NodeJS.ErrnoException | undefined;\n\n if (error?.code === 'ENOENT') {\n const printed = printOnlyMessage(installedPath);\n console.log(`[skill-forge] ${bin} not found on PATH — run this yourself:`);\n console.log(printed);\n return { ran: false, command, printed, error: error.message };\n }\n if (error) {\n console.error(`[skill-forge] failed to launch handoff: ${error.message}`);\n return { ran: false, command, error: error.message };\n }\n\n return { ran: true, command };\n}\n","import { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { createInterface, type Interface } from 'node:readline/promises';\nimport { AGENTS, detectAgentRoot, type Agent, type DetectedRoot } from '../agents.js';\nimport { getConfigPath, loadConfig, saveConfig, type Config } from '../config.js';\nimport { HANDOFF_INSTRUCTION } from '../handoff.js';\n\nexport interface InitOptions {\n /** Non-interactive: use all detected agents as gate targets, first as the default target. */\n defaults?: boolean;\n /** Print detection results only — no writes. */\n list?: boolean;\n /** Overrides for tests; default to the real process cwd / home dir. */\n cwd?: string;\n home?: string;\n}\n\ninterface DetectedAgent extends DetectedRoot {\n agent: Agent;\n}\n\nfunction detectAgents(cwd: string, home: string): DetectedAgent[] {\n const found: DetectedAgent[] = [];\n for (const agent of AGENTS) {\n const hit = detectAgentRoot(agent, cwd, home);\n if (hit) found.push({ agent, ...hit });\n }\n return found;\n}\n\nfunction printDetectionResults(detected: DetectedAgent[]): void {\n console.log(`[skill-forge] init — agent detection (${AGENTS.length} known agents)`);\n if (detected.length === 0) {\n console.log(' none detected');\n return;\n }\n for (const d of detected) {\n console.log(` ${d.agent.displayName} (${d.agent.id}) — ${d.scope}: ${d.root}`);\n }\n}\n\nfunction handoffTemplateFor(agent: Agent): string[] {\n // Best-effort fallback when we don't have a confirmed CLI invocation: use the agent id as\n // the binary name. May not match the real CLI for lesser-known agents — defaultHandoffTemplate\n // should be preferred whenever it's set.\n return agent.defaultHandoffTemplate ?? [agent.id, '-p', HANDOFF_INSTRUCTION];\n}\n\nfunction buildConfig(\n selected: DetectedAgent[],\n defaultTargetEntry: DetectedAgent | undefined,\n handoffAgent: Agent | undefined,\n): Config {\n const existing = loadConfig();\n return {\n ...existing,\n skillsRoots: selected.map((s) => s.root),\n agents: selected.map((s) => ({ id: s.agent.id, skillsRoot: s.root })),\n defaultTarget: defaultTargetEntry?.root ?? existing.defaultTarget,\n handoffCommand: handoffAgent ? handoffTemplateFor(handoffAgent) : existing.handoffCommand,\n };\n}\n\nfunction printSummary(config: Config): void {\n console.log(`\\n[skill-forge] wrote config to ${getConfigPath()}`);\n console.log(` gate targets: ${config.skillsRoots.join(', ')}`);\n if (config.defaultTarget) console.log(` default target: ${config.defaultTarget}`);\n if (config.handoffCommand?.length) console.log(` handoff command: ${config.handoffCommand.join(' ')}`);\n}\n\n/** Parses a 1-based menu answer into a zero-based index, or undefined when it isn't a listed number. */\nfunction parseChoice(answer: string, max: number): number | undefined {\n const n = Number(answer);\n return Number.isInteger(n) && n >= 1 && n <= max ? n - 1 : undefined;\n}\n\nasync function promptMultiSelect(rl: Interface, detected: DetectedAgent[]): Promise<DetectedAgent[]> {\n const chosen = new Set(detected.map((_, i) => i)); // all detected roots pre-selected\n for (;;) {\n console.log('\\nGate targets (all detected roots pre-selected — type a number to toggle, Enter to confirm):');\n detected.forEach((d, i) => {\n const mark = chosen.has(i) ? '[x]' : '[ ]';\n console.log(` ${mark} ${i + 1}. ${d.agent.displayName} (${d.scope}) — ${d.root}`);\n });\n const answer = (await rl.question('> ')).trim();\n if (answer === '') break;\n const idx = parseChoice(answer, detected.length);\n if (idx !== undefined) {\n if (chosen.has(idx)) chosen.delete(idx);\n else chosen.add(idx);\n } else {\n console.log(' (invalid selection — enter a listed number, or press Enter to confirm)');\n }\n }\n return detected.filter((_, i) => chosen.has(i));\n}\n\nasync function promptDefaultTarget(rl: Interface, selected: DetectedAgent[]): Promise<DetectedAgent> {\n console.log('\\nDefault promotion target:');\n selected.forEach((d, i) => console.log(` ${i + 1}. ${d.agent.displayName} — ${d.root}`));\n const answer = (await rl.question(`Choose [1-${selected.length}] (default 1): `)).trim();\n const idx = answer === '' ? 0 : (parseChoice(answer, selected.length) ?? 0);\n return selected[idx];\n}\n\nasync function promptHandoffAgent(rl: Interface, detected: DetectedAgent[]): Promise<Agent | undefined> {\n console.log('\\nHandoff agent (used to hand off follow-up prompts after promotion; optional):');\n detected.forEach((d, i) => {\n const note = d.agent.defaultHandoffTemplate ? '' : ' (no confirmed CLI — best-effort)';\n console.log(` ${i + 1}. ${d.agent.displayName}${note}`);\n });\n const answer = (await rl.question(`Choose [1-${detected.length}] or Enter to skip: `)).trim();\n if (answer === '') return undefined;\n const idx = parseChoice(answer, detected.length);\n return idx === undefined ? undefined : detected[idx].agent;\n}\n\n/**\n * `skill-forge init` — setup wizard. Probes all known agents (see src/agents.ts), lets the\n * user pick which detected skill roots become gate targets, a default promotion target, and\n * an optional handoff agent, then writes the result via saveConfig.\n */\nexport async function init(options: InitOptions = {}): Promise<void> {\n const cwd = options.cwd ?? process.cwd();\n const home = options.home ?? homedir();\n const detected = detectAgents(cwd, home);\n\n if (options.list) {\n printDetectionResults(detected);\n return;\n }\n\n if (detected.length === 0) {\n console.log('[skill-forge] No known agent skill directories were found.');\n console.log(' Install a coding agent (Claude Code, Codex, Cursor, ...) or create a skills root, then re-run `skill-forge init`.');\n return;\n }\n\n if (options.defaults) {\n const config = buildConfig(detected, detected[0], undefined);\n saveConfig(config);\n printSummary(config);\n return;\n }\n\n if (!process.stdin.isTTY) {\n console.log('[skill-forge] Not an interactive terminal — run with --defaults for non-interactive setup.');\n return;\n }\n\n printDetectionResults(detected);\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const selected = await promptMultiSelect(rl, detected);\n if (selected.length === 0) {\n console.log('[skill-forge] No targets selected — aborting, nothing written.');\n return;\n }\n const target = await promptDefaultTarget(rl, selected);\n const handoffAgent = await promptHandoffAgent(rl, detected);\n const config = buildConfig(selected, target, handoffAgent);\n saveConfig(config);\n printSummary(config);\n } finally {\n rl.close();\n }\n}\n\nexport interface OfferInitOptions {\n json?: boolean;\n yes?: boolean;\n}\n\n/**\n * Prints a one-line offer to run `skill-forge init` when no config.json exists yet, and (in an\n * interactive terminal) asks whether to run it now. Never fires for automation: no-ops entirely\n * when stdin isn't a TTY, or `--json`/`--yes` was passed, so scripted invocations never see this\n * output or block on a prompt. Called at startup by add/scan/list/status.\n */\nexport async function maybeOfferInit(options: OfferInitOptions = {}): Promise<void> {\n if (options.json || options.yes || !process.stdin.isTTY) return;\n if (existsSync(getConfigPath())) return;\n\n console.log('[skill-forge] No config found — run `skill-forge init` to detect your agents and set gate targets.');\n\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n let answer = '';\n try {\n answer = (await rl.question('Run `skill-forge init` now? [y/N] ')).trim().toLowerCase();\n } finally {\n rl.close();\n }\n if (answer === 'y' || answer === 'yes') {\n await init();\n }\n}\n","import { homedir } from 'node:os';\nimport { join, resolve } from 'node:path';\nimport { HANDOFF_INSTRUCTION } from './handoff.js';\nimport { isDir } from './util.js';\n\n/**\n * A known agent-skill ecosystem: where it looks for skills, and (optionally) how to hand a\n * prompt off to it once a skill has been promoted.\n *\n * Path data (`projectRoot` / `globalRoot`) is sourced directly from the vercel-labs/skills\n * README's \"Supported Agents\" table\n * (raw.githubusercontent.com/vercel-labs/skills/main/README.md, fetched 2026-07-11) — the\n * canonical list the `skills` CLI itself installs to. Entries marked `verified: false` could\n * not be confirmed against that source and should be treated as best-effort, never invented.\n */\nexport interface Agent {\n id: string;\n displayName: string;\n /** Project-relative skills root (relative to cwd), e.g. `.claude/skills`. */\n projectRoot?: string;\n /** User-home skills root, `~`-relative, e.g. `~/.claude/skills`. Absent = no global install path (project-only agent). */\n globalRoot?: string;\n /** False if this entry's paths are inferred rather than confirmed against the vercel-labs/skills source — never invented, always disclosed. */\n verified: boolean;\n /**\n * Best-effort argv template for handing a prompt to this agent's CLI, e.g.\n * `['claude', '-p', '{prompt}']`. Substituted by `src/handoff.ts` — never shell-parsed, so\n * each element is a literal argv token. Undefined where we don't have a confirmed CLI binary\n * name (IDE-only agents, or agents we couldn't verify an invocation for) — callers should\n * fall back to something explicit rather than guess.\n */\n defaultHandoffTemplate?: string[];\n /**\n * True if either the project or global skills root already exists on disk — i.e. this agent\n * looks installed. `cwd`/`home` default to the real process cwd / home dir; pass overrides in\n * tests to probe against temp dirs instead of the real filesystem.\n */\n detect(cwd?: string, home?: string): boolean;\n}\n\ninterface AgentSpec {\n id: string;\n displayName: string;\n projectRoot?: string;\n globalRoot?: string;\n verified?: boolean;\n defaultHandoffTemplate?: string[];\n}\n\n/** Resolves a project-relative skills root against `cwd`, or undefined if the agent has none. */\nfunction projectPath(root: string | undefined, cwd: string): string | undefined {\n return root ? resolve(cwd, root) : undefined;\n}\n\n/** Resolves a `~`-relative global skills root against `home`, or undefined if the agent has none. */\nfunction globalPath(root: string | undefined, home: string): string | undefined {\n if (!root) return undefined;\n if (root === '~') return home;\n if (root.startsWith('~/')) return join(home, root.slice(2));\n return root;\n}\n\nfunction makeAgent(spec: AgentSpec): Agent {\n const agent: Agent = {\n id: spec.id,\n displayName: spec.displayName,\n projectRoot: spec.projectRoot,\n globalRoot: spec.globalRoot,\n verified: spec.verified ?? true,\n defaultHandoffTemplate: spec.defaultHandoffTemplate,\n detect: (cwd?: string, home?: string): boolean => detectAgentRoot(agent, cwd, home) !== undefined,\n };\n return agent;\n}\n\n/**\n * Resolves the absolute filesystem path for one of an agent's roots, without probing for\n * existence. `cwd`/`home` default to the real process cwd / home dir.\n */\nexport function resolveAgentRoot(\n agent: Agent,\n scope: 'project' | 'global',\n cwd: string = process.cwd(),\n home: string = homedir(),\n): string | undefined {\n return scope === 'project' ? projectPath(agent.projectRoot, cwd) : globalPath(agent.globalRoot, home);\n}\n\n/** A skills root that exists on disk for an agent: which scope matched, and its absolute path. */\nexport interface DetectedRoot {\n scope: 'project' | 'global';\n root: string;\n}\n\n/**\n * Probes an agent's skills roots on disk — project first, then global — and returns the first\n * that exists, or undefined when neither does. Single source of truth for agent detection:\n * `Agent#detect` and `skill-forge init` both resolve through it. `cwd`/`home` default to the\n * real process cwd / home dir; pass overrides in tests to probe temp dirs instead.\n */\nexport function detectAgentRoot(\n agent: Agent,\n cwd: string = process.cwd(),\n home: string = homedir(),\n): DetectedRoot | undefined {\n for (const scope of ['project', 'global'] as const) {\n const root = resolveAgentRoot(agent, scope, cwd, home);\n if (root && isDir(root)) return { scope, root };\n }\n return undefined;\n}\n\n/** Full agent matrix, in the order the vercel-labs/skills README lists them. */\nexport const AGENTS: Agent[] = (\n [\n { id: 'aider-desk', displayName: 'AiderDesk', projectRoot: '.aider-desk/skills', globalRoot: '~/.aider-desk/skills' },\n { id: 'amp', displayName: 'Amp', projectRoot: '.agents/skills', globalRoot: '~/.config/agents/skills' },\n { id: 'replit', displayName: 'Replit', projectRoot: '.agents/skills', globalRoot: '~/.config/agents/skills' },\n { id: 'universal', displayName: 'Universal', projectRoot: '.agents/skills', globalRoot: '~/.config/agents/skills' },\n { id: 'antigravity', displayName: 'Antigravity', projectRoot: '.agents/skills', globalRoot: '~/.gemini/antigravity/skills' },\n {\n id: 'antigravity-cli',\n displayName: 'Antigravity CLI',\n projectRoot: '.agents/skills',\n globalRoot: '~/.gemini/antigravity-cli/skills',\n },\n { id: 'astrbot', displayName: 'AstrBot', projectRoot: 'data/skills', globalRoot: '~/.astrbot/data/skills' },\n { id: 'autohand-code', displayName: 'Autohand Code CLI', projectRoot: '.autohand/skills', globalRoot: '~/.autohand/skills' },\n { id: 'augment', displayName: 'Augment', projectRoot: '.augment/skills', globalRoot: '~/.augment/skills' },\n { id: 'bob', displayName: 'IBM Bob', projectRoot: '.bob/skills', globalRoot: '~/.bob/skills' },\n {\n id: 'claude-code',\n displayName: 'Claude Code',\n projectRoot: '.claude/skills',\n globalRoot: '~/.claude/skills',\n defaultHandoffTemplate: ['claude', '-p', HANDOFF_INSTRUCTION],\n },\n { id: 'openclaw', displayName: 'OpenClaw', projectRoot: 'skills', globalRoot: '~/.openclaw/skills' },\n { id: 'cline', displayName: 'Cline', projectRoot: '.agents/skills', globalRoot: '~/.agents/skills' },\n { id: 'dexto', displayName: 'Dexto', projectRoot: '.agents/skills', globalRoot: '~/.agents/skills' },\n { id: 'kimi-code-cli', displayName: 'Kimi Code CLI', projectRoot: '.agents/skills', globalRoot: '~/.agents/skills' },\n { id: 'loaf', displayName: 'Loaf', projectRoot: '.agents/skills', globalRoot: '~/.agents/skills' },\n { id: 'warp', displayName: 'Warp', projectRoot: '.agents/skills', globalRoot: '~/.agents/skills' },\n { id: 'zed', displayName: 'Zed', projectRoot: '.agents/skills', globalRoot: '~/.agents/skills' },\n {\n id: 'codearts-agent',\n displayName: 'CodeArts Agent',\n projectRoot: '.codeartsdoer/skills',\n globalRoot: '~/.codeartsdoer/skills',\n },\n { id: 'codebuddy', displayName: 'CodeBuddy', projectRoot: '.codebuddy/skills', globalRoot: '~/.codebuddy/skills' },\n { id: 'codemaker', displayName: 'Codemaker', projectRoot: '.codemaker/skills', globalRoot: '~/.codemaker/skills' },\n { id: 'codestudio', displayName: 'Code Studio', projectRoot: '.codestudio/skills', globalRoot: '~/.codestudio/skills' },\n {\n id: 'codex',\n displayName: 'Codex',\n projectRoot: '.agents/skills',\n globalRoot: '~/.codex/skills',\n defaultHandoffTemplate: ['codex', 'exec', HANDOFF_INSTRUCTION],\n },\n {\n id: 'command-code',\n displayName: 'Command Code',\n projectRoot: '.commandcode/skills',\n globalRoot: '~/.commandcode/skills',\n },\n { id: 'continue', displayName: 'Continue', projectRoot: '.continue/skills', globalRoot: '~/.continue/skills' },\n { id: 'cortex', displayName: 'Cortex Code', projectRoot: '.cortex/skills', globalRoot: '~/.snowflake/cortex/skills' },\n { id: 'crush', displayName: 'Crush', projectRoot: '.crush/skills', globalRoot: '~/.config/crush/skills' },\n { id: 'cursor', displayName: 'Cursor', projectRoot: '.agents/skills', globalRoot: '~/.cursor/skills' },\n {\n id: 'deepagents',\n displayName: 'Deep Agents',\n projectRoot: '.agents/skills',\n globalRoot: '~/.deepagents/agent/skills',\n },\n { id: 'devin', displayName: 'Devin for Terminal', projectRoot: '.devin/skills', globalRoot: '~/.config/devin/skills' },\n { id: 'droid', displayName: 'Droid', projectRoot: '.factory/skills', globalRoot: '~/.factory/skills' },\n { id: 'eve', displayName: 'Eve', projectRoot: 'agent/skills' },\n { id: 'firebender', displayName: 'Firebender', projectRoot: '.agents/skills', globalRoot: '~/.firebender/skills' },\n { id: 'forgecode', displayName: 'ForgeCode', projectRoot: '.forge/skills', globalRoot: '~/.forge/skills' },\n {\n id: 'gemini-cli',\n displayName: 'Gemini CLI',\n projectRoot: '.agents/skills',\n globalRoot: '~/.gemini/skills',\n defaultHandoffTemplate: ['gemini', '-p', HANDOFF_INSTRUCTION],\n },\n {\n id: 'github-copilot',\n displayName: 'GitHub Copilot',\n projectRoot: '.agents/skills',\n globalRoot: '~/.copilot/skills',\n },\n { id: 'goose', displayName: 'Goose', projectRoot: '.goose/skills', globalRoot: '~/.config/goose/skills' },\n { id: 'hermes-agent', displayName: 'Hermes Agent', projectRoot: '.hermes/skills', globalRoot: '~/.hermes/skills' },\n {\n id: 'inference-sh',\n displayName: 'inference.sh',\n projectRoot: '.inferencesh/skills',\n globalRoot: '~/.inferencesh/skills',\n },\n { id: 'jazz', displayName: 'Jazz', projectRoot: '.jazz/skills', globalRoot: '~/.jazz/skills' },\n { id: 'junie', displayName: 'Junie', projectRoot: '.junie/skills', globalRoot: '~/.junie/skills' },\n { id: 'iflow-cli', displayName: 'iFlow CLI', projectRoot: '.iflow/skills', globalRoot: '~/.iflow/skills' },\n { id: 'kilo', displayName: 'Kilo Code', projectRoot: '.kilocode/skills', globalRoot: '~/.kilocode/skills' },\n { id: 'kiro-cli', displayName: 'Kiro CLI', projectRoot: '.kiro/skills', globalRoot: '~/.kiro/skills' },\n { id: 'kode', displayName: 'Kode', projectRoot: '.kode/skills', globalRoot: '~/.kode/skills' },\n { id: 'lingma', displayName: 'Lingma', projectRoot: '.lingma/skills', globalRoot: '~/.lingma/skills' },\n { id: 'mcpjam', displayName: 'MCPJam', projectRoot: '.mcpjam/skills', globalRoot: '~/.mcpjam/skills' },\n { id: 'mistral-vibe', displayName: 'Mistral Vibe', projectRoot: '.vibe/skills', globalRoot: '~/.vibe/skills' },\n { id: 'moxby', displayName: 'Moxby', projectRoot: '.moxby/skills', globalRoot: '~/.moxby/skills' },\n { id: 'mux', displayName: 'Mux', projectRoot: '.mux/skills', globalRoot: '~/.mux/skills' },\n {\n id: 'opencode',\n displayName: 'OpenCode',\n projectRoot: '.agents/skills',\n globalRoot: '~/.config/opencode/skills',\n defaultHandoffTemplate: ['opencode', 'run', HANDOFF_INSTRUCTION],\n },\n { id: 'openhands', displayName: 'OpenHands', projectRoot: '.openhands/skills', globalRoot: '~/.openhands/skills' },\n { id: 'ona', displayName: 'Ona', projectRoot: '.ona/skills', globalRoot: '~/.ona/skills' },\n { id: 'pi', displayName: 'Pi', projectRoot: '.pi/skills', globalRoot: '~/.pi/agent/skills' },\n { id: 'qoder', displayName: 'Qoder', projectRoot: '.qoder/skills', globalRoot: '~/.qoder/skills' },\n { id: 'qoder-cn', displayName: 'Qoder CN', projectRoot: '.qoder/skills', globalRoot: '~/.qoder-cn/skills' },\n { id: 'qwen-code', displayName: 'Qwen Code', projectRoot: '.qwen/skills', globalRoot: '~/.qwen/skills' },\n { id: 'reasonix', displayName: 'Reasonix', projectRoot: '.reasonix/skills', globalRoot: '~/.reasonix/skills' },\n { id: 'rovodev', displayName: 'Rovo Dev', projectRoot: '.rovodev/skills', globalRoot: '~/.rovodev/skills' },\n { id: 'roo', displayName: 'Roo Code', projectRoot: '.roo/skills', globalRoot: '~/.roo/skills' },\n {\n id: 'tabnine-cli',\n displayName: 'Tabnine CLI',\n projectRoot: '.tabnine/agent/skills',\n globalRoot: '~/.tabnine/agent/skills',\n },\n { id: 'terramind', displayName: 'Terramind', projectRoot: '.terramind/skills', globalRoot: '~/.terramind/skills' },\n { id: 'tinycloud', displayName: 'Tinycloud', projectRoot: '.tinycloud/skills', globalRoot: '~/.tinycloud/skills' },\n { id: 'trae', displayName: 'Trae', projectRoot: '.trae/skills', globalRoot: '~/.trae/skills' },\n { id: 'trae-cn', displayName: 'Trae CN', projectRoot: '.trae/skills', globalRoot: '~/.trae-cn/skills' },\n { id: 'windsurf', displayName: 'Windsurf', projectRoot: '.windsurf/skills', globalRoot: '~/.codeium/windsurf/skills' },\n { id: 'zcode', displayName: 'ZCode', projectRoot: '.zcode/skills', globalRoot: '~/.zcode/skills' },\n { id: 'zencoder', displayName: 'Zencoder', projectRoot: '.zencoder/skills', globalRoot: '~/.zencoder/skills' },\n { id: 'zenflow', displayName: 'Zenflow', projectRoot: '.zencoder/skills', globalRoot: '~/.zencoder/skills' },\n { id: 'neovate', displayName: 'Neovate', projectRoot: '.neovate/skills', globalRoot: '~/.neovate/skills' },\n { id: 'pochi', displayName: 'Pochi', projectRoot: '.pochi/skills', globalRoot: '~/.pochi/skills' },\n { id: 'promptscript', displayName: 'PromptScript', projectRoot: '.agents/skills' },\n { id: 'adal', displayName: 'AdaL', projectRoot: '.adal/skills', globalRoot: '~/.adal/skills' },\n ] satisfies AgentSpec[]\n).map(makeAgent);\n\n/** Looks up a known agent by its `--agent` id (e.g. `claude-code`). */\nexport function getAgent(id: string): Agent | undefined {\n return AGENTS.find((a) => a.id === id);\n}\n","import { loadConfig } from '../config.js';\nimport { resolve as resolveSource } from '../install/resolve.js';\nimport { quarantine } from '../install/quarantine.js';\nimport { reject } from '../install/promote.js';\nimport { printGateOutput, runGate } from './gatePipeline.js';\nimport { maybeOfferInit } from './init.js';\n\nexport interface ScanOptions {\n json?: boolean;\n}\n\n/**\n * `skill-forge scan <source>` — run the safety gate against a skill without\n * installing it into a target skills root: quarantine-install for\n * inspection, gate + report, then always clean up the quarantine sandbox\n * regardless of verdict. Exits nonzero when the safety verdict is \"block\".\n * `--json` prints the same gate-result payload shape as `add`'s.\n */\nexport async function scan(source: string, options: ScanOptions = {}): Promise<void> {\n await maybeOfferInit({ json: options.json });\n\n const config = loadConfig();\n const skillsRoot = config.skillsRoots[0];\n if (!skillsRoot) {\n throw new Error('no skills root configured; set skillsRoots in the config');\n }\n\n const resolved = resolveSource(source);\n const entry = await quarantine(resolved, config.quarantineDir);\n\n try {\n if (entry.skillDirs.length === 0) {\n console.error(`[skill-forge] no SKILL.md found in installed source: ${source}`);\n process.exitCode = 1;\n return;\n }\n if (entry.skillDirs.length > 1) {\n console.error(\n `[skill-forge] ${entry.skillDirs.length} skills found in ${source}; scanning the first:\\n` +\n entry.skillDirs.map((d) => ` - ${d}`).join('\\n')\n );\n }\n\n const { safety, gateResult } = await runGate(entry.skillDirs[0], skillsRoot);\n\n printGateOutput(gateResult, options.json);\n\n if (safety.verdict === 'block') {\n process.exitCode = 1;\n }\n } finally {\n reject(entry);\n }\n}\n","import { join } from 'node:path';\nimport { loadConfig } from '../config.js';\nimport { findSkillDirs, listQuarantineIds } from '../install/quarantine.js';\nimport { readFrontmatter } from '../gate/overlap.js';\nimport { maybeOfferInit } from './init.js';\n\n/**\n * `skill-forge list` — list skills currently held in quarantine (installed\n * but not yet promoted or rejected via `add`).\n */\nexport async function list(): Promise<void> {\n await maybeOfferInit();\n\n const config = loadConfig();\n const { quarantineDir } = config;\n\n const ids = listQuarantineIds(quarantineDir);\n\n if (ids.length === 0) {\n console.log(`[skill-forge] quarantine dir: ${quarantineDir}`);\n console.log(' (empty — nothing held)');\n return;\n }\n\n console.log(`[skill-forge] ${ids.length} held in quarantine (${quarantineDir}):`);\n for (const id of ids) {\n const skillDirs = findSkillDirs(join(quarantineDir, id));\n if (skillDirs.length === 0) {\n console.log(` ${id} (no SKILL.md found)`);\n continue;\n }\n const names = skillDirs.map((dir) => {\n try {\n return readFrontmatter(join(dir, 'SKILL.md')).name;\n } catch {\n return dir;\n }\n });\n console.log(` ${id} -> ${names.join(', ')}`);\n }\n}\n","import { loadConfig } from '../config.js';\nimport { listQuarantineIds } from '../install/quarantine.js';\nimport { maybeOfferInit } from './init.js';\n\n/**\n * `skill-forge status` — show current configuration and a summary of what's\n * held in quarantine.\n */\nexport async function status(): Promise<void> {\n await maybeOfferInit();\n\n const config = loadConfig();\n const heldCount = listQuarantineIds(config.quarantineDir).length;\n\n console.log('[skill-forge] status');\n console.log(` skillsRoots: ${config.skillsRoots.join(', ')}`);\n console.log(` quarantineDir: ${config.quarantineDir}`);\n console.log(` strictness: ${config.strictness}`);\n console.log(` held in quarantine: ${heldCount}`);\n}\n"],"mappings":"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","names":["createInterface","existsSync","existsSync","mkdirSync","join","join","mkdirSync","existsSync","walk","reject","cpSync","existsSync","mkdirSync","basename","join","existsSync","writeFileSync","join","existsSync","readFileSync","readdirSync","rmSync","statSync","basename","join","resolve","statSync","homedir","join","resolve","existsSync","statSync","join","basename","rmSync","readdirSync","readFileSync","m","readFileSync","readdirSync","statSync","extname","join","SKIP_DIRS","existsSync","readFileSync","readdirSync","basename","dirname","join","resolve","readFileSync","basename","dirname","existsSync","readdirSync","join","resolve","status","randomUUID","existsSync","mkdirSync","readFileSync","writeFileSync","join","join","existsSync","readFileSync","mkdirSync","writeFileSync","randomUUID","existsSync","dirname","join","spawn","existsSync","homedir","homedir","join","resolve","resolve","join","homedir","homedir","existsSync","createInterface","join","join","require"]}