@rhize/skill-forge 0.1.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/LICENSE +21 -0
- package/README.md +218 -0
- package/dist/cli.js +1341 -0
- package/dist/cli.js.map +1 -0
- package/package.json +48 -0
package/dist/cli.js.map
ADDED
|
@@ -0,0 +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"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rhize/skill-forge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"description": "The supply-chain gate for agent skills — quarantine, scan, and curate agent skills before they enter your set",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"bin": {
|
|
11
|
+
"skill-forge": "dist/cli.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"skills",
|
|
20
|
+
"agents",
|
|
21
|
+
"claude",
|
|
22
|
+
"cli",
|
|
23
|
+
"security",
|
|
24
|
+
"supply-chain",
|
|
25
|
+
"skills.sh"
|
|
26
|
+
],
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/Rhize-Media/skill-forge.git"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsup",
|
|
36
|
+
"dev": "tsup --watch",
|
|
37
|
+
"test": "vitest run"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"commander": "^12.1.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^22.10.5",
|
|
44
|
+
"tsup": "^8.3.5",
|
|
45
|
+
"typescript": "^5.7.3",
|
|
46
|
+
"vitest": "^2.1.8"
|
|
47
|
+
}
|
|
48
|
+
}
|