@rhize/skill-forge 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +3 -0
- package/README.md +215 -4
- package/dist/cli.js +2226 -306
- package/dist/cli.js.map +1 -1
- package/dist/curation-prompt.md +121 -0
- package/dist/ingest-prompt.md +107 -15
- package/package.json +3 -2
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/gate/scanShared.ts","../src/install/mcpCandidate.ts","../src/provenance.ts","../src/commands/gatePipeline.ts","../src/gate/profile.ts","../src/gate/mcpCapabilities.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/decision.ts","../src/commands/scan.ts","../src/commands/list.ts","../src/commands/status.ts","../src/evolve.ts","../src/commands/evolve.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 { evolveCommand } from './commands/evolve.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\n/** Commander option parser for `--lookback-hours` — throws UserError (caught below) on a non-finite, empty, or negative number. */\nfunction parseLookbackHours(value: string): number {\n const n = Number(value);\n if (value.trim() === '' || !Number.isFinite(n) || n < 0) {\n throw new UserError(`--lookback-hours must be a non-negative number (got \"${value}\")`);\n }\n return n;\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('evolve <target-skill-dir>')\n .description('Self-evolve a skill via SkillOpt-Sleep, re-gate the proposal, then promote/hold/reject (Pro)')\n .option('--project <dir>', 'project dir passed to SkillOpt-Sleep as --project (defaults to cwd)')\n .option('--dry-run', 'SkillOpt-Sleep dry-run: report only, nothing staged')\n .option('--backend <name>', 'SkillOpt-Sleep backend (default: mock, offline — a real backend requires --yes or interactive confirmation)')\n .option('--lookback-hours <n>', 'hours of session history for SkillOpt-Sleep to harvest', parseLookbackHours)\n .option('-y, --yes', 'skip interactive prompts; honor the re-gate verdict automatically')\n .option('--json', 'print the re-gate result as JSON instead of the terminal report')\n .option('--force', 'allow re-adopting a staging dir whose proposed content already matches the live skill')\n .action(evolveCommand);\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 { 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 { formatSafetyFinding } from '../gate/scanShared.js';\nimport { suggestVerbCode } from '../gate/overlap.js';\nimport { appendQueueEntry } from '../queue.js';\nimport { runHandoff } from '../handoff.js';\nimport { maybeOfferInit } from './init.js';\nimport { logOutcome, promptDecision, yesDecision } from '../decision.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\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 =\n options.yes || options.json ? yesDecision(safety.verdict) : await promptDecision(safety.verdict, 'this skill');\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(formatSafetyFinding),\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 =\n options.yes || options.json ? yesDecision(safety.verdict) : await promptDecision(safety.verdict, 'this skill');\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 // packageName (v0.7) — the candidate's own scanned package.json name, so\n // deriveServerEntry can auto-pin an unpinned npx spec in candidateEntry\n // only when it provably matches the package that was actually scanned.\n packageName: detail.packageName,\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.autoPinned) {\n logOutcome(`[skill-forge] pinned launch spec ${result.autoPinned.from} -> ${result.autoPinned.to}`, options.json);\n }\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(formatSafetyFinding),\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","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\n/** Real argv-array subprocess runner (no shell) — the default ExecFn, and shared by evolve.ts's skillopt-sleep invocations. */\nexport function 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';\nimport { collectNpxPackageSpecs, isNpxCommand, isPinnedPackageSpec, packageSpecBase } from '../gate/scanShared.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>` — used on the\n * npx-fallback path (no `candidateEntry`; see `deriveServerEntry` for the\n * refusal behavior when both are absent) and, when it matches\n * `packageName`, to auto-pin an unpinned `npx` spec found in a documented\n * `candidateEntry` (v0.7).\n */\n version?: string;\n /**\n * The package name from the candidate's own scanned, self-declared\n * `package.json` (v0.7, from `install/mcpCandidate.ts`'s sidecar read) —\n * for an npm-sourced candidate this IS the registry artifact being\n * profiled, but for a git/local source it's just whatever the candidate's\n * own `package.json` claims, unverified against any registry. Only when a\n * `candidateEntry`'s unpinned `npx` launch spec's package matches this\n * name is `version` above trusted to auto-pin it — a spec for some other\n * package is never pinned to a version scanned for a different package.\n */\n packageName?: 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 for command/args when present instead of the\n * npx-derived default. Any `env` values on it are always stripped before use.\n * An unpinned `npx` launch spec on this entry is auto-pinned when it matches\n * the candidate's own scanned (self-declared) package (see\n * `packageName`/`version` above), otherwise promotion is refused — see\n * `deriveServerEntry` (v0.7).\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 /** Set when an unpinned `npx` spec in a documented `candidateEntry` was auto-pinned (v0.7) — the exact spec text before/after. */\n autoPinned?: { from: string; to: 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, autoPinned } = 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, autoPinned };\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 *\n * A documented `candidateEntry` is used verbatim EXCEPT when its own launch\n * command is `npx` and it carries an unpinned package spec (v0.7 —\n * previously written through unchanged, only ever flagged as a MEDIUM\n * safety warning). `gate/scanShared.ts`'s `collectNpxPackageSpecs()` parses\n * the args the way npx itself does — including every repeatable `-p`/\n * `--package` value, not just the first non-flag token (a naive \"first\n * non-flag arg\" heuristic misses that `-p`/`--package` consumes its value\n * even when that value doesn't start with `-`, which real npx/npm-exec\n * still installs — see that function's own doc comment):\n * - **no specs at all, or every spec found is already pinned** → verbatim.\n * - **exactly one spec total, it's the positional launch spec (not a `-p`\n * value), and it's unpinned**: auto-pinned to `<packageName>@<version>`\n * when the spec's package matches the candidate's own scanned\n * (self-declared) `packageName` and a `version` was scanned for it\n * (`autoPinned` records the change); otherwise refused.\n * - **anything else with an unpinned spec** (an unpinned `-p` value, or\n * more than one spec at all) — refused, naming every unpinned spec.\n * Under-checking a more complex shape must fail closed: there is no\n * auto-pin for a `-p` dependency (it isn't \"the launched package,\" even\n * if it happens to match) or for a multi-spec shape.\n * For an npm-sourced candidate, `packageName`/`version` come from the\n * registry artifact itself; for a git/local source they're only what the\n * candidate's own `package.json` self-declares. Auto-pinning there reduces\n * moving-tag drift (the spec can't silently resolve to something different\n * later) — it does not authenticate registry ownership of that name.\n * There is no override flag for either refusal. Both still run before any\n * read/backup (see promoteMcp() above), so a refusal leaves nothing behind.\n */\nfunction deriveServerEntry(profile: McpServerProfile): {\n command: string;\n args: string[];\n envVarsToFill: string[];\n autoPinned?: { from: string; to: 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 const { command, args = [] } = profile.candidateEntry;\n\n if (isNpxCommand(command)) {\n const { packageFlagSpecs, positionalSpec } = collectNpxPackageSpecs(args);\n const allSpecs = positionalSpec ? [...packageFlagSpecs, positionalSpec] : packageFlagSpecs;\n const unpinned = allSpecs.filter((spec) => !isPinnedPackageSpec(spec));\n\n if (unpinned.length > 0) {\n const isSoleUnpinnedPositional =\n packageFlagSpecs.length === 0 && allSpecs.length === 1 && positionalSpec === unpinned[0];\n\n if (isSoleUnpinnedPositional) {\n const spec = unpinned[0];\n const specPackage = packageSpecBase(spec);\n if (profile.packageName && specPackage === profile.packageName && profile.version) {\n const to = `${profile.packageName}@${profile.version}`;\n const specIdx = args.indexOf(spec);\n const pinnedArgs = [...args];\n pinnedArgs[specIdx] = to;\n return {\n command,\n args: pinnedArgs,\n envVarsToFill: Array.from(envNames),\n autoPinned: { from: spec, to },\n };\n }\n\n const reason =\n profile.packageName && specPackage !== profile.packageName\n ? `the launch spec names package \"${specPackage}\", but the candidate's own package.json is \"${profile.packageName}\" — ` +\n 'pinning it to a version scanned for a different package would be unsafe'\n : \"no version was found in the candidate's own package.json to pin it to\";\n throw new UserError(\n `cannot promote MCP server \"${profile.name}\": its documented launch command \"npx ${spec}\" is unpinned ` +\n `and can't be safely auto-pinned (${reason}). Pin the spec to an exact version in the candidate's ` +\n `config (e.g. \\`${specPackage}@<version>\\`) and try again — there is no override for this.`\n );\n }\n\n throw new UserError(\n `cannot promote MCP server \"${profile.name}\": its documented launch command \"npx\" has unpinned package ` +\n `spec(s) — ${unpinned.map((s) => `\"${s}\"`).join(', ')} — and this shape (a -p/--package dependency, ` +\n \"or more than one spec) can't be safely auto-pinned. Pin every spec to an exact version in the \" +\n \"candidate's config and try again — there is no override for this.\"\n );\n }\n }\n\n return {\n command,\n 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, 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\n/** Formats a single finding as `SEVERITY [rule] file: detail` — shared by the pending-queue `safetyFindings` formatters in commands/add.ts and evolve.ts. */\nexport function formatSafetyFinding(f: SafetyFinding): string {\n return `${f.severity} [${f.rule}] ${f.file}: ${f.detail}`;\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 * Shared by `gate/mcpSafety.ts`'s scan and `install/promoteMcp.ts`'s\n * documented-candidateEntry version-pin enforcement (v0.7).\n */\nexport function 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. Shared by `gate/mcpSafety.ts` and `install/promoteMcp.ts` (v0.7). */\nexport function 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. Shared by `gate/mcpSafety.ts` and `install/promoteMcp.ts` (v0.7). */\nexport function 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\n/**\n * npx's own value-consuming CLI flags — verified against the installed npm\n * 12.0.1's `node_modules/npm/bin/npx-cli.js`. That file hardcodes an `opts`\n * set of flags that ALWAYS consume the next token (or an attached\n * `=value`) as their value rather than letting it become the launched\n * command: `package`/`p`, `cache`, `userconfig`, `call`/`c`, `shell`,\n * `node-arg`/`n`, and the deprecated-but-still-parsed `npm`. `registry` is\n * added here deliberately even though it is NOT in that hardcoded set:\n * npx-cli.js's parser has a generic fallback (any unrecognized flag that\n * isn't a known boolean switch consumes the next token as a value unless\n * that token itself looks like a flag) which makes `--registry <url>`\n * behave the same way in practice — verified empirically. This is a\n * DELIBERATE, DOCUMENTED SIMPLIFICATION, not a full reimplementation of\n * npx's parser: that fallback rule also consults npm's complete,\n * version-dependent set of boolean switches (`@npmcli/config`'s\n * `definitions`), which this stdlib-only tool does not bundle. Every other\n * `-`-prefixed token is treated as a boolean flag.\n */\nconst NPX_VALUE_FLAGS = new Set(['p', 'package', 'c', 'call', 'cache', 'userconfig', 'shell', 'n', 'node-arg', 'npm', 'registry']);\n\n/** Of NPX_VALUE_FLAGS, only `-p`/`--package`'s value(s) are themselves package specs — the rest (cache paths, config files, shell binaries, registry URLs, ...) are not. */\nconst NPX_PACKAGE_SPEC_FLAGS = new Set(['p', 'package']);\n\n/**\n * Every `-p`/`--package` value found (repeatable — npx installs ALL of\n * them, in addition to the launched command, before running it), and the\n * positional launch spec if one was found — kept separate rather than a\n * flat list because policy (`install/promoteMcp.ts`) treats them\n * differently: only the positional (the package actually being launched)\n * is ever eligible for auto-pinning; a `-p` dependency, even alone and\n * even if it happens to match the candidate's own package, is not.\n */\nexport interface NpxSpecCollection {\n packageFlagSpecs: string[];\n positionalSpec?: string;\n}\n\n/**\n * Collects every package spec that `npx -y <args>` would actually resolve\n * and install: every repeated `-p`/`--package` value, plus the positional\n * launch spec — the first token that's neither a recognized flag nor a\n * value a recognized flag consumed. Collection stops at that positional\n * token (mirroring npx-cli.js: once the positional is found, `--` is\n * spliced in front of it and everything after belongs to the *launched*\n * command's own argv, not npx's argument parsing). See NPX_VALUE_FLAGS\n * above for the documented scope/limits of this model. Shared by\n * `gate/mcpSafety.ts`'s scan and `install/promoteMcp.ts`'s\n * documented-candidateEntry version-pin enforcement (v0.7).\n */\nexport function collectNpxPackageSpecs(args: string[]): NpxSpecCollection {\n const packageFlagSpecs: string[] = [];\n let positionalSpec: string | undefined;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === '--') break;\n\n if (arg.startsWith('-')) {\n const eqIdx = arg.indexOf('=');\n const key = (eqIdx === -1 ? arg : arg.slice(0, eqIdx)).replace(/^-+/, '');\n const attached = eqIdx === -1 ? undefined : arg.slice(eqIdx + 1);\n\n if (NPX_VALUE_FLAGS.has(key)) {\n let value = attached;\n if (value === undefined) {\n // No attached =value — the next token is consumed as this flag's\n // value, whatever it looks like (mirrors npx-cli.js's unconditional\n // consumption for a known value-flag).\n value = args[i + 1];\n i++;\n }\n if (NPX_PACKAGE_SPEC_FLAGS.has(key) && value !== undefined) {\n packageFlagSpecs.push(value);\n }\n continue;\n }\n\n // Unrecognized flag — treated as boolean (see NPX_VALUE_FLAGS's doc\n // comment for this simplification's documented limits).\n continue;\n }\n\n // First token that's neither a flag nor a consumed value — the\n // positional launch spec. Collect it and stop.\n positionalSpec = arg;\n break;\n }\n\n return { packageFlagSpecs, positionalSpec };\n}\n","import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\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 * The package `name` from the candidate's own sibling `package.json`\n * (v0.7), when one exists next to an explicit `.mcp.json`/`mcp.json`. Used\n * by `install/promoteMcp.ts` to auto-pin an unpinned `npx` launch spec in\n * a documented `candidateEntry` — only when the spec's package matches\n * this name is the candidate's own scanned `version` trusted for pinning.\n * On the package.json-profile branch, `name` above already comes from\n * this same file, so `packageName` is left unset there (redundant).\n */\n packageName?: string;\n}\n\ninterface ConfigEntry {\n command?: string;\n args?: string[];\n env?: Record<string, unknown>;\n}\n\ninterface PackageInfo {\n name?: string;\n version?: string;\n}\n\n/** Tolerant read of a package.json's `name`/`version` — never throws; returns {} if missing/malformed. */\nfunction readPackageInfo(pkgPath: string): PackageInfo {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as Record<string, unknown>;\n return {\n name: typeof pkg.name === 'string' ? pkg.name : undefined,\n version: typeof pkg.version === 'string' ? pkg.version : undefined,\n };\n } catch {\n return {};\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 *\n * On the explicit `.mcp.json`/`mcp.json` branch (v0.7), also tolerantly reads\n * a sibling `package.json` next to the config file for its `name`/`version`\n * — populating `version`/`packageName` there too — so `promoteMcp.ts` can\n * auto-pin an unpinned `npx` spec in a documented `candidateEntry` when it\n * provably matches the candidate's own scanned package.\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: readPackageInfo(profile.config).version };\n }\n\n const entry = readConfigEntry(profile.config, name);\n const sidecar = readPackageInfo(join(dirname(profile.config), 'package.json'));\n return {\n name,\n command: entry?.command,\n args: entry?.args,\n env: entry?.env,\n version: sidecar.version,\n packageName: sidecar.name,\n };\n}\n","/*\n * Rhize Commercial License — NOT covered by the MIT license that applies to\n * the rest of this repository. Production use of this module's functionality\n * requires a valid license key. See LICENSE and LICENSE-COMMERCIAL.\n * Copyright (c) 2026 Rhize Media\n */\nimport { appendFileSync, existsSync, statSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/**\n * Provenance ledger — direct TypeScript port of `record_provenance.py`'s\n * recording path (rhize-meta/skills/rhize-skill-forge/scripts). Appends a\n * structured entry to `<skillsRoot>/SOURCES.md`, the audit ledger of every\n * external-skill ingestion decision. Field names and formatting mirror the\n * Python source exactly so an existing SOURCES.md ledger stays consistent\n * regardless of which tool wrote which entry.\n */\n\nexport const FORGE_VERBS = ['DEFER', 'ABSORB', 'FORK', 'REJECT', 'WATCH'] as const;\nexport type ForgeVerb = (typeof FORGE_VERBS)[number];\n\nexport interface ProvenanceEntry {\n name: string;\n source: string;\n verb: ForgeVerb;\n version?: string;\n license?: string;\n target?: string;\n took?: string;\n verified?: string;\n driftCheck?: string;\n notes?: string;\n}\n\nconst LEDGER_HEADER = '# Skill Forge — Provenance Ledger\\n\\nOne entry per external-skill ingestion decision.\\n';\n\nfunction todayLocalIsoDate(): string {\n const d = new Date();\n const year = d.getFullYear();\n const month = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${year}-${month}-${day}`;\n}\n\n/**\n * Appends a provenance entry to `<skillsRoot>/SOURCES.md`, creating the\n * ledger with its header on first write. Throws if `verb` isn't a valid\n * forge verb or `skillsRoot` isn't an existing directory — mirrors the\n * Python script's fail-loud `fail()` calls. Returns the ledger path.\n */\nexport function appendProvenance(entry: ProvenanceEntry, skillsRoot: string): string {\n if (!FORGE_VERBS.includes(entry.verb)) {\n throw new Error(`verb must be one of ${FORGE_VERBS.join(', ')}`);\n }\n if (!existsSync(skillsRoot) || !statSync(skillsRoot).isDirectory()) {\n throw new Error(`skills-root not a directory: ${skillsRoot}`);\n }\n\n const today = todayLocalIsoDate();\n const driftCheck = entry.driftCheck ?? `# define how to detect upstream change for ${entry.name}`;\n\n const block =\n `\\n## ${entry.name} — ${today}\\n` +\n `- **Source:** ${entry.source}\\n` +\n `- **Upstream ref:** ${entry.version ?? 'n/a'}\\n` +\n `- **License:** ${entry.license ?? 'UNKNOWN'}\\n` +\n `- **Verb:** ${entry.verb}\\n` +\n `- **Target:** ${entry.target ?? 'n/a'}\\n` +\n `- **Took:** ${entry.took ?? 'nothing'}\\n` +\n `- **Verified:** ${entry.verified ?? 'n/a'}\\n` +\n `- **Drift check:** \\`${driftCheck}\\`\\n` +\n `- **Notes:** ${entry.notes ?? ''}\\n`;\n\n const ledgerPath = join(skillsRoot, 'SOURCES.md');\n if (!existsSync(ledgerPath)) {\n writeFileSync(ledgerPath, LEDGER_HEADER, 'utf-8');\n }\n appendFileSync(ledgerPath, block, 'utf-8');\n\n return ledgerPath;\n}\n","import { readdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { DEFAULT_STRICTNESS, type Strictness } from '../config.js';\nimport { asProfile, 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 { SAFETY_VERDICT_MAP, mapSafetyFindings, type GateResult } from '../report/render.js';\n\nexport { printGateOutput } 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\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/** 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: mapSafetyFindings(safety.findings),\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/**\n * Narrows an already-resolved `Profile` to a `SkillProfile`, throwing a\n * descriptive error otherwise. For a caller that has already profiled a\n * path (e.g. via `profileSkill()`) and needs the skill-specific shape to\n * proceed. `context` is appended to the error message (e.g. `at ${path}`,\n * `for the evolved proposal`). Shared by `commands/gatePipeline.ts`'s\n * `asProfile()` (its skill branch) and `evolve.ts`'s re-gate step.\n */\nexport function asSkillProfile(profile: Profile, context: string): SkillProfile {\n if (profile.kind !== 'skill') {\n throw new Error(`expected a skill candidate ${context}, found a ${profile.kind}`);\n }\n return profile;\n}\n\n/**\n * Profiles `candidatePath` and asserts the result matches `artifact`\n * ('mcp' expects kind 'mcp'; 'skill'/absent expects kind 'skill'),\n * narrowing the return type accordingly. Throws a descriptive error\n * otherwise, rather than guessing. Moved here (v0.7 simplify pass) from\n * `commands/gatePipeline.ts`, which still owns calling it as part of the\n * gate pipeline; `evolve.ts` also profiles a skill through this same\n * skill-branch logic via `asSkillProfile()` above.\n */\nexport function asProfile(candidatePath: string, artifact: ArtifactType): Profile {\n const profile = profileSkill(candidatePath);\n if (artifact === 'mcp') {\n if (profile.kind !== 'mcp') {\n throw new Error(`expected an MCP candidate at ${candidatePath} (--artifact mcp), found a ${profile.kind}`);\n }\n return profile;\n }\n return asSkillProfile(profile, `at ${candidatePath}`);\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 { 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 {\n collectNpxPackageSpecs,\n isNpxCommand,\n isPinnedPackageSpec,\n packageSpecBase,\n readTextFile,\n verdictFor,\n walk,\n} 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\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 // -y/--yes present is still the gate for the rule itself (without it,\n // npx would prompt before installing anything — a different risk\n // profile) — but once gated, every unpinned spec is surfaced, not just\n // the first: collectNpxPackageSpecs() parses the way npx itself does,\n // including every repeatable -p/--package value, not just the first\n // non-flag token (see its own doc comment in scanShared.ts).\n const flagIdx = args.findIndex((a) => a === '-y' || a === '--yes');\n if (flagIdx !== -1) {\n const { packageFlagSpecs, positionalSpec } = collectNpxPackageSpecs(args);\n const allSpecs = positionalSpec ? [...packageFlagSpecs, positionalSpec] : packageFlagSpecs;\n for (const pkgSpec of allSpecs) {\n if (!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\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 type { SafetyFinding as GateSafetyFinding, Verdict as GateVerdict } from '../gate/safety.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\n/** Maps gate/safety.ts's pass|warn|block verdict to this module's ALLOW|CAUTION|BLOCK — shared by commands/gatePipeline.ts's runGate() and evolve.ts's re-gate step. */\nexport const SAFETY_VERDICT_MAP: Record<GateVerdict, SafetyVerdict> = {\n pass: 'ALLOW',\n warn: 'CAUTION',\n block: 'BLOCK',\n};\n\n/** Maps gate/safety.ts's {severity, rule, file, detail} findings into this module's {severity, rule, message} shape — shared by the same two call sites as SAFETY_VERDICT_MAP. */\nexport function mapSafetyFindings(findings: GateSafetyFinding[]): SafetyFinding[] {\n return findings.map((f) => ({ severity: f.severity, rule: f.rule, message: `${f.file}: ${f.detail}` }));\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/**\n * Prints a gate result the same way for `add`/`scan`/`evolve`: 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). Moved here (v0.7 simplify pass) from\n * `commands/gatePipeline.ts`, which re-exports it so `add.ts`/`scan.ts`'s\n * existing import path keeps working.\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","/*\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\n/** How this entry was produced, when it isn't a normal `add`-sourced external install. Absent means a normal entry. 'evolve' (v0.7): self-evolved via `skill-forge evolve` (SkillOpt-Sleep) and already re-gated at adopt time. */\nexport type QueueEntryOrigin = 'evolve';\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 /** How this entry was produced; optional — absent means a normal `add`-sourced entry. See QueueEntryOrigin. */\n origin?: QueueEntryOrigin;\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 { createInterface } from 'node:readline/promises';\nimport type { Verdict } from './gate/safety.js';\n\n/**\n * Shared promote/hold/reject decision helpers — the same decision flow\n * `add`/`scan` use (`docs/gate-policy.md`'s \"Decision flow\" section),\n * reused by `evolve.ts` for its re-gate step. Moved here (v0.7 simplify\n * pass) from `commands/add.ts`, which imports these instead of defining\n * its own copies.\n */\n\nexport type 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. */\nexport function logOutcome(message: string, json: boolean | undefined): void {\n if (json) console.error(message);\n else console.log(message);\n}\n\n/** --yes/--json honors the verdict: a block is rejected (nonzero exit); pass/warn is promoted. */\nexport function yesDecision(verdict: Verdict): Decision {\n return verdict === 'block' ? 'reject' : 'promote';\n}\n\n/**\n * Prompts interactively for promote/hold/reject, defaulting the suggested\n * answer to the verdict. `subject` names what's being decided in the\n * prompt text (e.g. `\"this skill\"`, `\"this evolution proposal\"`) so each\n * caller keeps its own exact prompt wording.\n */\nexport async function promptDecision(verdict: Verdict, subject: string): 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 ${subject}? [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 { 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","/*\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 { createInterface } from 'node:readline/promises';\nimport { createHash } from 'node:crypto';\nimport { cpSync, existsSync, lstatSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { basename, dirname, join, resolve as resolvePath, sep } from 'node:path';\nimport { UserError } from './errors.js';\nimport { loadConfig } from './config.js';\nimport { proFeatureStatus } from './license.js';\nimport { profileSkill, asSkillProfile } from './gate/profile.js';\nimport type { SkillProfile } from './gate/profile.js';\nimport { scanSafety } from './gate/safety.js';\nimport type { SafetyResult } from './gate/safety.js';\nimport { formatSafetyFinding } from './gate/scanShared.js';\nimport { SAFETY_VERDICT_MAP, mapSafetyFindings, printGateOutput } from './report/render.js';\nimport type { GateResult } from './report/render.js';\nimport { logOutcome, promptDecision, yesDecision } from './decision.js';\nimport { appendProvenance } from './provenance.js';\nimport { appendQueueEntry } from './queue.js';\nimport { defaultExec } from './install/quarantine.js';\nimport type { ExecFn } from './install/quarantine.js';\n\n/**\n * `skill-forge evolve` (v0.7, Pro) — orchestrates microsoft/SkillOpt's\n * `skillopt-sleep` CLI to propose a self-evolution of an already-installed\n * skill (harvest recent sessions -> generate a replacement `SKILL.md`/\n * `CLAUDE.md` -> stage it, never auto-adopted), then re-gates the STAGED\n * PROPOSAL — never the live skill — through the same static safety ruleset\n * `add`/`scan` use, before a promote/hold/reject decision.\n *\n * `skillopt-sleep` is never installed by this module (detect-don't-install:\n * absence prints `pip install skillopt` guidance and refuses) and every\n * invocation of it goes through argv arrays with no shell, mirroring\n * `install/quarantine.ts`'s subprocess discipline. Nothing from a staged\n * proposal is ever executed — the re-gate step only reads\n * `proposed_SKILL.md`/`proposed_CLAUDE.md` as text.\n *\n * The entire command is Pro (unlike `add`, which is free with only specific\n * sub-features Pro-gated): self-evolution reuses the provenance ledger and\n * pending-ingestion queue, and is a power-user workflow layered on the free\n * gate. Free during the 0.x beta via `proFeatureStatus()`, same as every\n * other Pro feature — see src/license.ts.\n *\n * `staging_dir` (from skillopt-sleep's own JSON output) is untrusted input:\n * it's validated to be strictly contained under the expected sandbox\n * (`<project>/.skillopt-sleep/staging/`) before anything reads, copies, or\n * deletes it — mirroring `install/quarantine.ts`'s tar-member path-traversal\n * guard. The staged proposal is further treated as untrusted: a symlink in\n * place of a proposed file is refused, and the exact bytes re-gated are\n * hash-pinned and re-verified immediately before `adopt` runs, so a mutation\n * between the scan and the adopt (TOCTOU) is caught rather than silently\n * adopted.\n */\n\nconst SKILLOPT_BIN = 'skillopt-sleep';\nconst PRO_FEATURE_LABEL = 'self-evolution via SkillOpt-Sleep (evolve) — re-gating, provenance, and the pending-ingestion queue';\n\nexport interface EvolveOptions {\n /** Project dir passed to skillopt-sleep as --project. Defaults to cwd. */\n project?: string;\n /** Use skillopt-sleep's `dry-run` subcommand (report only, nothing staged) instead of `run`. */\n dryRun?: boolean;\n /** skillopt-sleep backend. Defaults to \"mock\" (offline, no session data leaves the machine). */\n backend?: string;\n /** Hours of session history for skillopt-sleep to harvest. Passed through as --lookback-hours. */\n lookbackHours?: number;\n /** Skip interactive prompts (the disclosure confirm and the promote/hold/reject prompt); honor the re-gate verdict automatically. */\n yes?: boolean;\n /** Print the re-gate result as JSON instead of the terminal report. Implies non-interactive, same as `add`'s --json. */\n json?: boolean;\n /** Allow re-adopting a staging dir whose proposed content already matches the live skill (double-adopt guard override). */\n force?: boolean;\n}\n\nexport interface EvolveDeps {\n /** Injected subprocess runner for skillopt-sleep invocations. Defaults to a real argv-array spawn (no shell). */\n exec?: ExecFn;\n /** Injected y/N confirm for the data-boundary disclosure prompt. Defaults to a real stdin readline confirm. */\n confirm?: (question: string) => Promise<boolean>;\n}\n\ninterface SkilloptRunOutput {\n staging_dir: string | null;\n accepted: boolean;\n}\n\ninterface StagingManifest {\n live_skill_path: string;\n live_memory_path?: string | null;\n has_skill: boolean;\n has_memory: boolean;\n}\n\nexport async function evolve(targetSkillDir: string, options: EvolveOptions = {}, deps: EvolveDeps = {}): Promise<void> {\n const exec = deps.exec ?? defaultExec;\n const json = options.json;\n\n // 1. Validate the target skill dir.\n const resolvedTargetDir = resolvePath(targetSkillDir);\n const skillMdPath = join(resolvedTargetDir, 'SKILL.md');\n if (!existsSync(skillMdPath)) {\n throw new UserError(`no SKILL.md found at ${skillMdPath} — evolve requires an existing, installed skill directory`);\n }\n\n // 2. Pro gate — the whole command is Pro; free during the 0.x beta.\n const proStatus = proFeatureStatus(PRO_FEATURE_LABEL);\n if (!proStatus.enabled) {\n throw new UserError(proStatus.notice ?? 'evolve is a Pro feature — set SKILL_FORGE_LICENSE or config.licenseKey to a valid license to unlock.');\n }\n const notices: string[] = proStatus.notice ? [proStatus.notice] : [];\n\n // 3. Data-boundary disclosure.\n const backend = options.backend ?? 'mock';\n if (backend !== 'mock') {\n const disclosure =\n `data boundary: this run uses the \"${backend}\" backend. Per SkillOpt-Sleep's own docs, harvesting itself ` +\n 'is local and read-only, but a real backend sends truncated excerpts from harvested sessions and derived ' +\n 'tasks to the provider you selected — this is not currently guaranteed to be secret-free.';\n if (options.yes) {\n notices.push(`${disclosure} Proceeding (--yes).`);\n } else if (json) {\n throw new UserError(`${disclosure} Pass --yes to proceed — --json is non-interactive and cannot prompt for confirmation.`);\n } else {\n console.log(`[skill-forge] ${disclosure}`);\n const confirmFn = deps.confirm ?? defaultConfirm;\n const confirmed = await confirmFn('Proceed and send session data to this backend? [y/N] ');\n if (!confirmed) {\n throw new UserError('evolve cancelled — data-boundary disclosure was not accepted.');\n }\n notices.push(`${disclosure} Confirmed interactively.`);\n }\n } else {\n notices.push('using the offline \"mock\" backend — deterministic, no network calls, no session data leaves this machine.');\n }\n\n // 4. Detect (never install), then run skillopt-sleep.\n const project = resolvePath(options.project ?? process.cwd());\n const present = await detectSkillopt(exec, project);\n if (!present) {\n throw new UserError(\n '`skillopt-sleep` not found on PATH. Install it with `pip install skillopt` ' +\n '(https://github.com/microsoft/SkillOpt) and re-run `skill-forge evolve`.'\n );\n }\n\n const subcommand = options.dryRun ? 'dry-run' : 'run';\n const runArgs = [subcommand, '--project', project, '--target-skill-path', skillMdPath, '--json', '--backend', backend];\n if (options.lookbackHours !== undefined) {\n runArgs.push('--lookback-hours', String(options.lookbackHours));\n }\n\n const runResult = await exec(SKILLOPT_BIN, runArgs, { cwd: project });\n if (runResult.code !== 0) {\n throw new UserError(`skillopt-sleep ${subcommand} failed (exit ${runResult.code}): ${tail(runResult.stderr || runResult.stdout)}`);\n }\n\n let rawOutput: unknown;\n try {\n rawOutput = JSON.parse(runResult.stdout);\n } catch (err) {\n throw new UserError(`could not parse skillopt-sleep JSON output: ${(err as Error).message}`);\n }\n const parsed = validateRunOutput(rawOutput);\n\n if (!parsed.accepted || !parsed.staging_dir) {\n if (json) {\n console.log(JSON.stringify({ ok: true, accepted: parsed.accepted, stagingDir: parsed.staging_dir }, null, 2));\n } else {\n console.log('[skill-forge] skillopt-sleep produced no accepted proposal — nothing to re-gate or adopt.');\n }\n return;\n }\n\n // `staging_dir` is untrusted (skillopt-sleep's own JSON output) — require it to\n // be strictly contained under the expected sandbox before it's read, copied,\n // adopted, or deleted anywhere below. A naive `startsWith(stagingRoot)` would\n // be defeated by a sibling directory sharing the same string prefix (e.g.\n // `.skillopt-sleep/staging-evil` next to `.skillopt-sleep/staging`) — this\n // checks for either exact equality or the separator-qualified prefix, mirroring\n // `install/quarantine.ts`'s tar-member path-traversal guard.\n const stagingRoot = join(project, '.skillopt-sleep', 'staging');\n const stagingDir = resolvePath(parsed.staging_dir);\n if (stagingDir !== stagingRoot && !stagingDir.startsWith(stagingRoot + sep)) {\n throw new UserError(\n `skillopt-sleep reported a staging_dir outside the expected sandbox (${stagingRoot}): ${stagingDir} — refusing to use it`\n );\n }\n\n // 5. Re-gate: scan ONLY the staged proposal, in a fresh temp copy — never the live skill, never report.json/diagnostics.json.\n const proposedSkillMd = join(stagingDir, 'proposed_SKILL.md');\n const proposedClaudeMd = join(stagingDir, 'proposed_CLAUDE.md');\n const reportMdPath = join(stagingDir, 'report.md');\n if (!existsSync(proposedSkillMd)) {\n throw new UserError(`skillopt-sleep reported an accepted proposal but ${proposedSkillMd} is missing`);\n }\n if (existsSync(reportMdPath)) {\n notices.push(`SkillOpt-Sleep's full report: ${reportMdPath}`);\n }\n\n // A staged proposal is untrusted content: refuse a symlink in place of a\n // proposed file (spirit of the tar-member guard — never follow a link out\n // of the sandbox) before reading either one.\n assertNotSymlink(proposedSkillMd, 'proposed_SKILL.md');\n assertNotSymlink(proposedClaudeMd, 'proposed_CLAUDE.md');\n\n // Hash-pin what's about to be scanned, so a mutation of the staged files\n // between this scan and the `adopt` call below (TOCTOU) is caught rather\n // than silently adopted — see the re-check immediately before `adopt`.\n const scannedSkillHash = sha256File(proposedSkillMd);\n const scannedClaudeHash = sha256File(proposedClaudeMd);\n\n const tempDir = mkdtempSync(join(tmpdir(), 'skill-forge-evolve-'));\n let profile: SkillProfile;\n let safety: SafetyResult;\n try {\n cpSync(proposedSkillMd, join(tempDir, 'SKILL.md'));\n if (existsSync(proposedClaudeMd)) {\n cpSync(proposedClaudeMd, join(tempDir, 'CLAUDE.md'));\n }\n\n // Unreachable in practice: the temp dir only ever contains a renamed\n // proposed_SKILL.md (+ proposed_CLAUDE.md), never an .mcp.json or\n // plugin.json — asSkillProfile() throws before this could matter.\n profile = asSkillProfile(profileSkill(tempDir), 'for the evolved proposal');\n\n const strictness = loadConfig().strictness;\n safety = await scanSafety(tempDir, strictness);\n } finally {\n // A cleanup failure here is secondary to whatever the try block already\n // produced (a result, or an error already propagating) — never let it\n // mask that.\n try {\n rmSync(tempDir, { recursive: true, force: true });\n } catch (err) {\n console.error(`[skill-forge] failed to clean up temp re-gate dir ${tempDir}: ${(err as Error).message}`);\n }\n }\n\n const safetyVerdict = safety.verdict;\n const gateResult: GateResult = {\n profile,\n safety: {\n verdict: SAFETY_VERDICT_MAP[safetyVerdict],\n findings: mapSafetyFindings(safety.findings),\n },\n notices,\n };\n printGateOutput(gateResult, json);\n\n // --dry-run belt-and-braces: never stage/adopt anything, regardless of what\n // skillopt-sleep's JSON output claims — even if it (bug or otherwise)\n // reports an accepted proposal + a real staging_dir for a dry-run call,\n // this never reaches the decision/adopt path below.\n if (options.dryRun) {\n logOutcome('[skill-forge] --dry-run: report only, nothing staged or adopted.', json);\n return;\n }\n\n // 6. Decide — same promote/hold/reject semantics as add/scan.\n const decision =\n options.yes || json ? yesDecision(safetyVerdict) : await promptDecision(safetyVerdict, 'this evolution proposal');\n\n if (decision === 'promote') {\n const manifest = readManifest(stagingDir);\n if (isAlreadyAdopted(stagingDir, manifest) && !options.force) {\n throw new UserError(\n `staging dir ${stagingDir} looks already adopted — its proposed content is byte-identical to the live ` +\n 'skill (and memory, if present). Pass --force to re-adopt anyway.'\n );\n }\n\n // Re-verify immediately before adopting: if the staged files changed since\n // they were scanned above, what's about to be adopted is no longer what\n // was re-gated.\n if (sha256File(proposedSkillMd) !== scannedSkillHash || sha256File(proposedClaudeMd) !== scannedClaudeHash) {\n throw new UserError(`proposal changed after it was re-gated (staging dir: ${stagingDir}) — refusing to adopt`);\n }\n\n const adoptResult = await exec(SKILLOPT_BIN, ['adopt', '--staging', stagingDir, '--project', project], { cwd: project });\n if (adoptResult.code !== 0) {\n throw new UserError(`skillopt-sleep adopt failed (exit ${adoptResult.code}): ${tail(adoptResult.stderr || adoptResult.stdout)}`);\n }\n\n // The live skill has now changed — report that immediately, before the\n // bookkeeping below, which can itself fail without undoing the adopt.\n logOutcome(`[skill-forge] adopted -> ${resolvedTargetDir}`, json);\n\n // Free-tier `add` records DEFER for \"adopt as-is, no absorb/fork judgment\" —\n // the same is true here (the re-gate is a safety check, not a redundancy\n // judgment against the existing set), so this reuses DEFER rather than\n // adding a sixth member to provenance.ts's five-verb FORGE_VERBS/ForgeVerb\n // (referenced elsewhere as exactly five: docs/gate-policy.md,\n // assets/ingest-prompt.md, README.md, CLAUDE.md). The distinguishing\n // \"this was self-evolved\" signal lives in the note below and in the\n // queue entry's `origin: 'evolve'` field instead.\n const skillsRootForProvenance = dirname(resolvedTargetDir);\n let ledgerPath: string;\n try {\n ledgerPath = appendProvenance(\n {\n name: profile.name ?? basename(resolvedTargetDir),\n source: resolvedTargetDir,\n version: profile.version,\n license: profile.license,\n verb: 'DEFER',\n target: resolvedTargetDir,\n took: `self-evolved via SkillOpt-Sleep (staging: ${stagingDir})`,\n notes: `self-evolved via SkillOpt-Sleep; re-gated (verdict: ${safetyVerdict})`,\n },\n skillsRootForProvenance\n );\n } catch (err) {\n throw new UserError(\n `adopt succeeded and the live skill at ${resolvedTargetDir} has already changed, but recording the ` +\n `provenance entry failed: ${(err as Error).message}`\n );\n }\n logOutcome(`[skill-forge] provenance recorded -> ${ledgerPath}`, json);\n\n try {\n appendQueueEntry({\n source: resolvedTargetDir,\n sourceType: 'local',\n installedPath: resolvedTargetDir,\n quarantinePath: stagingDir,\n gate: {\n license: profile.license ?? null,\n safetyVerdict,\n safetyFindings: safety.findings.map(formatSafetyFinding),\n overlapTop: [],\n suggestedVerb: null,\n },\n status: 'pending',\n artifactType: 'skill',\n origin: 'evolve',\n });\n } catch (err) {\n throw new UserError(\n `adopt succeeded and the live skill at ${resolvedTargetDir} has already changed (provenance was recorded), ` +\n `but appending the pending-ingestion queue entry failed: ${(err as Error).message}`\n );\n }\n logOutcome('[skill-forge] queued for ingestion review', json);\n } else if (decision === 'reject') {\n try {\n rmSync(stagingDir, { recursive: true, force: true });\n } catch (err) {\n throw new UserError(\n `rejected the proposal, but removing the staging dir ${stagingDir} failed: ${(err as Error).message} — ` +\n 'remove it manually.'\n );\n }\n logOutcome('[skill-forge] rejected — staging dir removed', json);\n if (safetyVerdict === 'block') process.exitCode = 1;\n } else {\n logOutcome(`[skill-forge] held — staging dir left in place -> ${stagingDir}`, json);\n if (existsSync(reportMdPath)) {\n logOutcome(`[skill-forge] see ${reportMdPath} for the full evidence report`, json);\n }\n }\n}\n\nasync function defaultConfirm(question: string): Promise<boolean> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n // Iterate lines via the async-iterator protocol rather than rl.question() —\n // same rationale as decision.ts's promptDecision: question() attaches a\n // one-shot 'line' listener, and on piped/closed (non-TTY) stdin that ends\n // without ever emitting a line, that listener never fires and the promise\n // never resolves — the process hangs forever. The async iterator both\n // queues lines and terminates cleanly at EOF, so a closed stream resolves\n // (falling through to `false`, \"not confirmed\") instead of hanging.\n process.stdout.write(question);\n for await (const raw of rl) {\n return /^y(es)?$/i.test(raw.trim());\n }\n return false;\n } finally {\n rl.close();\n }\n}\n\n/** Detects skillopt-sleep on PATH by running `--help` via the injected exec — never installs it. Any rejection (ENOENT) or nonzero exit is treated as absent. */\nasync function detectSkillopt(exec: ExecFn, cwd: string): Promise<boolean> {\n try {\n const result = await exec(SKILLOPT_BIN, ['--help'], { cwd });\n return result.code === 0;\n } catch {\n return false;\n }\n}\n\n/** Last `maxLines` lines of `text`, for surfacing a subprocess failure without dumping unbounded output. */\nfunction tail(text: string, maxLines = 20): string {\n return text.trim().split('\\n').slice(-maxLines).join('\\n');\n}\n\n/**\n * Validates skillopt-sleep's `run`/`dry-run` JSON output — an external-process\n * trust boundary — before any field is used: must be a plain object,\n * `staging_dir` must be a string or null/undefined, otherwise a UserError\n * (not a TypeError surfacing later at some unrelated call site). `accepted`\n * is coerced to a boolean rather than strictly typechecked, matching how\n * loosely the field is actually consumed downstream.\n */\nfunction validateRunOutput(raw: unknown): SkilloptRunOutput {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new UserError(`skillopt-sleep produced unexpected JSON output (expected an object): ${JSON.stringify(raw)}`);\n }\n const obj = raw as Record<string, unknown>;\n const stagingDirRaw = obj.staging_dir;\n if (stagingDirRaw !== null && stagingDirRaw !== undefined && typeof stagingDirRaw !== 'string') {\n throw new UserError(\n `skillopt-sleep produced an unexpected \"staging_dir\" (expected a string or null): ${JSON.stringify(stagingDirRaw)}`\n );\n }\n return { accepted: Boolean(obj.accepted), staging_dir: stagingDirRaw ?? null };\n}\n\nfunction readManifest(stagingDir: string): StagingManifest {\n const manifestPath = join(stagingDir, 'manifest.json');\n let raw: string;\n try {\n raw = readFileSync(manifestPath, 'utf-8');\n } catch (err) {\n throw new UserError(`could not read staging manifest ${manifestPath}: ${(err as Error).message}`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new UserError(`could not parse staging manifest ${manifestPath}: ${(err as Error).message}`);\n }\n return validateManifest(parsed, manifestPath);\n}\n\n/** Validates manifest.json's shape (another external trust boundary) before any field is used. */\nfunction validateManifest(raw: unknown, manifestPath: string): StagingManifest {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new UserError(`staging manifest ${manifestPath} has an unexpected shape (expected an object)`);\n }\n const obj = raw as Record<string, unknown>;\n if (typeof obj.live_skill_path !== 'string') {\n throw new UserError(`staging manifest ${manifestPath} is missing a valid \"live_skill_path\" string`);\n }\n if (obj.live_memory_path !== undefined && obj.live_memory_path !== null && typeof obj.live_memory_path !== 'string') {\n throw new UserError(`staging manifest ${manifestPath} has an unexpected \"live_memory_path\" (expected a string or null)`);\n }\n if (typeof obj.has_skill !== 'boolean' || typeof obj.has_memory !== 'boolean') {\n throw new UserError(`staging manifest ${manifestPath} is missing valid \"has_skill\"/\"has_memory\" booleans`);\n }\n return {\n live_skill_path: obj.live_skill_path,\n live_memory_path: (obj.live_memory_path as string | null | undefined) ?? null,\n has_skill: obj.has_skill,\n has_memory: obj.has_memory,\n };\n}\n\nfunction filesIdentical(a: string, b: string): boolean {\n if (!existsSync(a) || !existsSync(b)) return false;\n return readFileSync(a).equals(readFileSync(b));\n}\n\n/**\n * Double-adopt guard: true when the staged proposal's content is\n * byte-identical to the live file(s) it would replace — i.e. this staging\n * dir already appears to have been adopted once. skillopt-sleep's own\n * `adopt` has no confirmation and a second adopt overwrites its own backup\n * (the pre-evolution original is lost), so this guard exists purely on our\n * side of the wrapper.\n *\n * Grounded in which proposed files actually exist on disk (`existsSync`)\n * rather than trusting the manifest's `has_skill`/`has_memory` flags blindly\n * — a stale or malformed manifest claiming `false` shouldn't be able to\n * silently skip a comparison for a file that's actually sitting right there.\n */\nfunction isAlreadyAdopted(stagingDir: string, manifest: StagingManifest): boolean {\n const proposedSkillMd = join(stagingDir, 'proposed_SKILL.md');\n const proposedClaudeMd = join(stagingDir, 'proposed_CLAUDE.md');\n let sawComparison = false;\n\n if (existsSync(proposedSkillMd)) {\n sawComparison = true;\n if (!filesIdentical(proposedSkillMd, manifest.live_skill_path)) return false;\n }\n if (existsSync(proposedClaudeMd) && manifest.live_memory_path) {\n sawComparison = true;\n if (!filesIdentical(proposedClaudeMd, manifest.live_memory_path)) return false;\n }\n\n return sawComparison;\n}\n\n/** Throws a UserError if `path` is a symlink. Silent no-op if `path` doesn't exist (existsSync() calls elsewhere already gate required-vs-optional). Uses lstatSync (never follows the link) so a broken symlink is still caught. */\nfunction assertNotSymlink(path: string, label: string): void {\n let stat;\n try {\n stat = lstatSync(path);\n } catch {\n return;\n }\n if (stat.isSymbolicLink()) {\n throw new UserError(`refusing to re-gate ${label}: ${path} is a symlink (a staged proposal must be a plain file)`);\n }\n}\n\n/** sha256 hex digest of a file's contents, or null if it doesn't exist. Exported for direct unit coverage of the TOCTOU hash-pin (see evolve()'s scan-time / pre-adopt re-check). */\nexport function sha256File(path: string): string | null {\n if (!existsSync(path)) return null;\n return createHash('sha256').update(readFileSync(path)).digest('hex');\n}\n","import { evolve } from '../evolve.js';\nimport type { EvolveOptions } from '../evolve.js';\n\n/**\n * `skill-forge evolve <target-skill-dir>` — thin CLI wiring around the Pro\n * self-evolution flow in src/evolve.ts: orchestrate a SkillOpt-Sleep run,\n * re-gate the staged proposal through the same static skill safety ruleset\n * `add`/`scan` use, then promote/hold/reject. See src/evolve.ts for the\n * actual pipeline; this file only adapts commander's positional/options\n * shape into a call.\n */\nexport async function evolveCommand(targetSkillDir: string, options: EvolveOptions = {}): Promise<void> {\n await evolve(targetSkillDir, options);\n}\n"],"mappings":"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","names":["join","existsSync","homedir","join","existsSync","existsSync","mkdirSync","join","join","mkdirSync","existsSync","walk","reject","cpSync","existsSync","mkdirSync","basename","join","existsSync","mkdirSync","readFileSync","writeFileSync","readFileSync","readdirSync","statSync","join","SKIP_DIRS","existsSync","readFileSync","writeFileSync","mkdirSync","readFileSync","dirname","join","pkg","existsSync","statSync","writeFileSync","join","readdirSync","join","existsSync","readFileSync","readdirSync","rmSync","statSync","basename","dirname","extname","join","resolve","existsSync","readFileSync","extname","join","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","sep","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","createInterface","join","join","join","createInterface","cpSync","existsSync","mkdtempSync","readFileSync","rmSync","tmpdir","basename","dirname","join","resolvePath","PRO_FEATURE_LABEL","resolvePath","join","existsSync","mkdtempSync","tmpdir","cpSync","rmSync","dirname","basename","createInterface","readFileSync","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/gate/scanShared.ts","../src/install/mcpCandidate.ts","../src/provenance.ts","../src/commands/gatePipeline.ts","../src/gate/profile.ts","../src/gate/mcpCapabilities.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/audit.ts","../src/audit.ts","../src/decision.ts","../src/commands/scan.ts","../src/commands/list.ts","../src/commands/status.ts","../src/evolve.ts","../src/commands/evolve.ts","../src/commands/organize.ts","../src/organize.ts","../src/gate/skillsShApi.ts","../src/commands/find.ts","../src/commands/watch.ts","../src/commands/ingest.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 { evolveCommand } from './commands/evolve.js';\nimport { auditCommand } from './commands/audit.js';\nimport { organizeCommand } from './commands/organize.js';\nimport { find } from './commands/find.js';\nimport { watchCommand } from './commands/watch.js';\nimport { ingest, queueClose } from './commands/ingest.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\n/** Commander option parser for `--lookback-hours` — throws UserError (caught below) on a non-finite, empty, or negative number. */\nfunction parseLookbackHours(value: string): number {\n const n = Number(value);\n if (value.trim() === '' || !Number.isFinite(n) || n < 0) {\n throw new UserError(`--lookback-hours must be a non-negative number (got \"${value}\")`);\n }\n return n;\n}\n\n/** Commander option parser for `find --limit` — throws UserError (caught below) on a non-positive-integer value. */\nfunction parseLimit(value: string): number {\n const n = Number(value);\n if (value.trim() === '' || !Number.isInteger(n) || n < 1 || n > 50) {\n throw new UserError(`--limit must be an integer between 1 and 50 (got \"${value}\")`);\n }\n return n;\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('evolve <target-skill-dir>')\n .description('Self-evolve a skill via SkillOpt-Sleep, re-gate the proposal, then promote/hold/reject (Pro)')\n .option('--project <dir>', 'project dir passed to SkillOpt-Sleep as --project (defaults to cwd)')\n .option('--dry-run', 'SkillOpt-Sleep dry-run: report only, nothing staged')\n .option('--backend <name>', 'SkillOpt-Sleep backend (default: mock, offline — a real backend requires --yes or interactive confirmation)')\n .option('--lookback-hours <n>', 'hours of session history for SkillOpt-Sleep to harvest', parseLookbackHours)\n .option('-y, --yes', 'skip interactive prompts; honor the re-gate verdict automatically')\n .option('--json', 'print the re-gate result as JSON instead of the terminal report')\n .option('--force', 'allow re-adopting a staging dir whose proposed content already matches the live skill')\n .action(evolveCommand);\n\nprogram\n .command('audit')\n .alias('doctor')\n .description('Doctor-style health check over the configured skill/MCP set: inventory, hygiene findings, and consolidation/refinement opportunities')\n .option('--json', 'print the audit report as JSON instead of the terminal summary (non-interactive: skips the business-profile prompt)')\n .option('-y, --yes', 'skip interactive prompts (business profile, foundation, handoff); never implies --foundation or --handoff')\n .option('--report <file>', 'write the report to this path instead of the default ~/.skill-forge/reports/audit-<timestamp>.md (refuses an existing path)')\n .option('--foundation', 'authorizes exactly one skills-root write: scaffold a business-foundation skill from the captured business profile')\n .option('--handoff', 'authorizes launching the configured agent with the bundled curation prompt against the written report')\n .action(auditCommand);\n\nprogram\n .command('organize')\n .description('Build the set-level capability registry + dependency graph across configured skills roots (Pro)')\n .option('--json', 'print the registry+graph report as JSON instead of the terminal summary')\n .option('--out <file>', 'write the full report JSON to this path (refuses an existing path)')\n .option('--usage-snapshot <file>', 'join per-skill usage counts from a skill-monitor snapshot JSON')\n .action(organizeCommand);\n\nprogram\n .command('find [query]')\n .description('Discover skills via skills.sh and check partner security audits (requires VERCEL_OIDC_TOKEN)')\n .option('--limit <n>', 'max search results (default 10)', parseLimit)\n .option('--audit <id>', 'show partner audit verdicts (Socket/Snyk/…) for a skills.sh id')\n .option('--get <id>', 'show detail + file tree for a skills.sh id')\n .option('--curated', 'list skills.sh curated skills')\n .option('--json', 'print the raw API payload as JSON')\n .action(find);\n\nprogram\n .command('watch')\n .description('Drift check across every SOURCES.md provenance ledger: which ingested upstreams have moved (Pro)')\n .option('--offline', 'skip all network checks; list entries for manual comparison only')\n .option('--json', 'print the drift report as JSON instead of the terminal summary')\n .action(watchCommand);\n\nprogram\n .command('ingest')\n .description(\n 'Hand the pending-ingestion queue to the configured coding agent for the decide/absorb pass (Pro). ' +\n 'For a single new source, use `skill-forge add <source> --ingest` instead.'\n )\n .option('--list', 'print pending queue entries and exit — no handoff')\n .option('--json', 'with --list: print pending entries as JSON')\n .action(ingest);\n\nconst queue = program.command('queue').description('Manage the pending-ingestion queue (Pro)');\nqueue\n .command('close <id>')\n .description('Close a queue entry after the decide pass — agents use this instead of hand-editing queue.json')\n .requiredOption('--status <status>', 'final status: \"ingested\" or \"dismissed\"')\n .action(queueClose);\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 { 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 { formatSafetyFinding } from '../gate/scanShared.js';\nimport { suggestVerbCode } from '../gate/overlap.js';\nimport { appendQueueEntry } from '../queue.js';\nimport { runHandoff } from '../handoff.js';\nimport { maybeOfferInit } from './init.js';\nimport { logOutcome, promptDecision, yesDecision } from '../decision.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\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 =\n options.yes || options.json ? yesDecision(safety.verdict) : await promptDecision(safety.verdict, 'this skill');\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(formatSafetyFinding),\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 =\n options.yes || options.json ? yesDecision(safety.verdict) : await promptDecision(safety.verdict, 'this skill');\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 // packageName (v0.7) — the candidate's own scanned package.json name, so\n // deriveServerEntry can auto-pin an unpinned npx spec in candidateEntry\n // only when it provably matches the package that was actually scanned.\n packageName: detail.packageName,\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.autoPinned) {\n logOutcome(`[skill-forge] pinned launch spec ${result.autoPinned.from} -> ${result.autoPinned.to}`, options.json);\n }\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(formatSafetyFinding),\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","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\n/**\n * Business context captured interactively by `skill-forge audit` (v0.8, never invented) —\n * grounds the business-foundation skill scaffold and the curation handoff prompt. See\n * src/audit.ts.\n */\nexport interface BusinessProfile {\n businessName?: string;\n industry?: string;\n audiences: string[];\n workflows: string[];\n constraints: string[];\n /** ISO-8601 timestamp of the last time this profile was captured/confirmed. */\n updatedAt: string;\n}\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 /** Business context captured by `skill-forge audit`'s interactive Q&A. See BusinessProfile above. */\n businessProfile?: BusinessProfile;\n /** Path to the scaffolded business-foundation skill, once created by `skill-forge audit --foundation`. See src/audit.ts. */\n foundationSkillPath?: 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\n/**\n * Repairs a possibly hand-edited `businessProfile` rather than letting a malformed one crash\n * later: `audiences`/`workflows`/`constraints` MUST be arrays of strings (consumed by\n * `bulletList()` in `src/audit.ts`'s foundation-skill renderer) — a missing or wrong-shaped\n * value is coerced to `[]` instead of propagating a raw TypeError out of `renderFoundationSkillMd`.\n * `businessName`/`industry`/`updatedAt` pass through unchanged.\n */\nfunction normalizeBusinessProfile(raw: BusinessProfile | undefined): BusinessProfile | undefined {\n if (!raw || typeof raw !== 'object') return undefined;\n const toStringArray = (v: unknown): string[] => (Array.isArray(v) ? v.filter((x): x is string => typeof x === 'string') : []);\n return {\n ...raw,\n audiences: toStringArray(raw.audiences),\n workflows: toStringArray(raw.workflows),\n constraints: toStringArray(raw.constraints),\n };\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 businessProfile: normalizeBusinessProfile(parsed.businessProfile) ?? defaults.businessProfile,\n foundationSkillPath: parsed.foundationSkillPath ?? defaults.foundationSkillPath,\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\n/** Real argv-array subprocess runner (no shell) — the default ExecFn, and shared by evolve.ts's skillopt-sleep invocations. */\nexport function 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';\nimport { collectNpxPackageSpecs, isNpxCommand, isPinnedPackageSpec, packageSpecBase } from '../gate/scanShared.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>` — used on the\n * npx-fallback path (no `candidateEntry`; see `deriveServerEntry` for the\n * refusal behavior when both are absent) and, when it matches\n * `packageName`, to auto-pin an unpinned `npx` spec found in a documented\n * `candidateEntry` (v0.7).\n */\n version?: string;\n /**\n * The package name from the candidate's own scanned, self-declared\n * `package.json` (v0.7, from `install/mcpCandidate.ts`'s sidecar read) —\n * for an npm-sourced candidate this IS the registry artifact being\n * profiled, but for a git/local source it's just whatever the candidate's\n * own `package.json` claims, unverified against any registry. Only when a\n * `candidateEntry`'s unpinned `npx` launch spec's package matches this\n * name is `version` above trusted to auto-pin it — a spec for some other\n * package is never pinned to a version scanned for a different package.\n */\n packageName?: 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 for command/args when present instead of the\n * npx-derived default. Any `env` values on it are always stripped before use.\n * An unpinned `npx` launch spec on this entry is auto-pinned when it matches\n * the candidate's own scanned (self-declared) package (see\n * `packageName`/`version` above), otherwise promotion is refused — see\n * `deriveServerEntry` (v0.7).\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 /** Set when an unpinned `npx` spec in a documented `candidateEntry` was auto-pinned (v0.7) — the exact spec text before/after. */\n autoPinned?: { from: string; to: 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, autoPinned } = 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, autoPinned };\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 *\n * A documented `candidateEntry` is used verbatim EXCEPT when its own launch\n * command is `npx` and it carries an unpinned package spec (v0.7 —\n * previously written through unchanged, only ever flagged as a MEDIUM\n * safety warning). `gate/scanShared.ts`'s `collectNpxPackageSpecs()` parses\n * the args the way npx itself does — including every repeatable `-p`/\n * `--package` value, not just the first non-flag token (a naive \"first\n * non-flag arg\" heuristic misses that `-p`/`--package` consumes its value\n * even when that value doesn't start with `-`, which real npx/npm-exec\n * still installs — see that function's own doc comment):\n * - **no specs at all, or every spec found is already pinned** → verbatim.\n * - **exactly one spec total, it's the positional launch spec (not a `-p`\n * value), and it's unpinned**: auto-pinned to `<packageName>@<version>`\n * when the spec's package matches the candidate's own scanned\n * (self-declared) `packageName` and a `version` was scanned for it\n * (`autoPinned` records the change); otherwise refused.\n * - **anything else with an unpinned spec** (an unpinned `-p` value, or\n * more than one spec at all) — refused, naming every unpinned spec.\n * Under-checking a more complex shape must fail closed: there is no\n * auto-pin for a `-p` dependency (it isn't \"the launched package,\" even\n * if it happens to match) or for a multi-spec shape.\n * For an npm-sourced candidate, `packageName`/`version` come from the\n * registry artifact itself; for a git/local source they're only what the\n * candidate's own `package.json` self-declares. Auto-pinning there reduces\n * moving-tag drift (the spec can't silently resolve to something different\n * later) — it does not authenticate registry ownership of that name.\n * There is no override flag for either refusal. Both still run before any\n * read/backup (see promoteMcp() above), so a refusal leaves nothing behind.\n */\nfunction deriveServerEntry(profile: McpServerProfile): {\n command: string;\n args: string[];\n envVarsToFill: string[];\n autoPinned?: { from: string; to: 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 const { command, args = [] } = profile.candidateEntry;\n\n if (isNpxCommand(command)) {\n const { packageFlagSpecs, positionalSpec } = collectNpxPackageSpecs(args);\n const allSpecs = positionalSpec ? [...packageFlagSpecs, positionalSpec] : packageFlagSpecs;\n const unpinned = allSpecs.filter((spec) => !isPinnedPackageSpec(spec));\n\n if (unpinned.length > 0) {\n const isSoleUnpinnedPositional =\n packageFlagSpecs.length === 0 && allSpecs.length === 1 && positionalSpec === unpinned[0];\n\n if (isSoleUnpinnedPositional) {\n const spec = unpinned[0];\n const specPackage = packageSpecBase(spec);\n if (profile.packageName && specPackage === profile.packageName && profile.version) {\n const to = `${profile.packageName}@${profile.version}`;\n const specIdx = args.indexOf(spec);\n const pinnedArgs = [...args];\n pinnedArgs[specIdx] = to;\n return {\n command,\n args: pinnedArgs,\n envVarsToFill: Array.from(envNames),\n autoPinned: { from: spec, to },\n };\n }\n\n const reason =\n profile.packageName && specPackage !== profile.packageName\n ? `the launch spec names package \"${specPackage}\", but the candidate's own package.json is \"${profile.packageName}\" — ` +\n 'pinning it to a version scanned for a different package would be unsafe'\n : \"no version was found in the candidate's own package.json to pin it to\";\n throw new UserError(\n `cannot promote MCP server \"${profile.name}\": its documented launch command \"npx ${spec}\" is unpinned ` +\n `and can't be safely auto-pinned (${reason}). Pin the spec to an exact version in the candidate's ` +\n `config (e.g. \\`${specPackage}@<version>\\`) and try again — there is no override for this.`\n );\n }\n\n throw new UserError(\n `cannot promote MCP server \"${profile.name}\": its documented launch command \"npx\" has unpinned package ` +\n `spec(s) — ${unpinned.map((s) => `\"${s}\"`).join(', ')} — and this shape (a -p/--package dependency, ` +\n \"or more than one spec) can't be safely auto-pinned. Pin every spec to an exact version in the \" +\n \"candidate's config and try again — there is no override for this.\"\n );\n }\n }\n\n return {\n command,\n 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, 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\n/** Formats a single finding as `SEVERITY [rule] file: detail` — shared by the pending-queue `safetyFindings` formatters in commands/add.ts and evolve.ts. */\nexport function formatSafetyFinding(f: SafetyFinding): string {\n return `${f.severity} [${f.rule}] ${f.file}: ${f.detail}`;\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 * Shared by `gate/mcpSafety.ts`'s scan and `install/promoteMcp.ts`'s\n * documented-candidateEntry version-pin enforcement (v0.7).\n */\nexport function 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. Shared by `gate/mcpSafety.ts` and `install/promoteMcp.ts` (v0.7). */\nexport function 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. Shared by `gate/mcpSafety.ts` and `install/promoteMcp.ts` (v0.7). */\nexport function 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\n/**\n * npx's own value-consuming CLI flags — verified against the installed npm\n * 12.0.1's `node_modules/npm/bin/npx-cli.js`. That file hardcodes an `opts`\n * set of flags that ALWAYS consume the next token (or an attached\n * `=value`) as their value rather than letting it become the launched\n * command: `package`/`p`, `cache`, `userconfig`, `call`/`c`, `shell`,\n * `node-arg`/`n`, and the deprecated-but-still-parsed `npm`. `registry` is\n * added here deliberately even though it is NOT in that hardcoded set:\n * npx-cli.js's parser has a generic fallback (any unrecognized flag that\n * isn't a known boolean switch consumes the next token as a value unless\n * that token itself looks like a flag) which makes `--registry <url>`\n * behave the same way in practice — verified empirically. This is a\n * DELIBERATE, DOCUMENTED SIMPLIFICATION, not a full reimplementation of\n * npx's parser: that fallback rule also consults npm's complete,\n * version-dependent set of boolean switches (`@npmcli/config`'s\n * `definitions`), which this stdlib-only tool does not bundle. Every other\n * `-`-prefixed token is treated as a boolean flag.\n */\nconst NPX_VALUE_FLAGS = new Set(['p', 'package', 'c', 'call', 'cache', 'userconfig', 'shell', 'n', 'node-arg', 'npm', 'registry']);\n\n/** Of NPX_VALUE_FLAGS, only `-p`/`--package`'s value(s) are themselves package specs — the rest (cache paths, config files, shell binaries, registry URLs, ...) are not. */\nconst NPX_PACKAGE_SPEC_FLAGS = new Set(['p', 'package']);\n\n/**\n * Every `-p`/`--package` value found (repeatable — npx installs ALL of\n * them, in addition to the launched command, before running it), and the\n * positional launch spec if one was found — kept separate rather than a\n * flat list because policy (`install/promoteMcp.ts`) treats them\n * differently: only the positional (the package actually being launched)\n * is ever eligible for auto-pinning; a `-p` dependency, even alone and\n * even if it happens to match the candidate's own package, is not.\n */\nexport interface NpxSpecCollection {\n packageFlagSpecs: string[];\n positionalSpec?: string;\n}\n\n/**\n * Collects every package spec that `npx -y <args>` would actually resolve\n * and install: every repeated `-p`/`--package` value, plus the positional\n * launch spec — the first token that's neither a recognized flag nor a\n * value a recognized flag consumed. Collection stops at that positional\n * token (mirroring npx-cli.js: once the positional is found, `--` is\n * spliced in front of it and everything after belongs to the *launched*\n * command's own argv, not npx's argument parsing). See NPX_VALUE_FLAGS\n * above for the documented scope/limits of this model. Shared by\n * `gate/mcpSafety.ts`'s scan and `install/promoteMcp.ts`'s\n * documented-candidateEntry version-pin enforcement (v0.7).\n */\nexport function collectNpxPackageSpecs(args: string[]): NpxSpecCollection {\n const packageFlagSpecs: string[] = [];\n let positionalSpec: string | undefined;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === '--') break;\n\n if (arg.startsWith('-')) {\n const eqIdx = arg.indexOf('=');\n const key = (eqIdx === -1 ? arg : arg.slice(0, eqIdx)).replace(/^-+/, '');\n const attached = eqIdx === -1 ? undefined : arg.slice(eqIdx + 1);\n\n if (NPX_VALUE_FLAGS.has(key)) {\n let value = attached;\n if (value === undefined) {\n // No attached =value — the next token is consumed as this flag's\n // value, whatever it looks like (mirrors npx-cli.js's unconditional\n // consumption for a known value-flag).\n value = args[i + 1];\n i++;\n }\n if (NPX_PACKAGE_SPEC_FLAGS.has(key) && value !== undefined) {\n packageFlagSpecs.push(value);\n }\n continue;\n }\n\n // Unrecognized flag — treated as boolean (see NPX_VALUE_FLAGS's doc\n // comment for this simplification's documented limits).\n continue;\n }\n\n // First token that's neither a flag nor a consumed value — the\n // positional launch spec. Collect it and stop.\n positionalSpec = arg;\n break;\n }\n\n return { packageFlagSpecs, positionalSpec };\n}\n","import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\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 * The package `name` from the candidate's own sibling `package.json`\n * (v0.7), when one exists next to an explicit `.mcp.json`/`mcp.json`. Used\n * by `install/promoteMcp.ts` to auto-pin an unpinned `npx` launch spec in\n * a documented `candidateEntry` — only when the spec's package matches\n * this name is the candidate's own scanned `version` trusted for pinning.\n * On the package.json-profile branch, `name` above already comes from\n * this same file, so `packageName` is left unset there (redundant).\n */\n packageName?: string;\n}\n\ninterface ConfigEntry {\n command?: string;\n args?: string[];\n env?: Record<string, unknown>;\n}\n\ninterface PackageInfo {\n name?: string;\n version?: string;\n}\n\n/** Tolerant read of a package.json's `name`/`version` — never throws; returns {} if missing/malformed. */\nfunction readPackageInfo(pkgPath: string): PackageInfo {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as Record<string, unknown>;\n return {\n name: typeof pkg.name === 'string' ? pkg.name : undefined,\n version: typeof pkg.version === 'string' ? pkg.version : undefined,\n };\n } catch {\n return {};\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 *\n * On the explicit `.mcp.json`/`mcp.json` branch (v0.7), also tolerantly reads\n * a sibling `package.json` next to the config file for its `name`/`version`\n * — populating `version`/`packageName` there too — so `promoteMcp.ts` can\n * auto-pin an unpinned `npx` spec in a documented `candidateEntry` when it\n * provably matches the candidate's own scanned package.\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: readPackageInfo(profile.config).version };\n }\n\n const entry = readConfigEntry(profile.config, name);\n const sidecar = readPackageInfo(join(dirname(profile.config), 'package.json'));\n return {\n name,\n command: entry?.command,\n args: entry?.args,\n env: entry?.env,\n version: sidecar.version,\n packageName: sidecar.name,\n };\n}\n","/*\n * Rhize Commercial License — NOT covered by the MIT license that applies to\n * the rest of this repository. Production use of this module's functionality\n * requires a valid license key. See LICENSE and LICENSE-COMMERCIAL.\n * Copyright (c) 2026 Rhize Media\n */\nimport { appendFileSync, existsSync, readFileSync, 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\n// ---------------------------------------------------------------------------\n// Ledger PARSING — inverse of appendProvenance's format, tolerant of hand-edits.\n// Backs `skill-forge watch` (src/commands/watch.ts). Never throws on a single\n// malformed block: it's skipped and a warning is recorded, so one bad\n// hand-edit can't take down the whole drift check.\n// ---------------------------------------------------------------------------\n\n/** One parsed `## <name> — <date>` block from a SOURCES.md ledger. Field values are raw ledger text — untrusted, and must be sanitized (see audit.ts's `sanitizeForMarkdown`) before landing in human-readable output. */\nexport interface ProvenanceLedgerEntry {\n name: string;\n date: string;\n source: string;\n upstreamRef: string;\n license: string;\n verb: ForgeVerb;\n target: string;\n took: string;\n verified: string;\n /** The stored drift-check command string, exactly as recorded — DATA ONLY. Never execute this; it is attacker-influenceable ledger content (see src/commands/watch.ts). */\n driftCheck: string;\n notes: string;\n}\n\nexport interface ParsedProvenanceLedger {\n entries: ProvenanceLedgerEntry[];\n /** One line per skipped/malformed block, describing why it was skipped. Parsing never throws — a bad block is reported here instead. */\n warnings: string[];\n}\n\n/** Extracts a single `**Label:** value` field's value (up to end of line) from a ledger block, or undefined if absent. */\nfunction extractField(block: string, label: string): string | undefined {\n const escaped = label.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const match = block.match(new RegExp(`\\\\*\\\\*${escaped}:\\\\*\\\\*\\\\s*(.*)`));\n return match ? match[1].trim() : undefined;\n}\n\n/**\n * Parses a SOURCES.md ledger's full text into structured entries — the inverse of\n * `appendProvenance`'s block format. Tolerant of hand-edits: a block missing a name/date\n * header or a missing/invalid `Verb` is skipped with a warning rather than thrown. The\n * leading document header (everything before the first `## `) is ignored.\n */\nexport function parseProvenanceLedger(text: string): ParsedProvenanceLedger {\n const entries: ProvenanceLedgerEntry[] = [];\n const warnings: string[] = [];\n\n // Mirrors record_provenance.py's `re.split(r\"\\n## \", text)` — blocks[0] is\n // the document header (or leading garbage before the first entry) and is\n // never treated as an entry.\n const blocks = text.split(/\\n## /);\n\n for (const block of blocks.slice(1)) {\n const headerLine = block.split('\\n', 1)[0] ?? '';\n const [namePart, ...dateParts] = headerLine.split(' — ');\n const name = namePart?.trim();\n if (!name) {\n warnings.push(`skipped malformed ledger block: missing \"## <name> — <date>\" header (saw \"${headerLine.slice(0, 80)}\")`);\n continue;\n }\n const date = dateParts.join(' — ').trim();\n\n const verbRaw = extractField(block, 'Verb');\n if (!verbRaw || !(FORGE_VERBS as readonly string[]).includes(verbRaw)) {\n warnings.push(`skipped malformed ledger block for \"${name}\": missing or invalid Verb (saw \"${verbRaw ?? ''}\")`);\n continue;\n }\n\n const driftCheckMatch = block.match(/\\*\\*Drift check:\\*\\*\\s*`([^`]*)`/);\n\n entries.push({\n name,\n date,\n source: extractField(block, 'Source') ?? '',\n upstreamRef: extractField(block, 'Upstream ref') ?? '',\n license: extractField(block, 'License') ?? '',\n verb: verbRaw as ForgeVerb,\n target: extractField(block, 'Target') ?? '',\n took: extractField(block, 'Took') ?? '',\n verified: extractField(block, 'Verified') ?? '',\n driftCheck: driftCheckMatch ? driftCheckMatch[1] : '',\n notes: extractField(block, 'Notes') ?? '',\n });\n }\n\n return { entries, warnings };\n}\n\nexport interface ReadLedgerResult extends ParsedProvenanceLedger {\n ledgerPath: string;\n exists: boolean;\n}\n\n/**\n * Reads and parses `<skillsRoot>/SOURCES.md`. A missing ledger is not an error here (unlike\n * `appendProvenance`, which is a write path) — `exists: false` with empty entries/warnings lets\n * callers (e.g. `skill-forge watch`) report \"no ledger yet\" for a root cleanly.\n */\nexport function readProvenanceLedger(skillsRoot: string): ReadLedgerResult {\n const ledgerPath = join(skillsRoot, 'SOURCES.md');\n if (!existsSync(ledgerPath)) {\n return { ledgerPath, exists: false, entries: [], warnings: [] };\n }\n const text = readFileSync(ledgerPath, 'utf-8');\n const parsed = parseProvenanceLedger(text);\n return { ledgerPath, exists: true, ...parsed };\n}\n","import { readdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { DEFAULT_STRICTNESS, type Strictness } from '../config.js';\nimport { asProfile, 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 { SAFETY_VERDICT_MAP, mapSafetyFindings, type GateResult } from '../report/render.js';\n\nexport { printGateOutput } 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\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/** 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: mapSafetyFindings(safety.findings),\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/**\n * Narrows an already-resolved `Profile` to a `SkillProfile`, throwing a\n * descriptive error otherwise. For a caller that has already profiled a\n * path (e.g. via `profileSkill()`) and needs the skill-specific shape to\n * proceed. `context` is appended to the error message (e.g. `at ${path}`,\n * `for the evolved proposal`). Shared by `commands/gatePipeline.ts`'s\n * `asProfile()` (its skill branch) and `evolve.ts`'s re-gate step.\n */\nexport function asSkillProfile(profile: Profile, context: string): SkillProfile {\n if (profile.kind !== 'skill') {\n throw new Error(`expected a skill candidate ${context}, found a ${profile.kind}`);\n }\n return profile;\n}\n\n/**\n * Profiles `candidatePath` and asserts the result matches `artifact`\n * ('mcp' expects kind 'mcp'; 'skill'/absent expects kind 'skill'),\n * narrowing the return type accordingly. Throws a descriptive error\n * otherwise, rather than guessing. Moved here (v0.7 simplify pass) from\n * `commands/gatePipeline.ts`, which still owns calling it as part of the\n * gate pipeline; `evolve.ts` also profiles a skill through this same\n * skill-branch logic via `asSkillProfile()` above.\n */\nexport function asProfile(candidatePath: string, artifact: ArtifactType): Profile {\n const profile = profileSkill(candidatePath);\n if (artifact === 'mcp') {\n if (profile.kind !== 'mcp') {\n throw new Error(`expected an MCP candidate at ${candidatePath} (--artifact mcp), found a ${profile.kind}`);\n }\n return profile;\n }\n return asSkillProfile(profile, `at ${candidatePath}`);\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 { 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/** `OverlapPair` plus the absolute SKILL.md paths behind `a`/`b` — paths are the stable identity across roots (names can collide). */\nexport interface OverlapPathPair extends OverlapPair {\n aPath: string;\n bPath: string;\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\n/** The typed union `suggestVerbCode` returns — shared by callers (e.g. `audit.ts`'s `OverlapCluster.suggestedVerb`) that want the bare enum's type without re-deriving it. */\nexport type SuggestedVerbCode = ReturnType<typeof suggestVerbCode>;\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 an arbitrary set of SKILL.md paths; reports pairs scoring >=\n * threshold. Symmetric — the pair score is max(score(a,b), score(b,a)) since score() is\n * candidate-centric. Paths (not names) are the stable identity: names can collide across roots,\n * which is exactly why `skill-forge audit`'s cross-root clustering (src/audit.ts) needs the path\n * alongside each name rather than `scanOverlapSet`'s name-only `OverlapPair`.\n */\nexport function scanOverlapPaths(\n skillMdPaths: readonly string[],\n threshold: number = DEFAULT_SET_THRESHOLD,\n): OverlapPathPair[] {\n const metas = skillMdPaths.map(readFrontmatter);\n\n const pairs: OverlapPathPair[] = [];\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({\n a: metas[i].name,\n b: metas[j].name,\n aPath: skillMdPaths[i],\n bPath: skillMdPaths[j],\n score: round3(sc),\n });\n }\n }\n }\n pairs.sort((a, b) => b.score - a.score);\n return pairs;\n}\n\n/**\n * N-way: pairwise overlap across the whole installed set; reports pairs scoring >= threshold.\n * Delegates to `scanOverlapPaths` (path-based identity) and strips paths back off, so its public\n * behavior — exact scoring, ordering, and the name-only `OverlapPair` shape — is unchanged.\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 return scanOverlapPaths(paths, threshold).map(({ a, b, score: s }) => ({ a, b, score: s }));\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 {\n collectNpxPackageSpecs,\n isNpxCommand,\n isPinnedPackageSpec,\n packageSpecBase,\n readTextFile,\n verdictFor,\n walk,\n} 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\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 // -y/--yes present is still the gate for the rule itself (without it,\n // npx would prompt before installing anything — a different risk\n // profile) — but once gated, every unpinned spec is surfaced, not just\n // the first: collectNpxPackageSpecs() parses the way npx itself does,\n // including every repeatable -p/--package value, not just the first\n // non-flag token (see its own doc comment in scanShared.ts).\n const flagIdx = args.findIndex((a) => a === '-y' || a === '--yes');\n if (flagIdx !== -1) {\n const { packageFlagSpecs, positionalSpec } = collectNpxPackageSpecs(args);\n const allSpecs = positionalSpec ? [...packageFlagSpecs, positionalSpec] : packageFlagSpecs;\n for (const pkgSpec of allSpecs) {\n if (!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\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 type { SafetyFinding as GateSafetyFinding, Verdict as GateVerdict } from '../gate/safety.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\n/** Maps gate/safety.ts's pass|warn|block verdict to this module's ALLOW|CAUTION|BLOCK — shared by commands/gatePipeline.ts's runGate() and evolve.ts's re-gate step. */\nexport const SAFETY_VERDICT_MAP: Record<GateVerdict, SafetyVerdict> = {\n pass: 'ALLOW',\n warn: 'CAUTION',\n block: 'BLOCK',\n};\n\n/** Maps gate/safety.ts's {severity, rule, file, detail} findings into this module's {severity, rule, message} shape — shared by the same two call sites as SAFETY_VERDICT_MAP. */\nexport function mapSafetyFindings(findings: GateSafetyFinding[]): SafetyFinding[] {\n return findings.map((f) => ({ severity: f.severity, rule: f.rule, message: `${f.file}: ${f.detail}` }));\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/**\n * Prints a gate result the same way for `add`/`scan`/`evolve`: 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). Moved here (v0.7 simplify pass) from\n * `commands/gatePipeline.ts`, which re-exports it so `add.ts`/`scan.ts`'s\n * existing import path keeps working.\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","/*\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, renameSync, rmSync, 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\n/** How this entry was produced, when it isn't a normal `add`-sourced external install. Absent means a normal entry. 'evolve' (v0.7): self-evolved via `skill-forge evolve` (SkillOpt-Sleep) and already re-gated at adopt time. */\nexport type QueueEntryOrigin = 'evolve';\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 /** How this entry was produced; optional — absent means a normal `add`-sourced entry. See QueueEntryOrigin. */\n origin?: QueueEntryOrigin;\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\n/**\n * Writes the queue atomically: serialize to a temp file in the same directory (so the rename\n * below is same-filesystem and therefore atomic), then `renameSync` it over the real path. A\n * reader can never observe a partially-written queue.json, and a crash mid-write leaves the\n * previous queue.json intact plus an orphaned temp file rather than a corrupted one. The temp\n * file is cleaned up on any write failure.\n */\nfunction writeQueue(queue: Queue): void {\n const home = getHomeDir();\n mkdirSync(home, { recursive: true }); // recursive mkdir is a no-op when the dir exists\n const finalPath = getQueuePath();\n const tempPath = join(home, `.queue.json.tmp-${randomUUID()}`);\n try {\n writeFileSync(tempPath, JSON.stringify(queue, null, 2) + '\\n', 'utf-8');\n renameSync(tempPath, finalPath);\n } catch (err) {\n rmSync(tempPath, { force: true });\n throw err;\n }\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 * Which bundled `assets/` file to hand off — passed through to `resolvePromptPath()`.\n * Defaults to `'ingest-prompt.md'` (unchanged for `add --ingest`). `skill-forge audit`'s\n * `--handoff` passes `'curation-prompt.md'` instead (v0.8).\n */\n promptAsset?: string;\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 * Sink for the \"no agent available\" / \"binary not found on PATH\" print-only messages this\n * function itself emits (NOT the spawned child's own stdio, which `stdio` above controls).\n * Defaults to `console.log`. `skill-forge audit --json --handoff` passes `console.error` here\n * so a print-only message can never land on stdout after the one JSON document already\n * written there (v0.8 fix) — `add --ingest`'s call site is unaffected, still defaulting to\n * `console.log`.\n */\n log?: (message: string) => void;\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 a bundled handoff prompt asset (default `ingest-prompt.md`; `skill-forge\n * audit --handoff` passes `curation-prompt.md` — v0.8), resolved relative to this module so it\n * works both from source (tests run against `src/`) and from 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/<assetName>`\n * sitting next to the bundled `dist/cli.js` — see tsup.config.ts. The dev/\n * test layout is the repo's own `assets/<assetName>`, one level up from\n * `src/`.\n */\nexport function resolvePromptPath(assetName: string = 'ingest-prompt.md'): string {\n const here = dirname(fileURLToPath(import.meta.url));\n const builtCandidate = join(here, assetName);\n if (existsSync(builtCandidate)) return builtCandidate;\n return join(here, '..', 'assets', assetName);\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(deps.promptAsset);\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, promptAsset?: string): string {\n const promptPath = resolvePromptPath(promptAsset);\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 const log = options.log ?? console.log;\n\n if (command.source === 'none' || command.argv.length === 0) {\n const printed = printOnlyMessage(installedPath, options.promptAsset);\n 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, options.promptAsset);\n log(`[skill-forge] ${bin} not found on PATH — run this yourself:`);\n 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';\nimport { auditCommand } from './audit.js';\nimport type { AuditCommandOptions } from './audit.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 * When explicitly `false`, suppresses the interactive \"Run the skills & MCP audit now?\" tail\n * offer entirely — no question is asked and this run of `init` never calls `runAudit` itself.\n * Set only by `maybeOfferInit`'s call from `auditCommand` (v0.8 fix): that caller is already\n * about to run the single audit itself once `init()` returns, so init's own tail-audit offer\n * must stay silent — otherwise the audit (and its interactive business-profile Q&A) runs\n * twice. Direct `skill-forge init` (default/omitted, i.e. `true`) is unaffected.\n */\n offerAudit?: boolean;\n}\n\nexport interface InitDeps {\n /** Injected audit runner — init's final phase (v0.8) is `skill-forge audit`. Defaults to the real `auditCommand`; overridable so init tests don't have to run a full audit end-to-end. */\n runAudit?: (options?: AuditCommandOptions) => Promise<void>;\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 = {}, deps: InitDeps = {}): Promise<void> {\n const runAudit = deps.runAudit ?? auditCommand;\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 // Non-interactive final phase (v0.8): report written, no profile Q&A, no foundation, no handoff.\n await runAudit({ yes: true });\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 let runAuditNow = false;\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\n if (options.offerAudit !== false) {\n const answer = (await rl.question('\\nRun the skills & MCP audit now? [Y/n] ')).trim().toLowerCase();\n runAuditNow = answer === '' || answer === 'y' || answer === 'yes';\n }\n } finally {\n // Closed BEFORE any audit run below — auditCommand's own interactive prompts open a fresh\n // readline interface over the same stdin, and two concurrent interfaces would race on it.\n rl.close();\n }\n\n if (runAuditNow) {\n await runAudit();\n }\n}\n\nexport interface OfferInitOptions {\n json?: boolean;\n yes?: boolean;\n /**\n * Set by `auditCommand`'s own call (v0.8 fix): if the user accepts this offer, the `init` run\n * it launches must not ALSO offer/run its own tail audit — `auditCommand` is already the\n * single audit run that will happen once `init()` returns here.\n */\n suppressAuditOffer?: 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(\n '[skill-forge] No config found — run `skill-forge init` to detect your agents and set gate targets ' +\n '(init finishes with a `skill-forge audit` health check over what it just configured).'\n );\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(options.suppressAuditOffer ? { offerAudit: false } : {});\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 { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, join, resolve as resolvePath } from 'node:path';\nimport { createInterface, type Interface } from 'node:readline/promises';\nimport { auditConfiguredSet, renderAuditMarkdown, scaffoldFoundationSkill, type AuditReport } from '../audit.js';\nimport { getConfigPath, getHomeDir, loadConfig, saveConfig, type BusinessProfile, type Config } from '../config.js';\nimport { logOutcome } from '../decision.js';\nimport { UserError } from '../errors.js';\nimport { runHandoff } from '../handoff.js';\nimport { maybeOfferInit } from './init.js';\n\n/** Bundled agent-side curation prompt handed off by `--handoff` — see assets/curation-prompt.md and src/handoff.ts's `resolvePromptPath`. */\nconst CURATION_PROMPT_ASSET = 'curation-prompt.md';\n\nexport interface AuditCommandOptions {\n json?: boolean;\n yes?: boolean;\n /** Overrides the default `~/.skill-forge/reports/audit-<ISO-ts>.md` path. */\n report?: string;\n /** Scaffold a business-foundation skill after the audit — same effect as answering yes to the interactive prompt. */\n foundation?: boolean;\n /** Hand the report off to a coding agent after the audit — same effect as answering yes to the interactive prompt. */\n handoff?: boolean;\n}\n\nfunction splitCsv(s: string): string[] {\n return s\n .split(',')\n .map((x) => x.trim())\n .filter(Boolean);\n}\n\n/** Asks `prompt` and returns the trimmed, lowercased answer — the mechanical part shared by this file's four y/n prompts; each call site keeps its own exact comparison/default logic. */\nasync function askAnswer(rl: Interface, prompt: string): Promise<string> {\n return (await rl.question(prompt)).trim().toLowerCase();\n}\n\n/**\n * Interactive business-profile capture — offers to reuse an existing stored profile (never\n * re-invents one silently), warns against entering credentials/confidential data before asking\n * anything, and shows a summarized confirm before returning. Returns `existing` unchanged if the\n * user declines to reuse-or-replace it (including on a fresh capture the user doesn't confirm).\n */\nasync function captureBusinessProfile(rl: Interface, existing: BusinessProfile | undefined): Promise<BusinessProfile | undefined> {\n console.log('\\n[skill-forge] Business profile — grounds the business-foundation skill scaffold and the curation handoff prompt.');\n console.log(\n '[skill-forge] Do not enter credentials, API keys, tokens, or confidential customer data — this is stored in plain text in config.json.'\n );\n\n if (existing) {\n const keep = await askAnswer(rl, `Reuse the existing business profile (captured ${existing.updatedAt})? [Y/n] `);\n if (keep === '' || keep === 'y' || keep === 'yes') return existing;\n }\n\n const businessName = (await rl.question('Business name (optional, Enter to skip): ')).trim() || undefined;\n const industry = (await rl.question('Industry (optional, Enter to skip): ')).trim() || undefined;\n const audiences = splitCsv(await rl.question('Audiences (comma-separated, optional): '));\n const workflows = splitCsv(await rl.question('Workflows (comma-separated, optional): '));\n const constraints = splitCsv(await rl.question('Constraints (comma-separated, optional): '));\n\n const profile: BusinessProfile = { businessName, industry, audiences, workflows, constraints, updatedAt: new Date().toISOString() };\n\n console.log('\\n[skill-forge] Business profile summary:');\n console.log(` Business name: ${profile.businessName ?? '(none)'}`);\n console.log(` Industry: ${profile.industry ?? '(none)'}`);\n console.log(` Audiences: ${profile.audiences.join(', ') || '(none)'}`);\n console.log(` Workflows: ${profile.workflows.join(', ') || '(none)'}`);\n console.log(` Constraints: ${profile.constraints.join(', ') || '(none)'}`);\n const confirm = await askAnswer(rl, 'Save this business profile? [Y/n] ');\n if (confirm === 'n' || confirm === 'no') return existing;\n\n return profile;\n}\n\nfunction resolveReportPath(reportOption: string | undefined): string {\n if (reportOption) return resolvePath(reportOption);\n const iso = new Date().toISOString().replace(/[:.]/g, '-');\n return join(getHomeDir(), 'reports', `audit-${iso}.md`);\n}\n\n/** Writes the report exclusively (`wx` + 0600) — never overwrites an existing file at that path. */\nfunction writeReport(reportPath: string, contents: string): void {\n mkdirSync(dirname(reportPath), { recursive: true });\n try {\n writeFileSync(reportPath, contents, { flag: 'wx', mode: 0o600 });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'EEXIST') {\n throw new UserError(`report file already exists: ${reportPath} — pass a different --report path`);\n }\n throw err;\n }\n}\n\nfunction printTerminalSummary(report: AuditReport, reportPath: string): void {\n const { summary } = report;\n console.log('[skill-forge] audit complete');\n console.log(` skill roots: ${summary.rootCount} skills: ${summary.skillCount} MCP targets: ${summary.mcpTargetCount}`);\n console.log(\n ` findings: ${summary.findingCounts.CRITICAL} CRITICAL, ${summary.findingCounts.HIGH} HIGH, ` +\n `${summary.findingCounts.MEDIUM} MEDIUM, ${summary.findingCounts.LOW} LOW`\n );\n console.log(` overlap clusters: ${summary.overlapClusterCount} evolve-eligible: ${summary.evolveEligibleCount}`);\n console.log(` report -> ${reportPath}`);\n for (const notice of report.notices) console.log(` [notice] ${notice}`);\n console.log(\n ' next steps: `skill-forge add <source>` to install something new; ' +\n '`skill-forge evolve <skill-dir>` to refine an evolve-eligible skill.'\n );\n}\n\nasync function maybeScaffoldFoundation(\n config: Config,\n report: AuditReport,\n options: AuditCommandOptions,\n rl: Interface | undefined\n): Promise<Config> {\n let wantFoundation = options.foundation ?? false;\n\n if (!wantFoundation && rl && config.businessProfile && report.opportunities.foundation.available) {\n const answer = await askAnswer(rl, '\\nCreate a business-foundation skill now? [y/N] ');\n wantFoundation = answer === 'y' || answer === 'yes';\n }\n if (!wantFoundation) return config;\n\n if (!config.businessProfile) {\n throw new UserError(\n '--foundation requires a business profile — run `skill-forge audit` interactively first to capture one.'\n );\n }\n\n const targetRoot = config.defaultTarget ?? config.skillsRoots[0];\n if (!targetRoot) {\n throw new UserError('cannot scaffold a foundation skill: no skills root configured.');\n }\n\n const foundationPath = await scaffoldFoundationSkill(config, config.businessProfile, targetRoot);\n const updated: Config = { ...config, foundationSkillPath: foundationPath };\n saveConfig(updated);\n logOutcome(`[skill-forge] scaffolded business-foundation skill -> ${foundationPath}`, options.json);\n return updated;\n}\n\nasync function maybeHandoff(config: Config, reportPath: string, options: AuditCommandOptions, rl: Interface | undefined): Promise<void> {\n let wantHandoff = options.handoff ?? false;\n\n if (!wantHandoff && rl) {\n const answer = await askAnswer(rl, '\\nHand the curation report to a coding agent now? [y/N] ');\n wantHandoff = answer === 'y' || answer === 'yes';\n }\n if (!wantHandoff) return;\n\n // In --json mode, stdout must stay exactly one JSON document (already printed above) — route\n // runHandoff's own print-only messaging (no agent found / configured binary missing) to\n // stderr instead of its default stdout sink (v0.8 fix).\n runHandoff(config, reportPath, { promptAsset: CURATION_PROMPT_ASSET, log: options.json ? console.error : console.log });\n}\n\n/**\n * `skill-forge audit` (alias `doctor`) — a re-runnable, doctor-style health check over the\n * configured skill/MCP set: inventory, hygiene/safety findings, and consolidation/refinement\n * opportunities (src/audit.ts). Requires a real, persisted config — never silently audits an\n * invented default. `--foundation`/`--handoff` are separate, explicit opt-ins: `--yes`/`--json`\n * never imply either, matching the plan's \"agent handoff and foundation writes are opt-in\" hard\n * invariant.\n */\nexport async function auditCommand(options: AuditCommandOptions = {}): Promise<void> {\n // suppressAuditOffer: true — if this offers and runs `init`, init must NOT also offer/run its\n // own tail audit (that would run the whole interactive profile Q&A + report a second time);\n // this function is already the single audit run that happens once init returns (v0.8 fix).\n await maybeOfferInit({ json: options.json, yes: options.yes, suppressAuditOffer: true });\n\n if (!existsSync(getConfigPath())) {\n throw new UserError(\n 'no skill-forge config found — run `skill-forge init` first (audit requires a configured skill/MCP set; it never audits an invented default).'\n );\n }\n\n let config = loadConfig();\n const interactive = !options.json && !options.yes && Boolean(process.stdin.isTTY);\n\n const rl = interactive ? createInterface({ input: process.stdin, output: process.stdout }) : undefined;\n try {\n if (rl) {\n const profile = await captureBusinessProfile(rl, config.businessProfile);\n if (profile !== config.businessProfile) {\n config = { ...config, businessProfile: profile };\n saveConfig(config);\n }\n }\n\n const report = await auditConfiguredSet(config);\n\n const reportPath = resolveReportPath(options.report);\n writeReport(reportPath, renderAuditMarkdown(report));\n\n if (options.json) {\n console.log(JSON.stringify(report, null, 2));\n } else {\n printTerminalSummary(report, reportPath);\n }\n\n config = await maybeScaffoldFoundation(config, report, options, rl);\n await maybeHandoff(config, reportPath, options, rl);\n } finally {\n rl?.close();\n }\n}\n","import {\n existsSync,\n lstatSync,\n mkdtempSync,\n readFileSync,\n readdirSync,\n realpathSync,\n renameSync,\n rmSync,\n statSync,\n writeFileSync,\n type Dirent,\n} from 'node:fs';\nimport { basename, delimiter, extname, join, resolve as resolvePath } from 'node:path';\nimport type { BusinessProfile, Config } from './config.js';\nimport { UserError } from './errors.js';\nimport { scanSafety } from './gate/safety.js';\nimport { scanMcpFindings } from './gate/mcpSafety.js';\nimport { scanOverlapPaths, suggestVerbCode, type OverlapPathPair, type SuggestedVerbCode } from './gate/overlap.js';\nimport {\n collectNpxPackageSpecs,\n formatSafetyFinding,\n isNpxCommand,\n type SafetyFinding,\n type Severity,\n type Verdict,\n} from './gate/scanShared.js';\nimport { proFeatureStatus } from './license.js';\nimport { appendProvenance } from './provenance.js';\nimport { expandHome, isDir, stripQuotes } from './util.js';\n\n/**\n * `skill-forge audit` engine (v0.8, MIT, side-effect-free except\n * `scaffoldFoundationSkill`) — a doctor-style health check over the\n * EXISTING configured skill roots and MCP targets: inventory, static\n * hygiene/safety findings, and consolidation/refinement opportunities. The\n * decide/absorb judgment itself is handed to the user's coding agent via the\n * bundled `assets/curation-prompt.md` (see src/handoff.ts) — this module\n * only produces the static evidence, never a verdict on what to actually do.\n *\n * Nothing scanned by this module is ever executed. `auditConfiguredSet` only\n * reads; `scaffoldFoundationSkill` is the one function here that writes, and\n * only under an explicit caller opt-in (see src/commands/audit.ts).\n */\n\n/** Threshold above which a SKILL.md's estimated token count becomes an `oversized-skill` finding. */\nexport const OVERSIZED_SKILL_EST_TOKENS = 5_000;\n\nexport interface AuditOptions {\n /** Overrides gate/overlap.ts's default cross-root overlap threshold (0.45). */\n overlapThreshold?: number;\n /** Overrides OVERSIZED_SKILL_EST_TOKENS for the oversized-skill finding. */\n oversizedSkillTokens?: number;\n /** Injectable clock for deterministic `generatedAt` in tests. Defaults to `() => new Date()`. */\n now?: () => Date;\n /** Injectable PATH lookup for the tooling section (`skillspector` detection). Defaults to a real PATH scan. */\n which?: (cmd: string) => string | undefined;\n /** Injectable environment for the tooling section (VERCEL_OIDC_TOKEN / SKILLSPECTOR_PROVIDER / provider key presence). Defaults to `process.env`. */\n env?: NodeJS.ProcessEnv;\n}\n\nexport interface AuditedRoot {\n /** The root exactly as configured (may contain `~`). */\n root: string;\n /** Resolved realpath, or the configured root itself when it doesn't exist / can't be resolved. */\n realPath: string;\n exists: boolean;\n skillCount: number;\n}\n\nexport interface AuditedSkill {\n name: string;\n /** Canonical (first-seen, realpath-deduped) skill directory. */\n path: string;\n skillMdPath: string;\n /** The configured root this skill was discovered under. */\n root: string;\n /** Other configured-root paths (symlink aliases) that resolve to this same physical skill. */\n aliasPaths: string[];\n validFrontmatter: boolean;\n description?: string;\n sizeBytes: number;\n estTokens: number;\n}\n\nexport interface AuditedMcpServer {\n name: string;\n commandBasename?: string;\n packageSpec?: string;\n argCount: number;\n envCount: number;\n}\n\nexport type AuditedMcpTargetStatus = 'clean' | 'missing' | 'unreadable' | 'malformed';\n\nexport interface AuditedMcpTarget {\n path: string;\n format: 'json' | 'toml';\n status: AuditedMcpTargetStatus;\n /** Server enumeration — JSON targets only; always empty for TOML (structured enumeration unsupported) or a non-clean status. */\n servers: AuditedMcpServer[];\n}\n\nexport interface AuditFinding {\n severity: Severity;\n rule: string;\n target: string;\n detail: string;\n}\n\n/** Reshapes gate/scanShared.ts's `{severity,rule,file,detail}` SafetyFinding into this module's `{severity,rule,target,detail}` AuditFinding — shared by the per-skill safety scan and the MCP target scan. */\nfunction toAuditFindings(source: SafetyFinding[]): AuditFinding[] {\n return source.map((f) => ({ severity: f.severity, rule: f.rule, target: f.file, detail: f.detail }));\n}\n\nexport interface OverlapCluster {\n /** SKILL.md paths in this connected component, sorted for determinism — always at least 2 (a cluster only exists when at least one scored pair formed it). */\n members: [string, string, ...string[]];\n topScore: number;\n /** Bare verb code (`suggestVerbCode`'s typed union) — markdown renders it as-is; prose belongs to the agent-side curation prompt, not this report. */\n suggestedVerb: SuggestedVerbCode;\n}\n\nexport interface EvolveCandidate {\n name: string;\n path: string;\n}\n\n/** Discriminated on `available`: the `false` branch always carries the existing path (never optional-and-absent by accident). */\nexport type FoundationOpportunity =\n | { available: true; message: string }\n | { available: false; existingPath: string; message: string };\n\nexport interface AuditSummary {\n rootCount: number;\n skillCount: number;\n mcpTargetCount: number;\n findingCounts: Record<Severity, number>;\n overlapClusterCount: number;\n evolveEligibleCount: number;\n}\n\nexport interface AuditInventory {\n skillRoots: AuditedRoot[];\n skills: AuditedSkill[];\n mcpTargets: AuditedMcpTarget[];\n}\n\nexport interface AuditOpportunities {\n overlapClusters: OverlapCluster[];\n evolveEligible: EvolveCandidate[];\n foundation: FoundationOpportunity;\n /** True when the Pro overlap gate was locked for this run (see `notices` for the upgrade notice) — lets JSON consumers branch on this instead of string-matching `notices`. */\n overlapLocked: boolean;\n}\n\n/** `skillspector` CLI (deep local safety scan, github.com/NVIDIA/SkillSpector) readiness — see gate/safety.ts's own PATH-based `scanWithSkillSpector`, which this only reports on, never invokes. */\nexport interface AuditToolingSkillspector {\n installed: boolean;\n /** Setup guidance, present only when `installed` is false. */\n hint?: string;\n}\n\n/** `VERCEL_OIDC_TOKEN` presence — the auth anchor for skills.sh discovery/partner audits (`skill-forge find`). Presence only; the token value is never read into a report. */\nexport interface AuditToolingSkillsShToken {\n present: boolean;\n /** Setup guidance, present only when `present` is false. */\n hint?: string;\n}\n\n/** Optional SkillSpector LLM (semantic) stage readiness — static scanning works without it. Provider NAME only; the associated API key's value is never read into a report. */\nexport interface AuditToolingSkillspectorLlm {\n configured: boolean;\n provider?: string;\n}\n\nexport interface AuditTooling {\n skillspector: AuditToolingSkillspector;\n skillsShToken: AuditToolingSkillsShToken;\n skillspectorLlm: AuditToolingSkillspectorLlm;\n}\n\nexport interface AuditReport {\n schemaVersion: 1;\n generatedAt: string;\n inventory: AuditInventory;\n hygiene: AuditFinding[];\n opportunities: AuditOpportunities;\n /** Skill/MCP tooling readiness (skillspector CLI, skills.sh auth, optional SkillSpector LLM stage) — port of skill_doctor.py. Additive (v0.8.1); absent only on a pre-existing report object built by hand rather than by `auditConfiguredSet`. */\n tooling?: AuditTooling;\n notices: string[];\n summary: AuditSummary;\n}\n\n// ---------------------------------------------------------------------------\n// Inventory: symlink-aware skill discovery\n// ---------------------------------------------------------------------------\n\n/** Resolves a configured root (which may contain `~`) to an absolute filesystem path, without probing existence. */\nfunction resolveConfiguredRoot(root: string): string {\n return resolvePath(expandHome(root));\n}\n\n/** `statSync` that follows symlinks and swallows any error (missing/broken/permission) rather than throwing. */\nfunction safeStat(p: string) {\n try {\n return statSync(p);\n } catch {\n return undefined;\n }\n}\n\nfunction isDirFollowingSymlink(p: string): boolean {\n return safeStat(p)?.isDirectory() ?? false;\n}\n\n/**\n * Subdirectories of `dir`, including symlinked ones that resolve to a directory. Deliberately\n * does NOT rely on `Dirent#isDirectory()` alone — for a symlink entry, `readdir`'s dirent type\n * reflects the link itself (DT_LNK), not what it points at, so a symlinked skills root or skill\n * dir would otherwise be silently skipped (the same lesson noted on gate/overlap.ts's `find`-based\n * walker, which has the identical gap).\n */\nfunction listSubdirsFollowingSymlinks(dir: string): string[] {\n let entries: Dirent[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n const out: string[] = [];\n for (const entry of entries) {\n const full = join(dir, entry.name);\n if (entry.isDirectory() || (entry.isSymbolicLink() && isDirFollowingSymlink(full))) {\n out.push(full);\n }\n }\n return out;\n}\n\n/**\n * Same shape as `listSubdirsFollowingSymlinks`, but does NOT follow symlinked entries — only\n * real directories (`Dirent#isDirectory()`, no stat-follow). Used exclusively by\n * `findSkillDirsUnderRoot`'s third (nested `topDir/skills/*`) discovery loop, to close a\n * symlink-escape path: a malicious INSTALLED skill (itself a legitimate top-level entry under a\n * configured root) could otherwise plant its own `skills/<name>` symlink pointing outside the\n * root, and have whatever SKILL.md lives at that external destination silently absorbed into\n * this root's inventory — audited and disclosed into reports/handoffs as if it belonged to the\n * configured root. Top-level (`root/*`) and `root/skills/*` discovery deliberately keep\n * following symlinks — legitimately symlinked skill dirs at those levels are a primary use case.\n */\nfunction listSubdirsNoSymlinks(dir: string): string[] {\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/** Same three glob shapes as gate/overlap.ts's `findSkillMdFiles`, but symlink-aware (see `listSubdirsFollowingSymlinks`) and returning skill DIRECTORIES, not SKILL.md paths. */\nfunction findSkillDirsUnderRoot(root: string): string[] {\n const found = new Set<string>();\n const rootSubdirs = listSubdirsFollowingSymlinks(root);\n\n for (const dir of rootSubdirs) {\n if (existsSync(join(dir, 'SKILL.md'))) found.add(dir);\n }\n for (const dir of listSubdirsFollowingSymlinks(join(root, 'skills'))) {\n if (existsSync(join(dir, 'SKILL.md'))) found.add(dir);\n }\n for (const topDir of rootSubdirs) {\n // Nested loop: does NOT follow symlinked entries — see listSubdirsNoSymlinks's doc comment.\n for (const dir of listSubdirsNoSymlinks(join(topDir, 'skills'))) {\n if (existsSync(join(dir, 'SKILL.md'))) found.add(dir);\n }\n }\n\n return Array.from(found).filter((p) => !p.includes('/.git/'));\n}\n\ninterface SkillCandidate {\n /** Path as discovered by the shape-walk above — may traverse through a symlink. */\n configuredPath: string;\n /** The configured root (raw, as given in config.skillsRoots) this candidate was found under. */\n root: string;\n}\n\ninterface RootMeta {\n exists: boolean;\n realPath?: string;\n}\n\n/** Probes every configured root and collects every skill-dir candidate found under it (pre-dedupe). */\nfunction collectSkillCandidates(roots: readonly string[]): {\n candidates: SkillCandidate[];\n rootMeta: Map<string, RootMeta>;\n} {\n const candidates: SkillCandidate[] = [];\n const rootMeta = new Map<string, RootMeta>();\n\n for (const root of roots) {\n const resolvedRoot = resolveConfiguredRoot(root);\n const exists = isDir(resolvedRoot);\n let realPath: string | undefined;\n if (exists) {\n try {\n realPath = realpathSync(resolvedRoot);\n } catch {\n realPath = undefined;\n }\n }\n rootMeta.set(root, { exists, realPath });\n if (!exists) continue;\n\n for (const dir of findSkillDirsUnderRoot(resolvedRoot)) {\n candidates.push({ configuredPath: dir, root });\n }\n }\n\n return { candidates, rootMeta };\n}\n\ninterface ResolvedSkillGroup {\n realPath: string;\n /** First configured path seen for this physical skill — the canonical location reported. */\n canonicalPath: string;\n root: string;\n /** Other configured-root paths (symlink aliases) resolving to the same realPath. */\n aliasPaths: string[];\n}\n\n/**\n * Dedupes skill candidates by `realpathSync` so the same physical skill reached via two roots\n * (or a symlinked alias within one root) is reported once, with alias locations retained rather\n * than dropped. A candidate whose realpath can't be resolved (broken symlink, or an `ELOOP`\n * self-referential symlink cycle) becomes an `unreadable-skill` finding and is skipped, rather\n * than aborting the whole audit.\n */\nfunction dedupeSkillCandidates(candidates: SkillCandidate[], findings: AuditFinding[]): ResolvedSkillGroup[] {\n const byRealPath = new Map<string, ResolvedSkillGroup>();\n const order: string[] = [];\n\n for (const candidate of candidates) {\n let realPath: string;\n try {\n realPath = realpathSync(candidate.configuredPath);\n } catch (err) {\n findings.push({\n severity: 'MEDIUM',\n rule: 'unreadable-skill',\n target: candidate.configuredPath,\n detail: `could not resolve skill directory (broken symlink or cycle): ${(err as Error).message}`,\n });\n continue;\n }\n\n const existing = byRealPath.get(realPath);\n if (existing) {\n if (existing.canonicalPath !== candidate.configuredPath && !existing.aliasPaths.includes(candidate.configuredPath)) {\n existing.aliasPaths.push(candidate.configuredPath);\n }\n continue;\n }\n\n byRealPath.set(realPath, { realPath, canonicalPath: candidate.configuredPath, root: candidate.root, aliasPaths: [] });\n order.push(realPath);\n }\n\n return order.map((realPath) => byRealPath.get(realPath)!);\n}\n\n// ---------------------------------------------------------------------------\n// Strict frontmatter validation — deliberately NOT gate/overlap.ts's readFrontmatter,\n// whose dirname/heading fallbacks exist for scoring robustness, not validation.\n// ---------------------------------------------------------------------------\n\ninterface StrictFrontmatterResult {\n valid: boolean;\n name?: string;\n description?: string;\n errors: string[];\n}\n\n/** Strict frontmatter parse: a missing/unclosed `---` fence or a missing/empty `name`/`description` field is a finding, never silently backfilled. */\nfunction parseStrictFrontmatter(text: string): StrictFrontmatterResult {\n if (!text.startsWith('---\\n')) {\n return { valid: false, errors: ['missing frontmatter fence (SKILL.md must start with \"---\")'] };\n }\n\n const afterOpen = text.slice(4);\n const closeIdx = afterOpen.indexOf('\\n---');\n if (closeIdx === -1) {\n return { valid: false, errors: ['unclosed frontmatter fence (no closing \"---\" found)'] };\n }\n const block = afterOpen.slice(0, closeIdx);\n const errors: string[] = [];\n\n const nameMatch = block.match(/^name:\\s*(.+)$/m);\n const name = nameMatch ? stripQuotes(nameMatch[1].trim()) : undefined;\n if (!name) errors.push('missing required \"name\" field in frontmatter');\n\n const descMatch = block.match(/^description:\\s*(.*)$/m);\n let description: string | undefined;\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)) buf.push(l.trim());\n else if (l.trim() === '') continue;\n else break;\n }\n }\n description = buf.join(' ').trim() || undefined;\n } else {\n description = stripQuotes(first);\n }\n }\n if (!description) errors.push('missing required \"description\" field in frontmatter');\n\n return { valid: errors.length === 0, name, description, errors };\n}\n\n/** Ceil(byteLength/4) — a rough, always-labeled-\"est.\" token estimate. Shared by the hygiene oversized check and the report renderer. */\nexport function estimateTokens(text: string): number {\n return Math.ceil(Buffer.byteLength(text, 'utf-8') / 4);\n}\n\nfunction profileAuditedSkill(\n group: ResolvedSkillGroup,\n findings: AuditFinding[],\n unreadableSkillMdPaths: Set<string>\n): AuditedSkill {\n const skillMdPath = join(group.canonicalPath, 'SKILL.md');\n const base: Omit<AuditedSkill, 'name' | 'validFrontmatter' | 'description' | 'sizeBytes' | 'estTokens'> = {\n path: group.canonicalPath,\n skillMdPath,\n root: group.root,\n aliasPaths: group.aliasPaths,\n };\n\n let text: string;\n let sizeBytes: number;\n try {\n sizeBytes = statSync(skillMdPath).size;\n text = readFileSync(skillMdPath, 'utf-8');\n } catch (err) {\n findings.push({\n severity: 'MEDIUM',\n rule: 'unreadable-skill',\n target: skillMdPath,\n detail: `could not read SKILL.md: ${(err as Error).message}`,\n });\n // Excluded from the cross-root overlap scan below (see auditConfiguredSet) — that scanner's\n // readFrontmatter does an unguarded readFileSync, so feeding it a path we already know is\n // unreadable would abort the whole audit rather than degrade gracefully.\n unreadableSkillMdPaths.add(skillMdPath);\n return { ...base, name: basename(group.canonicalPath), validFrontmatter: false, sizeBytes: 0, estTokens: 0 };\n }\n\n const fm = parseStrictFrontmatter(text);\n if (!fm.valid) {\n for (const error of fm.errors) {\n findings.push({ severity: 'MEDIUM', rule: 'malformed-frontmatter', target: skillMdPath, detail: error });\n }\n }\n\n return {\n ...base,\n name: fm.name ?? basename(group.canonicalPath),\n validFrontmatter: fm.valid,\n description: fm.description,\n sizeBytes,\n estTokens: estimateTokens(text),\n };\n}\n\n// ---------------------------------------------------------------------------\n// MCP target inventory — JSON structured enumeration (names/basenames/specs/COUNTS\n// only, never env values or arbitrary arg values); TOML is textual-scan-only.\n// ---------------------------------------------------------------------------\n\ninterface RawMcpServerEntry {\n command?: unknown;\n args?: unknown;\n env?: unknown;\n}\n\n/** True when a parsed JSON document has an `mcpServers` or `servers` object — the shape `enumerateMcpServers` knows how to read. Shared with `auditMcpTarget`'s `unrecognized-mcp-target-shape` finding so the two can't drift on what counts as \"MCP-shaped\". */\nfunction hasRecognizedMcpShape(parsed: unknown): boolean {\n if (typeof parsed !== 'object' || parsed === null) return false;\n const obj = parsed as Record<string, unknown>;\n const serversRaw = obj.mcpServers ?? obj.servers;\n return typeof serversRaw === 'object' && serversRaw !== null;\n}\n\n/** Enumerates a parsed MCP config's `mcpServers`/`servers` map into name/command-basename/package-spec/COUNTS only — see AuditedMcpServer's doc comment on the report shape. */\nfunction enumerateMcpServers(parsed: unknown): AuditedMcpServer[] {\n if (!hasRecognizedMcpShape(parsed)) return [];\n const obj = parsed as Record<string, unknown>;\n const serversRaw = obj.mcpServers ?? obj.servers;\n\n const out: AuditedMcpServer[] = [];\n for (const [name, raw] of Object.entries(serversRaw as Record<string, unknown>)) {\n if (typeof raw !== 'object' || raw === null) continue;\n const entry = raw as RawMcpServerEntry;\n const command = typeof entry.command === 'string' ? entry.command : undefined;\n const args = Array.isArray(entry.args) ? entry.args.filter((a): a is string => typeof a === 'string') : [];\n const env = entry.env && typeof entry.env === 'object' && !Array.isArray(entry.env) ? (entry.env as Record<string, unknown>) : {};\n\n out.push({\n name,\n commandBasename: command ? basename(command) : undefined,\n packageSpec: command && isNpxCommand(command) ? collectNpxPackageSpecs(args).positionalSpec : undefined,\n argCount: args.length,\n envCount: Object.keys(env).length,\n });\n }\n return out;\n}\n\nfunction auditMcpTarget(targetPath: string, notices: string[]): { target: AuditedMcpTarget; findings: AuditFinding[] } {\n const findings: AuditFinding[] = [];\n const isToml = extname(targetPath).toLowerCase() === '.toml';\n const format: AuditedMcpTarget['format'] = isToml ? 'toml' : 'json';\n\n if (!existsSync(targetPath)) {\n findings.push({\n severity: 'LOW',\n rule: 'missing-mcp-target',\n target: targetPath,\n detail: 'configured MCP target file does not exist',\n });\n return { target: { path: targetPath, format, status: 'missing', servers: [] }, findings };\n }\n\n // Textual scan (unpinned npx, inline credentials, dangerous flags) runs for both\n // formats — it's the ONLY enumeration TOML gets; JSON also gets structured below.\n try {\n findings.push(...toAuditFindings(scanMcpFindings(targetPath)));\n } catch (err) {\n findings.push({\n severity: 'MEDIUM',\n rule: 'unreadable-mcp-target',\n target: targetPath,\n detail: `MCP safety scan failed: ${(err as Error).message}`,\n });\n }\n\n if (isToml) {\n // Explicit readability probe: scanMcpFindings above swallows read failures internally\n // (readTextFile catches and returns undefined -> no findings, no throw), so without this\n // check an unreadable TOML file would otherwise fall straight through to a hard-coded\n // 'clean' status below.\n try {\n readFileSync(targetPath, 'utf-8');\n } catch (err) {\n findings.push({\n severity: 'MEDIUM',\n rule: 'unreadable-mcp-target',\n target: targetPath,\n detail: `could not read: ${(err as Error).message}`,\n });\n return { target: { path: targetPath, format, status: 'unreadable', servers: [] }, findings };\n }\n\n notices.push(`MCP target ${targetPath}: TOML format — structured server enumeration unsupported (textual safety scan only)`);\n return { target: { path: targetPath, format, status: 'clean', servers: [] }, findings };\n }\n\n let raw: string;\n try {\n raw = readFileSync(targetPath, 'utf-8');\n } catch (err) {\n findings.push({\n severity: 'MEDIUM',\n rule: 'unreadable-mcp-target',\n target: targetPath,\n detail: `could not read: ${(err as Error).message}`,\n });\n return { target: { path: targetPath, format, status: 'unreadable', servers: [] }, findings };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n findings.push({\n severity: 'MEDIUM',\n rule: 'malformed-mcp-target',\n target: targetPath,\n detail: `invalid JSON: ${(err as Error).message}`,\n });\n return { target: { path: targetPath, format, status: 'malformed', servers: [] }, findings };\n }\n\n if (!hasRecognizedMcpShape(parsed)) {\n findings.push({\n severity: 'LOW',\n rule: 'unrecognized-mcp-target-shape',\n target: targetPath,\n detail: 'valid JSON but no \"mcpServers\" or \"servers\" object was found — reporting 0 servers',\n });\n }\n\n return { target: { path: targetPath, format, status: 'clean', servers: enumerateMcpServers(parsed) }, findings };\n}\n\n// ---------------------------------------------------------------------------\n// Opportunities: cross-root overlap clustering (Pro), evolve eligibility, foundation\n// ---------------------------------------------------------------------------\n\n/** Union-find connected-component clustering over overlap pairs already filtered to >= threshold by scanOverlapPaths. */\nfunction clusterOverlapPairs(pairs: OverlapPathPair[]): OverlapCluster[] {\n const parent = new Map<string, string>();\n\n function find(x: string): string {\n if (!parent.has(x)) parent.set(x, x);\n let root = x;\n while (parent.get(root) !== root) root = parent.get(root)!;\n let cur = x;\n while (parent.get(cur) !== root) {\n const next = parent.get(cur)!;\n parent.set(cur, root);\n cur = next;\n }\n return root;\n }\n\n function union(a: string, b: string): void {\n const ra = find(a);\n const rb = find(b);\n if (ra !== rb) parent.set(ra, rb);\n }\n\n for (const pair of pairs) union(pair.aPath, pair.bPath);\n\n const groups = new Map<string, { members: Set<string>; topScore: number }>();\n for (const pair of pairs) {\n const root = find(pair.aPath);\n const group = groups.get(root) ?? { members: new Set<string>(), topScore: 0 };\n group.members.add(pair.aPath);\n group.members.add(pair.bPath);\n group.topScore = Math.max(group.topScore, pair.score);\n groups.set(root, group);\n }\n\n return Array.from(groups.values())\n .map((g) => ({ members: toClusterMembers(g.members), topScore: g.topScore, suggestedVerb: suggestVerbCode(g.topScore) }))\n .sort((a, b) => b.topScore - a.topScore || a.members[0].localeCompare(b.members[0]));\n}\n\n/** Sorts and type-asserts a cluster's member-path set into the `[string, string, ...string[]]` tuple floor — safe because a cluster is only ever built from >= 1 scored pair (see `clusterOverlapPairs`), so it always has at least 2 distinct member paths. */\nfunction toClusterMembers(members: Set<string>): [string, string, ...string[]] {\n const sorted = Array.from(members).sort();\n if (sorted.length < 2) {\n throw new Error(`internal error: overlap cluster has fewer than 2 members (${sorted.length})`);\n }\n return sorted as [string, string, ...string[]];\n}\n\n// ---------------------------------------------------------------------------\n// Tooling readiness — port of skill_doctor.py (rhize-plugins). Read-only:\n// PATH lookup + environment-variable PRESENCE checks only. Never reads a\n// secret's value into the report or --json output — provider names and\n// booleans only.\n// ---------------------------------------------------------------------------\n\n/** Locate an executable on PATH without shelling out — mirrors gate/safety.ts's own (unexported) `which`, duplicated here rather than imported so audit.ts's tooling check stays a pure PATH/env read with no dependency on gate/safety.ts's internals. */\nfunction defaultWhich(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\n/** Maps a `SKILLSPECTOR_PROVIDER` value to the env var holding its API key — mirrors skill_doctor.py's `keymap`. */\nconst SKILLSPECTOR_PROVIDER_KEYMAP: Record<string, string> = {\n openai: 'OPENAI_API_KEY',\n anthropic: 'ANTHROPIC_API_KEY',\n nv_build: 'NVIDIA_INFERENCE_KEY',\n};\n\n/**\n * Port of skill_doctor.py's readiness checks: `skillspector` on PATH (deep local safety scan),\n * `VERCEL_OIDC_TOKEN` presence (skills.sh discovery/partner audits — presence only, deliberately\n * narrower than skill_doctor.py's additional `.env`/`.env.local` file grep, since this is a\n * side-effect-free audit engine that otherwise never reads arbitrary project files), and the\n * optional SkillSpector LLM stage (provider name + whether its key env var is set — never the\n * key's value).\n */\nfunction checkTooling(which: (cmd: string) => string | undefined, env: NodeJS.ProcessEnv): AuditTooling {\n const skillspectorPath = which('skillspector');\n const skillspector: AuditToolingSkillspector = skillspectorPath\n ? { installed: true }\n : {\n installed: false,\n hint: 'NOT installed — github.com/NVIDIA/skillspector (git clone + make install; Python 3.12+)',\n };\n\n const tokenPresent = Boolean(env.VERCEL_OIDC_TOKEN);\n const skillsShToken: AuditToolingSkillsShToken = tokenPresent\n ? { present: true }\n : {\n present: false,\n hint: 'missing — enable OIDC Federation on your Vercel project, then `vercel link && vercel env pull`',\n };\n\n const provider = env.SKILLSPECTOR_PROVIDER;\n const providerKeyEnv = provider ? SKILLSPECTOR_PROVIDER_KEYMAP[provider] : undefined;\n const skillspectorLlm: AuditToolingSkillspectorLlm = {\n configured: Boolean(provider && providerKeyEnv && env[providerKeyEnv]),\n provider: provider || undefined,\n };\n\n return { skillspector, skillsShToken, skillspectorLlm };\n}\n\n// ---------------------------------------------------------------------------\n// Top-level engine\n// ---------------------------------------------------------------------------\n\n/**\n * Side-effect-free audit of every configured skills root and MCP target: inventory, structural +\n * safety hygiene findings, and consolidation/refinement opportunities. Never executes anything it\n * scans. Per-item failures (unreadable skill, missing MCP target, malformed JSON, ...) become\n * findings and never abort the run.\n */\nexport async function auditConfiguredSet(config: Config, options: AuditOptions = {}): Promise<AuditReport> {\n const now = options.now ?? (() => new Date());\n const oversizedThreshold = options.oversizedSkillTokens ?? OVERSIZED_SKILL_EST_TOKENS;\n const findings: AuditFinding[] = [];\n const notices: string[] = [];\n\n // --- Inventory: skill roots + skills ---\n const { candidates, rootMeta } = collectSkillCandidates(config.skillsRoots);\n const groups = dedupeSkillCandidates(candidates, findings);\n const unreadableSkillMdPaths = new Set<string>();\n const skills = groups.map((g) => profileAuditedSkill(g, findings, unreadableSkillMdPaths));\n\n const skillRoots: AuditedRoot[] = config.skillsRoots.map((root) => {\n const meta = rootMeta.get(root) ?? { exists: false };\n if (!meta.exists) {\n findings.push({ severity: 'MEDIUM', rule: 'missing-skills-root', target: root, detail: 'configured skills root does not exist on disk' });\n } else if (!meta.realPath) {\n findings.push({\n severity: 'MEDIUM',\n rule: 'unreadable-skills-root',\n target: root,\n detail: 'could not resolve skills root to a real path (broken symlink or cycle)',\n });\n }\n return {\n root,\n realPath: meta.realPath ?? resolveConfiguredRoot(root),\n exists: meta.exists,\n skillCount: skills.filter((s) => s.root === root).length,\n };\n });\n\n // --- Hygiene: duplicate names across roots, oversized skills ---\n const byName = new Map<string, AuditedSkill[]>();\n for (const skill of skills) {\n const arr = byName.get(skill.name) ?? [];\n arr.push(skill);\n byName.set(skill.name, arr);\n }\n for (const [name, group] of byName) {\n if (group.length > 1) {\n // One finding per duplicate path (not one comma-joined finding) — keeps AuditFinding.target\n // single-path everywhere, matching every other finding this module emits.\n for (const skill of group) {\n const otherPaths = group.filter((g) => g.path !== skill.path).map((g) => g.path);\n findings.push({\n severity: 'MEDIUM',\n rule: 'duplicate-skill-name',\n target: skill.path,\n detail: `name \"${name}\" is duplicated — also found at ${otherPaths.join(', ')}`,\n });\n }\n }\n }\n for (const skill of skills) {\n if (skill.estTokens > oversizedThreshold) {\n findings.push({\n severity: 'LOW',\n rule: 'oversized-skill',\n target: skill.skillMdPath,\n detail: `est. ${skill.estTokens} tokens exceeds the ${oversizedThreshold}-token threshold — consider trimming or splitting`,\n });\n }\n }\n\n // --- Hygiene: per-skill safety scan (never aborts the run on a single failure) ---\n const safetyVerdictBySkillPath = new Map<string, Verdict>();\n for (const skill of skills) {\n try {\n const result = await scanSafety(skill.path, config.strictness);\n findings.push(...toAuditFindings(result.findings));\n safetyVerdictBySkillPath.set(skill.path, result.verdict);\n } catch (err) {\n findings.push({\n severity: 'MEDIUM',\n rule: 'unreadable-skill',\n target: skill.path,\n detail: `safety scan failed: ${(err as Error).message}`,\n });\n safetyVerdictBySkillPath.set(skill.path, 'block');\n }\n }\n\n // --- Inventory: MCP targets ---\n const mcpTargetPaths = config.mcpTargets ?? [];\n const mcpTargets: AuditedMcpTarget[] = [];\n for (const targetPath of mcpTargetPaths) {\n const { target, findings: targetFindings } = auditMcpTarget(targetPath, notices);\n mcpTargets.push(target);\n findings.push(...targetFindings);\n }\n\n // --- Opportunities: cross-root overlap clusters (Pro) ---\n const overlapStatus = proFeatureStatus('installed-set overlap analysis');\n let overlapClusters: OverlapCluster[] = [];\n if (overlapStatus.enabled) {\n if (overlapStatus.notice) notices.push(overlapStatus.notice);\n try {\n // Excludes any SKILL.md path already known unreadable (see profileAuditedSkill) —\n // scanOverlapPaths's readFrontmatter does an unguarded readFileSync, so feeding it an\n // unreadable path would otherwise throw and abort the whole audit with no report.\n const readableSkillMdPaths = skills.map((s) => s.skillMdPath).filter((p) => !unreadableSkillMdPaths.has(p));\n const pairs = scanOverlapPaths(readableSkillMdPaths, options.overlapThreshold);\n overlapClusters = clusterOverlapPairs(pairs);\n } catch (err) {\n findings.push({\n severity: 'MEDIUM',\n rule: 'overlap-scan-failed',\n target: 'installed-set overlap analysis',\n detail: `cross-root overlap scan failed: ${(err as Error).message}`,\n });\n notices.push(`cross-root overlap scan failed — overlap clusters unavailable this run: ${(err as Error).message}`);\n overlapClusters = [];\n }\n } else {\n notices.push(overlapStatus.notice ?? 'Pro feature (installed-set overlap analysis) locked.');\n }\n\n // --- Opportunities: evolve-eligible (structurally valid + a clean safety verdict) ---\n const evolveEligible: EvolveCandidate[] = skills\n .filter((s) => s.validFrontmatter && safetyVerdictBySkillPath.get(s.path) === 'pass')\n .map((s) => ({ name: s.name, path: s.path }));\n\n // --- Opportunities: foundation skill ---\n const foundation: FoundationOpportunity = config.foundationSkillPath\n ? {\n available: false,\n existingPath: config.foundationSkillPath,\n message: `a business-foundation skill already exists at ${config.foundationSkillPath}`,\n }\n : {\n available: true,\n message: 'no business-foundation skill yet — `skill-forge audit --foundation` can scaffold one to ground future skills in your business profile',\n };\n\n // --- Summary ---\n const findingCounts: Record<Severity, number> = { LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0 };\n for (const f of findings) findingCounts[f.severity]++;\n\n const summary: AuditSummary = {\n rootCount: skillRoots.length,\n skillCount: skills.length,\n mcpTargetCount: mcpTargets.length,\n findingCounts,\n overlapClusterCount: overlapClusters.length,\n evolveEligibleCount: evolveEligible.length,\n };\n\n // --- Tooling readiness (skill_doctor.py port) ---\n const tooling = checkTooling(options.which ?? defaultWhich, options.env ?? process.env);\n\n return {\n schemaVersion: 1,\n generatedAt: now().toISOString(),\n inventory: { skillRoots, skills, mcpTargets },\n hygiene: findings,\n opportunities: { overlapClusters, evolveEligible, foundation, overlapLocked: !overlapStatus.enabled },\n tooling,\n notices,\n summary,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Markdown report rendering\n// ---------------------------------------------------------------------------\n\nfunction bulletList(items: string[], emptyPlaceholder: string): string[] {\n return items.length > 0 ? items.map((i) => `- ${i}`) : [`- ${emptyPlaceholder}`];\n}\n\n/**\n * Strips C0 (0x00-0x1F) and C1 (0x7F-0x9F) control characters from an untrusted string before\n * it's interpolated into markdown report output — these are single-line interpolations, so\n * nothing needs to replace what's stripped. Guards against e.g. an installed skill's name or an\n * MCP server's name/command/package-spec carrying an embedded ANSI escape sequence that would\n * otherwise render raw into a terminal later `cat`ing the report. JSON output (`--json`) is\n * untouched — `JSON.stringify` already escapes control characters. Exported for reuse by every\n * command that renders untrusted inventory/ledger/API strings into human-readable output\n * (organize, find, watch, ingest).\n */\nexport function sanitizeForMarkdown(s: string): string {\n // eslint-disable-next-line no-control-regex -- intentional: stripping C0/C1 control chars\n return s.replace(/[\\x00-\\x1F\\x7F-\\x9F]/g, '');\n}\n\n/** Comma-joins the defined/truthy parts of a list of optional strings — shared by the skills and MCP-targets sections' inline detail summaries. */\nfunction joinDefined(parts: (string | undefined)[]): string {\n return parts.filter((v): v is string => Boolean(v)).join(', ');\n}\n\nfunction renderRootsSection(roots: AuditedRoot[]): string[] {\n return [\n '### Skill roots',\n '',\n ...bulletList(\n roots.map((r) => `${r.root}${r.exists ? '' : ' (MISSING)'} — ${r.skillCount} skill${r.skillCount === 1 ? '' : 's'}`),\n '(none configured)'\n ),\n ];\n}\n\nfunction renderSkillsSection(skills: AuditedSkill[]): string[] {\n return [\n '### Skills',\n '',\n ...bulletList(\n skills.map((s) => {\n const flags = joinDefined([\n s.validFrontmatter ? undefined : 'INVALID FRONTMATTER',\n s.aliasPaths.length > 0 ? `${s.aliasPaths.length} alias path(s)` : undefined,\n ]);\n return `${sanitizeForMarkdown(s.name)} — ${s.path} (est. ${s.estTokens} tokens)${flags ? ` [${flags}]` : ''}`;\n }),\n '(none found)'\n ),\n ];\n}\n\nfunction renderMcpTargetsSection(targets: AuditedMcpTarget[]): string[] {\n const lines = ['### MCP targets', ''];\n if (targets.length === 0) {\n lines.push('- (none configured)');\n return lines;\n }\n for (const t of targets) {\n lines.push(`- ${t.path} — ${t.format}, ${t.status}`);\n for (const server of t.servers) {\n const detail = joinDefined([\n server.commandBasename ? sanitizeForMarkdown(server.commandBasename) : undefined,\n server.packageSpec ? sanitizeForMarkdown(server.packageSpec) : undefined,\n `${server.argCount} arg${server.argCount === 1 ? '' : 's'}`,\n `${server.envCount} env var${server.envCount === 1 ? '' : 's'}`,\n ]);\n lines.push(` - ${sanitizeForMarkdown(server.name)} (${detail})`);\n }\n }\n return lines;\n}\n\nfunction renderFindingsSection(findings: AuditFinding[]): string[] {\n return [\n '## Hygiene findings',\n '',\n ...bulletList(\n findings.map((f) => `[${f.severity}] ${f.rule} — ${sanitizeForMarkdown(f.target)}: ${sanitizeForMarkdown(f.detail)}`),\n '(none — clean)'\n ),\n ];\n}\n\nfunction renderOpportunitiesSection(report: AuditReport): string[] {\n const { overlapClusters, evolveEligible, foundation } = report.opportunities;\n return [\n '## Opportunities',\n '',\n '### Overlap clusters',\n '',\n ...bulletList(\n overlapClusters.map((c) => `top score ${c.topScore} — ${c.suggestedVerb} — ${c.members.join(', ')}`),\n '(none — either no cross-root overlap found, or the Pro feature is locked, see Notices)'\n ),\n '',\n '### Evolve-eligible',\n '',\n ...bulletList(\n evolveEligible.map((c) => `${sanitizeForMarkdown(c.name)} — ${c.path}`),\n '(none)'\n ),\n '',\n '### Foundation skill',\n '',\n `- ${foundation.message}`,\n ];\n}\n\nfunction renderToolingSection(tooling: AuditTooling | undefined): string[] {\n const lines = ['## Tooling', ''];\n if (!tooling) {\n lines.push('- (not available in this report)');\n return lines;\n }\n\n lines.push(`- SkillSpector CLI: ${tooling.skillspector.installed ? 'installed' : 'NOT installed'}`);\n if (tooling.skillspector.hint) lines.push(` - ${tooling.skillspector.hint}`);\n\n lines.push(`- skills.sh auth (VERCEL_OIDC_TOKEN): ${tooling.skillsShToken.present ? 'present' : 'missing'}`);\n if (tooling.skillsShToken.hint) lines.push(` - ${tooling.skillsShToken.hint}`);\n\n const llm = tooling.skillspectorLlm;\n if (llm.configured) {\n lines.push(`- SkillSpector LLM stage (optional): configured (provider: ${sanitizeForMarkdown(llm.provider ?? '')})`);\n } else if (llm.provider) {\n lines.push(`- SkillSpector LLM stage (optional): not configured — SKILLSPECTOR_PROVIDER is \"${sanitizeForMarkdown(llm.provider)}\" but its API key env var is not set`);\n } else {\n lines.push(\n '- SkillSpector LLM stage (optional): not set — static scan works without it (use --no-llm); ' +\n 'set SKILLSPECTOR_PROVIDER + its API key for semantic analysis'\n );\n }\n\n return lines;\n}\n\n/** Renders an `AuditReport` as a standalone markdown document — the file `skill-forge audit` writes under `~/.skill-forge/reports/`. */\nexport function renderAuditMarkdown(report: AuditReport): string {\n const { summary } = report;\n const lines: string[] = [\n `# skill-forge audit — ${report.generatedAt}`,\n '',\n '## Summary',\n '',\n `- Skill roots: ${summary.rootCount}`,\n `- Skills: ${summary.skillCount}`,\n `- MCP targets: ${summary.mcpTargetCount}`,\n `- Findings: ${summary.findingCounts.CRITICAL} CRITICAL, ${summary.findingCounts.HIGH} HIGH, ${summary.findingCounts.MEDIUM} MEDIUM, ${summary.findingCounts.LOW} LOW`,\n `- Overlap clusters: ${summary.overlapClusterCount}`,\n `- Evolve-eligible skills: ${summary.evolveEligibleCount}`,\n '',\n '## Inventory',\n '',\n ...renderRootsSection(report.inventory.skillRoots),\n '',\n ...renderSkillsSection(report.inventory.skills),\n '',\n ...renderMcpTargetsSection(report.inventory.mcpTargets),\n '',\n ...renderFindingsSection(report.hygiene),\n '',\n ...renderOpportunitiesSection(report),\n '',\n ...renderToolingSection(report.tooling),\n '',\n '## Notices',\n '',\n ...bulletList(report.notices, '(none)'),\n ];\n return lines.join('\\n') + '\\n';\n}\n\n// ---------------------------------------------------------------------------\n// Foundation skill scaffold — the one write path in this module\n// ---------------------------------------------------------------------------\n\nfunction renderFoundationSkillMd(profile: BusinessProfile): string {\n const description = profile.businessName\n ? `Business-foundation context for ${profile.businessName} — grounds new and existing skills in its audiences, workflows, and constraints. Generated by skill-forge audit.`\n : 'Business-foundation context for this business — grounds new and existing skills in its audiences, workflows, and constraints. Generated by skill-forge audit.';\n\n const lines: (string | undefined)[] = [\n '---',\n 'name: business-foundation',\n `description: ${description}`,\n '---',\n '',\n `# Business Foundation${profile.businessName ? `: ${profile.businessName}` : ''}`,\n '',\n profile.industry ? `**Industry:** ${profile.industry}` : undefined,\n '',\n '## Audiences',\n '',\n ...bulletList(profile.audiences, '(none captured yet)'),\n '',\n '## Workflows',\n '',\n ...bulletList(profile.workflows, '(none captured yet)'),\n '',\n '## Constraints',\n '',\n ...bulletList(profile.constraints, '(none captured yet)'),\n '',\n '## Integration guidance',\n '',\n 'New and existing skills in this set should align with the business context above:',\n '- Match the audiences, terminology, and tone described here.',\n '- Respect the workflows and constraints listed above when proposing changes.',\n '- When curating or evolving skills (`skill-forge evolve`, `skill-forge add`), treat this file as ground ' +\n 'truth for what the business actually needs — update it by re-running `skill-forge audit` rather than ' +\n 'editing it out of band, so it stays a single source of truth.',\n ];\n\n return lines.filter((l): l is string => l !== undefined).join('\\n') + '\\n';\n}\n\n/**\n * Scaffolds `<targetRoot>/business-foundation/SKILL.md` from a captured business profile.\n * `targetRoot` must be one of `config.skillsRoots` (containment check) — this is the one write\n * path in this module, gated entirely behind the caller's explicit opt-in (see\n * src/commands/audit.ts's `--foundation` flag / interactive confirm). Refuses an existing\n * dir/file or a symlinked destination; writes atomically (builds in a sibling temp dir, then\n * renames the whole dir into place, so a failure never leaves a partial `business-foundation/`\n * visible); runs `scanSafety` on the generated content before the rename and refuses to place it\n * on a block verdict. Returns the canonical path — the caller is responsible for persisting it to\n * `config.foundationSkillPath`. Deliberately async (unlike the plan's originally sketched sync\n * signature) because `scanSafety`, which this function must run before placement, is itself\n * async — there is no synchronous way to honor \"scan before write\" here.\n */\nexport async function scaffoldFoundationSkill(config: Config, profile: BusinessProfile, targetRoot: string): Promise<string> {\n const resolvedRoot = resolveConfiguredRoot(targetRoot);\n const configuredRoots = config.skillsRoots.map(resolveConfiguredRoot);\n if (!configuredRoots.includes(resolvedRoot)) {\n throw new UserError(`refusing to scaffold a foundation skill under ${resolvedRoot}: not one of the configured skillsRoots`);\n }\n if (!isDir(resolvedRoot)) {\n throw new UserError(`skills root does not exist: ${resolvedRoot}`);\n }\n\n const destDir = join(resolvedRoot, 'business-foundation');\n\n let destLstat;\n try {\n destLstat = lstatSync(destDir);\n } catch {\n destLstat = undefined;\n }\n if (destLstat?.isSymbolicLink()) {\n throw new UserError(`refusing to scaffold a foundation skill at ${destDir}: destination is a symlink`);\n }\n if (destLstat) {\n throw new UserError(`refusing to scaffold a foundation skill at ${destDir}: already exists`);\n }\n\n const content = renderFoundationSkillMd(profile);\n const tempDir = mkdtempSync(join(resolvedRoot, '.business-foundation-'));\n try {\n writeFileSync(join(tempDir, 'SKILL.md'), content, { flag: 'wx', mode: 0o600 });\n\n const safety = await scanSafety(tempDir, config.strictness);\n if (safety.verdict === 'block') {\n throw new UserError(\n `refusing to scaffold a foundation skill: the generated SKILL.md was flagged by the safety scan (${safety.findings\n .map(formatSafetyFinding)\n .join('; ')})`\n );\n }\n\n renameSync(tempDir, destDir);\n } catch (err) {\n rmSync(tempDir, { recursive: true, force: true });\n throw err;\n }\n\n // The skill is already on disk at this point (renameSync above succeeded) — a provenance\n // failure here must not surface as a raw, unattributed error, and must not silently leave the\n // caller believing nothing happened (mirrors the v0.7 pattern in src/evolve.ts's adopt path).\n try {\n appendProvenance(\n {\n name: 'business-foundation',\n source: 'skill-forge audit (business profile)',\n verb: 'DEFER',\n target: destDir,\n took: 'scaffolded a new business-foundation skill from the captured business profile',\n notes: 'scaffolded by skill-forge audit',\n },\n resolvedRoot\n );\n } catch (err) {\n throw new UserError(\n `the business-foundation skill was created at ${destDir}, but recording the provenance entry failed: ${(err as Error).message}`\n );\n }\n\n return destDir;\n}\n","import { createInterface } from 'node:readline/promises';\nimport type { Verdict } from './gate/safety.js';\n\n/**\n * Shared promote/hold/reject decision helpers — the same decision flow\n * `add`/`scan` use (`docs/gate-policy.md`'s \"Decision flow\" section),\n * reused by `evolve.ts` for its re-gate step. Moved here (v0.7 simplify\n * pass) from `commands/add.ts`, which imports these instead of defining\n * its own copies.\n */\n\nexport type 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. */\nexport function logOutcome(message: string, json: boolean | undefined): void {\n if (json) console.error(message);\n else console.log(message);\n}\n\n/** --yes/--json honors the verdict: a block is rejected (nonzero exit); pass/warn is promoted. */\nexport function yesDecision(verdict: Verdict): Decision {\n return verdict === 'block' ? 'reject' : 'promote';\n}\n\n/**\n * Prompts interactively for promote/hold/reject, defaulting the suggested\n * answer to the verdict. `subject` names what's being decided in the\n * prompt text (e.g. `\"this skill\"`, `\"this evolution proposal\"`) so each\n * caller keeps its own exact prompt wording.\n */\nexport async function promptDecision(verdict: Verdict, subject: string): 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 ${subject}? [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 { 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","/*\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 { createInterface } from 'node:readline/promises';\nimport { createHash } from 'node:crypto';\nimport { cpSync, existsSync, lstatSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { basename, dirname, join, resolve as resolvePath, sep } from 'node:path';\nimport { UserError } from './errors.js';\nimport { loadConfig } from './config.js';\nimport { proFeatureStatus } from './license.js';\nimport { profileSkill, asSkillProfile } from './gate/profile.js';\nimport type { SkillProfile } from './gate/profile.js';\nimport { scanSafety } from './gate/safety.js';\nimport type { SafetyResult } from './gate/safety.js';\nimport { formatSafetyFinding } from './gate/scanShared.js';\nimport { SAFETY_VERDICT_MAP, mapSafetyFindings, printGateOutput } from './report/render.js';\nimport type { GateResult } from './report/render.js';\nimport { logOutcome, promptDecision, yesDecision } from './decision.js';\nimport { appendProvenance } from './provenance.js';\nimport { appendQueueEntry } from './queue.js';\nimport { defaultExec } from './install/quarantine.js';\nimport type { ExecFn } from './install/quarantine.js';\n\n/**\n * `skill-forge evolve` (v0.7, Pro) — orchestrates microsoft/SkillOpt's\n * `skillopt-sleep` CLI to propose a self-evolution of an already-installed\n * skill (harvest recent sessions -> generate a replacement `SKILL.md`/\n * `CLAUDE.md` -> stage it, never auto-adopted), then re-gates the STAGED\n * PROPOSAL — never the live skill — through the same static safety ruleset\n * `add`/`scan` use, before a promote/hold/reject decision.\n *\n * `skillopt-sleep` is never installed by this module (detect-don't-install:\n * absence prints `pip install skillopt` guidance and refuses) and every\n * invocation of it goes through argv arrays with no shell, mirroring\n * `install/quarantine.ts`'s subprocess discipline. Nothing from a staged\n * proposal is ever executed — the re-gate step only reads\n * `proposed_SKILL.md`/`proposed_CLAUDE.md` as text.\n *\n * The entire command is Pro (unlike `add`, which is free with only specific\n * sub-features Pro-gated): self-evolution reuses the provenance ledger and\n * pending-ingestion queue, and is a power-user workflow layered on the free\n * gate. Free during the 0.x beta via `proFeatureStatus()`, same as every\n * other Pro feature — see src/license.ts.\n *\n * `staging_dir` (from skillopt-sleep's own JSON output) is untrusted input:\n * it's validated to be strictly contained under the expected sandbox\n * (`<project>/.skillopt-sleep/staging/`) before anything reads, copies, or\n * deletes it — mirroring `install/quarantine.ts`'s tar-member path-traversal\n * guard. The staged proposal is further treated as untrusted: a symlink in\n * place of a proposed file is refused, and the exact bytes re-gated are\n * hash-pinned and re-verified immediately before `adopt` runs, so a mutation\n * between the scan and the adopt (TOCTOU) is caught rather than silently\n * adopted.\n */\n\nconst SKILLOPT_BIN = 'skillopt-sleep';\nconst PRO_FEATURE_LABEL = 'self-evolution via SkillOpt-Sleep (evolve) — re-gating, provenance, and the pending-ingestion queue';\n\nexport interface EvolveOptions {\n /** Project dir passed to skillopt-sleep as --project. Defaults to cwd. */\n project?: string;\n /** Use skillopt-sleep's `dry-run` subcommand (report only, nothing staged) instead of `run`. */\n dryRun?: boolean;\n /** skillopt-sleep backend. Defaults to \"mock\" (offline, no session data leaves the machine). */\n backend?: string;\n /** Hours of session history for skillopt-sleep to harvest. Passed through as --lookback-hours. */\n lookbackHours?: number;\n /** Skip interactive prompts (the disclosure confirm and the promote/hold/reject prompt); honor the re-gate verdict automatically. */\n yes?: boolean;\n /** Print the re-gate result as JSON instead of the terminal report. Implies non-interactive, same as `add`'s --json. */\n json?: boolean;\n /** Allow re-adopting a staging dir whose proposed content already matches the live skill (double-adopt guard override). */\n force?: boolean;\n}\n\nexport interface EvolveDeps {\n /** Injected subprocess runner for skillopt-sleep invocations. Defaults to a real argv-array spawn (no shell). */\n exec?: ExecFn;\n /** Injected y/N confirm for the data-boundary disclosure prompt. Defaults to a real stdin readline confirm. */\n confirm?: (question: string) => Promise<boolean>;\n}\n\ninterface SkilloptRunOutput {\n staging_dir: string | null;\n accepted: boolean;\n}\n\ninterface StagingManifest {\n live_skill_path: string;\n live_memory_path?: string | null;\n has_skill: boolean;\n has_memory: boolean;\n}\n\nexport async function evolve(targetSkillDir: string, options: EvolveOptions = {}, deps: EvolveDeps = {}): Promise<void> {\n const exec = deps.exec ?? defaultExec;\n const json = options.json;\n\n // 1. Validate the target skill dir.\n const resolvedTargetDir = resolvePath(targetSkillDir);\n const skillMdPath = join(resolvedTargetDir, 'SKILL.md');\n if (!existsSync(skillMdPath)) {\n throw new UserError(`no SKILL.md found at ${skillMdPath} — evolve requires an existing, installed skill directory`);\n }\n\n // 2. Pro gate — the whole command is Pro; free during the 0.x beta.\n const proStatus = proFeatureStatus(PRO_FEATURE_LABEL);\n if (!proStatus.enabled) {\n throw new UserError(proStatus.notice ?? 'evolve is a Pro feature — set SKILL_FORGE_LICENSE or config.licenseKey to a valid license to unlock.');\n }\n const notices: string[] = proStatus.notice ? [proStatus.notice] : [];\n\n // 3. Data-boundary disclosure.\n const backend = options.backend ?? 'mock';\n if (backend !== 'mock') {\n const disclosure =\n `data boundary: this run uses the \"${backend}\" backend. Per SkillOpt-Sleep's own docs, harvesting itself ` +\n 'is local and read-only, but a real backend sends truncated excerpts from harvested sessions and derived ' +\n 'tasks to the provider you selected — this is not currently guaranteed to be secret-free.';\n if (options.yes) {\n notices.push(`${disclosure} Proceeding (--yes).`);\n } else if (json) {\n throw new UserError(`${disclosure} Pass --yes to proceed — --json is non-interactive and cannot prompt for confirmation.`);\n } else {\n console.log(`[skill-forge] ${disclosure}`);\n const confirmFn = deps.confirm ?? defaultConfirm;\n const confirmed = await confirmFn('Proceed and send session data to this backend? [y/N] ');\n if (!confirmed) {\n throw new UserError('evolve cancelled — data-boundary disclosure was not accepted.');\n }\n notices.push(`${disclosure} Confirmed interactively.`);\n }\n } else {\n notices.push('using the offline \"mock\" backend — deterministic, no network calls, no session data leaves this machine.');\n }\n\n // 4. Detect (never install), then run skillopt-sleep.\n const project = resolvePath(options.project ?? process.cwd());\n const present = await detectSkillopt(exec, project);\n if (!present) {\n throw new UserError(\n '`skillopt-sleep` not found on PATH. Install it with `pip install skillopt` ' +\n '(https://github.com/microsoft/SkillOpt) and re-run `skill-forge evolve`.'\n );\n }\n\n const subcommand = options.dryRun ? 'dry-run' : 'run';\n const runArgs = [subcommand, '--project', project, '--target-skill-path', skillMdPath, '--json', '--backend', backend];\n if (options.lookbackHours !== undefined) {\n runArgs.push('--lookback-hours', String(options.lookbackHours));\n }\n\n const runResult = await exec(SKILLOPT_BIN, runArgs, { cwd: project });\n if (runResult.code !== 0) {\n throw new UserError(`skillopt-sleep ${subcommand} failed (exit ${runResult.code}): ${tail(runResult.stderr || runResult.stdout)}`);\n }\n\n let rawOutput: unknown;\n try {\n rawOutput = JSON.parse(runResult.stdout);\n } catch (err) {\n throw new UserError(`could not parse skillopt-sleep JSON output: ${(err as Error).message}`);\n }\n const parsed = validateRunOutput(rawOutput);\n\n if (!parsed.accepted || !parsed.staging_dir) {\n if (json) {\n console.log(JSON.stringify({ ok: true, accepted: parsed.accepted, stagingDir: parsed.staging_dir }, null, 2));\n } else {\n console.log('[skill-forge] skillopt-sleep produced no accepted proposal — nothing to re-gate or adopt.');\n }\n return;\n }\n\n // `staging_dir` is untrusted (skillopt-sleep's own JSON output) — require it to\n // be strictly contained under the expected sandbox before it's read, copied,\n // adopted, or deleted anywhere below. A naive `startsWith(stagingRoot)` would\n // be defeated by a sibling directory sharing the same string prefix (e.g.\n // `.skillopt-sleep/staging-evil` next to `.skillopt-sleep/staging`) — this\n // checks for either exact equality or the separator-qualified prefix, mirroring\n // `install/quarantine.ts`'s tar-member path-traversal guard.\n const stagingRoot = join(project, '.skillopt-sleep', 'staging');\n const stagingDir = resolvePath(parsed.staging_dir);\n if (stagingDir !== stagingRoot && !stagingDir.startsWith(stagingRoot + sep)) {\n throw new UserError(\n `skillopt-sleep reported a staging_dir outside the expected sandbox (${stagingRoot}): ${stagingDir} — refusing to use it`\n );\n }\n\n // 5. Re-gate: scan ONLY the staged proposal, in a fresh temp copy — never the live skill, never report.json/diagnostics.json.\n const proposedSkillMd = join(stagingDir, 'proposed_SKILL.md');\n const proposedClaudeMd = join(stagingDir, 'proposed_CLAUDE.md');\n const reportMdPath = join(stagingDir, 'report.md');\n if (!existsSync(proposedSkillMd)) {\n throw new UserError(`skillopt-sleep reported an accepted proposal but ${proposedSkillMd} is missing`);\n }\n if (existsSync(reportMdPath)) {\n notices.push(`SkillOpt-Sleep's full report: ${reportMdPath}`);\n }\n\n // A staged proposal is untrusted content: refuse a symlink in place of a\n // proposed file (spirit of the tar-member guard — never follow a link out\n // of the sandbox) before reading either one.\n assertNotSymlink(proposedSkillMd, 'proposed_SKILL.md');\n assertNotSymlink(proposedClaudeMd, 'proposed_CLAUDE.md');\n\n // Hash-pin what's about to be scanned, so a mutation of the staged files\n // between this scan and the `adopt` call below (TOCTOU) is caught rather\n // than silently adopted — see the re-check immediately before `adopt`.\n const scannedSkillHash = sha256File(proposedSkillMd);\n const scannedClaudeHash = sha256File(proposedClaudeMd);\n\n const tempDir = mkdtempSync(join(tmpdir(), 'skill-forge-evolve-'));\n let profile: SkillProfile;\n let safety: SafetyResult;\n try {\n cpSync(proposedSkillMd, join(tempDir, 'SKILL.md'));\n if (existsSync(proposedClaudeMd)) {\n cpSync(proposedClaudeMd, join(tempDir, 'CLAUDE.md'));\n }\n\n // Unreachable in practice: the temp dir only ever contains a renamed\n // proposed_SKILL.md (+ proposed_CLAUDE.md), never an .mcp.json or\n // plugin.json — asSkillProfile() throws before this could matter.\n profile = asSkillProfile(profileSkill(tempDir), 'for the evolved proposal');\n\n const strictness = loadConfig().strictness;\n safety = await scanSafety(tempDir, strictness);\n } finally {\n // A cleanup failure here is secondary to whatever the try block already\n // produced (a result, or an error already propagating) — never let it\n // mask that.\n try {\n rmSync(tempDir, { recursive: true, force: true });\n } catch (err) {\n console.error(`[skill-forge] failed to clean up temp re-gate dir ${tempDir}: ${(err as Error).message}`);\n }\n }\n\n const safetyVerdict = safety.verdict;\n const gateResult: GateResult = {\n profile,\n safety: {\n verdict: SAFETY_VERDICT_MAP[safetyVerdict],\n findings: mapSafetyFindings(safety.findings),\n },\n notices,\n };\n printGateOutput(gateResult, json);\n\n // --dry-run belt-and-braces: never stage/adopt anything, regardless of what\n // skillopt-sleep's JSON output claims — even if it (bug or otherwise)\n // reports an accepted proposal + a real staging_dir for a dry-run call,\n // this never reaches the decision/adopt path below.\n if (options.dryRun) {\n logOutcome('[skill-forge] --dry-run: report only, nothing staged or adopted.', json);\n return;\n }\n\n // 6. Decide — same promote/hold/reject semantics as add/scan.\n const decision =\n options.yes || json ? yesDecision(safetyVerdict) : await promptDecision(safetyVerdict, 'this evolution proposal');\n\n if (decision === 'promote') {\n const manifest = readManifest(stagingDir);\n if (isAlreadyAdopted(stagingDir, manifest) && !options.force) {\n throw new UserError(\n `staging dir ${stagingDir} looks already adopted — its proposed content is byte-identical to the live ` +\n 'skill (and memory, if present). Pass --force to re-adopt anyway.'\n );\n }\n\n // Re-verify immediately before adopting: if the staged files changed since\n // they were scanned above, what's about to be adopted is no longer what\n // was re-gated.\n if (sha256File(proposedSkillMd) !== scannedSkillHash || sha256File(proposedClaudeMd) !== scannedClaudeHash) {\n throw new UserError(`proposal changed after it was re-gated (staging dir: ${stagingDir}) — refusing to adopt`);\n }\n\n const adoptResult = await exec(SKILLOPT_BIN, ['adopt', '--staging', stagingDir, '--project', project], { cwd: project });\n if (adoptResult.code !== 0) {\n throw new UserError(`skillopt-sleep adopt failed (exit ${adoptResult.code}): ${tail(adoptResult.stderr || adoptResult.stdout)}`);\n }\n\n // The live skill has now changed — report that immediately, before the\n // bookkeeping below, which can itself fail without undoing the adopt.\n logOutcome(`[skill-forge] adopted -> ${resolvedTargetDir}`, json);\n\n // Free-tier `add` records DEFER for \"adopt as-is, no absorb/fork judgment\" —\n // the same is true here (the re-gate is a safety check, not a redundancy\n // judgment against the existing set), so this reuses DEFER rather than\n // adding a sixth member to provenance.ts's five-verb FORGE_VERBS/ForgeVerb\n // (referenced elsewhere as exactly five: docs/gate-policy.md,\n // assets/ingest-prompt.md, README.md, CLAUDE.md). The distinguishing\n // \"this was self-evolved\" signal lives in the note below and in the\n // queue entry's `origin: 'evolve'` field instead.\n const skillsRootForProvenance = dirname(resolvedTargetDir);\n let ledgerPath: string;\n try {\n ledgerPath = appendProvenance(\n {\n name: profile.name ?? basename(resolvedTargetDir),\n source: resolvedTargetDir,\n version: profile.version,\n license: profile.license,\n verb: 'DEFER',\n target: resolvedTargetDir,\n took: `self-evolved via SkillOpt-Sleep (staging: ${stagingDir})`,\n notes: `self-evolved via SkillOpt-Sleep; re-gated (verdict: ${safetyVerdict})`,\n },\n skillsRootForProvenance\n );\n } catch (err) {\n throw new UserError(\n `adopt succeeded and the live skill at ${resolvedTargetDir} has already changed, but recording the ` +\n `provenance entry failed: ${(err as Error).message}`\n );\n }\n logOutcome(`[skill-forge] provenance recorded -> ${ledgerPath}`, json);\n\n try {\n appendQueueEntry({\n source: resolvedTargetDir,\n sourceType: 'local',\n installedPath: resolvedTargetDir,\n quarantinePath: stagingDir,\n gate: {\n license: profile.license ?? null,\n safetyVerdict,\n safetyFindings: safety.findings.map(formatSafetyFinding),\n overlapTop: [],\n suggestedVerb: null,\n },\n status: 'pending',\n artifactType: 'skill',\n origin: 'evolve',\n });\n } catch (err) {\n throw new UserError(\n `adopt succeeded and the live skill at ${resolvedTargetDir} has already changed (provenance was recorded), ` +\n `but appending the pending-ingestion queue entry failed: ${(err as Error).message}`\n );\n }\n logOutcome('[skill-forge] queued for ingestion review', json);\n } else if (decision === 'reject') {\n try {\n rmSync(stagingDir, { recursive: true, force: true });\n } catch (err) {\n throw new UserError(\n `rejected the proposal, but removing the staging dir ${stagingDir} failed: ${(err as Error).message} — ` +\n 'remove it manually.'\n );\n }\n logOutcome('[skill-forge] rejected — staging dir removed', json);\n if (safetyVerdict === 'block') process.exitCode = 1;\n } else {\n logOutcome(`[skill-forge] held — staging dir left in place -> ${stagingDir}`, json);\n if (existsSync(reportMdPath)) {\n logOutcome(`[skill-forge] see ${reportMdPath} for the full evidence report`, json);\n }\n }\n}\n\nasync function defaultConfirm(question: string): Promise<boolean> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n // Iterate lines via the async-iterator protocol rather than rl.question() —\n // same rationale as decision.ts's promptDecision: question() attaches a\n // one-shot 'line' listener, and on piped/closed (non-TTY) stdin that ends\n // without ever emitting a line, that listener never fires and the promise\n // never resolves — the process hangs forever. The async iterator both\n // queues lines and terminates cleanly at EOF, so a closed stream resolves\n // (falling through to `false`, \"not confirmed\") instead of hanging.\n process.stdout.write(question);\n for await (const raw of rl) {\n return /^y(es)?$/i.test(raw.trim());\n }\n return false;\n } finally {\n rl.close();\n }\n}\n\n/** Detects skillopt-sleep on PATH by running `--help` via the injected exec — never installs it. Any rejection (ENOENT) or nonzero exit is treated as absent. */\nasync function detectSkillopt(exec: ExecFn, cwd: string): Promise<boolean> {\n try {\n const result = await exec(SKILLOPT_BIN, ['--help'], { cwd });\n return result.code === 0;\n } catch {\n return false;\n }\n}\n\n/** Last `maxLines` lines of `text`, for surfacing a subprocess failure without dumping unbounded output. */\nfunction tail(text: string, maxLines = 20): string {\n return text.trim().split('\\n').slice(-maxLines).join('\\n');\n}\n\n/**\n * Validates skillopt-sleep's `run`/`dry-run` JSON output — an external-process\n * trust boundary — before any field is used: must be a plain object,\n * `staging_dir` must be a string or null/undefined, otherwise a UserError\n * (not a TypeError surfacing later at some unrelated call site). `accepted`\n * is coerced to a boolean rather than strictly typechecked, matching how\n * loosely the field is actually consumed downstream.\n */\nfunction validateRunOutput(raw: unknown): SkilloptRunOutput {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new UserError(`skillopt-sleep produced unexpected JSON output (expected an object): ${JSON.stringify(raw)}`);\n }\n const obj = raw as Record<string, unknown>;\n const stagingDirRaw = obj.staging_dir;\n if (stagingDirRaw !== null && stagingDirRaw !== undefined && typeof stagingDirRaw !== 'string') {\n throw new UserError(\n `skillopt-sleep produced an unexpected \"staging_dir\" (expected a string or null): ${JSON.stringify(stagingDirRaw)}`\n );\n }\n return { accepted: Boolean(obj.accepted), staging_dir: stagingDirRaw ?? null };\n}\n\nfunction readManifest(stagingDir: string): StagingManifest {\n const manifestPath = join(stagingDir, 'manifest.json');\n let raw: string;\n try {\n raw = readFileSync(manifestPath, 'utf-8');\n } catch (err) {\n throw new UserError(`could not read staging manifest ${manifestPath}: ${(err as Error).message}`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new UserError(`could not parse staging manifest ${manifestPath}: ${(err as Error).message}`);\n }\n return validateManifest(parsed, manifestPath);\n}\n\n/** Validates manifest.json's shape (another external trust boundary) before any field is used. */\nfunction validateManifest(raw: unknown, manifestPath: string): StagingManifest {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new UserError(`staging manifest ${manifestPath} has an unexpected shape (expected an object)`);\n }\n const obj = raw as Record<string, unknown>;\n if (typeof obj.live_skill_path !== 'string') {\n throw new UserError(`staging manifest ${manifestPath} is missing a valid \"live_skill_path\" string`);\n }\n if (obj.live_memory_path !== undefined && obj.live_memory_path !== null && typeof obj.live_memory_path !== 'string') {\n throw new UserError(`staging manifest ${manifestPath} has an unexpected \"live_memory_path\" (expected a string or null)`);\n }\n if (typeof obj.has_skill !== 'boolean' || typeof obj.has_memory !== 'boolean') {\n throw new UserError(`staging manifest ${manifestPath} is missing valid \"has_skill\"/\"has_memory\" booleans`);\n }\n return {\n live_skill_path: obj.live_skill_path,\n live_memory_path: (obj.live_memory_path as string | null | undefined) ?? null,\n has_skill: obj.has_skill,\n has_memory: obj.has_memory,\n };\n}\n\nfunction filesIdentical(a: string, b: string): boolean {\n if (!existsSync(a) || !existsSync(b)) return false;\n return readFileSync(a).equals(readFileSync(b));\n}\n\n/**\n * Double-adopt guard: true when the staged proposal's content is\n * byte-identical to the live file(s) it would replace — i.e. this staging\n * dir already appears to have been adopted once. skillopt-sleep's own\n * `adopt` has no confirmation and a second adopt overwrites its own backup\n * (the pre-evolution original is lost), so this guard exists purely on our\n * side of the wrapper.\n *\n * Grounded in which proposed files actually exist on disk (`existsSync`)\n * rather than trusting the manifest's `has_skill`/`has_memory` flags blindly\n * — a stale or malformed manifest claiming `false` shouldn't be able to\n * silently skip a comparison for a file that's actually sitting right there.\n */\nfunction isAlreadyAdopted(stagingDir: string, manifest: StagingManifest): boolean {\n const proposedSkillMd = join(stagingDir, 'proposed_SKILL.md');\n const proposedClaudeMd = join(stagingDir, 'proposed_CLAUDE.md');\n let sawComparison = false;\n\n if (existsSync(proposedSkillMd)) {\n sawComparison = true;\n if (!filesIdentical(proposedSkillMd, manifest.live_skill_path)) return false;\n }\n if (existsSync(proposedClaudeMd) && manifest.live_memory_path) {\n sawComparison = true;\n if (!filesIdentical(proposedClaudeMd, manifest.live_memory_path)) return false;\n }\n\n return sawComparison;\n}\n\n/** Throws a UserError if `path` is a symlink. Silent no-op if `path` doesn't exist (existsSync() calls elsewhere already gate required-vs-optional). Uses lstatSync (never follows the link) so a broken symlink is still caught. */\nfunction assertNotSymlink(path: string, label: string): void {\n let stat;\n try {\n stat = lstatSync(path);\n } catch {\n return;\n }\n if (stat.isSymbolicLink()) {\n throw new UserError(`refusing to re-gate ${label}: ${path} is a symlink (a staged proposal must be a plain file)`);\n }\n}\n\n/** sha256 hex digest of a file's contents, or null if it doesn't exist. Exported for direct unit coverage of the TOCTOU hash-pin (see evolve()'s scan-time / pre-adopt re-check). */\nexport function sha256File(path: string): string | null {\n if (!existsSync(path)) return null;\n return createHash('sha256').update(readFileSync(path)).digest('hex');\n}\n","import { evolve } from '../evolve.js';\nimport type { EvolveOptions } from '../evolve.js';\n\n/**\n * `skill-forge evolve <target-skill-dir>` — thin CLI wiring around the Pro\n * self-evolution flow in src/evolve.ts: orchestrate a SkillOpt-Sleep run,\n * re-gate the staged proposal through the same static skill safety ruleset\n * `add`/`scan` use, then promote/hold/reject. See src/evolve.ts for the\n * actual pipeline; this file only adapts commander's positional/options\n * shape into a call.\n */\nexport async function evolveCommand(targetSkillDir: string, options: EvolveOptions = {}): Promise<void> {\n await evolve(targetSkillDir, options);\n}\n","import { mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve as resolvePath } from 'node:path';\nimport { sanitizeForMarkdown } from '../audit.js';\nimport { loadConfig } from '../config.js';\nimport { UserError } from '../errors.js';\nimport { buildOrganizeReport } from '../organize.js';\nimport type { OrganizeReport } from '../organize.js';\n\n/**\n * `skill-forge organize` — thin CLI wiring around the Pro capability\n * registry + dependency graph engine in src/organize.ts. This file only\n * adapts commander's options shape into a call, renders the human-readable\n * summary (sanitizing every untrusted string — names/tiers/domains/consumes\n * targets/paths — via audit.ts's `sanitizeForMarkdown`), and handles\n * `--out`/`--json`. See src/organize.ts for the actual scan/graph pipeline.\n */\nexport interface OrganizeCommandOptions {\n json?: boolean;\n /** Write the full registry+graph report JSON to this path (`wx` mode 0o600 — refuses an existing file). */\n out?: string;\n usageSnapshot?: string;\n}\n\n/** Writes the report exclusively (`wx` + 0600) — never overwrites an existing file at that path, mirroring src/commands/audit.ts's report write. */\nfunction writeReport(outPath: string, contents: string): string {\n const resolved = resolvePath(outPath);\n mkdirSync(dirname(resolved), { recursive: true });\n try {\n writeFileSync(resolved, contents, { flag: 'wx', mode: 0o600 });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'EEXIST') {\n throw new UserError(`output file already exists: ${resolved} — pass a different --out path`);\n }\n throw err;\n }\n return resolved;\n}\n\nfunction renderSummary(report: OrganizeReport): string[] {\n const { registry, graph } = report;\n const lines: string[] = [];\n\n lines.push(`[skill-forge] organize: ${registry.count} skill(s) across ${registry.skillsRoots.length} root(s)`);\n lines.push(` by tier: ${JSON.stringify(registry.byTier)}`);\n lines.push(` by domain: ${JSON.stringify(registry.byDomain)}`);\n lines.push(` usage joined: ${registry.usageJoined}`);\n lines.push(` untagged (missing tier/domain): ${registry.untagged.length}`);\n for (const u of registry.untagged.slice(0, 30)) {\n lines.push(` - ${sanitizeForMarkdown(u.name)}: missing ${u.missing.join(', ')}`);\n }\n if (registry.warnings.length > 0) {\n lines.push(' warnings:');\n for (const w of registry.warnings) lines.push(` ! ${sanitizeForMarkdown(w)}`);\n }\n\n lines.push('');\n lines.push(` graph: ${graph.nodeCount} nodes, ${graph.edgeCount} edges`);\n if (graph.hotResources.length > 0) {\n lines.push(' hot resources (consumed by N customs):');\n for (const h of graph.hotResources) lines.push(` ${h.consumedBy}x ${sanitizeForMarkdown(h.name)}`);\n }\n if (graph.danglingEdges.length > 0) {\n lines.push(' dangling edges (consumes a target not in the set):');\n for (const d of graph.danglingEdges) lines.push(` ${sanitizeForMarkdown(d.from)} -> ${sanitizeForMarkdown(d.to)} (missing)`);\n }\n if (graph.ambiguousConsumesTargets.length > 0) {\n lines.push(' ambiguous consumes targets (name matched multiple skills):');\n for (const a of graph.ambiguousConsumesTargets) {\n lines.push(\n ` ${sanitizeForMarkdown(a.from)} -> ${sanitizeForMarkdown(a.to)} (matches: ${a.matches.map(sanitizeForMarkdown).join(', ')})`\n );\n }\n }\n if (graph.cycles.length > 0) {\n lines.push(' cycles (consumes should be acyclic):');\n for (const c of graph.cycles) lines.push(` ${c.map(sanitizeForMarkdown).join(' -> ')}`);\n }\n if (graph.orphans.length > 0) {\n lines.push(` custom skills with no declared consumes (${graph.orphans.length}):`);\n for (const o of graph.orphans.slice(0, 30)) lines.push(` - ${sanitizeForMarkdown(o)}`);\n }\n\n for (const n of report.notices) lines.push(` [notice] ${sanitizeForMarkdown(n)}`);\n return lines;\n}\n\nexport async function organizeCommand(options: OrganizeCommandOptions = {}): Promise<void> {\n const config = loadConfig();\n const report = buildOrganizeReport(config, { usageSnapshot: options.usageSnapshot });\n\n let outPath: string | undefined;\n if (options.out) {\n outPath = writeReport(options.out, JSON.stringify(report, null, 2) + '\\n');\n }\n\n if (options.json) {\n // Notices (incl. the beta notice) go to stderr — stdout stays exactly one JSON document.\n for (const n of report.notices) console.error(`[skill-forge] ${n}`);\n console.log(JSON.stringify(report, null, 2));\n return;\n }\n\n for (const line of renderSummary(report)) console.log(line);\n if (outPath) console.log(` report -> ${outPath}`);\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, realpathSync, statSync, type Dirent } from 'node:fs';\nimport { basename, join, resolve as resolvePath } from 'node:path';\nimport type { Config } from './config.js';\nimport { UserError } from './errors.js';\nimport { proFeatureStatus, upgradeNotice } from './license.js';\nimport { expandHome, isDir, stripQuotes } from './util.js';\n\n/**\n * `skill-forge organize` engine (Pro) — TypeScript port of the rhize-meta\n * `rhize-skill-forge` plugin's `index_skills.py` (capability registry) and\n * `build_dependency_graph.py` (consumes-edge dependency graph), merged into\n * one command. See references/capability-schema.md in that plugin for the\n * frontmatter keys this reads (`tier` / `domain` / `consumes` / `provenance`\n * / `maturity`).\n *\n * This module performs its OWN self-contained scan of `config.skillsRoots` —\n * it deliberately does NOT import `audit.ts`'s inventory walker, keeping the\n * two modules' file sets disjoint. Nodes and registry entries are keyed by\n * REALPATH-QUALIFIED stable IDs, not bare frontmatter names: two distinct\n * skills in two different roots that happen to share a `name:` must NOT\n * collapse into one node/adjacency entry the way a naive name-keyed port of\n * the Python scripts would. Ambiguous (`consumes` target name matches more\n * than one skill) and dangling (matches none) edges are reported rather than\n * silently guessed at.\n *\n * Nothing this module scans is ever executed — it only reads SKILL.md text\n * and (optionally) a usage-snapshot JSON file. The whole command is Pro\n * (`proFeatureStatus('set-level organizer reports')`), gated before any\n * filesystem walk, matching every other Pro entry point in this repo (see\n * src/evolve.ts). Free during the 0.x beta via `proFeatureStatus()`.\n */\n\nconst PRO_FEATURE_LABEL = 'set-level organizer reports';\n\nconst TIERS = new Set(['resource', 'custom']);\nconst MATURITIES = new Set(['seedling', 'stable', 'deprecated']);\n\nexport interface OrganizeOptions {\n /** Path to a skill-monitor-style snapshot JSON (`{ report: { top_skills: [[name, count], ...] } }`) to join usage counts from, by skill name. */\n usageSnapshot?: string;\n}\n\nexport interface OrganizeRegistryEntry {\n /** Realpath-qualified stable id — NOT the bare frontmatter name (which may collide across roots). */\n id: string;\n name: string;\n /** Realpath of the skill directory (same value as `id`). */\n path: string;\n /** The configured root (as given in config.skillsRoots) this skill was discovered under. */\n root: string;\n tier?: string;\n domain?: string;\n consumes: string[];\n provenance?: string;\n maturity?: string;\n validFrontmatter: boolean;\n usage?: number;\n}\n\nexport interface OrganizeUntaggedEntry {\n id: string;\n name: string;\n missing: string[];\n}\n\nexport interface OrganizeRegistry {\n skillsRoots: string[];\n count: number;\n byTier: Record<string, number>;\n byDomain: Record<string, number>;\n untagged: OrganizeUntaggedEntry[];\n warnings: string[];\n usageJoined: boolean;\n skills: OrganizeRegistryEntry[];\n}\n\nexport interface OrganizeGraphNode {\n id: string;\n name: string;\n tier?: string;\n domain?: string;\n}\n\nexport interface OrganizeGraphEdge {\n from: string;\n /** Raw `consumes` target name as declared in frontmatter — not yet resolved to an id. */\n to: string;\n /** ids of every skill whose name matches `to` — empty (dangling), one (resolved), or many (ambiguous). */\n resolvedIds: string[];\n}\n\nexport interface OrganizeGraphDanglingEdge {\n from: string;\n to: string;\n}\n\nexport interface OrganizeGraphAmbiguousEdge {\n from: string;\n to: string;\n matches: string[];\n}\n\nexport interface OrganizeGraphHotResource {\n id: string;\n name: string;\n consumedBy: number;\n}\n\nexport interface OrganizeGraph {\n nodeCount: number;\n edgeCount: number;\n nodes: OrganizeGraphNode[];\n edges: OrganizeGraphEdge[];\n /** ids of `tier: custom` skills with no declared `consumes`. */\n orphans: string[];\n hotResources: OrganizeGraphHotResource[];\n danglingEdges: OrganizeGraphDanglingEdge[];\n ambiguousConsumesTargets: OrganizeGraphAmbiguousEdge[];\n /** Each cycle is a sequence of ids, first === last (closing the loop) — only built from unambiguous (single-match) edges. */\n cycles: string[][];\n}\n\nexport interface OrganizeReport {\n generatedAt: string;\n registry: OrganizeRegistry;\n graph: OrganizeGraph;\n notices: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Self-contained scan (deliberately independent of audit.ts's walker)\n// ---------------------------------------------------------------------------\n\ninterface SkillCandidate {\n dirPath: string;\n root: string;\n}\n\n/** Subdirectories of `dir`, following directory-shaped symlinks. Swallows any read error (missing/permission) rather than throwing. */\nfunction listSubdirs(dir: string): string[] {\n let entries: Dirent[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n const out: string[] = [];\n for (const entry of entries) {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n out.push(full);\n continue;\n }\n if (entry.isSymbolicLink()) {\n try {\n if (statSync(full).isDirectory()) out.push(full);\n } catch {\n // broken symlink — skip\n }\n }\n }\n return out;\n}\n\n/** Mirrors index_skills.py's three glob shapes (top-level dirs, a \"skills\" subdir, and nested \"skills\" subdirs one level down), scoped to one resolved root. */\nfunction findSkillDirsUnderRoot(root: string): string[] {\n const found = new Set<string>();\n const topDirs = listSubdirs(root);\n\n for (const dir of topDirs) {\n if (existsSync(join(dir, 'SKILL.md'))) found.add(dir);\n }\n for (const dir of listSubdirs(join(root, 'skills'))) {\n if (existsSync(join(dir, 'SKILL.md'))) found.add(dir);\n }\n for (const topDir of topDirs) {\n for (const dir of listSubdirs(join(topDir, 'skills'))) {\n if (existsSync(join(dir, 'SKILL.md'))) found.add(dir);\n }\n }\n\n return Array.from(found).filter((p) => !p.includes('/.git/'));\n}\n\nfunction collectCandidates(roots: readonly string[]): SkillCandidate[] {\n const out: SkillCandidate[] = [];\n for (const root of roots) {\n const resolvedRoot = resolvePath(expandHome(root));\n if (!isDir(resolvedRoot)) continue;\n for (const dirPath of findSkillDirsUnderRoot(resolvedRoot)) {\n out.push({ dirPath, root });\n }\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Capability frontmatter parsing — port of index_skills.py's parse_frontmatter/parse_list\n// ---------------------------------------------------------------------------\n\ninterface ParsedCapabilityFrontmatter {\n name?: string;\n tier?: string;\n domain?: string;\n provenance?: string;\n maturity?: string;\n consumes: string[];\n validFrontmatter: boolean;\n}\n\n/** Parses a YAML list value, inline (`key: [a, b]`) or block (`- a` lines) — port of index_skills.py's parse_list. */\nfunction parseYamlList(block: string, key: string): string[] {\n const inlineMatch = block.match(new RegExp(`^${key}:\\\\s*\\\\[(.*?)\\\\]\\\\s*$`, 'm'));\n if (inlineMatch) {\n return inlineMatch[1]\n .split(',')\n .map((x) => stripQuotes(x.trim()))\n .filter(Boolean);\n }\n if (!new RegExp(`^${key}:\\\\s*$`, 'm').test(block)) return [];\n\n const lines = block.split(/\\r\\n|\\r|\\n/);\n const startIdx = lines.findIndex((l) => new RegExp(`^${key}:\\\\s*$`).test(l));\n if (startIdx === -1) return [];\n\n const out: string[] = [];\n for (const line of lines.slice(startIdx + 1)) {\n const itemMatch = line.match(/^\\s*-\\s*(.+)$/);\n if (itemMatch) {\n out.push(stripQuotes(itemMatch[1].trim()));\n } else if (line.trim() === '') {\n continue;\n } else {\n break;\n }\n }\n return out;\n}\n\n/** Extracts the frontmatter block (text between the opening and closing `---` fences), or null if unfenced/unclosed. */\nfunction extractFrontmatterBlock(text: string): string | null {\n if (!text.startsWith('---\\n')) return null;\n const afterOpen = text.slice(4);\n const closeIdx = afterOpen.indexOf('\\n---');\n if (closeIdx === -1) return null;\n return afterOpen.slice(0, closeIdx);\n}\n\n/** Port of index_skills.py's parse_frontmatter, scoped to the capability-schema fields organize needs (name/tier/domain/provenance/maturity/consumes). */\nfunction parseCapabilityFrontmatter(text: string, fallbackName: string): ParsedCapabilityFrontmatter {\n const block = extractFrontmatterBlock(text);\n if (block === null) {\n return { name: fallbackName, consumes: [], validFrontmatter: false };\n }\n\n const fields: Record<string, string> = {};\n for (const key of ['name', 'tier', 'domain', 'provenance', 'maturity']) {\n const m = block.match(new RegExp(`^${key}:\\\\s*(.+)$`, 'm'));\n if (m) fields[key] = stripQuotes(m[1].trim());\n }\n\n return {\n name: fields.name ?? fallbackName,\n tier: fields.tier,\n domain: fields.domain,\n provenance: fields.provenance,\n maturity: fields.maturity,\n consumes: parseYamlList(block, 'consumes'),\n validFrontmatter: true,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Usage snapshot join — port of index_skills.py's load_usage\n// ---------------------------------------------------------------------------\n\ninterface UsageSnapshotResult {\n usage: Map<string, number>;\n warning?: string;\n}\n\n/** Best-effort join of usage counts from a skill-monitor-style snapshot's `report.top_skills`. Never throws — any failure becomes a warning and an empty map. */\nfunction loadUsageSnapshot(snapshotPath: string): UsageSnapshotResult {\n const resolvedPath = resolvePath(expandHome(snapshotPath));\n let raw: string;\n try {\n raw = readFileSync(resolvedPath, 'utf-8');\n } catch (err) {\n return { usage: new Map(), warning: `could not read usage snapshot (${(err as Error).message}); proceeding without usage` };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n return { usage: new Map(), warning: `could not parse usage snapshot (${(err as Error).message}); proceeding without usage` };\n }\n\n const usage = new Map<string, number>();\n const outer = parsed as Record<string, unknown> | null;\n const report = outer && typeof outer === 'object' ? (outer.report as Record<string, unknown> | undefined) : undefined;\n const topSkills = report && typeof report === 'object' ? report.top_skills : undefined;\n\n if (Array.isArray(topSkills)) {\n for (const row of topSkills) {\n if (Array.isArray(row) && row.length === 2 && typeof row[0] === 'string' && typeof row[1] === 'number') {\n const [name, count] = row as [string, number];\n usage.set(name, count);\n if (name.includes(':')) {\n // bare-slug fallback for `plugin:skill` names, matching index_skills.py.\n const bare = name.split(':').pop()!;\n if (!usage.has(bare)) usage.set(bare, count);\n }\n }\n }\n }\n\n return { usage };\n}\n\n// ---------------------------------------------------------------------------\n// Dependency graph — port of build_dependency_graph.py\n// ---------------------------------------------------------------------------\n\nconst WHITE = 0;\nconst GRAY = 1;\nconst BLACK = 2;\n\n/** DFS cycle detection over an id-keyed adjacency map — direct port of build_dependency_graph.py's find_cycles. Only ever called with unambiguous (single-match) edges, since an ambiguous target has no single node to traverse to. */\nfunction findCycles(adjacency: Map<string, string[]>): string[][] {\n const color = new Map<string, number>();\n for (const id of adjacency.keys()) color.set(id, WHITE);\n const stack: string[] = [];\n const cycles: string[][] = [];\n\n function dfs(u: string): void {\n color.set(u, GRAY);\n stack.push(u);\n for (const v of adjacency.get(u) ?? []) {\n if (!color.has(v)) continue; // not a node in this adjacency map\n if (color.get(v) === GRAY && stack.includes(v)) {\n const idx = stack.indexOf(v);\n cycles.push([...stack.slice(idx), v]);\n } else if (color.get(v) === WHITE) {\n dfs(v);\n }\n }\n color.set(u, BLACK);\n stack.pop();\n }\n\n for (const id of adjacency.keys()) {\n if (color.get(id) === WHITE) dfs(id);\n }\n return cycles;\n}\n\nfunction buildGraph(entries: OrganizeRegistryEntry[], nameToIds: Map<string, string[]>): OrganizeGraph {\n const nodes: OrganizeGraphNode[] = entries.map((e) => ({ id: e.id, name: e.name, tier: e.tier, domain: e.domain }));\n const edges: OrganizeGraphEdge[] = [];\n const dangling: OrganizeGraphDanglingEdge[] = [];\n const ambiguous: OrganizeGraphAmbiguousEdge[] = [];\n const incoming = new Map<string, number>(entries.map((e) => [e.id, 0]));\n const adjacency = new Map<string, string[]>(entries.map((e) => [e.id, []]));\n\n for (const e of entries) {\n for (const target of e.consumes) {\n const matches = nameToIds.get(target) ?? [];\n edges.push({ from: e.id, to: target, resolvedIds: matches });\n\n if (matches.length === 0) {\n dangling.push({ from: e.id, to: target });\n } else if (matches.length === 1) {\n incoming.set(matches[0], (incoming.get(matches[0]) ?? 0) + 1);\n adjacency.get(e.id)!.push(matches[0]);\n } else {\n ambiguous.push({ from: e.id, to: target, matches });\n }\n }\n }\n\n const orphans = entries.filter((e) => e.tier === 'custom' && e.consumes.length === 0).map((e) => e.id);\n\n const byId = new Map(entries.map((e) => [e.id, e]));\n const hotResources: OrganizeGraphHotResource[] = Array.from(incoming.entries())\n .filter(([, count]) => count > 0)\n .map(([id, count]) => ({ id, name: byId.get(id)!.name, consumedBy: count }))\n .sort((a, b) => b.consumedBy - a.consumedBy);\n\n const cycles = findCycles(adjacency);\n\n return {\n nodeCount: nodes.length,\n edgeCount: edges.length,\n nodes,\n edges,\n orphans,\n hotResources,\n danglingEdges: dangling,\n ambiguousConsumesTargets: ambiguous,\n cycles,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Top-level engine\n// ---------------------------------------------------------------------------\n\n/** Resolves each candidate to a deduped, realpath-qualified registry entry. Read/resolve failures become warnings, not thrown errors. */\nfunction buildRegistryEntries(candidates: SkillCandidate[], warnings: string[]): OrganizeRegistryEntry[] {\n const entries: OrganizeRegistryEntry[] = [];\n const seenRealPaths = new Set<string>();\n\n for (const candidate of candidates) {\n let realPath: string;\n try {\n realPath = realpathSync(candidate.dirPath);\n } catch (err) {\n warnings.push(`could not resolve skill directory ${candidate.dirPath}: ${(err as Error).message}`);\n continue;\n }\n if (seenRealPaths.has(realPath)) continue;\n seenRealPaths.add(realPath);\n\n const skillMdPath = join(realPath, 'SKILL.md');\n let text: string;\n try {\n text = readFileSync(skillMdPath, 'utf-8');\n } catch (err) {\n warnings.push(`could not read ${skillMdPath}: ${(err as Error).message}`);\n continue;\n }\n\n const fm = parseCapabilityFrontmatter(text, basename(realPath));\n const name = fm.name ?? basename(realPath);\n if (fm.tier && !TIERS.has(fm.tier)) {\n warnings.push(`${name}: tier '${fm.tier}' not in ${JSON.stringify(Array.from(TIERS).sort())}`);\n }\n if (fm.maturity && !MATURITIES.has(fm.maturity)) {\n warnings.push(`${name}: maturity '${fm.maturity}' not in ${JSON.stringify(Array.from(MATURITIES).sort())}`);\n }\n\n entries.push({\n id: realPath,\n name,\n path: realPath,\n root: candidate.root,\n tier: fm.tier,\n domain: fm.domain,\n consumes: fm.consumes,\n provenance: fm.provenance,\n maturity: fm.maturity,\n validFrontmatter: fm.validFrontmatter,\n });\n }\n\n return entries;\n}\n\n/** Joins usage counts from a snapshot onto `entries` by name (mutates entries in place). Returns whether a non-empty snapshot was loaded. */\nfunction joinUsageSnapshot(entries: OrganizeRegistryEntry[], snapshotPath: string, warnings: string[]): boolean {\n const { usage, warning } = loadUsageSnapshot(snapshotPath);\n if (warning) warnings.push(warning);\n if (usage.size === 0) return false;\n\n for (const entry of entries) {\n const count = usage.get(entry.name);\n if (count !== undefined) entry.usage = count;\n }\n if (!entries.some((e) => e.usage !== undefined)) {\n warnings.push(\n 'usage snapshot loaded but 0 skills matched by name — registry `name:` vs telemetry key mismatch; the usage column will be empty'\n );\n }\n return true;\n}\n\n/** Groups entry ids by name, warning on any name shared across >1 id (e.g. duplicate skill names across roots). */\nfunction buildNameToIds(entries: OrganizeRegistryEntry[], warnings: string[]): Map<string, string[]> {\n const nameToIds = new Map<string, string[]>();\n for (const e of entries) {\n const ids = nameToIds.get(e.name) ?? [];\n ids.push(e.id);\n nameToIds.set(e.name, ids);\n }\n for (const [name, ids] of nameToIds) {\n if (ids.length > 1) {\n warnings.push(`duplicate skill name \"${name}\" across ${ids.length} locations: ${ids.join(', ')}`);\n }\n }\n return nameToIds;\n}\n\n/** Entries missing `tier` and/or `domain` — rot detection. */\nfunction findUntagged(entries: OrganizeRegistryEntry[]): OrganizeUntaggedEntry[] {\n return entries\n .filter((e) => !e.tier || !e.domain)\n .map((e) => ({\n id: e.id,\n name: e.name,\n missing: [!e.tier ? 'tier' : undefined, !e.domain ? 'domain' : undefined].filter((v): v is string => Boolean(v)),\n }));\n}\n\n/** Entry counts grouped by tier and by domain, with `undefined` bucketed as `'untagged'`. */\nfunction countByTierAndDomain(entries: OrganizeRegistryEntry[]): { byTier: Record<string, number>; byDomain: Record<string, number> } {\n const byTier: Record<string, number> = {};\n const byDomain: Record<string, number> = {};\n for (const e of entries) {\n byTier[e.tier ?? 'untagged'] = (byTier[e.tier ?? 'untagged'] ?? 0) + 1;\n byDomain[e.domain ?? 'untagged'] = (byDomain[e.domain ?? 'untagged'] ?? 0) + 1;\n }\n return { byTier, byDomain };\n}\n\n/**\n * Builds the capability registry + dependency graph for every skill under\n * `config.skillsRoots`. Pro-gated BEFORE any filesystem walk — a locked\n * license (post-1.0, no key) throws a UserError and never touches disk.\n * Never executes anything it scans.\n */\nexport function buildOrganizeReport(config: Config, options: OrganizeOptions = {}): OrganizeReport {\n const proStatus = proFeatureStatus(PRO_FEATURE_LABEL);\n if (!proStatus.enabled) {\n throw new UserError(proStatus.notice ?? upgradeNotice(PRO_FEATURE_LABEL));\n }\n const notices: string[] = proStatus.notice ? [proStatus.notice] : [];\n const warnings: string[] = [];\n\n const candidates = collectCandidates(config.skillsRoots);\n if (candidates.length === 0) {\n throw new UserError(\n `no SKILL.md found under any configured skills root (${config.skillsRoots.join(', ') || '(none configured)'})`\n );\n }\n\n const entries = buildRegistryEntries(candidates, warnings);\n\n const usageJoined = options.usageSnapshot ? joinUsageSnapshot(entries, options.usageSnapshot, warnings) : false;\n\n const nameToIds = buildNameToIds(entries, warnings);\n const untagged = findUntagged(entries);\n const { byTier, byDomain } = countByTierAndDomain(entries);\n\n const registry: OrganizeRegistry = {\n skillsRoots: [...config.skillsRoots],\n count: entries.length,\n byTier,\n byDomain,\n untagged,\n warnings,\n usageJoined,\n skills: entries,\n };\n\n const graph = buildGraph(entries, nameToIds);\n\n return {\n generatedAt: new Date().toISOString(),\n registry,\n graph,\n notices,\n };\n}\n","/**\n * skillsShApi.ts — hardened HTTP client for the skills.sh discovery/audit API\n * (https://skills.sh/docs/api, base https://skills.sh/api/v1). TypeScript port of\n * the free-tier `skills_sh.py` script (rhize-plugins repo) — MIT, no commercial header.\n *\n * Hardening (all required by the Lane B1 spec, see .claude/plans/forge-plugin-merge.md):\n * - every URL is built from a fixed origin constant via `new URL` + `URLSearchParams`\n * - candidate ids (\"{source}/{slug}\") are validated against a strict charset and each\n * path segment is individually `encodeURIComponent`-encoded (slashes preserved)\n * - every request uses `redirect: 'error'` and a 15s `AbortSignal.timeout`\n * - response bodies are capped at 2 MB (checked before JSON.parse) and the parsed shape\n * is validated before use — malformed/oversized bodies fail loud, never partially used\n * - the bearer token is read from `process.env.VERCEL_OIDC_TOKEN` only, is never logged,\n * echoed, or included in any thrown error message\n * - `fetch` is injectable so tests never hit the network (mirrors the `ExecFn` DI idiom\n * in src/install/quarantine.ts)\n */\nexport const SKILLS_SH_ORIGIN = 'https://skills.sh';\nexport const SKILLS_SH_API_BASE = '/api/v1';\nconst MAX_RESPONSE_BYTES = 2 * 1024 * 1024; // 2 MB\nconst REQUEST_TIMEOUT_MS = 15_000;\nconst ID_PATTERN = /^[A-Za-z0-9._/-]+$/;\n\nexport const TOKEN_HINT =\n 'No VERCEL_OIDC_TOKEN found. skills.sh authenticates with a short-lived Vercel OIDC token:\\n' +\n ' 1. Vercel dashboard -> your project -> Settings -> OIDC Federation -> enable.\\n' +\n ' 2. npm i -g vercel && vercel link && vercel env pull (writes VERCEL_OIDC_TOKEN to .env.local)\\n' +\n ' 3. export VERCEL_OIDC_TOKEN from .env.local into your shell before running this command\\n' +\n ' (or set it directly: export VERCEL_OIDC_TOKEN=...) - docs: https://skills.sh/docs/api';\n\n/** Thrown for missing token / hostile id / oversized or malformed body / unexpected response shape. Callers set `process.exitCode` from `.exitCode`. */\nexport class SkillsShError extends Error {\n exitCode: number;\n constructor(message: string, exitCode: number) {\n super(message);\n this.name = 'SkillsShError';\n this.exitCode = exitCode;\n }\n}\n\nexport type FetchFn = typeof fetch;\n\nexport interface SkillsShSearchItem {\n id: string;\n name?: string;\n installs?: number | string;\n installUrl?: string;\n [key: string]: unknown;\n}\n\nexport interface SkillsShSearchResponse {\n data: SkillsShSearchItem[];\n count?: number;\n searchType?: string;\n [key: string]: unknown;\n}\n\nexport interface SkillsShAuditEntry {\n provider?: string;\n status?: string;\n riskLevel?: string;\n summary?: string;\n [key: string]: unknown;\n}\n\nexport interface SkillsShAuditResponse {\n audits: SkillsShAuditEntry[];\n [key: string]: unknown;\n}\n\nconst RISK_ORDER: Record<string, number> = { NONE: 0, LOW: 1, MEDIUM: 2, HIGH: 3, CRITICAL: 4 };\n\n/** Reads the OIDC token from the environment only — never from a config file or CLI flag, so it's never persisted or accidentally echoed by `status`/`--json`. */\nexport function getToken(env: NodeJS.ProcessEnv = process.env): string | undefined {\n const tok = env.VERCEL_OIDC_TOKEN;\n return tok ? tok : undefined;\n}\n\n/** Validates a skills.sh candidate id (\"{source}/{slug}\", e.g. \"vercel-labs/skills/find-skills\") against a strict charset before it ever reaches URL construction. */\nfunction assertValidId(id: string): void {\n if (id.length === 0 || !ID_PATTERN.test(id)) {\n throw new SkillsShError(`invalid skills.sh id: ${JSON.stringify(id)}`, 1);\n }\n}\n\n/**\n * Validates `id` and splits it into path segments — the id must never reach `call()` unvalidated.\n * Beyond the charset check, rejects empty, `.` and `..` segments: WHATWG URL parsing normalizes\n * dot-segments on the ASSEMBLED path (after per-segment encoding), so `../..` in an id would\n * otherwise walk the request out of the /api/v1 namespace while still carrying the bearer token.\n */\nfunction validatedIdSegments(id: string): string[] {\n assertValidId(id);\n const segments = id.split('/');\n if (segments.some((s) => s === '' || s === '.' || s === '..')) {\n throw new SkillsShError(`invalid skills.sh id: ${JSON.stringify(id)}`, 1);\n }\n return segments;\n}\n\n/** Builds a same-origin skills.sh API URL from a fixed origin constant, encoding each path segment individually so slashes in ids stay structural rather than escaped. */\nfunction buildUrl(pathSegments: string[], params?: Record<string, string | number>): URL {\n const url = new URL(SKILLS_SH_API_BASE + '/' + pathSegments.map((s) => encodeURIComponent(s)).join('/'), SKILLS_SH_ORIGIN);\n if (params) {\n const search = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) search.set(k, String(v));\n url.search = search.toString();\n }\n return url;\n}\n\n/** Reads a Response body as text, refusing anything over MAX_RESPONSE_BYTES before it's ever handed to JSON.parse. */\nasync function readCappedText(res: Response): Promise<string> {\n const text = await res.text();\n // TextEncoder gives the actual byte length (UTF-8), not just JS string length.\n if (new TextEncoder().encode(text).length > MAX_RESPONSE_BYTES) {\n throw new SkillsShError('skills.sh response exceeded the 2 MB size cap', 1);\n }\n return text;\n}\n\ninterface CallOptions {\n fetchImpl?: FetchFn;\n env?: NodeJS.ProcessEnv;\n}\n\n/**\n * Core authenticated GET against the skills.sh API. Returns `undefined` for a 404\n * (matches skills_sh.py's `return None` for \"not found yet\"). Never echoes the raw\n * response body of a non-2xx status back to the caller — only a truncated, token-free\n * snippet — so credentials or internal detail can't leak through an error message.\n */\nasync function call(pathSegments: string[], params: Record<string, string | number> | undefined, options: CallOptions = {}): Promise<unknown> {\n const env = options.env ?? process.env;\n const token = getToken(env);\n if (!token) {\n throw new SkillsShError(TOKEN_HINT, 3);\n }\n\n const url = buildUrl(pathSegments, params);\n const fetchImpl = options.fetchImpl ?? fetch;\n\n let res: Response;\n try {\n res = await fetchImpl(url, {\n headers: { Authorization: `Bearer ${token}` },\n redirect: 'error',\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new SkillsShError(`skills.sh request failed: ${message}`, 1);\n }\n\n if (res.status === 404) {\n return undefined;\n }\n\n if (!res.ok) {\n // 401 never echoes the response body — an auth-failure body can carry request\n // detail we don't want surfaced (and never the token, but keep the margin wide).\n if (res.status === 401) {\n throw new SkillsShError('skills.sh HTTP 401: unauthorized — the VERCEL_OIDC_TOKEN was rejected (expired or invalid)', 3);\n }\n let snippet = '';\n try {\n snippet = (await readCappedText(res)).slice(0, 300);\n } catch {\n // ignore body-read failures on an already-erroring response\n }\n throw new SkillsShError(`skills.sh HTTP ${res.status}${snippet ? `: ${snippet}` : ''}`, 1);\n }\n\n const text = await readCappedText(res);\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n throw new SkillsShError('skills.sh returned malformed JSON', 1);\n }\n return parsed;\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/** `GET /skills/search?q=&limit=` — validates the response has a `data` array before returning it. */\nexport async function search(query: string, limit: number, options: CallOptions = {}): Promise<SkillsShSearchResponse> {\n const data = await call(['skills', 'search'], { q: query, limit }, options);\n if (!isRecord(data) || !Array.isArray(data.data)) {\n throw new SkillsShError('skills.sh search response had an unexpected shape', 1);\n }\n return data as unknown as SkillsShSearchResponse;\n}\n\n/** `GET /skills/audit/{id}` — `undefined` means no partner audits exist yet for this id. */\nexport async function audit(id: string, options: CallOptions = {}): Promise<SkillsShAuditResponse | undefined> {\n const data = await call(['skills', 'audit', ...validatedIdSegments(id)], undefined, options);\n if (data === undefined) return undefined;\n if (!isRecord(data) || !Array.isArray(data.audits)) {\n throw new SkillsShError('skills.sh audit response had an unexpected shape', 1);\n }\n return data as unknown as SkillsShAuditResponse;\n}\n\n/** `GET /skills/{id}` — full detail + file tree, returned raw (only used for `--json`/get output). */\nexport async function get(id: string, options: CallOptions = {}): Promise<unknown> {\n return call(['skills', ...validatedIdSegments(id)], undefined, options);\n}\n\n/** `GET /skills/curated`. */\nexport async function curated(options: CallOptions = {}): Promise<unknown> {\n return call(['skills', 'curated'], undefined, options);\n}\n\n/** Aggregates a `SkillsShAuditResponse` into (verdict, worst risk level, audits) — port of skills_sh.py's `audit_verdict`. */\nexport function auditVerdict(data: SkillsShAuditResponse): { verdict: 'fail' | 'warn' | 'pass' | 'none'; risk: string; audits: SkillsShAuditEntry[] } {\n const audits = data.audits ?? [];\n const statuses = new Set(audits.map((a) => a.status));\n let worst = 'NONE';\n let worstRank = 0;\n for (const a of audits) {\n const rl = a.riskLevel ?? 'NONE';\n const rank = RISK_ORDER[rl] ?? 0;\n if (rank > worstRank) {\n worstRank = rank;\n worst = rl;\n }\n }\n const verdict: 'fail' | 'warn' | 'pass' | 'none' = statuses.has('fail')\n ? 'fail'\n : statuses.has('warn')\n ? 'warn'\n : audits.length > 0\n ? 'pass'\n : 'none';\n return { verdict, risk: worst, audits };\n}\n","import { sanitizeForMarkdown } from '../audit.js';\nimport { UserError } from '../errors.js';\nimport {\n audit as apiAudit,\n auditVerdict,\n curated as apiCurated,\n get as apiGet,\n search as apiSearch,\n SkillsShError,\n type FetchFn,\n type SkillsShSearchItem,\n} from '../gate/skillsShApi.js';\n\nexport interface FindOptions {\n limit?: number;\n json?: boolean;\n audit?: string;\n get?: string;\n curated?: boolean;\n}\n\nexport interface FindDeps {\n fetchImpl?: FetchFn;\n env?: NodeJS.ProcessEnv;\n}\n\nconst INSTALL_HINT_NOTE =\n 'Results are unvetted — `skill-forge add <id>` runs the full quarantine + safety gate; ' +\n '`skill-forge find --audit <id>` shows partner audit verdicts.';\n\nfunction isRecordWithDataArray(v: unknown): v is { data: SkillsShSearchItem[]; count?: number; searchType?: string } {\n return typeof v === 'object' && v !== null && Array.isArray((v as { data?: unknown }).data);\n}\n\n/** Emits exactly one JSON document on stdout — the `--json` contract shared by every mode. */\nfunction printJson(data: unknown): void {\n console.log(JSON.stringify(data, null, 2));\n}\n\nfunction printSearchResults(data: { data: SkillsShSearchItem[]; count?: number; searchType?: string }, query: string): void {\n const items = data.data;\n const count = data.count ?? items.length;\n const searchType = data.searchType ? ` (${sanitizeForMarkdown(data.searchType)})` : '';\n console.log(`skills.sh — ${count} matches for ${JSON.stringify(query)}${searchType}:`);\n for (const item of items) {\n const name = sanitizeForMarkdown(String(item.name ?? item.id));\n const id = sanitizeForMarkdown(String(item.id));\n const installs = item.installs ?? '?';\n console.log(` - ${name} [${id}] ${installs} installs [unvetted]`);\n console.log(` install: skill-forge add ${id}`);\n }\n console.log('');\n console.log(INSTALL_HINT_NOTE);\n}\n\n/**\n * `skill-forge find` — TypeScript port of the plugin's `skills_sh.py`. Exactly one mode\n * per invocation: a search query (default), `--audit <id>`, `--get <id>`, or `--curated`.\n * All requests hit https://skills.sh/api/v1 over HTTPS with a `VERCEL_OIDC_TOKEN` bearer\n * token (never printed); a missing/invalid token fails loud with setup guidance and\n * `process.exitCode = 3` before any request is made. Search/curated/audit results are\n * always labeled unvetted — the deep quarantine + safety gate only runs on `add`.\n */\nexport async function find(query: string | undefined, options: FindOptions = {}, deps: FindDeps = {}): Promise<void> {\n const modes = [query !== undefined, options.audit !== undefined, options.get !== undefined, Boolean(options.curated)];\n const activeCount = modes.filter(Boolean).length;\n if (activeCount === 0) {\n throw new UserError('skill-forge find: provide a search query, or exactly one of --audit <id>, --get <id>, --curated');\n }\n if (activeCount > 1) {\n throw new UserError('skill-forge find: exactly one mode per invocation — a search query, or exactly one of --audit <id>, --get <id>, --curated');\n }\n\n const callOptions = { fetchImpl: deps.fetchImpl, env: deps.env };\n\n try {\n if (query !== undefined) {\n if (query.trim().length < 2) {\n throw new UserError('skill-forge find: query must be at least 2 characters');\n }\n const limit = options.limit ?? 10;\n const data = await apiSearch(query, limit, callOptions);\n if (options.json) {\n printJson(data);\n return;\n }\n printSearchResults(data, query);\n return;\n }\n\n if (options.audit !== undefined) {\n const id = options.audit;\n const data = await apiAudit(id, callOptions);\n if (!data) {\n console.log(`no partner audits yet for ${sanitizeForMarkdown(id)} (generated after first install).`);\n return;\n }\n if (options.json) {\n printJson(data);\n return;\n }\n const { verdict, risk, audits } = auditVerdict(data);\n console.log(\n `skills.sh audit for ${sanitizeForMarkdown(id)}: ${verdict.toUpperCase()} (max risk ${sanitizeForMarkdown(risk)}) — ${audits.length} partner(s)`\n );\n for (const entry of audits) {\n const provider = sanitizeForMarkdown(String(entry.provider ?? 'unknown'));\n const status = sanitizeForMarkdown(String(entry.status ?? 'unknown'));\n const riskLevel = entry.riskLevel ? ` · ${sanitizeForMarkdown(String(entry.riskLevel))}` : '';\n const summary = entry.summary ? ` — ${sanitizeForMarkdown(String(entry.summary))}` : '';\n console.log(` - ${provider}: ${status}${riskLevel}${summary}`);\n }\n return;\n }\n\n if (options.get !== undefined) {\n const data = await apiGet(options.get, callOptions);\n printJson(data);\n return;\n }\n\n // --curated\n const data = await apiCurated(callOptions);\n if (options.json) {\n printJson(data);\n return;\n }\n if (isRecordWithDataArray(data)) {\n printSearchResults(data, '(curated)');\n return;\n }\n printJson(data);\n } catch (err) {\n if (err instanceof SkillsShError) {\n console.error(err.message);\n process.exitCode = err.exitCode;\n return;\n }\n throw err;\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 { realpathSync } from 'node:fs';\nimport { join, resolve as resolvePath } from 'node:path';\nimport { sanitizeForMarkdown } from '../audit.js';\nimport { loadConfig, type Config } from '../config.js';\nimport { defaultExec } from '../install/quarantine.js';\nimport type { ExecFn } from '../install/quarantine.js';\nimport { proFeatureStatus } from '../license.js';\nimport { readProvenanceLedger } from '../provenance.js';\nimport { expandHome, isDir } from '../util.js';\n\n/**\n * `skill-forge watch` (Pro) — TS port of `record_provenance.py --check-drift`. Scans EVERY\n * configured skills root's `SOURCES.md` provenance ledger (src/provenance.ts's\n * `readProvenanceLedger`/`parseProvenanceLedger`) and reports, per tracked (non-REJECT) entry,\n * whether its recorded upstream ref still matches what the source currently resolves to.\n *\n * HARD SECURITY INVARIANT: the ledger's stored `Drift check` command string is\n * attacker-influenceable data (anyone who can write to a skills root's SOURCES.md controls it) —\n * it is NEVER executed, under any circumstances. It is only ever printed, sanitized, as a\n * suggestion for the human/agent to run themselves. The only subprocess this module ever spawns\n * is a fixed, first-party `git ls-remote <url> [ref]` invocation (argv array, no shell), and only\n * for entries whose Source is recognizably a git URL and whose Upstream ref looks like a git ref\n * — see `test/watch.test.ts`'s guard test asserting no spawn happens for anything else.\n */\n\nconst PRO_FEATURE_LABEL = 'provenance drift watch (watch)';\n\nexport type DriftCheckMethod = 'git-ls-remote' | 'manual';\nexport type DriftStatus = 'moved' | 'unchanged' | 'unreachable' | 'not-checked';\n\nexport interface WatchRow {\n /** The configured root (raw, as given in config.skillsRoots) this entry's ledger belongs to. */\n root: string;\n ledgerPath: string;\n name: string;\n verb: string;\n source: string;\n upstreamRef: string;\n checkMethod: DriftCheckMethod;\n driftStatus: DriftStatus;\n detail: string;\n /** Stored drift-check command string, as recorded — DATA ONLY, never executed. See module doc comment. */\n driftCheckCommand: string;\n}\n\nexport interface WatchedRoot {\n root: string;\n realPath: string;\n exists: boolean;\n ledgerPath: string;\n ledgerExists: boolean;\n}\n\nexport interface WatchReport {\n roots: WatchedRoot[];\n rows: WatchRow[];\n /** Ledger parse warnings (malformed blocks skipped) — see provenance.ts's `parseProvenanceLedger`. */\n warnings: string[];\n notices: string[];\n}\n\nexport interface WatchOptions {\n json?: boolean;\n /** Skip all network calls — every entry is reported `not-checked` for manual comparison instead. */\n offline?: boolean;\n}\n\nexport interface WatchDeps {\n /** Injected subprocess runner for `git ls-remote`. Defaults to a real argv-array spawn (no shell). */\n exec?: ExecFn;\n}\n\n/** True for an `https://`/`git@` URL — the only source shapes `git ls-remote` can be pointed at. */\nfunction isGitUrl(source: string): boolean {\n const s = source.trim();\n return /^https?:\\/\\/\\S+$/i.test(s) || /^git@[\\w.-]+:\\S+$/i.test(s);\n}\n\n/** True when `ref` is a plausible git ref token (commit sha or tag/branch name) — no whitespace or shell metacharacters, not empty, not the ledger's \"n/a\" placeholder. */\n/**\n * Only a commit-sha pin can be drift-compared against `git ls-remote` output — the remote\n * always resolves to a sha, so a tag/version-string pin (e.g. `v1.2.0`, or a frontmatter\n * version recorded by `add`) would compare a name against a sha and report \"moved\" on every\n * run. Non-sha refs route to the manual lane instead of getting a false verdict.\n */\nfunction looksLikeGitRef(ref: string): boolean {\n const r = ref.trim();\n return /^[0-9a-fA-F]{7,40}$/.test(r);\n}\n\nfunction firstNonEmptyLine(text: string): string {\n return text.split('\\n').find((l) => l.trim().length > 0)?.trim() ?? '(no output)';\n}\n\n/**\n * Runs `git ls-remote -- <url> <ref>` (fixed argv, no shell) and compares the resolved sha\n * against the recorded `ref`. This is the ONLY subprocess this module ever spawns, and it never\n * touches the ledger's stored `driftCheck` field.\n */\nasync function checkGitDrift(exec: ExecFn, url: string, ref: string): Promise<{ status: DriftStatus; detail: string }> {\n let result;\n try {\n result = await exec('git', ['ls-remote', '--', url, ref], { cwd: process.cwd() });\n } catch (err) {\n return { status: 'unreachable', detail: `git ls-remote failed to run: ${(err as Error).message}` };\n }\n if (result.code !== 0) {\n return { status: 'unreachable', detail: `git ls-remote exited ${result.code}: ${firstNonEmptyLine(result.stderr || result.stdout)}` };\n }\n // An annotated tag returns two lines (tag object + peeled `^{}` commit) — compare the\n // recorded ref against EVERY returned sha so a peeled-commit pin isn't a false \"moved\".\n const shas = result.stdout\n .split('\\n')\n .map((l) => l.trim())\n .filter((l) => l.length > 0)\n .map((l) => l.split(/\\s+/)[0] ?? '')\n .filter((sha) => sha.length > 0);\n if (shas.length === 0) {\n return { status: 'unreachable', detail: `no ref matching \"${ref}\" found on remote` };\n }\n const recorded = ref.trim().toLowerCase();\n if (shas.some((sha) => sha.toLowerCase() === recorded)) {\n return { status: 'unchanged', detail: `current: ${recorded}` };\n }\n return { status: 'moved', detail: `recorded ${ref}, remote now resolves to ${shas[0]}` };\n}\n\n/**\n * Scans every configured skills root's provenance ledger and classifies each tracked\n * (non-REJECT) entry's drift-check status. Pro-gated BEFORE any file read — a locked license\n * returns an empty report with a notice and touches nothing on disk. `config` is an explicit\n * parameter (rather than this function calling `loadConfig()` itself) so tests can point it at a\n * fixture set of skills roots without touching `~/.skill-forge/config.json` — mirrors\n * `auditConfiguredSet(config, options)`'s shape in src/audit.ts. `watchCommand` below is the CLI\n * entry point that supplies the real, loaded config.\n */\nexport async function runWatch(config: Config, options: WatchOptions = {}, deps: WatchDeps = {}): Promise<WatchReport> {\n const proStatus = proFeatureStatus(PRO_FEATURE_LABEL);\n if (!proStatus.enabled) {\n return { roots: [], rows: [], warnings: [], notices: [proStatus.notice ?? 'Pro feature (watch) locked.'] };\n }\n\n const notices: string[] = proStatus.notice ? [proStatus.notice] : [];\n const exec = deps.exec ?? defaultExec;\n\n const roots: WatchedRoot[] = [];\n const rows: WatchRow[] = [];\n const warnings: string[] = [];\n const seenRealPaths = new Set<string>();\n\n for (const root of config.skillsRoots) {\n const resolvedRoot = resolvePath(expandHome(root));\n const exists = isDir(resolvedRoot);\n let realPath = resolvedRoot;\n if (exists) {\n try {\n realPath = realpathSync(resolvedRoot);\n } catch {\n realPath = resolvedRoot;\n }\n }\n if (seenRealPaths.has(realPath)) continue;\n seenRealPaths.add(realPath);\n\n // Per-root failure tolerance (mirrors audit.ts's per-item philosophy): an unreadable\n // ledger in one root becomes a warning, never an abort of the whole drift check.\n let ledger: ReturnType<typeof readProvenanceLedger>;\n try {\n ledger = readProvenanceLedger(resolvedRoot);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const ledgerPath = join(resolvedRoot, 'SOURCES.md');\n roots.push({ root, realPath, exists, ledgerPath, ledgerExists: false });\n warnings.push(`${ledgerPath}: could not read ledger: ${message}`);\n continue;\n }\n roots.push({ root, realPath, exists, ledgerPath: ledger.ledgerPath, ledgerExists: ledger.exists });\n warnings.push(...ledger.warnings.map((w) => `${ledger.ledgerPath}: ${w}`));\n\n for (const entry of ledger.entries) {\n // Mirrors record_provenance.py's check-drift: a rejected source isn't tracked going forward.\n if (entry.verb === 'REJECT') continue;\n\n const rowBase = {\n root,\n ledgerPath: ledger.ledgerPath,\n name: entry.name,\n verb: entry.verb,\n source: entry.source,\n upstreamRef: entry.upstreamRef,\n driftCheckCommand: entry.driftCheck,\n };\n\n if (!(isGitUrl(entry.source) && looksLikeGitRef(entry.upstreamRef))) {\n rows.push({\n ...rowBase,\n checkMethod: 'manual',\n driftStatus: 'not-checked',\n detail: 'source is not a recognized git URL + ref pin — check manually',\n });\n continue;\n }\n\n if (options.offline) {\n rows.push({\n ...rowBase,\n checkMethod: 'git-ls-remote',\n driftStatus: 'not-checked',\n detail: '--offline: network check skipped',\n });\n continue;\n }\n\n const { status, detail } = await checkGitDrift(exec, entry.source, entry.upstreamRef);\n rows.push({ ...rowBase, checkMethod: 'git-ls-remote', driftStatus: status, detail });\n }\n }\n\n return { roots, rows, warnings, notices };\n}\n\nfunction printWatchTerminal(report: WatchReport): void {\n console.log('[skill-forge] watch — provenance drift check');\n for (const notice of report.notices) console.log(`[notice] ${notice}`);\n\n if (report.roots.length === 0) {\n console.log('\\nNo configured skills roots.');\n }\n\n for (const root of report.roots) {\n console.log(`\\n${sanitizeForMarkdown(root.root)} (${sanitizeForMarkdown(root.realPath)})`);\n if (!root.exists) {\n console.log(' root does not exist on disk');\n continue;\n }\n if (!root.ledgerExists) {\n console.log(` no ledger at ${sanitizeForMarkdown(root.ledgerPath)}`);\n continue;\n }\n console.log(` ledger: ${sanitizeForMarkdown(root.ledgerPath)}`);\n\n const rowsForRoot = report.rows.filter((r) => r.root === root.root);\n if (rowsForRoot.length === 0) {\n console.log(' (no tracked, non-REJECT sources)');\n continue;\n }\n for (const row of rowsForRoot) {\n console.log(` • ${sanitizeForMarkdown(row.name)} [${sanitizeForMarkdown(row.verb)}] ref=${sanitizeForMarkdown(row.upstreamRef)}`);\n if (row.checkMethod === 'git-ls-remote') {\n console.log(` drift: ${row.driftStatus} — ${sanitizeForMarkdown(row.detail)}`);\n } else {\n console.log(` manual check — stored drift-check command (data, NOT executed): ${sanitizeForMarkdown(row.driftCheckCommand)}`);\n }\n }\n }\n\n if (report.warnings.length > 0) {\n console.log('\\nWarnings:');\n for (const w of report.warnings) console.log(` - ${sanitizeForMarkdown(w)}`);\n }\n\n console.log('\\nCompare each stored ref to current upstream; re-run ingestion on any that moved.');\n}\n\n/**\n * `skill-forge watch` CLI entry point. `--json` prints exactly one JSON document on stdout;\n * notices go to stderr in that mode so stdout stays parseable.\n */\nexport async function watchCommand(options: WatchOptions = {}): Promise<void> {\n const report = await runWatch(loadConfig(), options);\n\n if (options.json) {\n console.log(JSON.stringify(report, null, 2));\n for (const notice of report.notices) console.error(`[skill-forge] ${notice}`);\n return;\n }\n\n printWatchTerminal(report);\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 { realpathSync } from 'node:fs';\nimport { dirname, isAbsolute, relative, resolve as resolvePath } from 'node:path';\nimport { loadConfig, type Config } from '../config.js';\nimport { UserError } from '../errors.js';\nimport { runHandoff } from '../handoff.js';\nimport { proFeatureStatus } from '../license.js';\nimport { getQueuePath, readQueue, updateQueueEntry, type QueueEntry, type QueueStatus } from '../queue.js';\n\n/**\n * `skill-forge ingest` (Pro) — the queue-drain UX that previously only existed as the\n * `rhize-meta` plugin's `/rhize-meta:forge-ingest` slash command (no `<source>` form). Direct\n * single-source ingestion is NOT this command — use `skill-forge add <source> --ingest`, which\n * runs the full quarantine/gate pipeline before optionally handing off.\n *\n * `--list` is read-only (no handoff, no writes). The no-arg form validates every pending entry's\n * recorded path before handing off ONCE to the configured agent with the bundled\n * `assets/ingest-prompt.md` and the queue file path — see src/handoff.ts. `queueClose` below is\n * the thin `skill-forge queue close <id> --status ...` wiring so an agent never hand-edits\n * queue.json directly.\n */\n\nconst PRO_FEATURE_LABEL = 'the pending-ingestion queue';\nconst VALID_STATUSES: QueueStatus[] = ['ingested', 'dismissed'];\n\nexport interface IngestOptions {\n list?: boolean;\n json?: boolean;\n}\n\nexport interface QueueCloseOptions {\n status?: string;\n}\n\ninterface ListRow {\n id: string;\n source: string;\n artifactType: 'skill' | 'mcp';\n safetyVerdict: QueueEntry['gate']['safetyVerdict'];\n createdAt: string;\n}\n\ninterface ExcludedEntry {\n entry: QueueEntry;\n reason: string;\n}\n\nexport interface ValidationResult {\n valid: QueueEntry[];\n excluded: ExcludedEntry[];\n}\n\nfunction requireProFeature(): void {\n const status = proFeatureStatus(PRO_FEATURE_LABEL);\n if (!status.enabled) {\n throw new UserError(\n status.notice ?? 'ingest is a Pro feature — set SKILL_FORGE_LICENSE or config.licenseKey to a valid license to unlock.'\n );\n }\n if (status.notice) console.error(`[skill-forge] ${status.notice}`);\n}\n\n/** `entr` + `y`/`ies` for `count`, e.g. `pluralizeEntries(1)` -> `'entry'`, `pluralizeEntries(2)` -> `'entries'`. */\nfunction pluralizeEntries(count: number): string {\n return `entr${count === 1 ? 'y' : 'ies'}`;\n}\n\nfunction toListRow(entry: QueueEntry): ListRow {\n return {\n id: entry.id,\n source: entry.source,\n artifactType: entry.artifactType ?? 'skill',\n safetyVerdict: entry.gate.safetyVerdict,\n createdAt: entry.createdAt,\n };\n}\n\nfunction printPendingList(pending: QueueEntry[], json?: boolean): void {\n if (json) {\n console.log(JSON.stringify({ entries: pending.map(toListRow) }, null, 2));\n return;\n }\n if (pending.length === 0) {\n console.log('[skill-forge] no pending queue entries.');\n return;\n }\n console.log(`[skill-forge] ${pending.length} pending ${pluralizeEntries(pending.length)}:`);\n for (const entry of pending) {\n const row = toListRow(entry);\n console.log(` ${row.id} ${row.source} (${row.artifactType}) verdict=${row.safetyVerdict} created=${row.createdAt}`);\n }\n}\n\n/** `realpathSync`, swallowing any failure (missing path, broken symlink, permission error) to null. */\nfunction realpathOrNull(p: string): string | null {\n try {\n return realpathSync(p);\n } catch {\n return null;\n }\n}\n\n/** True when `candidate` is `root` or a descendant of it, comparing already-canonicalized (realpath'd) paths. */\nfunction isUnderRoot(candidate: string, root: string): boolean {\n const rel = relative(root, candidate);\n return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));\n}\n\n/**\n * Validates that every pending entry's effective path (`installedPath` once promoted, else\n * `quarantinePath`) canonicalizes under one of the configured roots: the quarantine dir, a\n * configured skills root, or a configured MCP target file's directory. `queue.json` is a plain,\n * user-writable file — nothing stops a hand-edited or malicious entry from pointing anywhere on\n * disk, so this check runs before the handoff and every failure is reported. NOTE: the handoff\n * passes the queue FILE path, so excluded entries still exist inside it — the ingest prompt\n * instructs the agent to re-apply the same containment check per entry, and the terminal output\n * tells the user how to dismiss a failed entry. Entries that fail (path\n * missing, broken/cyclic symlink, or a realpath that escapes every configured root — e.g. a\n * symlink planted inside quarantineDir pointing outside it) are excluded with a reason rather\n * than blocking the rest of the drain.\n */\nexport function validatePendingEntries(entries: QueueEntry[], config: Config): ValidationResult {\n const quarantineRoot = realpathOrNull(resolvePath(config.quarantineDir));\n const skillsRoots = config.skillsRoots.map((r) => realpathOrNull(resolvePath(r))).filter((r): r is string => r !== null);\n const mcpTargetDirs = (config.mcpTargets ?? [])\n .map((f) => realpathOrNull(dirname(resolvePath(f))))\n .filter((r): r is string => r !== null);\n const allowedRoots = [quarantineRoot, ...skillsRoots, ...mcpTargetDirs].filter((r): r is string => r !== null);\n\n const valid: QueueEntry[] = [];\n const excluded: ExcludedEntry[] = [];\n\n for (const entry of entries) {\n const effectivePath = entry.installedPath ?? entry.quarantinePath;\n if (!effectivePath) {\n excluded.push({ entry, reason: 'no installedPath or quarantinePath recorded on the entry' });\n continue;\n }\n const real = realpathOrNull(resolvePath(effectivePath));\n if (!real) {\n excluded.push({ entry, reason: `path does not exist or could not be resolved: ${effectivePath}` });\n continue;\n }\n if (allowedRoots.length === 0 || !allowedRoots.some((root) => isUnderRoot(real, root))) {\n excluded.push({ entry, reason: `path escapes every configured root (quarantineDir/skillsRoots/mcpTargets): ${effectivePath}` });\n continue;\n }\n valid.push(entry);\n }\n\n return { valid, excluded };\n}\n\n/**\n * `skill-forge ingest [--list] [--json]` — see module doc comment. `--list` is read-only and\n * skipped from the Pro gate's write path but still Pro-gated (the queue itself is a Pro\n * artifact); the no-arg form gates first, then reads the queue, validates pending entries, and\n * hands off once via `runHandoff` with the queue file path as the handoff target.\n */\nexport async function ingest(options: IngestOptions = {}): Promise<void> {\n requireProFeature();\n\n const queue = readQueue();\n const pending = queue.entries.filter((e) => e.status === 'pending');\n\n if (options.list) {\n printPendingList(pending, options.json);\n return;\n }\n\n if (pending.length === 0) {\n console.log('[skill-forge] no pending queue entries to ingest.');\n return;\n }\n\n const config = loadConfig();\n const { valid, excluded } = validatePendingEntries(pending, config);\n\n if (excluded.length > 0) {\n console.error(`[skill-forge] excluded ${excluded.length} pending ${pluralizeEntries(excluded.length)} that failed path validation:`);\n for (const { entry, reason } of excluded) {\n console.error(` - ${entry.id} (${entry.source}): ${reason}`);\n }\n console.error(\n '[skill-forge] excluded entries stay in the queue — review each, then dismiss with: skill-forge queue close <id> --status dismissed'\n );\n }\n\n if (valid.length === 0) {\n console.log('[skill-forge] no pending entries passed path validation — nothing to hand off.');\n return;\n }\n\n console.log(`[skill-forge] ${valid.length} pending ${pluralizeEntries(valid.length)} ready for ingest:`);\n for (const entry of valid) {\n console.log(` - ${entry.id} ${entry.source} (${entry.artifactType ?? 'skill'})`);\n }\n\n runHandoff(config, getQueuePath(), { promptAsset: 'ingest-prompt.md' });\n}\n\n/**\n * `skill-forge queue close <id> --status ingested|dismissed` (Pro) — thin wiring over\n * `queue.ts`'s existing `updateQueueEntry` so an agent never hand-edits `queue.json` directly.\n * Refuses an unknown `id` or a status outside `ingested`/`dismissed` (never `pending` — closing\n * doesn't reopen an entry, and there's no \"close to pending\" use case).\n */\nexport async function queueClose(id: string, options: QueueCloseOptions = {}): Promise<void> {\n requireProFeature();\n\n const status = options.status;\n if (!status || !VALID_STATUSES.includes(status as QueueStatus)) {\n throw new UserError(`--status must be one of: ${VALID_STATUSES.join(', ')} (got ${status ? JSON.stringify(status) : 'nothing'})`);\n }\n\n // updateQueueEntry throws a plain Error on an unknown id — rewrap as UserError so a\n // typo'd id gets the standard `skill-forge: <message>` output, not \"unexpected error\".\n let updated;\n try {\n updated = updateQueueEntry(id, { status: status as QueueStatus });\n } catch (err) {\n throw new UserError(err instanceof Error ? err.message : String(err));\n }\n console.log(`[skill-forge] queue entry ${updated.id} closed -> status: ${updated.status}`);\n}\n"],"mappings":"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","names":["join","existsSync","homedir","join","existsSync","existsSync","mkdirSync","join","join","mkdirSync","existsSync","walk","reject","cpSync","existsSync","mkdirSync","basename","join","existsSync","mkdirSync","readFileSync","writeFileSync","readFileSync","readdirSync","statSync","join","SKIP_DIRS","existsSync","readFileSync","writeFileSync","mkdirSync","readFileSync","dirname","join","pkg","existsSync","readFileSync","statSync","writeFileSync","join","readdirSync","join","existsSync","readFileSync","readdirSync","rmSync","statSync","basename","dirname","extname","join","resolve","existsSync","readFileSync","extname","join","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","sep","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","renameSync","rmSync","writeFileSync","join","join","existsSync","readFileSync","queue","mkdirSync","randomUUID","writeFileSync","renameSync","rmSync","existsSync","dirname","join","spawn","existsSync","homedir","createInterface","statSync","homedir","join","resolve","statSync","resolve","join","homedir","existsSync","mkdirSync","writeFileSync","dirname","join","resolvePath","createInterface","existsSync","mkdtempSync","readFileSync","readdirSync","renameSync","rmSync","statSync","writeFileSync","basename","delimiter","extname","join","resolvePath","resolvePath","statSync","readdirSync","join","existsSync","readFileSync","basename","extname","find","delimiter","which","mkdtempSync","writeFileSync","renameSync","rmSync","resolvePath","join","mkdirSync","dirname","writeFileSync","existsSync","createInterface","homedir","createInterface","existsSync","join","join","join","createInterface","cpSync","existsSync","lstatSync","mkdtempSync","readFileSync","rmSync","tmpdir","basename","dirname","join","resolvePath","PRO_FEATURE_LABEL","resolvePath","join","existsSync","mkdtempSync","tmpdir","cpSync","rmSync","dirname","basename","createInterface","readFileSync","lstatSync","mkdirSync","writeFileSync","dirname","resolvePath","existsSync","readFileSync","readdirSync","realpathSync","statSync","basename","join","resolvePath","PRO_FEATURE_LABEL","listSubdirs","readdirSync","join","statSync","findSkillDirsUnderRoot","existsSync","resolvePath","readFileSync","realpathSync","basename","writeReport","resolvePath","mkdirSync","dirname","writeFileSync","search","data","status","realpathSync","join","resolvePath","PRO_FEATURE_LABEL","resolvePath","realpathSync","join","status","realpathSync","dirname","resolvePath","PRO_FEATURE_LABEL","status","realpathSync","resolvePath","dirname","queue","require"]}
|