skilld 1.5.4 → 1.5.5

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.
@@ -1 +1 @@
1
- {"version":3,"file":"cli-helpers.mjs","names":["dirname","resolve","agents"],"sources":["../../src/core/config.ts","../../src/version.ts","../../src/cli-helpers.ts"],"sourcesContent":["import type { OptimizeModel } from '../agent/index.ts'\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'pathe'\nimport { yamlEscape, yamlParseKV, yamlUnescape } from './yaml.ts'\n\nexport interface FeaturesConfig {\n search: boolean\n issues: boolean\n discussions: boolean\n releases: boolean\n}\n\nexport const defaultFeatures: FeaturesConfig = {\n search: true,\n issues: true,\n discussions: true,\n releases: true,\n}\n\nexport interface SkilldConfig {\n model?: OptimizeModel\n agent?: string\n features?: FeaturesConfig\n projects?: string[]\n skipLlm?: boolean\n}\n\nconst CONFIG_DIR = join(homedir(), '.skilld')\nconst CONFIG_PATH = join(CONFIG_DIR, 'config.yaml')\n\nlet configCache: SkilldConfig | undefined\n\nexport function hasConfig(): boolean {\n return existsSync(CONFIG_PATH)\n}\n\n/** Whether the first-run wizard has been completed (not just agent selection) */\nexport function hasCompletedWizard(): boolean {\n if (!existsSync(CONFIG_PATH))\n return false\n const config = readConfig()\n return config.features !== undefined || config.model !== undefined || config.skipLlm !== undefined\n}\n\nexport function readConfig(): SkilldConfig {\n if (configCache) {\n return {\n ...configCache,\n features: configCache.features ? { ...configCache.features } : undefined,\n projects: configCache.projects ? [...configCache.projects] : undefined,\n }\n }\n if (!existsSync(CONFIG_PATH))\n return {}\n\n const content = readFileSync(CONFIG_PATH, 'utf-8')\n const config: SkilldConfig = {}\n let inBlock: 'projects' | 'features' | null = null\n const projects: string[] = []\n const features: Partial<FeaturesConfig> = {}\n\n for (const line of content.split('\\n')) {\n if (line.startsWith('projects:')) {\n inBlock = 'projects'\n continue\n }\n if (line.startsWith('features:')) {\n inBlock = 'features'\n continue\n }\n if (inBlock === 'projects') {\n if (line.startsWith(' - ')) {\n projects.push(yamlUnescape(line.slice(4)))\n continue\n }\n inBlock = null\n }\n if (inBlock === 'features') {\n const m = line.match(/^ {2}(\\w+):\\s*(.+)/)\n if (m) {\n const key = m[1] as keyof FeaturesConfig\n if (key in defaultFeatures)\n features[key] = m[2] === 'true'\n continue\n }\n inBlock = null\n }\n const kv = yamlParseKV(line)\n if (!kv)\n continue\n const [key, value] = kv\n if (key === 'model' && value)\n config.model = value as OptimizeModel\n if (key === 'agent' && value)\n config.agent = value\n if (key === 'skipLlm')\n config.skipLlm = value === 'true'\n }\n\n if (projects.length > 0)\n config.projects = projects\n if (Object.keys(features).length > 0)\n config.features = { ...defaultFeatures, ...features }\n configCache = config\n return config\n}\n\nexport function writeConfig(config: SkilldConfig): void {\n mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 })\n\n let yaml = ''\n if (config.model)\n yaml += `model: ${config.model}\\n`\n if (config.agent)\n yaml += `agent: ${config.agent}\\n`\n if (config.skipLlm)\n yaml += `skipLlm: true\\n`\n if (config.features) {\n yaml += 'features:\\n'\n for (const [k, v] of Object.entries(config.features)) {\n yaml += ` ${k}: ${v}\\n`\n }\n }\n if (config.projects?.length) {\n yaml += 'projects:\\n'\n for (const p of config.projects) {\n yaml += ` - ${yamlEscape(p)}\\n`\n }\n }\n\n writeFileSync(CONFIG_PATH, yaml, { mode: 0o600 })\n configCache = undefined\n}\n\nexport function updateConfig(updates: Partial<SkilldConfig>): void {\n const config = readConfig()\n writeConfig({ ...config, ...updates })\n}\n\nexport function registerProject(projectPath: string): void {\n const config = readConfig()\n const projects = new Set(config.projects || [])\n projects.add(projectPath)\n writeConfig({ ...config, projects: [...projects] })\n}\n\nexport function unregisterProject(projectPath: string): void {\n const config = readConfig()\n const projects = (config.projects || []).filter(p => p !== projectPath)\n writeConfig({ ...config, projects })\n}\n\nexport function getRegisteredProjects(): string[] {\n return readConfig().projects || []\n}\n","import { readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n// Walk up from current file to find package.json (works in both src/ and dist/_chunks/)\nfunction findPackageJson(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (let i = 0; i < 5; i++) {\n const candidate = resolve(dir, 'package.json')\n try {\n const content = readFileSync(candidate, 'utf8')\n if (content)\n return content\n }\n catch {}\n dir = resolve(dir, '..')\n }\n return '{\"version\":\"0.0.0\"}'\n}\n\nexport const version: string = JSON.parse(findPackageJson()).version\n","/**\n * Shared CLI helpers used by subcommand definitions and the main CLI entry.\n * Extracted to avoid circular deps between cli.ts and commands/*.ts.\n */\n\nimport type { AgentType, OptimizeModel } from './agent/index.ts'\nimport type { ProjectState } from './core/skills.ts'\nimport * as p from '@clack/prompts'\nimport { parseTree } from 'jsonc-parser'\nimport { join } from 'pathe'\nimport { detectCurrentAgent } from 'unagent/env'\nimport { agents, detectInstalledAgents, detectProjectAgents, detectTargetAgent, getAgentVersion, getModelName } from './agent/index.ts'\nimport { readConfig, updateConfig } from './core/config.ts'\nimport { editJsonProperty, patchPackageJson, readPackageJsonSafe } from './core/package-json.ts'\nimport { version } from './version.ts'\n\nexport type { AgentType, OptimizeModel }\n\nexport interface IntroOptions {\n state: ProjectState\n /** Installed CLIs that can serve as enhancement models */\n generators?: Array<{ name: string, version: string }>\n /** Configured enhancement model ID */\n modelId?: string\n /** Resolved target agent ID */\n agentId?: string\n}\n\nexport const sharedArgs = {\n global: {\n type: 'boolean' as const,\n alias: 'g',\n description: 'Install globally to ~/<agent>/skills',\n default: false,\n },\n agent: {\n type: 'enum' as const,\n options: Object.keys(agents),\n alias: 'a',\n description: 'Target agent — where skills are installed',\n },\n model: {\n type: 'string' as const,\n alias: 'm',\n description: 'Enhancement model for SKILL.md generation',\n valueHint: 'id',\n },\n yes: {\n type: 'boolean' as const,\n alias: 'y',\n description: 'Skip prompts, use defaults',\n default: false,\n },\n force: {\n type: 'boolean' as const,\n alias: 'f',\n description: 'Ignore all caches, re-fetch docs and regenerate',\n default: false,\n },\n debug: {\n type: 'boolean' as const,\n description: 'Save raw enhancement output to logs/ for each section',\n default: false,\n },\n}\n\n// ── Menu loop utility ─────────────────────────────────────────────────\n\n/** Thrown when a clack prompt is cancelled inside a menuLoop handler */\nexport class MenuCancel extends Error { override name = 'MenuCancel' }\n\n/** Assert a clack prompt result is not cancelled. Throws MenuCancel if cancelled. */\nexport function guard<T>(value: T | symbol): T {\n if (p.isCancel(value))\n throw new MenuCancel()\n return value as T\n}\n\nexport interface MenuOption {\n label: string\n value: string\n hint?: string\n}\n\n/**\n * Run a select menu in a loop with automatic back-navigation.\n *\n * - Cancel (Escape) at the menu itself → exits (returns)\n * - Cancel inside a handler (via guard()) → caught, loops back to menu\n * - Handler returns truthy → exits (returns)\n * - Handler returns void/false → loops back to menu\n *\n * Options are rebuilt each iteration so hints stay fresh after changes.\n */\nexport async function menuLoop(opts: {\n message: string\n options: () => MenuOption[] | Promise<MenuOption[]>\n onSelect: (value: string) => Promise<boolean | void>\n initialValue?: string | (() => string | undefined)\n /** Use fuzzy-searchable autocomplete instead of static select */\n searchable?: boolean\n}): Promise<void> {\n while (true) {\n const options = await opts.options()\n const initial = typeof opts.initialValue === 'function' ? opts.initialValue() : opts.initialValue\n const choice = opts.searchable\n ? await p.autocomplete({ message: opts.message, options, ...(initial != null ? { initialValue: initial } : {}) })\n : await p.select({ message: opts.message, options, ...(initial != null ? { initialValue: initial } : {}) })\n if (p.isCancel(choice))\n return\n try {\n if (await opts.onSelect(choice as string))\n return\n }\n catch (err) {\n if (err instanceof MenuCancel)\n continue\n throw err\n }\n }\n}\n\n/** Check if we're running inside an AI coding agent */\nexport function isRunningInsideAgent(): boolean {\n return !!detectCurrentAgent()\n}\n\n/** Check if the current environment supports interactive prompts */\nexport function isInteractive(): boolean {\n if (isRunningInsideAgent())\n return false\n if (process.env.CI)\n return false\n if (!process.stdout.isTTY)\n return false\n return true\n}\n\n/** Exit with error if interactive terminal is required but unavailable */\nexport function requireInteractive(command: string): void {\n if (!isInteractive()) {\n console.error(`Error: \\`skilld ${command}\\` requires an interactive terminal`)\n process.exit(1)\n }\n}\n\n/** Resolve agent from flags/cwd/config. cwd is source of truth over config. */\nexport function resolveAgent(agentFlag?: string): AgentType | 'none' | null {\n if (process.env.SKILLD_NO_AGENT)\n return null\n return (agentFlag as AgentType | undefined)\n ?? detectTargetAgent()\n ?? (readConfig().agent as AgentType | undefined)\n ?? null\n}\n\nlet _warnedNoAgent = false\nfunction warnNoAgent(): void {\n if (_warnedNoAgent)\n return\n _warnedNoAgent = true\n p.log.warn('No target agent detected — falling back to prompt-only mode.\\n Use --agent <name> to specify, or run `skilld config` to set a default.')\n}\n\n/** Prompt user to pick an agent when auto-detection fails */\nexport async function promptForAgent(): Promise<AgentType | 'none' | null> {\n const noAgent = !!process.env.SKILLD_NO_AGENT\n const installed = noAgent ? [] : detectInstalledAgents()\n const projectMatches = noAgent ? [] : detectProjectAgents()\n\n // Non-interactive: auto-select sole installed agent or fall back to prompt-only\n if (!isInteractive()) {\n if (installed.length === 1) {\n updateConfig({ agent: installed[0] })\n return installed[0]!\n }\n warnNoAgent()\n return 'none'\n }\n\n // Brief context before asking about agents\n p.log.info(\n `Skilld generates reference cards from package docs so your AI agent\\n`\n + ` always has accurate APIs for your exact dependency versions.`,\n )\n\n // Build options: prefer project-matched agents, then installed, then all\n const candidateIds = projectMatches.length > 0\n ? projectMatches\n : installed.length > 0\n ? installed\n : Object.keys(agents) as AgentType[]\n\n // Agents that also read .claude/skills/\n const sharedAgents = new Set(\n Object.entries(agents)\n .filter(([, a]) => a.additionalSkillsDirs.some(d => d.includes('.claude/skills')))\n .map(([id]) => id),\n )\n\n // Group: agents that share skills vs agents with their own directory\n const sharedIds = candidateIds.filter(id => id === 'claude-code' || sharedAgents.has(id))\n const isolatedIds = candidateIds.filter(id => id !== 'claude-code' && !sharedAgents.has(id))\n\n const options: Array<{ label: string, value: AgentType | 'none', hint?: string }> = []\n\n // Show shared-compatible agents first\n if (sharedIds.length > 0 && isolatedIds.length > 0) {\n for (const id of sharedIds) {\n const a = agents[id]\n const hint = id === 'claude-code'\n ? `skills shared with ${sharedIds.length - 1} other agents`\n : `skills shared with Claude Code and others`\n options.push({ label: a.displayName, value: id as AgentType, hint })\n }\n }\n\n // Agents with isolated skill dirs\n const isolatedAgentIds = new Set(\n Object.entries(agents)\n .filter(([, a]) => a.additionalSkillsDirs.length === 0)\n .map(([id]) => id),\n )\n\n for (const id of (sharedIds.length > 0 && isolatedIds.length > 0 ? isolatedIds : candidateIds)) {\n if (options.some(o => o.value === id))\n continue\n const a = agents[id]\n const hint = sharedAgents.has(id) && id !== 'claude-code'\n ? 'skills shared with Claude Code and others'\n : isolatedAgentIds.has(id)\n ? 'skills only visible to this agent'\n : undefined\n options.push({ label: a.displayName, value: id as AgentType, hint })\n }\n\n options.push({ label: 'No agent', value: 'none', hint: 'export as standalone files for any AI' })\n\n if (!_warnedNoAgent) {\n _warnedNoAgent = true\n const hint = projectMatches.length > 1\n ? `Multiple agent directories found: ${projectMatches.map(t => agents[t].displayName).join(', ')}`\n : installed.length > 0\n ? `Found ${installed.map(t => agents[t].displayName).join(', ')} but couldn't determine which to use`\n : 'No agents auto-detected'\n const crossNote = sharedIds.length > 1\n ? `\\n \\x1B[90mTip: Picking Claude Code shares skills with ${sharedIds.filter(id => id !== 'claude-code').map(id => agents[id].displayName).join(', ')} automatically.\\x1B[0m`\n : ''\n p.log.warn(`${hint}\\n Pick the agent you actively code with.${crossNote}`)\n }\n\n const choice = await p.select({\n message: 'Which AI coding agent do you use?',\n options,\n })\n\n if (p.isCancel(choice))\n return null\n\n if (choice === 'none')\n return 'none'\n\n // Save as default so they don't get asked again\n updateConfig({ agent: choice })\n p.log.success(`Target agent set to ${agents[choice].displayName}`)\n return choice\n}\n\n/** Get installed LLM generators with working CLIs (verified via --version) */\nexport function getInstalledGenerators(): Array<{ name: string, version: string }> {\n const installed = detectInstalledAgents()\n return installed\n .filter(id => agents[id].cli)\n .map((id) => {\n const ver = getAgentVersion(id)\n return ver ? { name: agents[id].displayName, version: ver } : null\n })\n .filter((a): a is { name: string, version: string } => a !== null)\n}\n\nexport function relativeTime(date: Date): string {\n const now = Date.now()\n const diff = now - date.getTime()\n const mins = Math.floor(diff / 60000)\n const hours = Math.floor(diff / 3600000)\n const days = Math.floor(diff / 86400000)\n if (mins < 1)\n return 'just now'\n if (mins < 60)\n return `${mins}m ago`\n if (hours < 24)\n return `${hours}h ago`\n return `${days}d ago`\n}\n\nexport function getLastSynced(state: ProjectState): string | null {\n let latest: Date | null = null\n for (const skill of state.skills) {\n if (skill.info?.syncedAt) {\n const d = new Date(skill.info.syncedAt)\n if (!latest || d > latest)\n latest = d\n }\n }\n return latest ? relativeTime(latest) : null\n}\n\nexport function introLine({ state, generators, modelId, agentId }: IntroOptions): string {\n const name = '\\x1B[1m\\x1B[35mskilld\\x1B[0m'\n const ver = `\\x1B[90mv${version}\\x1B[0m`\n const lastSynced = getLastSynced(state)\n const synced = lastSynced ? ` · \\x1B[90msynced ${lastSynced}\\x1B[0m` : ''\n\n // Status line: enhancement model → target agent\n const parts: string[] = []\n if (modelId)\n parts.push(getModelName(modelId as any))\n else if (generators?.length)\n parts.push(generators.map(g => `${g.name} v${g.version}`).join(', '))\n if (agentId && agents[agentId as AgentType])\n parts.push(agents[agentId as AgentType].displayName)\n const statusLine = parts.length > 0\n ? `\\n\\x1B[90m↳ ${parts.join(' → ')}\\x1B[0m`\n : ''\n\n return `${name} ${ver}${synced}${statusLine}`\n}\n\nexport function formatStatus(synced: number, outdated: number): string {\n const parts: string[] = []\n if (synced > 0)\n parts.push(`\\x1B[32m${synced} synced\\x1B[0m`)\n if (outdated > 0)\n parts.push(`\\x1B[33m${outdated} outdated\\x1B[0m`)\n return `Skills: ${parts.join(' · ')}`\n}\n\n// ── Shared UI constants ───────────────────────────────────────────────\n\nexport const OAUTH_NOTE\n = 'Use an existing subscription (Claude Pro, ChatGPT Plus, Gemini)\\n'\n + 'without an API key. You authenticate directly with the provider\\n'\n + 'in your browser - no data leaves your machine.\\n'\n + '\\n'\n + 'A refresh token is stored locally at ~/.skilld/pi-ai-auth.json\\n'\n + 'and used to call the provider API directly from your computer.\\n'\n + '\\x1B[90mOAuth handled by pi-ai, an open-source local client library:\\n'\n + 'https://github.com/badlogic/pi-mono\\x1B[0m'\n\nexport const NO_MODELS_MESSAGE = 'No enhancement models detected.\\n'\n + ' \\x1B[90mSkills work fine without this - you get raw docs, issues, and types.\\n'\n + ' Enhancement compresses them into a concise cheat sheet with gotchas.\\x1B[0m\\n'\n + '\\n'\n + ' To connect a model (optional):\\n'\n + ' 1. Connect a subscription via OAuth below (Claude Pro, ChatGPT Plus, Copilot, Gemini)\\n'\n + ' 2. Set an env var: ANTHROPIC_API_KEY, GEMINI_API_KEY, or OPENAI_API_KEY\\n'\n + ' 3. Install a CLI tool: \\x1B[36mclaude\\x1B[0m, \\x1B[36mgemini\\x1B[0m, or \\x1B[36mcodex\\x1B[0m (restart wizard after)'\n\n/** Group models by vendor for provider→model selection. Uses vendorGroup to merge CLI and API entries under one heading. */\nexport function groupModelsByProvider<T extends { provider: string, providerName: string, vendorGroup?: string }>(models: T[]): Map<string, { name: string, models: T[] }> {\n const byVendor = new Map<string, { name: string, models: T[] }>()\n for (const m of models) {\n const key = m.vendorGroup ?? m.provider\n if (!byVendor.has(key))\n byVendor.set(key, { name: key, models: [] })\n byVendor.get(key)!.models.push(m)\n }\n return byVendor\n}\n\nexport interface ModelPickerOptions {\n /** Extra options prepended (e.g. Auto, Connect OAuth) */\n before?: Array<{ label: string, value: string, hint?: string }>\n /** Extra options appended (e.g. Skip) */\n after?: Array<{ label: string, value: string, hint?: string }>\n}\n\n/**\n * Smart provider→model picker. Skips the provider step when there's only 1 provider.\n * Returns the selected model value, or a sentinel string from before/after options.\n */\nexport async function pickModel<T extends { provider: string, providerName: string, name: string, id: string, hint: string, recommended?: boolean }>(\n models: T[],\n opts: ModelPickerOptions = {},\n): Promise<string | null> {\n const byProvider = groupModelsByProvider(models)\n const before = opts.before ?? []\n const after = opts.after ?? []\n\n // Single provider → skip provider step, show models directly\n if (byProvider.size === 1 && before.length === 0) {\n const [, group] = [...byProvider.entries()][0]!\n const choice = await p.select({\n message: `${group.name}`,\n options: [\n ...group.models.map(m => ({\n label: m.recommended ? `${m.name} (recommended - fast and cheap)` : m.name,\n value: m.id,\n hint: m.hint,\n })),\n ...after,\n ],\n })\n return p.isCancel(choice) ? null : choice as string\n }\n\n // Multiple providers or has before options - two-step\n const providerChoice = await p.select({\n message: 'Select provider',\n options: [\n ...before,\n ...Array.from(byProvider.entries(), ([key, { name, models: ms }]) => ({\n label: name,\n value: key,\n hint: `${ms.length} models`,\n })),\n ...after,\n ],\n })\n\n if (p.isCancel(providerChoice))\n return null\n\n // Check if it's a sentinel from before/after\n const providerStr = providerChoice as string\n if (before.some(o => o.value === providerStr) || after.some(o => o.value === providerStr))\n return providerStr\n\n // Drill into provider's models\n const group = byProvider.get(providerStr)!\n const modelChoice = await p.select({\n message: `Select model (${group.name})`,\n options: group.models.map(m => ({\n label: m.recommended ? `${m.name} (recommended - fast and cheap)` : m.name,\n value: m.id,\n hint: m.hint,\n })),\n })\n\n return p.isCancel(modelChoice) ? null : modelChoice as string\n}\n\n/**\n * Check if the prepare hook is already installed in package.json.\n */\nexport function hasPrepareHook(cwd: string = process.cwd()): boolean {\n const pkg = readPackageJsonSafe(join(cwd, 'package.json'))\n if (!pkg)\n return true // no package.json means nothing to suggest\n const existing = (pkg.parsed.scripts as Record<string, unknown> | undefined)?.prepare\n return typeof existing === 'string' && existing.includes('skilld')\n}\n\n/**\n * Prompt to add `skilld prepare` to package.json \"prepare\" script.\n * In non-interactive environments, falls back to an info log.\n * Returns true if the hook was added or already present.\n */\nexport async function suggestPrepareHook(cwd: string = process.cwd()): Promise<boolean> {\n const pkgJsonPath = join(cwd, 'package.json')\n const pkg = readPackageJsonSafe(pkgJsonPath)\n if (!pkg)\n return false\n\n const rawExisting = (pkg.parsed.scripts as Record<string, unknown> | undefined)?.prepare\n const existing: string | undefined = typeof rawExisting === 'string' ? rawExisting : undefined\n\n if (existing?.includes('skilld'))\n return true\n\n const prepareCmd = buildPrepareScript(existing, cwd)\n\n if (!isInteractive()) {\n p.log.info(\n `\\x1B[90mAdd to package.json scripts:\\n`\n + ` \\x1B[36m\"prepare\": \"${prepareCmd}\"\\x1B[0m\\n`\n + ` \\x1B[90mRestores references and shipped skills on install.\\x1B[0m`,\n )\n return false\n }\n\n const confirmed = await p.confirm({\n message: `Add \\x1B[36m\"prepare\": \"${prepareCmd}\"\\x1B[0m to package.json?`,\n initialValue: true,\n })\n if (p.isCancel(confirmed) || !confirmed)\n return false\n\n patchPackageJson(pkgJsonPath, (content) => {\n const tree = parseTree(content)\n const hasScripts = tree?.children?.some(c =>\n c.type === 'property' && c.children?.[0]?.value === 'scripts',\n )\n\n let patched = content\n if (!hasScripts)\n patched = editJsonProperty(patched, ['scripts'], {})\n\n return editJsonProperty(patched, ['scripts', 'prepare'], prepareCmd)\n })\n p.log.success('Added \\x1B[36mskilld prepare\\x1B[0m to package.json')\n return true\n}\n\n/**\n * Build the full prepare script value, safely appending to any existing command.\n */\nexport function buildPrepareScript(existing: string | undefined, cwd: string = process.cwd()): string {\n const bin = isNpxExecution() && !isSkilldDep(cwd) ? 'npx skilld' : 'skilld'\n const cmd = `${bin} prepare || true`\n if (!existing || !existing.trim())\n return cmd\n\n const trimmed = existing.trim()\n\n // Strip trailing && or ; that would leave a dangling operator\n const cleaned = trimmed.replace(/[&|;]+\\s*$/, '').trim()\n if (!cleaned)\n return cmd\n\n return `${cleaned} && (${cmd})`\n}\n\n/**\n * Detect if the current process was launched via npx, pnpm dlx, or similar one-shot runners.\n */\nfunction isNpxExecution(): boolean {\n // npm/pnpm set npm_command=exec when running via npx/dlx\n if (process.env.npm_command === 'exec')\n return true\n // Fallback: check if the resolved binary path contains npx or dlx cache dirs\n const execPath = process.env._ || ''\n return /npx|\\.store|dlx/.test(execPath)\n}\n\n/**\n * Check if skilld is listed as a dependency (dev or regular) in the project's package.json.\n */\nfunction isSkilldDep(cwd: string): boolean {\n const pkg = readPackageJsonSafe(join(cwd, 'package.json'))\n if (!pkg)\n return false\n const deps = pkg.parsed as Record<string, any>\n return !!(deps.dependencies?.skilld || deps.devDependencies?.skilld)\n}\n\nexport function getRepoHint(name: string, cwd: string): string | undefined {\n const result = readPackageJsonSafe(join(cwd, 'node_modules', name, 'package.json'))\n if (!result)\n return undefined\n const pkg = result.parsed as Record<string, any>\n const url = typeof pkg.repository === 'string'\n ? pkg.repository\n : pkg.repository?.url\n if (!url)\n return undefined\n return url\n .replace(/^git\\+/, '')\n .replace(/\\.git$/, '')\n .replace(/^git:\\/\\//, 'https://')\n .replace(/^ssh:\\/\\/git@github\\.com/, 'https://github.com')\n .replace(/^https?:\\/\\/(www\\.)?github\\.com\\//, '')\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli-helpers.mjs","names":["dirname","resolve","agents"],"sources":["../../src/core/config.ts","../../src/version.ts","../../src/cli-helpers.ts"],"sourcesContent":["import type { OptimizeModel } from '../agent/index.ts'\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'pathe'\nimport { yamlEscape, yamlParseKV, yamlUnescape } from './yaml.ts'\n\nexport interface FeaturesConfig {\n search: boolean\n issues: boolean\n discussions: boolean\n releases: boolean\n}\n\nexport const defaultFeatures: FeaturesConfig = {\n search: true,\n issues: true,\n discussions: true,\n releases: true,\n}\n\nexport interface SkilldConfig {\n model?: OptimizeModel\n agent?: string\n features?: FeaturesConfig\n projects?: string[]\n skipLlm?: boolean\n}\n\nconst CONFIG_DIR = join(homedir(), '.skilld')\nconst CONFIG_PATH = join(CONFIG_DIR, 'config.yaml')\n\nlet configCache: SkilldConfig | undefined\n\nexport function hasConfig(): boolean {\n return existsSync(CONFIG_PATH)\n}\n\n/** Whether the first-run wizard has been completed (not just agent selection) */\nexport function hasCompletedWizard(): boolean {\n if (!existsSync(CONFIG_PATH))\n return false\n const config = readConfig()\n return config.features !== undefined || config.model !== undefined || config.skipLlm !== undefined\n}\n\nexport function readConfig(): SkilldConfig {\n if (configCache) {\n return {\n ...configCache,\n features: configCache.features ? { ...configCache.features } : undefined,\n projects: configCache.projects ? [...configCache.projects] : undefined,\n }\n }\n if (!existsSync(CONFIG_PATH))\n return {}\n\n const content = readFileSync(CONFIG_PATH, 'utf-8')\n const config: SkilldConfig = {}\n let inBlock: 'projects' | 'features' | null = null\n const projects: string[] = []\n const features: Partial<FeaturesConfig> = {}\n\n for (const line of content.split('\\n')) {\n if (line.startsWith('projects:')) {\n inBlock = 'projects'\n continue\n }\n if (line.startsWith('features:')) {\n inBlock = 'features'\n continue\n }\n if (inBlock === 'projects') {\n if (line.startsWith(' - ')) {\n projects.push(yamlUnescape(line.slice(4)))\n continue\n }\n inBlock = null\n }\n if (inBlock === 'features') {\n const m = line.match(/^ {2}(\\w+):\\s*(.+)/)\n if (m) {\n const key = m[1] as keyof FeaturesConfig\n if (key in defaultFeatures)\n features[key] = m[2] === 'true'\n continue\n }\n inBlock = null\n }\n const kv = yamlParseKV(line)\n if (!kv)\n continue\n const [key, value] = kv\n if (key === 'model' && value)\n config.model = value as OptimizeModel\n if (key === 'agent' && value)\n config.agent = value\n if (key === 'skipLlm')\n config.skipLlm = value === 'true'\n }\n\n if (projects.length > 0)\n config.projects = projects\n if (Object.keys(features).length > 0)\n config.features = { ...defaultFeatures, ...features }\n configCache = config\n return config\n}\n\nexport function writeConfig(config: SkilldConfig): void {\n mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 })\n\n let yaml = ''\n if (config.model)\n yaml += `model: ${config.model}\\n`\n if (config.agent)\n yaml += `agent: ${config.agent}\\n`\n if (config.skipLlm)\n yaml += `skipLlm: true\\n`\n if (config.features) {\n yaml += 'features:\\n'\n for (const [k, v] of Object.entries(config.features)) {\n yaml += ` ${k}: ${v}\\n`\n }\n }\n if (config.projects?.length) {\n yaml += 'projects:\\n'\n for (const p of config.projects) {\n yaml += ` - ${yamlEscape(p)}\\n`\n }\n }\n\n writeFileSync(CONFIG_PATH, yaml, { mode: 0o600 })\n configCache = undefined\n}\n\nexport function updateConfig(updates: Partial<SkilldConfig>): void {\n const config = readConfig()\n writeConfig({ ...config, ...updates })\n}\n\nexport function registerProject(projectPath: string): void {\n const config = readConfig()\n const projects = new Set(config.projects || [])\n projects.add(projectPath)\n writeConfig({ ...config, projects: [...projects] })\n}\n\nexport function unregisterProject(projectPath: string): void {\n const config = readConfig()\n const projects = (config.projects || []).filter(p => p !== projectPath)\n writeConfig({ ...config, projects })\n}\n\nexport function getRegisteredProjects(): string[] {\n return readConfig().projects || []\n}\n","import { readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n// Walk up from current file to find package.json (works in both src/ and dist/_chunks/)\nfunction findPackageJson(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (let i = 0; i < 5; i++) {\n const candidate = resolve(dir, 'package.json')\n try {\n const content = readFileSync(candidate, 'utf8')\n if (content)\n return content\n }\n catch {}\n dir = resolve(dir, '..')\n }\n return '{\"version\":\"0.0.0\"}'\n}\n\nexport const version: string = JSON.parse(findPackageJson()).version\n","/**\n * Shared CLI helpers used by subcommand definitions and the main CLI entry.\n * Extracted to avoid circular deps between cli.ts and commands/*.ts.\n */\n\nimport type { AgentType, OptimizeModel } from './agent/index.ts'\nimport type { ProjectState } from './core/skills.ts'\nimport * as p from '@clack/prompts'\nimport { parseTree } from 'jsonc-parser'\nimport { join } from 'pathe'\nimport { detectCurrentAgent } from 'unagent/env'\nimport { agents, detectInstalledAgents, detectProjectAgents, detectTargetAgent, getAgentVersion, getModelName } from './agent/index.ts'\nimport { readConfig, updateConfig } from './core/config.ts'\nimport { editJsonProperty, patchPackageJson, readPackageJsonSafe } from './core/package-json.ts'\nimport { version } from './version.ts'\n\nexport type { AgentType, OptimizeModel }\n\nexport interface IntroOptions {\n state: ProjectState\n /** Installed CLIs that can serve as enhancement models */\n generators?: Array<{ name: string, version: string }>\n /** Configured enhancement model ID */\n modelId?: string\n /** Resolved target agent ID */\n agentId?: string\n}\n\nexport const sharedArgs = {\n global: {\n type: 'boolean' as const,\n alias: 'g',\n description: 'Install globally to ~/<agent>/skills',\n default: false,\n },\n agent: {\n type: 'enum' as const,\n options: Object.keys(agents),\n alias: 'a',\n description: 'Target agent — where skills are installed',\n },\n model: {\n type: 'string' as const,\n alias: 'm',\n description: 'Enhancement model for SKILL.md generation',\n valueHint: 'id',\n },\n yes: {\n type: 'boolean' as const,\n alias: 'y',\n description: 'Skip prompts, use defaults',\n default: false,\n },\n force: {\n type: 'boolean' as const,\n alias: 'f',\n description: 'Ignore all caches, re-fetch docs and regenerate',\n default: false,\n },\n debug: {\n type: 'boolean' as const,\n description: 'Save raw enhancement output to logs/ for each section',\n default: false,\n },\n}\n\n// ── Menu loop utility ─────────────────────────────────────────────────\n\n/** Thrown when a clack prompt is cancelled inside a menuLoop handler */\nexport class MenuCancel extends Error { override name = 'MenuCancel' }\n\n/** Assert a clack prompt result is not cancelled. Throws MenuCancel if cancelled. */\nexport function guard<T>(value: T | symbol): T {\n if (p.isCancel(value))\n throw new MenuCancel()\n return value as T\n}\n\nexport interface MenuOption {\n label: string\n value: string\n hint?: string\n}\n\n/**\n * Run a select menu in a loop with automatic back-navigation.\n *\n * - Cancel (Escape) at the menu itself → exits (returns)\n * - Cancel inside a handler (via guard()) → caught, loops back to menu\n * - Handler returns truthy → exits (returns)\n * - Handler returns void/false → loops back to menu\n *\n * Options are rebuilt each iteration so hints stay fresh after changes.\n */\nexport async function menuLoop(opts: {\n message: string\n options: () => MenuOption[] | Promise<MenuOption[]>\n onSelect: (value: string) => Promise<boolean | void>\n initialValue?: string | (() => string | undefined)\n /** Use fuzzy-searchable autocomplete instead of static select */\n searchable?: boolean\n}): Promise<void> {\n while (true) {\n const options = await opts.options()\n const initial = typeof opts.initialValue === 'function' ? opts.initialValue() : opts.initialValue\n const choice = opts.searchable\n ? await p.autocomplete({ message: opts.message, options, ...(initial != null ? { initialValue: initial } : {}) })\n : await p.select({ message: opts.message, options, ...(initial != null ? { initialValue: initial } : {}) })\n if (p.isCancel(choice))\n return\n try {\n if (await opts.onSelect(choice as string))\n return\n }\n catch (err) {\n if (err instanceof MenuCancel)\n continue\n throw err\n }\n }\n}\n\n/** Check if we're running inside an AI coding agent */\nexport function isRunningInsideAgent(): boolean {\n return !!detectCurrentAgent()\n}\n\n/** Check if the current environment supports interactive prompts */\nexport function isInteractive(): boolean {\n if (isRunningInsideAgent())\n return false\n if (process.env.CI)\n return false\n if (!process.stdout.isTTY)\n return false\n return true\n}\n\n/** Exit with error if interactive terminal is required but unavailable */\nexport function requireInteractive(command: string): void {\n if (!isInteractive()) {\n console.error(`Error: \\`skilld ${command}\\` requires an interactive terminal`)\n process.exit(1)\n }\n}\n\n/** Resolve agent from flags/cwd/config. cwd is source of truth over config. */\nexport function resolveAgent(agentFlag?: string): AgentType | 'none' | null {\n if (process.env.SKILLD_NO_AGENT)\n return null\n return (agentFlag as AgentType | undefined)\n ?? detectTargetAgent()\n ?? (readConfig().agent as AgentType | undefined)\n ?? null\n}\n\nlet _warnedNoAgent = false\nfunction warnNoAgent(): void {\n if (_warnedNoAgent)\n return\n _warnedNoAgent = true\n p.log.warn('No target agent detected — falling back to prompt-only mode.\\n Use --agent <name> to specify, or run `skilld config` to set a default.')\n}\n\n/** Prompt user to pick an agent when auto-detection fails */\nexport async function promptForAgent(): Promise<AgentType | 'none' | null> {\n const noAgent = !!process.env.SKILLD_NO_AGENT\n const installed = noAgent ? [] : detectInstalledAgents()\n const projectMatches = noAgent ? [] : detectProjectAgents()\n\n // Non-interactive: auto-select sole installed agent or fall back to prompt-only\n if (!isInteractive()) {\n if (installed.length === 1) {\n updateConfig({ agent: installed[0] })\n return installed[0]!\n }\n warnNoAgent()\n return 'none'\n }\n\n // Brief context before asking about agents\n p.log.info(\n `Skilld generates reference cards from package docs so your AI agent\\n`\n + ` always has accurate APIs for your exact dependency versions.`,\n )\n\n // Build options: prefer project-matched agents, then installed, then all\n const candidateIds = projectMatches.length > 0\n ? projectMatches\n : installed.length > 0\n ? installed\n : Object.keys(agents) as AgentType[]\n\n // Agents that also read .claude/skills/\n const sharedAgents = new Set(\n Object.entries(agents)\n .filter(([, a]) => a.additionalSkillsDirs.some(d => d.includes('.claude/skills')))\n .map(([id]) => id),\n )\n\n // Group: agents that share skills vs agents with their own directory\n const sharedIds = candidateIds.filter(id => id === 'claude-code' || sharedAgents.has(id))\n const isolatedIds = candidateIds.filter(id => id !== 'claude-code' && !sharedAgents.has(id))\n\n const options: Array<{ label: string, value: AgentType | 'none', hint?: string }> = []\n\n // Show shared-compatible agents first\n if (sharedIds.length > 0 && isolatedIds.length > 0) {\n for (const id of sharedIds) {\n const a = agents[id]\n const hint = id === 'claude-code'\n ? `skills shared with ${sharedIds.length - 1} other agents`\n : `skills shared with Claude Code and others`\n options.push({ label: a.displayName, value: id as AgentType, hint })\n }\n }\n\n // Agents with isolated skill dirs\n const isolatedAgentIds = new Set(\n Object.entries(agents)\n .filter(([, a]) => a.additionalSkillsDirs.length === 0)\n .map(([id]) => id),\n )\n\n for (const id of (sharedIds.length > 0 && isolatedIds.length > 0 ? isolatedIds : candidateIds)) {\n if (options.some(o => o.value === id))\n continue\n const a = agents[id]\n const hint = sharedAgents.has(id) && id !== 'claude-code'\n ? 'skills shared with Claude Code and others'\n : isolatedAgentIds.has(id)\n ? 'skills only visible to this agent'\n : undefined\n options.push({ label: a.displayName, value: id as AgentType, hint })\n }\n\n options.push({ label: 'No agent', value: 'none', hint: 'export as standalone files for any AI' })\n\n if (!_warnedNoAgent) {\n _warnedNoAgent = true\n const hint = projectMatches.length > 1\n ? `Multiple agent directories found: ${projectMatches.map(t => agents[t].displayName).join(', ')}`\n : installed.length > 0\n ? `Found ${installed.map(t => agents[t].displayName).join(', ')} but couldn't determine which to use`\n : 'No agents auto-detected'\n const crossNote = sharedIds.length > 1\n ? `\\n \\x1B[90mTip: Picking Claude Code shares skills with ${sharedIds.filter(id => id !== 'claude-code').map(id => agents[id].displayName).join(', ')} automatically.\\x1B[0m`\n : ''\n p.log.warn(`${hint}\\n Pick the agent you actively code with.${crossNote}`)\n }\n\n const choice = await p.select({\n message: 'Which AI coding agent do you use?',\n options,\n })\n\n if (p.isCancel(choice))\n return null\n\n if (choice === 'none')\n return 'none'\n\n // Save as default so they don't get asked again\n updateConfig({ agent: choice })\n p.log.success(`Target agent set to ${agents[choice].displayName}`)\n return choice\n}\n\n/** Get installed LLM generators with working CLIs (verified via --version) */\nexport function getInstalledGenerators(): Array<{ name: string, version: string }> {\n const installed = detectInstalledAgents()\n return installed\n .filter(id => agents[id].cli)\n .map((id) => {\n const ver = getAgentVersion(id)\n return ver ? { name: agents[id].displayName, version: ver } : null\n })\n .filter((a): a is { name: string, version: string } => a !== null)\n}\n\nexport function relativeTime(date: Date): string {\n const now = Date.now()\n const diff = now - date.getTime()\n const mins = Math.floor(diff / 60000)\n const hours = Math.floor(diff / 3600000)\n const days = Math.floor(diff / 86400000)\n if (mins < 1)\n return 'just now'\n if (mins < 60)\n return `${mins}m ago`\n if (hours < 24)\n return `${hours}h ago`\n return `${days}d ago`\n}\n\nexport function getLastSynced(state: ProjectState): string | null {\n let latest: Date | null = null\n for (const skill of state.skills) {\n if (skill.info?.syncedAt) {\n const d = new Date(skill.info.syncedAt)\n if (!latest || d > latest)\n latest = d\n }\n }\n return latest ? relativeTime(latest) : null\n}\n\nexport function introLine({ state, generators, modelId, agentId }: IntroOptions): string {\n const name = '\\x1B[1m\\x1B[35mskilld\\x1B[0m'\n const ver = `\\x1B[90mv${version}\\x1B[0m`\n const lastSynced = getLastSynced(state)\n const synced = lastSynced ? ` · \\x1B[90msynced ${lastSynced}\\x1B[0m` : ''\n\n // Status line: enhancement model → target agent\n const parts: string[] = []\n if (modelId)\n parts.push(getModelName(modelId as any))\n else if (generators?.length)\n parts.push(generators.map(g => `${g.name} v${g.version}`).join(', '))\n if (agentId && agents[agentId as AgentType])\n parts.push(agents[agentId as AgentType].displayName)\n const statusLine = parts.length > 0\n ? `\\n\\x1B[90m↳ ${parts.join(' → ')}\\x1B[0m`\n : ''\n\n return `${name} ${ver}${synced}${statusLine}`\n}\n\nexport function formatStatus(synced: number, outdated: number): string {\n const parts: string[] = []\n if (synced > 0)\n parts.push(`\\x1B[32m${synced} synced\\x1B[0m`)\n if (outdated > 0)\n parts.push(`\\x1B[33m${outdated} outdated\\x1B[0m`)\n return `Skills: ${parts.join(' · ')}`\n}\n\n// ── Shared UI constants ───────────────────────────────────────────────\n\nexport const OAUTH_NOTE\n = '\\x1B[33m⚠\\x1B[0m OAuth providers are disabled.\\n'\n + '\\n'\n + 'Consumer subscription OAuth impersonates official CLI clients and\\n'\n + 'violates provider Terms of Service, risking account bans.\\n'\n + '\\n'\n + 'Use API keys or native CLI tools instead:\\n'\n + ' \\x1B[36mANTHROPIC_API_KEY\\x1B[0m / \\x1B[36mclaude\\x1B[0m CLI\\n'\n + ' \\x1B[36mOPENAI_API_KEY\\x1B[0m / \\x1B[36mcodex\\x1B[0m CLI\\n'\n + ' \\x1B[36mGEMINI_API_KEY\\x1B[0m / \\x1B[36mgemini\\x1B[0m CLI'\n\nexport const NO_MODELS_MESSAGE = 'No enhancement models detected.\\n'\n + ' \\x1B[90mSkills work fine without this, you get raw docs, issues, and types.\\n'\n + ' Enhancement compresses them into a concise cheat sheet with gotchas.\\x1B[0m\\n'\n + '\\n'\n + ' To connect a model (optional):\\n'\n + ' 1. Set an env var: ANTHROPIC_API_KEY, GEMINI_API_KEY, or OPENAI_API_KEY\\n'\n + ' 2. Install a CLI tool: \\x1B[36mclaude\\x1B[0m, \\x1B[36mgemini\\x1B[0m, or \\x1B[36mcodex\\x1B[0m (restart wizard after)'\n\n/** Group models by vendor for provider→model selection. Uses vendorGroup to merge CLI and API entries under one heading. */\nexport function groupModelsByProvider<T extends { provider: string, providerName: string, vendorGroup?: string }>(models: T[]): Map<string, { name: string, models: T[] }> {\n const byVendor = new Map<string, { name: string, models: T[] }>()\n for (const m of models) {\n const key = m.vendorGroup ?? m.provider\n if (!byVendor.has(key))\n byVendor.set(key, { name: key, models: [] })\n byVendor.get(key)!.models.push(m)\n }\n return byVendor\n}\n\nexport interface ModelPickerOptions {\n /** Extra options prepended (e.g. Auto, Connect OAuth) */\n before?: Array<{ label: string, value: string, hint?: string }>\n /** Extra options appended (e.g. Skip) */\n after?: Array<{ label: string, value: string, hint?: string }>\n}\n\n/**\n * Smart provider→model picker. Skips the provider step when there's only 1 provider.\n * Returns the selected model value, or a sentinel string from before/after options.\n */\nexport async function pickModel<T extends { provider: string, providerName: string, name: string, id: string, hint: string, recommended?: boolean }>(\n models: T[],\n opts: ModelPickerOptions = {},\n): Promise<string | null> {\n const byProvider = groupModelsByProvider(models)\n const before = opts.before ?? []\n const after = opts.after ?? []\n\n // Single provider → skip provider step, show models directly\n if (byProvider.size === 1 && before.length === 0) {\n const [, group] = [...byProvider.entries()][0]!\n const choice = await p.select({\n message: `${group.name}`,\n options: [\n ...group.models.map(m => ({\n label: m.recommended ? `${m.name} (recommended - fast and cheap)` : m.name,\n value: m.id,\n hint: m.hint,\n })),\n ...after,\n ],\n })\n return p.isCancel(choice) ? null : choice as string\n }\n\n // Multiple providers or has before options - two-step\n const providerChoice = await p.select({\n message: 'Select provider',\n options: [\n ...before,\n ...Array.from(byProvider.entries(), ([key, { name, models: ms }]) => ({\n label: name,\n value: key,\n hint: `${ms.length} models`,\n })),\n ...after,\n ],\n })\n\n if (p.isCancel(providerChoice))\n return null\n\n // Check if it's a sentinel from before/after\n const providerStr = providerChoice as string\n if (before.some(o => o.value === providerStr) || after.some(o => o.value === providerStr))\n return providerStr\n\n // Drill into provider's models\n const group = byProvider.get(providerStr)!\n const modelChoice = await p.select({\n message: `Select model (${group.name})`,\n options: group.models.map(m => ({\n label: m.recommended ? `${m.name} (recommended - fast and cheap)` : m.name,\n value: m.id,\n hint: m.hint,\n })),\n })\n\n return p.isCancel(modelChoice) ? null : modelChoice as string\n}\n\n/**\n * Check if the prepare hook is already installed in package.json.\n */\nexport function hasPrepareHook(cwd: string = process.cwd()): boolean {\n const pkg = readPackageJsonSafe(join(cwd, 'package.json'))\n if (!pkg)\n return true // no package.json means nothing to suggest\n const existing = (pkg.parsed.scripts as Record<string, unknown> | undefined)?.prepare\n return typeof existing === 'string' && existing.includes('skilld')\n}\n\n/**\n * Prompt to add `skilld prepare` to package.json \"prepare\" script.\n * In non-interactive environments, falls back to an info log.\n * Returns true if the hook was added or already present.\n */\nexport async function suggestPrepareHook(cwd: string = process.cwd()): Promise<boolean> {\n const pkgJsonPath = join(cwd, 'package.json')\n const pkg = readPackageJsonSafe(pkgJsonPath)\n if (!pkg)\n return false\n\n const rawExisting = (pkg.parsed.scripts as Record<string, unknown> | undefined)?.prepare\n const existing: string | undefined = typeof rawExisting === 'string' ? rawExisting : undefined\n\n if (existing?.includes('skilld'))\n return true\n\n const prepareCmd = buildPrepareScript(existing, cwd)\n\n if (!isInteractive()) {\n p.log.info(\n `\\x1B[90mAdd to package.json scripts:\\n`\n + ` \\x1B[36m\"prepare\": \"${prepareCmd}\"\\x1B[0m\\n`\n + ` \\x1B[90mRestores references and shipped skills on install.\\x1B[0m`,\n )\n return false\n }\n\n const confirmed = await p.confirm({\n message: `Add \\x1B[36m\"prepare\": \"${prepareCmd}\"\\x1B[0m to package.json?`,\n initialValue: true,\n })\n if (p.isCancel(confirmed) || !confirmed)\n return false\n\n patchPackageJson(pkgJsonPath, (content) => {\n const tree = parseTree(content)\n const hasScripts = tree?.children?.some(c =>\n c.type === 'property' && c.children?.[0]?.value === 'scripts',\n )\n\n let patched = content\n if (!hasScripts)\n patched = editJsonProperty(patched, ['scripts'], {})\n\n return editJsonProperty(patched, ['scripts', 'prepare'], prepareCmd)\n })\n p.log.success('Added \\x1B[36mskilld prepare\\x1B[0m to package.json')\n return true\n}\n\n/**\n * Build the full prepare script value, safely appending to any existing command.\n */\nexport function buildPrepareScript(existing: string | undefined, cwd: string = process.cwd()): string {\n const bin = isNpxExecution() && !isSkilldDep(cwd) ? 'npx skilld' : 'skilld'\n const cmd = `${bin} prepare || true`\n if (!existing || !existing.trim())\n return cmd\n\n const trimmed = existing.trim()\n\n // Strip trailing && or ; that would leave a dangling operator\n const cleaned = trimmed.replace(/[&|;]+\\s*$/, '').trim()\n if (!cleaned)\n return cmd\n\n return `${cleaned} && (${cmd})`\n}\n\n/**\n * Detect if the current process was launched via npx, pnpm dlx, or similar one-shot runners.\n */\nfunction isNpxExecution(): boolean {\n // npm/pnpm set npm_command=exec when running via npx/dlx\n if (process.env.npm_command === 'exec')\n return true\n // Fallback: check if the resolved binary path contains npx or dlx cache dirs\n const execPath = process.env._ || ''\n return /npx|\\.store|dlx/.test(execPath)\n}\n\n/**\n * Check if skilld is listed as a dependency (dev or regular) in the project's package.json.\n */\nfunction isSkilldDep(cwd: string): boolean {\n const pkg = readPackageJsonSafe(join(cwd, 'package.json'))\n if (!pkg)\n return false\n const deps = pkg.parsed as Record<string, any>\n return !!(deps.dependencies?.skilld || deps.devDependencies?.skilld)\n}\n\nexport function getRepoHint(name: string, cwd: string): string | undefined {\n const result = readPackageJsonSafe(join(cwd, 'node_modules', name, 'package.json'))\n if (!result)\n return undefined\n const pkg = result.parsed as Record<string, any>\n const url = typeof pkg.repository === 'string'\n ? pkg.repository\n : pkg.repository?.url\n if (!url)\n return undefined\n return url\n .replace(/^git\\+/, '')\n .replace(/\\.git$/, '')\n .replace(/^git:\\/\\//, 'https://')\n .replace(/^ssh:\\/\\/git@github\\.com/, 'https://github.com')\n .replace(/^https?:\\/\\/(www\\.)?github\\.com\\//, '')\n}\n"],"mappings":"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"}
@@ -73,7 +73,7 @@ every session - no hallucinated APIs.
73
73
  discussions: selected.includes("discussions"),
74
74
  releases: selected.includes("releases")
75
75
  };
