Commit Graph

216 Commits

Author SHA1 Message Date
melkeydev 61b2838afb updating cursor plugin requirements 2026-03-12 14:10:55 -07:00
John Lindquist 8e3350454c feat: prompt scoring improvements, compaction re-injection, validation dedup, strip droppedByCap from context; bump to 0.18.0
- Add dominant-topic suppression (top score >= 600 filters scores < 50)
- Add project-context boost (+3 for VERCEL_PLUGIN_LIKELY_SKILLS skills)
- Add lexical fallback floor (reject raw score < 20)
- Add compaction re-injection for priority >= 7 skills (VERCEL_PLUGIN_CONTEXT_COMPACTED)
- Add validation rule dedup tracking (ruleId + filePath on violations)
- Strip droppedByCap from injected HTML comments (kept in debug logs)
- Fix ncc/SKILL.md and next-forge/SKILL.md YAML frontmatter
- Add startup diagnostic for broken skill frontmatter
2026-03-11 19:01:59 -06:00
John Lindquist ed7c3eb8ad fix(skill-frontmatter): warn on broken startup skills
Normalize fragile SKILL.md frontmatter in ncc and next-forge so the custom
frontmatter parser keeps their regex and nested prompt signal data intact.
Add a session-start summary log for broken skill frontmatter and keep
telemetry env export non-fatal when the env file is missing.

Verified: bun test tests/skill-map-frontmatter.test.ts tests/session-start-profiler.test.ts

Swarm-Agent: codex-skill-reliability
2026-03-11 19:01:59 -06:00
melkeydev 5ef93cb0c0 adding plugins add 2026-03-11 16:58:28 -07:00
melkeydev 6caf9f9494 adding npx 2026-03-11 16:41:51 -07:00
MelkeyDev 13bb53fe82 Merge pull request #4 from vercel-labs/change/readme-for-internal-release
modifying readme
2026-03-11 15:48:13 -07:00
melkeydev b4f819a57a fix 2026-03-11 15:47:45 -07:00
melkeydev 89ef27e143 modifying readme 2026-03-11 15:46:05 -07:00
John Lindquist b7e9b1b053 feat: merge upstream telemetry + Cursor compat, fix tests and dedup migration; bump to 0.17.0 2026-03-11 16:12:55 -06:00
John Lindquist aba9c59158 test(pretooluse): sync temp hook fixtures with runtime deps
Temp hook fixtures had a stale hardcoded module list, so debug and empty-skill tests
were failing before their assertions ran once pretooluse imported telemetry.
This centralizes fixture copying and keeps overrides available for patched
patterns tests.

Verified: bun test tests/pretooluse-skill-inject.test.ts -t "returns \\{\\} when skills directory is empty|SKILLMAP_EMPTY issue emitted when skills directory has no SKILL\.md files|SKILLMD_PARSE_FAIL issue emitted for malformed YAML frontmatter|SKILLMD_PARSE_FAIL not emitted when debug is off" (4 tests pass)
Attempted: bun test tests/pretooluse-skill-inject.test.ts (7 unrelated failures remain in dedup/validation expectations)
Swarm-Agent: codex-pretooluse
2026-03-11 15:27:50 -06:00
John Lindquist 2e216044ee fix(routing-middleware): match NextResponse import order
The routing-middleware validate and chainTo regex expected the import source before NextResponse, so standard imports like import { NextResponse } from 'next/server' never matched.

This updates both rules to accept the real import order while preserving support for the previous form.

Verified: bun test tests/validate-rules.test.ts (227 tests pass)
Verified: bun -e "import { loadValidateRules } from './hooks/src/posttooluse-validate.mts'; import { resolve } from 'node:path'; const data = loadValidateRules(resolve('.')); const validateRule = data.rulesMap.get('routing-middleware').find(r => r.upgradeToSkill === 'nextjs'); const chainRule = data.chainMap.get('routing-middleware').find(r => r.targetSkill === 'nextjs'); const content = \"import { NextResponse } from 'next/server';\\n\"; const validateOk = new RegExp(validateRule.pattern, 'g').test(content); const chainOk = new RegExp(chainRule.pattern, 'm').test(content); if (!validateOk || !chainOk) throw new Error(JSON.stringify({validateOk, chainOk})); console.log('ok');" (pass)
Swarm-Agent: codex-validate-rules
2026-03-11 15:25:19 -06:00
John Lindquist 3348f2a5bc fix(tsup): externalize sibling hook imports to prevent double-JSON output, enable lexical prompt matching by default
In hooks/tsup.config.ts, add an esbuild plugin (externalize-sibling-hooks) that marks sibling ./*.mjs imports as external before tsup's noExternal catch-all runs. The previous config used noExternal: [/.*/] which overrode the external array, causing sibling hooks to be inlined with their side-effecting isMainModule() guards — producing invalid concatenated JSON output like "{}{}". The fix uses build.onResolve to intercept ./foo.mjs imports matching the hookExternalSet and return { external: true }. npm dependencies (minisearch, etc.) remain bundled inline.

In hooks/src/user-prompt-submit-skill-inject.mts line 581, change the lexical check from process.env.VERCEL_PLUGIN_LEXICAL_PROMPT === "1" to !== "0" so lexical stemmer fallback is on by default. Update the CLAUDE.md env var table to show default as "on" with "0" to disable.

Rebuild all compiled hooks via bun run build:hooks.
2026-03-11 15:18:35 -06:00
John Lindquist 8e24d0694a feat: add chainTo skill chaining, prompt signals, validation upgrades, and dedup reset on context clear
Add a chainTo frontmatter field to skills that triggers follow-up skill injection
when PostToolUse file contents match regex patterns. Add upgradeToSkill/upgradeWhy
fields to validation rules so validate errors can recommend loading a specific skill.
Register posttooluse-bash-chain.mjs in hooks.json. Add ChainToRule interface to
patterns.mts and skill-map-frontmatter.mts, with parseChainToRules() parser. Add
duplicate-key detection to the YAML parser. Reset dedup claim dir and session file
on clear/compact events in session-start-seen-skills so skills re-inject into fresh
context windows.

