skilld 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +54 -4
  2. package/dist/_chunks/agent.mjs +2 -1
  3. package/dist/_chunks/agent.mjs.map +1 -1
  4. package/dist/_chunks/assemble.mjs +1 -0
  5. package/dist/_chunks/assemble.mjs.map +1 -1
  6. package/dist/_chunks/author.mjs +478 -0
  7. package/dist/_chunks/author.mjs.map +1 -0
  8. package/dist/_chunks/cli-helpers.mjs +133 -2
  9. package/dist/_chunks/cli-helpers.mjs.map +1 -1
  10. package/dist/_chunks/detect.mjs.map +1 -1
  11. package/dist/_chunks/index2.d.mts +2 -0
  12. package/dist/_chunks/index2.d.mts.map +1 -1
  13. package/dist/_chunks/install.mjs +7 -17
  14. package/dist/_chunks/install.mjs.map +1 -1
  15. package/dist/_chunks/list.mjs +43 -4
  16. package/dist/_chunks/list.mjs.map +1 -1
  17. package/dist/_chunks/lockfile.mjs +140 -0
  18. package/dist/_chunks/lockfile.mjs.map +1 -0
  19. package/dist/_chunks/prepare.mjs +94 -0
  20. package/dist/_chunks/prepare.mjs.map +1 -0
  21. package/dist/_chunks/prompts.mjs +32 -43
  22. package/dist/_chunks/prompts.mjs.map +1 -1
  23. package/dist/_chunks/sanitize.mjs.map +1 -1
  24. package/dist/_chunks/search-interactive.mjs +1 -0
  25. package/dist/_chunks/search-interactive.mjs.map +1 -1
  26. package/dist/_chunks/search.mjs +146 -9
  27. package/dist/_chunks/search.mjs.map +1 -1
  28. package/dist/_chunks/setup.mjs +1 -1
  29. package/dist/_chunks/skills.mjs +28 -142
  30. package/dist/_chunks/skills.mjs.map +1 -1
  31. package/dist/_chunks/sources.mjs +4 -2
  32. package/dist/_chunks/sources.mjs.map +1 -1
  33. package/dist/_chunks/sync-shared.mjs +14 -0
  34. package/dist/_chunks/sync-shared2.mjs +1054 -0
  35. package/dist/_chunks/sync-shared2.mjs.map +1 -0
  36. package/dist/_chunks/sync.mjs +72 -1065
  37. package/dist/_chunks/sync.mjs.map +1 -1
  38. package/dist/_chunks/uninstall.mjs +5 -3
  39. package/dist/_chunks/uninstall.mjs.map +1 -1
  40. package/dist/agent/index.d.mts +4 -2
  41. package/dist/agent/index.d.mts.map +1 -1
  42. package/dist/cli.mjs +76 -10
  43. package/dist/cli.mjs.map +1 -1
  44. package/package.json +11 -10
package/README.md CHANGED
@@ -99,16 +99,18 @@ pnpm add -D skilld
99
99
 
100
100
  ### Automatic Updates
101
101
 
102
- Add to `package.json` to keep skills fresh on install:
102
+ Add to `package.json` to restore references and sync shipped skills on install:
103
103
 
104
104
  ```json
105
105
  {
106
106
  "scripts": {
107
- "prepare": "skilld update -b"
107
+ "prepare": "skilld prepare"
108
108
  }
109
109
  }
110
110
  ```
111
111
 
112
+ This restores symlinks, auto-installs shipped skills from your deps, and notifies you when packages have new features or breaking changes. Run `skilld update` to regenerate LLM enhancements.
113
+
112
114
  ## FAQ
113
115
 
114
116
  ### Why don't the skills run?
@@ -133,7 +135,7 @@ However, always be cautious when using skills from untrusted sources.
133
135
 
134
136
  ### Do skills update when my deps update?
135
137
 
136
- Yes. Run `skilld update` to regenerate outdated skills, or add `skilld update -b` to your prepare script and they regenerate in the background whenever you install packages.
138
+ Yes. Add `skilld prepare` to your prepare script. It restores references, auto-installs shipped skills, and tells you when packages are outdated. Run `skilld update` to regenerate LLM enhancements.
137
139
 
138
140
  ## CLI Usage
139
141
 
@@ -150,6 +152,8 @@ skilld update tailwindcss
150
152
 
151
153
  # Search docs across installed skills
152
154
  skilld search "useFetch options" -p nuxt
155
+ skilld search "error" -p nuxt --filter '{"type":"issue"}'
156
+ skilld search --guide -p nuxt
153
157
 
154
158
  # Target a specific agent
155
159
  skilld add react --agent cursor
@@ -178,7 +182,7 @@ skilld config
178
182
  | `skilld` | Interactive wizard (first run) or status menu (existing skills) |
179
183
  | `skilld add <pkg...>` | Add skills for package(s), space or comma-separated |
180
184
  | `skilld update [pkg]` | Update outdated skills (all or specific) |
181
- | `skilld search <query>` | Search indexed docs (`-p` to filter by package) |
185
+ | `skilld search [query]` | Search indexed docs (`-p` package, `--filter` JSON, `--limit`, `--guide`) |
182
186
  | `skilld list` | List installed skills (`--json` for machine-readable output) |
183
187
  | `skilld info` | Show skill info and config |
184
188
  | `skilld config` | Configure agent, model, preferences |
@@ -236,6 +240,52 @@ Share via `skilld add owner/repo` - consumers get fully functional skills with n
236
240
  | `--from` | | | Collect releases/issues/discussions from this date (YYYY-MM-DD, eject only) |
237
241
  | `--debug` | | `false` | Save raw LLM output to logs/ for each section |
238
242
 
243
+ ## For Maintainers
244
+
245
+ Ship skills with your npm package so consumers get them automatically. No LLM needed on their end.
246
+
247
+ ### Generate a skill
248
+
249
+ From your package root (or monorepo root):
250
+
251
+ ```bash
252
+ npx skilld author
253
+ ```
254
+
255
+ In a monorepo, skilld auto-detects workspaces and prompts which packages to generate for. Docs are resolved from: package `docs/`, monorepo `docs/content/`, `llms.txt`, or `README.md`.
256
+
257
+ This creates a `skills/<your-package>/` directory with a `SKILL.md` and ejected reference files. It also adds `"skills"` to your `package.json` `files` array.
258
+
259
+ ### How consumers get it
260
+
261
+ Once published, consumers run:
262
+
263
+ ```bash
264
+ npx skilld prepare
265
+ ```
266
+
267
+ Or add it to their `package.json` so it runs on every install:
268
+
269
+ ```json
270
+ {
271
+ "scripts": {
272
+ "prepare": "skilld prepare"
273
+ }
274
+ }
275
+ ```
276
+
277
+ `skilld prepare` auto-detects shipped skills in `node_modules` and symlinks them into the agent's skill directory. Compatible with [skills-npm](https://github.com/antfu/skills-npm).
278
+
279
+ ### Options
280
+
281
+ | Flag | Alias | Default | Description |
282
+ |:----------|:-----:|:-------:|:------------|
283
+ | `--model` | `-m` | | LLM model for enhancement |
284
+ | `--out` | `-o` | | Output directory (single package only) |
285
+ | `--force` | `-f` | `false` | Clear cache and regenerate |
286
+ | `--yes` | `-y` | `false` | Skip prompts, use defaults |
287
+ | `--debug` | | `false` | Save raw LLM output to logs/ |
288
+
239
289
  ## The Landscape