76
- p.note("An LLM can optionally summarize raw docs into a focused reference\nhighlighting best practices, gotchas, and migrations.\n\n\x1B[1mWithout LLM:\x1B[0m ~2 KB skill with package metadata, types, and links\n\x1B[1mWith LLM:\x1B[0m ~5 KB skill with curated gotchas, patterns, and migration notes\n\n\x1B[1mThis is a one-time build step\x1B[0m - it generates the SKILL.md, then your\ncoding agent reads the result every session. Can be a different model.\n\n\x1B[90mWorks with API keys, existing subscriptions (Claude Pro, ChatGPT Plus,\nCopilot, Gemini) via OAuth, or CLI tools (claude, gemini, codex).\x1B[0m", "Enhancement model (optional)");
76
+ p.note("An LLM can optionally summarize raw docs into a focused reference\nhighlighting best practices, gotchas, and migrations.\n\n\x1B[1mWithout LLM:\x1B[0m ~2 KB skill with package metadata, types, and links\n\x1B[1mWith LLM:\x1B[0m ~5 KB skill with curated gotchas, patterns, and migration notes\n\n\x1B[1mThis is a one-time build step\x1B[0m - it generates the SKILL.md, then your\ncoding agent reads the result every session. Can be a different model.\n\n\x1B[90mWorks with API keys (ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY)\nor CLI tools (claude, gemini, codex).\x1B[0m", "Enhancement model (optional)");
77
77
  let modelId;