Add chainTo rules to: agent-browser-verify, agent-browser, ai-elements, ai-gateway,
ai-generation-persistence, ai-sdk, auth, bootstrap, chat-sdk, cms, cron-jobs,
deployments-cicd, email, env-vars, geist, investigation-mode, json-render,
marketplace, micro, ncc, next-forge, nextjs, observability, payments,
react-best-practices, routing-middleware, runtime-cache, satori, shadcn,
sign-in-with-vercel, swr, turbopack, turborepo, v0-dev, vercel-agent, vercel-api,
vercel-cli, vercel-firewall, vercel-flags, vercel-functions, vercel-queues,
vercel-sandbox, vercel-storage, verification, workflow. Add upgradeToSkill to
ai-elements and ai-sdk validate rules. Expand ai-sdk validate messages with
Run Skill() hints. Update nextjs, vercel-storage, runtime-cache, workflow, turborepo
skill bodies.

Add new skills: geistdocs (Geist design system docs), zzz-test-meta-name-mask
(test fixture). Add skills/_chain-audit.md chain coverage audit doc.

Delete .claude-plugin/marketplace.json, .claude-plugin/plugin.json (deprecated),
skills/edge-runtime/SKILL.md (consolidated into vercel-functions).

Add tests: posttooluse-chain.test.ts (4699 lines, chain injection e2e),
ai-sdk-companion.test.ts (181 lines). Expand build-skill-map.test.ts (+335 lines),
validate-rules.test.ts (+936 lines), session-start-seen-skills.test.ts (+74 lines),
skill-map-frontmatter.test.ts (+50 lines), verification-skill.test.ts (+20 lines).

