SPB Git

Repository Public Research preview

build-skill.mjs 6.4 KB 37 lines javascript

1// Builds the skill from the MCP package so both ship the same verified rules:2//  - copies the dependency-free engine (knowledge, linter, cost, scaffolds, guides) into scripts/lib3//  - generates references/models.md and references/request-rules.md from knowledge.json4import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";5const src = new URL("./mcp/", import.meta.url), dst = new URL("./skill/claude-api-toolkit/", import.meta.url);6mkdirSync(new URL("scripts/lib/", dst), { recursive: true });7for (const f of ["knowledge.mjs", "lint.mjs", "cost.mjs", "scaffold.mjs", "guides.mjs"]) copyFileSync(new URL(`lib/${f}`, src), new URL(`scripts/lib/${f}`, dst));8copyFileSync(new URL("knowledge.json", src), new URL("scripts/knowledge.json", dst));9copyFileSync(new URL("LICENSE", src), new URL("LICENSE", dst));1011const kb = JSON.parse(readFileSync(new URL("knowledge.json", src)));12const ms = Object.values(kb.models).sort((a, b) => b.released.localeCompare(a.released));13const date = kb.observed_at.slice(0, 10);14const yn = (b) => (b ? "yes" : "no");15const k = (n) => (n >= 1e6 ? `${n / 1e6}M` : `${n / 1e3}K`);16const head = `<!-- Generated by build-skill.mjs from live API probes (${date}). Do not edit by hand. -->\n`;1718let models = `${head}# Models\n\nObserved ${date} from \`GET /v1/models\` and live requests. Prices: official pricing page, USD per million tokens. Models the probing key could not see (for example the Mythos line) are absent.\n\nCurrent generation: **claude-fable-5-2** (most capable), **claude-opus-5-5** (default), **claude-sonnet-5-5** (balanced), **claude-haiku-5-5** (cheapest).\n\n| Model id | Context | Max output | Input | Output | Cache read | Batch in / out |\n|---|---|---|---|---|---|---|\n`;19for (const m of ms) { const p = m.pricing_usd_per_mtok; models += `| \`${m.id}\` | ${k(m.max_input_tokens)} | ${k(m.max_output_tokens)} | $${p.input} | $${p.output} | $${p.cache_read} | $${p.batch_input} / $${p.batch_output} |\n`; }20models += `\nNotes:\n- Ids of the current generation have no date suffix. The 4.5 models keep one; \`claude-opus-4-5\`, \`claude-sonnet-4-5\`, \`claude-haiku-4-5\` are accepted aliases in the scripts.\n- \`claude-haiku-5-5\` switches to $0.50 / $2.50 when the prompt exceeds 100K tokens.\n- Cache writes cost 1.25× input (5 minutes) or 2× input (1 hour).\n- Fast mode prices: ${ms.filter((m) => m.pricing_usd_per_mtok.fast).map((m) => `\`${m.id}\` $${m.pricing_usd_per_mtok.fast.input} / $${m.pricing_usd_per_mtok.fast.output}`).join(", ")}.\n- \`claude-sonnet-4-5-20250929\` returned a deprecation warning: end-of-life 2026-11-30.\n- Never write a model id from memory. Run \`node scripts/claude-api.mjs models\`, or call the Models API.\n`;21writeFileSync(new URL("references/models.md", dst), models);2223const off = (m) => ({ accepted: "allowed", rejected: "**rejected**", use_between_tools: "**rejected** — use `between_tools`" })[m.thinking.disabled];24let rules = `${head}# What each model accepts\n\nEvery cell is an observed API response (${date}, API version 2026-10-21 unless stated). "rejected" means HTTP 400.\n\n## Thinking and effort\n\n| Model | \`adaptive\` | \`enabled\` + \`budget_tokens\` | \`disabled\` | Effort levels |\n|---|---|---|---|---|\n`;25for (const m of ms) rules += `| \`${m.id}\` | ${m.thinking.adaptive ? "yes" : "**rejected**"} | ${m.thinking.budget_tokens ? (m.thinking.adaptive ? "yes (deprecated)" : "yes (required for thinking)") : "**rejected**"} | ${off(m)}${m.thinking.disabled_blocked_at_effort.length ? " (not at xhigh/max)" : ""} | ${m.effort.levels.join(", ") || "**no effort parameter**"} |\n`;26rules += `\n## Request features\n\n| Model | Forced \`tool_choice\` (\`any\`/\`tool\`) | Assistant prefill | \`temperature\`/\`top_p\`/\`top_k\` (2026-10-21) | Same (2023-06-01) | \`role: "system"\` in messages |\n|---|---|---|---|---|---|\n`;27for (const m of ms) rules += `| \`${m.id}\` | ${m.forced_tool_choice ? "yes" : "**rejected**"} | ${m.assistant_prefill ? "yes" : "**rejected**"} | ${m.sampling_params.api_2026_10_21 ? "yes" : "**rejected**"} | ${m.sampling_params.api_2023_06_01 ? "yes" : "**rejected**"} | ${m.mid_conversation_system ? "yes" : "**rejected**"} |\n`;28rules += `\n## Beta and platform features\n\n| Model | Task budget | Compaction | Fast mode | \`inference_geo\` | \`web_search_20260209\` |\n|---|---|---|---|---|---|\n`;29for (const m of ms) rules += `| \`${m.id}\` | ${yn(m.task_budget_beta)} | ${yn(m.compaction_beta)} | ${m.fast_mode.replace(/_/g, " ")} | ${yn(m.inference_geo)} | ${m.server_tools.web_search_20260209 ? "yes" : "no — use `web_search_20250305`"} |\n`;30rules += `\n## Beta flags (exact strings)\n\nSend them in \`betas\` on the beta endpoint (\`client.beta.messages\`), or as the \`anthropic-beta\` header.\n\n| Feature | Flag | Request field |\n|---|---|---|\n| Task budget | \`task-budgets-2026-03-13\` | \`output_config.task_budget\` |\n| Compaction | \`compact-2026-01-12\` | \`context_management.edits\` → \`compact_20260112\` |\n| Context editing | \`context-management-2025-06-27\` | \`context_management.edits\` → \`clear_tool_uses_20250919\`, \`clear_thinking_20251015\` |\n| Server-side fallbacks | \`server-side-fallback-2026-07-01\` | \`fallbacks: "default"\` |\n| Fast mode on Opus 5 / 4.8 / 4.6 | \`fast-mode-2026-02-01\` | \`speed: "fast"\` (no flag needed on \`claude-opus-5-5\`, \`claude-fable-5-2\`) |\n| Thinking progress updates | \`thinking-display-updates-2026-08-18\` | \`thinking.display: "updates"\` |\n| MCP connector | \`mcp-client-2025-11-20\` | \`mcp_servers\` + an \`mcp_toolset\` entry in \`tools\` |\n\nFast mode also needs account access: without it the API answers 429 (a fast-mode rate limit of 0), not 400.\n`;31rules += `\nAccepted by every probed model: structured outputs (\`output_config.format\`), \`tool_choice\` \`auto\` and \`none\`, strict tools, top-level \`cache_control\`, \`stop_sequences\`, token counting, citations, context editing (beta), server-side fallbacks (beta), the memory, bash and text-editor client tools, tool search.\n\n## Exact error texts\n\n`;32const texts = new Map();33for (const m of ms) for (const [probe, t] of Object.entries(m.observed_errors)) { const key = t.replace(m.id, "<model>").replace(/\d{4,}/g, "N"); if (!texts.has(key)) texts.set(key, probe); }34for (const [t, probe] of texts) rules += `- \`${probe}\`: ${t}\n`;35writeFileSync(new URL("references/request-rules.md", dst), rules);36console.log(`skill built: ${ms.length} models, ${texts.size} distinct error texts`);37
Files main
Menu

Theme