78
78
  let skippedEnhancement = false;
79
79
  let oauthJustConnected = false;
@@ -91,21 +91,26 @@ every session - no hallucinated APIs.
91
91
  }
92
92
  if (providers.size > 0) p.log.success(`Found: ${[...providers].join(", ")}`);
93
93
  }
94
+ const afterOptions = getOAuthProviderList().length > 0 ? [{
95
+ label: "⚠ Connect OAuth provider...",
96
+ value: "_connect",
97
+ hint: "may violate provider ToS"
98
+ }, {
99
+ label: "Skip enhancement",
100
+ value: "_skip",
101
+ hint: "base skill with docs, issues, and types, add LLM later via `skilld config`"
102
+ }] : [{
103
+ label: "Skip enhancement",
104
+ value: "_skip",
105
+ hint: "base skill with docs, issues, and types, add LLM later via `skilld config`"
106
+ }];
94
107
  const choice = await pickModel(allModels, {
95
108
  before: allModels.length > 0 ? [{
96
109
  label: "Auto",
97
110
  value: "_auto",
98
111
  hint: "picks best available model from connected providers"
99
112
  }] : [],
100
- after: [{
101
- label: "Connect OAuth provider...",
102
- value: "_connect",
103
- hint: "use existing Claude Pro, ChatGPT Plus, etc."
104
- }, {
105
- label: "Skip enhancement",
106
- value: "_skip",
107
- hint: "base skill with docs, issues, and types - add LLM later via `skilld config`"
108
- }]
113
+ after: afterOptions
109
114
  });