Update build-manifest.ts to emit chainTo rules and upgradeToSkill fields. Rebuild
generated/skill-manifest.json, generated/skill-catalog.md, generated/build-from-skills.manifest.json.
Rebuild all compiled hooks/*.mjs. Update CLAUDE.md lexical prompt default to on.
Update vercel.md ecosystem graph, docs, and cli-reference.
2026-03-11 15:18:35 -06:00
John Lindquist 089b5aefa4 fix(benchmark-sandbox): extend sandbox before snapshot
Move the timeout extension ahead of the snapshot call so long builds do not
expire before the restore point is created.
Also double the sandbox creation lifetime and only apply a post-snapshot
extension when keep-alive is enabled.

Verified: bun test tests/run-eval-build-polling.test.ts
Swarm-Agent: codex-snapshot-timing
2026-03-11 15:18:19 -06:00
John Lindquist 4c00fc0a00 fix(prompt-submit): drop seen-skills env merge
UserPromptSubmit no longer merges or rewrites seen-skill state through VERCEL_PLUGIN_SEEN_SKILLS when file-backed dedup state exists.
The helper now keeps session-file state authoritative and only forwards the dedup flag into injectSkills inline.

Verified: bun run build:hooks
Verified: bun test hooks/user-prompt-submit-skill-inject.test.ts tests/user-prompt-submit-skill-inject.test.ts
Swarm-Agent: codex-prompt-drop-env-v2
2026-03-11 15:18:19 -06:00
John Lindquist 9bec068d63 fix(posttool): drop seen-skills env merge from validate and bash-chain hooks
Both posttool hooks now consult the persisted session-backed seen-skills state instead of merging in VERCEL_PLUGIN_SEEN_SKILLS. This keeps dedup behavior consistent across subagents and avoids env leakage into hook chaining decisions.

Verified: bun run build:hooks
Verified: tsc -p hooks/tsconfig.json --noEmit
Verified: bun test tests/posttooluse-validate.test.ts
Verified: bun test hooks/platform-hook-compat.test.ts
Verified: bun -e '...runChainInjection smoke for session-file-only seen-skills...'
Verified: bun -e '...runBashChainInjection smoke for session-file-only seen-skills...'
Swarm-Agent: codex-posttool-commit
2026-03-11 15:18:19 -06:00
John Lindquist e048cbc9bd fix(pretooluse): drop seen env merge writes
The pretool hook no longer folds VERCEL_PLUGIN_SEEN_SKILLS into
session-file dedup state or writes claim-sync results back into
process.env. File-backed dedup still claims and syncs session state,
while env-only dedup remains a fallback when no session scope exists.

Verified: bun run build:hooks
Verified: bun test tests/hook-sync.test.ts
Verified: bun test tests/pretooluse-skill-inject.test.ts --test-name-pattern "file-based dedup persists across invocations with same session_id|debug mode logs dedup strategy for file, memory-only, and disabled"
Verified: node --input-type=module <<'EOF' ... fresh cursor Write payload asserts no VERCEL_PLUGIN_SEEN_SKILLS env update and TSX counter update EOF
Verified: node --input-type=module <<'EOF' ... env-only Read payload without session_id returns {} when VERCEL_PLUGIN_SEEN_SKILLS=nextjs EOF
Swarm-Agent: codex-pretool-drop-env-merge
2026-03-11 15:18:19 -06:00
John Lindquist a80ac3581c fix(session-start-profiler): store profiler state in session files
Claude session-start-profiler no longer requires CLAUDE_ENV_FILE to persist likely skills or greenfield state.
Those values now come from the hook input session ID and are written to session files,
while Claude env exports remain only for the other profiler vars.

Verified: bun run build:hooks
Verified: bun test tests/session-start-profiler.test.ts
Swarm-Agent: codex-profiler-env-cleanup
2026-03-11 15:18:19 -06:00
John Lindquist 7566bed4d4 fix(session-start): noop Claude seen-skills init
Claude session-start no longer appends VERCEL_PLUGIN_SEEN_SKILLS to
CLAUDE_ENV_FILE now that claim-dir/session initialization handles dedup.
Cursor still emits the empty env var payload on stdout.

Verified: bun run build:hooks
Verified: bun test tests/session-start-seen-skills.test.ts
Verified: bun test tests/session-timeline-subagent.test.ts
Verified: bun test hooks/session-hooks-platform-compat.test.ts
Swarm-Agent: codex-seen-skills-init-cleanup
2026-03-11 15:18:19 -06:00
John Lindquist 8e7c146c73 fix(prompt-submit): drop seen-skills env appends
UserPromptSubmit no longer appends VERCEL_PLUGIN_SEEN_SKILLS into CLAUDE_ENV_FILE.
Session claims/files remain the persistence path, while Cursor keeps its existing output-env fallback when no env file is present.

Verified: bun run build:hooks
Verified: bun test tests/user-prompt-submit.test.ts -t "cursor payload returns flat output with continue and env patch"
Verified: bun test tests/user-prompt-submit.test.ts -t "does not append seen skills to CLAUDE_ENV_FILE when available"
Swarm-Agent: codex-prompt-submit-env-cleanup
2026-03-11 15:18:19 -06:00
John Lindquist 95176e59dc fix(pretooluse): move hook counters off Claude env file
PreToolUse now persists TSX review and dev-server verify counters via session files,
and it no longer appends runtime env exports to CLAUDE_ENV_FILE for Claude.
This keeps seen-skills dedup on the claim/session-file path and avoids shell-init
side effects during hook execution.

Verified: bun run build:hooks
Verified: bun test tests/pretooluse-skill-inject.test.ts -t "file-based dedup persists across invocations with same session_id"
Verified: bun test tests/pretooluse-skill-inject.test.ts -t "empty VERCEL_PLUGIN_SEEN_SKILLS env var dedups across invocations"
Verified: custom node smoke test (tsx-migration-check) — CLAUDE_ENV_FILE unchanged, tsx-edit-count reset in session file, seen-skills persisted
Verified: custom node smoke test (dev-migration-check) — CLAUDE_ENV_FILE unchanged, dev-verify-count persisted in session file
Swarm-Agent: codex-pretooluse-env-cleanup
2026-03-11 15:18:18 -06:00
John Lindquist 91fd7a9da1 fix(run-eval): fire-and-forget deploy polling
Deploy now mirrors the build phase fire-and-forget pattern so long Claude deploy sessions keep running past the 300s sandbox API timeout.
The build snapshot is also taken before the build score call so the restore point exists before any extra analysis work.

Verified: bun test tests/run-eval-build-polling.test.ts
Verified: deploy phase now reads /tmp/claude-deploy.log after polling to extract the vercel.app URL
Swarm-Agent: codex-deploy-fireforget
2026-03-11 15:18:18 -06:00
John Lindquist 14e951e1df test(pretooluse-skill-inject): align budget expectations
Update the remaining budget-related assertions in pretooluse-skill-inject tests to match the current hook output and skillInjection metadata.\n\nVerified: bun test tests/pretooluse-skill-inject.test.ts (279 pass, 0 fail)\nHow to test: bun test tests/pretooluse-skill-inject.test.ts\nSwarm-Agent: codex-budget-fix
2026-03-11 15:18:18 -06:00
John Lindquist b9fcf22191 feat(hooks): add Cursor compat to pretooluse-skill-inject
Detect platform via conversation_id/workspace_roots/cursor_version
fields in stdin JSON. Normalize sessionId, cwd, and output format:
- Cursor: flat { additional_context, env } output
- Claude Code: nested { hookSpecificOutput } output (unchanged)

Env var updates use JSON env field for Cursor instead of
CLAUDE_ENV_FILE. Includes platform-hook-compat test suite.
2026-03-11 15:18:18 -06:00
John Lindquist 92b69b6ee7 fix(hooks): support Cursor session-start profiler output
Finish the session-start profiler Cursor path without touching the profiling logic.
Platform detection now keys off hook input and CLAUDE_ENV_FILE, project root lookup falls back to CURSOR_PROJECT_DIR, and Cursor receives env plus additional_context JSON on stdout.

Verified: bun test hooks/session-start-profiler-platform.test.ts
Verified: bun test hooks/session-hooks-platform-compat.test.ts
Verified: zsh -lc 'tmpdir=/var/folders/c3/r013q3_93_s4zycmx0mdnt2h0000gn/T/tmp.p6JmjuMjls; printf "{\"conversation_id\":\"conv-123\",\"cursor_version\":\"1.0.0\"}" | env -u CLAUDE_ENV_FILE -u CLAUDE_PROJECT_ROOT CURSOR_PROJECT_DIR="" bun hooks/src/session-start-profiler.mts'
Swarm-Agent: codex-profiler-v2
2026-03-11 15:18:18 -06:00
John Lindquist a78ac86798 fix(hooks): add Cursor support for session hooks
Session start hooks now detect Cursor payloads from stdin and emit Cursor-compatible JSON while keeping Claude Code env-file behavior intact.
Session end cleanup now falls back to conversation_id so Cursor sessions clean up the same temp artifacts.

Verified: bun test hooks/session-hooks-platform-compat.test.ts tests/session-start-seen-skills.test.ts tests/session-end-cleanup.test.ts
How to test: run the same bun test command, or pipe Cursor-style JSON into the three hook scripts under hooks/*.mjs.
Swarm-Agent: codex-session-start-v2
2026-03-11 15:17:31 -06:00
John Lindquist 8d8faffe57 fix(hooks): add cursor support to posttool validator
Normalize PostToolUse stdin with compat-based platform detection and Cursor workspace/session fallbacks.
Emit Cursor additional_context/env payloads while preserving Claude output shape and empty-result behavior.

Verified: bun test hooks/platform-hook-compat.test.ts
Verified: bun --eval 'import assert from "node:assert/strict"; import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { formatOutput, markValidated } from "./hooks/src/posttooluse-validate.mts"; const tempDir = mkdtempSync(join(tmpdir(), "posttooluse-validate-")); const envFile = join(tempDir, "claude.env"); writeFileSync(envFile, "", "utf-8"); delete process.env.VERCEL_PLUGIN_VALIDATED_FILES; process.env.CLAUDE_ENV_FILE = envFile; const next = markValidated("app/page.tsx", "abc123def456", null); assert.equal(next, "app/page.tsx:abc123def456"); assert.match(readFileSync(envFile, "utf-8"), /VERCEL_PLUGIN_VALIDATED_FILES="app\/page\.tsx:abc123def456"/); const output = JSON.parse(formatOutput([{ skill: "ai-sdk", line: 8, message: "Use streamText for streaming responses.", severity: "recommended", matchedText: "generateText" }], ["ai-sdk"], "app/page.tsx", undefined, "cursor", { VERCEL_PLUGIN_VALIDATED_FILES: next })); assert.equal(output.env.VERCEL_PLUGIN_VALIDATED_FILES, next); rmSync(tempDir, { recursive: true, force: true }); delete process.env.CLAUDE_ENV_FILE; delete process.env.VERCEL_PLUGIN_VALIDATED_FILES;'
Swarm-Agent: codex-posttool-validate-v2
2026-03-11 15:17:02 -06:00
John Lindquist e91067f1aa chore(hooks): drop accidental generated artifacts
Restore generated hook outputs that were accidentally committed when the
repo pre-commit hook rebuilt the entire hooks directory. This follow-up
keeps this agent's change set focused on the UserPromptSubmit source and
tests without touching the in-progress source files owned by other agents.

Verified: bun test hooks/user-prompt-submit-skill-inject.test.ts tests/user-prompt-submit.test.ts
Verified: restored generated outputs to their HEAD~1 content for six unrelated hook artifacts
Swarm-Agent: codex-prompt-hook
2026-03-11 15:17:02 -06:00
John Lindquist 1142c51906 fix(user-prompt-submit): support cursor prompt hook
Normalize UserPromptSubmit input fields across Claude Code and Cursor,
including conversation/session IDs, cwd roots, and prompt/message
fallbacks. Cursor now returns flat beforeSubmitPrompt output with
continue:true and emits seen-skill env patches when no CLAUDE_ENV_FILE is
available, while Claude continues using hookSpecificOutput and appends
seen-skill exports to CLAUDE_ENV_FILE when present.

Verified: bun test hooks/user-prompt-submit-skill-inject.test.ts tests/user-prompt-submit.test.ts
Verified: cursor beforeSubmitPrompt payload returns continue:true + env patch in tests/user-prompt-submit.test.ts
Swarm-Agent: codex-prompt-hook
2026-03-11 15:17:02 -06:00
John Lindquist 0beeedb807 chore(hooks): restore unrelated generated outputs
The local pre-commit hook rebuilt several hooks/*.mjs files from other agents' dirty source changes while I was committing the new compat layer. Restore the unrelated generated artifacts to their previous state so this task only carries the compat-layer files it owns.

Verified: bun test hooks/compat.test.ts
Swarm-Agent: codex-compat-layer
2026-03-11 15:16:27 -06:00
John Lindquist e37004a002 feat(hooks): add compat layer for cursor payloads
Add a shared Claude Code/Cursor compatibility module that normalizes hook input, formats platform-specific output, and persists session env state across both runtimes.

Verified: bun test hooks/compat.test.ts
Verified: bun x tsc --noEmit --module nodenext --moduleResolution nodenext --target es2022 --types node hooks/src/compat.mts
Swarm-Agent: codex-compat-layer
2026-03-11 15:16:27 -06:00
John Lindquist f438cb7313 fix(skills): normalize manifest regex escaping
Regenerate the skill manifest so the new micro and deployments-cicd validate rules use the canonical escaping emitted by build:manifest.
The prior shared-index commit captured non-canonical escaping in generated/skill-manifest.json.

Verified: bun run build:manifest
Verified: git diff --cached -- generated/skill-manifest.json shows only the 2 regex escaping normalizations
Swarm-Agent: codex-rules-wave2b
2026-03-11 15:16:21 -06:00
John Lindquist 875a9e7d20 test(upgrade-rules): cover skill upgrade validate fixtures
Add focused fixture tests for each current upgradeToSkill validate rule.
Each case asserts the positive trigger and the skipIfFileContains path so future SKILL.md changes do not silently break upgrade guidance.

Verified: bun test tests/upgrade-rules.test.ts
How to test: bun test tests/upgrade-rules.test.ts
Swarm-Agent: codex-test-fixtures
2026-03-11 15:16:21 -06:00
John Lindquist a6077bb0c8 feat(skills): add validate upgrades to three skills
Add top-level validate blocks to react-best-practices, auth, and routing-middleware so the manifest can recommend SWR, Sign in with Vercel, and Vercel Firewall/WAF when legacy patterns are detected.

Verified: bun run build:manifest
Verified: git diff -- generated/skill-manifest.json shows only these three skills gained validate entries
Swarm-Agent: codex-rules-wave2a
2026-03-11 15:16:21 -06:00
John Lindquist ca89a80912 feat(nextjs): add upgrade validate rules for auth, functions, and caching
Add three Next.js validate rules that recommend upgrading legacy auth, Pages Router API routes, and in-memory caches to dedicated Vercel skills.
Regenerate the static skill manifest so the new upgrade metadata is available at runtime; this also syncs a few pre-existing source-of-truth manifest entries that were stale in the generated file.

Verified: bun run build:manifest (isolated clean checkout based on HEAD + skills/nextjs/SKILL.md)
Verified: git diff --cached --check
Swarm-Agent: codex-commit-nextjs
2026-03-11 15:16:21 -06:00
John Lindquist 1c86a15282 feat(ai-sdk): add upgrade validation rules
Add upgrade-oriented validate entries for AI Gateway adoption, AI Elements rendering, and message.parts-based UIMessage rendering in the AI SDK skill.

Verified: bun run build:manifest
Verified: git diff --check -- skills/ai-sdk/SKILL.md generated/skill-manifest.json
Swarm-Agent: codex-rules-aisdk
2026-03-11 15:16:21 -06:00
John Lindquist 589f7a792d feat(vercel-functions): add upgrade validate rules
Add upgrade-oriented validate rules for provider SDK usage, long-running logic, filesystem writes, and missing observability in Vercel route handlers.

Verified: bun run build:manifest
Swarm-Agent: codex-rules-functions
2026-03-11 15:16:21 -06:00
John Lindquist 9fdcc5c8b0 chore(pretooluse-skill-inject): restore generated bundle
The previous commit unintentionally included this compiled hook artifact because the local pre-commit hook rebuilt and auto-staged hook bundles. This restores the generated file to its prior committed state without touching the dirty source file.

Verified: tmp=/var/folders/c3/r013q3_93_s4zycmx0mdnt2h0000gn/T/tmp.2y6bxG5m6P && git show HEAD^:hooks/pretooluse-skill-inject.mjs > "" && cmp -s hooks/pretooluse-skill-inject.mjs "" && rm ""
Swarm-Agent: codex-hook-chain
2026-03-11 15:16:21 -06:00
John Lindquist 994a5431a8 fix(posttooluse-validate): dedupe skill upgrade instructions
formatOutput now emits a single skill-upgrade instruction block per target skill, adds the requested blank line and reason text, and marks hard upgrades as REQUIRED so agents get clearer follow-up actions.

Verified: bun run build:hooks
Verified: bun test tests/posttooluse-validate.test.ts
Verified: bun run typecheck
Swarm-Agent: codex-hook-chain
2026-03-11 15:16:21 -06:00
John Lindquist ab8810509e fix(hooks): drop unrelated generated injector diff
The repo pre-commit hook rebuilt hook outputs and accidentally staged a generated pretooluse injector artifact from another in-progress source change. This commit restores that generated file to its pre-commit content so the skill-upgrade change stays isolated.

Verified: diff -u <(git show HEAD^:hooks/pretooluse-skill-inject.mjs) hooks/pretooluse-skill-inject.mjs
Swarm-Agent: codex-schema
2026-03-11 15:16:21 -06:00
John Lindquist 4525723e15 feat(hooks): add skill upgrade metadata to validate rules
Validate rules can now carry optional skill-upgrade metadata from SKILL.md frontmatter through the post-tool-use validator. When a matching rule requests an upgrade, the hook output now emits both human-readable Skill tool guidance and a machine-readable HTML comment for downstream parsing.

Verified: bun run typecheck
Verified: bun -e 'const mod = await import("./hooks/src/posttooluse-validate.mts"); const violations = mod.runValidation("foo\n", ["source-skill"], new Map([["source-skill", [{ pattern: "foo", message: "needs upgrade", severity: "error", upgradeToSkill: "target-skill", upgradeWhy: "more specific context" }]] ])); const out = JSON.parse(mod.formatOutput(violations, ["source-skill"], "/tmp/test.ts")); console.log(JSON.stringify(violations)); console.log(out.hookSpecificOutput.additionalContext);'
Verified: bun -e 'const mod = await import("./hooks/src/skill-map-frontmatter.mts"); const parsed = mod.parseSkillFrontmatter(`validate:\n  -\n    pattern: foo\n    message: needs upgrade\n    severity: error\n    upgradeToSkill: target-skill\n    upgradeWhy: use the more specific skill\n  -\n    pattern: bar\n    message: hard upgrade\n    severity: warn\n    upgradeToSkill: hard-skill\n    upgradeMode: hard\n`); console.log(JSON.stringify(parsed.validate));'
Swarm-Agent: codex-schema
2026-03-11 15:16:21 -06:00
John Lindquist b0ef7b05cc fix(shadcn): move validate rules to top-level
The PostToolUse hook reads top-level validate rules, so the shadcn skill's frontmatter must expose the existing rule outside metadata.

Verified: ruby -e 'require "yaml"; text=---
name: shadcn
description: shadcn/ui expert guidance — CLI, component installation, composition patterns, custom registries, theming, Tailwind CSS integration, and high-quality interface design. Use when initializing shadcn, adding components, composing product UI, building custom registries, configuring themes, or troubleshooting component issues.
metadata:
  priority: 6
  docs:
    - "https://ui.shadcn.com/docs"
    - "https://ui.shadcn.com/docs/components"
  pathPatterns:
    - 'components.json'
    - 'components/ui/**'
    - 'src/components/ui/**'
    - 'apps/*/components/ui/**'
    - 'apps/*/src/components/ui/**'
    - 'packages/*/components/ui/**'
    - 'packages/*/src/components/ui/**'
  bashPatterns:
    - '\bnpx\s+shadcn\b'
    - '\bnpx\s+shadcn@latest\s+(init|add|build|search|list|migrate|info|docs|view)\b'
    - '\bnpx\s+create-next-app\b'
    - '\bbunx\s+create-next-app\b'
    - '\bpnpm\s+create\s+next-app\b'
    - '\bnpm\s+create\s+next-app\b'
validate:
  -
    pattern: '"base"\s*:\s*"base-ui"'
    message: 'AI Elements components use Radix-specific APIs (asChild, openDelay) and have type errors with Base UI. If this project uses AI Elements, reinitialize with: npx shadcn@latest init -d --base radix -f'
    severity: warn
retrieval:
  aliases:
    - shadcn ui
    - component library
    - ui components
    - tailwind components
  intents:
    - add shadcn component
    - set up shadcn
    - customize theme
    - build ui
  entities:
    - shadcn/ui
    - Tailwind CSS
    - registry
    - theme
    - components.json

---

# shadcn/ui

You are an expert in shadcn/ui — a collection of beautifully designed, accessible, and customizable React components built on Radix UI primitives and Tailwind CSS. Components are added directly to your codebase as source code, not installed as a dependency.

## Key Concept

shadcn/ui is **not a component library** in the traditional sense. You don't install it as a package. Instead, the CLI copies component source code into your project, giving you full ownership and customization ability.

## CLI Commands

### Initialize (non-interactive — ALWAYS use this)

**IMPORTANT**: `shadcn init` is interactive by default. Always use `-d` (defaults) for non-interactive initialization:

```bash
# Non-interactive init with defaults — USE THIS
npx shadcn@latest init -d

# Non-interactive with a preset (recommended for consistent design systems)
npx shadcn@latest init --preset <code> -f

# Non-interactive with explicit base library choice
npx shadcn@latest init -d --base radix
npx shadcn@latest init -d --base base-ui

# Scaffold a full project template (CLI v4)
npx shadcn@latest init --template next -d
npx shadcn@latest init --template vite -d
```

Options:
- `-d, --defaults` — **Use default configuration, skip all interactive prompts** (REQUIRED for CI/agent use)
- `-y, --yes` — Skip confirmation prompts (does NOT skip library selection — use `-d` instead)
- `-f, --force` — Force overwrite existing configuration
- `-t, --template` — Scaffold full project template (`next`, `vite`, `react-router`, `astro`, `laravel`, `tanstack-start`)
- `--preset` — Apply a design system preset (colors, theme, icons, fonts, radius) as a single shareable code
- `--base` — Choose primitive library: `radix` (default) or `base-ui`
- `--monorepo` — Set up a monorepo structure

> **WARNING**: `-y`/`--yes` alone does NOT make init fully non-interactive — it still prompts for component library selection. Always use `-d` to skip ALL prompts.

> **Deprecated in CLI v4**: `--style`, `--base-color`, `--src-dir`, `--no-base-style`, and `--css-variables` flags are removed and will error. The `registry:build` and `registry:mcp` registry types are also deprecated. Use `registry:base` and `registry:font` instead.

The init command:
1. Detects your framework (Next.js, Vite, React Router, Astro, Laravel, TanStack Start)
2. Installs required dependencies (Radix UI, tailwind-merge, class-variance-authority)
3. Creates `components.json` configuration
4. Sets up the `cn()` utility function
5. Configures CSS variables for theming

### Add Components

```bash
# Add specific components
npx shadcn@latest add button dialog card

# Add all available components
npx shadcn@latest add --all

# Add from a custom registry
npx shadcn@latest add @v0/dashboard
npx shadcn@latest add @acme/custom-button

# Add from AI Elements registry
npx shadcn@latest add https://elements.ai-sdk.dev/api/registry/all.json
```

Options:
- `-o, --overwrite` — Overwrite existing files
- `-p, --path` — Custom install path
- `-a, --all` — Install all components
- `--dry-run` — Preview what will be added without writing files
- `--diff` — Show diff of changes when updating existing components
- `--view` — Display a registry item's source code inline

### Search & List

```bash
npx shadcn@latest search button
npx shadcn@latest list @v0
```

### Build (Custom Registry)

```bash
npx shadcn@latest build
npx shadcn@latest build ./registry.json -o ./public/r
```

### View, Info & Docs (CLI v4)

```bash
# View a registry item's source before installing
npx shadcn@latest view button

# Show project diagnostics — config, installed components, dependencies
npx shadcn@latest info

# Get docs, code, and examples for any component (agent-friendly output)
npx shadcn@latest docs button
npx shadcn@latest docs dialog
```

> **`shadcn docs`** gives coding agents the context to use primitives correctly — returns code examples, API reference, and usage patterns inline.

### Migrate

```bash
npx shadcn@latest migrate rtl    # RTL support migration
npx shadcn@latest migrate radix  # Migrate to unified radix-ui package
npx shadcn@latest migrate icons  # Icon library changes

# Migrate components outside the default ui directory
npx shadcn@latest migrate radix src/components/custom
```

## shadcn/skills (CLI v4)

shadcn/skills gives coding agents the context they need to work with components and registries correctly. It covers both Radix and Base UI primitives, updated APIs, component patterns, and registry workflows. The skill knows how to use the CLI, when to invoke it, and which flags to pass — so agents produce code that matches your design system.

Install: `pnpm dlx skills add shadcn/ui`

## Unified Radix UI Package (February 2026)

The `new-york` style now uses a single `radix-ui` package instead of individual `@radix-ui/react-*` packages:

```tsx
// OLD — individual packages
import * as DialogPrimitive from "@radix-ui/react-dialog"

// NEW — unified package
import { Dialog as DialogPrimitive } from "radix-ui"
```

To migrate existing projects: `npx shadcn@latest migrate radix`. After migration, remove unused `@radix-ui/react-*` packages from `package.json`.

## Base UI Support (January 2026)

shadcn/ui now supports **Base UI** as an alternative to Radix UI for the underlying primitive library. Components look and behave the same way regardless of which library you choose — only the underlying implementation changes.

Choose during init: `npx shadcn@latest init --base base-ui`

The CLI pulls the correct component variant based on your project configuration automatically.

## Configuration (components.json)

The `components.json` file configures how shadcn/ui works in your project:

```json
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.ts",
    "css": "src/app/globals.css",
    "baseColor": "zinc",  // Options: gray, neutral, slate, stone, zinc, mauve, olive, mist, taupe
    "cssVariables": true
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  },
  "registries": {
    "v0": {
      "url": "https://v0.dev/chat/api/registry"
    },
    "ai-elements": {
      "url": "https://elements.ai-sdk.dev/api/registry"
    }
  }
}
```

### Namespaced Registries

Configure multiple registries for your project:

```json
{
  "registries": {
    "acme": {
      "url": "https://acme.com/registry/{name}.json"
    },
    "private": {
      "url": "https://internal.company.com/registry/{name}.json",
      "headers": {
        "Authorization": "Bearer ${REGISTRY_TOKEN}"
      }
    }
  }
}
```

Install using namespace syntax:

```bash
npx shadcn@latest add @acme/header @private/auth-form
```

## Theming

### CSS Variables

shadcn/ui uses CSS custom properties for theming, defined in `globals.css`:

```css
@theme inline {
  --color-background: oklch(0.145 0 0);
  --color-foreground: oklch(0.985 0 0);
  --color-card: oklch(0.205 0 0);
  --color-card-foreground: oklch(0.985 0 0);
  --color-primary: oklch(0.488 0.243 264.376);
  --color-primary-foreground: oklch(0.985 0 0);
  --color-secondary: oklch(0.269 0 0);
  --color-secondary-foreground: oklch(0.985 0 0);
  --color-muted: oklch(0.269 0 0);
  --color-muted-foreground: oklch(0.708 0 0);
  --color-accent: oklch(0.269 0 0);
  --color-accent-foreground: oklch(0.985 0 0);
  --color-destructive: oklch(0.396 0.141 25.723);
  --color-border: oklch(0.269 0 0);
  --color-input: oklch(0.269 0 0);
  --color-ring: oklch(0.488 0.243 264.376);
  --radius: 0.625rem;
  /* CLI v4: radius tokens use multiplicative calc instead of additive */
  --radius-xs: calc(var(--radius) * 0.5);
  --radius-sm: calc(var(--radius) * 0.75);
  --radius-md: calc(var(--radius) * 0.875);
  --radius-lg: var(--radius);
  --radius-xl: calc(var(--radius) * 1.5);
}
```

### Dark Mode

For dark mode, use the `dark` class on `<html>`:

```tsx
// app/layout.tsx
<html lang="en" className="dark">
```

Or use next-themes for toggling:

```tsx
import { ThemeProvider } from 'next-themes'

