Files
vercel__vercel-plugin/docs/developer-guide.md
John Lindquist c6e4beacf2 refactor: complete zero-bundle orchestrator
Align the orchestrator branch with the engine-first runtime so skill
resolution, registry metadata, and operator docs all describe the
same source of truth.

Make first-session registry installs resilient in restricted
environments by treating install-plan persistence as best-effort,
which prevents hook execution from failing when the home-state cache
cannot be written.

Ploop-Iter: 2
2026-04-02 05:27:10 -06:00

408 lines
13 KiB
Markdown

# Developer Workflows & CLI Reference
This guide covers every build command, CLI tool, testing workflow, and development process in vercel-plugin.
---
## Table of Contents
- [Build Pipeline](#build-pipeline)
- [Build Commands](#build-commands)
- [Testing Architecture](#testing-architecture)
- [Pre-Commit Hook](#pre-commit-hook)
- [Playground System](#playground-system)
- [Environment Variables](#environment-variables)
- [Troubleshooting](#troubleshooting)
---
## Build Pipeline
The project has two independent build stages that combine into a single `bun run build`:
```mermaid
graph TD
subgraph "Stage 1 — Hook Compilation"
MTS["hooks/src/*.mts<br/>(TypeScript sources)"]
MTS -->|"tsup<br/>hooks/tsup.config.ts"| MJS["hooks/*.mjs<br/>(ESM, committed)"]
end
subgraph "Stage 2 — Manifest Generation"
RULES["engine/*.md<br/>(40+ rule files, YAML frontmatter)"]
RULES -->|"build-manifest.ts<br/>glob→regex pre-compile"| MANIFEST["generated/skill-rules.json<br/>(version 2, paired arrays)"]
end
MJS --> RUNTIME["Runtime: Claude Code<br/>hook execution"]
MANIFEST --> RUNTIME
```
### Data flow summary
1. **TypeScript hooks** (`hooks/src/*.mts`) compile via tsup to ESM modules (`hooks/*.mjs`). Target: `node20`, no bundling, no sourcemaps. The compiled `.mjs` files are committed to the repo so the Claude Agent SDK can execute them directly.
2. **Engine rule frontmatter** from 40+ `engine/*.md` files gets pre-compiled into `generated/skill-rules.json` with glob-to-regex conversion for fast runtime matching. The manifest uses a version 2 format with paired arrays (`pathPatterns` ↔ `pathRegexSources`, `bashPatterns` ↔ `bashRegexSources`).
Agents and commands (`agents/*.md`, `commands/*.md`) are standalone files that do not require a build step.
### Stage execution order
```
bun run build
├── bun run build:hooks # Stage 1: .mts → .mjs
└── bun run build:manifest # Stage 2: engine/*.md → skill-rules.json
```
Both stages are independent and can run in any order, but `build` runs them sequentially for simplicity.
---
## Build Commands
### `bun run build:hooks`
Compiles all TypeScript hook sources to ESM.
| Detail | Value |
|--------|-------|
| Source | `hooks/src/*.mts` |
| Output | `hooks/*.mjs` |
| Tool | tsup with `hooks/tsup.config.ts` |
| Target | `node20`, no bundling, no sourcemaps |
Run this after editing any `.mts` file. The pre-commit hook runs it automatically when `.mts` files are staged.
### `bun run build:manifest`
Generates the skill rules manifest from engine rule frontmatter.
| Detail | Value |
|--------|-------|
| Script | `scripts/build-manifest.ts` |
| Input | `engine/*.md` (40+ rule files) |
| Output | `generated/skill-rules.json` |
The manifest pre-compiles glob patterns to regex at build time so runtime hooks avoid expensive parsing. Version 2 format with paired arrays (`pathPatterns` ↔ `pathRegexSources`).
### `bun run build`
Runs both stages sequentially:
```
bun run build:hooks && bun run build:manifest
```
### `bun run typecheck`
Runs TypeScript type checking on hook sources without emitting files:
```
tsc -p hooks/tsconfig.json --noEmit
```
### `bun run doctor`
Runs `vercel-plugin doctor` (see [docs/cli-reference.md](cli-reference.md) for full details). Self-diagnosis for the plugin setup.
---
## Testing Architecture
### Running tests
```bash
bun test # Typecheck + all test files
bun test tests/<file>.test.ts # Single test file
bun run test:update-snapshots # Regenerate golden snapshots
```
`bun test` runs typecheck first (`tsc -p hooks/tsconfig.json --noEmit`), then all test files.
### Test categories
The test suite is organized into functional categories:
```mermaid
graph LR
subgraph "Hook Integration"
A1["pretooluse-skill-inject"]
A2["user-prompt-submit"]
A3["posttooluse-validate"]
A4["session-start-profiler"]
A5["session-start-seen-skills"]
A6["session-end-cleanup"]
end
subgraph "Subagent Lifecycle"
B1["subagent-fresh-env"]
B2["subagent-lifecycle-integration"]
B3["subagent-start-context"]
B4["subagent-start-bootstrap-routing"]
B5["subagent-scope-dedup"]
B6["subagent-state"]
B7["session-timeline-subagent"]
end
subgraph "Pattern Matching"
C1["patterns"]
C2["fuzz-glob"]
C3["fuzz-yaml"]
C4["prompt-signals"]
C5["prompt-analysis"]
C6["prompt-patterns-lexical"]
C7["unified-ranker"]
end
subgraph "Snapshots & Golden Files"
D1["snapshot-runner"]
D2["snapshots"]
end
subgraph "Validation & Skill Map"
E1["validate"]
E2["validate-rules"]
E3["build-skill-map"]
E4["skill-map-frontmatter"]
E5["external-skill-resolution"]
end
subgraph "CLI & Infrastructure"
G1["cli-explain"]
G2["hooks-json-structural"]
G3["doctor-subagent-hooks"]
G4["hook-sync"]
G5["logger"]
G6["vercel-config"]
end
subgraph "Scenario & Specialized"
H1["notion-clone-patterns"]
H2["slack-clone-patterns"]
H3["tsx-review-trigger"]
H4["dev-server-verify"]
H5["verification-skill"]
H6["verification-logging"]
H7["verification-intent-routing"]
H8["redaction"]
H9["stemmer"]
H10["lexical-index"]
end
subgraph "Benchmark"
I1["benchmark-pipeline"]
I2["benchmark-analyze"]
end
```
#### Hook integration tests
End-to-end tests for each hook entry point. They simulate Claude Agent SDK hook invocations with realistic tool input and verify the correct skills are injected, dedup state is maintained, and output conforms to `SyncHookJSONOutput`.
| Test file | Hook under test | Key assertions |
|-----------|----------------|----------------|
| `pretooluse-skill-inject` | PreToolUse | Path/bash/import matching, priority ranking, budget enforcement, dedup |
| `user-prompt-submit` | UserPromptSubmit | Prompt signal scoring (phrases/allOf/anyOf/noneOf), 2-skill cap, 8KB budget |
| `posttooluse-validate` | PostToolUse | Validation rule matching, severity levels, `skipIfFileContains` |
| `session-start-profiler` | SessionStart | Config file scanning, dependency detection, greenfield mode |
| `session-start-seen-skills` | SessionStart | Env var initialization, claim dir creation |
| `session-end-cleanup` | SessionEnd | Temp file deletion, claim dir cleanup |
#### Pattern matching tests
Unit tests for the matching and compilation layer. Cover glob-to-regex conversion, bash command regex, import pattern detection, YAML parsing edge cases, and prompt signal scoring.
#### Snapshot tests
Golden-file regression tests. `snapshot-runner` generates skill injection metadata for each `vercel.json` fixture and compares against committed baselines. Update with `bun run test:update-snapshots`.
#### Validation tests
Test the YAML frontmatter parser, skill map construction, structural validation rules, and external skill resolution. Exercises the custom `parseSimpleYaml` parser's intentional differences from `js-yaml` (bare `null` → string `"null"`, bare booleans → strings, unclosed `[` → scalar).
#### Build & template tests
Test the template include engine: marker regex matching, section extraction with nested headings, frontmatter field resolution, code block fence skipping, and full compilation pipeline.
#### Benchmark tests
Performance regression tests for the injection pipeline. `benchmark-pipeline` measures pattern compilation and matching latency; `benchmark-analyze` validates that results stay within acceptable bounds.
#### CLI tests
Tests for `vercel-plugin explain` covering target type detection (file vs bash), pattern matching output, priority calculations with profiler/vercel.json boosts, budget simulation, and collision detection.
#### Scenario tests
Real-world regression tests that simulate specific project types (Notion clone, Slack clone) to verify correct skill injection for realistic file and dependency combinations.
---
## Pre-Commit Hook
The `.git/hooks/pre-commit` script automates hook compilation.
When any `hooks/src/*.mts` file is staged:
```
1. Typecheck: bun run typecheck
2. Compile: bun run build:hooks
3. Stage: git add hooks/*.mjs
```
---
## Playground System
The playground generates static skill files for external AI coding tools. Lives in `.playground/`.
### Structure
```
.playground/
├── generate-all.ts # Unified CLI entry point
├── _shared/
│ ├── emitter.ts # Context creation + skill flattening
│ ├── plugin-discovery.ts # Discovers skills from plugin root
│ ├── skill-discovery.ts # Skill data extraction
│ ├── types.ts # Shared types (DiscoveredSkill, PluginManifest, etc.)
│ └── marker-patch.ts # {{include:…}} marker resolution for external tools
├── codex-cli/generate.ts # → .codex/ directory structure
├── cursor/generate.ts # → .cursor/rules/
├── vscode-copilot/generate.ts # → .github/copilot-instructions.md
├── opencode/generate.ts # → .opencode/
├── gemini-cli/generate.ts # → .gemini/commands/
├── gemini-code-assist/generate.ts # → .gemini/skills/
├── _fixtures/ # Test plugins (full, minimal, collision, oversized, etc.)
└── _snapshots/ # Golden output snapshots
```
### Running the generator
```bash
bun run playground:generate
```
**Options:**
| Flag | Description |
|------|-------------|
| `--plugins <dir>` | Plugin root to discover skills from (default: `.playground/_fixtures`) |
| `--out <dir>` | Output directory (default: `.playground/_output`) |
| `--dry-run` | Preview without writing files |
| `--target <name>` | Comma-separated generator names (e.g., `cursor,codex-cli`) |
**Supported generators:** `codex-cli`, `cursor`, `vscode-copilot`, `opencode`, `gemini-cli`, `gemini-code-assist`
### Workflow
```mermaid
sequenceDiagram
participant Dev as Developer
participant Gen as generate-all.ts
participant Disc as plugin-discovery.ts
participant Emit as Per-tool generator
Dev->>Gen: bun run playground:generate
Gen->>Disc: Discover plugins (skills, commands, agents, hooks)
Disc-->>Gen: DiscoveredPlugin[]
Gen->>Emit: Spawn generators in parallel (one per target tool)
Emit-->>Gen: Generated files + stats
Gen-->>Dev: JSON report (file counts, durations, skill counts)
```
Output is a JSON report with file counts, durations, and per-generator statistics.
---
## Environment Variables
These variables control runtime behavior. Set them before running Claude Code or in tests.
| Variable | Default | Description |
|----------|---------|-------------|
| `VERCEL_PLUGIN_LOG_LEVEL` | `off` | Logging verbosity: `off`, `summary`, `debug`, `trace` |
| `VERCEL_PLUGIN_DEBUG` | — | Legacy: `1` maps to `debug` level |
| `VERCEL_PLUGIN_SEEN_SKILLS` | `""` | Comma-delimited already-injected skill slugs |
| `VERCEL_PLUGIN_HOOK_DEDUP` | — | Set to `off` to disable deduplication |
| `VERCEL_PLUGIN_LIKELY_SKILLS` | — | Profiler-detected skills (comma-delimited, +5 boost) |
| `VERCEL_PLUGIN_GREENFIELD` | — | `true` when project is empty (set by profiler) |
| `VERCEL_PLUGIN_INJECTION_BUDGET` | `18000` | PreToolUse byte budget |
| `VERCEL_PLUGIN_PROMPT_INJECTION_BUDGET` | `8000` | UserPromptSubmit byte budget |
| `VERCEL_PLUGIN_REVIEW_THRESHOLD` | `3` | TSX edits before `react-best-practices` injection |
| `VERCEL_PLUGIN_TSX_EDIT_COUNT` | `0` | Current `.tsx` edit count |
| `VERCEL_PLUGIN_AUDIT_LOG_FILE` | — | Audit log path or `off` |
---
## Troubleshooting
### Manifest parity errors from `doctor`
The `generated/skill-rules.json` is out of sync with live `engine/*.md` files.
**Fix:**
```bash
bun run build:manifest
```
### Typecheck failures
Hook source uses TypeScript features that need compilation. The `tsc` target is `hooks/tsconfig.json`.
**Fix:**
```bash
bun run typecheck # See errors
# Fix the .mts files, then:
bun run build:hooks
```
### Hook timeout (5-second limit)
All PreToolUse, UserPromptSubmit, PostToolUse, SubagentStart, and SubagentStop hooks have a 5-second timeout. If you add many skills or patterns, `doctor` will warn you.
**Diagnose:**
```bash
bun run doctor
```
**Mitigations:**
- Use the pre-built manifest (`build:manifest`) to avoid live YAML scanning
- Consolidate low-priority skills
- Increase pattern specificity to reduce false-positive matching
### Dedup not working (skills injected twice)
**Check:** Is `session-start-seen-skills.mjs` running on SessionStart? Run `doctor` to verify.
**Debug:** Set `VERCEL_PLUGIN_LOG_LEVEL=debug` to see dedup strategy selection and claim attempts in stderr.
### Pre-commit hook not running
Verify the hook exists and is executable:
```bash
ls -la .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
```
### Playground generator fails
Ensure the plugin root has an `engine/` directory with valid rule files:
```bash
bun run playground:generate --dry-run
```
The `--dry-run` flag previews without writing, showing discovery errors on stderr.
### Tests fail after adding a new skill
After adding a new `engine/<name>.md`:
```bash
bun run build:manifest # Update manifest
bun run test:update-snapshots # Update golden snapshots
bun test # Verify everything passes
```