110
115
  if (choice === null) {
111
116
  p.cancel("Setup cancelled");
@@ -1 +1 @@
1
- {"version":3,"file":"wizard.mjs","names":["agents"],"sources":["../../src/commands/wizard.ts"],"sourcesContent":["import type { AgentType, OptimizeModel } from '../agent/index.ts'\nimport type { FeaturesConfig } from '../core/config.ts'\nimport { execSync } from 'node:child_process'\nimport * as p from '@clack/prompts'\nimport { getOAuthProviderList, loginOAuthProvider } from '../agent/clis/pi-ai.ts'\nimport { agents, getAvailableModels, getModelName } from '../agent/index.ts'\nimport { isInteractive, NO_MODELS_MESSAGE, OAUTH_NOTE, pickModel } from '../cli-helpers.ts'\nimport { defaultFeatures, updateConfig } from '../core/config.ts'\n\nfunction hasGhCli(): boolean {\n if (process.env.SKILLD_NO_GH)\n return false\n try {\n execSync('gh --version', { stdio: 'ignore' })\n return true\n }\n catch {\n return false\n }\n}\n\nexport interface WizardOptions {\n /** Resolved target agent, if known */\n agent?: AgentType\n /** Show next-steps outro when done (default: true) */\n showOutro?: boolean\n}\n\nexport async function runWizard(opts: WizardOptions = {}): Promise<boolean> {\n if (!isInteractive())\n return false\n\n const agentLabel = opts.agent ? agents[opts.agent].displayName : null\n const skillsDir = opts.agent ? agents[opts.agent].skillsDir : '.claude/skills'\n const agentLine = agentLabel\n ? `\\n\\x1B[90mTarget agent: ${agentLabel}\\x1B[0m`\n : ''\n\n p.note(\n `Your AI agent reads docs from its training data - but APIs change,\\n`\n + `versions drift, and patterns go stale. Skilld fixes this.\\n`\n + `\\n`\n + `It generates a \\x1B[1mSKILL.md\\x1B[0m - a markdown reference card built from\\n`\n + `the \\x1B[1mactual docs, issues, and release notes\\x1B[0m for the exact\\n`\n + `package versions in your project. Your agent reads this file\\n`\n + `every session - no hallucinated APIs.\\n`\n + `\\n`\n + `\\x1B[1mHow it works:\\x1B[0m\\n`\n + ` 1. Fetch docs, issues, and types for your packages\\n`\n + ` 2. Optionally compress with an LLM into a concise cheat sheet\\n`\n + `\\n`\n + `\\x1B[90mExample: \\`skilld add vue\\` creates ${skillsDir}/vue-skilld/SKILL.md\\n`\n + `Your agent then knows the right APIs, gotchas, and patterns\\n`\n + `for your exact version.\\x1B[0m${\n agentLine}`,\n 'Welcome to skilld',\n )\n\n const ghInstalled = hasGhCli()\n\n if (ghInstalled) {\n p.log.success(\n 'GitHub CLI detected — will use it to pull issues and discussions.',\n )\n }\n else {\n p.log.info(\n '\\x1B[90mGitHub CLI not installed — issues and discussions disabled.\\n'\n + ' Install later to enable: \\x1B[36mhttps://cli.github.com\\x1B[0m',\n )\n }\n\n // Feature toggles\n const selected = await p.multiselect({\n message: 'What data sources should skills include?',\n options: [\n { label: 'Local search', value: 'search' as const, hint: 'query engine for `skilld search` across all skill docs' },\n { label: 'Release notes', value: 'releases' as const, hint: 'changelogs and migration notes per version' },\n { label: 'GitHub issues', value: 'issues' as const, hint: 'common bugs, workarounds, and solutions', disabled: !ghInstalled },\n { label: 'GitHub discussions', value: 'discussions' as const, hint: 'community Q&A and usage examples', disabled: !ghInstalled },\n ],\n initialValues: [\n ...Object.entries(defaultFeatures)\n .filter(([, v]) => v)\n .map(([k]) => k),\n ...(ghInstalled ? ['issues', 'discussions'] as const : []),\n ] as Array<keyof FeaturesConfig>,\n required: false,\n })\n\n if (p.isCancel(selected)) {\n p.cancel('Setup cancelled')\n return false\n }\n\n const features: FeaturesConfig = {\n search: selected.includes('search'),\n issues: selected.includes('issues'),\n discussions: selected.includes('discussions'),\n releases: selected.includes('releases'),\n }\n\n // Enhancement model - optional, independent of target agent\n p.note(\n 'An LLM can optionally summarize raw docs into a focused reference\\n'\n + 'highlighting best practices, gotchas, and migrations.\\n'\n + '\\n'\n + '\\x1B[1mWithout LLM:\\x1B[0m ~2 KB skill with package metadata, types, and links\\n'\n + '\\x1B[1mWith LLM:\\x1B[0m ~5 KB skill with curated gotchas, patterns, and migration notes\\n'\n + '\\n'\n + '\\x1B[1mThis is a one-time build step\\x1B[0m - it generates the SKILL.md, then your\\n'\n + 'coding agent reads the result every session. Can be a different model.\\n'\n + '\\n'\n + '\\x1B[90mWorks with API keys, existing subscriptions (Claude Pro, ChatGPT Plus,\\n'\n + 'Copilot, Gemini) via OAuth, or CLI tools (claude, gemini, codex).\\x1B[0m',\n 'Enhancement model (optional)',\n )\n\n let modelId: OptimizeModel | undefined\n let skippedEnhancement = false\n let oauthJustConnected = false\n\n // Loop so user can connect OAuth then come back to pick a model\n while (true) {\n const allModels = process.env.SKILLD_NO_AGENTS ? [] : await getAvailableModels()\n\n if (allModels.length === 0) {\n p.log.warn(NO_MODELS_MESSAGE)\n }\n else if (oauthJustConnected) {\n p.log.step(`${allModels.length} models now available. Select one below.`)\n }\n else {\n // Show which providers were found by name (e.g. \"Anthropic via CLI, OpenAI via API key\")\n const providers = new Set<string>()\n for (const m of allModels) {\n const vendor = m.vendorGroup ?? m.providerName\n if (!m.id.startsWith('pi:'))\n providers.add(`${vendor} via CLI`)\n else if (m.hint?.includes('API key'))\n providers.add(`${vendor} via API key`)\n else if (m.hint?.includes('OAuth'))\n providers.add(`${vendor} via OAuth`)\n }\n if (providers.size > 0)\n p.log.success(`Found: ${[...providers].join(', ')}`)\n }\n\n const choice = await pickModel(allModels, {\n before: allModels.length > 0\n ? [{ label: 'Auto', value: '_auto', hint: 'picks best available model from connected providers' }]\n : [],\n after: [\n { label: 'Connect OAuth provider...', value: '_connect', hint: 'use existing Claude Pro, ChatGPT Plus, etc.' },\n { label: 'Skip enhancement', value: '_skip', hint: 'base skill with docs, issues, and types - add LLM later via `skilld config`' },\n ],\n })\n\n if (choice === null) {\n p.cancel('Setup cancelled')\n return false\n }\n\n if (choice === '_connect') {\n await wizardConnectProvider()\n oauthJustConnected = true\n continue\n }\n\n if (choice === '_skip') {\n skippedEnhancement = true\n break\n }\n if (choice === '_auto')\n break\n\n modelId = choice as OptimizeModel\n break\n }\n\n updateConfig({\n features,\n ...(modelId\n ? { model: modelId, skipLlm: false }\n : { model: undefined, skipLlm: skippedEnhancement }),\n })\n\n // Summary of what was saved\n const modelSummary = modelId\n ? getModelName(modelId)\n : skippedEnhancement\n ? 'none (raw docs)'\n : 'auto'\n const featureList = Object.entries(features).filter(([, v]) => v).map(([k]) => k).join(', ') || 'none'\n p.log.success(`Model: ${modelSummary} · Features: ${featureList}`)\n\n if (opts.showOutro !== false) {\n p.note(\n 'Run \\x1B[36mskilld add <pkg>\\x1B[0m to generate skills for specific packages\\n'\n + 'Run \\x1B[36mskilld\\x1B[0m to scan your project and pick packages interactively\\n'\n + 'Run \\x1B[36mskilld config\\x1B[0m to change settings later',\n 'Setup complete',\n )\n }\n return true\n}\n\nasync function wizardConnectProvider(): Promise<void> {\n p.note(OAUTH_NOTE, 'How OAuth works')\n\n const providers = getOAuthProviderList()\n const provider = await p.select({\n message: 'Connect provider',\n options: providers.map(pr => ({\n label: pr.name,\n value: pr.id,\n hint: pr.loggedIn ? 'connected' : undefined,\n })),\n })\n\n if (p.isCancel(provider))\n return\n\n const spinner = p.spinner()\n spinner.start('Connecting...')\n\n const success = await loginOAuthProvider(provider as string, {\n onAuth: (url, instructions) => {\n spinner.stop('Open this URL in your browser:')\n p.log.info(` \\x1B[36m${url}\\x1B[0m`)\n if (instructions)\n p.log.info(` \\x1B[90m${instructions}\\x1B[0m`)\n spinner.start('Waiting for authentication...')\n },\n onPrompt: async (message, placeholder) => {\n const value = await p.text({ message, placeholder })\n if (p.isCancel(value))\n return ''\n return value as string\n },\n onProgress: msg => p.log.step(msg),\n }).catch((err: Error) => {\n spinner.stop(`Login failed: ${err.message}`)\n return false\n })\n\n spinner.stop()\n\n if (success) {\n const name = providers.find(pr => pr.id === provider)?.name ?? provider\n p.log.success(`Connected to ${name}`)\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"wizard.mjs","names":["agents"],"sources":["../../src/commands/wizard.ts"],"sourcesContent":["import type { AgentType, OptimizeModel } from '../agent/index.ts'\nimport type { FeaturesConfig } from '../core/config.ts'\nimport { execSync } from 'node:child_process'\nimport * as p from '@clack/prompts'\nimport { getOAuthProviderList, loginOAuthProvider } from '../agent/clis/pi-ai.ts'\nimport { agents, getAvailableModels, getModelName } from '../agent/index.ts'\nimport { isInteractive, NO_MODELS_MESSAGE, OAUTH_NOTE, pickModel } from '../cli-helpers.ts'\nimport { defaultFeatures, updateConfig } from '../core/config.ts'\n\nfunction hasGhCli(): boolean {\n if (process.env.SKILLD_NO_GH)\n return false\n try {\n execSync('gh --version', { stdio: 'ignore' })\n return true\n }\n catch {\n return false\n }\n}\n\nexport interface WizardOptions {\n /** Resolved target agent, if known */\n agent?: AgentType\n /** Show next-steps outro when done (default: true) */\n showOutro?: boolean\n}\n\nexport async function runWizard(opts: WizardOptions = {}): Promise<boolean> {\n if (!isInteractive())\n return false\n\n const agentLabel = opts.agent ? agents[opts.agent].displayName : null\n const skillsDir = opts.agent ? agents[opts.agent].skillsDir : '.claude/skills'\n const agentLine = agentLabel\n ? `\\n\\x1B[90mTarget agent: ${agentLabel}\\x1B[0m`\n : ''\n\n p.note(\n `Your AI agent reads docs from its training data - but APIs change,\\n`\n + `versions drift, and patterns go stale. Skilld fixes this.\\n`\n + `\\n`\n + `It generates a \\x1B[1mSKILL.md\\x1B[0m - a markdown reference card built from\\n`\n + `the \\x1B[1mactual docs, issues, and release notes\\x1B[0m for the exact\\n`\n + `package versions in your project. Your agent reads this file\\n`\n + `every session - no hallucinated APIs.\\n`\n + `\\n`\n + `\\x1B[1mHow it works:\\x1B[0m\\n`\n + ` 1. Fetch docs, issues, and types for your packages\\n`\n + ` 2. Optionally compress with an LLM into a concise cheat sheet\\n`\n + `\\n`\n + `\\x1B[90mExample: \\`skilld add vue\\` creates ${skillsDir}/vue-skilld/SKILL.md\\n`\n + `Your agent then knows the right APIs, gotchas, and patterns\\n`\n + `for your exact version.\\x1B[0m${\n agentLine}`,\n 'Welcome to skilld',\n )\n\n const ghInstalled = hasGhCli()\n\n if (ghInstalled) {\n p.log.success(\n 'GitHub CLI detected — will use it to pull issues and discussions.',\n )\n }\n else {\n p.log.info(\n '\\x1B[90mGitHub CLI not installed — issues and discussions disabled.\\n'\n + ' Install later to enable: \\x1B[36mhttps://cli.github.com\\x1B[0m',\n )\n }\n\n // Feature toggles\n const selected = await p.multiselect({\n message: 'What data sources should skills include?',\n options: [\n { label: 'Local search', value: 'search' as const, hint: 'query engine for `skilld search` across all skill docs' },\n { label: 'Release notes', value: 'releases' as const, hint: 'changelogs and migration notes per version' },\n { label: 'GitHub issues', value: 'issues' as const, hint: 'common bugs, workarounds, and solutions', disabled: !ghInstalled },\n { label: 'GitHub discussions', value: 'discussions' as const, hint: 'community Q&A and usage examples', disabled: !ghInstalled },\n ],\n initialValues: [\n ...Object.entries(defaultFeatures)\n .filter(([, v]) => v)\n .map(([k]) => k),\n ...(ghInstalled ? ['issues', 'discussions'] as const : []),\n ] as Array<keyof FeaturesConfig>,\n required: false,\n })\n\n if (p.isCancel(selected)) {\n p.cancel('Setup cancelled')\n return false\n }\n\n const features: FeaturesConfig = {\n search: selected.includes('search'),\n issues: selected.includes('issues'),\n discussions: selected.includes('discussions'),\n releases: selected.includes('releases'),\n }\n\n // Enhancement model - optional, independent of target agent\n p.note(\n 'An LLM can optionally summarize raw docs into a focused reference\\n'\n + 'highlighting best practices, gotchas, and migrations.\\n'\n + '\\n'\n + '\\x1B[1mWithout LLM:\\x1B[0m ~2 KB skill with package metadata, types, and links\\n'\n + '\\x1B[1mWith LLM:\\x1B[0m ~5 KB skill with curated gotchas, patterns, and migration notes\\n'\n + '\\n'\n + '\\x1B[1mThis is a one-time build step\\x1B[0m - it generates the SKILL.md, then your\\n'\n + 'coding agent reads the result every session. Can be a different model.\\n'\n + '\\n'\n + '\\x1B[90mWorks with API keys (ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY)\\n'\n + 'or CLI tools (claude, gemini, codex).\\x1B[0m',\n 'Enhancement model (optional)',\n )\n\n let modelId: OptimizeModel | undefined\n let skippedEnhancement = false\n let oauthJustConnected = false\n\n // Loop so user can connect OAuth then come back to pick a model\n while (true) {\n const allModels = process.env.SKILLD_NO_AGENTS ? [] : await getAvailableModels()\n\n if (allModels.length === 0) {\n p.log.warn(NO_MODELS_MESSAGE)\n }\n else if (oauthJustConnected) {\n p.log.step(`${allModels.length} models now available. Select one below.`)\n }\n else {\n // Show which providers were found by name (e.g. \"Anthropic via CLI, OpenAI via API key\")\n const providers = new Set<string>()\n for (const m of allModels) {\n const vendor = m.vendorGroup ?? m.providerName\n if (!m.id.startsWith('pi:'))\n providers.add(`${vendor} via CLI`)\n else if (m.hint?.includes('API key'))\n providers.add(`${vendor} via API key`)\n else if (m.hint?.includes('OAuth'))\n providers.add(`${vendor} via OAuth`)\n }\n if (providers.size > 0)\n p.log.success(`Found: ${[...providers].join(', ')}`)\n }\n\n const oauthProviders = getOAuthProviderList()\n const afterOptions = oauthProviders.length > 0\n ? [\n { label: '⚠ Connect OAuth provider...', value: '_connect', hint: 'may violate provider ToS' },\n { label: 'Skip enhancement', value: '_skip', hint: 'base skill with docs, issues, and types, add LLM later via `skilld config`' },\n ]\n : [\n { label: 'Skip enhancement', value: '_skip', hint: 'base skill with docs, issues, and types, add LLM later via `skilld config`' },\n ]\n\n const choice = await pickModel(allModels, {\n before: allModels.length > 0\n ? [{ label: 'Auto', value: '_auto', hint: 'picks best available model from connected providers' }]\n : [],\n after: afterOptions,\n })\n\n if (choice === null) {\n p.cancel('Setup cancelled')\n return false\n }\n\n if (choice === '_connect') {\n await wizardConnectProvider()\n oauthJustConnected = true\n continue\n }\n\n if (choice === '_skip') {\n skippedEnhancement = true\n break\n }\n if (choice === '_auto')\n break\n\n modelId = choice as OptimizeModel\n break\n }\n\n updateConfig({\n features,\n ...(modelId\n ? { model: modelId, skipLlm: false }\n : { model: undefined, skipLlm: skippedEnhancement }),\n })\n\n // Summary of what was saved\n const modelSummary = modelId\n ? getModelName(modelId)\n : skippedEnhancement\n ? 'none (raw docs)'\n : 'auto'\n const featureList = Object.entries(features).filter(([, v]) => v).map(([k]) => k).join(', ') || 'none'\n p.log.success(`Model: ${modelSummary} · Features: ${featureList}`)\n\n if (opts.showOutro !== false) {\n p.note(\n 'Run \\x1B[36mskilld add <pkg>\\x1B[0m to generate skills for specific packages\\n'\n + 'Run \\x1B[36mskilld\\x1B[0m to scan your project and pick packages interactively\\n'\n + 'Run \\x1B[36mskilld config\\x1B[0m to change settings later',\n 'Setup complete',\n )\n }\n return true\n}\n\nasync function wizardConnectProvider(): Promise<void> {\n p.note(OAUTH_NOTE, 'How OAuth works')\n\n const providers = getOAuthProviderList()\n const provider = await p.select({\n message: 'Connect provider',\n options: providers.map(pr => ({\n label: pr.name,\n value: pr.id,\n hint: pr.loggedIn ? 'connected' : undefined,\n })),\n })\n\n if (p.isCancel(provider))\n return\n\n const spinner = p.spinner()\n spinner.start('Connecting...')\n\n const success = await loginOAuthProvider(provider as string, {\n onAuth: (url, instructions) => {\n spinner.stop('Open this URL in your browser:')\n p.log.info(` \\x1B[36m${url}\\x1B[0m`)\n if (instructions)\n p.log.info(` \\x1B[90m${instructions}\\x1B[0m`)\n spinner.start('Waiting for authentication...')\n },\n onPrompt: async (message, placeholder) => {\n const value = await p.text({ message, placeholder })\n if (p.isCancel(value))\n return ''\n return value as string\n },\n onProgress: msg => p.log.step(msg),\n }).catch((err: Error) => {\n spinner.stop(`Login failed: ${err.message}`)\n return false\n })\n\n spinner.stop()\n\n if (success) {\n const name = providers.find(pr => pr.id === provider)?.name ?? provider\n p.log.success(`Connected to ${name}`)\n }\n}\n"],"mappings":"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"}
package/dist/cli.mjs CHANGED
@@ -38,30 +38,31 @@ async function configCommand() {
38
38
  const features = config.features ?? defaultFeatures;
39
39
  const enabledCount = Object.values(features).filter(Boolean).length;
40
40
  const modelHint = config.skipLlm ? "disabled" : config.model ? getModelName(config.model) : "auto";
41
- const connectedOAuth = getOAuthProviderList().filter((pr) => pr.loggedIn).length;
42
- const oauthHint = connectedOAuth > 0 ? `${connectedOAuth} connected` : "none";
43
- return [
44
- {
45
- label: "Data sources",
46
- value: "features",
47
- hint: `${enabledCount}/4 enabled · issues, releases, search, discussions`
48
- },
49
- {
41
+ const oauthProviders = getOAuthProviderList();
42
+ const options = [{
43
+ label: "Data sources",
44
+ value: "features",
45
+ hint: `${enabledCount}/4 enabled · issues, releases, search, discussions`
46
+ }];
47
+ if (oauthProviders.length > 0) {
48
+ const connectedOAuth = oauthProviders.filter((pr) => pr.loggedIn).length;
49
+ const oauthHint = connectedOAuth > 0 ? `${connectedOAuth} connected` : "none";
50
+ options.push({
50
51
  label: "OAuth providers",
51
52
  value: "oauth",
52
- hint: `${oauthHint} · pi-ai direct API`
53
- },
54
- {
55
- label: "Enhancement model",
56
- value: "model",
57
- hint: `${modelHint} · rewrites SKILL.md with best practices`
58
- },
59
- {
60
- label: "Target agent",
61
- value: "agent",
62
- hint: `${config.agent || "auto-detect"} · where skills are installed`
63
- }
64
- ];
53
+ hint: `${oauthHint} · may violate provider ToS`
54
+ });
55
+ }
56
+ options.push({
57
+ label: "Enhancement model",
58
+ value: "model",
59
+ hint: `${modelHint} · rewrites SKILL.md with best practices`
60
+ }, {
61
+ label: "Target agent",
62
+ value: "agent",
63
+ hint: `${config.agent || "auto-detect"} · where skills are installed`
64
+ });
65
+ return options;
65
66
  },
66
67
  onSelect: async (action) => {
67
68
  switch (action) {
@@ -192,21 +193,26 @@ async function configureModel() {
192
193
  while (true) {
193
194
  const available = await getAvailableModels();
194
195
  if (available.length === 0) p.log.warn(NO_MODELS_MESSAGE);
196
+ const afterOptions = getOAuthProviderList().length > 0 ? [{
197
+ label: "⚠ Connect OAuth provider...",
198
+ value: "_connect",
199
+ hint: "may violate provider ToS"
200
+ }, {
201
+ label: "Skip enhancement",
202
+ value: "_skip",
203
+ hint: "base skill with docs, issues, and types"
204
+ }] : [{
205
+ label: "Skip enhancement",
206
+ value: "_skip",
207
+ hint: "base skill with docs, issues, and types"
208
+ }];
195
209
  const choice = await pickModel(available, {
196
210
  before: available.length > 0 ? [{
197
211
  label: "Auto",
198
212
  value: "_auto",
199
213
  hint: "picks best available model from connected providers"
200
214
  }] : [],
201
- after: [{
202
- label: "Connect OAuth provider...",
203
- value: "_connect",
204
- hint: "use existing Claude Pro, ChatGPT Plus, etc."
205
- }, {
206
- label: "Skip enhancement",
207
- value: "_skip",
208
- hint: "base skill with docs, issues, and types"
209
- }]
215
+ after: afterOptions
210
216
  });
211
217
  if (!choice) return;
212
218
  if (choice === "_connect") {