<ThemeProvider attribute="class" defaultTheme="dark">
  {children}
</ThemeProvider>
```

### Custom Colors

Add application-specific colors alongside shadcn defaults:

```css
@theme inline {
  /* shadcn defaults above... */

  /* Custom app colors */
  --color-priority-urgent: oklch(0.637 0.237 15.163);
  --color-priority-high: oklch(0.705 0.213 47.604);
  --color-status-done: oklch(0.723 0.219 149.579);
}
```

Use in components:

```tsx
<span className="text-[var(--color-priority-urgent)]">Urgent</span>
// Or with Tailwind v4 theme():
<span className="text-priority-urgent">Urgent</span>
```

## Most Common Components

| Component | Use Case |
|-----------|----------|
| `button` | Actions, form submission |
| `card` | Content containers |
| `dialog` | Modals, confirmation prompts |
| `input` / `textarea` | Form fields |
| `select` | Dropdowns |
| `table` | Data display |
| `tabs` | View switching |
| `command` | Command palette (Cmd+K) |
| `dropdown-menu` | Context menus |
| `popover` | Floating content |
| `tooltip` | Hover hints |
| `badge` | Status indicators |
| `avatar` | User profile images |
| `scroll-area` | Scrollable containers |
| `separator` | Visual dividers |
| `label` | Form labels |
| `sheet` | Slide-out panels |
| `skeleton` | Loading placeholders |

## Design Direction for shadcn on Vercel

shadcn/ui is not only a component source generator. In the Vercel stack it is the default interface language. Do not stop at "the component works." Compose pages that feel deliberate, high-signal, and consistent.

### Default aesthetic for product UI

- Prefer style: `new-york` for product, dashboard, AI, and admin surfaces.
- Default to dark mode for dashboards, AI apps, internal tools, settings, and developer-facing products. Use light mode only when the product is clearly content-first or editorial.
- Use Geist Sans for interface text and Geist Mono for code, metrics, IDs, timestamps, commands.
- Prefer zinc, neutral, or slate as the base palette. Use one accent color through `--color-primary`.
- Build core surfaces from tokens: `bg-background`, `bg-card`, `text-foreground`, `text-muted-foreground`, `border-border`, `ring-ring`. Avoid ad-hoc hex values.
- Keep radius consistent. The default `--radius: 0.625rem` is a strong baseline.
- Use one density system per page: comfortable (`gap-6` / `p-6` / `text-sm`) or compact (`gap-4` / `p-4` / `text-sm`).
- Keep icons quiet and consistent. Lucide icons at `h-4 w-4` or `h-5 w-5`.

### Reach for this first

| Use case | Reach for this first | Why |
|----------|----------------------|-----|
| Settings page | `Tabs` + `Card` + `Form` | Clear information grouping with predictable save flows |
| Data dashboard | `Card` + `Badge` + `Table` + `DropdownMenu` | Covers summary, status, dense data, and row actions without custom shells |
| CRUD table | `Table` + `DropdownMenu` + `Sheet` + `AlertDialog` | Supports browse, act, edit, and destructive confirmation in a standard pattern |
| Auth screen | `Card` + `Label` + `Input` + `Button` + `Alert` | Keeps entry flows focused and gives errors a proper treatment |
| Global search | `Command` + `Dialog` | Fast keyboard-first discovery with an established interaction model |
| Mobile nav | `Sheet` + `Button` + `Separator` | Provides a compact navigation shell that adapts cleanly to small screens |
| Detail page | header + `Badge` + `Separator` + `Card` | Balances hierarchy, metadata, and supporting content without over-nesting |
| Filters | `Card` sidebar + `Sheet` + `Select` | Works for persistent desktop filters and collapsible mobile controls |
| Empty/loading/error states | `Card` + `Skeleton` + `Alert` | Gives non-happy paths a designed surface instead of placeholder text |

### Composition recipes

- Settings page: `Tabs` + `Card` per group + `Separator` + save action
- Admin dashboard: summary `Card`s + filter bar + `Table`
- Entity detail: header + status `Badge` + main `Card` + side `Card` + `AlertDialog` for destructive
- Search-heavy: `Command` for quick find, `Popover` for pickers, `Sheet` for mobile filters
- Auth/onboarding: centered `Card` + social `Separator` + inline `Alert` for errors
- Destructive flows: `AlertDialog` (not `Dialog`) for confirmation

### Anti-patterns to avoid

- Raw `button` / `input` / `select` / `div` when shadcn primitives exist
- Repeated `div rounded-xl border p-6` instead of `Tabs` / `Table` / `Sheet` / `Dialog`
- Multiple accent colors fighting each other
- Nested cards inside cards inside cards
- Large gradient backgrounds and glassmorphism on every surface
- Mixing arbitrary spacing and radius values
- Using `Dialog` for destructive confirmation instead of `AlertDialog`
- Shipping empty/loading/error states without design treatment
- Using ad-hoc Tailwind palette classes for foundational surfaces instead of theme tokens

## Building a Custom Registry

Create your own component registry to share across projects:

### Registry Types (CLI v4)

| Type | Purpose |
|------|---------|
| `registry:ui` | Individual UI components |
| `registry:base` | Full design system payload — components, deps, CSS vars, fonts, config |
| `registry:font` | Font configuration as a first-class registry item |

### 1. Define registry.json

```json
[
  {
    "name": "my-component",
    "type": "registry:ui",
    "title": "My Component",
    "description": "A custom component",
    "files": [
      {
        "path": "components/my-component.tsx",
        "type": "registry:ui"
      }
    ],
    "dependencies": ["lucide-react"]
  }
]
```

### 2. Build

```bash
npx shadcn@latest build
# Outputs to public/r/my-component.json
```

### 3. Consume

```bash
npx shadcn@latest add https://your-domain.com/r/my-component.json
```

## Component Gotchas

### `shadcn init` Breaks Geist Font in Next.js (Tailwind v4)

`shadcn init` rewrites `globals.css` and may introduce `--font-sans: var(--font-sans)` — a circular self-reference that breaks font loading. Tailwind v4's `@theme inline` resolves CSS custom properties at **parse time**, not runtime — so even `var(--font-geist-sans)` won't work because Next.js injects that variable via className at runtime.

**The fix**: Use literal font family names in `@theme inline`:

```css
/* In @theme inline — CORRECT (literal names) */
--font-sans: "Geist", "Geist Fallback", ui-sans-serif, system-ui, sans-serif;
--font-mono: "Geist Mono", "Geist Mono Fallback", ui-monospace, monospace;

