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.
11 KiB
CLI Reference
The vercel-plugin CLI provides two commands for debugging and validating the skill injection system: explain and doctor.
Entry point: src/cli/index.ts
vercel-plugin <command> [options]
Table of Contents
explain
Shows which skills match a given file path or bash command, with priority breakdown, budget simulation, and collision detection. Mirrors the runtime selection pipeline used by pretooluse-skill-inject.mjs.
Usage
vercel-plugin explain <target> [options]
The <target> is a file path or bash command string. The CLI auto-detects the target type based on heuristics:
- Contains spaces + starts with a known CLI tool (
vercel,npm,bun, etc.) → bash - Contains flag-like patterns (
--flag) → bash - Otherwise → file path
Flags
| Flag | Type | Default | Description |
|---|---|---|---|
--json |
boolean | false |
Machine-readable JSON output (full ExplainResult structure) |
--project <path> |
string | auto-detected | Plugin root directory. Must contain a skills/ directory |
--likely-skills <s1,s2> |
string | — | Comma-delimited skill slugs to simulate profiler boost (+5 priority each) |
--budget <bytes> |
number | 12000 |
Override injection byte budget for simulation |
Pipeline simulation
The explain command replicates the runtime injection pipeline:
graph TD
TARGET["Target input<br/>(file path or bash command)"]
TARGET --> DETECT["Auto-detect target type<br/>(file vs bash)"]
DETECT --> COMPILE["Compile skill patterns<br/>(from manifest or live scan)"]
COMPILE --> MATCH["Match target against<br/>path/bash/import patterns"]
MATCH --> VCONFIG["Apply vercel.json routing<br/>(±10 priority)"]
VCONFIG --> PROFILER["Apply profiler boost<br/>(+5 for --likely-skills)"]
PROFILER --> RANK["Rank by effective priority<br/>(DESC, then alpha ASC)"]
RANK --> BUDGET["Simulate byte budget<br/>(MAX_SKILLS=3, default 12KB)"]
BUDGET --> OUTPUT["Output: matches, collisions,<br/>injection modes, budget usage"]
Injection modes
Each matched skill is assigned an injection mode:
| Mode | Meaning |
|---|---|
full |
Full SKILL.md body injected within budget |
summary |
Body exceeded budget; summary field used instead |
droppedByCap |
Exceeded MAX_SKILLS hard cap (3 skills) |
droppedByBudget |
Neither body nor summary fit within remaining budget |
The first matched skill always gets full injection regardless of size. Subsequent skills must fit within the remaining budget.
Match types
| Match type | Description |
|---|---|
file:full |
Full glob pattern match against file path |
file:basename |
Basename-only match |
file:suffix |
File extension/suffix match |
file:import |
Import/require pattern found in file content |
bash:full |
Regex match against bash command string |
Human-readable output
Target: middleware.ts (file)
Skills in manifest: 43
Budget: 8234 / 12000 bytes
Matched: 3 skill(s)
Injected: 2 | Summary-only: 1
[INJECT] routing-middleware (4521 bytes)
priority: 8
pattern: middleware.{ts,js} (full)
reason: injected #1 (4521B, total 4521B / 12000B)
[INJECT] nextjs (3713 bytes)
priority: 6
pattern: **/*.{ts,tsx} (suffix)
reason: injected #2 (3713B, total 8234B / 12000B)
JSON output
With --json, the full ExplainResult object is emitted:
interface ExplainResult {
target: string; // Input target
targetType: "file" | "bash"; // Detected type
toolName?: string; // Explicit tool override
matches: ExplainMatch[]; // All matched skills with injection details
collisions: ExplainCollision[]; // Skills sharing the same effective priority
injectedCount: number; // Skills that will be injected (full + summary)
cappedCount: number; // Skills dropped by cap or budget
droppedByBudgetCount: number;
summaryOnlyCount: number;
skillCount: number; // Total skills in manifest
budgetBytes: number; // Budget used for simulation
usedBytes: number; // Actual bytes consumed
buildWarnings: string[]; // Warnings from SKILL.md parsing
}
Collision detection
When multiple skills share the same effective priority, explain reports a collision. At runtime, ties are broken alphabetically — the collision warning helps skill authors adjust priorities to get deterministic ordering.
Collisions:
- vercel-functions, routing-middleware: 2 skills share effective priority 5; tie-broken alphabetically
doctor
Self-diagnosis command that validates the plugin setup. Checks manifest consistency, hook configuration, dedup state, skill validity, template freshness, and subagent hook registration.
Usage
vercel-plugin doctor [options]
Flags
| Flag | Type | Default | Description |
|---|---|---|---|
--json |
boolean | false |
Machine-readable JSON output (full DoctorResult structure) |
--project <path> |
string | auto-detected | Plugin root directory |
Checks performed
1. skill-validation — Skill map validity
Loads all skills/*/SKILL.md files and validates:
- Valid YAML frontmatter is present
- Required fields exist (name, description, summary, metadata)
- Pattern arrays contain valid entries
Reports both errors (invalid skills) and warnings (non-critical issues).
2. manifest-parity — Manifest vs live scan consistency
Compares generated/skill-manifest.json against a live scan of all SKILL.md files:
| Sub-check | Severity | Condition |
|---|---|---|
| Missing manifest file | warning | generated/skill-manifest.json does not exist |
| Parse failure | error | Manifest JSON is malformed |
| Skills in live but not manifest | error | New skills added without rebuilding |
| Skills in manifest but not live | error | Skills deleted without rebuilding |
| Priority drift | error | Priority value differs between live and manifest |
| Pattern drift | error | pathPatterns or bashPatterns differ |
Fix: bun run build:manifest
3. hook-timeout — Performance risk assessment
Warns when the number of skills or patterns approaches levels that could cause the 5-second hook timeout:
| Threshold | Severity | Trigger |
|---|---|---|
| 50+ skills | warning | liveSkillCount > 50 |
| 200+ total patterns | warning | totalPatterns > 200 |
Mitigation: Use the pre-built manifest, consolidate low-priority skills, increase pattern specificity.
4. dedup — Deduplication state
Validates the VERCEL_PLUGIN_SEEN_SKILLS environment variable and dedup strategy:
| Condition | Severity | Message |
|---|---|---|
VERCEL_PLUGIN_HOOK_DEDUP=off |
warning | Dedup is explicitly disabled |
| Invalid format | error | Expected empty or comma-delimited slugs |
| Env var not set | warning | Dedup limited to single invocation |
5. template-staleness — Generated file freshness
Checks whether .md.tmpl templates or SKILL.md sources are newer than their generated .md outputs:
| Condition | Severity |
|---|---|
| Template has no generated output | error |
| Template is newer than output | error |
| SKILL.md is newer than output | warning |
Fix: bun run build:from-skills
6. subagent-hooks — Subagent hook registration
Validates that hooks/hooks.json has proper SubagentStart and SubagentStop entries:
| Check | Severity | Description |
|---|---|---|
| Missing hook entry | error | Required event not registered |
| Timeout too high | warning | Exceeds recommended 5-second max |
| No matcher | warning | Hook won't match any agent types |
| Uncovered agent types | warning | Expected types (Explore, Plan, general-purpose) not covered by matchers |
Output format
vercel-plugin doctor
====================
Skills (live scan): 43
Skills (manifest): 43
Total patterns: 127
Dedup strategy: env-var
All checks passed.
Result: 0 error(s), 0 warning(s)
When issues are found:
vercel-plugin doctor
====================
Skills (live scan): 43
Skills (manifest): 42
Total patterns: 127
Dedup strategy: env-var
Errors (1):
[manifest-parity] Skills in live scan but missing from manifest: vercel-queues
-> Run `bun run build:manifest` to regenerate
Warnings (1):
[template-staleness] A SKILL.md was modified after commands/deploy.md was last generated
-> Run `bun run build:from-skills` to regenerate (skill content may have changed)
Result: 1 error(s), 1 warning(s)
JSON output
With --json, the full DoctorResult object is emitted:
interface DoctorResult {
issues: DoctorIssue[];
summary: {
manifestSkillCount: number | null;
liveSkillCount: number;
totalPatterns: number;
dedupStrategy: string; // "env-var" | "disabled" | "memory-only"
};
}
interface DoctorIssue {
severity: "error" | "warning";
check: string; // e.g., "manifest-parity", "hook-timeout"
message: string;
hint?: string; // Suggested fix
}
Exit codes
| Code | Meaning |
|---|---|
0 |
Success (explain: results returned; doctor: all checks pass) |
1 |
Issues found (doctor: at least one error-severity issue) |
2 |
Unexpected failure (missing arguments, parse errors, no skills/ directory) |
Examples & user stories
Debugging why a skill isn't injected
# See all skills that match your API route file
vercel-plugin explain app/api/chat/route.ts
# Simulate what happens when the profiler detects ai-gateway
vercel-plugin explain app/api/chat/route.ts --likely-skills ai-gateway
# Check if budget is the bottleneck
vercel-plugin explain app/api/chat/route.ts --budget 30000
Validating after adding a new skill
# 1. Build the manifest
bun run build:manifest
# 2. Verify the new skill matches expected files
vercel-plugin explain "the/file/it/should/match.ts"
# 3. Run full diagnostics
vercel-plugin doctor
CI integration
# In your CI pipeline, verify plugin health
vercel-plugin doctor --json | jq '.issues | length'
# Check template freshness (non-zero exit on drift)
bun run build:from-skills:check
Comparing priorities across tools
# JSON output for scripting — pipe to jq
vercel-plugin explain vercel.json --json | jq '.matches[] | {skill, effectivePriority, injectionMode}'