skilld 1.2.1 → 1.2.2

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.
@@ -801,6 +801,10 @@ function cleanSectionOutput(content) {
801
801
  if (secondIdx - afterFirst < 200) cleaned = cleaned.slice(secondIdx).trim();
802
802
  }
803
803
  }
804
+ cleaned = cleaned.replace(/\(?\[`?\.\/(?:\.skilld\/|references\/)[^)\]]*\]\(([^)]+)\)\)?/g, (match, url) => {
805
+ if (/^\.\/(?:\.skilld\/|references\/)/.test(url)) return `[source](${url})`;
806
+ return match;
807
+ });
804
808
  cleaned = cleaned.replace(/\[source\]\(\.\/((docs|issues|discussions|releases|pkg|guide)\/)/g, "[source](./.skilld/$1");
805
809
  cleaned = sanitizeMarkdown(cleaned);
806
810
  if (!/^##\s/m.test(cleaned) && !/^- (?:BREAKING|DEPRECATED|NEW): /m.test(cleaned) && !/\[source\]/.test(cleaned)) return "";
@@ -1 +1 @@
1
- {"version":3,"file":"agent.mjs","names":["cli","agentId","models","buildArgs","parseLine","cli","agentId","models","buildArgs","parseLine","claude","gemini","codex","claude.buildArgs","gemini.buildArgs","codex.buildArgs","claude.parseLine","gemini.parseLine","codex.parseLine","agents","delay"],"sources":["../../src/agent/clis/claude.ts","../../src/agent/clis/codex.ts","../../src/agent/clis/gemini.ts","../../src/agent/clis/index.ts","../../src/agent/detect-presets.ts","../../src/agent/detect-imports.ts"],"sourcesContent":["/**\n * Claude Code CLI — token-level streaming via --include-partial-messages\n *\n * Write permission: Claude Code has hardcoded .claude/ write protection and\n * --allowedTools glob patterns are broken (github.com/anthropics/claude-code/issues/6881).\n * Instead of fighting the permission system, we let Write be auto-denied in pipe mode\n * and capture the content via writeContent fallback in parseLine().\n */\n\nimport type { CliModelEntry, ParsedEvent } from './types.ts'\n\nexport const cli = 'claude' as const\nexport const agentId = 'claude-code' as const\n\nexport const models: Record<string, CliModelEntry> = {\n opus: { model: 'opus', name: 'Opus 4.6', hint: 'Most capable for complex work' },\n sonnet: { model: 'sonnet', name: 'Sonnet 4.6', hint: 'Best for everyday tasks' },\n haiku: { model: 'haiku', name: 'Haiku 4.5', hint: 'Fastest for quick answers', recommended: true },\n}\n\nexport function buildArgs(model: string, skillDir: string, symlinkDirs: string[]): string[] {\n const allowedTools = [\n // Bare tool names — --add-dir already scopes visibility\n 'Read',\n 'Glob',\n 'Grep',\n 'Bash(*skilld search*)',\n 'Bash(*skilld validate*)',\n // Write intentionally omitted — auto-denied in pipe mode, content\n // captured via writeContent fallback (see parseLine + index.ts:373)\n ].join(' ')\n return [\n '-p',\n '--model',\n model,\n '--output-format',\n 'stream-json',\n '--verbose',\n '--include-partial-messages',\n '--allowedTools',\n allowedTools,\n '--disallowedTools',\n 'WebSearch WebFetch Task',\n '--add-dir',\n skillDir,\n ...symlinkDirs.flatMap(d => ['--add-dir', d]),\n '--no-session-persistence',\n ]\n}\n\n/**\n * Parse claude stream-json events\n *\n * Event types:\n * - stream_event/content_block_delta/text_delta → token streaming\n * - stream_event/content_block_start/tool_use → tool invocation starting\n * - assistant message with tool_use content → tool name + input\n * - assistant message with text content → full text (non-streaming fallback)\n * - result → usage, cost, turns\n */\nexport function parseLine(line: string): ParsedEvent {\n try {\n const obj = JSON.parse(line)\n\n // Token-level streaming (--include-partial-messages)\n if (obj.type === 'stream_event') {\n const evt = obj.event\n if (!evt)\n return {}\n\n // Text delta — the main streaming path\n if (evt.type === 'content_block_delta' && evt.delta?.type === 'text_delta') {\n return { textDelta: evt.delta.text }\n }\n\n return {}\n }\n\n // Full assistant message (complete turn, after streaming)\n if (obj.type === 'assistant' && obj.message?.content) {\n const content = obj.message.content as any[]\n\n // Extract tool uses with inputs for progress hints\n const tools = content.filter((c: any) => c.type === 'tool_use')\n if (tools.length) {\n const names = tools.map((t: any) => t.name)\n // Extract useful hint from tool input (file path, query, etc)\n const hint = tools.map((t: any) => {\n const input = t.input || {}\n return input.file_path || input.path || input.pattern || input.query || input.command || ''\n }).filter(Boolean).join(', ')\n // Capture Write content — primary output path since Write is auto-denied\n const writeTool = tools.find((t: any) => t.name === 'Write' && t.input?.content)\n return { toolName: names.join(', '), toolHint: hint || undefined, writeContent: writeTool?.input?.content }\n }\n\n // Text content (fallback for non-partial mode)\n const text = content\n .filter((c: any) => c.type === 'text')\n .map((c: any) => c.text)\n .join('')\n if (text)\n return { fullText: text }\n }\n\n // Final result\n if (obj.type === 'result') {\n const u = obj.usage\n return {\n done: true,\n usage: u ? { input: u.input_tokens ?? u.inputTokens ?? 0, output: u.output_tokens ?? u.outputTokens ?? 0 } : undefined,\n cost: obj.total_cost_usd,\n turns: obj.num_turns,\n }\n }\n }\n catch {}\n return {}\n}\n","/**\n * OpenAI Codex CLI — exec subcommand with JSON output\n * Prompt passed via stdin with `-` sentinel\n *\n * Event types:\n * - turn.started / turn.completed → turn lifecycle + usage\n * - item.started → command_execution in progress\n * - item.completed → agent_message (text), command_execution (result), file_change (apply_patch)\n * - error / turn.failed → errors\n */\n\nimport type { CliModelEntry, ParsedEvent } from './types.ts'\n\nexport const cli = 'codex' as const\nexport const agentId = 'codex' as const\n\nexport const models: Record<string, CliModelEntry> = {\n 'gpt-5.3-codex': { model: 'gpt-5.3-codex', name: 'GPT-5.3 Codex', hint: 'Latest frontier Codex model' },\n 'gpt-5.3-codex-spark': { model: 'gpt-5.3-codex-spark', name: 'GPT-5.3 Codex Spark', hint: 'Faster GPT-5.3 Codex variant', recommended: true },\n 'gpt-5.2-codex': { model: 'gpt-5.2-codex', name: 'GPT-5.2 Codex', hint: 'Frontier agentic coding model' },\n}\n\nexport function buildArgs(model: string, _skillDir: string, _symlinkDirs: string[]): string[] {\n return [\n 'exec',\n '--json',\n '--ephemeral',\n '--model',\n model,\n // Permissions aligned with Claude's scoped model:\n // --full-auto = --sandbox workspace-write + --ask-for-approval on-request\n // → writes scoped to CWD (.skilld/, set in spawn), reads unrestricted, network blocked\n // Shell remains enabled for `skilld` / `npx -y skilld` search/validate (no per-command allowlist in Codex)\n // --ephemeral → no session persistence (equivalent to Claude's --no-session-persistence)\n '--full-auto',\n '-',\n ]\n}\n\nexport function parseLine(line: string): ParsedEvent {\n try {\n const obj = JSON.parse(line)\n\n if (obj.type === 'item.completed' && obj.item) {\n const item = obj.item\n // Agent message — the main text output\n if (item.type === 'agent_message' && item.text)\n return { fullText: item.text }\n // Command execution completed — log as tool progress\n // If the command writes to a file (redirect or cat >), capture output as writeContent fallback\n if (item.type === 'command_execution' && item.aggregated_output) {\n const cmd = item.command || ''\n const writeContent = (/^cat\\s*>|>/.test(cmd)) ? item.aggregated_output : undefined\n return { toolName: 'Bash', toolHint: `(${item.aggregated_output.length} chars output)`, writeContent }\n }\n // apply_patch completed — file written directly to disk\n if (item.type === 'file_change' && item.changes?.length) {\n const paths = item.changes.map((c: { path: string, kind: string }) => c.path).join(', ')\n return { toolName: 'Write', toolHint: paths }\n }\n }\n\n // Command starting — show progress\n if (obj.type === 'item.started' && obj.item?.type === 'command_execution') {\n return { toolName: 'Bash', toolHint: obj.item.command }\n }\n\n // Turn completed — usage stats\n if (obj.type === 'turn.completed' && obj.usage) {\n return {\n done: true,\n usage: {\n input: obj.usage.input_tokens ?? 0,\n output: obj.usage.output_tokens ?? 0,\n },\n }\n }\n\n // Error events\n if (obj.type === 'turn.failed' || obj.type === 'error') {\n return { done: true }\n }\n }\n catch {}\n return {}\n}\n","/**\n * Gemini CLI — turn-level streaming via -o stream-json\n * Write scoping: relies on cwd being set to .skilld/ (no native --writeable-dirs)\n */\n\nimport type { CliModelEntry, ParsedEvent } from './types.ts'\nimport { resolveSkilldCommand } from '../../core/shared.ts'\n\nexport const cli = 'gemini' as const\nexport const agentId = 'gemini-cli' as const\n\nexport const models: Record<string, CliModelEntry> = {\n 'gemini-3.1-pro': { model: 'gemini-3.1-pro', name: 'Gemini 3.1 Pro', hint: 'Most capable' },\n 'gemini-3-flash': { model: 'gemini-3-flash', name: 'Gemini 3 Flash', hint: 'Balanced', recommended: true },\n}\n\nexport function buildArgs(model: string, skillDir: string, symlinkDirs: string[]): string[] {\n return [\n '-o',\n 'stream-json',\n '-m',\n model,\n '--allowed-tools',\n `read_file,write_file,glob_tool,list_directory,search_file_content,run_shell_command(${resolveSkilldCommand()}),run_shell_command(grep),run_shell_command(head)`,\n '--include-directories',\n skillDir,\n ...symlinkDirs.flatMap(d => ['--include-directories', d]),\n ]\n}\n\n/** Parse gemini stream-json events — turn level (full message per event) */\nexport function parseLine(line: string): ParsedEvent {\n try {\n const obj = JSON.parse(line)\n\n // Text message (delta or full)\n if (obj.type === 'message' && obj.role === 'assistant' && obj.content) {\n return obj.delta ? { textDelta: obj.content } : { fullText: obj.content }\n }\n\n // Tool invocation\n if (obj.type === 'tool_use' || obj.type === 'tool_call') {\n const name = obj.tool_name || obj.name || obj.tool || 'tool'\n const params = obj.parameters || obj.args || obj.input || {}\n const hint = params.file_path || params.path || params.dir_path || params.pattern || params.query || params.command || ''\n // Capture write_file content as fallback (matches Claude's Write tool behavior)\n if (name === 'write_file' && params.content) {\n return { toolName: name, toolHint: hint || undefined, writeContent: params.content }\n }\n return { toolName: name, toolHint: hint || undefined }\n }\n\n // Final result\n if (obj.type === 'result') {\n const s = obj.stats\n return {\n done: true,\n usage: s ? { input: s.input_tokens ?? s.input ?? 0, output: s.output_tokens ?? s.output ?? 0 } : undefined,\n turns: s?.tool_calls,\n }\n }\n }\n catch {}\n return {}\n}\n","/**\n * CLI orchestrator — spawns per-CLI processes for skill generation\n * Each CLI (claude, gemini, codex) has its own buildArgs + parseLine in separate files\n */\n\nimport type { SkillSection } from '../prompts/index.ts'\nimport type { AgentType } from '../types.ts'\nimport type { CliModelConfig, CliName, OptimizeDocsOptions, OptimizeModel, OptimizeResult, ParsedEvent, SectionResult, StreamProgress, ValidationWarning } from './types.ts'\nimport { exec, spawn } from 'node:child_process'\nimport { createHash } from 'node:crypto'\nimport { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, realpathSync, unlinkSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { promisify } from 'node:util'\nimport { dirname, join } from 'pathe'\nimport { isWindows } from 'std-env'\nimport { readCachedSection, writeSections } from '../../cache/index.ts'\nimport { sanitizeMarkdown } from '../../core/sanitize.ts'\nimport { detectInstalledAgents } from '../detect.ts'\nimport { buildAllSectionPrompts, getSectionValidator, SECTION_MERGE_ORDER, SECTION_OUTPUT_FILES, wrapSection } from '../prompts/index.ts'\nimport { agents } from '../registry.ts'\nimport * as claude from './claude.ts'\nimport * as codex from './codex.ts'\nimport * as gemini from './gemini.ts'\n\nexport { buildAllSectionPrompts, buildSectionPrompt, SECTION_MERGE_ORDER, SECTION_OUTPUT_FILES } from '../prompts/index.ts'\nexport type { CustomPrompt, SkillSection } from '../prompts/index.ts'\nexport type { CliModelConfig, CliName, ModelInfo, OptimizeDocsOptions, OptimizeModel, OptimizeResult, StreamProgress } from './types.ts'\n\n// ── Tool progress display ────────────────────────────────────────────\n\nconst TOOL_VERBS: Record<string, string> = {\n // Claude\n Read: 'Reading',\n Glob: 'Searching',\n Grep: 'Searching',\n Write: 'Writing',\n Bash: 'Running',\n // Gemini\n read_file: 'Reading',\n glob_tool: 'Searching',\n write_file: 'Writing',\n list_directory: 'Listing',\n search_file_content: 'Searching',\n run_shell_command: 'Running',\n}\n\ninterface ToolProgressLog {\n message: (msg: string) => void\n}\n\n/** Create a progress callback that emits one line per tool call, Claude Code style */\nexport function createToolProgress(log: ToolProgressLog): (progress: StreamProgress) => void {\n let lastMsg = ''\n let repeatCount = 0\n\n function emit(msg: string) {\n if (msg === lastMsg) {\n repeatCount++\n log.message(`${msg} \\x1B[90m(+${repeatCount})\\x1B[0m`)\n }\n else {\n lastMsg = msg\n repeatCount = 0\n log.message(msg)\n }\n }\n\n return ({ type, chunk, section }) => {\n if (type === 'text') {\n emit(`${section ? `\\x1B[90m[${section}]\\x1B[0m ` : ''}Writing...`)\n return\n }\n if (type !== 'reasoning' || !chunk.startsWith('['))\n return\n\n // Parse individual tool names and hints from \"[Read: path]\" or \"[Read, Glob: path1, path2]\"\n const match = chunk.match(/^\\[([^:[\\]]+)(?::\\s(.+))?\\]$/)\n if (!match)\n return\n\n const names = match[1]!.split(',').map(n => n.trim())\n const hints = match[2]?.split(',').map(h => h.trim()) ?? []\n\n for (let i = 0; i < names.length; i++) {\n const rawName = names[i]!\n const hint = hints[i] ?? hints[0] ?? ''\n const verb = TOOL_VERBS[rawName] ?? rawName\n const prefix = section ? `\\x1B[90m[${section}]\\x1B[0m ` : ''\n\n if ((rawName === 'Bash' || rawName === 'run_shell_command') && hint) {\n const searchMatch = hint.match(/skilld search\\s+\"([^\"]+)\"/)\n if (searchMatch) {\n emit(`${prefix}Searching \\x1B[36m\"${searchMatch[1]}\"\\x1B[0m`)\n }\n else if (hint.includes('skilld validate')) {\n emit(`${prefix}Validating...`)\n }\n else {\n const shortened = shortenCommand(hint)\n emit(`${prefix}Running ${shortened.length > 50 ? `${shortened.slice(0, 47)}...` : shortened}`)\n }\n }\n else {\n const path = shortenPath(hint || '...')\n emit(`${prefix}${verb} \\x1B[90m${path}\\x1B[0m`)\n }\n }\n }\n}\n\n// ── Per-CLI dispatch ─────────────────────────────────────────────────\n\nconst CLI_DEFS = [claude, gemini, codex] as const\n\nconst CLI_BUILD_ARGS: Record<CliName, (model: string, skillDir: string, symlinkDirs: string[]) => string[]> = {\n claude: claude.buildArgs,\n gemini: gemini.buildArgs,\n codex: codex.buildArgs,\n}\n\nconst CLI_PARSE_LINE: Record<CliName, (line: string) => ParsedEvent> = {\n claude: claude.parseLine,\n gemini: gemini.parseLine,\n codex: codex.parseLine,\n}\n\n// ── Assemble CLI_MODELS from per-CLI model definitions ───────────────\n\nexport const CLI_MODELS: Partial<Record<OptimizeModel, CliModelConfig>> = Object.fromEntries(\n CLI_DEFS.flatMap(def =>\n Object.entries(def.models).map(([id, entry]) => [\n id,\n { ...entry, cli: def.cli, agentId: def.agentId },\n ]),\n ),\n)\n\n// ── Model helpers ────────────────────────────────────────────────────\n\nexport function getModelName(id: OptimizeModel): string {\n return CLI_MODELS[id]?.name ?? id\n}\n\nexport function getModelLabel(id: OptimizeModel): string {\n const config = CLI_MODELS[id]\n if (!config)\n return id\n const agentName = agents[config.agentId]?.displayName ?? config.cli\n return `${agentName} · ${config.name}`\n}\n\nexport async function getAvailableModels(): Promise<import('./types.ts').ModelInfo[]> {\n const execAsync = promisify(exec)\n const lookupCmd = isWindows ? 'where' : 'which'\n\n const installedAgents = detectInstalledAgents()\n const agentsWithCli = installedAgents.filter(id => agents[id].cli)\n\n const cliChecks = await Promise.all(\n agentsWithCli.map(async (agentId) => {\n const cli = agents[agentId].cli!\n try {\n await execAsync(`${lookupCmd} ${cli}`)\n return agentId\n }\n catch { return null }\n }),\n )\n const availableAgentIds = new Set(cliChecks.filter((id): id is AgentType => id != null))\n\n return (Object.entries(CLI_MODELS) as [OptimizeModel, CliModelConfig][])\n .filter(([_, config]) => availableAgentIds.has(config.agentId))\n .map(([id, config]) => ({\n id,\n name: config.name,\n hint: config.hint,\n recommended: config.recommended,\n agentId: config.agentId,\n agentName: agents[config.agentId]?.displayName ?? config.agentId,\n }))\n}\n\n// ── Reference dirs ───────────────────────────────────────────────────\n\n/** Resolve symlinks in .skilld/ to get real paths for --add-dir */\nfunction resolveReferenceDirs(skillDir: string): string[] {\n const refsDir = join(skillDir, '.skilld')\n if (!existsSync(refsDir))\n return []\n const resolved = readdirSync(refsDir)\n .map(entry => join(refsDir, entry))\n .filter(p => lstatSync(p).isSymbolicLink() && existsSync(p))\n .map(p => realpathSync(p))\n\n // Include parent directories so CLIs can search across all references at once\n // (e.g. Gemini's sandbox requires the parent dir to be explicitly included)\n const parents = new Set<string>()\n for (const p of resolved) {\n const parent = dirname(p)\n if (!resolved.includes(parent))\n parents.add(parent)\n }\n\n return [...resolved, ...parents]\n}\n\n// ── Cache ────────────────────────────────────────────────────────────\n\nconst CACHE_DIR = join(homedir(), '.skilld', 'llm-cache')\n\n/** Strip absolute paths from prompt so the hash is project-independent */\nfunction normalizePromptForHash(prompt: string): string {\n return prompt.replace(/\\/[^\\s`]*\\.(?:claude|codex|gemini)\\/skills\\/[^\\s/`]+/g, '<SKILL_DIR>')\n}\n\nfunction hashPrompt(prompt: string, model: OptimizeModel, section: SkillSection): string {\n return createHash('sha256').update(`exec:${model}:${section}:${normalizePromptForHash(prompt)}`).digest('hex').slice(0, 16)\n}\n\nfunction getCached(prompt: string, model: OptimizeModel, section: SkillSection, maxAge = 7 * 24 * 60 * 60 * 1000): string | null {\n const path = join(CACHE_DIR, `${hashPrompt(prompt, model, section)}.json`)\n if (!existsSync(path))\n return null\n try {\n const { text, timestamp } = JSON.parse(readFileSync(path, 'utf-8'))\n return Date.now() - timestamp > maxAge ? null : text\n }\n catch { return null }\n}\n\nfunction setCache(prompt: string, model: OptimizeModel, section: SkillSection, text: string): void {\n mkdirSync(CACHE_DIR, { recursive: true, mode: 0o700 })\n writeFileSync(\n join(CACHE_DIR, `${hashPrompt(prompt, model, section)}.json`),\n JSON.stringify({ text, model, section, timestamp: Date.now() }),\n { mode: 0o600 },\n )\n}\n\n// ── Per-section spawn ────────────────────────────────────────────────\n\ninterface OptimizeSectionOptions {\n section: SkillSection\n prompt: string\n outputFile: string\n skillDir: string\n model: OptimizeModel\n packageName: string\n onProgress?: (progress: StreamProgress) => void\n timeout: number\n debug?: boolean\n preExistingFiles: Set<string>\n}\n\n/** Spawn a single CLI process for one section */\nfunction optimizeSection(opts: OptimizeSectionOptions): Promise<SectionResult> {\n const { section, prompt, outputFile, skillDir, model, onProgress, timeout, debug, preExistingFiles } = opts\n\n const cliConfig = CLI_MODELS[model]\n if (!cliConfig) {\n return Promise.resolve({ section, content: '', wasOptimized: false, error: `No CLI mapping for model: ${model}` })\n }\n\n const { cli, model: cliModel } = cliConfig\n const symlinkDirs = resolveReferenceDirs(skillDir)\n const args = CLI_BUILD_ARGS[cli](cliModel, skillDir, symlinkDirs)\n const parseLine = CLI_PARSE_LINE[cli]\n\n const skilldDir = join(skillDir, '.skilld')\n const outputPath = join(skilldDir, outputFile)\n\n // Remove stale output so we don't read a leftover from a previous run\n if (existsSync(outputPath))\n unlinkSync(outputPath)\n\n // Write prompt for debugging\n writeFileSync(join(skilldDir, `PROMPT_${section}.md`), prompt)\n\n return new Promise<SectionResult>((resolve) => {\n const proc = spawn(cli, args, {\n cwd: skilldDir,\n stdio: ['pipe', 'pipe', 'pipe'],\n timeout,\n env: { ...process.env, NO_COLOR: '1' },\n shell: isWindows,\n })\n\n let buffer = ''\n let accumulatedText = ''\n let lastWriteContent = ''\n let usage: { input: number, output: number } | undefined\n let cost: number | undefined\n const rawLines: string[] = []\n\n onProgress?.({ chunk: '[starting...]', type: 'reasoning', text: '', reasoning: '', section })\n\n proc.stdin.write(prompt)\n proc.stdin.end()\n\n proc.stdout.on('data', (chunk: Buffer) => {\n buffer += chunk.toString()\n const lines = buffer.split('\\n')\n buffer = lines.pop() || ''\n\n for (const line of lines) {\n if (!line.trim())\n continue\n if (debug)\n rawLines.push(line)\n const evt = parseLine(line)\n\n if (evt.textDelta)\n accumulatedText += evt.textDelta\n if (evt.fullText)\n accumulatedText = evt.fullText\n\n if (evt.writeContent)\n lastWriteContent = evt.writeContent\n\n if (evt.toolName) {\n const hint = evt.toolHint\n ? `[${evt.toolName}: ${evt.toolHint}]`\n : `[${evt.toolName}]`\n onProgress?.({ chunk: hint, type: 'reasoning', text: '', reasoning: hint, section })\n }\n\n if (evt.usage)\n usage = evt.usage\n if (evt.cost != null)\n cost = evt.cost\n }\n })\n\n let stderr = ''\n proc.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString()\n })\n\n proc.on('close', (code) => {\n // Drain remaining buffer for metadata\n if (buffer.trim()) {\n const evt = parseLine(buffer)\n if (evt.textDelta)\n accumulatedText += evt.textDelta\n if (evt.fullText)\n accumulatedText = evt.fullText\n if (evt.writeContent)\n lastWriteContent = evt.writeContent\n if (evt.usage)\n usage = evt.usage\n if (evt.cost != null)\n cost = evt.cost\n }\n\n // Remove unexpected files the LLM may have written (prompt injection defense)\n // Only clean files not in the pre-existing snapshot and not our expected output\n for (const entry of readdirSync(skilldDir)) {\n if (entry !== outputFile && !preExistingFiles.has(entry)) {\n // Allow other section output files and debug prompts\n if (Object.values(SECTION_OUTPUT_FILES).includes(entry))\n continue\n if (entry.startsWith('PROMPT_') || entry === 'logs')\n continue\n try {\n unlinkSync(join(skilldDir, entry))\n }\n catch {}\n }\n }\n\n // Prefer file written by LLM, fall back to Write tool content (if denied), then accumulated stdout\n const raw = (existsSync(outputPath) ? readFileSync(outputPath, 'utf-8') : lastWriteContent || accumulatedText).trim()\n\n // Always write stderr on failure; write all logs in debug mode\n const logsDir = join(skilldDir, 'logs')\n const logName = section.toUpperCase().replace(/-/g, '_')\n if (debug || (stderr && (!raw || code !== 0))) {\n mkdirSync(logsDir, { recursive: true })\n if (stderr)\n writeFileSync(join(logsDir, `${logName}.stderr.log`), stderr)\n }\n if (debug) {\n mkdirSync(logsDir, { recursive: true })\n if (rawLines.length)\n writeFileSync(join(logsDir, `${logName}.jsonl`), rawLines.join('\\n'))\n if (raw)\n writeFileSync(join(logsDir, `${logName}.md`), raw)\n }\n\n if (!raw && code !== 0) {\n resolve({ section, content: '', wasOptimized: false, error: stderr.trim() || `CLI exited with code ${code}` })\n return\n }\n\n // Clean the section output (strip markdown fences, frontmatter, sanitize)\n const content = raw ? cleanSectionOutput(raw) : ''\n\n if (content) {\n // Write cleaned content back to the output file for debugging\n writeFileSync(outputPath, content)\n }\n\n const validator = getSectionValidator(section)\n const rawWarnings = content && validator ? validator(content) : []\n const warnings: ValidationWarning[] = rawWarnings.map(w => ({ section, warning: w.warning }))\n\n resolve({\n section,\n content,\n wasOptimized: !!content,\n warnings: warnings?.length ? warnings : undefined,\n usage,\n cost,\n })\n })\n\n proc.on('error', (err) => {\n resolve({ section, content: '', wasOptimized: false, error: err.message })\n })\n })\n}\n\n// ── Main orchestrator ────────────────────────────────────────────────\n\nexport async function optimizeDocs(opts: OptimizeDocsOptions): Promise<OptimizeResult> {\n const { packageName, skillDir, model = 'sonnet', version, hasGithub, hasReleases, hasChangelog, docFiles, docsType, hasShippedDocs, onProgress, timeout = 180000, debug, noCache, sections, customPrompt, features, pkgFiles } = opts\n\n const selectedSections = sections ?? ['api-changes', 'best-practices'] as SkillSection[]\n\n // Build all section prompts\n const sectionPrompts = buildAllSectionPrompts({\n packageName,\n skillDir,\n version,\n hasIssues: hasGithub,\n hasDiscussions: hasGithub,\n hasReleases,\n hasChangelog,\n docFiles,\n docsType,\n hasShippedDocs,\n customPrompt,\n features,\n pkgFiles,\n sections: selectedSections,\n })\n\n if (sectionPrompts.size === 0) {\n return { optimized: '', wasOptimized: false, error: 'No valid sections to generate' }\n }\n\n const cliConfig = CLI_MODELS[model]\n if (!cliConfig) {\n return { optimized: '', wasOptimized: false, error: `No CLI mapping for model: ${model}` }\n }\n\n // Check per-section cache: references dir first (version-keyed), then LLM cache (prompt-hashed)\n const cachedResults: SectionResult[] = []\n const uncachedSections: Array<{ section: SkillSection, prompt: string }> = []\n\n for (const [section, prompt] of sectionPrompts) {\n if (!noCache) {\n // Check global references dir (cross-project, version-keyed)\n if (version) {\n const outputFile = SECTION_OUTPUT_FILES[section]\n const refCached = readCachedSection(packageName, version, outputFile)\n if (refCached) {\n onProgress?.({ chunk: `[${section}: cached]`, type: 'text', text: refCached, reasoning: '', section })\n cachedResults.push({ section, content: refCached, wasOptimized: true })\n continue\n }\n }\n\n // Check LLM prompt-hash cache\n const cached = getCached(prompt, model, section)\n if (cached) {\n onProgress?.({ chunk: `[${section}: cached]`, type: 'text', text: cached, reasoning: '', section })\n cachedResults.push({ section, content: cached, wasOptimized: true })\n continue\n }\n }\n uncachedSections.push({ section, prompt })\n }\n\n // Prepare .skilld/ dir and snapshot before spawns\n const skilldDir = join(skillDir, '.skilld')\n mkdirSync(skilldDir, { recursive: true })\n\n // Pre-flight: warn about broken symlinks in .skilld/ (avoids wasting tokens on missing refs)\n for (const entry of readdirSync(skilldDir)) {\n const entryPath = join(skilldDir, entry)\n try {\n if (lstatSync(entryPath).isSymbolicLink() && !existsSync(entryPath))\n onProgress?.({ chunk: `[warn: broken symlink .skilld/${entry}]`, type: 'reasoning', text: '', reasoning: '' })\n }\n catch {}\n }\n\n const preExistingFiles = new Set(readdirSync(skilldDir))\n\n // Spawn uncached sections with staggered starts to avoid rate-limit collisions\n const STAGGER_MS = 3000\n const spawnResults = uncachedSections.length > 0\n ? await Promise.allSettled(\n uncachedSections.map(({ section, prompt }, i) => {\n const outputFile = SECTION_OUTPUT_FILES[section]\n const run = () => optimizeSection({\n section,\n prompt,\n outputFile,\n skillDir,\n model,\n packageName,\n onProgress,\n timeout,\n debug,\n preExistingFiles,\n })\n // Stagger: first section starts immediately, rest delayed\n if (i === 0)\n return run()\n return delay(i * STAGGER_MS).then(run)\n }),\n )\n : []\n\n // Collect results, retry failed sections once\n const allResults: SectionResult[] = [...cachedResults]\n let totalUsage: { input: number, output: number } | undefined\n let totalCost = 0\n const retryQueue: Array<{ index: number, section: SkillSection, prompt: string }> = []\n\n for (let i = 0; i < spawnResults.length; i++) {\n const r = spawnResults[i]!\n const { section, prompt } = uncachedSections[i]!\n if (r.status === 'fulfilled' && r.value.wasOptimized) {\n allResults.push(r.value)\n if (r.value.usage) {\n totalUsage = totalUsage ?? { input: 0, output: 0 }\n totalUsage.input += r.value.usage.input\n totalUsage.output += r.value.usage.output\n }\n if (r.value.cost != null)\n totalCost += r.value.cost\n if (!noCache)\n setCache(prompt, model, section, r.value.content)\n }\n else {\n retryQueue.push({ index: i, section, prompt })\n }\n }\n\n // Retry failed sections once (sequential to avoid rate limits)\n for (const { section, prompt } of retryQueue) {\n onProgress?.({ chunk: `[${section}: retrying...]`, type: 'reasoning', text: '', reasoning: '', section })\n await delay(STAGGER_MS)\n const result = await optimizeSection({\n section,\n prompt,\n outputFile: SECTION_OUTPUT_FILES[section],\n skillDir,\n model,\n packageName,\n onProgress,\n timeout,\n debug,\n preExistingFiles,\n }).catch((err: Error) => ({ section, content: '', wasOptimized: false, error: err.message }) as SectionResult)\n\n allResults.push(result)\n if (result.wasOptimized && !noCache)\n setCache(prompt, model, section, result.content)\n if (result.usage) {\n totalUsage = totalUsage ?? { input: 0, output: 0 }\n totalUsage.input += result.usage.input\n totalUsage.output += result.usage.output\n }\n if (result.cost != null)\n totalCost += result.cost\n }\n\n // Write successful sections to global references dir for cross-project reuse\n if (version) {\n const sectionFiles = allResults\n .filter(r => r.wasOptimized && r.content)\n .map(r => ({ file: SECTION_OUTPUT_FILES[r.section], content: r.content }))\n if (sectionFiles.length > 0) {\n writeSections(packageName, version, sectionFiles)\n }\n }\n\n // Merge results in SECTION_MERGE_ORDER, wrapped with comment markers\n const mergedParts: string[] = []\n for (const section of SECTION_MERGE_ORDER) {\n const result = allResults.find(r => r.section === section)\n if (result?.wasOptimized && result.content) {\n mergedParts.push(wrapSection(section, result.content))\n }\n }\n\n const optimized = mergedParts.join('\\n\\n')\n const wasOptimized = mergedParts.length > 0\n\n const usageResult = totalUsage\n ? { inputTokens: totalUsage.input, outputTokens: totalUsage.output, totalTokens: totalUsage.input + totalUsage.output }\n : undefined\n\n // Collect errors and warnings from sections\n const errors = allResults.filter(r => r.error).map(r => `${r.section}: ${r.error}`)\n const warnings = allResults.flatMap(r => r.warnings ?? []).map(w => `${w.section}: ${w.warning}`)\n\n const debugLogsDir = debug && uncachedSections.length > 0\n ? join(skillDir, '.skilld', 'logs')\n : undefined\n\n return {\n optimized,\n wasOptimized,\n error: errors.length > 0 ? errors.join('; ') : undefined,\n warnings: warnings.length > 0 ? warnings : undefined,\n finishReason: wasOptimized ? 'stop' : 'error',\n usage: usageResult,\n cost: totalCost || undefined,\n debugLogsDir,\n }\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\n/** Shorten absolute paths for display: /home/user/project/.claude/skills/vue/SKILL.md → .claude/.../SKILL.md */\nfunction shortenPath(p: string): string {\n const refIdx = p.indexOf('.skilld/')\n if (refIdx !== -1)\n return p.slice(refIdx + '.skilld/'.length)\n // Keep just filename for other paths\n const parts = p.split('/')\n return parts.length > 2 ? `.../${parts.slice(-2).join('/')}` : p\n}\n\n/** Replace absolute paths in a command string with shortened versions */\nfunction shortenCommand(cmd: string): string {\n return cmd.replace(/\\/[^\\s\"']+/g, (match) => {\n // Only shorten paths that look like they're inside a project\n if (match.includes('.claude/') || match.includes('.skilld/') || match.includes('node_modules/'))\n return `.../${match.split('/').slice(-2).join('/')}`\n return match\n })\n}\n\n/** Clean a single section's LLM output: strip markdown fences, frontmatter, sanitize */\nexport function cleanSectionOutput(content: string): string {\n let cleaned = content.trim()\n\n // Strip wrapping fences if output is wrapped in ```markdown, ```md, or bare ```\n // Requires matched open+close pair to avoid stripping internal code blocks\n const wrapMatch = cleaned.match(/^```(?:markdown|md)?[^\\S\\n]*\\n([\\s\\S]+)\\n```[^\\S\\n]*$/)\n if (wrapMatch) {\n const inner = wrapMatch[1]!.trim()\n // For bare ``` wrappers (no markdown/md tag), verify inner looks like section output\n const isExplicitWrapper = /^```(?:markdown|md)/.test(cleaned)\n if (isExplicitWrapper || /^##\\s/m.test(inner) || /^- (?:BREAKING|DEPRECATED|NEW): /m.test(inner)) {\n cleaned = inner\n }\n }\n\n // Normalize h1 headers to h2 — LLMs sometimes use # instead of ##\n cleaned = cleaned.replace(/^# (?!#)/gm, '## ')\n\n // Strip accidental frontmatter or leading horizontal rules\n const fmMatch = cleaned.match(/^-{3,}\\n/)\n if (fmMatch) {\n const afterOpen = fmMatch[0].length\n const closeMatch = cleaned.slice(afterOpen).match(/\\n-{3,}/)\n if (closeMatch) {\n cleaned = cleaned.slice(afterOpen + closeMatch.index! + closeMatch[0].length).trim()\n }\n else {\n cleaned = cleaned.slice(afterOpen).trim()\n }\n }\n\n // Strip raw code preamble before first section marker (defense against LLMs dumping source)\n // Section markers: ## heading, BREAKING/DEPRECATED/NEW labels\n const firstMarker = cleaned.match(/^(##\\s|- (?:BREAKING|DEPRECATED|NEW): )/m)\n if (firstMarker?.index && firstMarker.index > 0) {\n const preamble = cleaned.slice(0, firstMarker.index)\n // Only strip if preamble looks like code (contains function/const/export/return patterns)\n if (/\\b(?:function|const |let |var |export |return |import |async |class )\\b/.test(preamble)) {\n cleaned = cleaned.slice(firstMarker.index).trim()\n }\n }\n\n // Strip duplicate section headings (LLM echoing the format example before real content)\n // Handles headings separated by blank lines or boilerplate text\n const headingMatch = cleaned.match(/^(## .+)\\n/)\n if (headingMatch) {\n const heading = headingMatch[1]!\n const afterFirst = headingMatch[0].length\n const secondIdx = cleaned.indexOf(heading, afterFirst)\n if (secondIdx !== -1) {\n // Only strip if the gap between duplicates is small (< 200 chars of boilerplate)\n if (secondIdx - afterFirst < 200)\n cleaned = cleaned.slice(secondIdx).trim()\n }\n }\n\n // Normalize source link paths: ensure .skilld/ prefix is present\n // LLMs sometimes emit [source](./docs/...) instead of [source](./.skilld/docs/...)\n cleaned = cleaned.replace(\n /\\[source\\]\\(\\.\\/((docs|issues|discussions|releases|pkg|guide)\\/)/g,\n '[source](./.skilld/$1',\n )\n\n cleaned = sanitizeMarkdown(cleaned)\n\n // Reject content that lacks any section structure — likely leaked LLM reasoning/narration\n // Valid sections contain headings (##), API change labels, or source-linked items\n if (!/^##\\s/m.test(cleaned) && !/^- (?:BREAKING|DEPRECATED|NEW): /m.test(cleaned) && !/\\[source\\]/.test(cleaned)) {\n return ''\n }\n\n return cleaned\n}\n","/**\n * Detect packages from framework presets (e.g., Nuxt modules in nuxt.config)\n * These are string literals in config arrays, not imports — the import scanner misses them.\n */\n\nimport type { PackageUsage } from './detect-imports.ts'\nimport { readFile } from 'node:fs/promises'\nimport { parseSync } from 'oxc-parser'\nimport { join } from 'pathe'\n\nconst NUXT_CONFIG_FILES = ['nuxt.config.ts', 'nuxt.config.js', 'nuxt.config.mjs']\nconst NUXT_ECOSYSTEM = ['vue', 'nitro', 'h3']\n\nasync function findNuxtConfig(cwd: string): Promise<{ path: string, content: string } | null> {\n for (const name of NUXT_CONFIG_FILES) {\n const path = join(cwd, name)\n const content = await readFile(path, 'utf8').catch(() => null)\n if (content)\n return { path, content }\n }\n return null\n}\n\n/**\n * Walk AST node to find all string values inside a `modules` array property.\n * Handles: defineNuxtConfig({ modules: [...] }) and export default { modules: [...] }\n */\nexport function extractModuleStrings(node: any): string[] {\n if (!node || typeof node !== 'object')\n return []\n\n // Found a Property with key \"modules\" and an ArrayExpression value\n if (node.type === 'Property' && !node.computed\n && (node.key?.type === 'Identifier' && node.key.name === 'modules')\n && node.value?.type === 'ArrayExpression') { return node.value.elements.filter((el: any) => el?.type === 'Literal' && typeof el.value === 'string').map((el: any) => el.value as string) }\n\n // Recurse into arrays and object values\n const results: string[] = []\n if (Array.isArray(node)) {\n for (const child of node)\n results.push(...extractModuleStrings(child))\n }\n else {\n for (const key of Object.keys(node)) {\n if (key === 'start' || key === 'end' || key === 'type')\n continue\n const val = node[key]\n if (val && typeof val === 'object')\n results.push(...extractModuleStrings(val))\n }\n }\n return results\n}\n\n/**\n * Detect Nuxt modules from nuxt.config.{ts,js,mjs}\n */\nexport async function detectNuxtModules(cwd: string): Promise<PackageUsage[]> {\n const config = await findNuxtConfig(cwd)\n if (!config)\n return []\n\n const result = parseSync(config.path, config.content)\n const modules = extractModuleStrings(result.program)\n\n // Dedupe and build results\n const seen = new Set<string>()\n const packages: PackageUsage[] = []\n\n for (const mod of modules) {\n if (!seen.has(mod)) {\n seen.add(mod)\n packages.push({ name: mod, count: 0, source: 'preset' })\n }\n }\n\n // Add core ecosystem packages\n for (const pkg of NUXT_ECOSYSTEM) {\n if (!seen.has(pkg)) {\n seen.add(pkg)\n packages.push({ name: pkg, count: 0, source: 'preset' })\n }\n }\n\n return packages\n}\n\n/**\n * Run all preset detectors and merge results\n */\nexport async function detectPresetPackages(cwd: string): Promise<PackageUsage[]> {\n // Currently only Nuxt, but extensible for other frameworks\n return detectNuxtModules(cwd)\n}\n","/**\n * Detect directly-used npm packages by scanning source files\n * Uses mlly for proper ES module parsing + tinyglobby for file discovery\n */\n\nimport { readFile } from 'node:fs/promises'\nimport { findDynamicImports, findStaticImports } from 'mlly'\nimport { glob } from 'tinyglobby'\nimport { detectPresetPackages } from './detect-presets.ts'\n\nexport interface PackageUsage {\n name: string\n count: number\n source?: 'import' | 'preset'\n}\n\nexport interface DetectResult {\n packages: PackageUsage[]\n error?: string\n}\n\nconst PATTERNS = ['**/*.{ts,js,vue,mjs,cjs,tsx,jsx,mts,cts}']\nconst IGNORE = ['**/node_modules/**', '**/dist/**', '**/.nuxt/**', '**/.output/**', '**/coverage/**']\n\nfunction addPackage(counts: Map<string, number>, specifier: string | undefined) {\n if (!specifier || specifier.startsWith('.') || specifier.startsWith('/'))\n return\n\n // Extract package name (handle subpaths like 'pkg/subpath')\n const name = specifier.startsWith('@')\n ? specifier.split('/').slice(0, 2).join('/')\n : specifier.split('/')[0]!\n\n if (!isNodeBuiltin(name)) {\n counts.set(name, (counts.get(name) || 0) + 1)\n }\n}\n\n/**\n * Scan source files to detect all directly-imported npm packages\n * Async with gitignore support for proper spinner animation\n */\nexport async function detectImportedPackages(cwd: string = process.cwd()): Promise<DetectResult> {\n try {\n const counts = new Map<string, number>()\n\n const files = await glob(PATTERNS, {\n cwd,\n ignore: IGNORE,\n absolute: true,\n expandDirectories: false,\n })\n\n await Promise.all(files.map(async (file) => {\n const content = await readFile(file, 'utf8')\n\n // Static: import x from 'pkg'\n for (const imp of findStaticImports(content)) {\n addPackage(counts, imp.specifier)\n }\n\n // Dynamic: import('pkg') - expression is the string literal\n for (const imp of findDynamicImports(content)) {\n // expression includes quotes, extract string value\n const match = imp.expression.match(/^['\"]([^'\"]+)['\"]$/)\n if (match)\n addPackage(counts, match[1]!)\n }\n }))\n\n // Sort by usage count (descending), then alphabetically\n const packages: PackageUsage[] = Array.from(counts.entries(), ([name, count]) => ({ name, count, source: 'import' as const }))\n .sort((a, b) => b.count - a.count || a.name.localeCompare(b.name))\n\n // Merge preset-detected packages (imports take priority)\n const presets = await detectPresetPackages(cwd)\n const importNames = new Set(packages.map(p => p.name))\n for (const preset of presets) {\n if (!importNames.has(preset.name))\n packages.push(preset)\n }\n\n return { packages }\n }\n catch (err) {\n return { packages: [], error: String(err) }\n }\n}\n\nconst NODE_BUILTINS = new Set([\n 'assert',\n 'async_hooks',\n 'buffer',\n 'child_process',\n 'cluster',\n 'console',\n 'constants',\n 'crypto',\n 'dgram',\n 'diagnostics_channel',\n 'dns',\n 'domain',\n 'events',\n 'fs',\n 'http',\n 'http2',\n 'https',\n 'inspector',\n 'module',\n 'net',\n 'os',\n 'path',\n 'perf_hooks',\n 'process',\n 'punycode',\n 'querystring',\n 'readline',\n 'repl',\n 'sea',\n 'sqlite',\n 'stream',\n 'string_decoder',\n 'sys',\n 'test',\n 'timers',\n 'tls',\n 'trace_events',\n 'tty',\n 'url',\n 'util',\n 'v8',\n 'vm',\n 'wasi',\n 'worker_threads',\n 'zlib',\n])\n\nfunction isNodeBuiltin(pkg: string): boolean {\n const base = pkg.startsWith('node:') ? pkg.slice(5) : pkg\n return NODE_BUILTINS.has(base.split('/')[0]!)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agent.mjs","names":["cli","agentId","models","buildArgs","parseLine","cli","agentId","models","buildArgs","parseLine","claude","gemini","codex","claude.buildArgs","gemini.buildArgs","codex.buildArgs","claude.parseLine","gemini.parseLine","codex.parseLine","agents","delay"],"sources":["../../src/agent/clis/claude.ts","../../src/agent/clis/codex.ts","../../src/agent/clis/gemini.ts","../../src/agent/clis/index.ts","../../src/agent/detect-presets.ts","../../src/agent/detect-imports.ts"],"sourcesContent":["/**\n * Claude Code CLI — token-level streaming via --include-partial-messages\n *\n * Write permission: Claude Code has hardcoded .claude/ write protection and\n * --allowedTools glob patterns are broken (github.com/anthropics/claude-code/issues/6881).\n * Instead of fighting the permission system, we let Write be auto-denied in pipe mode\n * and capture the content via writeContent fallback in parseLine().\n */\n\nimport type { CliModelEntry, ParsedEvent } from './types.ts'\n\nexport const cli = 'claude' as const\nexport const agentId = 'claude-code' as const\n\nexport const models: Record<string, CliModelEntry> = {\n opus: { model: 'opus', name: 'Opus 4.6', hint: 'Most capable for complex work' },\n sonnet: { model: 'sonnet', name: 'Sonnet 4.6', hint: 'Best for everyday tasks' },\n haiku: { model: 'haiku', name: 'Haiku 4.5', hint: 'Fastest for quick answers', recommended: true },\n}\n\nexport function buildArgs(model: string, skillDir: string, symlinkDirs: string[]): string[] {\n const allowedTools = [\n // Bare tool names — --add-dir already scopes visibility\n 'Read',\n 'Glob',\n 'Grep',\n 'Bash(*skilld search*)',\n 'Bash(*skilld validate*)',\n // Write intentionally omitted — auto-denied in pipe mode, content\n // captured via writeContent fallback (see parseLine + index.ts:373)\n ].join(' ')\n return [\n '-p',\n '--model',\n model,\n '--output-format',\n 'stream-json',\n '--verbose',\n '--include-partial-messages',\n '--allowedTools',\n allowedTools,\n '--disallowedTools',\n 'WebSearch WebFetch Task',\n '--add-dir',\n skillDir,\n ...symlinkDirs.flatMap(d => ['--add-dir', d]),\n '--no-session-persistence',\n ]\n}\n\n/**\n * Parse claude stream-json events\n *\n * Event types:\n * - stream_event/content_block_delta/text_delta → token streaming\n * - stream_event/content_block_start/tool_use → tool invocation starting\n * - assistant message with tool_use content → tool name + input\n * - assistant message with text content → full text (non-streaming fallback)\n * - result → usage, cost, turns\n */\nexport function parseLine(line: string): ParsedEvent {\n try {\n const obj = JSON.parse(line)\n\n // Token-level streaming (--include-partial-messages)\n if (obj.type === 'stream_event') {\n const evt = obj.event\n if (!evt)\n return {}\n\n // Text delta — the main streaming path\n if (evt.type === 'content_block_delta' && evt.delta?.type === 'text_delta') {\n return { textDelta: evt.delta.text }\n }\n\n return {}\n }\n\n // Full assistant message (complete turn, after streaming)\n if (obj.type === 'assistant' && obj.message?.content) {\n const content = obj.message.content as any[]\n\n // Extract tool uses with inputs for progress hints\n const tools = content.filter((c: any) => c.type === 'tool_use')\n if (tools.length) {\n const names = tools.map((t: any) => t.name)\n // Extract useful hint from tool input (file path, query, etc)\n const hint = tools.map((t: any) => {\n const input = t.input || {}\n return input.file_path || input.path || input.pattern || input.query || input.command || ''\n }).filter(Boolean).join(', ')\n // Capture Write content — primary output path since Write is auto-denied\n const writeTool = tools.find((t: any) => t.name === 'Write' && t.input?.content)\n return { toolName: names.join(', '), toolHint: hint || undefined, writeContent: writeTool?.input?.content }\n }\n\n // Text content (fallback for non-partial mode)\n const text = content\n .filter((c: any) => c.type === 'text')\n .map((c: any) => c.text)\n .join('')\n if (text)\n return { fullText: text }\n }\n\n // Final result\n if (obj.type === 'result') {\n const u = obj.usage\n return {\n done: true,\n usage: u ? { input: u.input_tokens ?? u.inputTokens ?? 0, output: u.output_tokens ?? u.outputTokens ?? 0 } : undefined,\n cost: obj.total_cost_usd,\n turns: obj.num_turns,\n }\n }\n }\n catch {}\n return {}\n}\n","/**\n * OpenAI Codex CLI — exec subcommand with JSON output\n * Prompt passed via stdin with `-` sentinel\n *\n * Event types:\n * - turn.started / turn.completed → turn lifecycle + usage\n * - item.started → command_execution in progress\n * - item.completed → agent_message (text), command_execution (result), file_change (apply_patch)\n * - error / turn.failed → errors\n */\n\nimport type { CliModelEntry, ParsedEvent } from './types.ts'\n\nexport const cli = 'codex' as const\nexport const agentId = 'codex' as const\n\nexport const models: Record<string, CliModelEntry> = {\n 'gpt-5.3-codex': { model: 'gpt-5.3-codex', name: 'GPT-5.3 Codex', hint: 'Latest frontier Codex model' },\n 'gpt-5.3-codex-spark': { model: 'gpt-5.3-codex-spark', name: 'GPT-5.3 Codex Spark', hint: 'Faster GPT-5.3 Codex variant', recommended: true },\n 'gpt-5.2-codex': { model: 'gpt-5.2-codex', name: 'GPT-5.2 Codex', hint: 'Frontier agentic coding model' },\n}\n\nexport function buildArgs(model: string, _skillDir: string, _symlinkDirs: string[]): string[] {\n return [\n 'exec',\n '--json',\n '--ephemeral',\n '--model',\n model,\n // Permissions aligned with Claude's scoped model:\n // --full-auto = --sandbox workspace-write + --ask-for-approval on-request\n // → writes scoped to CWD (.skilld/, set in spawn), reads unrestricted, network blocked\n // Shell remains enabled for `skilld` / `npx -y skilld` search/validate (no per-command allowlist in Codex)\n // --ephemeral → no session persistence (equivalent to Claude's --no-session-persistence)\n '--full-auto',\n '-',\n ]\n}\n\nexport function parseLine(line: string): ParsedEvent {\n try {\n const obj = JSON.parse(line)\n\n if (obj.type === 'item.completed' && obj.item) {\n const item = obj.item\n // Agent message — the main text output\n if (item.type === 'agent_message' && item.text)\n return { fullText: item.text }\n // Command execution completed — log as tool progress\n // If the command writes to a file (redirect or cat >), capture output as writeContent fallback\n if (item.type === 'command_execution' && item.aggregated_output) {\n const cmd = item.command || ''\n const writeContent = (/^cat\\s*>|>/.test(cmd)) ? item.aggregated_output : undefined\n return { toolName: 'Bash', toolHint: `(${item.aggregated_output.length} chars output)`, writeContent }\n }\n // apply_patch completed — file written directly to disk\n if (item.type === 'file_change' && item.changes?.length) {\n const paths = item.changes.map((c: { path: string, kind: string }) => c.path).join(', ')\n return { toolName: 'Write', toolHint: paths }\n }\n }\n\n // Command starting — show progress\n if (obj.type === 'item.started' && obj.item?.type === 'command_execution') {\n return { toolName: 'Bash', toolHint: obj.item.command }\n }\n\n // Turn completed — usage stats\n if (obj.type === 'turn.completed' && obj.usage) {\n return {\n done: true,\n usage: {\n input: obj.usage.input_tokens ?? 0,\n output: obj.usage.output_tokens ?? 0,\n },\n }\n }\n\n // Error events\n if (obj.type === 'turn.failed' || obj.type === 'error') {\n return { done: true }\n }\n }\n catch {}\n return {}\n}\n","/**\n * Gemini CLI — turn-level streaming via -o stream-json\n * Write scoping: relies on cwd being set to .skilld/ (no native --writeable-dirs)\n */\n\nimport type { CliModelEntry, ParsedEvent } from './types.ts'\nimport { resolveSkilldCommand } from '../../core/shared.ts'\n\nexport const cli = 'gemini' as const\nexport const agentId = 'gemini-cli' as const\n\nexport const models: Record<string, CliModelEntry> = {\n 'gemini-3.1-pro': { model: 'gemini-3.1-pro', name: 'Gemini 3.1 Pro', hint: 'Most capable' },\n 'gemini-3-flash': { model: 'gemini-3-flash', name: 'Gemini 3 Flash', hint: 'Balanced', recommended: true },\n}\n\nexport function buildArgs(model: string, skillDir: string, symlinkDirs: string[]): string[] {\n return [\n '-o',\n 'stream-json',\n '-m',\n model,\n '--allowed-tools',\n `read_file,write_file,glob_tool,list_directory,search_file_content,run_shell_command(${resolveSkilldCommand()}),run_shell_command(grep),run_shell_command(head)`,\n '--include-directories',\n skillDir,\n ...symlinkDirs.flatMap(d => ['--include-directories', d]),\n ]\n}\n\n/** Parse gemini stream-json events — turn level (full message per event) */\nexport function parseLine(line: string): ParsedEvent {\n try {\n const obj = JSON.parse(line)\n\n // Text message (delta or full)\n if (obj.type === 'message' && obj.role === 'assistant' && obj.content) {\n return obj.delta ? { textDelta: obj.content } : { fullText: obj.content }\n }\n\n // Tool invocation\n if (obj.type === 'tool_use' || obj.type === 'tool_call') {\n const name = obj.tool_name || obj.name || obj.tool || 'tool'\n const params = obj.parameters || obj.args || obj.input || {}\n const hint = params.file_path || params.path || params.dir_path || params.pattern || params.query || params.command || ''\n // Capture write_file content as fallback (matches Claude's Write tool behavior)\n if (name === 'write_file' && params.content) {\n return { toolName: name, toolHint: hint || undefined, writeContent: params.content }\n }\n return { toolName: name, toolHint: hint || undefined }\n }\n\n // Final result\n if (obj.type === 'result') {\n const s = obj.stats\n return {\n done: true,\n usage: s ? { input: s.input_tokens ?? s.input ?? 0, output: s.output_tokens ?? s.output ?? 0 } : undefined,\n turns: s?.tool_calls,\n }\n }\n }\n catch {}\n return {}\n}\n","/**\n * CLI orchestrator — spawns per-CLI processes for skill generation\n * Each CLI (claude, gemini, codex) has its own buildArgs + parseLine in separate files\n */\n\nimport type { SkillSection } from '../prompts/index.ts'\nimport type { AgentType } from '../types.ts'\nimport type { CliModelConfig, CliName, OptimizeDocsOptions, OptimizeModel, OptimizeResult, ParsedEvent, SectionResult, StreamProgress, ValidationWarning } from './types.ts'\nimport { exec, spawn } from 'node:child_process'\nimport { createHash } from 'node:crypto'\nimport { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, realpathSync, unlinkSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { promisify } from 'node:util'\nimport { dirname, join } from 'pathe'\nimport { isWindows } from 'std-env'\nimport { readCachedSection, writeSections } from '../../cache/index.ts'\nimport { sanitizeMarkdown } from '../../core/sanitize.ts'\nimport { detectInstalledAgents } from '../detect.ts'\nimport { buildAllSectionPrompts, getSectionValidator, SECTION_MERGE_ORDER, SECTION_OUTPUT_FILES, wrapSection } from '../prompts/index.ts'\nimport { agents } from '../registry.ts'\nimport * as claude from './claude.ts'\nimport * as codex from './codex.ts'\nimport * as gemini from './gemini.ts'\n\nexport { buildAllSectionPrompts, buildSectionPrompt, SECTION_MERGE_ORDER, SECTION_OUTPUT_FILES } from '../prompts/index.ts'\nexport type { CustomPrompt, SkillSection } from '../prompts/index.ts'\nexport type { CliModelConfig, CliName, ModelInfo, OptimizeDocsOptions, OptimizeModel, OptimizeResult, StreamProgress } from './types.ts'\n\n// ── Tool progress display ────────────────────────────────────────────\n\nconst TOOL_VERBS: Record<string, string> = {\n // Claude\n Read: 'Reading',\n Glob: 'Searching',\n Grep: 'Searching',\n Write: 'Writing',\n Bash: 'Running',\n // Gemini\n read_file: 'Reading',\n glob_tool: 'Searching',\n write_file: 'Writing',\n list_directory: 'Listing',\n search_file_content: 'Searching',\n run_shell_command: 'Running',\n}\n\ninterface ToolProgressLog {\n message: (msg: string) => void\n}\n\n/** Create a progress callback that emits one line per tool call, Claude Code style */\nexport function createToolProgress(log: ToolProgressLog): (progress: StreamProgress) => void {\n let lastMsg = ''\n let repeatCount = 0\n\n function emit(msg: string) {\n if (msg === lastMsg) {\n repeatCount++\n log.message(`${msg} \\x1B[90m(+${repeatCount})\\x1B[0m`)\n }\n else {\n lastMsg = msg\n repeatCount = 0\n log.message(msg)\n }\n }\n\n return ({ type, chunk, section }) => {\n if (type === 'text') {\n emit(`${section ? `\\x1B[90m[${section}]\\x1B[0m ` : ''}Writing...`)\n return\n }\n if (type !== 'reasoning' || !chunk.startsWith('['))\n return\n\n // Parse individual tool names and hints from \"[Read: path]\" or \"[Read, Glob: path1, path2]\"\n const match = chunk.match(/^\\[([^:[\\]]+)(?::\\s(.+))?\\]$/)\n if (!match)\n return\n\n const names = match[1]!.split(',').map(n => n.trim())\n const hints = match[2]?.split(',').map(h => h.trim()) ?? []\n\n for (let i = 0; i < names.length; i++) {\n const rawName = names[i]!\n const hint = hints[i] ?? hints[0] ?? ''\n const verb = TOOL_VERBS[rawName] ?? rawName\n const prefix = section ? `\\x1B[90m[${section}]\\x1B[0m ` : ''\n\n if ((rawName === 'Bash' || rawName === 'run_shell_command') && hint) {\n const searchMatch = hint.match(/skilld search\\s+\"([^\"]+)\"/)\n if (searchMatch) {\n emit(`${prefix}Searching \\x1B[36m\"${searchMatch[1]}\"\\x1B[0m`)\n }\n else if (hint.includes('skilld validate')) {\n emit(`${prefix}Validating...`)\n }\n else {\n const shortened = shortenCommand(hint)\n emit(`${prefix}Running ${shortened.length > 50 ? `${shortened.slice(0, 47)}...` : shortened}`)\n }\n }\n else {\n const path = shortenPath(hint || '...')\n emit(`${prefix}${verb} \\x1B[90m${path}\\x1B[0m`)\n }\n }\n }\n}\n\n// ── Per-CLI dispatch ─────────────────────────────────────────────────\n\nconst CLI_DEFS = [claude, gemini, codex] as const\n\nconst CLI_BUILD_ARGS: Record<CliName, (model: string, skillDir: string, symlinkDirs: string[]) => string[]> = {\n claude: claude.buildArgs,\n gemini: gemini.buildArgs,\n codex: codex.buildArgs,\n}\n\nconst CLI_PARSE_LINE: Record<CliName, (line: string) => ParsedEvent> = {\n claude: claude.parseLine,\n gemini: gemini.parseLine,\n codex: codex.parseLine,\n}\n\n// ── Assemble CLI_MODELS from per-CLI model definitions ───────────────\n\nexport const CLI_MODELS: Partial<Record<OptimizeModel, CliModelConfig>> = Object.fromEntries(\n CLI_DEFS.flatMap(def =>\n Object.entries(def.models).map(([id, entry]) => [\n id,\n { ...entry, cli: def.cli, agentId: def.agentId },\n ]),\n ),\n)\n\n// ── Model helpers ────────────────────────────────────────────────────\n\nexport function getModelName(id: OptimizeModel): string {\n return CLI_MODELS[id]?.name ?? id\n}\n\nexport function getModelLabel(id: OptimizeModel): string {\n const config = CLI_MODELS[id]\n if (!config)\n return id\n const agentName = agents[config.agentId]?.displayName ?? config.cli\n return `${agentName} · ${config.name}`\n}\n\nexport async function getAvailableModels(): Promise<import('./types.ts').ModelInfo[]> {\n const execAsync = promisify(exec)\n const lookupCmd = isWindows ? 'where' : 'which'\n\n const installedAgents = detectInstalledAgents()\n const agentsWithCli = installedAgents.filter(id => agents[id].cli)\n\n const cliChecks = await Promise.all(\n agentsWithCli.map(async (agentId) => {\n const cli = agents[agentId].cli!\n try {\n await execAsync(`${lookupCmd} ${cli}`)\n return agentId\n }\n catch { return null }\n }),\n )\n const availableAgentIds = new Set(cliChecks.filter((id): id is AgentType => id != null))\n\n return (Object.entries(CLI_MODELS) as [OptimizeModel, CliModelConfig][])\n .filter(([_, config]) => availableAgentIds.has(config.agentId))\n .map(([id, config]) => ({\n id,\n name: config.name,\n hint: config.hint,\n recommended: config.recommended,\n agentId: config.agentId,\n agentName: agents[config.agentId]?.displayName ?? config.agentId,\n }))\n}\n\n// ── Reference dirs ───────────────────────────────────────────────────\n\n/** Resolve symlinks in .skilld/ to get real paths for --add-dir */\nfunction resolveReferenceDirs(skillDir: string): string[] {\n const refsDir = join(skillDir, '.skilld')\n if (!existsSync(refsDir))\n return []\n const resolved = readdirSync(refsDir)\n .map(entry => join(refsDir, entry))\n .filter(p => lstatSync(p).isSymbolicLink() && existsSync(p))\n .map(p => realpathSync(p))\n\n // Include parent directories so CLIs can search across all references at once\n // (e.g. Gemini's sandbox requires the parent dir to be explicitly included)\n const parents = new Set<string>()\n for (const p of resolved) {\n const parent = dirname(p)\n if (!resolved.includes(parent))\n parents.add(parent)\n }\n\n return [...resolved, ...parents]\n}\n\n// ── Cache ────────────────────────────────────────────────────────────\n\nconst CACHE_DIR = join(homedir(), '.skilld', 'llm-cache')\n\n/** Strip absolute paths from prompt so the hash is project-independent */\nfunction normalizePromptForHash(prompt: string): string {\n return prompt.replace(/\\/[^\\s`]*\\.(?:claude|codex|gemini)\\/skills\\/[^\\s/`]+/g, '<SKILL_DIR>')\n}\n\nfunction hashPrompt(prompt: string, model: OptimizeModel, section: SkillSection): string {\n return createHash('sha256').update(`exec:${model}:${section}:${normalizePromptForHash(prompt)}`).digest('hex').slice(0, 16)\n}\n\nfunction getCached(prompt: string, model: OptimizeModel, section: SkillSection, maxAge = 7 * 24 * 60 * 60 * 1000): string | null {\n const path = join(CACHE_DIR, `${hashPrompt(prompt, model, section)}.json`)\n if (!existsSync(path))\n return null\n try {\n const { text, timestamp } = JSON.parse(readFileSync(path, 'utf-8'))\n return Date.now() - timestamp > maxAge ? null : text\n }\n catch { return null }\n}\n\nfunction setCache(prompt: string, model: OptimizeModel, section: SkillSection, text: string): void {\n mkdirSync(CACHE_DIR, { recursive: true, mode: 0o700 })\n writeFileSync(\n join(CACHE_DIR, `${hashPrompt(prompt, model, section)}.json`),\n JSON.stringify({ text, model, section, timestamp: Date.now() }),\n { mode: 0o600 },\n )\n}\n\n// ── Per-section spawn ────────────────────────────────────────────────\n\ninterface OptimizeSectionOptions {\n section: SkillSection\n prompt: string\n outputFile: string\n skillDir: string\n model: OptimizeModel\n packageName: string\n onProgress?: (progress: StreamProgress) => void\n timeout: number\n debug?: boolean\n preExistingFiles: Set<string>\n}\n\n/** Spawn a single CLI process for one section */\nfunction optimizeSection(opts: OptimizeSectionOptions): Promise<SectionResult> {\n const { section, prompt, outputFile, skillDir, model, onProgress, timeout, debug, preExistingFiles } = opts\n\n const cliConfig = CLI_MODELS[model]\n if (!cliConfig) {\n return Promise.resolve({ section, content: '', wasOptimized: false, error: `No CLI mapping for model: ${model}` })\n }\n\n const { cli, model: cliModel } = cliConfig\n const symlinkDirs = resolveReferenceDirs(skillDir)\n const args = CLI_BUILD_ARGS[cli](cliModel, skillDir, symlinkDirs)\n const parseLine = CLI_PARSE_LINE[cli]\n\n const skilldDir = join(skillDir, '.skilld')\n const outputPath = join(skilldDir, outputFile)\n\n // Remove stale output so we don't read a leftover from a previous run\n if (existsSync(outputPath))\n unlinkSync(outputPath)\n\n // Write prompt for debugging\n writeFileSync(join(skilldDir, `PROMPT_${section}.md`), prompt)\n\n return new Promise<SectionResult>((resolve) => {\n const proc = spawn(cli, args, {\n cwd: skilldDir,\n stdio: ['pipe', 'pipe', 'pipe'],\n timeout,\n env: { ...process.env, NO_COLOR: '1' },\n shell: isWindows,\n })\n\n let buffer = ''\n let accumulatedText = ''\n let lastWriteContent = ''\n let usage: { input: number, output: number } | undefined\n let cost: number | undefined\n const rawLines: string[] = []\n\n onProgress?.({ chunk: '[starting...]', type: 'reasoning', text: '', reasoning: '', section })\n\n proc.stdin.write(prompt)\n proc.stdin.end()\n\n proc.stdout.on('data', (chunk: Buffer) => {\n buffer += chunk.toString()\n const lines = buffer.split('\\n')\n buffer = lines.pop() || ''\n\n for (const line of lines) {\n if (!line.trim())\n continue\n if (debug)\n rawLines.push(line)\n const evt = parseLine(line)\n\n if (evt.textDelta)\n accumulatedText += evt.textDelta\n if (evt.fullText)\n accumulatedText = evt.fullText\n\n if (evt.writeContent)\n lastWriteContent = evt.writeContent\n\n if (evt.toolName) {\n const hint = evt.toolHint\n ? `[${evt.toolName}: ${evt.toolHint}]`\n : `[${evt.toolName}]`\n onProgress?.({ chunk: hint, type: 'reasoning', text: '', reasoning: hint, section })\n }\n\n if (evt.usage)\n usage = evt.usage\n if (evt.cost != null)\n cost = evt.cost\n }\n })\n\n let stderr = ''\n proc.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString()\n })\n\n proc.on('close', (code) => {\n // Drain remaining buffer for metadata\n if (buffer.trim()) {\n const evt = parseLine(buffer)\n if (evt.textDelta)\n accumulatedText += evt.textDelta\n if (evt.fullText)\n accumulatedText = evt.fullText\n if (evt.writeContent)\n lastWriteContent = evt.writeContent\n if (evt.usage)\n usage = evt.usage\n if (evt.cost != null)\n cost = evt.cost\n }\n\n // Remove unexpected files the LLM may have written (prompt injection defense)\n // Only clean files not in the pre-existing snapshot and not our expected output\n for (const entry of readdirSync(skilldDir)) {\n if (entry !== outputFile && !preExistingFiles.has(entry)) {\n // Allow other section output files and debug prompts\n if (Object.values(SECTION_OUTPUT_FILES).includes(entry))\n continue\n if (entry.startsWith('PROMPT_') || entry === 'logs')\n continue\n try {\n unlinkSync(join(skilldDir, entry))\n }\n catch {}\n }\n }\n\n // Prefer file written by LLM, fall back to Write tool content (if denied), then accumulated stdout\n const raw = (existsSync(outputPath) ? readFileSync(outputPath, 'utf-8') : lastWriteContent || accumulatedText).trim()\n\n // Always write stderr on failure; write all logs in debug mode\n const logsDir = join(skilldDir, 'logs')\n const logName = section.toUpperCase().replace(/-/g, '_')\n if (debug || (stderr && (!raw || code !== 0))) {\n mkdirSync(logsDir, { recursive: true })\n if (stderr)\n writeFileSync(join(logsDir, `${logName}.stderr.log`), stderr)\n }\n if (debug) {\n mkdirSync(logsDir, { recursive: true })\n if (rawLines.length)\n writeFileSync(join(logsDir, `${logName}.jsonl`), rawLines.join('\\n'))\n if (raw)\n writeFileSync(join(logsDir, `${logName}.md`), raw)\n }\n\n if (!raw && code !== 0) {\n resolve({ section, content: '', wasOptimized: false, error: stderr.trim() || `CLI exited with code ${code}` })\n return\n }\n\n // Clean the section output (strip markdown fences, frontmatter, sanitize)\n const content = raw ? cleanSectionOutput(raw) : ''\n\n if (content) {\n // Write cleaned content back to the output file for debugging\n writeFileSync(outputPath, content)\n }\n\n const validator = getSectionValidator(section)\n const rawWarnings = content && validator ? validator(content) : []\n const warnings: ValidationWarning[] = rawWarnings.map(w => ({ section, warning: w.warning }))\n\n resolve({\n section,\n content,\n wasOptimized: !!content,\n warnings: warnings?.length ? warnings : undefined,\n usage,\n cost,\n })\n })\n\n proc.on('error', (err) => {\n resolve({ section, content: '', wasOptimized: false, error: err.message })\n })\n })\n}\n\n// ── Main orchestrator ────────────────────────────────────────────────\n\nexport async function optimizeDocs(opts: OptimizeDocsOptions): Promise<OptimizeResult> {\n const { packageName, skillDir, model = 'sonnet', version, hasGithub, hasReleases, hasChangelog, docFiles, docsType, hasShippedDocs, onProgress, timeout = 180000, debug, noCache, sections, customPrompt, features, pkgFiles } = opts\n\n const selectedSections = sections ?? ['api-changes', 'best-practices'] as SkillSection[]\n\n // Build all section prompts\n const sectionPrompts = buildAllSectionPrompts({\n packageName,\n skillDir,\n version,\n hasIssues: hasGithub,\n hasDiscussions: hasGithub,\n hasReleases,\n hasChangelog,\n docFiles,\n docsType,\n hasShippedDocs,\n customPrompt,\n features,\n pkgFiles,\n sections: selectedSections,\n })\n\n if (sectionPrompts.size === 0) {\n return { optimized: '', wasOptimized: false, error: 'No valid sections to generate' }\n }\n\n const cliConfig = CLI_MODELS[model]\n if (!cliConfig) {\n return { optimized: '', wasOptimized: false, error: `No CLI mapping for model: ${model}` }\n }\n\n // Check per-section cache: references dir first (version-keyed), then LLM cache (prompt-hashed)\n const cachedResults: SectionResult[] = []\n const uncachedSections: Array<{ section: SkillSection, prompt: string }> = []\n\n for (const [section, prompt] of sectionPrompts) {\n if (!noCache) {\n // Check global references dir (cross-project, version-keyed)\n if (version) {\n const outputFile = SECTION_OUTPUT_FILES[section]\n const refCached = readCachedSection(packageName, version, outputFile)\n if (refCached) {\n onProgress?.({ chunk: `[${section}: cached]`, type: 'text', text: refCached, reasoning: '', section })\n cachedResults.push({ section, content: refCached, wasOptimized: true })\n continue\n }\n }\n\n // Check LLM prompt-hash cache\n const cached = getCached(prompt, model, section)\n if (cached) {\n onProgress?.({ chunk: `[${section}: cached]`, type: 'text', text: cached, reasoning: '', section })\n cachedResults.push({ section, content: cached, wasOptimized: true })\n continue\n }\n }\n uncachedSections.push({ section, prompt })\n }\n\n // Prepare .skilld/ dir and snapshot before spawns\n const skilldDir = join(skillDir, '.skilld')\n mkdirSync(skilldDir, { recursive: true })\n\n // Pre-flight: warn about broken symlinks in .skilld/ (avoids wasting tokens on missing refs)\n for (const entry of readdirSync(skilldDir)) {\n const entryPath = join(skilldDir, entry)\n try {\n if (lstatSync(entryPath).isSymbolicLink() && !existsSync(entryPath))\n onProgress?.({ chunk: `[warn: broken symlink .skilld/${entry}]`, type: 'reasoning', text: '', reasoning: '' })\n }\n catch {}\n }\n\n const preExistingFiles = new Set(readdirSync(skilldDir))\n\n // Spawn uncached sections with staggered starts to avoid rate-limit collisions\n const STAGGER_MS = 3000\n const spawnResults = uncachedSections.length > 0\n ? await Promise.allSettled(\n uncachedSections.map(({ section, prompt }, i) => {\n const outputFile = SECTION_OUTPUT_FILES[section]\n const run = () => optimizeSection({\n section,\n prompt,\n outputFile,\n skillDir,\n model,\n packageName,\n onProgress,\n timeout,\n debug,\n preExistingFiles,\n })\n // Stagger: first section starts immediately, rest delayed\n if (i === 0)\n return run()\n return delay(i * STAGGER_MS).then(run)\n }),\n )\n : []\n\n // Collect results, retry failed sections once\n const allResults: SectionResult[] = [...cachedResults]\n let totalUsage: { input: number, output: number } | undefined\n let totalCost = 0\n const retryQueue: Array<{ index: number, section: SkillSection, prompt: string }> = []\n\n for (let i = 0; i < spawnResults.length; i++) {\n const r = spawnResults[i]!\n const { section, prompt } = uncachedSections[i]!\n if (r.status === 'fulfilled' && r.value.wasOptimized) {\n allResults.push(r.value)\n if (r.value.usage) {\n totalUsage = totalUsage ?? { input: 0, output: 0 }\n totalUsage.input += r.value.usage.input\n totalUsage.output += r.value.usage.output\n }\n if (r.value.cost != null)\n totalCost += r.value.cost\n if (!noCache)\n setCache(prompt, model, section, r.value.content)\n }\n else {\n retryQueue.push({ index: i, section, prompt })\n }\n }\n\n // Retry failed sections once (sequential to avoid rate limits)\n for (const { section, prompt } of retryQueue) {\n onProgress?.({ chunk: `[${section}: retrying...]`, type: 'reasoning', text: '', reasoning: '', section })\n await delay(STAGGER_MS)\n const result = await optimizeSection({\n section,\n prompt,\n outputFile: SECTION_OUTPUT_FILES[section],\n skillDir,\n model,\n packageName,\n onProgress,\n timeout,\n debug,\n preExistingFiles,\n }).catch((err: Error) => ({ section, content: '', wasOptimized: false, error: err.message }) as SectionResult)\n\n allResults.push(result)\n if (result.wasOptimized && !noCache)\n setCache(prompt, model, section, result.content)\n if (result.usage) {\n totalUsage = totalUsage ?? { input: 0, output: 0 }\n totalUsage.input += result.usage.input\n totalUsage.output += result.usage.output\n }\n if (result.cost != null)\n totalCost += result.cost\n }\n\n // Write successful sections to global references dir for cross-project reuse\n if (version) {\n const sectionFiles = allResults\n .filter(r => r.wasOptimized && r.content)\n .map(r => ({ file: SECTION_OUTPUT_FILES[r.section], content: r.content }))\n if (sectionFiles.length > 0) {\n writeSections(packageName, version, sectionFiles)\n }\n }\n\n // Merge results in SECTION_MERGE_ORDER, wrapped with comment markers\n const mergedParts: string[] = []\n for (const section of SECTION_MERGE_ORDER) {\n const result = allResults.find(r => r.section === section)\n if (result?.wasOptimized && result.content) {\n mergedParts.push(wrapSection(section, result.content))\n }\n }\n\n const optimized = mergedParts.join('\\n\\n')\n const wasOptimized = mergedParts.length > 0\n\n const usageResult = totalUsage\n ? { inputTokens: totalUsage.input, outputTokens: totalUsage.output, totalTokens: totalUsage.input + totalUsage.output }\n : undefined\n\n // Collect errors and warnings from sections\n const errors = allResults.filter(r => r.error).map(r => `${r.section}: ${r.error}`)\n const warnings = allResults.flatMap(r => r.warnings ?? []).map(w => `${w.section}: ${w.warning}`)\n\n const debugLogsDir = debug && uncachedSections.length > 0\n ? join(skillDir, '.skilld', 'logs')\n : undefined\n\n return {\n optimized,\n wasOptimized,\n error: errors.length > 0 ? errors.join('; ') : undefined,\n warnings: warnings.length > 0 ? warnings : undefined,\n finishReason: wasOptimized ? 'stop' : 'error',\n usage: usageResult,\n cost: totalCost || undefined,\n debugLogsDir,\n }\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\n/** Shorten absolute paths for display: /home/user/project/.claude/skills/vue/SKILL.md → .claude/.../SKILL.md */\nfunction shortenPath(p: string): string {\n const refIdx = p.indexOf('.skilld/')\n if (refIdx !== -1)\n return p.slice(refIdx + '.skilld/'.length)\n // Keep just filename for other paths\n const parts = p.split('/')\n return parts.length > 2 ? `.../${parts.slice(-2).join('/')}` : p\n}\n\n/** Replace absolute paths in a command string with shortened versions */\nfunction shortenCommand(cmd: string): string {\n return cmd.replace(/\\/[^\\s\"']+/g, (match) => {\n // Only shorten paths that look like they're inside a project\n if (match.includes('.claude/') || match.includes('.skilld/') || match.includes('node_modules/'))\n return `.../${match.split('/').slice(-2).join('/')}`\n return match\n })\n}\n\n/** Clean a single section's LLM output: strip markdown fences, frontmatter, sanitize */\nexport function cleanSectionOutput(content: string): string {\n let cleaned = content.trim()\n\n // Strip wrapping fences if output is wrapped in ```markdown, ```md, or bare ```\n // Requires matched open+close pair to avoid stripping internal code blocks\n const wrapMatch = cleaned.match(/^```(?:markdown|md)?[^\\S\\n]*\\n([\\s\\S]+)\\n```[^\\S\\n]*$/)\n if (wrapMatch) {\n const inner = wrapMatch[1]!.trim()\n // For bare ``` wrappers (no markdown/md tag), verify inner looks like section output\n const isExplicitWrapper = /^```(?:markdown|md)/.test(cleaned)\n if (isExplicitWrapper || /^##\\s/m.test(inner) || /^- (?:BREAKING|DEPRECATED|NEW): /m.test(inner)) {\n cleaned = inner\n }\n }\n\n // Normalize h1 headers to h2 — LLMs sometimes use # instead of ##\n cleaned = cleaned.replace(/^# (?!#)/gm, '## ')\n\n // Strip accidental frontmatter or leading horizontal rules\n const fmMatch = cleaned.match(/^-{3,}\\n/)\n if (fmMatch) {\n const afterOpen = fmMatch[0].length\n const closeMatch = cleaned.slice(afterOpen).match(/\\n-{3,}/)\n if (closeMatch) {\n cleaned = cleaned.slice(afterOpen + closeMatch.index! + closeMatch[0].length).trim()\n }\n else {\n cleaned = cleaned.slice(afterOpen).trim()\n }\n }\n\n // Strip raw code preamble before first section marker (defense against LLMs dumping source)\n // Section markers: ## heading, BREAKING/DEPRECATED/NEW labels\n const firstMarker = cleaned.match(/^(##\\s|- (?:BREAKING|DEPRECATED|NEW): )/m)\n if (firstMarker?.index && firstMarker.index > 0) {\n const preamble = cleaned.slice(0, firstMarker.index)\n // Only strip if preamble looks like code (contains function/const/export/return patterns)\n if (/\\b(?:function|const |let |var |export |return |import |async |class )\\b/.test(preamble)) {\n cleaned = cleaned.slice(firstMarker.index).trim()\n }\n }\n\n // Strip duplicate section headings (LLM echoing the format example before real content)\n // Handles headings separated by blank lines or boilerplate text\n const headingMatch = cleaned.match(/^(## .+)\\n/)\n if (headingMatch) {\n const heading = headingMatch[1]!\n const afterFirst = headingMatch[0].length\n const secondIdx = cleaned.indexOf(heading, afterFirst)\n if (secondIdx !== -1) {\n // Only strip if the gap between duplicates is small (< 200 chars of boilerplate)\n if (secondIdx - afterFirst < 200)\n cleaned = cleaned.slice(secondIdx).trim()\n }\n }\n\n // Normalize citation link text to [source] — LLMs sometimes use the path as link text\n // e.g. [./references/docs/api.md](./references/docs/api.md) or [`./references/...`](...)\n // Also handles paren-wrapped variants: ([`path`](url))\n cleaned = cleaned.replace(\n /\\(?\\[`?\\.\\/(?:\\.skilld\\/|references\\/)[^)\\]]*\\]\\(([^)]+)\\)\\)?/g,\n (match, url: string) => {\n // Only normalize if the URL points to a reference path\n if (/^\\.\\/(?:\\.skilld\\/|references\\/)/.test(url))\n return `[source](${url})`\n return match\n },\n )\n\n // Normalize source link paths: ensure .skilld/ prefix is present\n // LLMs sometimes emit [source](./docs/...) instead of [source](./.skilld/docs/...)\n cleaned = cleaned.replace(\n /\\[source\\]\\(\\.\\/((docs|issues|discussions|releases|pkg|guide)\\/)/g,\n '[source](./.skilld/$1',\n )\n\n cleaned = sanitizeMarkdown(cleaned)\n\n // Reject content that lacks any section structure — likely leaked LLM reasoning/narration\n // Valid sections contain headings (##), API change labels, or source-linked items\n if (!/^##\\s/m.test(cleaned) && !/^- (?:BREAKING|DEPRECATED|NEW): /m.test(cleaned) && !/\\[source\\]/.test(cleaned)) {\n return ''\n }\n\n return cleaned\n}\n","/**\n * Detect packages from framework presets (e.g., Nuxt modules in nuxt.config)\n * These are string literals in config arrays, not imports — the import scanner misses them.\n */\n\nimport type { PackageUsage } from './detect-imports.ts'\nimport { readFile } from 'node:fs/promises'\nimport { parseSync } from 'oxc-parser'\nimport { join } from 'pathe'\n\nconst NUXT_CONFIG_FILES = ['nuxt.config.ts', 'nuxt.config.js', 'nuxt.config.mjs']\nconst NUXT_ECOSYSTEM = ['vue', 'nitro', 'h3']\n\nasync function findNuxtConfig(cwd: string): Promise<{ path: string, content: string } | null> {\n for (const name of NUXT_CONFIG_FILES) {\n const path = join(cwd, name)\n const content = await readFile(path, 'utf8').catch(() => null)\n if (content)\n return { path, content }\n }\n return null\n}\n\n/**\n * Walk AST node to find all string values inside a `modules` array property.\n * Handles: defineNuxtConfig({ modules: [...] }) and export default { modules: [...] }\n */\nexport function extractModuleStrings(node: any): string[] {\n if (!node || typeof node !== 'object')\n return []\n\n // Found a Property with key \"modules\" and an ArrayExpression value\n if (node.type === 'Property' && !node.computed\n && (node.key?.type === 'Identifier' && node.key.name === 'modules')\n && node.value?.type === 'ArrayExpression') { return node.value.elements.filter((el: any) => el?.type === 'Literal' && typeof el.value === 'string').map((el: any) => el.value as string) }\n\n // Recurse into arrays and object values\n const results: string[] = []\n if (Array.isArray(node)) {\n for (const child of node)\n results.push(...extractModuleStrings(child))\n }\n else {\n for (const key of Object.keys(node)) {\n if (key === 'start' || key === 'end' || key === 'type')\n continue\n const val = node[key]\n if (val && typeof val === 'object')\n results.push(...extractModuleStrings(val))\n }\n }\n return results\n}\n\n/**\n * Detect Nuxt modules from nuxt.config.{ts,js,mjs}\n */\nexport async function detectNuxtModules(cwd: string): Promise<PackageUsage[]> {\n const config = await findNuxtConfig(cwd)\n if (!config)\n return []\n\n const result = parseSync(config.path, config.content)\n const modules = extractModuleStrings(result.program)\n\n // Dedupe and build results\n const seen = new Set<string>()\n const packages: PackageUsage[] = []\n\n for (const mod of modules) {\n if (!seen.has(mod)) {\n seen.add(mod)\n packages.push({ name: mod, count: 0, source: 'preset' })\n }\n }\n\n // Add core ecosystem packages\n for (const pkg of NUXT_ECOSYSTEM) {\n if (!seen.has(pkg)) {\n seen.add(pkg)\n packages.push({ name: pkg, count: 0, source: 'preset' })\n }\n }\n\n return packages\n}\n\n/**\n * Run all preset detectors and merge results\n */\nexport async function detectPresetPackages(cwd: string): Promise<PackageUsage[]> {\n // Currently only Nuxt, but extensible for other frameworks\n return detectNuxtModules(cwd)\n}\n","/**\n * Detect directly-used npm packages by scanning source files\n * Uses mlly for proper ES module parsing + tinyglobby for file discovery\n */\n\nimport { readFile } from 'node:fs/promises'\nimport { findDynamicImports, findStaticImports } from 'mlly'\nimport { glob } from 'tinyglobby'\nimport { detectPresetPackages } from './detect-presets.ts'\n\nexport interface PackageUsage {\n name: string\n count: number\n source?: 'import' | 'preset'\n}\n\nexport interface DetectResult {\n packages: PackageUsage[]\n error?: string\n}\n\nconst PATTERNS = ['**/*.{ts,js,vue,mjs,cjs,tsx,jsx,mts,cts}']\nconst IGNORE = ['**/node_modules/**', '**/dist/**', '**/.nuxt/**', '**/.output/**', '**/coverage/**']\n\nfunction addPackage(counts: Map<string, number>, specifier: string | undefined) {\n if (!specifier || specifier.startsWith('.') || specifier.startsWith('/'))\n return\n\n // Extract package name (handle subpaths like 'pkg/subpath')\n const name = specifier.startsWith('@')\n ? specifier.split('/').slice(0, 2).join('/')\n : specifier.split('/')[0]!\n\n if (!isNodeBuiltin(name)) {\n counts.set(name, (counts.get(name) || 0) + 1)\n }\n}\n\n/**\n * Scan source files to detect all directly-imported npm packages\n * Async with gitignore support for proper spinner animation\n */\nexport async function detectImportedPackages(cwd: string = process.cwd()): Promise<DetectResult> {\n try {\n const counts = new Map<string, number>()\n\n const files = await glob(PATTERNS, {\n cwd,\n ignore: IGNORE,\n absolute: true,\n expandDirectories: false,\n })\n\n await Promise.all(files.map(async (file) => {\n const content = await readFile(file, 'utf8')\n\n // Static: import x from 'pkg'\n for (const imp of findStaticImports(content)) {\n addPackage(counts, imp.specifier)\n }\n\n // Dynamic: import('pkg') - expression is the string literal\n for (const imp of findDynamicImports(content)) {\n // expression includes quotes, extract string value\n const match = imp.expression.match(/^['\"]([^'\"]+)['\"]$/)\n if (match)\n addPackage(counts, match[1]!)\n }\n }))\n\n // Sort by usage count (descending), then alphabetically\n const packages: PackageUsage[] = Array.from(counts.entries(), ([name, count]) => ({ name, count, source: 'import' as const }))\n .sort((a, b) => b.count - a.count || a.name.localeCompare(b.name))\n\n // Merge preset-detected packages (imports take priority)\n const presets = await detectPresetPackages(cwd)\n const importNames = new Set(packages.map(p => p.name))\n for (const preset of presets) {\n if (!importNames.has(preset.name))\n packages.push(preset)\n }\n\n return { packages }\n }\n catch (err) {\n return { packages: [], error: String(err) }\n }\n}\n\nconst NODE_BUILTINS = new Set([\n 'assert',\n 'async_hooks',\n 'buffer',\n 'child_process',\n 'cluster',\n 'console',\n 'constants',\n 'crypto',\n 'dgram',\n 'diagnostics_channel',\n 'dns',\n 'domain',\n 'events',\n 'fs',\n 'http',\n 'http2',\n 'https',\n 'inspector',\n 'module',\n 'net',\n 'os',\n 'path',\n 'perf_hooks',\n 'process',\n 'punycode',\n 'querystring',\n 'readline',\n 'repl',\n 'sea',\n 'sqlite',\n 'stream',\n 'string_decoder',\n 'sys',\n 'test',\n 'timers',\n 'tls',\n 'trace_events',\n 'tty',\n 'url',\n 'util',\n 'v8',\n 'vm',\n 'wasi',\n 'worker_threads',\n 'zlib',\n])\n\nfunction isNodeBuiltin(pkg: string): boolean {\n const base = pkg.startsWith('node:') ? pkg.slice(5) : pkg\n return NODE_BUILTINS.has(base.split('/')[0]!)\n}\n"],"mappings":"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"}
@@ -12,6 +12,18 @@ import { defineCommand } from "citty";
12
12
  */
13
13
  const LLM_CACHE_DIR = join(CACHE_DIR, "llm-cache");
14
14
  const LLM_CACHE_MAX_AGE = 10080 * 60 * 1e3;
15
+ function safeRemove(path) {
16
+ try {
17
+ const size = statSync(path).size;
18
+ rmSync(path);
19
+ return size;
20
+ } catch {
21
+ try {
22
+ rmSync(path);
23
+ } catch {}
24
+ return 0;
25
+ }
26
+ }
15
27
  async function cacheCleanCommand() {
16
28
  let expiredLlm = 0;
17
29
  let freedBytes = 0;
@@ -22,16 +34,12 @@ async function cacheCleanCommand() {
22
34
  try {
23
35
  const { timestamp } = JSON.parse(readFileSync(path, "utf-8"));
24
36
  if (now - timestamp > LLM_CACHE_MAX_AGE) {
25
- const size = statSync(path).size;
26
- rmSync(path);
37
+ freedBytes += safeRemove(path);
27
38
  expiredLlm++;
28
- freedBytes += size;
29
39
  }
30
40
  } catch {
31
- const size = statSync(path).size;
32
- rmSync(path);
41
+ freedBytes += safeRemove(path);
33
42
  expiredLlm++;
34
- freedBytes += size;
35
43
  }
36
44
  }
37
45
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cache2.mjs","names":[],"sources":["../../src/commands/cache.ts"],"sourcesContent":["/**\n * Cache management commands\n */\n\nimport { existsSync, readdirSync, readFileSync, rmSync, statSync } from 'node:fs'\nimport * as p from '@clack/prompts'\nimport { defineCommand } from 'citty'\nimport { join } from 'pathe'\nimport { CACHE_DIR } from '../cache/index.ts'\nimport { clearEmbeddingCache } from '../retriv/embedding-cache.ts'\n\nconst LLM_CACHE_DIR = join(CACHE_DIR, 'llm-cache')\nconst LLM_CACHE_MAX_AGE = 7 * 24 * 60 * 60 * 1000\n\nexport async function cacheCleanCommand(): Promise<void> {\n let expiredLlm = 0\n let freedBytes = 0\n\n // Clean expired LLM cache entries\n if (existsSync(LLM_CACHE_DIR)) {\n const now = Date.now()\n for (const entry of readdirSync(LLM_CACHE_DIR)) {\n const path = join(LLM_CACHE_DIR, entry)\n try {\n const { timestamp } = JSON.parse(readFileSync(path, 'utf-8'))\n if (now - timestamp > LLM_CACHE_MAX_AGE) {\n const size = statSync(path).size\n rmSync(path)\n expiredLlm++\n freedBytes += size\n }\n }\n catch {\n // Corrupt cache entry — remove it\n const size = statSync(path).size\n rmSync(path)\n expiredLlm++\n freedBytes += size\n }\n }\n }\n\n // Clear embedding cache\n const embeddingDbPath = join(CACHE_DIR, 'embeddings.db')\n let embeddingCleared = false\n if (existsSync(embeddingDbPath)) {\n const size = statSync(embeddingDbPath).size\n clearEmbeddingCache()\n freedBytes += size\n embeddingCleared = true\n }\n\n const freedKB = Math.round(freedBytes / 1024)\n if (expiredLlm > 0 || embeddingCleared) {\n const parts: string[] = []\n if (expiredLlm > 0)\n parts.push(`${expiredLlm} expired LLM cache entries`)\n if (embeddingCleared)\n parts.push('embedding cache')\n p.log.success(`Removed ${parts.join(' + ')} (${freedKB}KB freed)`)\n }\n else {\n p.log.info('Cache is clean — no expired entries')\n }\n}\n\nexport const cacheCommandDef = defineCommand({\n meta: { name: 'cache', description: 'Cache management', hidden: true },\n args: {\n clean: {\n type: 'boolean',\n description: 'Remove expired LLM cache entries',\n default: true,\n },\n },\n async run() {\n p.intro(`\\x1B[1m\\x1B[35mskilld\\x1B[0m cache clean`)\n await cacheCleanCommand()\n },\n})\n"],"mappings":";;;;;;;;;;;;AAWA,MAAM,gBAAgB,KAAK,WAAW,YAAY;AAClD,MAAM,oBAAoB,QAAc,KAAK;AAE7C,eAAsB,oBAAmC;CACvD,IAAI,aAAa;CACjB,IAAI,aAAa;AAGjB,KAAI,WAAW,cAAc,EAAE;EAC7B,MAAM,MAAM,KAAK,KAAK;AACtB,OAAK,MAAM,SAAS,YAAY,cAAc,EAAE;GAC9C,MAAM,OAAO,KAAK,eAAe,MAAM;AACvC,OAAI;IACF,MAAM,EAAE,cAAc,KAAK,MAAM,aAAa,MAAM,QAAQ,CAAC;AAC7D,QAAI,MAAM,YAAY,mBAAmB;KACvC,MAAM,OAAO,SAAS,KAAK,CAAC;AAC5B,YAAO,KAAK;AACZ;AACA,mBAAc;;WAGZ;IAEJ,MAAM,OAAO,SAAS,KAAK,CAAC;AAC5B,WAAO,KAAK;AACZ;AACA,kBAAc;;;;CAMpB,MAAM,kBAAkB,KAAK,WAAW,gBAAgB;CACxD,IAAI,mBAAmB;AACvB,KAAI,WAAW,gBAAgB,EAAE;EAC/B,MAAM,OAAO,SAAS,gBAAgB,CAAC;AACvC,uBAAqB;AACrB,gBAAc;AACd,qBAAmB;;CAGrB,MAAM,UAAU,KAAK,MAAM,aAAa,KAAK;AAC7C,KAAI,aAAa,KAAK,kBAAkB;EACtC,MAAM,QAAkB,EAAE;AAC1B,MAAI,aAAa,EACf,OAAM,KAAK,GAAG,WAAW,4BAA4B;AACvD,MAAI,iBACF,OAAM,KAAK,kBAAkB;AAC/B,IAAE,IAAI,QAAQ,WAAW,MAAM,KAAK,MAAM,CAAC,IAAI,QAAQ,WAAW;OAGlE,GAAE,IAAI,KAAK,sCAAsC;;AAIrD,MAAa,kBAAkB,cAAc;CAC3C,MAAM;EAAE,MAAM;EAAS,aAAa;EAAoB,QAAQ;EAAM;CACtE,MAAM,EACJ,OAAO;EACL,MAAM;EACN,aAAa;EACb,SAAS;EACV,EACF;CACD,MAAM,MAAM;AACV,IAAE,MAAM,2CAA2C;AACnD,QAAM,mBAAmB;;CAE5B,CAAC"}
1
+ {"version":3,"file":"cache2.mjs","names":[],"sources":["../../src/commands/cache.ts"],"sourcesContent":["/**\n * Cache management commands\n */\n\nimport { existsSync, readdirSync, readFileSync, rmSync, statSync } from 'node:fs'\nimport * as p from '@clack/prompts'\nimport { defineCommand } from 'citty'\nimport { join } from 'pathe'\nimport { CACHE_DIR } from '../cache/index.ts'\nimport { clearEmbeddingCache } from '../retriv/embedding-cache.ts'\n\nconst LLM_CACHE_DIR = join(CACHE_DIR, 'llm-cache')\nconst LLM_CACHE_MAX_AGE = 7 * 24 * 60 * 60 * 1000\n\nfunction safeRemove(path: string): number {\n try {\n const size = statSync(path).size\n rmSync(path)\n return size\n }\n catch {\n try {\n rmSync(path)\n }\n catch {}\n return 0\n }\n}\n\nexport async function cacheCleanCommand(): Promise<void> {\n let expiredLlm = 0\n let freedBytes = 0\n\n // Clean expired LLM cache entries\n if (existsSync(LLM_CACHE_DIR)) {\n const now = Date.now()\n for (const entry of readdirSync(LLM_CACHE_DIR)) {\n const path = join(LLM_CACHE_DIR, entry)\n try {\n const { timestamp } = JSON.parse(readFileSync(path, 'utf-8'))\n if (now - timestamp > LLM_CACHE_MAX_AGE) {\n freedBytes += safeRemove(path)\n expiredLlm++\n }\n }\n catch {\n // Corrupt cache entry — remove it\n freedBytes += safeRemove(path)\n expiredLlm++\n }\n }\n }\n\n // Clear embedding cache\n const embeddingDbPath = join(CACHE_DIR, 'embeddings.db')\n let embeddingCleared = false\n if (existsSync(embeddingDbPath)) {\n const size = statSync(embeddingDbPath).size\n clearEmbeddingCache()\n freedBytes += size\n embeddingCleared = true\n }\n\n const freedKB = Math.round(freedBytes / 1024)\n if (expiredLlm > 0 || embeddingCleared) {\n const parts: string[] = []\n if (expiredLlm > 0)\n parts.push(`${expiredLlm} expired LLM cache entries`)\n if (embeddingCleared)\n parts.push('embedding cache')\n p.log.success(`Removed ${parts.join(' + ')} (${freedKB}KB freed)`)\n }\n else {\n p.log.info('Cache is clean — no expired entries')\n }\n}\n\nexport const cacheCommandDef = defineCommand({\n meta: { name: 'cache', description: 'Cache management', hidden: true },\n args: {\n clean: {\n type: 'boolean',\n description: 'Remove expired LLM cache entries',\n default: true,\n },\n },\n async run() {\n p.intro(`\\x1B[1m\\x1B[35mskilld\\x1B[0m cache clean`)\n await cacheCleanCommand()\n },\n})\n"],"mappings":";;;;;;;;;;;;AAWA,MAAM,gBAAgB,KAAK,WAAW,YAAY;AAClD,MAAM,oBAAoB,QAAc,KAAK;AAE7C,SAAS,WAAW,MAAsB;AACxC,KAAI;EACF,MAAM,OAAO,SAAS,KAAK,CAAC;AAC5B,SAAO,KAAK;AACZ,SAAO;SAEH;AACJ,MAAI;AACF,UAAO,KAAK;UAER;AACN,SAAO;;;AAIX,eAAsB,oBAAmC;CACvD,IAAI,aAAa;CACjB,IAAI,aAAa;AAGjB,KAAI,WAAW,cAAc,EAAE;EAC7B,MAAM,MAAM,KAAK,KAAK;AACtB,OAAK,MAAM,SAAS,YAAY,cAAc,EAAE;GAC9C,MAAM,OAAO,KAAK,eAAe,MAAM;AACvC,OAAI;IACF,MAAM,EAAE,cAAc,KAAK,MAAM,aAAa,MAAM,QAAQ,CAAC;AAC7D,QAAI,MAAM,YAAY,mBAAmB;AACvC,mBAAc,WAAW,KAAK;AAC9B;;WAGE;AAEJ,kBAAc,WAAW,KAAK;AAC9B;;;;CAMN,MAAM,kBAAkB,KAAK,WAAW,gBAAgB;CACxD,IAAI,mBAAmB;AACvB,KAAI,WAAW,gBAAgB,EAAE;EAC/B,MAAM,OAAO,SAAS,gBAAgB,CAAC;AACvC,uBAAqB;AACrB,gBAAc;AACd,qBAAmB;;CAGrB,MAAM,UAAU,KAAK,MAAM,aAAa,KAAK;AAC7C,KAAI,aAAa,KAAK,kBAAkB;EACtC,MAAM,QAAkB,EAAE;AAC1B,MAAI,aAAa,EACf,OAAM,KAAK,GAAG,WAAW,4BAA4B;AACvD,MAAI,iBACF,OAAM,KAAK,kBAAkB;AAC/B,IAAE,IAAI,QAAQ,WAAW,MAAM,KAAK,MAAM,CAAC,IAAI,QAAQ,WAAW;OAGlE,GAAE,IAAI,KAAK,sCAAsC;;AAIrD,MAAa,kBAAkB,cAAc;CAC3C,MAAM;EAAE,MAAM;EAAS,aAAa;EAAoB,QAAQ;EAAM;CACtE,MAAM,EACJ,OAAO;EACL,MAAM;EACN,aAAa;EACb,SAAS;EACV,EACF;CACD,MAAM,MAAM;AACV,IAAE,MAAM,2CAA2C;AACnD,QAAM,mBAAmB;;CAE5B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index2.d.mts","names":[],"sources":["../../src/sources/blog-releases.ts","../../src/sources/crawl.ts","../../src/sources/discussions.ts","../../src/sources/docs.ts","../../src/sources/entries.ts","../../src/sources/git-skills.ts","../../src/sources/types.ts","../../src/sources/github.ts","../../src/sources/issues.ts","../../src/sources/llms.ts","../../src/sources/npm.ts","../../src/sources/package-registry.ts","../../src/sources/releases.ts","../../src/sources/utils.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index2.d.mts","names":[],"sources":["../../src/sources/blog-releases.ts","../../src/sources/crawl.ts","../../src/sources/discussions.ts","../../src/sources/docs.ts","../../src/sources/entries.ts","../../src/sources/git-skills.ts","../../src/sources/types.ts","../../src/sources/github.ts","../../src/sources/issues.ts","../../src/sources/llms.ts","../../src/sources/npm.ts","../../src/sources/package-registry.ts","../../src/sources/releases.ts","../../src/sources/utils.ts"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { n as sanitizeMarkdown, t as repairMarkdown } from "./sanitize.mjs";
2
2
  import { t as yamlEscape } from "./yaml.mjs";
3
- import { i as resolveSkilldCommand, l as getFilePatterns, u as getPackageRules } from "./shared.mjs";
3
+ import { d as getPackageRules, i as resolveSkilldCommand, u as getFilePatterns } from "./shared.mjs";
4
4
  import { a as targets, t as detectInstalledAgents } from "./detect.mjs";
5
5
  import { dirname, join, relative } from "pathe";
6
6
  import { existsSync, lstatSync, mkdirSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
@@ -137,7 +137,6 @@ function processOutsideCodeBlocks(content, fn) {
137
137
  function sanitizeMarkdown(content) {
138
138
  if (!content) return content;
139
139
  let result = content.replace(ZERO_WIDTH_RE, "");
140
- result = result.replace(HTML_COMMENT_RE, "");
141
140
  result = stripTags(result, AGENT_DIRECTIVE_TAGS);
142
141
  result = processOutsideCodeBlocks(result, (text) => {
143
142
  const inlineCodeSpans = [];
@@ -146,6 +145,7 @@ function sanitizeMarkdown(content) {
146
145
  inlineCodeSpans.push(match);
147
146
  return `\x00IC${idx}\x00`;
148
147
  });
148
+ t = t.replace(HTML_COMMENT_RE, "");
149
149
  t = decodeAngleBracketEntities(t);
150
150
  t = stripTags(t, [...AGENT_DIRECTIVE_TAGS, ...DANGEROUS_HTML_TAGS]);
151
151
  t = t.replace(EXTERNAL_IMAGE_RE, "");
@@ -1 +1 @@
1
- {"version":3,"file":"sanitize.mjs","names":[],"sources":["../../src/core/sanitize.ts"],"sourcesContent":["/**\n * Markdown sanitizer for prompt injection defense.\n *\n * Strips injection vectors from untrusted markdown before it reaches\n * agent-readable files (cached references, SKILL.md, search output).\n *\n * Threat model: agent instruction injection, not browser XSS.\n * Lightweight regex-based — markdown is consumed as text by AI agents.\n */\n\n/** Zero-width and invisible formatting characters used to hide text from human review */\n// eslint-disable-next-line no-misleading-character-class -- intentionally matching individual invisible chars\nconst ZERO_WIDTH_RE = /[\\u200B\\u200C\\uFEFF\\u2060\\u200D\\u061C\\u180E\\u200E\\u200F\\u2028\\u2029]/gu\n\n/** HTML comments (single-line and multi-line), except skilld section markers */\nconst HTML_COMMENT_RE = /<!--(?!\\s*\\/?skilld:)[\\s\\S]*?-->/g\n\n/**\n * Agent directive tags — stripped globally (including inside code blocks).\n * These are never legitimate in any context; they're purely injection vectors.\n */\nconst AGENT_DIRECTIVE_TAGS = [\n 'system',\n 'instructions',\n 'override',\n 'prompt',\n 'context',\n 'role',\n 'user-prompt',\n 'assistant',\n 'tool-use',\n 'tool-result',\n 'system-prompt',\n 'human',\n 'admin',\n]\n\n/**\n * Dangerous HTML tags — stripped only outside fenced code blocks.\n * May appear legitimately in code examples (e.g. `<script setup>` in Vue docs).\n */\nconst DANGEROUS_HTML_TAGS = [\n 'script',\n 'iframe',\n 'style',\n 'meta',\n 'object',\n 'embed',\n 'form',\n]\n/**\n * Decode HTML entity-encoded angle brackets so tag stripping catches encoded variants.\n * Only decodes < and > (named, decimal, hex) — minimal to avoid false positives.\n */\nfunction decodeAngleBracketEntities(text: string): string {\n return text\n .replace(/&lt;/gi, '<')\n .replace(/&gt;/gi, '>')\n .replace(/&#0*60;/g, '<')\n .replace(/&#0*62;/g, '>')\n .replace(/&#x0*3c;/gi, '<')\n .replace(/&#x0*3e;/gi, '>')\n}\n\n/** Strip paired and standalone instances of the given tag names */\nfunction stripTags(text: string, tags: string[]): string {\n if (!tags.length)\n return text\n const tagGroup = tags.join('|')\n // First strip paired tags with content between them\n const pairedRe = new RegExp(`<(${tagGroup})(\\\\s[^>]*)?>([\\\\s\\\\S]*?)<\\\\/\\\\1>`, 'gi')\n let result = text.replace(pairedRe, '')\n // Then strip any remaining standalone open/close/self-closing tags\n const standaloneRe = new RegExp(`<\\\\/?(${tagGroup})(\\\\s[^>]*)?\\\\/?>`, 'gi')\n result = result.replace(standaloneRe, '')\n return result\n}\n\n/** External image markdown: ![alt](https://...) or ![alt](http://...) */\nconst EXTERNAL_IMAGE_RE = /!\\[([^\\]]*)\\]\\(https?:\\/\\/[^)]+\\)/gi\n\n/**\n * External link markdown: [text](https://...) or [text](http://...)\n * Preserves relative links and anchors.\n */\nconst EXTERNAL_LINK_RE = /\\[([^\\]]*)\\]\\((https?:\\/\\/[^)]+)\\)/gi\n\n/** Dangerous URI protocols in links/images — match entire [text](protocol:...) */\nconst DANGEROUS_PROTOCOL_RE = /!?\\[([^\\]]*)\\]\\(\\s*(javascript|data|vbscript|file)\\s*:[^)]*\\)/gi\nconst DANGEROUS_PROTOCOL_ENCODED_RE = /!?\\[([^\\]]*)\\]\\(\\s*(?:(?:j|%6a|%4a)(?:a|%61|%41)(?:v|%76|%56)(?:a|%61|%41)(?:s|%73|%53)(?:c|%63|%43)(?:r|%72|%52)(?:i|%69|%49)(?:p|%70|%50)(?:t|%74|%54)|(?:d|%64|%44)(?:a|%61|%41)(?:t|%74|%54)(?:a|%61|%41)|(?:v|%76|%56)(?:b|%62|%42)(?:s|%73|%53)(?:c|%63|%43)(?:r|%72|%52)(?:i|%69|%49)(?:p|%70|%50)(?:t|%74|%54))\\s*:[^)]*\\)/gi\n\n/** Directive-style lines that look like agent instructions */\nconst DIRECTIVE_LINE_RE = /^[ \\t]*(SYSTEM|OVERRIDE|INSTRUCTION|NOTE TO AI|IGNORE PREVIOUS|IGNORE ALL PREVIOUS|DISREGARD|FORGET ALL|NEW INSTRUCTIONS?|IMPORTANT SYSTEM|ADMIN OVERRIDE)\\s*[:>].*/gim\n\n/** Base64 blob: 100+ chars of pure base64 alphabet on a single line */\nconst BASE64_BLOB_RE = /^[A-Z0-9+/=]{100,}$/gim\n\n/** Unicode escape spam: 4+ consecutive \\uXXXX sequences */\nconst UNICODE_ESCAPE_SPAM_RE = /(\\\\u[\\dA-Fa-f]{4}){4,}/g\n\n/** Emoji characters — token-inefficient (2-3x cost), distort embeddings, semantically ambiguous for LLMs */\n// Also strips variation selectors (\\uFE0E text, \\uFE0F emoji) which dangle after emoji removal\nconst EMOJI_RE = /[\\p{Extended_Pictographic}\\uFE0E\\uFE0F]/gu\n\n/**\n * Process content outside of fenced code blocks.\n * Uses a line-by-line state machine to properly track fence boundaries,\n * handling nested fences, mismatched lengths, and mixed backtick/tilde fences.\n * Unclosed fences are treated as non-code for security (prevents bypass via malformed fences).\n */\nexport function processOutsideCodeBlocks(content: string, fn: (text: string) => string): string {\n const lines = content.split('\\n')\n const result: string[] = []\n let nonCodeBuffer: string[] = []\n let codeBuffer: string[] = []\n let inCodeBlock = false\n let fenceChar = ''\n let fenceLen = 0\n\n function flushNonCode() {\n if (nonCodeBuffer.length > 0) {\n result.push(fn(nonCodeBuffer.join('\\n')))\n nonCodeBuffer = []\n }\n }\n\n for (const line of lines) {\n const trimmed = line.trimStart()\n\n if (!inCodeBlock) {\n const match = trimmed.match(/^(`{3,}|~{3,})/)\n if (match) {\n flushNonCode()\n inCodeBlock = true\n fenceChar = match[1][0]!\n fenceLen = match[1].length\n codeBuffer = [line]\n continue\n }\n nonCodeBuffer.push(line)\n }\n else {\n const match = trimmed.match(/^(`{3,}|~{3,})\\s*$/)\n if (match && match[1][0] === fenceChar && match[1].length >= fenceLen) {\n // Properly closed — emit code block as-is\n result.push(codeBuffer.join('\\n'))\n result.push(line)\n codeBuffer = []\n inCodeBlock = false\n fenceChar = ''\n fenceLen = 0\n continue\n }\n codeBuffer.push(line)\n }\n }\n\n flushNonCode()\n\n // Unclosed fence: treat as non-code so sanitization still applies\n if (inCodeBlock && codeBuffer.length > 0) {\n result.push(fn(codeBuffer.join('\\n')))\n }\n\n return result.join('\\n')\n}\n\n/**\n * Sanitize markdown content to strip prompt injection vectors.\n * Applied at every markdown emission point (cache writes, SKILL.md, search output).\n */\nexport function sanitizeMarkdown(content: string): string {\n if (!content)\n return content\n\n // Layer 1: Strip zero-width characters (global, including in code blocks)\n let result = content.replace(ZERO_WIDTH_RE, '')\n\n // Layer 2: Strip HTML comments (global, including in code blocks)\n result = result.replace(HTML_COMMENT_RE, '')\n\n // Layer 3a: Strip agent directive tags globally (never legitimate, even in code blocks)\n result = stripTags(result, AGENT_DIRECTIVE_TAGS)\n\n // Layers 3b-8: Only outside fenced code blocks\n result = processOutsideCodeBlocks(result, (text) => {\n // Protect inline code spans from tag stripping (e.g. `<script setup>` in Vue docs)\n const inlineCodeSpans: string[] = []\n let t = text.replace(/(`+)([^`]+)\\1/g, (match) => {\n const idx = inlineCodeSpans.length\n inlineCodeSpans.push(match)\n return `\\x00IC${idx}\\x00`\n })\n\n // Layer 3b: Decode entities + strip remaining dangerous tags (HTML + entity-encoded agent directives)\n t = decodeAngleBracketEntities(t)\n t = stripTags(t, [...AGENT_DIRECTIVE_TAGS, ...DANGEROUS_HTML_TAGS])\n\n // Layer 4: Strip external images (exfil via query params)\n t = t.replace(EXTERNAL_IMAGE_RE, '')\n\n // Layer 5: Convert external links to plain text\n t = t.replace(EXTERNAL_LINK_RE, '$1')\n\n // Layer 6: Strip dangerous protocols (raw and URL-encoded)\n t = t.replace(DANGEROUS_PROTOCOL_RE, '')\n t = t.replace(DANGEROUS_PROTOCOL_ENCODED_RE, '')\n\n // Layer 7: Strip directive-style lines\n t = t.replace(DIRECTIVE_LINE_RE, '')\n\n // Layer 8: Strip encoded payloads\n t = t.replace(BASE64_BLOB_RE, '')\n t = t.replace(UNICODE_ESCAPE_SPAM_RE, '')\n\n // Layer 9: Strip emoji (token-inefficient, distort embeddings, semantically ambiguous)\n t = t.replace(EMOJI_RE, '')\n\n // Restore inline code spans\n t = t.replace(/\\0IC(\\d+)\\0/g, (_, idx) => inlineCodeSpans[Number(idx)] || '')\n\n return t\n })\n\n return result\n}\n\n// --- Markdown repair ---\n\n/** Heading missing space after #: `##Heading` → `## Heading` */\nconst HEADING_NO_SPACE_RE = /^(#{1,6})([^\\s#])/gm\n\n/** 3+ consecutive blank lines → 2 */\nconst EXCESSIVE_BLANKS_RE = /\\n{4,}/g\n\n/** Trailing whitespace on lines (preserve intentional double-space line breaks) */\nconst TRAILING_WHITESPACE_RE = /[ \\t]+$/gm\n\n/** Emoji at start of line inside a code block — LLM forgot to close the block */\nconst EMOJI_LINE_START_RE = /^\\p{Extended_Pictographic}/u\n\n/**\n * Close unclosed fenced code blocks.\n * Walks line-by-line tracking open/close state.\n */\nfunction closeUnclosedCodeBlocks(content: string): string {\n const lines = content.split('\\n')\n const result: string[] = []\n let inCodeBlock = false\n let fence = ''\n\n for (const line of lines) {\n const trimmed = line.trimStart()\n if (!inCodeBlock) {\n const match = trimmed.match(/^(`{3,}|~{3,})/)\n if (match) {\n inCodeBlock = true\n fence = match[1][0]!.repeat(match[1].length)\n }\n }\n else {\n // Check for closing fence (same char, at least same length)\n const match = trimmed.match(/^(`{3,}|~{3,})\\s*$/)\n if (match && match[1][0] === fence[0] && match[1].length >= fence.length) {\n inCodeBlock = false\n fence = ''\n }\n else {\n // New fence opener inside unclosed block (same char, same length, with lang tag)\n // LLMs commonly forget to close a code block before starting a new one\n const openMatch = trimmed.match(/^(`{3,}|~{3,})\\S/)\n if (openMatch && openMatch[1][0] === fence[0] && openMatch[1].length === fence.length) {\n result.push(fence)\n // fence char/length stays the same since both match\n }\n // Emoji at line start → LLM forgot to close code block before markdown content\n else if (EMOJI_LINE_START_RE.test(trimmed)) {\n result.push(fence)\n inCodeBlock = false\n fence = ''\n }\n }\n }\n result.push(line)\n }\n\n // If still inside a code block, close it\n if (inCodeBlock) {\n // Ensure trailing newline before closing fence\n if (result.length > 0 && result.at(-1) !== '')\n result.push('')\n result.push(fence)\n }\n\n return result.join('\\n')\n}\n\n/**\n * Remove empty code blocks and deduplicate consecutive identical code blocks.\n * Empty blocks arise when emoji/fence recovery leaves orphaned fences.\n * Duplicate blocks arise when LLMs repeat the same code example.\n */\nfunction cleanupCodeBlocks(content: string): string {\n const lines = content.split('\\n')\n const toRemove = new Set<number>()\n let prevCodeContent: string | undefined\n let i = 0\n\n while (i < lines.length) {\n const trimmed = lines[i]!.trimStart()\n const fm = trimmed.match(/^(`{3,}|~{3,})/)\n if (!fm) {\n // Non-blank text between code blocks resets dedup tracking\n if (trimmed)\n prevCodeContent = undefined\n i++\n continue\n }\n\n const fChar = fm[1][0]!\n const fLen = fm[1].length\n const openIdx = i\n i++\n\n let closeIdx = -1\n while (i < lines.length) {\n const ct = lines[i]!.trimStart()\n const cm = ct.match(/^(`{3,}|~{3,})\\s*$/)\n if (cm && cm[1][0] === fChar && cm[1].length >= fLen) {\n closeIdx = i\n i++\n break\n }\n i++\n }\n\n if (closeIdx === -1)\n continue\n\n const inner = lines.slice(openIdx + 1, closeIdx).join('\\n').trim()\n\n if (!inner) {\n for (let j = openIdx; j <= closeIdx; j++) toRemove.add(j)\n }\n else if (inner === prevCodeContent) {\n for (let j = openIdx; j <= closeIdx; j++) toRemove.add(j)\n }\n else {\n prevCodeContent = inner\n }\n }\n\n if (!toRemove.size)\n return content\n return lines.filter((_, idx) => !toRemove.has(idx)).join('\\n')\n}\n\n/**\n * Close unclosed inline code spans.\n * Scans each line for unmatched backtick(s) and appends closing backtick(s).\n * Tracks fenced code blocks internally to handle any fence length.\n */\nfunction closeUnclosedInlineCode(content: string): string {\n const lines = content.split('\\n')\n let inFence = false\n let fenceChar = ''\n let fenceLen = 0\n\n return lines.map((line) => {\n const trimmed = line.trimStart()\n if (!inFence) {\n const m = trimmed.match(/^(`{3,}|~{3,})/)\n if (m) {\n inFence = true\n fenceChar = m[1][0]!\n fenceLen = m[1].length\n return line\n }\n }\n else {\n const m = trimmed.match(/^(`{3,}|~{3,})\\s*$/)\n if (m && m[1][0] === fenceChar && m[1].length >= fenceLen) {\n inFence = false\n }\n return line\n }\n\n // Outside fenced code blocks — fix unclosed inline backticks\n let i = 0\n while (i < line.length) {\n if (line[i] === '`') {\n const seqStart = i\n while (i < line.length && line[i] === '`') i++\n const seqLen = i - seqStart\n let found = false\n let j = i\n while (j < line.length) {\n if (line[j] === '`') {\n const closeStart = j\n while (j < line.length && line[j] === '`') j++\n if (j - closeStart === seqLen) {\n found = true\n i = j\n break\n }\n }\n else {\n j++\n }\n }\n if (!found) {\n line = `${line}${'`'.repeat(seqLen)}`\n i = line.length\n }\n }\n else {\n i++\n }\n }\n return line\n }).join('\\n')\n}\n\n/**\n * Repair broken markdown syntax.\n * Fixes common issues in fetched documentation:\n * - Unclosed fenced code blocks\n * - Unclosed inline code spans\n * - Missing space after heading # markers\n * - Excessive consecutive blank lines\n * - Trailing whitespace\n */\nexport function repairMarkdown(content: string): string {\n if (!content)\n return content\n\n let result = content\n\n // Fix unclosed fenced code blocks (must run before other line-level fixes)\n result = closeUnclosedCodeBlocks(result)\n\n // Remove empty and duplicate code blocks (artifacts from fence recovery)\n result = cleanupCodeBlocks(result)\n\n // Fix unclosed inline code spans\n result = closeUnclosedInlineCode(result)\n\n // Fix heading spacing (only outside code blocks)\n result = processOutsideCodeBlocks(result, text =>\n text.replace(HEADING_NO_SPACE_RE, '$1 $2'))\n\n // Normalize excessive blank lines\n result = result.replace(EXCESSIVE_BLANKS_RE, '\\n\\n\\n')\n\n // Strip trailing whitespace\n result = result.replace(TRAILING_WHITESPACE_RE, '')\n\n return result\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sanitize.mjs","names":[],"sources":["../../src/core/sanitize.ts"],"sourcesContent":["/**\n * Markdown sanitizer for prompt injection defense.\n *\n * Strips injection vectors from untrusted markdown before it reaches\n * agent-readable files (cached references, SKILL.md, search output).\n *\n * Threat model: agent instruction injection, not browser XSS.\n * Lightweight regex-based — markdown is consumed as text by AI agents.\n */\n\n/** Zero-width and invisible formatting characters used to hide text from human review */\n// eslint-disable-next-line no-misleading-character-class -- intentionally matching individual invisible chars\nconst ZERO_WIDTH_RE = /[\\u200B\\u200C\\uFEFF\\u2060\\u200D\\u061C\\u180E\\u200E\\u200F\\u2028\\u2029]/gu\n\n/** HTML comments (single-line and multi-line), except skilld section markers */\nconst HTML_COMMENT_RE = /<!--(?!\\s*\\/?skilld:)[\\s\\S]*?-->/g\n\n/**\n * Agent directive tags — stripped globally (including inside code blocks).\n * These are never legitimate in any context; they're purely injection vectors.\n */\nconst AGENT_DIRECTIVE_TAGS = [\n 'system',\n 'instructions',\n 'override',\n 'prompt',\n 'context',\n 'role',\n 'user-prompt',\n 'assistant',\n 'tool-use',\n 'tool-result',\n 'system-prompt',\n 'human',\n 'admin',\n]\n\n/**\n * Dangerous HTML tags — stripped only outside fenced code blocks.\n * May appear legitimately in code examples (e.g. `<script setup>` in Vue docs).\n */\nconst DANGEROUS_HTML_TAGS = [\n 'script',\n 'iframe',\n 'style',\n 'meta',\n 'object',\n 'embed',\n 'form',\n]\n/**\n * Decode HTML entity-encoded angle brackets so tag stripping catches encoded variants.\n * Only decodes < and > (named, decimal, hex) — minimal to avoid false positives.\n */\nfunction decodeAngleBracketEntities(text: string): string {\n return text\n .replace(/&lt;/gi, '<')\n .replace(/&gt;/gi, '>')\n .replace(/&#0*60;/g, '<')\n .replace(/&#0*62;/g, '>')\n .replace(/&#x0*3c;/gi, '<')\n .replace(/&#x0*3e;/gi, '>')\n}\n\n/** Strip paired and standalone instances of the given tag names */\nfunction stripTags(text: string, tags: string[]): string {\n if (!tags.length)\n return text\n const tagGroup = tags.join('|')\n // First strip paired tags with content between them\n const pairedRe = new RegExp(`<(${tagGroup})(\\\\s[^>]*)?>([\\\\s\\\\S]*?)<\\\\/\\\\1>`, 'gi')\n let result = text.replace(pairedRe, '')\n // Then strip any remaining standalone open/close/self-closing tags\n const standaloneRe = new RegExp(`<\\\\/?(${tagGroup})(\\\\s[^>]*)?\\\\/?>`, 'gi')\n result = result.replace(standaloneRe, '')\n return result\n}\n\n/** External image markdown: ![alt](https://...) or ![alt](http://...) */\nconst EXTERNAL_IMAGE_RE = /!\\[([^\\]]*)\\]\\(https?:\\/\\/[^)]+\\)/gi\n\n/**\n * External link markdown: [text](https://...) or [text](http://...)\n * Preserves relative links and anchors.\n */\nconst EXTERNAL_LINK_RE = /\\[([^\\]]*)\\]\\((https?:\\/\\/[^)]+)\\)/gi\n\n/** Dangerous URI protocols in links/images — match entire [text](protocol:...) */\nconst DANGEROUS_PROTOCOL_RE = /!?\\[([^\\]]*)\\]\\(\\s*(javascript|data|vbscript|file)\\s*:[^)]*\\)/gi\nconst DANGEROUS_PROTOCOL_ENCODED_RE = /!?\\[([^\\]]*)\\]\\(\\s*(?:(?:j|%6a|%4a)(?:a|%61|%41)(?:v|%76|%56)(?:a|%61|%41)(?:s|%73|%53)(?:c|%63|%43)(?:r|%72|%52)(?:i|%69|%49)(?:p|%70|%50)(?:t|%74|%54)|(?:d|%64|%44)(?:a|%61|%41)(?:t|%74|%54)(?:a|%61|%41)|(?:v|%76|%56)(?:b|%62|%42)(?:s|%73|%53)(?:c|%63|%43)(?:r|%72|%52)(?:i|%69|%49)(?:p|%70|%50)(?:t|%74|%54))\\s*:[^)]*\\)/gi\n\n/** Directive-style lines that look like agent instructions */\nconst DIRECTIVE_LINE_RE = /^[ \\t]*(SYSTEM|OVERRIDE|INSTRUCTION|NOTE TO AI|IGNORE PREVIOUS|IGNORE ALL PREVIOUS|DISREGARD|FORGET ALL|NEW INSTRUCTIONS?|IMPORTANT SYSTEM|ADMIN OVERRIDE)\\s*[:>].*/gim\n\n/** Base64 blob: 100+ chars of pure base64 alphabet on a single line */\nconst BASE64_BLOB_RE = /^[A-Z0-9+/=]{100,}$/gim\n\n/** Unicode escape spam: 4+ consecutive \\uXXXX sequences */\nconst UNICODE_ESCAPE_SPAM_RE = /(\\\\u[\\dA-Fa-f]{4}){4,}/g\n\n/** Emoji characters — token-inefficient (2-3x cost), distort embeddings, semantically ambiguous for LLMs */\n// Also strips variation selectors (\\uFE0E text, \\uFE0F emoji) which dangle after emoji removal\nconst EMOJI_RE = /[\\p{Extended_Pictographic}\\uFE0E\\uFE0F]/gu\n\n/**\n * Process content outside of fenced code blocks.\n * Uses a line-by-line state machine to properly track fence boundaries,\n * handling nested fences, mismatched lengths, and mixed backtick/tilde fences.\n * Unclosed fences are treated as non-code for security (prevents bypass via malformed fences).\n */\nexport function processOutsideCodeBlocks(content: string, fn: (text: string) => string): string {\n const lines = content.split('\\n')\n const result: string[] = []\n let nonCodeBuffer: string[] = []\n let codeBuffer: string[] = []\n let inCodeBlock = false\n let fenceChar = ''\n let fenceLen = 0\n\n function flushNonCode() {\n if (nonCodeBuffer.length > 0) {\n result.push(fn(nonCodeBuffer.join('\\n')))\n nonCodeBuffer = []\n }\n }\n\n for (const line of lines) {\n const trimmed = line.trimStart()\n\n if (!inCodeBlock) {\n const match = trimmed.match(/^(`{3,}|~{3,})/)\n if (match) {\n flushNonCode()\n inCodeBlock = true\n fenceChar = match[1][0]!\n fenceLen = match[1].length\n codeBuffer = [line]\n continue\n }\n nonCodeBuffer.push(line)\n }\n else {\n const match = trimmed.match(/^(`{3,}|~{3,})\\s*$/)\n if (match && match[1][0] === fenceChar && match[1].length >= fenceLen) {\n // Properly closed — emit code block as-is\n result.push(codeBuffer.join('\\n'))\n result.push(line)\n codeBuffer = []\n inCodeBlock = false\n fenceChar = ''\n fenceLen = 0\n continue\n }\n codeBuffer.push(line)\n }\n }\n\n flushNonCode()\n\n // Unclosed fence: treat as non-code so sanitization still applies\n if (inCodeBlock && codeBuffer.length > 0) {\n result.push(fn(codeBuffer.join('\\n')))\n }\n\n return result.join('\\n')\n}\n\n/**\n * Sanitize markdown content to strip prompt injection vectors.\n * Applied at every markdown emission point (cache writes, SKILL.md, search output).\n */\nexport function sanitizeMarkdown(content: string): string {\n if (!content)\n return content\n\n // Layer 1: Strip zero-width characters (global, including in code blocks)\n let result = content.replace(ZERO_WIDTH_RE, '')\n\n // Layer 2: Strip agent directive tags globally (never legitimate, even in code blocks)\n result = stripTags(result, AGENT_DIRECTIVE_TAGS)\n\n // Layers 3-9: Only outside fenced code blocks\n result = processOutsideCodeBlocks(result, (text) => {\n // Protect inline code spans from tag stripping (e.g. `<script setup>` in Vue docs)\n const inlineCodeSpans: string[] = []\n let t = text.replace(/(`+)([^`]+)\\1/g, (match) => {\n const idx = inlineCodeSpans.length\n inlineCodeSpans.push(match)\n return `\\x00IC${idx}\\x00`\n })\n\n // Layer 3: Strip HTML comments (outside code blocks where they're hidden from review;\n // inside code blocks they render as visible text and are legitimate documentation)\n t = t.replace(HTML_COMMENT_RE, '')\n\n // Layer 4: Decode entities + strip remaining dangerous tags (HTML + entity-encoded agent directives)\n t = decodeAngleBracketEntities(t)\n t = stripTags(t, [...AGENT_DIRECTIVE_TAGS, ...DANGEROUS_HTML_TAGS])\n\n // Layer 5: Strip external images (exfil via query params)\n t = t.replace(EXTERNAL_IMAGE_RE, '')\n\n // Layer 6: Convert external links to plain text\n t = t.replace(EXTERNAL_LINK_RE, '$1')\n\n // Layer 7: Strip dangerous protocols (raw and URL-encoded)\n t = t.replace(DANGEROUS_PROTOCOL_RE, '')\n t = t.replace(DANGEROUS_PROTOCOL_ENCODED_RE, '')\n\n // Layer 8: Strip directive-style lines\n t = t.replace(DIRECTIVE_LINE_RE, '')\n\n // Layer 9: Strip encoded payloads\n t = t.replace(BASE64_BLOB_RE, '')\n t = t.replace(UNICODE_ESCAPE_SPAM_RE, '')\n\n // Layer 10: Strip emoji (token-inefficient, distort embeddings, semantically ambiguous)\n t = t.replace(EMOJI_RE, '')\n\n // Restore inline code spans\n t = t.replace(/\\0IC(\\d+)\\0/g, (_, idx) => inlineCodeSpans[Number(idx)] || '')\n\n return t\n })\n\n return result\n}\n\n// --- Markdown repair ---\n\n/** Heading missing space after #: `##Heading` → `## Heading` */\nconst HEADING_NO_SPACE_RE = /^(#{1,6})([^\\s#])/gm\n\n/** 3+ consecutive blank lines → 2 */\nconst EXCESSIVE_BLANKS_RE = /\\n{4,}/g\n\n/** Trailing whitespace on lines (preserve intentional double-space line breaks) */\nconst TRAILING_WHITESPACE_RE = /[ \\t]+$/gm\n\n/** Emoji at start of line inside a code block — LLM forgot to close the block */\nconst EMOJI_LINE_START_RE = /^\\p{Extended_Pictographic}/u\n\n/**\n * Close unclosed fenced code blocks.\n * Walks line-by-line tracking open/close state.\n */\nfunction closeUnclosedCodeBlocks(content: string): string {\n const lines = content.split('\\n')\n const result: string[] = []\n let inCodeBlock = false\n let fence = ''\n\n for (const line of lines) {\n const trimmed = line.trimStart()\n if (!inCodeBlock) {\n const match = trimmed.match(/^(`{3,}|~{3,})/)\n if (match) {\n inCodeBlock = true\n fence = match[1][0]!.repeat(match[1].length)\n }\n }\n else {\n // Check for closing fence (same char, at least same length)\n const match = trimmed.match(/^(`{3,}|~{3,})\\s*$/)\n if (match && match[1][0] === fence[0] && match[1].length >= fence.length) {\n inCodeBlock = false\n fence = ''\n }\n else {\n // New fence opener inside unclosed block (same char, same length, with lang tag)\n // LLMs commonly forget to close a code block before starting a new one\n const openMatch = trimmed.match(/^(`{3,}|~{3,})\\S/)\n if (openMatch && openMatch[1][0] === fence[0] && openMatch[1].length === fence.length) {\n result.push(fence)\n // fence char/length stays the same since both match\n }\n // Emoji at line start → LLM forgot to close code block before markdown content\n else if (EMOJI_LINE_START_RE.test(trimmed)) {\n result.push(fence)\n inCodeBlock = false\n fence = ''\n }\n }\n }\n result.push(line)\n }\n\n // If still inside a code block, close it\n if (inCodeBlock) {\n // Ensure trailing newline before closing fence\n if (result.length > 0 && result.at(-1) !== '')\n result.push('')\n result.push(fence)\n }\n\n return result.join('\\n')\n}\n\n/**\n * Remove empty code blocks and deduplicate consecutive identical code blocks.\n * Empty blocks arise when emoji/fence recovery leaves orphaned fences.\n * Duplicate blocks arise when LLMs repeat the same code example.\n */\nfunction cleanupCodeBlocks(content: string): string {\n const lines = content.split('\\n')\n const toRemove = new Set<number>()\n let prevCodeContent: string | undefined\n let i = 0\n\n while (i < lines.length) {\n const trimmed = lines[i]!.trimStart()\n const fm = trimmed.match(/^(`{3,}|~{3,})/)\n if (!fm) {\n // Non-blank text between code blocks resets dedup tracking\n if (trimmed)\n prevCodeContent = undefined\n i++\n continue\n }\n\n const fChar = fm[1][0]!\n const fLen = fm[1].length\n const openIdx = i\n i++\n\n let closeIdx = -1\n while (i < lines.length) {\n const ct = lines[i]!.trimStart()\n const cm = ct.match(/^(`{3,}|~{3,})\\s*$/)\n if (cm && cm[1][0] === fChar && cm[1].length >= fLen) {\n closeIdx = i\n i++\n break\n }\n i++\n }\n\n if (closeIdx === -1)\n continue\n\n const inner = lines.slice(openIdx + 1, closeIdx).join('\\n').trim()\n\n if (!inner) {\n for (let j = openIdx; j <= closeIdx; j++) toRemove.add(j)\n }\n else if (inner === prevCodeContent) {\n for (let j = openIdx; j <= closeIdx; j++) toRemove.add(j)\n }\n else {\n prevCodeContent = inner\n }\n }\n\n if (!toRemove.size)\n return content\n return lines.filter((_, idx) => !toRemove.has(idx)).join('\\n')\n}\n\n/**\n * Close unclosed inline code spans.\n * Scans each line for unmatched backtick(s) and appends closing backtick(s).\n * Tracks fenced code blocks internally to handle any fence length.\n */\nfunction closeUnclosedInlineCode(content: string): string {\n const lines = content.split('\\n')\n let inFence = false\n let fenceChar = ''\n let fenceLen = 0\n\n return lines.map((line) => {\n const trimmed = line.trimStart()\n if (!inFence) {\n const m = trimmed.match(/^(`{3,}|~{3,})/)\n if (m) {\n inFence = true\n fenceChar = m[1][0]!\n fenceLen = m[1].length\n return line\n }\n }\n else {\n const m = trimmed.match(/^(`{3,}|~{3,})\\s*$/)\n if (m && m[1][0] === fenceChar && m[1].length >= fenceLen) {\n inFence = false\n }\n return line\n }\n\n // Outside fenced code blocks — fix unclosed inline backticks\n let i = 0\n while (i < line.length) {\n if (line[i] === '`') {\n const seqStart = i\n while (i < line.length && line[i] === '`') i++\n const seqLen = i - seqStart\n let found = false\n let j = i\n while (j < line.length) {\n if (line[j] === '`') {\n const closeStart = j\n while (j < line.length && line[j] === '`') j++\n if (j - closeStart === seqLen) {\n found = true\n i = j\n break\n }\n }\n else {\n j++\n }\n }\n if (!found) {\n line = `${line}${'`'.repeat(seqLen)}`\n i = line.length\n }\n }\n else {\n i++\n }\n }\n return line\n }).join('\\n')\n}\n\n/**\n * Repair broken markdown syntax.\n * Fixes common issues in fetched documentation:\n * - Unclosed fenced code blocks\n * - Unclosed inline code spans\n * - Missing space after heading # markers\n * - Excessive consecutive blank lines\n * - Trailing whitespace\n */\nexport function repairMarkdown(content: string): string {\n if (!content)\n return content\n\n let result = content\n\n // Fix unclosed fenced code blocks (must run before other line-level fixes)\n result = closeUnclosedCodeBlocks(result)\n\n // Remove empty and duplicate code blocks (artifacts from fence recovery)\n result = cleanupCodeBlocks(result)\n\n // Fix unclosed inline code spans\n result = closeUnclosedInlineCode(result)\n\n // Fix heading spacing (only outside code blocks)\n result = processOutsideCodeBlocks(result, text =>\n text.replace(HEADING_NO_SPACE_RE, '$1 $2'))\n\n // Normalize excessive blank lines\n result = result.replace(EXCESSIVE_BLANKS_RE, '\\n\\n\\n')\n\n // Strip trailing whitespace\n result = result.replace(TRAILING_WHITESPACE_RE, '')\n\n return result\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { join } from "pathe";
2
2
  import { existsSync } from "node:fs";
3
3
  import { execSync } from "node:child_process";
4
- import { gt } from "semver";
4
+ import { gt, valid } from "semver";
5
5
  import { isWindows } from "std-env";
6
6
  //#region src/sources/package-registry.ts
7
7
  const REPO_REGISTRY = {
@@ -477,6 +477,10 @@ function mapInsert(map, key, create) {
477
477
  }
478
478
  return val;
479
479
  }
480
+ /** Returns the cleaned version if valid semver, null otherwise. */
481
+ function semverValid(v) {
482
+ return valid(v, true);
483
+ }
480
484
  /** Compare two semver strings: returns true if a > b. Handles prereleases. */
481
485
  function semverGt(a, b) {
482
486
  return gt(a, b, true);
@@ -500,6 +504,6 @@ function getSharedSkillsDir(cwd = process.cwd()) {
500
504
  return existsSync(dir) ? dir : null;
501
505
  }
502
506
  //#endregion
503
- export { semverGt as a, getDocOverride as c, getPrereleaseChangelogRef as d, getRelatedPackages as f, resolveSkilldCommand as i, getFilePatterns as l, getRepoKeyForPackage as m, getSharedSkillsDir as n, getBlogPreset as o, getRepoEntry as p, mapInsert as r, getCrawlUrl as s, SHARED_SKILLS_DIR as t, getPackageRules as u };
507
+ export { semverGt as a, getCrawlUrl as c, getPackageRules as d, getPrereleaseChangelogRef as f, getRepoKeyForPackage as h, resolveSkilldCommand as i, getDocOverride as l, getRepoEntry as m, getSharedSkillsDir as n, semverValid as o, getRelatedPackages as p, mapInsert as r, getBlogPreset as s, SHARED_SKILLS_DIR as t, getFilePatterns as u };
504
508
 
505
509
  //# sourceMappingURL=shared.mjs.map