/* WRONG — circular, resolves to nothing */
--font-sans: var(--font-sans);

/* ALSO WRONG — @theme inline can't resolve runtime CSS variables */
--font-sans: var(--font-geist-sans);
```

**After running `shadcn init`**, always:
1. Replace font declarations in `@theme inline` with literal Geist font names (as shown above)
2. Move the font variable classNames from `<body>` to `<html>` in `layout.tsx`:

```tsx
// layout.tsx — font variables on <html>, not <body>
<html lang="en" className={`${geistSans.variable} ${geistMono.variable}`}>
  <body className="antialiased">
```

### Avatar Has No `size` Prop

The shadcn Avatar component does **not** accept a `size` variant prop. Control size with Tailwind classes:

```tsx
// WRONG — no size variant exists
<Avatar size="lg" />  //  TypeScript error / silently ignored

// CORRECT — use Tailwind
<Avatar className="h-12 w-12">
  <AvatarImage src={user.image} />
  <AvatarFallback>JD</AvatarFallback>
</Avatar>

// Small avatar
<Avatar className="h-6 w-6"> ... </Avatar>
```

This applies to most shadcn components — they use Tailwind classes for sizing, not variant props. If you need reusable size variants, add them yourself via `cva` in the component source.

## Common Patterns

### cn() Utility

All shadcn components use the `cn()` utility for conditional class merging:

```ts
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}
```

### Extending Components

Since you own the source code, extend components directly:

```tsx
// components/ui/button.tsx — add your custom variant
const buttonVariants = cva('...', {
  variants: {
    variant: {
      default: '...',
      destructive: '...',
      // Add custom variants
      success: 'bg-green-600 text-white hover:bg-green-700',
      premium: 'bg-gradient-to-r from-purple-500 to-pink-500 text-white',
    },
  },
})
```

### Wrapping with TooltipProvider

Many components require `TooltipProvider` at the root:

```tsx
// app/layout.tsx
import { TooltipProvider } from '@/components/ui/tooltip'

