@rhize/skill-forge 0.5.0 → 0.6.1
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/README.md +36 -5
- package/dist/cli.js +413 -256
- package/dist/cli.js.map +1 -1
- package/dist/ingest-prompt.md +103 -10
- package/package.json +1 -1
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/errors.ts","../src/util.ts","../src/install/quarantine.ts","../src/install/promote.ts","../src/install/promoteMcp.ts","../src/install/mcpCandidate.ts","../src/provenance.ts","../src/commands/gatePipeline.ts","../src/gate/profile.ts","../src/gate/safety.ts","../src/gate/overlap.ts","../src/gate/mcpSafety.ts","../src/gate/mcpOverlap.ts","../src/license.ts","../src/report/render.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';\nimport type { ArtifactType } from './install/resolve.js';\n\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json') as { version: string };\n\n/** Commander option parser for `--artifact` — throws UserError (caught below) on anything but skill|mcp. */\nfunction parseArtifact(value: string): ArtifactType {\n if (value !== 'skill' && value !== 'mcp') {\n throw new UserError(`--artifact must be \"skill\" or \"mcp\" (got \"${value}\")`);\n }\n return value;\n}\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 defaultTarget, then 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 .option('--artifact <type>', 'gate as a \"skill\" or an \"mcp\" server (default: skill)', parseArtifact)\n .option(\n '--mcp-target <file>',\n 'MCP config file to promote into (mcp artifacts only; defaults to config.mcpTargets[0], then <cwd>/.mcp.json)'\n )\n .option('--force', 'overwrite an existing same-name MCP server entry when promoting (mcp artifacts only)')\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 .option('--artifact <type>', 'gate as a \"skill\" or an \"mcp\" server (default: skill)', parseArtifact)\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 { join } from 'node:path';\nimport { loadConfig, type Config } from '../config.js';\nimport { resolve as resolveSource, type ArtifactType } from '../install/resolve.js';\nimport { quarantine } from '../install/quarantine.js';\nimport { promote, reject, hold } from '../install/promote.js';\nimport { promoteMcp } from '../install/promoteMcp.js';\nimport type { McpServerProfile } from '../install/promoteMcp.js';\nimport { resolveMcpCandidateDetail } from '../install/mcpCandidate.js';\nimport { appendProvenance } from '../provenance.js';\nimport { printGateOutput, runGate, resolveMcpCandidatePath } 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';\nconst MCP_PRO_FEATURE_LABEL = 'overlap analysis and the pending-ingestion queue';\n\nexport interface AddOptions {\n yes?: boolean;\n target?: string;\n json?: boolean;\n ingest?: boolean;\n /** Gate/promote as a skill (default) or an MCP server — v0.5. */\n artifact?: ArtifactType;\n /** MCP config file to promote into (mcp artifacts only). Defaults to config.mcpTargets[0], then <cwd>/.mcp.json. */\n mcpTarget?: string;\n /** Overwrite an existing same-name MCP server entry when promoting (mcp artifacts only). */\n force?: 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 artifact: ArtifactType = options.artifact ?? 'skill';\n\n if (artifact === 'mcp') {\n await addMcp(source, config, options);\n return;\n }\n\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, artifact);\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, {\n proFeatureLabel: PRO_FEATURE_LABEL,\n strictness: config.strictness,\n artifact,\n });\n const { profile, overlap } = gateResult;\n if (profile.kind !== 'skill') {\n // Unreachable in practice: runGate()'s asProfile() already throws before\n // returning a non-skill profile for artifact 'skill'. Narrows the type.\n throw new Error(`expected a skill candidate, found a ${profile.kind}`);\n }\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/**\n * MCP branch of `add` (v0.5, `--artifact mcp`): quarantine-install an MCP\n * server (local dir / git URL / npm package — see `install/resolve.ts` and\n * `install/quarantine.ts`), gate it (`mcpScan` + MCP overlap via\n * `runGate`'s `artifact: 'mcp'` routing), then promote/hold/reject. Promote\n * writes the server entry into an MCP config file via `promoteMcp` instead of\n * moving anything into a skills root — there is no skill-style provenance\n * ledger for MCP artifacts (`SOURCES.md` is a skill-ingestion ledger; nothing\n * analogous exists for MCP config writes), but the pending-ingestion queue\n * and `--ingest` handoff both apply the same way, keyed on the written config\n * file path.\n */\nasync function addMcp(source: string, config: Config, options: AddOptions): Promise<void> {\n const resolved = resolveSource(source, 'mcp');\n const entry = await quarantine(resolved, config.quarantineDir);\n const candidatePath = resolveMcpCandidatePath(entry);\n\n const { safety, gateResult, proEnabled } = await runGate(candidatePath, '', {\n proFeatureLabel: MCP_PRO_FEATURE_LABEL,\n strictness: config.strictness,\n artifact: 'mcp',\n mcpTargets: config.mcpTargets ?? [],\n });\n const { profile, overlap } = gateResult;\n if (profile.kind !== 'mcp') {\n // Unreachable in practice: runGate()'s asProfile() already throws before\n // returning a non-mcp profile for artifact 'mcp'. Narrows the type.\n throw new Error(`expected an MCP candidate, found a ${profile.kind}`);\n }\n if (profile.serverCount > 1) {\n console.error(\n `[skill-forge] ${profile.serverCount} MCP servers found in ${source}; gating/promoting the first — ` +\n `the whole quarantine entry is promoted/rejected together:\\n ${profile.servers.join(', ')}`\n );\n }\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 detail = resolveMcpCandidateDetail(profile);\n const serverProfile: McpServerProfile = {\n name: detail.name,\n version: detail.version ?? 'unknown',\n envVars: detail.env ? Object.keys(detail.env) : undefined,\n candidateEntry: detail.command ? { command: detail.command, args: detail.args, env: detail.env } : undefined,\n };\n const mcpTargetPath = options.mcpTarget ?? config.mcpTargets?.[0] ?? join(process.cwd(), '.mcp.json');\n\n const result = promoteMcp(entry, serverProfile, mcpTargetPath, { force: options.force });\n reject(entry); // promoteMcp doesn't touch the quarantine sandbox — clean it up now that the entry is written\n\n logOutcome(`[skill-forge] promoted -> ${result.target} (server \"${result.serverName}\")`, options.json);\n if (result.backupPath) {\n logOutcome(`[skill-forge] backed up existing config -> ${result.backupPath}`, options.json);\n }\n if (result.envVarsToFill.length > 0) {\n logOutcome(`[skill-forge] fill in these env vars before use: ${result.envVarsToFill.join(', ')}`, options.json);\n }\n installedPath = result.target;\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.\n if (proEnabled) {\n appendQueueEntry({\n source,\n sourceType: resolved.type,\n installedPath,\n quarantinePath: entry.quarantinePath,\n gate: {\n 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 artifactType: 'mcp',\n });\n\n if (options.ingest && installedPath) {\n // Same generic handoff as the skill path — {path} substitutes to the\n // written MCP config file rather than an installed skill dir.\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\n/** Single source of truth for the strictness fallback — used by `defaultConfig()` and the gate's default parameters. */\nexport const DEFAULT_STRICTNESS: Strictness = 'block-high';\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 /** MCP config file targets recorded by `skill-forge init` (see Agent#mcpConfig in src/agents.ts). Consulted by MCP `promote` as a fallback when `--mcp-target` is omitted. */\n mcpTargets?: 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: DEFAULT_STRICTNESS,\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 mcpTargets: parsed.mcpTargets ?? defaults.mcpTargets,\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';\nimport { UserError } from '../errors.js';\nimport { expandHome } from '../util.js';\n\nexport type SourceType = 'skills.sh' | 'git' | 'local' | 'npm';\nexport type ArtifactType = 'skill' | 'mcp';\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// A scoped npm package spec: \"@scope/name\", optionally with a version/tag\n// suffix on the name half (e.g. \"@scope/name@1.2.3\"). Never collides with a\n// skills.sh `owner/name` slug, which has no leading \"@\".\nconst NPM_SCOPED_PATTERN = /^@[^/]+\\/[^/]+$/;\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 * - with `artifact: 'mcp'`, a scoped \"@scope/name\" package or a bare\n * (no-slash) package name is npm — an \"owner/name\" two-segment slug (no\n * leading \"@\") still falls through to skills.sh below, same as\n * `artifact: 'skill'`/absent: no ambiguity guessing between the two\n * owner/name-shaped source kinds\n * - everything else is treated as a skills.sh `owner/name` slug\n *\n * Skill classification (`artifact` 'skill' or absent) is unchanged from v0.4.\n */\nexport function resolve(source: string, artifact: ArtifactType = 'skill'): ResolvedSource {\n const value = source.trim();\n\n // Reject anything that looks like a CLI flag rather than a source: quarantine.ts\n // passes `value` straight through as a git-clone/npx/npm-pack argv positional, so\n // a source starting with \"-\" (e.g. \"--upload-pack=...\") could otherwise be\n // smuggled in as an injected flag. Fail fast with a friendly error instead of\n // reaching exec().\n if (value.startsWith('-')) {\n throw new UserError(`skill source cannot start with \"-\" (looks like a flag, not a source): ${value}`);\n }\n\n if (GIT_URL_PATTERN.test(value) || value.endsWith('.git')) {\n return { type: 'git', value };\n }\n\n // Expand ~ before existence checks so a real \"~/some-skill\" resolves instead\n // of erroring \"not found\" on the literal tilde string.\n const localCandidate = expandHome(value);\n if (existsSync(localCandidate) || LOCAL_PATH_PATTERN.test(value)) {\n return { type: 'local', value: localCandidate };\n }\n\n if (artifact === 'mcp' && (NPM_SCOPED_PATTERN.test(value) || !value.includes('/'))) {\n return { type: 'npm', value };\n }\n\n return { type: 'skills.sh', value };\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 { 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 { 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 /** Present only for npm-sourced (MCP) entries: the extracted `package/` dir inside the sandbox. */\n packageDir?: 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 let packageDir: string | undefined;\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 // `--` separates flags from positionals so a source value that looks like a\n // git flag (e.g. \"--upload-pack=...\") is always treated as the repo\n // positional, never parsed as an option — belt-and-suspenders alongside\n // resolve.ts's leading-dash rejection.\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 case 'npm': {\n // Fetch only — never `npm install`, never run scripts. `npm pack` downloads\n // the published tarball without executing any lifecycle scripts; we then\n // extract it ourselves with system `tar`, so nothing from the package ever\n // runs during quarantine.\n const packResult = await exec('npm', ['pack', source.value, '--pack-destination', quarantinePath], {\n cwd: quarantinePath,\n });\n if (packResult.code !== 0) {\n throw new UserError(\n `npm pack failed for \"${source.value}\" (exit ${packResult.code}): ${packResult.stderr || packResult.stdout}`\n );\n }\n\n const tarballPath = join(quarantinePath, findTarball(quarantinePath, source.value));\n const extractResult = await exec('tar', ['-xzf', tarballPath, '-C', quarantinePath], {\n cwd: quarantinePath,\n });\n if (extractResult.code !== 0) {\n throw new UserError(\n `tarball extraction failed for \"${source.value}\" (exit ${extractResult.code}): ${extractResult.stderr || extractResult.stdout}`\n );\n }\n\n // `npm pack` tarballs always extract to a top-level `package/` dir.\n packageDir = join(quarantinePath, 'package');\n if (!existsSync(packageDir)) {\n throw new UserError(\n `npm pack for \"${source.value}\" did not produce the expected package/ dir after extraction`\n );\n }\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), packageDir, 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/** Locates the .tgz produced by `npm pack --pack-destination <dir>` (exactly one is expected, since each quarantine sandbox is freshly created before use). */\nfunction findTarball(dir: string, sourceValue: string): string {\n const tarball = readdirSync(dir).find((name) => name.endsWith('.tgz'));\n if (!tarball) {\n throw new UserError(`npm pack for \"${sourceValue}\" did not produce a .tgz file in ${dir}`);\n }\n return tarball;\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 { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type { QuarantineEntry } from './quarantine.js';\nimport { UserError } from '../errors.js';\n\n/**\n * Minimal shape promoteMcp needs to derive a server config entry. Deliberately\n * decoupled from the skill/plugin/mcp-config profiling in gate/profile.ts —\n * this describes an MCP *server* candidate (name/version + whatever launch\n * info the profile/gate steps found), not a directory that merely ships an\n * .mcp.json referencing other servers.\n */\nexport interface McpServerProfile {\n /** Server name — used as the `mcpServers` config key (e.g. \"@scope/name\" or \"name\"). */\n name: string;\n /** Pinned version to launch via `npx <name>@<version>` — never launched unpinned. */\n version: string;\n /** Env var NAMES the server declares/needs. Values are never read or copied. */\n envVars?: string[];\n /**\n * The candidate's own documented server entry (e.g. from a README example or a\n * shipped .mcp.json), used verbatim for command/args when present instead of the\n * npx-derived default. Any `env` values on it are always stripped before use.\n */\n candidateEntry?: { command: string; args?: string[]; env?: Record<string, unknown> };\n}\n\nexport interface PromoteMcpOptions {\n force?: boolean;\n}\n\nexport interface PromoteMcpResult {\n /** Absolute path to the target MCP config file that was written. */\n target: string;\n /** Server name (the `mcpServers` key) that was written. */\n serverName: string;\n /** Env var names the user must fill in before the server will work — values were never copied. */\n envVarsToFill: string[];\n /** Backup path created before modifying an existing target file; undefined when the file didn't exist. */\n backupPath?: string;\n}\n\ninterface McpConfigFile {\n mcpServers?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * Writes `profile`'s derived server entry into `targetConfigPath`'s `mcpServers`\n * map — the only place an MCP candidate is written outside its quarantine\n * sandbox, mirroring install/promote.ts's target-untouched-until-promote\n * invariant for skills. Creates the file (and any missing parent dirs) if\n * absent. Refuses to overwrite an existing same-name server entry unless\n * `force` is set, checked before anything is read/backed up so a naming clash\n * never leaves a partial write behind. When an existing file *is* about to be\n * modified, it's backed up first to `<file>.bak-<timestamp>`. Env values are\n * NEVER copied from the candidate — they're always emitted as empty strings,\n * and the var names the user must fill in are returned.\n */\nexport function promoteMcp(\n _entry: QuarantineEntry,\n profile: McpServerProfile,\n targetConfigPath: string,\n options: PromoteMcpOptions = {}\n): PromoteMcpResult {\n const force = options.force ?? false;\n const serverName = profile.name;\n\n let config: McpConfigFile = {};\n let rawExisting: string | undefined;\n\n if (existsSync(targetConfigPath)) {\n rawExisting = readFileSync(targetConfigPath, 'utf-8');\n try {\n config = JSON.parse(rawExisting) as McpConfigFile;\n } catch (err) {\n throw new UserError(`could not parse existing MCP config ${targetConfigPath}: ${(err as Error).message}`);\n }\n }\n\n const servers = { ...(config.mcpServers ?? {}) };\n if (Object.prototype.hasOwnProperty.call(servers, serverName) && !force) {\n throw new UserError(\n `Refusing to overwrite existing MCP server entry \"${serverName}\" in ${targetConfigPath} (pass force to override)`\n );\n }\n\n let backupPath: string | undefined;\n if (rawExisting !== undefined) {\n backupPath = `${targetConfigPath}.bak-${Date.now()}`;\n writeFileSync(backupPath, rawExisting);\n }\n\n const { command, args, envVarsToFill } = deriveServerEntry(profile);\n const env: Record<string, string> = {};\n for (const name of envVarsToFill) env[name] = '';\n\n servers[serverName] = { command, args, env };\n config.mcpServers = servers;\n\n mkdirSync(dirname(targetConfigPath), { recursive: true });\n writeFileSync(targetConfigPath, `${JSON.stringify(config, null, 2)}\\n`);\n\n return { target: targetConfigPath, serverName, envVarsToFill, backupPath };\n}\n\n/**\n * Derives {command, args} from the candidate's own documented entry when\n * present, else `npx <name>@<version>` pinned to the profiled version. Env\n * values are always stripped from whichever source is used — only var names\n * are returned.\n */\nfunction deriveServerEntry(profile: McpServerProfile): {\n command: string;\n args: string[];\n envVarsToFill: string[];\n} {\n const envNames = new Set(profile.envVars ?? []);\n if (profile.candidateEntry?.env) {\n for (const key of Object.keys(profile.candidateEntry.env)) envNames.add(key);\n }\n\n if (profile.candidateEntry) {\n return {\n command: profile.candidateEntry.command,\n args: profile.candidateEntry.args ?? [],\n envVarsToFill: Array.from(envNames),\n };\n }\n\n return {\n command: 'npx',\n args: ['-y', `${profile.name}@${profile.version}`],\n envVarsToFill: Array.from(envNames),\n };\n}\n","import { readFileSync } from 'node:fs';\nimport type { McpProfile } from '../gate/profile.js';\n\n/**\n * Launch details for the ONE MCP server being gated/promoted, derived from a\n * `McpProfile` (`gate/profile.ts`) plus — when the profile came from an\n * explicit `.mcp.json`/`mcp.json` — a second read of that file's chosen\n * server entry. `McpProfile` intentionally only tracks server NAMES (see\n * `profileMcp` in `gate/profile.ts`), not launch command/args/env, since its\n * source config may list more than one server; this is the one place that\n * resolves a profile down to a single server's launch details. Shared by\n * `commands/gatePipeline.ts`'s MCP overlap step and `install/promoteMcp.ts`'s\n * promote step so both derive from exactly the same read.\n */\nexport interface McpCandidateDetail {\n name: string;\n version?: string;\n command?: string;\n args?: string[];\n env?: Record<string, unknown>;\n}\n\ninterface ConfigEntry {\n command?: string;\n args?: string[];\n env?: Record<string, unknown>;\n}\n\nfunction readPackageVersion(pkgPath: string): string | undefined {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as Record<string, unknown>;\n return typeof pkg.version === 'string' ? pkg.version : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction readConfigEntry(configPath: string, name: string): ConfigEntry | undefined {\n try {\n const cfg = JSON.parse(readFileSync(configPath, 'utf-8')) as Record<string, unknown>;\n const servers = (cfg.mcpServers ?? cfg.servers ?? {}) as Record<string, unknown>;\n const raw = (servers[name] ?? {}) as Record<string, unknown>;\n return {\n command: typeof raw.command === 'string' ? raw.command : undefined,\n args: Array.isArray(raw.args) ? raw.args.filter((a): a is string => typeof a === 'string') : undefined,\n env:\n raw.env && typeof raw.env === 'object' && !Array.isArray(raw.env)\n ? (raw.env as Record<string, unknown>)\n : undefined,\n };\n } catch {\n return undefined;\n }\n}\n\n/**\n * Resolves launch details for `profile.servers[0]` — when a config lists more\n * than one server, gates/promotes the first, matching `add`'s/`scan`'s\n * existing \"N skills found — using the first\" convention for multi-skill\n * sources. A package.json-shaped profile (npm-sourced server) has no\n * command/args/env of its own — `promoteMcp` derives the launch command from\n * `name`/`version` (`npx <name>@<version>`) when both are absent here.\n */\nexport function resolveMcpCandidateDetail(profile: McpProfile): McpCandidateDetail {\n const name = profile.servers[0];\n\n if (profile.config.endsWith('package.json')) {\n return { name, version: readPackageVersion(profile.config) };\n }\n\n const entry = readConfigEntry(profile.config, name);\n return { name, command: entry?.command, args: entry?.args, env: entry?.env };\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 { readdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { DEFAULT_STRICTNESS, type Strictness } from '../config.js';\nimport { profileSkill, type Profile, type McpProfile } from '../gate/profile.js';\nimport { scanSafety, type SafetyResult as GateSafetyResult } from '../gate/safety.js';\nimport { scanOverlap, type OverlapResult } from '../gate/overlap.js';\nimport { mcpScan } from '../gate/mcpSafety.js';\nimport { scanMcpOverlap } from '../gate/mcpOverlap.js';\nimport { resolveMcpCandidateDetail } from '../install/mcpCandidate.js';\nimport type { ArtifactType } from '../install/resolve.js';\nimport type { QuarantineEntry } from '../install/quarantine.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 asProfile(candidatePath: string, artifact: ArtifactType): Profile {\n const profile = profileSkill(candidatePath);\n const expectedKind = artifact === 'mcp' ? 'mcp' : 'skill';\n if (profile.kind !== expectedKind) {\n throw new Error(\n artifact === 'mcp'\n ? `expected an MCP candidate at ${candidatePath} (--artifact mcp), found a ${profile.kind}`\n : `expected a skill candidate at ${candidatePath}, found a ${profile.kind}`\n );\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 * MCP counterpart of `tryOverlap`: ranks the candidate against every server\n * entry found across `mcpTargets` (see `gate/mcpOverlap.ts`). `scanMcpOverlap`\n * itself already tolerates missing/malformed/empty target files (returns an\n * empty ranking rather than throwing) — this try/catch is defensive only.\n */\nfunction tryMcpOverlap(profile: McpProfile, mcpTargets: string[]): OverlapResult | undefined {\n try {\n const detail = resolveMcpCandidateDetail(profile);\n return scanMcpOverlap(\n {\n name: detail.name,\n command: detail.command,\n args: detail.args,\n packageName: detail.command ? undefined : detail.name,\n },\n mcpTargets\n );\n } catch (err) {\n console.error(`[skill-forge] mcp overlap scan skipped: ${(err as Error).message}`);\n return undefined;\n }\n}\n\n/**\n * Candidate directory to profile/gate for an MCP artifact (v0.5): the\n * extracted npm package dir when the source was npm, else the single\n * subdirectory `quarantine()` copied/cloned the source into — local dir and\n * git URL, the other two v0.5 MCP source forms, both land in exactly one\n * subdirectory of a freshly created quarantine sandbox (see\n * `install/quarantine.ts`). Falls back to the sandbox root itself if that\n * can't be determined unambiguously.\n */\nexport function resolveMcpCandidatePath(entry: QuarantineEntry): string {\n if (entry.packageDir) return entry.packageDir;\n let children: string[] = [];\n try {\n children = readdirSync(entry.quarantinePath, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n // fall through to the sandbox root\n }\n return children.length === 1 ? join(entry.quarantinePath, children[0]) : entry.quarantinePath;\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 * `strictness` selects which finding severities produce a `block` safety\n * verdict (see `src/gate/safety.ts`); `add`/`scan` pass the value from the\n * config they already load, so the gate honors whatever is configured without\n * re-reading `config.json` here. Callers that omit it (e.g. unit tests) get\n * the `block-high` default. `artifact` (v0.5) routes the whole pipeline: for\n * `'mcp'` (explicit `--artifact mcp` only — never auto-detected here, see\n * `install/resolve.ts`'s \"no ambiguity guessing\" source-resolution policy),\n * safety runs `mcpScan` (base ruleset + MCP-specific rules) instead of\n * `scanSafety`, and overlap ranks against `mcpTargets` (every configured MCP\n * config file) via `scanMcpOverlap` instead of `scanOverlap`-against-\n * `skillsRoot`; `skillsRoot`/`mcpTargets` are simply unused by whichever\n * branch doesn't apply. Defaults to `'skill'`, the pre-v0.5 behavior.\n */\nexport async function runGate(\n candidatePath: string,\n skillsRoot: string,\n {\n proFeatureLabel = 'overlap analysis',\n strictness = DEFAULT_STRICTNESS,\n artifact = 'skill',\n mcpTargets = [],\n }: {\n proFeatureLabel?: string;\n strictness?: Strictness;\n artifact?: ArtifactType;\n mcpTargets?: string[];\n } = {}\n): Promise<GateRunResult> {\n const profile = asProfile(candidatePath, artifact);\n const proStatus = proFeatureStatus(proFeatureLabel);\n\n const safety = artifact === 'mcp' ? await mcpScan(candidatePath, strictness) : await scanSafety(candidatePath, strictness);\n const overlap = !proStatus.enabled\n ? undefined\n : artifact === 'mcp'\n ? tryMcpOverlap(profile as McpProfile, mcpTargets)\n : tryOverlap(candidatePath, skillsRoot);\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","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\n/**\n * v0.5: the artifact this candidate should be gated/promoted as — 'mcp' when\n * an MCP config file or an MCP-server-shaped package.json is detected,\n * 'skill' otherwise (default). Orthogonal to `kind`, which selects which\n * profiler shape ran (skill dir / plugin / mcp config dir).\n */\nexport type ArtifactType = 'skill' | 'mcp';\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 artifactType: ArtifactType;\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 artifactType: ArtifactType;\n}\n\nexport interface McpProfile {\n kind: 'mcp';\n inputPath: string;\n config: string;\n servers: string[];\n serverCount: number;\n artifactType: ArtifactType;\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 (detectArtifactType(p) === 'mcp') return 'mcp';\n return 'skill';\n}\n\nconst MCP_SDK_DEP = '@modelcontextprotocol/sdk';\nconst MCP_KEYWORD = 'mcp-server';\nconst MCP_BIN_PREFIX = /^mcp-/i;\n\n/** Reads package.json for detection purposes only — malformed/missing is not a signal either way. */\nfunction readPackageJsonSafe(root: string): Record<string, unknown> | undefined {\n const pkgPath = join(root, 'package.json');\n if (!existsSync(pkgPath)) return undefined;\n try {\n return JSON.parse(readFileSync(pkgPath, 'utf-8')) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n\n/** deps containing @modelcontextprotocol/sdk, keywords containing \"mcp-server\", or bin names like mcp-*. */\nfunction hasMcpPackageMarkers(pkg: Record<string, unknown>): boolean {\n for (const section of ['dependencies', 'devDependencies', 'peerDependencies'] as const) {\n const deps = pkg[section];\n if (deps && typeof deps === 'object' && MCP_SDK_DEP in (deps as Record<string, unknown>)) return true;\n }\n\n const keywords = pkg.keywords;\n if (Array.isArray(keywords) && keywords.some((k) => k === MCP_KEYWORD)) return true;\n\n const bin = pkg.bin;\n if (typeof bin === 'string') {\n if (MCP_BIN_PREFIX.test(basename(bin))) return true;\n } else if (bin && typeof bin === 'object') {\n if (Object.keys(bin as Record<string, unknown>).some((k) => MCP_BIN_PREFIX.test(k))) return true;\n }\n\n return false;\n}\n\n/** v0.5 artifact classification: an mcp config file, or an MCP-server-shaped package.json, at `root`. */\nfunction detectArtifactType(root: string): ArtifactType {\n if (existsSync(join(root, '.mcp.json')) || existsSync(join(root, 'mcp.json'))) return 'mcp';\n const pkg = readPackageJsonSafe(root);\n if (pkg && hasMcpPackageMarkers(pkg)) 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 artifactType: detectArtifactType(root),\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 artifactType: detectArtifactType(p),\n };\n}\n\nfunction profileMcp(p: string): McpProfile {\n const explicitConfig = existsSync(join(p, '.mcp.json'))\n ? join(p, '.mcp.json')\n : existsSync(join(p, 'mcp.json'))\n ? join(p, 'mcp.json')\n : undefined;\n\n if (explicitConfig) {\n let cfg: Record<string, unknown>;\n try {\n cfg = JSON.parse(readFileSync(explicitConfig, 'utf-8'));\n } catch (err) {\n throw new Error(`could not parse MCP config ${explicitConfig}: ${(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: explicitConfig,\n servers: names,\n serverCount: names.length,\n artifactType: 'mcp',\n };\n }\n\n // No .mcp.json/mcp.json — this is an MCP server package identified by its own\n // package.json markers (see hasMcpPackageMarkers / detectArtifactType).\n const pkgPath = join(p, 'package.json');\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n } catch (err) {\n throw new Error(`could not parse package.json ${pkgPath}: ${(err as Error).message}`);\n }\n const name = typeof pkg.name === 'string' && pkg.name.length > 0 ? pkg.name : basename(p);\n\n return {\n kind: 'mcp',\n inputPath: p,\n config: pkgPath,\n servers: [name],\n serverCount: 1,\n artifactType: 'mcp',\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';\nimport { DEFAULT_STRICTNESS, type Strictness } from '../config.js';\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 *\n * `config.strictness` (see src/config.ts) selects which severities block:\n * `block-high` (default) is the mapping above; `block-medium` also blocks on\n * MEDIUM; `warn-only` never blocks (findings still surface, capped at warn).\n */\nconst BLOCK_SEVERITIES_BY_STRICTNESS: Record<Strictness, ReadonlySet<Severity>> = {\n 'block-high': new Set(['HIGH', 'CRITICAL']),\n 'block-medium': new Set(['MEDIUM', 'HIGH', 'CRITICAL']),\n 'warn-only': new Set(),\n};\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[], strictness: Strictness): Verdict {\n const blockSeverities = BLOCK_SEVERITIES_BY_STRICTNESS[strictness];\n if (findings.some((f) => blockSeverities.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. `strictness` (default `DEFAULT_STRICTNESS` from\n * `config.ts`) selects which severities produce a `block` verdict —\n * see `BLOCK_SEVERITIES_BY_STRICTNESS` above.\n */\nexport async function scanSafety(targetPath: string, strictness: Strictness = DEFAULT_STRICTNESS): Promise<SafetyResult> {\n const findings = [...scanStaticRules(targetPath), ...(await scanWithSkillSpector(targetPath))];\n return { findings, verdict: verdictFor(findings, strictness) };\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","import { readFileSync, readdirSync, statSync } from 'node:fs';\nimport { basename, extname, join } from 'node:path';\nimport { DEFAULT_STRICTNESS, type Strictness } from '../config.js';\nimport { scanSafety, type SafetyFinding, type SafetyResult, type Severity, type Verdict } from './safety.js';\n\n/**\n * MCP-specific findings, composed on top of the base deny-ruleset scan\n * (`scanSafety`). curl|bash is deliberately NOT re-implemented here — the\n * base scan's `curl-pipe-shell` rule already covers it; `mcpScan` merges its\n * findings in.\n *\n * Detects:\n * - mcp-inline-credential-value (HIGH): a *_KEY/*_TOKEN/*_SECRET-shaped env\n * var (or any high-entropy value) assigned a real-looking value instead of\n * an empty string / ${VAR} placeholder, in an MCP server's `env` block or\n * a config/example text file.\n * - mcp-unpinned-npx (MEDIUM): `npx -y <pkg>` / `npx --yes <pkg>` launch\n * commands (structured `{command,args}` or shell text) without a pinned\n * `@<version>`.\n * - mcp-dangerous-flag (HIGH): `--dangerously-*` or `--no-sandbox` launch args.\n * - mcp-filesystem-root-arg (MEDIUM): a launch arg that is exactly \"/\" or \"~\".\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;\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;\n return content;\n } catch {\n return undefined;\n }\n}\n\nfunction isConfigOrExampleFile(file: string): boolean {\n const base = basename(file).toLowerCase();\n if (base.startsWith('.env')) return true;\n if (/\\.(json|ya?ml)$/.test(base)) return true;\n return /(example|sample|config)/.test(base);\n}\n\nconst CREDENTIAL_KEY_RE = /(API[_-]?KEY|SECRET|TOKEN|PASSWORD|CREDENTIAL)/i;\nfunction looksCredentialKey(name: string): boolean {\n return CREDENTIAL_KEY_RE.test(name);\n}\n\nconst PLACEHOLDER_VALUE_RE =\n /^(\\$\\{.*\\}|\\$[A-Z0-9_]+|your[_-]?.*|<.*>|xxx+|changeme|replace[_-]?me|example|placeholder|todo|fill[_-]?me|none|null|redacted|\\.\\.\\.)$/i;\nfunction isPlaceholderValue(value: string): boolean {\n const v = value.trim();\n if (v === '') return true;\n if (v.startsWith('process.env')) return true;\n return PLACEHOLDER_VALUE_RE.test(v);\n}\n\n/** Length/charset heuristic for \"looks like a real secret\" — no whitespace, mixed charset, low repetition. */\nfunction looksHighEntropy(value: string): boolean {\n if (/\\s/.test(value)) return false;\n if (value.length < 20) return false;\n const hasLower = /[a-z]/.test(value);\n const hasUpper = /[A-Z]/.test(value);\n const hasDigit = /[0-9]/.test(value);\n const varietyCount = [hasLower, hasUpper, hasDigit].filter(Boolean).length;\n const uniqueChars = new Set(value).size;\n return varietyCount >= 2 && uniqueChars / value.length > 0.4;\n}\n\n/** Scoped packages pin as `@scope/name@version`; unscoped as `name@version`. */\nfunction isPinnedPackageSpec(spec: string): boolean {\n const withoutScope = spec.startsWith('@') ? spec.slice(spec.indexOf('/') + 1) : spec;\n return withoutScope.includes('@');\n}\n\ninterface ServerLikeNode {\n command?: unknown;\n args?: unknown;\n env?: unknown;\n}\n\nfunction collectServerNodes(node: unknown, out: ServerLikeNode[]): void {\n if (Array.isArray(node)) {\n for (const item of node) collectServerNodes(item, out);\n return;\n }\n if (node && typeof node === 'object') {\n const obj = node as Record<string, unknown>;\n if (typeof obj.command === 'string' || Array.isArray(obj.args) || (obj.env && typeof obj.env === 'object')) {\n out.push(obj);\n }\n for (const value of Object.values(obj)) collectServerNodes(value, out);\n }\n}\n\nfunction tryParseJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\n/** Structural scan of a parsed MCP-config-shaped JSON document (mcpServers/servers entries, nested or not). */\nfunction scanJsonConfig(file: string, parsed: unknown): SafetyFinding[] {\n const findings: SafetyFinding[] = [];\n const nodes: ServerLikeNode[] = [];\n collectServerNodes(parsed, nodes);\n\n for (const node of nodes) {\n const command = typeof node.command === 'string' ? node.command : undefined;\n const args = Array.isArray(node.args) ? node.args.filter((a): a is string => typeof a === 'string') : [];\n\n if (command === 'npx') {\n const flagIdx = args.findIndex((a) => a === '-y' || a === '--yes');\n if (flagIdx !== -1) {\n const pkgSpec = args.slice(flagIdx + 1).find((a) => !a.startsWith('-'));\n if (pkgSpec && !isPinnedPackageSpec(pkgSpec)) {\n findings.push({\n severity: 'MEDIUM',\n rule: 'mcp-unpinned-npx',\n file,\n detail: `npx -y ${pkgSpec} is unpinned — pin an exact version (${pkgSpec}@<version>) so the resolved server code can't change silently`,\n });\n }\n }\n }\n\n for (const a of args) {\n if (/^--dangerously-/i.test(a) || a === '--no-sandbox') {\n findings.push({\n severity: 'HIGH',\n rule: 'mcp-dangerous-flag',\n file,\n detail: `launch arg ${a} disables a safety guard`,\n });\n }\n if (a === '/' || a === '~') {\n findings.push({\n severity: 'MEDIUM',\n rule: 'mcp-filesystem-root-arg',\n file,\n detail: `launch arg \"${a}\" grants filesystem access at the root/home directory — scope this to a specific directory`,\n });\n }\n }\n\n if (node.env && typeof node.env === 'object' && !Array.isArray(node.env)) {\n for (const [key, value] of Object.entries(node.env as Record<string, unknown>)) {\n if (typeof value !== 'string' || isPlaceholderValue(value)) continue;\n if (looksCredentialKey(key) || looksHighEntropy(value)) {\n findings.push({\n severity: 'HIGH',\n rule: 'mcp-inline-credential-value',\n file,\n detail: `env.${key} has an inline credential value — emit an empty string and document that the user must fill it in`,\n });\n }\n }\n }\n }\n\n return findings;\n}\n\nconst NPX_SHELL_RE = /\\bnpx\\s+(?:-y|--yes)\\s+((?:@[\\w.-]+\\/)?[\\w.-]+)(@[\\w.-]+)?/g;\nconst DANGEROUS_FLAG_RE = /--dangerously-[\\w-]+|--no-sandbox\\b/g;\nconst CREDENTIAL_ASSIGNMENT_RE = /[\"']?([A-Za-z][A-Za-z0-9_]*)[\"']?\\s*[:=]\\s*[\"']([^\"']{6,})[\"']/g;\n\n/** Fallback line-based scan for non-JSON (or unparseable) text files: shell examples, README, .env. */\nfunction scanTextFile(file: string, content: string, allowCredentialCheck: boolean): SafetyFinding[] {\n const findings: SafetyFinding[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const lineNo = i + 1;\n\n NPX_SHELL_RE.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = NPX_SHELL_RE.exec(line)) !== null) {\n const pkgSpec = m[1];\n const pinned = m[2] !== undefined;\n if (!pinned) {\n findings.push({\n severity: 'MEDIUM',\n rule: 'mcp-unpinned-npx',\n file,\n detail: `line ${lineNo}: npx -y ${pkgSpec} is unpinned — pin an exact version (${pkgSpec}@<version>)`,\n });\n }\n }\n\n DANGEROUS_FLAG_RE.lastIndex = 0;\n while ((m = DANGEROUS_FLAG_RE.exec(line)) !== null) {\n findings.push({\n severity: 'HIGH',\n rule: 'mcp-dangerous-flag',\n file,\n detail: `line ${lineNo}: launch arg ${m[0]} disables a safety guard`,\n });\n }\n\n if (allowCredentialCheck) {\n CREDENTIAL_ASSIGNMENT_RE.lastIndex = 0;\n while ((m = CREDENTIAL_ASSIGNMENT_RE.exec(line)) !== null) {\n const varName = m[1];\n const value = m[2];\n if (isPlaceholderValue(value)) continue;\n if (looksCredentialKey(varName) || looksHighEntropy(value)) {\n findings.push({\n severity: 'HIGH',\n rule: 'mcp-inline-credential-value',\n file,\n detail: `line ${lineNo}: inline credential value assigned to ${varName}`,\n });\n }\n }\n }\n }\n\n return findings;\n}\n\nfunction scanFileForMcpFindings(file: string): SafetyFinding[] {\n const content = readTextFile(file);\n if (content === undefined) return [];\n\n if (extname(file).toLowerCase() === '.json') {\n const parsed = tryParseJson(content);\n if (parsed !== undefined) return scanJsonConfig(file, parsed);\n // malformed JSON — fall through to the text scan below\n }\n\n return scanTextFile(file, content, isConfigOrExampleFile(file));\n}\n\n/** Walks `targetPath` and returns MCP-specific findings only (not merged with the base ruleset). */\nexport function scanMcpFindings(targetPath: string): SafetyFinding[] {\n let root;\n try {\n root = statSync(targetPath);\n } catch {\n return [];\n }\n const files = root.isDirectory() ? Array.from(walk(targetPath)) : [targetPath];\n\n const findings: SafetyFinding[] = [];\n for (const file of files) {\n findings.push(...scanFileForMcpFindings(file));\n }\n return findings;\n}\n\n/**\n * Mirrors safety.ts's severity->verdict mapping. Duplicated (not imported) because\n * safety.ts doesn't export it and this module intentionally composes on top of\n * `scanSafety` rather than modifying it — keep in sync if that mapping changes.\n */\nconst BLOCK_SEVERITIES_BY_STRICTNESS: Record<Strictness, ReadonlySet<Severity>> = {\n 'block-high': new Set(['HIGH', 'CRITICAL']),\n 'block-medium': new Set(['MEDIUM', 'HIGH', 'CRITICAL']),\n 'warn-only': new Set(),\n};\n\nfunction verdictFor(findings: SafetyFinding[], strictness: Strictness): Verdict {\n const blockSeverities = BLOCK_SEVERITIES_BY_STRICTNESS[strictness];\n if (findings.some((f) => blockSeverities.has(f.severity))) return 'block';\n if (findings.length > 0) return 'warn';\n return 'pass';\n}\n\n/**\n * MCP gate safety scan: the base deny-ruleset (`scanSafety`, includes curl|bash,\n * credential-file-access, etc.) plus the MCP-specific findings above, merged\n * into a single verdict via the same strictness rules as `scanSafety`.\n */\nexport async function mcpScan(targetPath: string, strictness: Strictness = DEFAULT_STRICTNESS): Promise<SafetyResult> {\n const base = await scanSafety(targetPath, strictness);\n const mcpFindings = scanMcpFindings(targetPath);\n const findings = [...base.findings, ...mcpFindings];\n return { findings, verdict: verdictFor(findings, strictness) };\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 } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { expandHome } from '../util.js';\nimport { suggestVerb, type OverlapResult, type SkillOverlapScore } from './overlap.js';\n\n/**\n * MCP overlap gate (v0.5) — ranks a candidate MCP server against the server\n * entries already present in the user's detected mcp config files, by name\n * similarity + same package/command. No canonical Python source exists for\n * this one (unlike gate/overlap.ts's skill overlap); it's new for v0.5.\n *\n * Returns the same `OverlapResult` shape gate/overlap.ts's scanOverlap()\n * returns (and reuses its score-tier suggestVerb()) so `report/render.ts`\n * renders an MCP candidate's overlap without any changes: `ranking[].skill`\n * holds the existing server's name, `ranking[].path` the config file it came\n * from.\n */\n\n/**\n * Minimal shape scanMcpOverlap needs to describe the candidate being added.\n * Deliberately decoupled from src/gate/profile.ts's McpProfile — callers\n * adapt whatever candidate profile they built (npm package.json, a local\n * dir's mcp.json, etc.) into this shape.\n */\nexport interface McpOverlapCandidate {\n /** Candidate server name (the config key it would be promoted under). */\n name: string;\n /** Launch command (e.g. \"npx\", \"node\", a binary path) — bin/entry hint. */\n command?: string;\n /** Launch args — used for command/package similarity (e.g. [\"-y\", \"@scope/pkg\"]). */\n args?: string[];\n /** npm package name, when the candidate is an npm-sourced MCP server. */\n packageName?: string;\n}\n\ninterface McpTargetServer {\n name: string;\n command?: string;\n args?: string[];\n}\n\n/** Reads one mcp config file's `mcpServers` (falling back to `servers`) map. Missing/malformed files yield no entries. */\nfunction readTargetServers(targetPath: string): McpTargetServer[] {\n const path = resolve(expandHome(targetPath));\n if (!existsSync(path)) return [];\n\n let cfg: Record<string, unknown>;\n try {\n cfg = JSON.parse(readFileSync(path, 'utf-8'));\n } catch {\n return [];\n }\n\n const servers = (cfg.mcpServers ?? cfg.servers ?? {}) as Record<string, unknown>;\n if (typeof servers !== 'object' || servers === null) return [];\n\n return Object.entries(servers).map(([name, raw]) => {\n const v = (raw ?? {}) as Record<string, unknown>;\n return {\n name,\n command: typeof v.command === 'string' ? v.command : undefined,\n args: Array.isArray(v.args) ? v.args.filter((a): a is string => typeof a === 'string') : undefined,\n };\n });\n}\n\nfunction tokenizeName(name: string): Set<string> {\n return new Set(name.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean));\n}\n\n/** Exact match = 1; else Jaccard token overlap on the name split by non-alphanumerics. */\nfunction nameSimilarity(a: string, b: string): number {\n if (a.toLowerCase() === b.toLowerCase()) return 1;\n const ta = tokenizeName(a);\n const tb = tokenizeName(b);\n if (ta.size === 0 || tb.size === 0) return 0;\n let inter = 0;\n for (const t of ta) if (tb.has(t)) inter += 1;\n const union = new Set([...ta, ...tb]).size;\n return union ? inter / union : 0;\n}\n\n/** Strips a trailing \"@version\" from a package spec without breaking a leading \"@scope/name\". */\nfunction packageBaseName(spec: string): string {\n const searchFrom = spec.startsWith('@') ? 1 : 0;\n const atIdx = spec.indexOf('@', searchFrom);\n return atIdx === -1 ? spec : spec.slice(0, atIdx);\n}\n\n/** Identifies the package/binary a launch command targets: the first non-flag arg for a package-runner command, else the command's basename (so a bare binary name matches a full path to the same binary). */\nfunction extractPackageSpec(command?: string, args: string[] = []): string | undefined {\n if (!command) return undefined;\n const base = command.split('/').pop() ?? command;\n if (['npx', 'npm', 'pnpm', 'yarn', 'uvx'].includes(base)) {\n return args.find((a) => !a.startsWith('-'));\n }\n return base;\n}\n\n/** 1 if candidate and target resolve to the same package/binary identity, else 0. */\nfunction commandSimilarity(\n candidate: McpOverlapCandidate,\n target: Pick<McpTargetServer, 'command' | 'args'>,\n): number {\n const candSpec = candidate.packageName ?? extractPackageSpec(candidate.command, candidate.args ?? []);\n const targetSpec = extractPackageSpec(target.command, target.args ?? []);\n if (!candSpec || !targetSpec) return 0;\n return packageBaseName(candSpec).toLowerCase() === packageBaseName(targetSpec).toLowerCase() ? 1 : 0;\n}\n\nfunction round3(n: number): number {\n return Math.round(n * 1000) / 1000;\n}\n\nfunction score(candidate: McpOverlapCandidate, target: McpTargetServer): number {\n return round3(0.6 * nameSimilarity(candidate.name, target.name) + 0.4 * commandSimilarity(candidate, target));\n}\n\n/**\n * Ranks `candidate` against every server entry found across `mcpTargetPaths`\n * (each file's `mcpServers` map). Unlike scanOverlap(), a target file that's\n * missing or empty isn't an error — a brand-new mcp config with zero servers\n * is the normal case for a first MCP install — so this returns an\n * empty-ranking result (`nearest: ''`, `nearestScore: 0`) instead of\n * throwing.\n */\nexport function scanMcpOverlap(candidate: McpOverlapCandidate, mcpTargetPaths: string[]): OverlapResult {\n const ranking: SkillOverlapScore[] = [];\n\n for (const targetPath of mcpTargetPaths) {\n const resolvedPath = resolve(expandHome(targetPath));\n for (const target of readTargetServers(targetPath)) {\n ranking.push({ skill: target.name, path: resolvedPath, score: score(candidate, target) });\n }\n }\n ranking.sort((a, b) => b.score - a.score);\n\n const top = ranking[0];\n return {\n candidate: candidate.name,\n nearest: top?.skill ?? '',\n nearestScore: top?.score ?? 0,\n suggestedVerb: suggestVerb(top?.score ?? 0),\n ranking,\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 { 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 { Profile } 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: Profile;\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/**\n * Header fields for the \"🔨 Forge: ...\" line — a skill profile has\n * name/version/license; an MCP profile (v0.5) has no such fields (it may\n * describe more than one server entry), so it renders the server name(s)\n * instead and 'n/a' for version/license. Plugin profiles have name/version\n * but no license (not currently reachable via add/scan's CLI routing).\n */\nfunction profileHeaderFields(profile: Profile): { name: string; version: string; license: string } {\n if (profile.kind === 'mcp') {\n const name = profile.serverCount === 1 ? profile.servers[0] : `${profile.serverCount} servers`;\n return { name, version: 'n/a', license: 'n/a' };\n }\n return {\n name: profile.name ?? 'unknown',\n version: profile.version ?? 'unknown',\n license: profile.kind === 'skill' ? profile.license : 'UNKNOWN',\n };\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, version, license } = profileHeaderFields(profile);\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 `${padLabel('Artifact type')}: ${profile.artifactType}`,\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","/*\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\n/** What kind of candidate this queue entry is for. Absent on an entry means 'skill' (pre-v0.5 entries stay valid). */\nexport type ArtifactType = 'skill' | 'mcp';\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 /** 'skill' | 'mcp'; optional — absent means 'skill' (pre-v0.5 entries don't have this field). */\n artifactType?: ArtifactType;\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, detectMcpConfigFiles, type Agent, type DetectedMcpConfig, 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\ninterface DetectedMcpTarget extends DetectedMcpConfig {\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\n/**\n * Probes every known agent's `mcpConfig` locations (see src/agents.ts) and returns every MCP\n * config file that exists on disk. Unlike `detectAgents`, an agent can contribute more than one\n * hit (project and global config can both exist and both matter as promotion targets).\n */\nfunction detectMcpTargets(cwd: string, home: string): DetectedMcpTarget[] {\n const found: DetectedMcpTarget[] = [];\n for (const agent of AGENTS) {\n for (const hit of detectMcpConfigFiles(agent, cwd, home)) {\n found.push({ agent, ...hit });\n }\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 printMcpDetectionResults(detectedMcp: DetectedMcpTarget[]): void {\n if (detectedMcp.length === 0) return;\n console.log('\\n[skill-forge] init — MCP config detection');\n for (const d of detectedMcp) {\n console.log(` ${d.agent.displayName} (${d.agent.id}) — ${d.scope}: ${d.path}`);\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 selectedMcp: DetectedMcpTarget[] = [],\n): Config {\n const existing = loadConfig();\n return {\n ...existing,\n // Skill-root selections are only overwritten when this run actually offered a choice —\n // an mcp-only run (no skill roots detected) must not blank out an existing skillsRoots/agents.\n skillsRoots: selected.length > 0 ? selected.map((s) => s.root) : existing.skillsRoots,\n agents: selected.length > 0 ? selected.map((s) => ({ id: s.agent.id, skillsRoot: s.root })) : existing.agents,\n defaultTarget: defaultTargetEntry?.root ?? existing.defaultTarget,\n handoffCommand: handoffAgent ? handoffTemplateFor(handoffAgent) : existing.handoffCommand,\n mcpTargets: selectedMcp.length > 0 ? selectedMcp.map((m) => m.path) : existing.mcpTargets,\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 if (config.mcpTargets?.length) console.log(` mcp targets: ${config.mcpTargets.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\n/** Same UX as `promptMultiSelect`, offering detected MCP config files as `mcpTargets` candidates. */\nasync function promptMcpMultiSelect(rl: Interface, detectedMcp: DetectedMcpTarget[]): Promise<DetectedMcpTarget[]> {\n const chosen = new Set(detectedMcp.map((_, i) => i)); // all detected mcp config files pre-selected\n for (;;) {\n console.log('\\nMCP targets (all detected config files pre-selected — type a number to toggle, Enter to confirm):');\n detectedMcp.forEach((d, i) => {\n const mark = chosen.has(i) ? '[x]' : '[ ]';\n console.log(` ${mark} ${i + 1}. ${d.agent.displayName} (${d.scope}) — ${d.path}`);\n });\n const answer = (await rl.question('> ')).trim();\n if (answer === '') break;\n const idx = parseChoice(answer, detectedMcp.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 detectedMcp.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, an\n * optional handoff agent, and which detected MCP config files become `mcpTargets`, then writes\n * 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 const detectedMcp = detectMcpTargets(cwd, home);\n\n if (options.list) {\n printDetectionResults(detected);\n printMcpDetectionResults(detectedMcp);\n return;\n }\n\n if (detected.length === 0 && detectedMcp.length === 0) {\n console.log('[skill-forge] No known agent skill directories or MCP config files 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, detectedMcp);\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 printMcpDetectionResults(detectedMcp);\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const selected = detected.length > 0 ? await promptMultiSelect(rl, detected) : [];\n const selectedMcp = detectedMcp.length > 0 ? await promptMcpMultiSelect(rl, detectedMcp) : [];\n if (selected.length === 0 && selectedMcp.length === 0) {\n console.log('[skill-forge] No targets selected — aborting, nothing written.');\n return;\n }\n const target = selected.length > 0 ? await promptDefaultTarget(rl, selected) : undefined;\n const handoffAgent = detected.length > 0 ? await promptHandoffAgent(rl, detected) : undefined;\n const config = buildConfig(selected, target, handoffAgent, selectedMcp);\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 { statSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join, resolve } from 'node:path';\nimport { HANDOFF_INSTRUCTION } from './handoff.js';\nimport { isDir } from './util.js';\n\n/** True if `p` exists and is a regular file (false on any stat error). Mirrors util.ts's isDir. */\nfunction isFile(p: string): boolean {\n try {\n return statSync(p).isFile();\n } catch {\n return false;\n }\n}\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 verified against the vercel-labs/skills CLI's own\n * source of truth, `src/agents.ts`\n * (raw.githubusercontent.com/vercel-labs/skills/main/src/agents.ts, cross-checked via the\n * GitHub contents API 2026-07-11) — not just its README, which can drift from the code. All 73\n * entries below were diffed programmatically against that file and matched exactly (id,\n * displayName, skillsDir, and the default/no-env-override form of globalSkillsDir). A handful of\n * agents resolve their global root via an overridable env var upstream (e.g. Amp/OpenCode/Goose/\n * Devin/Universal/Replit via `XDG_CONFIG_HOME`, Codex via `CODEX_HOME`, Claude Code via\n * `CLAUDE_CONFIG_DIR`) — the tilde path here is upstream's default when that var is unset.\n * Entries marked `verified: false` could not be confirmed against that source and should be\n * treated as best-effort, never invented.\n */\n/**\n * Where an agent reads its MCP server configuration from, if known (added in v0.5 for MCP\n * gating). Each entry is verified against that agent's official docs/source — see the citation\n * comment above the entry in AGENTS below — and `mcpConfig` is left absent entirely on agents we\n * couldn't confirm a path for; never invented.\n */\nexport interface McpConfigLocation {\n /** Project-relative MCP config file path (relative to cwd), e.g. `.mcp.json`. */\n project?: string;\n /** User-home MCP config file path, `~`-relative, e.g. `~/.claude.json`. */\n global?: string;\n /**\n * File format when it isn't plain JSON with a top-level `mcpServers` key. v0.5's MCP promote\n * only WRITES json-shaped targets (see the v0.5 contract) — `format: 'toml'` targets are\n * detect/report-only.\n */\n format?: 'toml';\n /** True once the path(s) above were confirmed against this agent's official docs (cited in a comment on this entry). */\n verified: boolean;\n}\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 /** Where this agent reads its MCP server config from. Absent when unverified — see McpConfigLocation. */\n mcpConfig?: McpConfigLocation;\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 mcpConfig?: McpConfigLocation;\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 mcpConfig: spec.mcpConfig,\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/**\n * Resolves the absolute filesystem path for one of an agent's MCP config files, without probing\n * for existence. Returns undefined when the agent has no `mcpConfig`, or no path for that scope.\n * `cwd`/`home` default to the real process cwd / home dir.\n */\nexport function resolveMcpConfigPath(\n agent: Agent,\n scope: 'project' | 'global',\n cwd: string = process.cwd(),\n home: string = homedir(),\n): string | undefined {\n if (!agent.mcpConfig) return undefined;\n return scope === 'project' ? projectPath(agent.mcpConfig.project, cwd) : globalPath(agent.mcpConfig.global, home);\n}\n\n/** An MCP config file that exists on disk for an agent: which scope it is, and its absolute path. */\nexport interface DetectedMcpConfig {\n scope: 'project' | 'global';\n path: string;\n}\n\n/**\n * Probes an agent's MCP config file locations on disk and returns every one that exists as a\n * file. Unlike `detectAgentRoot` (which returns only the first matching skills root), both\n * project and global MCP config files can legitimately coexist and both matter as promotion\n * targets — e.g. a project `.mcp.json` and a user-scope `~/.claude.json` — so this returns all\n * matches, not just the first. `cwd`/`home` default to the real process cwd / home dir.\n */\nexport function detectMcpConfigFiles(\n agent: Agent,\n cwd: string = process.cwd(),\n home: string = homedir(),\n): DetectedMcpConfig[] {\n if (!agent.mcpConfig) return [];\n const found: DetectedMcpConfig[] = [];\n for (const scope of ['project', 'global'] as const) {\n const path = resolveMcpConfigPath(agent, scope, cwd, home);\n if (path && isFile(path)) found.push({ scope, path });\n }\n return found;\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 // `claude -p \"<prompt>\"` runs one non-interactive turn and exits. Verified 2026-07-11:\n // https://code.claude.com/docs/en/headless\n defaultHandoffTemplate: ['claude', '-p', HANDOFF_INSTRUCTION],\n // Project scope writes to `.mcp.json` at the project root (shared via version control).\n // User scope writes to a top-level `mcpServers` key in `~/.claude.json` (not exclusively\n // an MCP file — it's Claude Code's broader state file — but it's the correct read/write\n // target for user-scoped servers). Verified 2026-07-12: https://code.claude.com/docs/en/mcp\n mcpConfig: { project: '.mcp.json', global: '~/.claude.json', verified: true },\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 // `codex exec \"<prompt>\"` runs one non-interactive task and prints the final message.\n // Verified 2026-07-11: https://developers.openai.com/codex/noninteractive\n defaultHandoffTemplate: ['codex', 'exec', HANDOFF_INSTRUCTION],\n // TOML, not JSON: `[mcp_servers.<name>]` tables. User config at `~/.codex/config.toml`;\n // project config at `.codex/config.toml` (only honored for trusted projects). Verified\n // 2026-07-12: https://learn.chatgpt.com/docs/extend/mcp?surface=cli — detect/report-only\n // per the v0.5 contract (promote only writes JSON targets).\n mcpConfig: { project: '.codex/config.toml', global: '~/.codex/config.toml', format: 'toml', verified: true },\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 {\n id: 'cursor',\n displayName: 'Cursor',\n projectRoot: '.agents/skills',\n globalRoot: '~/.cursor/skills',\n // Both scopes use a top-level `mcpServers` key. Verified 2026-07-12:\n // https://cursor.com/docs/context/mcp (\"Create `.cursor/mcp.json` in your project ...\";\n // \"Create `~/.cursor/mcp.json` in your home directory ...\").\n mcpConfig: { project: '.cursor/mcp.json', global: '~/.cursor/mcp.json', verified: true },\n },\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 // `gemini -p \"<prompt>\"` executes a single turn headlessly and exits. Verified 2026-07-11:\n // https://google-gemini.github.io/gemini-cli/docs/cli/headless.html\n defaultHandoffTemplate: ['gemini', '-p', HANDOFF_INSTRUCTION],\n // `mcpServers` key inside `settings.json` (shared with other CLI settings, not an\n // MCP-exclusive file, but the correct read/write target). User config at\n // `~/.gemini/settings.json`, project config at `.gemini/settings.json`. Verified\n // 2026-07-12: https://google-gemini.github.io/gemini-cli/docs/tools/mcp-server.html\n mcpConfig: { project: '.gemini/settings.json', global: '~/.gemini/settings.json', verified: true },\n },\n {\n id: 'github-copilot',\n displayName: 'GitHub Copilot',\n projectRoot: '.agents/skills',\n globalRoot: '~/.copilot/skills',\n // Copilot CLI (matches the `~/.copilot` skills convention above, not the VS Code\n // extension — which uses a different `servers`-keyed `.vscode/mcp.json` and isn't covered\n // by this entry). User config `~/.copilot/mcp-config.json` (`mcpServers` key), repo-level\n // config `.github/mcp.json`. Verified 2026-07-12:\n // https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers\n mcpConfig: { project: '.github/mcp.json', global: '~/.copilot/mcp-config.json', verified: true },\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 // `opencode run \"<prompt>\"` runs one non-interactive prompt outside the TUI.\n // Verified 2026-07-11: https://opencode.ai/docs/cli/\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 {\n id: 'windsurf',\n displayName: 'Windsurf',\n projectRoot: '.windsurf/skills',\n globalRoot: '~/.codeium/windsurf/skills',\n // `mcpServers` key; global only — no project-scoped MCP config is documented. Verified\n // 2026-07-12: https://docs.windsurf.com/windsurf/cascade/mcp\n mcpConfig: { global: '~/.codeium/windsurf/mcp_config.json', verified: true },\n },\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, type ArtifactType } from '../install/resolve.js';\nimport { quarantine } from '../install/quarantine.js';\nimport { reject } from '../install/promote.js';\nimport { printGateOutput, runGate, resolveMcpCandidatePath } from './gatePipeline.js';\nimport { maybeOfferInit } from './init.js';\n\nexport interface ScanOptions {\n json?: boolean;\n /** Gate as a skill (default) or an MCP server — v0.5. */\n artifact?: ArtifactType;\n}\n\n/**\n * `skill-forge scan <source>` — run the safety gate against a skill (or, with\n * `--artifact mcp`, an MCP server — v0.5) without installing it into a target\n * skills root/MCP config: quarantine-install for inspection, gate + report,\n * then always clean up the quarantine sandbox regardless of verdict. Exits\n * nonzero when the safety verdict is \"block\". `--json` prints the same\n * 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 artifact: ArtifactType = options.artifact ?? 'skill';\n // Same resolution chain as `add`, so scan and add rank overlap against the same root.\n // Only required for skill artifacts — MCP overlap ranks against config.mcpTargets instead.\n const skillsRoot = config.defaultTarget ?? config.skillsRoots[0];\n if (artifact === 'skill' && !skillsRoot) {\n throw new Error('no skills root configured; set skillsRoots in the config');\n }\n\n const resolved = resolveSource(source, artifact);\n const entry = await quarantine(resolved, config.quarantineDir);\n\n try {\n let candidatePath: string;\n if (artifact === 'mcp') {\n candidatePath = resolveMcpCandidatePath(entry);\n } else {\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 candidatePath = entry.skillDirs[0];\n }\n\n const { safety, gateResult } = await runGate(candidatePath, skillsRoot ?? '', {\n strictness: config.strictness,\n artifact,\n mcpTargets: config.mcpTargets ?? [],\n });\n\n if (artifact === 'mcp' && gateResult.profile.kind === 'mcp' && gateResult.profile.serverCount > 1) {\n console.error(\n `[skill-forge] ${gateResult.profile.serverCount} MCP servers found in ${source}; scanning the first — ` +\n gateResult.profile.servers.join(', ')\n );\n }\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","join","existsSync","homedir","join","existsSync","existsSync","mkdirSync","join","join","mkdirSync","existsSync","walk","reject","cpSync","existsSync","mkdirSync","basename","join","existsSync","mkdirSync","readFileSync","writeFileSync","existsSync","readFileSync","writeFileSync","mkdirSync","readFileSync","pkg","existsSync","statSync","writeFileSync","join","readdirSync","join","existsSync","readFileSync","readdirSync","rmSync","statSync","basename","dirname","join","resolve","resolve","existsSync","statSync","join","readFileSync","pkg","basename","dirname","rmSync","readdirSync","m","readFileSync","readdirSync","statSync","extname","join","SKIP_DIRS","readdirSync","join","extname","statSync","readFileSync","existsSync","readFileSync","readdirSync","basename","dirname","join","resolve","readFileSync","basename","dirname","existsSync","readdirSync","join","resolve","readFileSync","readdirSync","statSync","basename","extname","join","SKIP_DIRS","BINARY_EXT","MAX_FILE_BYTES","walk","readdirSync","join","readTextFile","extname","statSync","readFileSync","basename","BLOCK_SEVERITIES_BY_STRICTNESS","verdictFor","existsSync","readFileSync","resolve","resolve","existsSync","readFileSync","round3","score","status","readdirSync","join","randomUUID","existsSync","mkdirSync","readFileSync","writeFileSync","join","join","existsSync","readFileSync","mkdirSync","writeFileSync","randomUUID","existsSync","dirname","join","spawn","existsSync","homedir","statSync","homedir","join","resolve","statSync","resolve","join","homedir","homedir","existsSync","join","createInterface","join","join","require"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/add.ts","../src/config.ts","../src/install/resolve.ts","../src/errors.ts","../src/util.ts","../src/install/quarantine.ts","../src/install/promote.ts","../src/install/promoteMcp.ts","../src/install/mcpCandidate.ts","../src/provenance.ts","../src/commands/gatePipeline.ts","../src/gate/profile.ts","../src/gate/mcpCapabilities.ts","../src/gate/scanShared.ts","../src/gate/safety.ts","../src/gate/overlap.ts","../src/gate/mcpSafety.ts","../src/gate/mcpOverlap.ts","../src/license.ts","../src/report/render.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';\nimport type { ArtifactType } from './install/resolve.js';\n\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json') as { version: string };\n\n/** Commander option parser for `--artifact` — throws UserError (caught below) on anything but skill|mcp. */\nfunction parseArtifact(value: string): ArtifactType {\n if (value !== 'skill' && value !== 'mcp') {\n throw new UserError(`--artifact must be \"skill\" or \"mcp\" (got \"${value}\")`);\n }\n return value;\n}\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 defaultTarget, then 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 .option('--artifact <type>', 'gate as a \"skill\" or an \"mcp\" server (default: skill)', parseArtifact)\n .option(\n '--mcp-target <file>',\n 'MCP config file to promote into (mcp artifacts only; defaults to config.mcpTargets[0], then <cwd>/.mcp.json)'\n )\n .option('--force', 'overwrite an existing same-name MCP server entry when promoting (mcp artifacts only)')\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 .option('--artifact <type>', 'gate as a \"skill\" or an \"mcp\" server (default: skill)', parseArtifact)\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 { join } from 'node:path';\nimport { loadConfig, type Config } from '../config.js';\nimport { resolve as resolveSource, type ArtifactType } from '../install/resolve.js';\nimport { quarantine } from '../install/quarantine.js';\nimport { promote, reject, hold } from '../install/promote.js';\nimport { promoteMcp } from '../install/promoteMcp.js';\nimport type { McpServerProfile } from '../install/promoteMcp.js';\nimport { resolveMcpCandidateDetail } from '../install/mcpCandidate.js';\nimport { appendProvenance } from '../provenance.js';\nimport { printGateOutput, runGate, resolveMcpCandidatePath } 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';\nconst MCP_PRO_FEATURE_LABEL = 'overlap analysis and the pending-ingestion queue';\n\nexport interface AddOptions {\n yes?: boolean;\n target?: string;\n json?: boolean;\n ingest?: boolean;\n /** Gate/promote as a skill (default) or an MCP server — v0.5. */\n artifact?: ArtifactType;\n /** MCP config file to promote into (mcp artifacts only). Defaults to config.mcpTargets[0], then <cwd>/.mcp.json. */\n mcpTarget?: string;\n /** Overwrite an existing same-name MCP server entry when promoting (mcp artifacts only). */\n force?: 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 artifact: ArtifactType = options.artifact ?? 'skill';\n\n if (artifact === 'mcp') {\n await addMcp(source, config, options);\n return;\n }\n\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, artifact);\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, {\n proFeatureLabel: PRO_FEATURE_LABEL,\n strictness: config.strictness,\n artifact,\n });\n const { profile, overlap } = gateResult;\n if (profile.kind !== 'skill') {\n // Unreachable in practice: runGate()'s asProfile() already throws before\n // returning a non-skill profile for artifact 'skill'. Narrows the type.\n throw new Error(`expected a skill candidate, found a ${profile.kind}`);\n }\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/**\n * MCP branch of `add` (v0.5, `--artifact mcp`): quarantine-install an MCP\n * server (local dir / git URL / npm package — see `install/resolve.ts` and\n * `install/quarantine.ts`), gate it (`mcpScan` + MCP overlap via\n * `runGate`'s `artifact: 'mcp'` routing), then promote/hold/reject. Promote\n * writes the server entry into an MCP config file via `promoteMcp` instead of\n * moving anything into a skills root — there is no skill-style provenance\n * ledger for MCP artifacts (`SOURCES.md` is a skill-ingestion ledger; nothing\n * analogous exists for MCP config writes), but the pending-ingestion queue\n * and `--ingest` handoff both apply the same way, keyed on the written config\n * file path.\n */\nasync function addMcp(source: string, config: Config, options: AddOptions): Promise<void> {\n const resolved = resolveSource(source, 'mcp');\n const entry = await quarantine(resolved, config.quarantineDir);\n const candidatePath = resolveMcpCandidatePath(entry);\n\n const { safety, gateResult, proEnabled } = await runGate(candidatePath, '', {\n proFeatureLabel: MCP_PRO_FEATURE_LABEL,\n strictness: config.strictness,\n artifact: 'mcp',\n mcpTargets: config.mcpTargets ?? [],\n });\n const { profile, overlap } = gateResult;\n if (profile.kind !== 'mcp') {\n // Unreachable in practice: runGate()'s asProfile() already throws before\n // returning a non-mcp profile for artifact 'mcp'. Narrows the type.\n throw new Error(`expected an MCP candidate, found a ${profile.kind}`);\n }\n if (profile.serverCount > 1) {\n console.error(\n `[skill-forge] ${profile.serverCount} MCP servers found in ${source}; gating/promoting the first — ` +\n `the whole quarantine entry is promoted/rejected together:\\n ${profile.servers.join(', ')}`\n );\n }\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 detail = resolveMcpCandidateDetail(profile);\n const serverProfile: McpServerProfile = {\n name: detail.name,\n // version may be absent — promoteMcp refuses the unpinnable npx fallback\n // rather than writing \"npx pkg@unknown\"; see deriveServerEntry in promoteMcp.ts.\n version: detail.version,\n envVars: detail.env ? Object.keys(detail.env) : undefined,\n candidateEntry: detail.command ? { command: detail.command, args: detail.args, env: detail.env } : undefined,\n };\n const mcpTargetPath = options.mcpTarget ?? config.mcpTargets?.[0] ?? join(process.cwd(), '.mcp.json');\n\n const result = promoteMcp(entry, serverProfile, mcpTargetPath, { force: options.force });\n reject(entry); // promoteMcp doesn't touch the quarantine sandbox — clean it up now that the entry is written\n\n logOutcome(`[skill-forge] promoted -> ${result.target} (server \"${result.serverName}\")`, options.json);\n if (result.backupPath) {\n logOutcome(`[skill-forge] backed up existing config -> ${result.backupPath}`, options.json);\n }\n if (result.envVarsToFill.length > 0) {\n logOutcome(`[skill-forge] fill in these env vars before use: ${result.envVarsToFill.join(', ')}`, options.json);\n }\n installedPath = result.target;\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.\n if (proEnabled) {\n appendQueueEntry({\n source,\n sourceType: resolved.type,\n installedPath,\n quarantinePath: entry.quarantinePath,\n gate: {\n 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 artifactType: 'mcp',\n capabilities: profile.capabilities,\n });\n\n if (options.ingest && installedPath) {\n // Same generic handoff as the skill path — {path} substitutes to the\n // written MCP config file rather than an installed skill dir.\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\n/** Single source of truth for the strictness fallback — used by `defaultConfig()` and the gate's default parameters. */\nexport const DEFAULT_STRICTNESS: Strictness = 'block-high';\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 /** MCP config file targets recorded by `skill-forge init` (see Agent#mcpConfig in src/agents.ts). Consulted by MCP `promote` as a fallback when `--mcp-target` is omitted. */\n mcpTargets?: 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: DEFAULT_STRICTNESS,\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 mcpTargets: parsed.mcpTargets ?? defaults.mcpTargets,\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';\nimport { UserError } from '../errors.js';\nimport { expandHome } from '../util.js';\n\nexport type SourceType = 'skills.sh' | 'git' | 'local' | 'npm';\nexport type ArtifactType = 'skill' | 'mcp';\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// A scoped npm package spec: \"@scope/name\", optionally with a version/tag\n// suffix on the name half (e.g. \"@scope/name@1.2.3\"). Never collides with a\n// skills.sh `owner/name` slug, which has no leading \"@\".\nconst NPM_SCOPED_PATTERN = /^@[^/]+\\/[^/]+$/;\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 * - with `artifact: 'mcp'`, a scoped \"@scope/name\" package or a bare\n * (no-slash) package name is npm — an \"owner/name\" two-segment slug (no\n * leading \"@\") still falls through to skills.sh below, same as\n * `artifact: 'skill'`/absent: no ambiguity guessing between the two\n * owner/name-shaped source kinds\n * - everything else is treated as a skills.sh `owner/name` slug\n *\n * Skill classification (`artifact` 'skill' or absent) is unchanged from v0.4.\n */\nexport function resolve(source: string, artifact: ArtifactType = 'skill'): ResolvedSource {\n const value = source.trim();\n\n // Reject anything that looks like a CLI flag rather than a source: quarantine.ts\n // passes `value` straight through as a git-clone/npx/npm-pack argv positional, so\n // a source starting with \"-\" (e.g. \"--upload-pack=...\") could otherwise be\n // smuggled in as an injected flag. Fail fast with a friendly error instead of\n // reaching exec().\n if (value.startsWith('-')) {\n throw new UserError(`skill source cannot start with \"-\" (looks like a flag, not a source): ${value}`);\n }\n\n if (GIT_URL_PATTERN.test(value) || value.endsWith('.git')) {\n return { type: 'git', value };\n }\n\n // Expand ~ before existence checks so a real \"~/some-skill\" resolves instead\n // of erroring \"not found\" on the literal tilde string.\n const localCandidate = expandHome(value);\n if (existsSync(localCandidate) || LOCAL_PATH_PATTERN.test(value)) {\n return { type: 'local', value: localCandidate };\n }\n\n if (artifact === 'mcp' && (NPM_SCOPED_PATTERN.test(value) || !value.includes('/'))) {\n return { type: 'npm', value };\n }\n\n return { type: 'skills.sh', value };\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 { 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 { 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 /** Present only for npm-sourced (MCP) entries: the extracted `package/` dir inside the sandbox. */\n packageDir?: 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 let packageDir: string | undefined;\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 // `--` separates flags from positionals so a source value that looks like a\n // git flag (e.g. \"--upload-pack=...\") is always treated as the repo\n // positional, never parsed as an option — belt-and-suspenders alongside\n // resolve.ts's leading-dash rejection.\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 case 'npm': {\n // Fetch only — never `npm install`, never run scripts (`--ignore-scripts`\n // belt-and-suspenders alongside `npm pack` never running lifecycle\n // scripts on its own). Before extracting, list the tarball's members and\n // refuse to proceed if any member path could escape the sandbox\n // (path-traversal guard) — only once every member is confirmed safe do\n // we extract with system `tar`, so nothing from the package ever runs\n // during quarantine and nothing can be written outside the sandbox.\n const packResult = await exec(\n 'npm',\n ['pack', source.value, '--ignore-scripts', '--pack-destination', quarantinePath],\n { cwd: quarantinePath }\n );\n if (packResult.code !== 0) {\n throw new UserError(\n `npm pack failed for \"${source.value}\" (exit ${packResult.code}): ${packResult.stderr || packResult.stdout}`\n );\n }\n\n const tarballPath = join(quarantinePath, findTarball(quarantinePath, source.value));\n\n const listResult = await exec('tar', ['-tzf', tarballPath], { cwd: quarantinePath });\n if (listResult.code !== 0) {\n throw new UserError(\n `could not list tarball contents for \"${source.value}\" (exit ${listResult.code}): ${listResult.stderr || listResult.stdout}`\n );\n }\n assertSafeTarMembers(listResult.stdout, source.value);\n\n const extractResult = await exec('tar', ['-xzf', tarballPath, '-C', quarantinePath], {\n cwd: quarantinePath,\n });\n if (extractResult.code !== 0) {\n throw new UserError(\n `tarball extraction failed for \"${source.value}\" (exit ${extractResult.code}): ${extractResult.stderr || extractResult.stdout}`\n );\n }\n\n // `npm pack` tarballs always extract to a top-level `package/` dir.\n packageDir = join(quarantinePath, 'package');\n if (!existsSync(packageDir)) {\n throw new UserError(\n `npm pack for \"${source.value}\" did not produce the expected package/ dir after extraction`\n );\n }\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), packageDir, 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/**\n * Path-traversal guard: throws a UserError if any member path listed by\n * `tar -tzf` is absolute or contains a `..` path segment, BEFORE extraction\n * ever runs. (This checks member *names* only; link-type member safety still\n * rests on the system tar's own symlink safeguards.)\n */\nfunction assertSafeTarMembers(tarListOutput: string, sourceValue: string): void {\n for (const raw of tarListOutput.split('\\n')) {\n const member = raw.trim();\n if (!member) continue;\n if (member.startsWith('/') || member.split('/').includes('..')) {\n throw new UserError(\n `refusing to extract tarball for \"${sourceValue}\": unsafe member path \"${member}\" ` +\n '(absolute path or \"..\" segment — possible path traversal)'\n );\n }\n }\n}\n\n/** Locates the .tgz produced by `npm pack --pack-destination <dir>` (exactly one is expected, since each quarantine sandbox is freshly created before use). */\nfunction findTarball(dir: string, sourceValue: string): string {\n const tarball = readdirSync(dir).find((name) => name.endsWith('.tgz'));\n if (!tarball) {\n throw new UserError(`npm pack for \"${sourceValue}\" did not produce a .tgz file in ${dir}`);\n }\n return tarball;\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 { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type { QuarantineEntry } from './quarantine.js';\nimport { UserError } from '../errors.js';\n\n/**\n * Minimal shape promoteMcp needs to derive a server config entry. Deliberately\n * decoupled from the skill/plugin/mcp-config profiling in gate/profile.ts —\n * this describes an MCP *server* candidate (name/version + whatever launch\n * info the profile/gate steps found), not a directory that merely ships an\n * .mcp.json referencing other servers.\n */\nexport interface McpServerProfile {\n /** Server name — used as the `mcpServers` config key (e.g. \"@scope/name\" or \"name\"). */\n name: string;\n /**\n * Pinned version to launch via `npx <name>@<version>` — only needed on the\n * npx-fallback path (no `candidateEntry`); see `deriveServerEntry` for the\n * refusal behavior when both are absent.\n */\n version?: string;\n /** Env var NAMES the server declares/needs. Values are never read or copied. */\n envVars?: string[];\n /**\n * The candidate's own documented server entry (e.g. from a README example or a\n * shipped .mcp.json), used verbatim for command/args when present instead of the\n * npx-derived default. Any `env` values on it are always stripped before use.\n */\n candidateEntry?: { command: string; args?: string[]; env?: Record<string, unknown> };\n}\n\nexport interface PromoteMcpOptions {\n force?: boolean;\n}\n\nexport interface PromoteMcpResult {\n /** Absolute path to the target MCP config file that was written. */\n target: string;\n /** Server name (the `mcpServers` key) that was written. */\n serverName: string;\n /** Env var names the user must fill in before the server will work — values were never copied. */\n envVarsToFill: string[];\n /** Backup path created before modifying an existing target file; undefined when the file didn't exist. */\n backupPath?: string;\n}\n\ninterface McpConfigFile {\n mcpServers?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * Writes `profile`'s derived server entry into `targetConfigPath`'s `mcpServers`\n * map — the only place an MCP candidate is written outside its quarantine\n * sandbox, mirroring install/promote.ts's target-untouched-until-promote\n * invariant for skills. Creates the file (and any missing parent dirs) if\n * absent. Every refusal path (unpinnable version, same-name clash without\n * `force`) is checked before anything is backed up, so a refusal never leaves\n * a partial write behind. When an existing file *is* about to be\n * modified, it's backed up first to `<file>.bak-<timestamp>`. Env values are\n * NEVER copied from the candidate — they're always emitted as empty strings,\n * and the var names the user must fill in are returned.\n */\nexport function promoteMcp(\n _entry: QuarantineEntry,\n profile: McpServerProfile,\n targetConfigPath: string,\n options: PromoteMcpOptions = {}\n): PromoteMcpResult {\n const force = options.force ?? false;\n const serverName = profile.name;\n\n // Can throw (version refusal) — derived before any read/backup so a refusal\n // never leaves a stray .bak file behind.\n const { command, args, envVarsToFill } = deriveServerEntry(profile);\n\n let config: McpConfigFile = {};\n let rawExisting: string | undefined;\n\n if (existsSync(targetConfigPath)) {\n rawExisting = readFileSync(targetConfigPath, 'utf-8');\n try {\n config = JSON.parse(rawExisting) as McpConfigFile;\n } catch (err) {\n throw new UserError(`could not parse existing MCP config ${targetConfigPath}: ${(err as Error).message}`);\n }\n }\n\n const servers = { ...(config.mcpServers ?? {}) };\n if (Object.prototype.hasOwnProperty.call(servers, serverName) && !force) {\n throw new UserError(\n `Refusing to overwrite existing MCP server entry \"${serverName}\" in ${targetConfigPath} (pass force to override)`\n );\n }\n\n let backupPath: string | undefined;\n if (rawExisting !== undefined) {\n backupPath = `${targetConfigPath}.bak-${Date.now()}`;\n writeFileSync(backupPath, rawExisting);\n }\n\n const env: Record<string, string> = {};\n for (const name of envVarsToFill) env[name] = '';\n\n servers[serverName] = { command, args, env };\n config.mcpServers = servers;\n\n mkdirSync(dirname(targetConfigPath), { recursive: true });\n writeFileSync(targetConfigPath, `${JSON.stringify(config, null, 2)}\\n`);\n\n return { target: targetConfigPath, serverName, envVarsToFill, backupPath };\n}\n\n/**\n * Derives {command, args} from the candidate's own documented entry when\n * present, else `npx <name>@<version>` pinned to the profiled version. Env\n * values are always stripped from whichever source is used — only var names\n * are returned. When there's no documented entry AND no resolved version,\n * throws a UserError instead of ever writing an unpinnable `npx pkg@unknown`\n * launch command.\n */\nfunction deriveServerEntry(profile: McpServerProfile): {\n command: string;\n args: string[];\n envVarsToFill: string[];\n} {\n const envNames = new Set(profile.envVars ?? []);\n if (profile.candidateEntry?.env) {\n for (const key of Object.keys(profile.candidateEntry.env)) envNames.add(key);\n }\n\n if (profile.candidateEntry) {\n return {\n command: profile.candidateEntry.command,\n args: profile.candidateEntry.args ?? [],\n envVarsToFill: Array.from(envNames),\n };\n }\n\n if (!profile.version) {\n throw new UserError(\n `cannot promote MCP server \"${profile.name}\": no version was found in its package.json, and there is ` +\n 'no documented launch command to fall back to — refusing to write an unpinnable \"npx ' +\n `${profile.name}@unknown\" command`\n );\n }\n\n return {\n command: 'npx',\n args: ['-y', `${profile.name}@${profile.version}`],\n envVarsToFill: Array.from(envNames),\n };\n}\n","import { readFileSync } from 'node:fs';\nimport type { McpProfile } from '../gate/profile.js';\n\n/**\n * Launch details for the ONE MCP server being gated/promoted, derived from a\n * `McpProfile` (`gate/profile.ts`) plus — when the profile came from an\n * explicit `.mcp.json`/`mcp.json` — a second read of that file's chosen\n * server entry. `McpProfile` intentionally only tracks server NAMES (see\n * `profileMcp` in `gate/profile.ts`), not launch command/args/env, since its\n * source config may list more than one server; this is the one place that\n * resolves a profile down to a single server's launch details. Shared by\n * `commands/gatePipeline.ts`'s MCP overlap step and `install/promoteMcp.ts`'s\n * promote step so both derive from exactly the same read.\n */\nexport interface McpCandidateDetail {\n name: string;\n version?: string;\n command?: string;\n args?: string[];\n env?: Record<string, unknown>;\n}\n\ninterface ConfigEntry {\n command?: string;\n args?: string[];\n env?: Record<string, unknown>;\n}\n\nfunction readPackageVersion(pkgPath: string): string | undefined {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as Record<string, unknown>;\n return typeof pkg.version === 'string' ? pkg.version : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction readConfigEntry(configPath: string, name: string): ConfigEntry | undefined {\n try {\n const cfg = JSON.parse(readFileSync(configPath, 'utf-8')) as Record<string, unknown>;\n const servers = (cfg.mcpServers ?? cfg.servers ?? {}) as Record<string, unknown>;\n const raw = (servers[name] ?? {}) as Record<string, unknown>;\n return {\n command: typeof raw.command === 'string' ? raw.command : undefined,\n args: Array.isArray(raw.args) ? raw.args.filter((a): a is string => typeof a === 'string') : undefined,\n env:\n raw.env && typeof raw.env === 'object' && !Array.isArray(raw.env)\n ? (raw.env as Record<string, unknown>)\n : undefined,\n };\n } catch {\n return undefined;\n }\n}\n\n/**\n * Resolves launch details for `profile.servers[0]` — when a config lists more\n * than one server, gates/promotes the first, matching `add`'s/`scan`'s\n * existing \"N skills found — using the first\" convention for multi-skill\n * sources. A package.json-shaped profile (npm-sourced server) has no\n * command/args/env of its own — `promoteMcp` derives the launch command from\n * `name`/`version` (`npx <name>@<version>`) when both are absent here.\n */\nexport function resolveMcpCandidateDetail(profile: McpProfile): McpCandidateDetail {\n const name = profile.servers[0];\n\n if (profile.config.endsWith('package.json')) {\n return { name, version: readPackageVersion(profile.config) };\n }\n\n const entry = readConfigEntry(profile.config, name);\n return { name, command: entry?.command, args: entry?.args, env: entry?.env };\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 { readdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { DEFAULT_STRICTNESS, type Strictness } from '../config.js';\nimport { profileSkill, type Profile, type McpProfile } from '../gate/profile.js';\nimport { scanSafety, type SafetyResult as GateSafetyResult } from '../gate/safety.js';\nimport { scanOverlap, type OverlapResult } from '../gate/overlap.js';\nimport { mcpScan } from '../gate/mcpSafety.js';\nimport { scanMcpOverlap } from '../gate/mcpOverlap.js';\nimport { resolveMcpCandidateDetail } from '../install/mcpCandidate.js';\nimport type { ArtifactType } from '../install/resolve.js';\nimport type { QuarantineEntry } from '../install/quarantine.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 asProfile(candidatePath: string, artifact: ArtifactType): Profile {\n const profile = profileSkill(candidatePath);\n const expectedKind = artifact === 'mcp' ? 'mcp' : 'skill';\n if (profile.kind !== expectedKind) {\n throw new Error(\n artifact === 'mcp'\n ? `expected an MCP candidate at ${candidatePath} (--artifact mcp), found a ${profile.kind}`\n : `expected a skill candidate at ${candidatePath}, found a ${profile.kind}`\n );\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 * MCP counterpart of `tryOverlap`: ranks the candidate against every server\n * entry found across `mcpTargets` (see `gate/mcpOverlap.ts`). `scanMcpOverlap`\n * itself already tolerates missing/malformed/empty target files (returns an\n * empty ranking rather than throwing) — this try/catch is defensive only.\n */\nfunction tryMcpOverlap(profile: McpProfile, mcpTargets: string[]): OverlapResult | undefined {\n try {\n const detail = resolveMcpCandidateDetail(profile);\n return scanMcpOverlap(\n {\n name: detail.name,\n command: detail.command,\n args: detail.args,\n packageName: detail.command ? undefined : detail.name,\n },\n mcpTargets\n );\n } catch (err) {\n console.error(`[skill-forge] mcp overlap scan skipped: ${(err as Error).message}`);\n return undefined;\n }\n}\n\n/**\n * Candidate directory to profile/gate for an MCP artifact (v0.5): the\n * extracted npm package dir when the source was npm, else the single\n * subdirectory `quarantine()` copied/cloned the source into — local dir and\n * git URL, the other two v0.5 MCP source forms, both land in exactly one\n * subdirectory of a freshly created quarantine sandbox (see\n * `install/quarantine.ts`). Falls back to the sandbox root itself if that\n * can't be determined unambiguously.\n */\nexport function resolveMcpCandidatePath(entry: QuarantineEntry): string {\n if (entry.packageDir) return entry.packageDir;\n let children: string[] = [];\n try {\n children = readdirSync(entry.quarantinePath, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n // fall through to the sandbox root\n }\n return children.length === 1 ? join(entry.quarantinePath, children[0]) : entry.quarantinePath;\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 * `strictness` selects which finding severities produce a `block` safety\n * verdict (see `src/gate/safety.ts`); `add`/`scan` pass the value from the\n * config they already load, so the gate honors whatever is configured without\n * re-reading `config.json` here. Callers that omit it (e.g. unit tests) get\n * the `block-high` default. `artifact` (v0.5) routes the whole pipeline: for\n * `'mcp'` (explicit `--artifact mcp` only — never auto-detected here, see\n * `install/resolve.ts`'s \"no ambiguity guessing\" source-resolution policy),\n * safety runs `mcpScan` (base ruleset + MCP-specific rules) instead of\n * `scanSafety`, and overlap ranks against `mcpTargets` (every configured MCP\n * config file) via `scanMcpOverlap` instead of `scanOverlap`-against-\n * `skillsRoot`; `skillsRoot`/`mcpTargets` are simply unused by whichever\n * branch doesn't apply. Defaults to `'skill'`, the pre-v0.5 behavior.\n */\nexport async function runGate(\n candidatePath: string,\n skillsRoot: string,\n {\n proFeatureLabel = 'overlap analysis',\n strictness = DEFAULT_STRICTNESS,\n artifact = 'skill',\n mcpTargets = [],\n }: {\n proFeatureLabel?: string;\n strictness?: Strictness;\n artifact?: ArtifactType;\n mcpTargets?: string[];\n } = {}\n): Promise<GateRunResult> {\n const profile = asProfile(candidatePath, artifact);\n const proStatus = proFeatureStatus(proFeatureLabel);\n\n const safety = artifact === 'mcp' ? await mcpScan(candidatePath, strictness) : await scanSafety(candidatePath, strictness);\n const overlap = !proStatus.enabled\n ? undefined\n : artifact === 'mcp'\n ? tryMcpOverlap(profile as McpProfile, mcpTargets)\n : tryOverlap(candidatePath, skillsRoot);\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","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';\nimport { extractMcpCapabilities, type McpCapabilities } from './mcpCapabilities.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\n/**\n * v0.5: the artifact this candidate should be gated/promoted as — 'mcp' when\n * an MCP config file or an MCP-server-shaped package.json is detected,\n * 'skill' otherwise (default). Orthogonal to `kind`, which selects which\n * profiler shape ran (skill dir / plugin / mcp config dir).\n */\nexport type ArtifactType = 'skill' | 'mcp';\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 artifactType: ArtifactType;\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 artifactType: ArtifactType;\n}\n\nexport interface McpProfile {\n kind: 'mcp';\n inputPath: string;\n config: string;\n servers: string[];\n serverCount: number;\n artifactType: ArtifactType;\n /** v0.6: static, best-effort declared capabilities (tools/resources/prompts) — see mcpCapabilities.ts. */\n capabilities: McpCapabilities;\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 (detectArtifactType(p) === 'mcp') return 'mcp';\n return 'skill';\n}\n\nconst MCP_SDK_DEP = '@modelcontextprotocol/sdk';\nconst MCP_KEYWORD = 'mcp-server';\nconst MCP_BIN_PREFIX = /^mcp-/i;\n\n/** Reads package.json for detection purposes only — malformed/missing is not a signal either way. */\nfunction readPackageJsonSafe(root: string): Record<string, unknown> | undefined {\n const pkgPath = join(root, 'package.json');\n if (!existsSync(pkgPath)) return undefined;\n try {\n return JSON.parse(readFileSync(pkgPath, 'utf-8')) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n\n/** deps containing @modelcontextprotocol/sdk, keywords containing \"mcp-server\", or bin names like mcp-*. */\nfunction hasMcpPackageMarkers(pkg: Record<string, unknown>): boolean {\n for (const section of ['dependencies', 'devDependencies', 'peerDependencies'] as const) {\n const deps = pkg[section];\n if (deps && typeof deps === 'object' && MCP_SDK_DEP in (deps as Record<string, unknown>)) return true;\n }\n\n const keywords = pkg.keywords;\n if (Array.isArray(keywords) && keywords.some((k) => k === MCP_KEYWORD)) return true;\n\n const bin = pkg.bin;\n if (typeof bin === 'string') {\n if (MCP_BIN_PREFIX.test(basename(bin))) return true;\n } else if (bin && typeof bin === 'object') {\n if (Object.keys(bin as Record<string, unknown>).some((k) => MCP_BIN_PREFIX.test(k))) return true;\n }\n\n return false;\n}\n\n/** v0.5 artifact classification: an mcp config file, or an MCP-server-shaped package.json, at `root`. */\nfunction detectArtifactType(root: string): ArtifactType {\n if (existsSync(join(root, '.mcp.json')) || existsSync(join(root, 'mcp.json'))) return 'mcp';\n const pkg = readPackageJsonSafe(root);\n if (pkg && hasMcpPackageMarkers(pkg)) 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 artifactType: detectArtifactType(root),\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 artifactType: detectArtifactType(p),\n };\n}\n\nfunction profileMcp(p: string): McpProfile {\n const explicitConfig = existsSync(join(p, '.mcp.json'))\n ? join(p, '.mcp.json')\n : existsSync(join(p, 'mcp.json'))\n ? join(p, 'mcp.json')\n : undefined;\n\n if (explicitConfig) {\n let cfg: Record<string, unknown>;\n try {\n cfg = JSON.parse(readFileSync(explicitConfig, 'utf-8'));\n } catch (err) {\n throw new Error(`could not parse MCP config ${explicitConfig}: ${(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: explicitConfig,\n servers: names,\n serverCount: names.length,\n artifactType: 'mcp',\n capabilities: extractMcpCapabilities(p),\n };\n }\n\n // No .mcp.json/mcp.json — this is an MCP server package identified by its own\n // package.json markers (see hasMcpPackageMarkers / detectArtifactType).\n const pkgPath = join(p, 'package.json');\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n } catch (err) {\n throw new Error(`could not parse package.json ${pkgPath}: ${(err as Error).message}`);\n }\n const name = typeof pkg.name === 'string' && pkg.name.length > 0 ? pkg.name : basename(p);\n\n return {\n kind: 'mcp',\n inputPath: p,\n config: pkgPath,\n servers: [name],\n serverCount: 1,\n artifactType: 'mcp',\n capabilities: extractMcpCapabilities(p),\n };\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { extname, join } from 'node:path';\nimport { readTextFile, walk } from './scanShared.js';\n\n/**\n * v0.6: static, best-effort MCP server capability profiling.\n *\n * STATIC ONLY — this module reads files as plain text and JSON. It never\n * dynamically imports, requires, spawns, or otherwise executes anything from\n * the profiled package to discover its tools/resources/prompts. Anything\n * that can't be determined from source text is left out of the result\n * rather than guessed at by running the server — see `declaredConfidence`.\n *\n * Signals parsed, in order of strength:\n * - source text of .ts/.tsx/.js/.jsx/.mjs/.cjs files (node_modules/.git/dist\n * skipped): MCP SDK registration call patterns —\n * server.tool(...)/server.registerTool(...)/server.resource(...)/server.prompt(...)\n * — plus a best-effort scan of setRequestHandler(List*RequestSchema, ...)\n * handler bodies for name/uri literals. Presence of either -> 'high'.\n * - a shipped .mcp.json / mcp.json manifest, if its declared server entries\n * carry an explicit tools/resources/prompts list (uncommon, but checked).\n * - package.json: bin names, keywords, an `@modelcontextprotocol/sdk`\n * dependency, or an `mcp` field — presence-only signal (no capability\n * names), used for 'partial' confidence when nothing more specific was\n * found in source text.\n */\n\nexport type McpTransport = 'stdio' | 'http' | 'unknown';\nexport type DeclaredConfidence = 'high' | 'partial' | 'none';\n\nexport interface McpCapabilities {\n tools: string[];\n resources: string[];\n prompts: string[];\n transport?: McpTransport;\n declaredConfidence: DeclaredConfidence;\n}\n\nconst SOURCE_EXT = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']);\nconst MAX_ITEMS_PER_CATEGORY = 200;\n\n/** The three capability categories, always collected together as one set of buckets. */\ntype Category = 'tools' | 'resources' | 'prompts';\ntype Buckets = Record<Category, Set<string>>;\n\nfunction readJsonSafe(file: string): unknown {\n try {\n return JSON.parse(readFileSync(file, 'utf-8'));\n } catch {\n return undefined;\n }\n}\n\n// ---- package.json markers (presence-only signal; never yields capability names) ----\n\nconst MCP_SDK_DEP = '@modelcontextprotocol/sdk';\nconst MCP_KEYWORD = 'mcp-server';\nconst MCP_BIN_PREFIX = /^mcp-/i;\n\nfunction hasPackageJsonMcpMarkers(pkg: Record<string, unknown>): boolean {\n for (const section of ['dependencies', 'devDependencies', 'peerDependencies'] as const) {\n const deps = pkg[section];\n if (deps && typeof deps === 'object' && MCP_SDK_DEP in (deps as Record<string, unknown>)) return true;\n }\n\n const keywords = pkg.keywords;\n if (Array.isArray(keywords) && keywords.some((k) => k === MCP_KEYWORD)) return true;\n\n const bin = pkg.bin;\n if (typeof bin === 'string') {\n if (MCP_BIN_PREFIX.test(bin.split('/').pop() ?? bin)) return true;\n } else if (bin && typeof bin === 'object') {\n if (Object.keys(bin as Record<string, unknown>).some((k) => MCP_BIN_PREFIX.test(k))) return true;\n }\n\n if (pkg.mcp && typeof pkg.mcp === 'object') return true;\n\n return false;\n}\n\n// ---- .mcp.json / mcp.json manifest: declared tools/resources/prompts arrays, if present ----\n\nfunction collectDeclaredFromManifest(node: unknown, buckets: Buckets): void {\n if (Array.isArray(node)) {\n for (const item of node) collectDeclaredFromManifest(item, buckets);\n return;\n }\n if (!node || typeof node !== 'object') return;\n\n const obj = node as Record<string, unknown>;\n for (const [key, value] of Object.entries(obj)) {\n if ((key === 'tools' || key === 'resources' || key === 'prompts') && Array.isArray(value)) {\n const target = buckets[key];\n for (const item of value) {\n if (typeof item === 'string') {\n target.add(item);\n } else if (item && typeof item === 'object') {\n const name = (item as Record<string, unknown>).name ?? (item as Record<string, unknown>).uri;\n if (typeof name === 'string') target.add(name);\n }\n }\n } else {\n collectDeclaredFromManifest(value, buckets);\n }\n }\n}\n\n// ---- MCP SDK registration call patterns ----\n// Single pass over server.tool()/registerTool()/resource()/prompt(): the method name\n// (group 1) routes the first string arg (group 2) to its category — tool and registerTool\n// both feed `tools`.\n\nconst REGISTRATION_CALL_RE = /\\bserver\\.(tool|registerTool|resource|prompt)\\(\\s*['\"]([^'\"]+)['\"]/g;\nconst REGISTRATION_METHOD_CATEGORY: Record<string, Category> = {\n tool: 'tools',\n registerTool: 'tools',\n resource: 'resources',\n prompt: 'prompts',\n};\n\nfunction captureRegistrations(text: string, buckets: Buckets): void {\n REGISTRATION_CALL_RE.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = REGISTRATION_CALL_RE.exec(text)) !== null) {\n buckets[REGISTRATION_METHOD_CATEGORY[m[1]]].add(m[2]);\n }\n}\n\n// ---- best-effort setRequestHandler(List*RequestSchema, ...) body scan ----\n\nconst LIST_HANDLER_SCHEMAS: Record<string, Category> = {\n ListToolsRequestSchema: 'tools',\n ListResourcesRequestSchema: 'resources',\n ListPromptsRequestSchema: 'prompts',\n};\nconst SET_REQUEST_HANDLER_RE =\n /setRequestHandler\\(\\s*(ListToolsRequestSchema|ListResourcesRequestSchema|ListPromptsRequestSchema)\\s*,/g;\nconst NAME_OR_URI_FIELD_RE = /\\b(?:name|uri)\\s*:\\s*['\"]([^'\"]+)['\"]/g;\n\n/** Returns the text of the balanced `(...)` call starting at `openParenIndex`, or a capped slice if unbalanced. */\nfunction extractBalancedCall(text: string, openParenIndex: number): string {\n let depth = 0;\n for (let i = openParenIndex; i < text.length; i++) {\n const ch = text[i];\n if (ch === '(') depth++;\n else if (ch === ')') {\n depth--;\n if (depth === 0) return text.slice(openParenIndex, i + 1);\n }\n }\n return text.slice(openParenIndex, Math.min(text.length, openParenIndex + 20000));\n}\n\nfunction extractFromListHandlers(text: string, buckets: Buckets): void {\n SET_REQUEST_HANDLER_RE.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = SET_REQUEST_HANDLER_RE.exec(text)) !== null) {\n const openParenIdx = text.indexOf('(', m.index);\n if (openParenIdx === -1) continue;\n const body = extractBalancedCall(text, openParenIdx);\n const target = buckets[LIST_HANDLER_SCHEMAS[m[1]]];\n\n NAME_OR_URI_FIELD_RE.lastIndex = 0;\n let fieldMatch: RegExpExecArray | null;\n while ((fieldMatch = NAME_OR_URI_FIELD_RE.exec(body)) !== null) {\n target.add(fieldMatch[1]);\n }\n }\n}\n\n// ---- transport hints ----\n\nconst STDIO_TRANSPORT_RE = /StdioServerTransport/;\nconst HTTP_TRANSPORT_RE = /StreamableHTTPServerTransport|SSEServerTransport|WebSocketServerTransport/;\nconst MCP_SDK_IMPORT_RE = /@modelcontextprotocol\\/sdk/;\n\nexport function extractMcpCapabilities(packageDir: string): McpCapabilities {\n const buckets: Buckets = { tools: new Set(), resources: new Set(), prompts: new Set() };\n\n let hasPackageJsonMarker = false;\n const pkgPath = join(packageDir, 'package.json');\n if (existsSync(pkgPath)) {\n const pkg = readJsonSafe(pkgPath);\n if (pkg && typeof pkg === 'object' && !Array.isArray(pkg)) {\n hasPackageJsonMarker = hasPackageJsonMcpMarkers(pkg as Record<string, unknown>);\n }\n }\n\n for (const manifestName of ['.mcp.json', 'mcp.json']) {\n const manifestPath = join(packageDir, manifestName);\n if (!existsSync(manifestPath)) continue;\n const parsed = readJsonSafe(manifestPath);\n if (parsed !== undefined) {\n collectDeclaredFromManifest(parsed, buckets);\n }\n }\n\n let sawStdioTransport = false;\n let sawHttpTransport = false;\n let sawMcpSdkImport = false;\n\n for (const file of walk(packageDir)) {\n if (!SOURCE_EXT.has(extname(file))) continue;\n const text = readTextFile(file);\n if (text === undefined) continue;\n\n captureRegistrations(text, buckets);\n extractFromListHandlers(text, buckets);\n\n if (STDIO_TRANSPORT_RE.test(text)) sawStdioTransport = true;\n if (HTTP_TRANSPORT_RE.test(text)) sawHttpTransport = true;\n if (MCP_SDK_IMPORT_RE.test(text)) sawMcpSdkImport = true;\n }\n\n const transport: McpTransport | undefined = sawStdioTransport\n ? 'stdio'\n : sawHttpTransport\n ? 'http'\n : sawMcpSdkImport\n ? 'unknown'\n : undefined;\n\n const sortCap = (set: Set<string>): string[] => Array.from(set).sort().slice(0, MAX_ITEMS_PER_CATEGORY);\n const tools = sortCap(buckets.tools);\n const resources = sortCap(buckets.resources);\n const prompts = sortCap(buckets.prompts);\n\n const hasExplicitSignal = tools.length > 0 || resources.length > 0 || prompts.length > 0;\n const declaredConfidence: DeclaredConfidence = hasExplicitSignal\n ? 'high'\n : hasPackageJsonMarker\n ? 'partial'\n : 'none';\n\n return {\n tools,\n resources,\n prompts,\n transport,\n declaredConfidence,\n };\n}\n","import { readFileSync, readdirSync, statSync } from 'node:fs';\nimport { extname, join } from 'node:path';\nimport type { Strictness } from '../config.js';\n\n/**\n * Helpers shared between the gate scanners — `gate/safety.ts` (the base\n * deny-ruleset scanner), `gate/mcpSafety.ts` (MCP-specific findings layered on\n * top of it), and `gate/mcpCapabilities.ts` (static capability profiling): the\n * file-tree walker, safe text-file reader, and the severity->verdict mapping.\n * These modules import from here instead of maintaining their own copies —\n * previously these were duplicated per file and risked drifting out of sync.\n */\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\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/servers ship small text/scripts\n\nexport function* 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\nexport function 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\n/**\n * Ported from skill_safety.py's SkillSpector severity->verdict mapping\n * (LOW allow, MEDIUM caution, HIGH/CRITICAL block).\n *\n * `config.strictness` (see src/config.ts) selects which severities block:\n * `block-high` (default) is the mapping above; `block-medium` also blocks on\n * MEDIUM; `warn-only` never blocks (findings still surface, capped at warn).\n */\nconst BLOCK_SEVERITIES_BY_STRICTNESS: Record<Strictness, ReadonlySet<Severity>> = {\n 'block-high': new Set(['HIGH', 'CRITICAL']),\n 'block-medium': new Set(['MEDIUM', 'HIGH', 'CRITICAL']),\n 'warn-only': new Set(),\n};\n\nexport function verdictFor(findings: SafetyFinding[], strictness: Strictness): Verdict {\n const blockSeverities = BLOCK_SEVERITIES_BY_STRICTNESS[strictness];\n if (findings.some((f) => blockSeverities.has(f.severity))) return 'block';\n if (findings.length > 0) return 'warn';\n return 'pass';\n}\n","import { statSync } from 'node:fs';\nimport { execFile } from 'node:child_process';\nimport { delimiter, join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { DEFAULT_STRICTNESS, type Strictness } from '../config.js';\nimport { readTextFile, verdictFor, walk } from './scanShared.js';\nimport type { SafetyFinding, SafetyResult, Severity } from './scanShared.js';\n\nexport type { SafetyFinding, SafetyResult, Severity, Verdict } from './scanShared.js';\n\nconst execFileAsync = promisify(execFile);\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\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\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. `strictness` (default `DEFAULT_STRICTNESS` from\n * `config.ts`) selects which severities produce a `block` verdict —\n * see `BLOCK_SEVERITIES_BY_STRICTNESS` in `./scanShared.ts`.\n */\nexport async function scanSafety(targetPath: string, strictness: Strictness = DEFAULT_STRICTNESS): Promise<SafetyResult> {\n const findings = [...scanStaticRules(targetPath), ...(await scanWithSkillSpector(targetPath))];\n return { findings, verdict: verdictFor(findings, strictness) };\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","import { statSync } from 'node:fs';\nimport { basename, extname } from 'node:path';\nimport { DEFAULT_STRICTNESS, type Strictness } from '../config.js';\nimport { scanSafety } from './safety.js';\nimport { readTextFile, verdictFor, walk } from './scanShared.js';\nimport type { SafetyFinding, SafetyResult } from './scanShared.js';\n\n/**\n * MCP-specific findings, composed on top of the base deny-ruleset scan\n * (`scanSafety`). curl|bash is deliberately NOT re-implemented here — the\n * base scan's `curl-pipe-shell` rule already covers it; `mcpScan` merges its\n * findings in.\n *\n * Detects:\n * - mcp-inline-credential-value (HIGH): a *_KEY/*_TOKEN/*_SECRET-shaped env\n * var (or any high-entropy value) assigned a real-looking value instead of\n * an empty string / ${VAR} placeholder, in an MCP server's `env` block or\n * a config/example text file.\n * - mcp-unpinned-npx (MEDIUM): `npx -y <pkg>` / `npx --yes <pkg>` launch\n * commands (structured `{command,args}` or shell text) without a pinned\n * `@<version>`.\n * - mcp-dangerous-flag (HIGH): `--dangerously-*` or `--no-sandbox` launch args.\n * - mcp-filesystem-root-arg (MEDIUM): a launch arg that is exactly \"/\" or \"~\".\n */\n\nfunction isConfigOrExampleFile(file: string): boolean {\n const base = basename(file).toLowerCase();\n if (base.startsWith('.env')) return true;\n if (/\\.(json|ya?ml)$/.test(base)) return true;\n return /(example|sample|config)/.test(base);\n}\n\nconst CREDENTIAL_KEY_RE = /(API[_-]?KEY|SECRET|TOKEN|PASSWORD|CREDENTIAL)/i;\nfunction looksCredentialKey(name: string): boolean {\n return CREDENTIAL_KEY_RE.test(name);\n}\n\nconst PLACEHOLDER_VALUE_RE =\n /^(\\$\\{.*\\}|\\$[A-Z0-9_]+|your[_-]?.*|<.*>|xxx+|changeme|replace[_-]?me|example|placeholder|todo|fill[_-]?me|none|null|redacted|\\.\\.\\.)$/i;\nfunction isPlaceholderValue(value: string): boolean {\n const v = value.trim();\n if (v === '') return true;\n if (v.startsWith('process.env')) return true;\n return PLACEHOLDER_VALUE_RE.test(v);\n}\n\n/** Length/charset heuristic for \"looks like a real secret\" — no whitespace, mixed charset, low repetition. */\nfunction looksHighEntropy(value: string): boolean {\n if (/\\s/.test(value)) return false;\n if (value.length < 20) return false;\n const hasLower = /[a-z]/.test(value);\n const hasUpper = /[A-Z]/.test(value);\n const hasDigit = /[0-9]/.test(value);\n const varietyCount = [hasLower, hasUpper, hasDigit].filter(Boolean).length;\n const uniqueChars = new Set(value).size;\n return varietyCount >= 2 && uniqueChars / value.length > 0.4;\n}\n\n/**\n * Scoped packages pin as `@scope/name@version`; unscoped as `name@version`.\n * A spec only counts as pinned when the version after the final `@` is\n * digit-leading (semver-ish, e.g. `1.2.3`) — moving/dist tags like `latest`,\n * `next`, `beta`, `canary`, or `dev` (or any other non-numeric tag) resolve to\n * whatever the registry currently points at and must be treated as unpinned.\n */\nfunction isPinnedPackageSpec(spec: string): boolean {\n const withoutScope = spec.startsWith('@') ? spec.slice(spec.indexOf('/') + 1) : spec;\n const atIdx = withoutScope.lastIndexOf('@');\n if (atIdx <= 0) return false;\n return /^\\d/.test(withoutScope.slice(atIdx + 1));\n}\n\n/** Strips a leading directory and a trailing `.cmd`/`.exe`, case-insensitively, so `/usr/bin/npx`, `npx.cmd`, and `NPX.CMD` all count. */\nfunction isNpxCommand(command: string): boolean {\n const base = command.split(/[\\\\/]/).pop() ?? command;\n return base.replace(/\\.(cmd|exe)$/i, '').toLowerCase() === 'npx';\n}\n\n/** `name@latest` -> `name` (drops a trailing `@tag`, preserving any `@scope/`) for suggestion text. */\nfunction packageSpecBase(spec: string): string {\n const scopeEnd = spec.startsWith('@') ? spec.indexOf('/') + 1 : 0;\n const atIdx = spec.lastIndexOf('@');\n return atIdx > scopeEnd ? spec.slice(0, atIdx) : spec;\n}\n\ninterface ServerLikeNode {\n command?: unknown;\n args?: unknown;\n env?: unknown;\n}\n\nfunction collectServerNodes(node: unknown, out: ServerLikeNode[]): void {\n if (Array.isArray(node)) {\n for (const item of node) collectServerNodes(item, out);\n return;\n }\n if (node && typeof node === 'object') {\n const obj = node as Record<string, unknown>;\n if (typeof obj.command === 'string' || Array.isArray(obj.args) || (obj.env && typeof obj.env === 'object')) {\n out.push(obj);\n }\n for (const value of Object.values(obj)) collectServerNodes(value, out);\n }\n}\n\nfunction tryParseJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\n/** Structural scan of a parsed MCP-config-shaped JSON document (mcpServers/servers entries, nested or not). */\nfunction scanJsonConfig(file: string, parsed: unknown): SafetyFinding[] {\n const findings: SafetyFinding[] = [];\n const nodes: ServerLikeNode[] = [];\n collectServerNodes(parsed, nodes);\n\n for (const node of nodes) {\n const command = typeof node.command === 'string' ? node.command : undefined;\n const args = Array.isArray(node.args) ? node.args.filter((a): a is string => typeof a === 'string') : [];\n\n if (command && isNpxCommand(command)) {\n const flagIdx = args.findIndex((a) => a === '-y' || a === '--yes');\n if (flagIdx !== -1) {\n const pkgSpec = args.slice(flagIdx + 1).find((a) => !a.startsWith('-'));\n if (pkgSpec && !isPinnedPackageSpec(pkgSpec)) {\n findings.push({\n severity: 'MEDIUM',\n rule: 'mcp-unpinned-npx',\n file,\n detail: `npx -y ${pkgSpec} is unpinned — pin an exact version (${packageSpecBase(pkgSpec)}@<version>) so the resolved server code can't change silently`,\n });\n }\n }\n }\n\n for (const a of args) {\n if (/^--dangerously-/i.test(a) || a === '--no-sandbox') {\n findings.push({\n severity: 'HIGH',\n rule: 'mcp-dangerous-flag',\n file,\n detail: `launch arg ${a} disables a safety guard`,\n });\n }\n if (a === '/' || a === '~') {\n findings.push({\n severity: 'MEDIUM',\n rule: 'mcp-filesystem-root-arg',\n file,\n detail: `launch arg \"${a}\" grants filesystem access at the root/home directory — scope this to a specific directory`,\n });\n }\n }\n\n if (node.env && typeof node.env === 'object' && !Array.isArray(node.env)) {\n for (const [key, value] of Object.entries(node.env as Record<string, unknown>)) {\n if (typeof value !== 'string' || isPlaceholderValue(value)) continue;\n if (looksCredentialKey(key) || looksHighEntropy(value)) {\n findings.push({\n severity: 'HIGH',\n rule: 'mcp-inline-credential-value',\n file,\n detail: `env.${key} has an inline credential value — emit an empty string and document that the user must fill it in`,\n });\n }\n }\n }\n }\n\n return findings;\n}\n\n// `(?:\\.cmd|\\.exe)?` and the `i` flag mirror isNpxCommand's basename\n// normalization so the text path honors the same \"recognized by basename\"\n// guarantee as the JSON path.\nconst NPX_SHELL_RE = /\\bnpx(?:\\.cmd|\\.exe)?\\s+(?:-y|--yes)\\s+((?:@[\\w.-]+\\/)?[\\w.-]+)(@[\\w.-]+)?/gi;\nconst DANGEROUS_FLAG_RE = /--dangerously-[\\w-]+|--no-sandbox\\b/g;\n/** Quoted assignments: `NAME=\"value\"` / `NAME: 'value'`. */\nconst CREDENTIAL_ASSIGNMENT_RE = /[\"']?([A-Za-z][A-Za-z0-9_]*)[\"']?\\s*[:=]\\s*[\"']([^\"']{6,})[\"']/g;\n/**\n * Unquoted assignments: `NAME=value` / `NAME: value` — no quotes around the\n * value — including the common prefixed forms `export NAME=value` (shell-sourced\n * .env) and `- NAME=value` (YAML list-style env, e.g. docker-compose).\n */\nconst CREDENTIAL_ASSIGNMENT_UNQUOTED_RE = /^\\s*(?:export\\s+|-\\s+)?([A-Za-z][A-Za-z0-9_]*)\\s*[:=]\\s*(\\S+)/;\n/** A value starting with a quote is the quoted regex's job — guards against double-reporting one line. */\nconst LEADING_QUOTE_RE = /^[\"']/;\n\nfunction pushCredentialFinding(\n findings: SafetyFinding[],\n file: string,\n lineNo: number,\n varName: string,\n value: string,\n): void {\n if (isPlaceholderValue(value)) return;\n if (looksCredentialKey(varName) || looksHighEntropy(value)) {\n findings.push({\n severity: 'HIGH',\n rule: 'mcp-inline-credential-value',\n file,\n detail: `line ${lineNo}: inline credential value assigned to ${varName}`,\n });\n }\n}\n\n/** Fallback line-based scan for non-JSON (or unparseable) text files: shell examples, README, .env. */\nfunction scanTextFile(file: string, content: string, allowCredentialCheck: boolean): SafetyFinding[] {\n const findings: SafetyFinding[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const lineNo = i + 1;\n\n NPX_SHELL_RE.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = NPX_SHELL_RE.exec(line)) !== null) {\n const pkgSpec = m[1];\n const versionSuffix = m[2] ?? '';\n if (!isPinnedPackageSpec(pkgSpec + versionSuffix)) {\n findings.push({\n severity: 'MEDIUM',\n rule: 'mcp-unpinned-npx',\n file,\n detail: `line ${lineNo}: npx -y ${pkgSpec} is unpinned — pin an exact version (${pkgSpec}@<version>)`,\n });\n }\n }\n\n DANGEROUS_FLAG_RE.lastIndex = 0;\n while ((m = DANGEROUS_FLAG_RE.exec(line)) !== null) {\n findings.push({\n severity: 'HIGH',\n rule: 'mcp-dangerous-flag',\n file,\n detail: `line ${lineNo}: launch arg ${m[0]} disables a safety guard`,\n });\n }\n\n if (allowCredentialCheck) {\n CREDENTIAL_ASSIGNMENT_RE.lastIndex = 0;\n while ((m = CREDENTIAL_ASSIGNMENT_RE.exec(line)) !== null) {\n pushCredentialFinding(findings, file, lineNo, m[1], m[2]);\n }\n\n const unquoted = CREDENTIAL_ASSIGNMENT_UNQUOTED_RE.exec(line);\n if (unquoted && !LEADING_QUOTE_RE.test(unquoted[2])) {\n pushCredentialFinding(findings, file, lineNo, unquoted[1], unquoted[2]);\n }\n }\n }\n\n return findings;\n}\n\nfunction scanFileForMcpFindings(file: string): SafetyFinding[] {\n const content = readTextFile(file);\n if (content === undefined) return [];\n\n if (extname(file).toLowerCase() === '.json') {\n const parsed = tryParseJson(content);\n if (parsed !== undefined) return scanJsonConfig(file, parsed);\n // malformed JSON — fall through to the text scan below\n }\n\n return scanTextFile(file, content, isConfigOrExampleFile(file));\n}\n\n/** Walks `targetPath` and returns MCP-specific findings only (not merged with the base ruleset). */\nexport function scanMcpFindings(targetPath: string): SafetyFinding[] {\n let root;\n try {\n root = statSync(targetPath);\n } catch {\n return [];\n }\n const files = root.isDirectory() ? Array.from(walk(targetPath)) : [targetPath];\n\n const findings: SafetyFinding[] = [];\n for (const file of files) {\n findings.push(...scanFileForMcpFindings(file));\n }\n return findings;\n}\n\n/**\n * MCP gate safety scan: the base deny-ruleset (`scanSafety`, includes curl|bash,\n * credential-file-access, etc.) plus the MCP-specific findings above, merged\n * into a single verdict via the same strictness rules as `scanSafety`.\n */\nexport async function mcpScan(targetPath: string, strictness: Strictness = DEFAULT_STRICTNESS): Promise<SafetyResult> {\n const base = await scanSafety(targetPath, strictness);\n const mcpFindings = scanMcpFindings(targetPath);\n const findings = [...base.findings, ...mcpFindings];\n return { findings, verdict: verdictFor(findings, strictness) };\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 } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { expandHome } from '../util.js';\nimport { suggestVerb, type OverlapResult, type SkillOverlapScore } from './overlap.js';\n\n/**\n * MCP overlap gate (v0.5) — ranks a candidate MCP server against the server\n * entries already present in the user's detected mcp config files, by name\n * similarity + same package/command. No canonical Python source exists for\n * this one (unlike gate/overlap.ts's skill overlap); it's new for v0.5.\n *\n * Returns the same `OverlapResult` shape gate/overlap.ts's scanOverlap()\n * returns (and reuses its score-tier suggestVerb()) so `report/render.ts`\n * renders an MCP candidate's overlap without any changes: `ranking[].skill`\n * holds the existing server's name, `ranking[].path` the config file it came\n * from.\n */\n\n/**\n * Minimal shape scanMcpOverlap needs to describe the candidate being added.\n * Deliberately decoupled from src/gate/profile.ts's McpProfile — callers\n * adapt whatever candidate profile they built (npm package.json, a local\n * dir's mcp.json, etc.) into this shape.\n */\nexport interface McpOverlapCandidate {\n /** Candidate server name (the config key it would be promoted under). */\n name: string;\n /** Launch command (e.g. \"npx\", \"node\", a binary path) — bin/entry hint. */\n command?: string;\n /** Launch args — used for command/package similarity (e.g. [\"-y\", \"@scope/pkg\"]). */\n args?: string[];\n /** npm package name, when the candidate is an npm-sourced MCP server. */\n packageName?: string;\n}\n\ninterface McpTargetServer {\n name: string;\n command?: string;\n args?: string[];\n}\n\n/** Reads one mcp config file's `mcpServers` (falling back to `servers`) map. Missing/malformed files yield no entries. */\nfunction readTargetServers(targetPath: string): McpTargetServer[] {\n const path = resolve(expandHome(targetPath));\n if (!existsSync(path)) return [];\n\n let cfg: Record<string, unknown>;\n try {\n cfg = JSON.parse(readFileSync(path, 'utf-8'));\n } catch {\n return [];\n }\n\n const servers = (cfg.mcpServers ?? cfg.servers ?? {}) as Record<string, unknown>;\n if (typeof servers !== 'object' || servers === null) return [];\n\n return Object.entries(servers).map(([name, raw]) => {\n const v = (raw ?? {}) as Record<string, unknown>;\n return {\n name,\n command: typeof v.command === 'string' ? v.command : undefined,\n args: Array.isArray(v.args) ? v.args.filter((a): a is string => typeof a === 'string') : undefined,\n };\n });\n}\n\nfunction tokenizeName(name: string): Set<string> {\n return new Set(name.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean));\n}\n\n/** Exact match = 1; else Jaccard token overlap on the name split by non-alphanumerics. */\nfunction nameSimilarity(a: string, b: string): number {\n if (a.toLowerCase() === b.toLowerCase()) return 1;\n const ta = tokenizeName(a);\n const tb = tokenizeName(b);\n if (ta.size === 0 || tb.size === 0) return 0;\n let inter = 0;\n for (const t of ta) if (tb.has(t)) inter += 1;\n const union = new Set([...ta, ...tb]).size;\n return union ? inter / union : 0;\n}\n\n/** Strips a trailing \"@version\" from a package spec without breaking a leading \"@scope/name\". */\nfunction packageBaseName(spec: string): string {\n const searchFrom = spec.startsWith('@') ? 1 : 0;\n const atIdx = spec.indexOf('@', searchFrom);\n return atIdx === -1 ? spec : spec.slice(0, atIdx);\n}\n\n/** Identifies the package/binary a launch command targets: the first non-flag arg for a package-runner command, else the command's basename (so a bare binary name matches a full path to the same binary). */\nfunction extractPackageSpec(command?: string, args: string[] = []): string | undefined {\n if (!command) return undefined;\n const base = command.split('/').pop() ?? command;\n if (['npx', 'npm', 'pnpm', 'yarn', 'uvx'].includes(base)) {\n return args.find((a) => !a.startsWith('-'));\n }\n return base;\n}\n\n/** 1 if candidate and target resolve to the same package/binary identity, else 0. */\nfunction commandSimilarity(\n candidate: McpOverlapCandidate,\n target: Pick<McpTargetServer, 'command' | 'args'>,\n): number {\n const candSpec = candidate.packageName ?? extractPackageSpec(candidate.command, candidate.args ?? []);\n const targetSpec = extractPackageSpec(target.command, target.args ?? []);\n if (!candSpec || !targetSpec) return 0;\n return packageBaseName(candSpec).toLowerCase() === packageBaseName(targetSpec).toLowerCase() ? 1 : 0;\n}\n\nfunction round3(n: number): number {\n return Math.round(n * 1000) / 1000;\n}\n\nfunction score(candidate: McpOverlapCandidate, target: McpTargetServer): number {\n return round3(0.6 * nameSimilarity(candidate.name, target.name) + 0.4 * commandSimilarity(candidate, target));\n}\n\n/**\n * Ranks `candidate` against every server entry found across `mcpTargetPaths`\n * (each file's `mcpServers` map). Unlike scanOverlap(), a target file that's\n * missing or empty isn't an error — a brand-new mcp config with zero servers\n * is the normal case for a first MCP install — so this returns an\n * empty-ranking result (`nearest: ''`, `nearestScore: 0`) instead of\n * throwing.\n */\nexport function scanMcpOverlap(candidate: McpOverlapCandidate, mcpTargetPaths: string[]): OverlapResult {\n const ranking: SkillOverlapScore[] = [];\n\n for (const targetPath of mcpTargetPaths) {\n const resolvedPath = resolve(expandHome(targetPath));\n for (const target of readTargetServers(targetPath)) {\n ranking.push({ skill: target.name, path: resolvedPath, score: score(candidate, target) });\n }\n }\n ranking.sort((a, b) => b.score - a.score);\n\n const top = ranking[0];\n return {\n candidate: candidate.name,\n nearest: top?.skill ?? '',\n nearestScore: top?.score ?? 0,\n suggestedVerb: suggestVerb(top?.score ?? 0),\n ranking,\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 { 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 { Profile, McpProfile } 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: Profile;\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/**\n * Header fields for the \"🔨 Forge: ...\" line — a skill profile has\n * name/version/license; an MCP profile (v0.5) has no such fields (it may\n * describe more than one server entry), so it renders the server name(s)\n * instead and 'n/a' for version/license. Plugin profiles have name/version\n * but no license (not currently reachable via add/scan's CLI routing).\n */\nfunction profileHeaderFields(profile: Profile): { name: string; version: string; license: string } {\n if (profile.kind === 'mcp') {\n const name = profile.serverCount === 1 ? profile.servers[0] : `${profile.serverCount} servers`;\n return { name, version: 'n/a', license: 'n/a' };\n }\n return {\n name: profile.name ?? 'unknown',\n version: profile.version ?? 'unknown',\n license: profile.kind === 'skill' ? profile.license : 'UNKNOWN',\n };\n}\n\nfunction pluralize(count: number, noun: string): string {\n return `${count} ${noun}${count === 1 ? '' : 's'}`;\n}\n\n/**\n * v0.6: one-line static capability summary for an MCP profile — tool/resource/prompt\n * counts plus `declaredConfidence`, so a candidate's declared surface is visible without\n * `--json`. Free tier (it's profiling, not the Pro overlap/decide pass) — always rendered\n * for `profile.kind === 'mcp'`, never gated on `overlapLocked`/`proEnabled`.\n */\nfunction renderCapabilitiesLine(profile: McpProfile): string {\n const { tools, resources, prompts, declaredConfidence } = profile.capabilities;\n const counts = [pluralize(tools.length, 'tool'), pluralize(resources.length, 'resource'), pluralize(prompts.length, 'prompt')].join(\n ', '\n );\n return `${padLabel('Capabilities')}: ${counts} (declaredConfidence: ${declaredConfidence})`;\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, version, license } = profileHeaderFields(profile);\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 `${padLabel('Artifact type')}: ${profile.artifactType}`,\n ...(profile.kind === 'mcp' ? [renderCapabilitiesLine(profile)] : []),\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","/*\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\n/** What kind of candidate this queue entry is for. Absent on an entry means 'skill' (pre-v0.5 entries stay valid). */\nexport type ArtifactType = 'skill' | 'mcp';\n\nexport type QueueStatus = 'pending' | 'ingested' | 'dismissed';\n\n/**\n * Statically-extracted MCP server capability profile (v0.6) — tool/resource/prompt names\n * and transport hints parsed from a quarantined MCP candidate's package.json, manifest, and\n * source text (NEVER by executing the server). Only present on `artifactType: 'mcp'` entries;\n * absent means the entry predates v0.6 or profiling couldn't determine anything.\n */\nexport interface McpCapabilities {\n tools: string[];\n resources: string[];\n prompts: string[];\n /** Best-effort transport hint (e.g. 'stdio', 'http'), when determinable statically. */\n transport?: string;\n /** How confidently `tools`/`resources`/`prompts` were determined from static analysis alone. */\n declaredConfidence: 'high' | 'partial' | 'none';\n}\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 /** 'skill' | 'mcp'; optional — absent means 'skill' (pre-v0.5 entries don't have this field). */\n artifactType?: ArtifactType;\n /** Statically-extracted MCP capability profile; optional — absent means pre-v0.6 or undetermined. */\n capabilities?: McpCapabilities;\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, detectMcpConfigFiles, type Agent, type DetectedMcpConfig, 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\ninterface DetectedMcpTarget extends DetectedMcpConfig {\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\n/**\n * Probes every known agent's `mcpConfig` locations (see src/agents.ts) and returns every MCP\n * config file that exists on disk. Unlike `detectAgents`, an agent can contribute more than one\n * hit (project and global config can both exist and both matter as promotion targets).\n */\nfunction detectMcpTargets(cwd: string, home: string): DetectedMcpTarget[] {\n const found: DetectedMcpTarget[] = [];\n for (const agent of AGENTS) {\n for (const hit of detectMcpConfigFiles(agent, cwd, home)) {\n found.push({ agent, ...hit });\n }\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 printMcpDetectionResults(detectedMcp: DetectedMcpTarget[]): void {\n if (detectedMcp.length === 0) return;\n console.log('\\n[skill-forge] init — MCP config detection');\n for (const d of detectedMcp) {\n console.log(` ${d.agent.displayName} (${d.agent.id}) — ${d.scope}: ${d.path}`);\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 selectedMcp: DetectedMcpTarget[] = [],\n): Config {\n const existing = loadConfig();\n return {\n ...existing,\n // Skill-root selections are only overwritten when this run actually offered a choice —\n // an mcp-only run (no skill roots detected) must not blank out an existing skillsRoots/agents.\n skillsRoots: selected.length > 0 ? selected.map((s) => s.root) : existing.skillsRoots,\n agents: selected.length > 0 ? selected.map((s) => ({ id: s.agent.id, skillsRoot: s.root })) : existing.agents,\n defaultTarget: defaultTargetEntry?.root ?? existing.defaultTarget,\n handoffCommand: handoffAgent ? handoffTemplateFor(handoffAgent) : existing.handoffCommand,\n mcpTargets: selectedMcp.length > 0 ? selectedMcp.map((m) => m.path) : existing.mcpTargets,\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 if (config.mcpTargets?.length) console.log(` mcp targets: ${config.mcpTargets.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\n/** Same UX as `promptMultiSelect`, offering detected MCP config files as `mcpTargets` candidates. */\nasync function promptMcpMultiSelect(rl: Interface, detectedMcp: DetectedMcpTarget[]): Promise<DetectedMcpTarget[]> {\n const chosen = new Set(detectedMcp.map((_, i) => i)); // all detected mcp config files pre-selected\n for (;;) {\n console.log('\\nMCP targets (all detected config files pre-selected — type a number to toggle, Enter to confirm):');\n detectedMcp.forEach((d, i) => {\n const mark = chosen.has(i) ? '[x]' : '[ ]';\n console.log(` ${mark} ${i + 1}. ${d.agent.displayName} (${d.scope}) — ${d.path}`);\n });\n const answer = (await rl.question('> ')).trim();\n if (answer === '') break;\n const idx = parseChoice(answer, detectedMcp.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 detectedMcp.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, an\n * optional handoff agent, and which detected MCP config files become `mcpTargets`, then writes\n * 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 const detectedMcp = detectMcpTargets(cwd, home);\n\n if (options.list) {\n printDetectionResults(detected);\n printMcpDetectionResults(detectedMcp);\n return;\n }\n\n if (detected.length === 0 && detectedMcp.length === 0) {\n console.log('[skill-forge] No known agent skill directories or MCP config files 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, detectedMcp);\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 printMcpDetectionResults(detectedMcp);\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const selected = detected.length > 0 ? await promptMultiSelect(rl, detected) : [];\n const selectedMcp = detectedMcp.length > 0 ? await promptMcpMultiSelect(rl, detectedMcp) : [];\n if (selected.length === 0 && selectedMcp.length === 0) {\n console.log('[skill-forge] No targets selected — aborting, nothing written.');\n return;\n }\n const target = selected.length > 0 ? await promptDefaultTarget(rl, selected) : undefined;\n const handoffAgent = detected.length > 0 ? await promptHandoffAgent(rl, detected) : undefined;\n const config = buildConfig(selected, target, handoffAgent, selectedMcp);\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 { statSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join, resolve } from 'node:path';\nimport { HANDOFF_INSTRUCTION } from './handoff.js';\nimport { isDir } from './util.js';\n\n/** True if `p` exists and is a regular file (false on any stat error). Mirrors util.ts's isDir. */\nfunction isFile(p: string): boolean {\n try {\n return statSync(p).isFile();\n } catch {\n return false;\n }\n}\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 verified against the vercel-labs/skills CLI's own\n * source of truth, `src/agents.ts`\n * (raw.githubusercontent.com/vercel-labs/skills/main/src/agents.ts, cross-checked via the\n * GitHub contents API 2026-07-11) — not just its README, which can drift from the code. All 73\n * entries below were diffed programmatically against that file and matched exactly (id,\n * displayName, skillsDir, and the default/no-env-override form of globalSkillsDir). A handful of\n * agents resolve their global root via an overridable env var upstream (e.g. Amp/OpenCode/Goose/\n * Devin/Universal/Replit via `XDG_CONFIG_HOME`, Codex via `CODEX_HOME`, Claude Code via\n * `CLAUDE_CONFIG_DIR`) — the tilde path here is upstream's default when that var is unset.\n * Entries marked `verified: false` could not be confirmed against that source and should be\n * treated as best-effort, never invented.\n */\n/**\n * Where an agent reads its MCP server configuration from, if known (added in v0.5 for MCP\n * gating). Each entry is verified against that agent's official docs/source — see the citation\n * comment above the entry in AGENTS below — and `mcpConfig` is left absent entirely on agents we\n * couldn't confirm a path for; never invented.\n */\nexport interface McpConfigLocation {\n /** Project-relative MCP config file path (relative to cwd), e.g. `.mcp.json`. */\n project?: string;\n /** User-home MCP config file path, `~`-relative, e.g. `~/.claude.json`. */\n global?: string;\n /**\n * File format when it isn't plain JSON with a top-level `mcpServers` key. v0.5's MCP promote\n * only WRITES json-shaped targets (see the v0.5 contract) — `format: 'toml'` targets are\n * detect/report-only.\n */\n format?: 'toml';\n /** True once the path(s) above were confirmed against this agent's official docs (cited in a comment on this entry). */\n verified: boolean;\n}\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 /** Where this agent reads its MCP server config from. Absent when unverified — see McpConfigLocation. */\n mcpConfig?: McpConfigLocation;\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 mcpConfig?: McpConfigLocation;\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 mcpConfig: spec.mcpConfig,\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/**\n * Resolves the absolute filesystem path for one of an agent's MCP config files, without probing\n * for existence. Returns undefined when the agent has no `mcpConfig`, or no path for that scope.\n * `cwd`/`home` default to the real process cwd / home dir.\n */\nexport function resolveMcpConfigPath(\n agent: Agent,\n scope: 'project' | 'global',\n cwd: string = process.cwd(),\n home: string = homedir(),\n): string | undefined {\n if (!agent.mcpConfig) return undefined;\n return scope === 'project' ? projectPath(agent.mcpConfig.project, cwd) : globalPath(agent.mcpConfig.global, home);\n}\n\n/** An MCP config file that exists on disk for an agent: which scope it is, and its absolute path. */\nexport interface DetectedMcpConfig {\n scope: 'project' | 'global';\n path: string;\n}\n\n/**\n * Probes an agent's MCP config file locations on disk and returns every one that exists as a\n * file. Unlike `detectAgentRoot` (which returns only the first matching skills root), both\n * project and global MCP config files can legitimately coexist and both matter as promotion\n * targets — e.g. a project `.mcp.json` and a user-scope `~/.claude.json` — so this returns all\n * matches, not just the first. `cwd`/`home` default to the real process cwd / home dir.\n */\nexport function detectMcpConfigFiles(\n agent: Agent,\n cwd: string = process.cwd(),\n home: string = homedir(),\n): DetectedMcpConfig[] {\n if (!agent.mcpConfig) return [];\n const found: DetectedMcpConfig[] = [];\n for (const scope of ['project', 'global'] as const) {\n const path = resolveMcpConfigPath(agent, scope, cwd, home);\n if (path && isFile(path)) found.push({ scope, path });\n }\n return found;\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 // `claude -p \"<prompt>\"` runs one non-interactive turn and exits. Verified 2026-07-11:\n // https://code.claude.com/docs/en/headless\n defaultHandoffTemplate: ['claude', '-p', HANDOFF_INSTRUCTION],\n // Project scope writes to `.mcp.json` at the project root (shared via version control).\n // User scope writes to a top-level `mcpServers` key in `~/.claude.json` (not exclusively\n // an MCP file — it's Claude Code's broader state file — but it's the correct read/write\n // target for user-scoped servers). Verified 2026-07-12: https://code.claude.com/docs/en/mcp\n mcpConfig: { project: '.mcp.json', global: '~/.claude.json', verified: true },\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 // `codex exec \"<prompt>\"` runs one non-interactive task and prints the final message.\n // Verified 2026-07-11: https://developers.openai.com/codex/noninteractive\n defaultHandoffTemplate: ['codex', 'exec', HANDOFF_INSTRUCTION],\n // TOML, not JSON: `[mcp_servers.<name>]` tables. User config at `~/.codex/config.toml`;\n // project config at `.codex/config.toml` (only honored for trusted projects). Verified\n // 2026-07-12: https://learn.chatgpt.com/docs/extend/mcp?surface=cli — detect/report-only\n // per the v0.5 contract (promote only writes JSON targets).\n mcpConfig: { project: '.codex/config.toml', global: '~/.codex/config.toml', format: 'toml', verified: true },\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 {\n id: 'cursor',\n displayName: 'Cursor',\n projectRoot: '.agents/skills',\n globalRoot: '~/.cursor/skills',\n // Both scopes use a top-level `mcpServers` key. Verified 2026-07-12:\n // https://cursor.com/docs/context/mcp (\"Create `.cursor/mcp.json` in your project ...\";\n // \"Create `~/.cursor/mcp.json` in your home directory ...\").\n mcpConfig: { project: '.cursor/mcp.json', global: '~/.cursor/mcp.json', verified: true },\n },\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 // `gemini -p \"<prompt>\"` executes a single turn headlessly and exits. Verified 2026-07-11:\n // https://google-gemini.github.io/gemini-cli/docs/cli/headless.html\n defaultHandoffTemplate: ['gemini', '-p', HANDOFF_INSTRUCTION],\n // `mcpServers` key inside `settings.json` (shared with other CLI settings, not an\n // MCP-exclusive file, but the correct read/write target). User config at\n // `~/.gemini/settings.json`, project config at `.gemini/settings.json`. Verified\n // 2026-07-12: https://google-gemini.github.io/gemini-cli/docs/tools/mcp-server.html\n mcpConfig: { project: '.gemini/settings.json', global: '~/.gemini/settings.json', verified: true },\n },\n {\n id: 'github-copilot',\n displayName: 'GitHub Copilot',\n projectRoot: '.agents/skills',\n globalRoot: '~/.copilot/skills',\n // Copilot CLI (matches the `~/.copilot` skills convention above, not the VS Code\n // extension — which uses a different `servers`-keyed `.vscode/mcp.json` and isn't covered\n // by this entry). User config `~/.copilot/mcp-config.json` (`mcpServers` key), repo-level\n // config `.github/mcp.json`. Verified 2026-07-12:\n // https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers\n mcpConfig: { project: '.github/mcp.json', global: '~/.copilot/mcp-config.json', verified: true },\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 // `opencode run \"<prompt>\"` runs one non-interactive prompt outside the TUI.\n // Verified 2026-07-11: https://opencode.ai/docs/cli/\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 {\n id: 'windsurf',\n displayName: 'Windsurf',\n projectRoot: '.windsurf/skills',\n globalRoot: '~/.codeium/windsurf/skills',\n // `mcpServers` key; global only — no project-scoped MCP config is documented. Verified\n // 2026-07-12: https://docs.windsurf.com/windsurf/cascade/mcp\n mcpConfig: { global: '~/.codeium/windsurf/mcp_config.json', verified: true },\n },\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, type ArtifactType } from '../install/resolve.js';\nimport { quarantine } from '../install/quarantine.js';\nimport { reject } from '../install/promote.js';\nimport { printGateOutput, runGate, resolveMcpCandidatePath } from './gatePipeline.js';\nimport { maybeOfferInit } from './init.js';\n\nexport interface ScanOptions {\n json?: boolean;\n /** Gate as a skill (default) or an MCP server — v0.5. */\n artifact?: ArtifactType;\n}\n\n/**\n * `skill-forge scan <source>` — run the safety gate against a skill (or, with\n * `--artifact mcp`, an MCP server — v0.5) without installing it into a target\n * skills root/MCP config: quarantine-install for inspection, gate + report,\n * then always clean up the quarantine sandbox regardless of verdict. Exits\n * nonzero when the safety verdict is \"block\". `--json` prints the same\n * 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 artifact: ArtifactType = options.artifact ?? 'skill';\n // Same resolution chain as `add`, so scan and add rank overlap against the same root.\n // Only required for skill artifacts — MCP overlap ranks against config.mcpTargets instead.\n const skillsRoot = config.defaultTarget ?? config.skillsRoots[0];\n if (artifact === 'skill' && !skillsRoot) {\n throw new Error('no skills root configured; set skillsRoots in the config');\n }\n\n const resolved = resolveSource(source, artifact);\n const entry = await quarantine(resolved, config.quarantineDir);\n\n try {\n let candidatePath: string;\n if (artifact === 'mcp') {\n candidatePath = resolveMcpCandidatePath(entry);\n } else {\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 candidatePath = entry.skillDirs[0];\n }\n\n const { safety, gateResult } = await runGate(candidatePath, skillsRoot ?? '', {\n strictness: config.strictness,\n artifact,\n mcpTargets: config.mcpTargets ?? [],\n });\n\n if (artifact === 'mcp' && gateResult.profile.kind === 'mcp' && gateResult.profile.serverCount > 1) {\n console.error(\n `[skill-forge] ${gateResult.profile.serverCount} MCP servers found in ${source}; scanning the first — ` +\n gateResult.profile.servers.join(', ')\n );\n }\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","join","existsSync","homedir","join","existsSync","existsSync","mkdirSync","join","join","mkdirSync","existsSync","walk","reject","cpSync","existsSync","mkdirSync","basename","join","existsSync","mkdirSync","readFileSync","writeFileSync","existsSync","readFileSync","writeFileSync","mkdirSync","readFileSync","pkg","existsSync","statSync","writeFileSync","join","readdirSync","join","existsSync","readFileSync","readdirSync","rmSync","statSync","basename","dirname","extname","join","resolve","existsSync","readFileSync","extname","join","readFileSync","readdirSync","statSync","join","SKIP_DIRS","readFileSync","pkg","join","existsSync","extname","resolve","existsSync","statSync","join","MCP_SDK_DEP","MCP_KEYWORD","MCP_BIN_PREFIX","readFileSync","pkg","basename","extname","dirname","rmSync","readdirSync","m","statSync","join","statSync","join","existsSync","readFileSync","readdirSync","basename","dirname","join","resolve","readFileSync","basename","dirname","existsSync","readdirSync","join","resolve","statSync","basename","extname","basename","extname","statSync","existsSync","readFileSync","resolve","resolve","existsSync","readFileSync","round3","score","status","readdirSync","join","randomUUID","existsSync","mkdirSync","readFileSync","writeFileSync","join","join","existsSync","readFileSync","mkdirSync","writeFileSync","randomUUID","existsSync","dirname","join","spawn","existsSync","homedir","statSync","homedir","join","resolve","statSync","resolve","join","homedir","homedir","existsSync","join","createInterface","join","join","require"]}
|