240
290
 
241
291
  Several approaches exist for steering agent knowledge. Each fills a different niche:
@@ -1005,7 +1005,7 @@ function optimizeSection(opts) {
1005
1005
  });
1006
1006
  }
1007
1007
  async function optimizeDocs(opts) {
1008
- const { packageName, skillDir, model = "sonnet", version, hasGithub, hasReleases, hasChangelog, docFiles, docsType, hasShippedDocs, onProgress, timeout = 18e4, debug, noCache, sections, customPrompt, features, pkgFiles } = opts;
1008
+ const { packageName, skillDir, model = "sonnet", version, hasGithub, hasReleases, hasChangelog, docFiles, docsType, hasShippedDocs, onProgress, timeout = 18e4, debug, noCache, sections, customPrompt, features, pkgFiles, overheadLines } = opts;
1009
1009
  const sectionPrompts = buildAllSectionPrompts({
1010
1010
  packageName,
1011
1011
  skillDir,
@@ -1020,6 +1020,7 @@ async function optimizeDocs(opts) {
1020
1020
  customPrompt,
1021
1021
  features,
1022
1022
  pkgFiles,
1023
+ overheadLines,
1023
1024
  sections: sections ?? ["api-changes", "best-practices"]
1024
1025
  });
1025
1026
  if (sectionPrompts.size === 0) 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/pi-ai.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 * pi-ai adapter — direct LLM API calls via @mariozechner/pi-ai\n *\n * Optional alternative to CLI spawning. Supports:\n * - Env-var API keys (ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY, etc.)\n * - OAuth login to piggyback on existing subscriptions (Claude Pro, ChatGPT Plus, Copilot)\n *\n * Models are enumerated dynamically from pi-ai's registry — no hardcoded list.\n * Reference content is inlined into the prompt via portabilizePrompt().\n */\n\nimport type { SkillSection } from '../prompts/index.ts'\nimport type { StreamProgress } from './types.ts'\nimport { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { getEnvApiKey, getModel, getModels, getProviders, streamSimple } from '@mariozechner/pi-ai'\nimport { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from '@mariozechner/pi-ai/oauth'\nimport { join } from 'pathe'\nimport { sanitizeMarkdown } from '../../core/sanitize.ts'\nimport { portabilizePrompt } from '../prompts/index.ts'\n\nexport function isPiAiModel(model: string): boolean {\n return model.startsWith('pi:')\n}\n\n/** Parse a pi:provider/model-id string → { provider, modelId } */\nexport function parsePiAiModelId(model: string): { provider: string, modelId: string } | null {\n if (!model.startsWith('pi:'))\n return null\n const rest = model.slice(3)\n const slashIdx = rest.indexOf('/')\n if (slashIdx === -1)\n return null\n return { provider: rest.slice(0, slashIdx), modelId: rest.slice(slashIdx + 1) }\n}\n\n// ── OAuth credentials ────────────────────────────────────────────────\n\n/** pi coding agent stores auth here; env var can override */\nconst PI_AGENT_AUTH_PATH = join(\n process.env.PI_CODING_AGENT_DIR || join(homedir(), '.pi', 'agent'),\n 'auth.json',\n)\n/** skilld's own auth file — used when user logs in via skilld */\nconst SKILLD_AUTH_PATH = join(homedir(), '.skilld', 'pi-ai-auth.json')\n\ninterface OAuthCredentials {\n type: 'oauth'\n refresh: string\n access: string\n expires: number\n [key: string]: unknown\n}\n\nfunction readAuthFile(path: string): Record<string, OAuthCredentials> {\n if (!existsSync(path))\n return {}\n try {\n return JSON.parse(readFileSync(path, 'utf-8'))\n }\n catch { return {} }\n}\n\n/** Load auth from pi coding agent first (~/.pi/agent/auth.json), then skilld's own */\nfunction loadAuth(): Record<string, OAuthCredentials> {\n const piAuth = readAuthFile(PI_AGENT_AUTH_PATH)\n const skilldAuth = readAuthFile(SKILLD_AUTH_PATH)\n // pi agent credentials take precedence (user's primary auth)\n return { ...skilldAuth, ...piAuth }\n}\n\n/** Save auth to skilld's own file — never writes to pi agent's auth */\nfunction saveAuth(auth: Record<string, OAuthCredentials>): void {\n mkdirSync(join(homedir(), '.skilld'), { recursive: true, mode: 0o700 })\n writeFileSync(SKILLD_AUTH_PATH, JSON.stringify(auth, null, 2), { mode: 0o600 })\n}\n\n/**\n * Overrides for model-provider → OAuth-provider mapping.\n * Most providers share the same ID in both systems (auto-matched).\n * Only list exceptions where the IDs diverge.\n */\nconst OAUTH_PROVIDER_OVERRIDES: Record<string, string> = {\n google: 'google-gemini-cli',\n openai: 'openai-codex',\n}\n\n/** Resolve model provider ID → OAuth provider ID */\nfunction resolveOAuthProviderId(modelProvider: string): string | null {\n if (OAUTH_PROVIDER_OVERRIDES[modelProvider])\n return OAUTH_PROVIDER_OVERRIDES[modelProvider]\n // Auto-match: if the model provider ID is also an OAuth provider, use it directly\n const oauthIds = new Set(getOAuthProviders().map((p: any) => p.id))\n if (oauthIds.has(modelProvider))\n return modelProvider\n return null\n}\n\n// ── Auth resolution ──────────────────────────────────────────────────\n\n/** Resolve API key for a provider — checks env vars first, then OAuth credentials */\nasync function resolveApiKey(provider: string): Promise<string | null> {\n // 1. Check env vars via pi-ai's own resolver\n const envKey = getEnvApiKey(provider)\n if (envKey)\n return envKey\n\n // 2. Check stored OAuth credentials\n const oauthProviderId = resolveOAuthProviderId(provider)\n if (!oauthProviderId)\n return null\n\n const auth = loadAuth()\n if (!auth[oauthProviderId])\n return null\n\n const result = await getOAuthApiKey(oauthProviderId, auth)\n if (!result)\n return null\n\n // Save refreshed credentials to skilld's own file only (never leak pi-agent tokens)\n const skilldAuth = readAuthFile(SKILLD_AUTH_PATH)\n skilldAuth[oauthProviderId] = { type: 'oauth', ...result.newCredentials }\n saveAuth(skilldAuth)\n return result.apiKey\n}\n\n// ── OAuth login flow ─────────────────────────────────────────────────\n\nexport interface LoginCallbacks {\n /** Called with the URL the user needs to open in their browser */\n onAuth: (url: string, instructions?: string) => void\n /** Called when pi-ai needs text input from the user */\n onPrompt: (message: string, placeholder?: string) => Promise<string>\n /** Status updates during the login flow */\n onProgress?: (message: string) => void\n}\n\n/** Get available OAuth providers for login */\nexport function getOAuthProviderList(): Array<{ id: string, name: string, loggedIn: boolean }> {\n const auth = loadAuth()\n const providers = getOAuthProviders() as Array<{ id: string, name: string }>\n return providers.map((p: any) => ({\n id: p.id,\n name: p.name ?? p.id,\n loggedIn: !!auth[p.id],\n }))\n}\n\n/** Run OAuth login for a provider, saving credentials to ~/.skilld/ */\nexport async function loginOAuthProvider(providerId: string, callbacks: LoginCallbacks): Promise<boolean> {\n const provider = getOAuthProvider(providerId)\n if (!provider)\n return false\n\n const credentials = await provider.login({\n onAuth: (info: any) => callbacks.onAuth(info.url, info.instructions),\n onPrompt: async (prompt: any) => callbacks.onPrompt(prompt.message, prompt.placeholder),\n onProgress: (msg: string) => callbacks.onProgress?.(msg),\n })\n\n const auth = loadAuth()\n auth[providerId] = { type: 'oauth', ...credentials }\n saveAuth(auth)\n return true\n}\n\n/** Remove OAuth credentials for a provider */\nexport function logoutOAuthProvider(providerId: string): void {\n const auth = loadAuth()\n delete auth[providerId]\n saveAuth(auth)\n}\n\n// ── Dynamic model enumeration ────────────────────────────────────────\n\nconst MIN_CONTEXT_WINDOW = 32_000\n\n/** Legacy model patterns — old generations that clutter the model list */\nconst LEGACY_MODEL_PATTERNS = [\n // Anthropic: claude 3.x family\n /^claude-3-/,\n /^claude-3\\.5-/,\n /^claude-3\\.7-/,\n // OpenAI: pre-gpt-5\n /^gpt-4(?!\\.\\d)/, // gpt-4, gpt-4-turbo, gpt-4o but not gpt-4.1\n /^o1/,\n /^o3-mini/,\n // Google: old gemini generations + non-text models\n /^gemini-1\\./,\n /^gemini-2\\.0/,\n /^gemini-live-/,\n // Preview snapshots with date suffixes (e.g. -preview-04-17, -preview-05-06)\n /-preview-\\d{2}-\\d{2,4}$/,\n // Dated model snapshots (e.g. -20240307, -20241022)\n /-\\d{8}$/,\n]\n\nfunction isLegacyModel(modelId: string): boolean {\n return LEGACY_MODEL_PATTERNS.some(p => p.test(modelId))\n}\n\n/** Preferred model per provider for auto-selection (cheapest reliable option) */\nconst RECOMMENDED_MODELS: Record<string, RegExp> = {\n anthropic: /haiku/,\n google: /flash/,\n openai: /gpt-4\\.1-mini/,\n}\n\nexport interface PiAiModelInfo {\n /** Full model ID: pi:provider/model-id */\n id: string\n /** Human-readable name */\n name: string\n /** Provider + context info */\n hint: string\n /** Auth source: 'env', 'oauth', or 'none' */\n authSource: 'env' | 'oauth' | 'none'\n /** Whether this is the recommended model for its provider */\n recommended: boolean\n}\n\n/** Get all pi-ai models for providers with auth configured */\nexport function getAvailablePiAiModels(): PiAiModelInfo[] {\n const providers: string[] = getProviders()\n const auth = loadAuth()\n const available: PiAiModelInfo[] = []\n const recommendedPicked = new Set<string>()\n\n for (const provider of providers) {\n let authSource: 'env' | 'oauth' | 'none' = 'none'\n if (getEnvApiKey(provider)) {\n authSource = 'env'\n }\n else {\n const oauthId = resolveOAuthProviderId(provider)\n if (oauthId && auth[oauthId])\n authSource = 'oauth'\n }\n\n if (authSource === 'none')\n continue\n\n const models: any[] = getModels(provider as any)\n // First pass: find the recommended model for this provider\n const recPattern = RECOMMENDED_MODELS[provider]\n let recModelId: string | null = null\n if (recPattern) {\n for (const model of models) {\n if (!isLegacyModel(model.id) && recPattern.test(model.id)) {\n recModelId = model.id\n break\n }\n }\n }\n\n for (const model of models) {\n if (model.contextWindow && model.contextWindow < MIN_CONTEXT_WINDOW)\n continue\n if (isLegacyModel(model.id))\n continue\n\n const id = `pi:${provider}/${model.id}`\n const ctx = model.contextWindow ? ` · ${Math.round(model.contextWindow / 1000)}k ctx` : ''\n const cost = model.cost?.input ? ` · $${model.cost.input}/Mtok` : ''\n const isRecommended = model.id === recModelId && !recommendedPicked.has(provider)\n\n if (isRecommended)\n recommendedPicked.add(provider)\n\n available.push({\n id,\n name: model.name || model.id,\n hint: `${authSource === 'oauth' ? 'OAuth' : 'API key'}${ctx}${cost}`,\n authSource,\n recommended: isRecommended,\n })\n }\n }\n\n return available\n}\n\n// ── Reference inlining ───────────────────────────────────────────────\n\nconst REFERENCE_SUBDIRS = ['docs', 'issues', 'discussions', 'releases']\nconst MAX_REFERENCE_CHARS = 150_000 // ~37k tokens\n\n/** Read reference files from .skilld/ and format as inline context */\nfunction collectReferenceContent(skillDir: string): string {\n const skilldDir = join(skillDir, '.skilld')\n if (!existsSync(skilldDir))\n return ''\n\n const files: Array<{ path: string, content: string }> = []\n let totalChars = 0\n\n for (const subdir of REFERENCE_SUBDIRS) {\n const dirPath = join(skilldDir, subdir)\n if (!existsSync(dirPath))\n continue\n\n const indexPath = join(dirPath, '_INDEX.md')\n if (existsSync(indexPath) && totalChars < MAX_REFERENCE_CHARS) {\n const content = sanitizeMarkdown(readFileSync(indexPath, 'utf-8'))\n if (totalChars + content.length <= MAX_REFERENCE_CHARS) {\n files.push({ path: `references/${subdir}/_INDEX.md`, content })\n totalChars += content.length\n }\n }\n\n const entries = readdirSync(dirPath, { recursive: true })\n for (const entry of entries) {\n if (totalChars >= MAX_REFERENCE_CHARS)\n break\n const entryStr = String(entry)\n if (!entryStr.endsWith('.md') || entryStr === '_INDEX.md')\n continue\n const fullPath = join(dirPath, entryStr)\n if (!existsSync(fullPath))\n continue\n try {\n const content = sanitizeMarkdown(readFileSync(fullPath, 'utf-8'))\n if (totalChars + content.length > MAX_REFERENCE_CHARS)\n continue\n files.push({ path: `references/${subdir}/${entryStr}`, content })\n totalChars += content.length\n }\n catch {}\n }\n }\n\n // Read pkg/README.md if available and within budget\n const readmePath = join(skilldDir, 'pkg', 'README.md')\n if (existsSync(readmePath) && totalChars < MAX_REFERENCE_CHARS) {\n const content = sanitizeMarkdown(readFileSync(readmePath, 'utf-8'))\n if (totalChars + content.length <= MAX_REFERENCE_CHARS) {\n files.push({ path: 'references/pkg/README.md', content })\n totalChars += content.length\n }\n }\n\n // Read type definitions if present\n const pkgDir = join(skilldDir, 'pkg')\n if (existsSync(pkgDir) && totalChars < MAX_REFERENCE_CHARS) {\n try {\n const pkgEntries = readdirSync(pkgDir, { recursive: true })\n for (const entry of pkgEntries) {\n const entryStr = String(entry)\n if (!entryStr.endsWith('.d.ts'))\n continue\n const fullPath = join(pkgDir, entryStr)\n if (!existsSync(fullPath))\n continue\n const content = sanitizeMarkdown(readFileSync(fullPath, 'utf-8'))\n if (totalChars + content.length <= MAX_REFERENCE_CHARS) {\n files.push({ path: `references/pkg/${entryStr}`, content })\n totalChars += content.length\n }\n break // only first .d.ts\n }\n }\n catch {}\n }\n\n if (files.length === 0)\n return ''\n\n const blocks = files.map(f => `<file path=\"${f.path}\">\\n${f.content.replaceAll('</file>', '&lt;/file&gt;').replaceAll('</reference-files>', '&lt;/reference-files&gt;')}\\n</file>`)\n return `<reference-files>\\n${blocks.join('\\n')}\\n</reference-files>`\n}\n\n// ── Section optimization ─────────────────────────────────────────────\n\nexport interface PiAiSectionOptions {\n section: SkillSection\n prompt: string\n skillDir: string\n model: string\n onProgress?: (progress: StreamProgress) => void\n signal?: AbortSignal\n}\n\nexport interface PiAiSectionResult {\n text: string\n usage?: { input: number, output: number }\n cost?: number\n}\n\n/** Optimize a single section using pi-ai direct API */\nexport async function optimizeSectionPiAi(opts: PiAiSectionOptions): Promise<PiAiSectionResult> {\n const parsed = parsePiAiModelId(opts.model)\n if (!parsed)\n throw new Error(`Invalid pi-ai model ID: ${opts.model}. Expected format: pi:provider/model-id`)\n\n const model = getModel(parsed.provider as any, parsed.modelId as any)\n const apiKey = await resolveApiKey(parsed.provider)\n\n // Build non-agentic prompt with inlined references\n const portablePrompt = portabilizePrompt(opts.prompt, opts.section)\n const references = collectReferenceContent(opts.skillDir)\n const fullPrompt = references\n ? `${portablePrompt}\\n\\n## Reference Content\\n\\nThe following files are provided inline for your reference:\\n\\n${references}`\n : portablePrompt\n\n opts.onProgress?.({ chunk: '[starting...]', type: 'reasoning', text: '', reasoning: '', section: opts.section })\n\n const stream = streamSimple(model, {\n systemPrompt: 'You are a technical documentation expert generating SKILL.md sections for AI agent skills. Output clean, structured markdown following the format instructions exactly.',\n messages: [{\n role: 'user' as const,\n content: [{ type: 'text' as const, text: fullPrompt }],\n timestamp: Date.now(),\n }],\n }, {\n reasoning: 'medium',\n maxTokens: 16_384,\n ...(apiKey ? { apiKey } : {}),\n })\n\n let text = ''\n let usage: { input: number, output: number } | undefined\n let cost: number | undefined\n\n for await (const event of stream) {\n if (opts.signal?.aborted)\n throw new Error('pi-ai request timed out')\n switch (event.type) {\n case 'text_delta':\n text += event.delta\n opts.onProgress?.({ chunk: event.delta, type: 'text', text, reasoning: '', section: opts.section })\n break\n case 'done':\n if (event.message?.usage) {\n usage = { input: event.message.usage.input, output: event.message.usage.output }\n cost = event.message.usage.cost?.total\n }\n break\n case 'error':\n throw new Error(event.error?.errorMessage ?? 'pi-ai stream error')\n }\n }\n\n return { text, usage, cost }\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'\nimport { getAvailablePiAiModels, isPiAiModel, optimizeSectionPiAi, parsePiAiModelId } from './pi-ai.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 /** Per-section timestamp of last \"Writing...\" emission — throttles text_delta spam */\n const lastTextEmit = new Map<string, number>()\n const TEXT_THROTTLE_MS = 2000\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 const key = section ?? ''\n const now = Date.now()\n const last = lastTextEmit.get(key) ?? 0\n if (now - last < TEXT_THROTTLE_MS)\n return\n lastTextEmit.set(key, now)\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// ── Provider display names ────────────────────────────────────────────\n\n/** Map CLI agent IDs to their LLM provider name (not the agent/tool name) */\nconst CLI_PROVIDER_NAMES: Record<string, string> = {\n 'claude-code': 'Anthropic',\n 'gemini-cli': 'Google',\n 'codex': 'OpenAI',\n}\n\nconst PI_PROVIDER_NAMES: Record<string, string> = {\n 'anthropic': 'Anthropic',\n 'google': 'Google',\n 'google-antigravity': 'Antigravity',\n 'google-gemini-cli': 'Google Gemini',\n 'google-vertex': 'Google Vertex',\n 'openai': 'OpenAI',\n 'openai-codex': 'OpenAI Codex',\n 'github-copilot': 'GitHub Copilot',\n 'groq': 'Groq',\n 'mistral': 'Mistral',\n 'xai': 'xAI',\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 if (isPiAiModel(id)) {\n const parsed = parsePiAiModelId(id)\n return parsed?.modelId ?? id\n }\n return CLI_MODELS[id]?.name ?? id\n}\n\nexport function getModelLabel(id: OptimizeModel): string {\n if (isPiAiModel(id)) {\n const parsed = parsePiAiModelId(id)\n return parsed ? `${PI_PROVIDER_NAMES[parsed.provider] ?? parsed.provider} · ${parsed.modelId}` : id\n }\n const config = CLI_MODELS[id]\n if (!config)\n return id\n const providerName = CLI_PROVIDER_NAMES[config.agentId] ?? config.cli\n return `${providerName} · ${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 const cliModels = (Object.entries(CLI_MODELS) as [OptimizeModel, CliModelConfig][])\n .filter(([_, config]) => availableAgentIds.has(config.agentId))\n .map(([id, config]) => {\n const providerName = CLI_PROVIDER_NAMES[config.agentId] ?? agents[config.agentId]?.displayName ?? config.agentId\n return {\n id,\n name: config.name,\n hint: config.hint,\n recommended: config.recommended,\n agentId: config.agentId,\n agentName: providerName,\n provider: config.agentId,\n providerName: `${providerName} (via ${config.cli} CLI)`,\n vendorGroup: providerName,\n }\n })\n\n // Append pi-ai direct API models (providers with auth configured)\n const piAiModels = getAvailablePiAiModels()\n const piAiEntries = piAiModels.map((m) => {\n const parsed = parsePiAiModelId(m.id)\n const piProvider = parsed?.provider ?? 'pi-ai'\n const displayName = PI_PROVIDER_NAMES[piProvider] ?? piProvider\n const authLabel = m.authSource === 'env' ? 'API' : 'OAuth'\n return {\n id: m.id as OptimizeModel,\n name: m.name,\n hint: m.hint,\n recommended: m.recommended,\n agentId: 'pi-ai',\n agentName: `pi-ai (${m.authSource})`,\n provider: `pi:${piProvider}:${m.authSource}`,\n providerName: `${displayName} (${authLabel})`,\n vendorGroup: displayName,\n }\n })\n\n return [...cliModels, ...piAiEntries]\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// ── pi-ai direct API path ────────────────────────────────────────────\n\nasync function optimizeSectionViaPiAi(opts: {\n section: SkillSection\n prompt: string\n outputFile: string\n skillDir: string\n model: OptimizeModel\n onProgress?: (progress: StreamProgress) => void\n timeout: number\n debug?: boolean\n}): Promise<SectionResult> {\n const { section, prompt, outputFile, skillDir, model, onProgress, timeout, debug } = opts\n const skilldDir = join(skillDir, '.skilld')\n const outputPath = join(skilldDir, outputFile)\n\n // Write prompt for debugging (same as CLI path)\n writeFileSync(join(skilldDir, `PROMPT_${section}.md`), prompt)\n\n try {\n const ac = new AbortController()\n const timer = setTimeout(() => ac.abort(), timeout)\n const result = await optimizeSectionPiAi({ section, prompt, skillDir, model, onProgress, signal: ac.signal })\n .finally(() => clearTimeout(timer))\n\n const raw = result.text.trim()\n\n // Debug logging\n if (debug) {\n const logsDir = join(skilldDir, 'logs')\n const logName = section.toUpperCase().replace(/-/g, '_')\n mkdirSync(logsDir, { recursive: true })\n if (raw)\n writeFileSync(join(logsDir, `${logName}.md`), raw)\n }\n\n if (!raw) {\n return { section, content: '', wasOptimized: false, error: 'pi-ai returned empty response' }\n }\n\n const content = cleanSectionOutput(raw)\n\n if (content)\n writeFileSync(outputPath, content)\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 return {\n section,\n content,\n wasOptimized: !!content,\n warnings: warnings?.length ? warnings : undefined,\n usage: result.usage,\n cost: result.cost,\n }\n }\n catch (err) {\n return { section, content: '', wasOptimized: false, error: (err as Error).message }\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, or call pi-ai directly */\nfunction optimizeSection(opts: OptimizeSectionOptions): Promise<SectionResult> {\n const { section, prompt, outputFile, skillDir, model, onProgress, timeout, debug, preExistingFiles } = opts\n\n // pi-ai direct API path — no CLI spawning\n if (isPiAiModel(model)) {\n return optimizeSectionViaPiAi({ section, prompt, outputFile, skillDir, model, onProgress, timeout, debug })\n }\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 if (isPiAiModel(model)) {\n const available = new Set(getAvailablePiAiModels().map(m => m.id as OptimizeModel))\n if (!available.has(model))\n return { optimized: '', wasOptimized: false, error: `Pi model unavailable or not authenticated: ${model}` }\n }\n else if (!CLI_MODELS[model]) {\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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"}
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/pi-ai.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 * pi-ai adapter — direct LLM API calls via @mariozechner/pi-ai\n *\n * Optional alternative to CLI spawning. Supports:\n * - Env-var API keys (ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY, etc.)\n * - OAuth login to piggyback on existing subscriptions (Claude Pro, ChatGPT Plus, Copilot)\n *\n * Models are enumerated dynamically from pi-ai's registry — no hardcoded list.\n * Reference content is inlined into the prompt via portabilizePrompt().\n */\n\nimport type { SkillSection } from '../prompts/index.ts'\nimport type { StreamProgress } from './types.ts'\nimport { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { getEnvApiKey, getModel, getModels, getProviders, streamSimple } from '@mariozechner/pi-ai'\nimport { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from '@mariozechner/pi-ai/oauth'\nimport { join } from 'pathe'\nimport { sanitizeMarkdown } from '../../core/sanitize.ts'\nimport { portabilizePrompt } from '../prompts/index.ts'\n\nexport function isPiAiModel(model: string): boolean {\n return model.startsWith('pi:')\n}\n\n/** Parse a pi:provider/model-id string → { provider, modelId } */\nexport function parsePiAiModelId(model: string): { provider: string, modelId: string } | null {\n if (!model.startsWith('pi:'))\n return null\n const rest = model.slice(3)\n const slashIdx = rest.indexOf('/')\n if (slashIdx === -1)\n return null\n return { provider: rest.slice(0, slashIdx), modelId: rest.slice(slashIdx + 1) }\n}\n\n// ── OAuth credentials ────────────────────────────────────────────────\n\n/** pi coding agent stores auth here; env var can override */\nconst PI_AGENT_AUTH_PATH = join(\n process.env.PI_CODING_AGENT_DIR || join(homedir(), '.pi', 'agent'),\n 'auth.json',\n)\n/** skilld's own auth file — used when user logs in via skilld */\nconst SKILLD_AUTH_PATH = join(homedir(), '.skilld', 'pi-ai-auth.json')\n\ninterface OAuthCredentials {\n type: 'oauth'\n refresh: string\n access: string\n expires: number\n [key: string]: unknown\n}\n\nfunction readAuthFile(path: string): Record<string, OAuthCredentials> {\n if (!existsSync(path))\n return {}\n try {\n return JSON.parse(readFileSync(path, 'utf-8'))\n }\n catch { return {} }\n}\n\n/** Load auth from pi coding agent first (~/.pi/agent/auth.json), then skilld's own */\nfunction loadAuth(): Record<string, OAuthCredentials> {\n const piAuth = readAuthFile(PI_AGENT_AUTH_PATH)\n const skilldAuth = readAuthFile(SKILLD_AUTH_PATH)\n // pi agent credentials take precedence (user's primary auth)\n return { ...skilldAuth, ...piAuth }\n}\n\n/** Save auth to skilld's own file — never writes to pi agent's auth */\nfunction saveAuth(auth: Record<string, OAuthCredentials>): void {\n mkdirSync(join(homedir(), '.skilld'), { recursive: true, mode: 0o700 })\n writeFileSync(SKILLD_AUTH_PATH, JSON.stringify(auth, null, 2), { mode: 0o600 })\n}\n\n/**\n * Overrides for model-provider → OAuth-provider mapping.\n * Most providers share the same ID in both systems (auto-matched).\n * Only list exceptions where the IDs diverge.\n */\nconst OAUTH_PROVIDER_OVERRIDES: Record<string, string> = {\n google: 'google-gemini-cli',\n openai: 'openai-codex',\n}\n\n/** Resolve model provider ID → OAuth provider ID */\nfunction resolveOAuthProviderId(modelProvider: string): string | null {\n if (OAUTH_PROVIDER_OVERRIDES[modelProvider])\n return OAUTH_PROVIDER_OVERRIDES[modelProvider]\n // Auto-match: if the model provider ID is also an OAuth provider, use it directly\n const oauthIds = new Set(getOAuthProviders().map((p: any) => p.id))\n if (oauthIds.has(modelProvider))\n return modelProvider\n return null\n}\n\n// ── Auth resolution ──────────────────────────────────────────────────\n\n/** Resolve API key for a provider — checks env vars first, then OAuth credentials */\nasync function resolveApiKey(provider: string): Promise<string | null> {\n // 1. Check env vars via pi-ai's own resolver\n const envKey = getEnvApiKey(provider)\n if (envKey)\n return envKey\n\n // 2. Check stored OAuth credentials\n const oauthProviderId = resolveOAuthProviderId(provider)\n if (!oauthProviderId)\n return null\n\n const auth = loadAuth()\n if (!auth[oauthProviderId])\n return null\n\n const result = await getOAuthApiKey(oauthProviderId, auth)\n if (!result)\n return null\n\n // Save refreshed credentials to skilld's own file only (never leak pi-agent tokens)\n const skilldAuth = readAuthFile(SKILLD_AUTH_PATH)\n skilldAuth[oauthProviderId] = { type: 'oauth', ...result.newCredentials }\n saveAuth(skilldAuth)\n return result.apiKey\n}\n\n// ── OAuth login flow ─────────────────────────────────────────────────\n\nexport interface LoginCallbacks {\n /** Called with the URL the user needs to open in their browser */\n onAuth: (url: string, instructions?: string) => void\n /** Called when pi-ai needs text input from the user */\n onPrompt: (message: string, placeholder?: string) => Promise<string>\n /** Status updates during the login flow */\n onProgress?: (message: string) => void\n}\n\n/** Get available OAuth providers for login */\nexport function getOAuthProviderList(): Array<{ id: string, name: string, loggedIn: boolean }> {\n const auth = loadAuth()\n const providers = getOAuthProviders() as Array<{ id: string, name: string }>\n return providers.map((p: any) => ({\n id: p.id,\n name: p.name ?? p.id,\n loggedIn: !!auth[p.id],\n }))\n}\n\n/** Run OAuth login for a provider, saving credentials to ~/.skilld/ */\nexport async function loginOAuthProvider(providerId: string, callbacks: LoginCallbacks): Promise<boolean> {\n const provider = getOAuthProvider(providerId)\n if (!provider)\n return false\n\n const credentials = await provider.login({\n onAuth: (info: any) => callbacks.onAuth(info.url, info.instructions),\n onPrompt: async (prompt: any) => callbacks.onPrompt(prompt.message, prompt.placeholder),\n onProgress: (msg: string) => callbacks.onProgress?.(msg),\n })\n\n const auth = loadAuth()\n auth[providerId] = { type: 'oauth', ...credentials }\n saveAuth(auth)\n return true\n}\n\n/** Remove OAuth credentials for a provider */\nexport function logoutOAuthProvider(providerId: string): void {\n const auth = loadAuth()\n delete auth[providerId]\n saveAuth(auth)\n}\n\n// ── Dynamic model enumeration ────────────────────────────────────────\n\nconst MIN_CONTEXT_WINDOW = 32_000\n\n/** Legacy model patterns — old generations that clutter the model list */\nconst LEGACY_MODEL_PATTERNS = [\n // Anthropic: claude 3.x family\n /^claude-3-/,\n /^claude-3\\.5-/,\n /^claude-3\\.7-/,\n // OpenAI: pre-gpt-5\n /^gpt-4(?!\\.\\d)/, // gpt-4, gpt-4-turbo, gpt-4o but not gpt-4.1\n /^o1/,\n /^o3-mini/,\n // Google: old gemini generations + non-text models\n /^gemini-1\\./,\n /^gemini-2\\.0/,\n /^gemini-live-/,\n // Preview snapshots with date suffixes (e.g. -preview-04-17, -preview-05-06)\n /-preview-\\d{2}-\\d{2,4}$/,\n // Dated model snapshots (e.g. -20240307, -20241022)\n /-\\d{8}$/,\n]\n\nfunction isLegacyModel(modelId: string): boolean {\n return LEGACY_MODEL_PATTERNS.some(p => p.test(modelId))\n}\n\n/** Preferred model per provider for auto-selection (cheapest reliable option) */\nconst RECOMMENDED_MODELS: Record<string, RegExp> = {\n anthropic: /haiku/,\n google: /flash/,\n openai: /gpt-4\\.1-mini/,\n}\n\nexport interface PiAiModelInfo {\n /** Full model ID: pi:provider/model-id */\n id: string\n /** Human-readable name */\n name: string\n /** Provider + context info */\n hint: string\n /** Auth source: 'env', 'oauth', or 'none' */\n authSource: 'env' | 'oauth' | 'none'\n /** Whether this is the recommended model for its provider */\n recommended: boolean\n}\n\n/** Get all pi-ai models for providers with auth configured */\nexport function getAvailablePiAiModels(): PiAiModelInfo[] {\n const providers: string[] = getProviders()\n const auth = loadAuth()\n const available: PiAiModelInfo[] = []\n const recommendedPicked = new Set<string>()\n\n for (const provider of providers) {\n let authSource: 'env' | 'oauth' | 'none' = 'none'\n if (getEnvApiKey(provider)) {\n authSource = 'env'\n }\n else {\n const oauthId = resolveOAuthProviderId(provider)\n if (oauthId && auth[oauthId])\n authSource = 'oauth'\n }\n\n if (authSource === 'none')\n continue\n\n const models: any[] = getModels(provider as any)\n // First pass: find the recommended model for this provider\n const recPattern = RECOMMENDED_MODELS[provider]\n let recModelId: string | null = null\n if (recPattern) {\n for (const model of models) {\n if (!isLegacyModel(model.id) && recPattern.test(model.id)) {\n recModelId = model.id\n break\n }\n }\n }\n\n for (const model of models) {\n if (model.contextWindow && model.contextWindow < MIN_CONTEXT_WINDOW)\n continue\n if (isLegacyModel(model.id))\n continue\n\n const id = `pi:${provider}/${model.id}`\n const ctx = model.contextWindow ? ` · ${Math.round(model.contextWindow / 1000)}k ctx` : ''\n const cost = model.cost?.input ? ` · $${model.cost.input}/Mtok` : ''\n const isRecommended = model.id === recModelId && !recommendedPicked.has(provider)\n\n if (isRecommended)\n recommendedPicked.add(provider)\n\n available.push({\n id,\n name: model.name || model.id,\n hint: `${authSource === 'oauth' ? 'OAuth' : 'API key'}${ctx}${cost}`,\n authSource,\n recommended: isRecommended,\n })\n }\n }\n\n return available\n}\n\n// ── Reference inlining ───────────────────────────────────────────────\n\nconst REFERENCE_SUBDIRS = ['docs', 'issues', 'discussions', 'releases']\nconst MAX_REFERENCE_CHARS = 150_000 // ~37k tokens\n\n/** Read reference files from .skilld/ and format as inline context */\nfunction collectReferenceContent(skillDir: string): string {\n const skilldDir = join(skillDir, '.skilld')\n if (!existsSync(skilldDir))\n return ''\n\n const files: Array<{ path: string, content: string }> = []\n let totalChars = 0\n\n for (const subdir of REFERENCE_SUBDIRS) {\n const dirPath = join(skilldDir, subdir)\n if (!existsSync(dirPath))\n continue\n\n const indexPath = join(dirPath, '_INDEX.md')\n if (existsSync(indexPath) && totalChars < MAX_REFERENCE_CHARS) {\n const content = sanitizeMarkdown(readFileSync(indexPath, 'utf-8'))\n if (totalChars + content.length <= MAX_REFERENCE_CHARS) {\n files.push({ path: `references/${subdir}/_INDEX.md`, content })\n totalChars += content.length\n }\n }\n\n const entries = readdirSync(dirPath, { recursive: true })\n for (const entry of entries) {\n if (totalChars >= MAX_REFERENCE_CHARS)\n break\n const entryStr = String(entry)\n if (!entryStr.endsWith('.md') || entryStr === '_INDEX.md')\n continue\n const fullPath = join(dirPath, entryStr)\n if (!existsSync(fullPath))\n continue\n try {\n const content = sanitizeMarkdown(readFileSync(fullPath, 'utf-8'))\n if (totalChars + content.length > MAX_REFERENCE_CHARS)\n continue\n files.push({ path: `references/${subdir}/${entryStr}`, content })\n totalChars += content.length\n }\n catch {}\n }\n }\n\n // Read pkg/README.md if available and within budget\n const readmePath = join(skilldDir, 'pkg', 'README.md')\n if (existsSync(readmePath) && totalChars < MAX_REFERENCE_CHARS) {\n const content = sanitizeMarkdown(readFileSync(readmePath, 'utf-8'))\n if (totalChars + content.length <= MAX_REFERENCE_CHARS) {\n files.push({ path: 'references/pkg/README.md', content })\n totalChars += content.length\n }\n }\n\n // Read type definitions if present\n const pkgDir = join(skilldDir, 'pkg')\n if (existsSync(pkgDir) && totalChars < MAX_REFERENCE_CHARS) {\n try {\n const pkgEntries = readdirSync(pkgDir, { recursive: true })\n for (const entry of pkgEntries) {\n const entryStr = String(entry)\n if (!entryStr.endsWith('.d.ts'))\n continue\n const fullPath = join(pkgDir, entryStr)\n if (!existsSync(fullPath))\n continue\n const content = sanitizeMarkdown(readFileSync(fullPath, 'utf-8'))\n if (totalChars + content.length <= MAX_REFERENCE_CHARS) {\n files.push({ path: `references/pkg/${entryStr}`, content })\n totalChars += content.length\n }\n break // only first .d.ts\n }\n }\n catch {}\n }\n\n if (files.length === 0)\n return ''\n\n const blocks = files.map(f => `<file path=\"${f.path}\">\\n${f.content.replaceAll('</file>', '&lt;/file&gt;').replaceAll('</reference-files>', '&lt;/reference-files&gt;')}\\n</file>`)\n return `<reference-files>\\n${blocks.join('\\n')}\\n</reference-files>`\n}\n\n// ── Section optimization ─────────────────────────────────────────────\n\nexport interface PiAiSectionOptions {\n section: SkillSection\n prompt: string\n skillDir: string\n model: string\n onProgress?: (progress: StreamProgress) => void\n signal?: AbortSignal\n}\n\nexport interface PiAiSectionResult {\n text: string\n usage?: { input: number, output: number }\n cost?: number\n}\n\n/** Optimize a single section using pi-ai direct API */\nexport async function optimizeSectionPiAi(opts: PiAiSectionOptions): Promise<PiAiSectionResult> {\n const parsed = parsePiAiModelId(opts.model)\n if (!parsed)\n throw new Error(`Invalid pi-ai model ID: ${opts.model}. Expected format: pi:provider/model-id`)\n\n const model = getModel(parsed.provider as any, parsed.modelId as any)\n const apiKey = await resolveApiKey(parsed.provider)\n\n // Build non-agentic prompt with inlined references\n const portablePrompt = portabilizePrompt(opts.prompt, opts.section)\n const references = collectReferenceContent(opts.skillDir)\n const fullPrompt = references\n ? `${portablePrompt}\\n\\n## Reference Content\\n\\nThe following files are provided inline for your reference:\\n\\n${references}`\n : portablePrompt\n\n opts.onProgress?.({ chunk: '[starting...]', type: 'reasoning', text: '', reasoning: '', section: opts.section })\n\n const stream = streamSimple(model, {\n systemPrompt: 'You are a technical documentation expert generating SKILL.md sections for AI agent skills. Output clean, structured markdown following the format instructions exactly.',\n messages: [{\n role: 'user' as const,\n content: [{ type: 'text' as const, text: fullPrompt }],\n timestamp: Date.now(),\n }],\n }, {\n reasoning: 'medium',\n maxTokens: 16_384,\n ...(apiKey ? { apiKey } : {}),\n })\n\n let text = ''\n let usage: { input: number, output: number } | undefined\n let cost: number | undefined\n\n for await (const event of stream) {\n if (opts.signal?.aborted)\n throw new Error('pi-ai request timed out')\n switch (event.type) {\n case 'text_delta':\n text += event.delta\n opts.onProgress?.({ chunk: event.delta, type: 'text', text, reasoning: '', section: opts.section })\n break\n case 'done':\n if (event.message?.usage) {\n usage = { input: event.message.usage.input, output: event.message.usage.output }\n cost = event.message.usage.cost?.total\n }\n break\n case 'error':\n throw new Error(event.error?.errorMessage ?? 'pi-ai stream error')\n }\n }\n\n return { text, usage, cost }\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'\nimport { getAvailablePiAiModels, isPiAiModel, optimizeSectionPiAi, parsePiAiModelId } from './pi-ai.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 /** Per-section timestamp of last \"Writing...\" emission — throttles text_delta spam */\n const lastTextEmit = new Map<string, number>()\n const TEXT_THROTTLE_MS = 2000\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 const key = section ?? ''\n const now = Date.now()\n const last = lastTextEmit.get(key) ?? 0\n if (now - last < TEXT_THROTTLE_MS)\n return\n lastTextEmit.set(key, now)\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// ── Provider display names ────────────────────────────────────────────\n\n/** Map CLI agent IDs to their LLM provider name (not the agent/tool name) */\nconst CLI_PROVIDER_NAMES: Record<string, string> = {\n 'claude-code': 'Anthropic',\n 'gemini-cli': 'Google',\n 'codex': 'OpenAI',\n}\n\nconst PI_PROVIDER_NAMES: Record<string, string> = {\n 'anthropic': 'Anthropic',\n 'google': 'Google',\n 'google-antigravity': 'Antigravity',\n 'google-gemini-cli': 'Google Gemini',\n 'google-vertex': 'Google Vertex',\n 'openai': 'OpenAI',\n 'openai-codex': 'OpenAI Codex',\n 'github-copilot': 'GitHub Copilot',\n 'groq': 'Groq',\n 'mistral': 'Mistral',\n 'xai': 'xAI',\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 if (isPiAiModel(id)) {\n const parsed = parsePiAiModelId(id)\n return parsed?.modelId ?? id\n }\n return CLI_MODELS[id]?.name ?? id\n}\n\nexport function getModelLabel(id: OptimizeModel): string {\n if (isPiAiModel(id)) {\n const parsed = parsePiAiModelId(id)\n return parsed ? `${PI_PROVIDER_NAMES[parsed.provider] ?? parsed.provider} · ${parsed.modelId}` : id\n }\n const config = CLI_MODELS[id]\n if (!config)\n return id\n const providerName = CLI_PROVIDER_NAMES[config.agentId] ?? config.cli\n return `${providerName} · ${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 const cliModels = (Object.entries(CLI_MODELS) as [OptimizeModel, CliModelConfig][])\n .filter(([_, config]) => availableAgentIds.has(config.agentId))\n .map(([id, config]) => {\n const providerName = CLI_PROVIDER_NAMES[config.agentId] ?? agents[config.agentId]?.displayName ?? config.agentId\n return {\n id,\n name: config.name,\n hint: config.hint,\n recommended: config.recommended,\n agentId: config.agentId,\n agentName: providerName,\n provider: config.agentId,\n providerName: `${providerName} (via ${config.cli} CLI)`,\n vendorGroup: providerName,\n }\n })\n\n // Append pi-ai direct API models (providers with auth configured)\n const piAiModels = getAvailablePiAiModels()\n const piAiEntries = piAiModels.map((m) => {\n const parsed = parsePiAiModelId(m.id)\n const piProvider = parsed?.provider ?? 'pi-ai'\n const displayName = PI_PROVIDER_NAMES[piProvider] ?? piProvider\n const authLabel = m.authSource === 'env' ? 'API' : 'OAuth'\n return {\n id: m.id as OptimizeModel,\n name: m.name,\n hint: m.hint,\n recommended: m.recommended,\n agentId: 'pi-ai',\n agentName: `pi-ai (${m.authSource})`,\n provider: `pi:${piProvider}:${m.authSource}`,\n providerName: `${displayName} (${authLabel})`,\n vendorGroup: displayName,\n }\n })\n\n return [...cliModels, ...piAiEntries]\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// ── pi-ai direct API path ────────────────────────────────────────────\n\nasync function optimizeSectionViaPiAi(opts: {\n section: SkillSection\n prompt: string\n outputFile: string\n skillDir: string\n model: OptimizeModel\n onProgress?: (progress: StreamProgress) => void\n timeout: number\n debug?: boolean\n}): Promise<SectionResult> {\n const { section, prompt, outputFile, skillDir, model, onProgress, timeout, debug } = opts\n const skilldDir = join(skillDir, '.skilld')\n const outputPath = join(skilldDir, outputFile)\n\n // Write prompt for debugging (same as CLI path)\n writeFileSync(join(skilldDir, `PROMPT_${section}.md`), prompt)\n\n try {\n const ac = new AbortController()\n const timer = setTimeout(() => ac.abort(), timeout)\n const result = await optimizeSectionPiAi({ section, prompt, skillDir, model, onProgress, signal: ac.signal })\n .finally(() => clearTimeout(timer))\n\n const raw = result.text.trim()\n\n // Debug logging\n if (debug) {\n const logsDir = join(skilldDir, 'logs')\n const logName = section.toUpperCase().replace(/-/g, '_')\n mkdirSync(logsDir, { recursive: true })\n if (raw)\n writeFileSync(join(logsDir, `${logName}.md`), raw)\n }\n\n if (!raw) {\n return { section, content: '', wasOptimized: false, error: 'pi-ai returned empty response' }\n }\n\n const content = cleanSectionOutput(raw)\n\n if (content)\n writeFileSync(outputPath, content)\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 return {\n section,\n content,\n wasOptimized: !!content,\n warnings: warnings?.length ? warnings : undefined,\n usage: result.usage,\n cost: result.cost,\n }\n }\n catch (err) {\n return { section, content: '', wasOptimized: false, error: (err as Error).message }\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, or call pi-ai directly */\nfunction optimizeSection(opts: OptimizeSectionOptions): Promise<SectionResult> {\n const { section, prompt, outputFile, skillDir, model, onProgress, timeout, debug, preExistingFiles } = opts\n\n // pi-ai direct API path — no CLI spawning\n if (isPiAiModel(model)) {\n return optimizeSectionViaPiAi({ section, prompt, outputFile, skillDir, model, onProgress, timeout, debug })\n }\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, overheadLines } = 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 overheadLines,\n sections: selectedSections,\n })\n\n if (sectionPrompts.size === 0) {\n return { optimized: '', wasOptimized: false, error: 'No valid sections to generate' }\n }\n\n if (isPiAiModel(model)) {\n const available = new Set(getAvailablePiAiModels().map(m => m.id as OptimizeModel))\n if (!available.has(model))\n return { optimized: '', wasOptimized: false, error: `Pi model unavailable or not authenticated: ${model}` }\n }\n else if (!CLI_MODELS[model]) {\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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"}
@@ -8,6 +8,7 @@ import "./shared.mjs";
8
8
  import "./sources.mjs";
9
9
  import "./detect.mjs";
10
10
  import { c as SECTION_OUTPUT_FILES, d as extractMarkedSections, f as getSectionValidator, m as wrapSection, s as SECTION_MERGE_ORDER } from "./prompts.mjs";
11
+ import "./lockfile.mjs";
11
12
  import { i as iterateSkills } from "./skills.mjs";
12
13
  import { join, relative, resolve } from "pathe";
13
14
  import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";