export default function RootLayout({ children }) {
  return (
    <html lang="en" className="dark">
      <body>
        <TooltipProvider>{children}</TooltipProvider>
      </body>
    </html>
  )
}
```

## Framework Support

- **Next.js** — Full support (App Router + Pages Router)
- **Vite** — Full support
- **React Router** — Full support
- **Astro** — Full support
- **Laravel** — Full support (via Inertia)
- **TanStack Start** — Full support

## Presets (CLI v4)

Presets bundle your entire design system config (colors, theme, icon library, fonts, radius) into a single shareable code. One string configures everything:

```bash
# Apply a preset during init
npx shadcn@latest init --preset <code>

# Switch presets in an existing project (reconfigures everything including components)
npx shadcn@latest init --preset <code>
```

Build custom presets on `shadcn/create` — preview how colors, fonts, and radius apply to real components before publishing.

## RTL Support (2026)

The CLI handles RTL transformation at install time:

```bash
npx shadcn@latest migrate rtl
```

Converts directional classes (`ml-4`, `left-2`) to logical properties (`ms-4`, `start-2`) automatically.

## Official Documentation

- [shadcn/ui](https://ui.shadcn.com)
- [Components](https://ui.shadcn.com/docs/components)
- [CLI](https://ui.shadcn.com/docs/cli)
- [Theming](https://ui.shadcn.com/docs/theming)
- [Custom Registry](https://ui.shadcn.com/docs/registry)
- [Registry Directory](https://ui.shadcn.com/docs/directory)
- [GitHub: shadcn/ui](https://github.com/shadcn-ui/ui); fm=text[/\A---\n(.*?)\n---\n/m, 1] or abort("missing frontmatter"); YAML.safe_load(fm, permitted_classes: [], aliases: true); puts "staged frontmatter ok"'
Verified: git diff --cached -- skills/shadcn/SKILL.md
Swarm-Agent: codex-shadcn-fix
2026-03-11 15:16:21 -06:00
John Lindquist 1aae11eab4 feat(benchmark-sandbox): add eval observability telemetry
Add per-phase timing telemetry to run-eval results and report output.
Expand artifact extraction into readable observability files with an archive manifest.

Verified: bun test tests/run-eval-build-polling.test.ts
Verified: bunx tsc --noEmit --module esnext --moduleResolution bundler --target es2022 --lib es2022 --types node .claude/skills/benchmark-sandbox/run-eval.ts
Swarm-Agent: codex-observability-v2
2026-03-11 15:16:21 -06:00
John Lindquist 5341ce8c0f Merge pull request #1 from vercel-labs/adding-telemetry
[plugin] Adding telemetry
2026-03-11 15:15:08 -06:00
melkeydev fd61d00d91 removing setup hook that doesnt exist 2026-03-11 14:11:14 -07:00
melkeydev 25c1cae890 fixing telemetry 2026-03-11 12:39:08 -07:00
melkeydev 16db2e9c95 removing timeouts 2026-03-11 12:24:48 -07:00
melkeydev 6258a7e349 adding event bridge and reviewer comments 2026-03-11 12:16:28 -07:00
melkeydev 55eeae089f adding telemetry 2026-03-11 10:46:51 -07:00
melkeydev c7a13ac307 adding telemetry 2026-03-11 09:23:07 -07:00