Developer Guide

July 22, 2026 · View on GitHub

Documentation for contributors to Impeccable.

Architecture

The skill at skill/ is transformed into provider-specific formats by a config-driven factory. Each provider is defined as a config object in scripts/lib/transformers/providers.js -- adding a new provider requires only a new config entry.

For detailed harness capabilities (which frontmatter fields each supports, placeholder systems, directory structures), see HARNESSES.md.

Source Format

Skill (skill/SKILL.src.md)

---
name: skill-name
description: What this skill provides
argument-hint: "[target]"
user-invocable: true
license: License info (optional)
compatibility: Environment requirements (optional)
---

Your skill instructions here...

Frontmatter fields (based on Agent Skills spec):

  • name (required): Skill identifier (1-64 chars, lowercase/numbers/hyphens)
  • description (required): What the skill provides (1-1024 chars)
  • user-invocable (optional): Boolean -- if true, the skill can be invoked as a slash command
  • argument-hint (optional): Hint shown during autocomplete (e.g., [target], [area (feature, page...)])
  • license (optional): License/attribution info
  • compatibility (optional): Environment requirements (1-500 chars)
  • metadata (optional): Arbitrary key-value pairs
  • allowed-tools (optional, experimental): Pre-approved tools list

Body placeholders (replaced per-provider during build):

  • {{model}} -- Provider-specific model name (e.g., "Claude", "Gemini", "GPT")
  • {{config_file}} -- Provider-specific config file (e.g., "CLAUDE.md", ".cursorrules")
  • {{ask_instruction}} -- How to ask the user for clarification
  • {{command_prefix}} -- Slash command prefix (/ for most, $ for Codex)
  • {{available_commands}} -- Comma-separated list of user-invocable commands

Building

Developer Lab URLs

No-index visual harnesses and internal inspectors live under /labs/<subject>. Use a short subject noun (/labs/live-ui, later /labs/detector), not a second -lab suffix. Stable public references such as /docs and /design-system stay top-level. Keep legacy top-level lab routes working through redirects when a lab moves; migrate existing exceptions when that surface is next changed rather than duplicating the page.

Prerequisites

  • Bun (fast JavaScript runtime and package manager)
  • No external dependencies required

Commands

# Build all provider formats
bun run build

# Clean dist folder
bun run clean

# Rebuild from scratch
bun run rebuild

What Gets Generated

source/                          -> dist/
  skills/{name}/SKILL.md           {provider}/{configDir}/skills/{name}/SKILL.md

Each provider gets its own output directory.

Build System Details

The build system uses a factory pattern under scripts/:

scripts/
  build.js                        # Main orchestrator
  lib/
    utils.js                      # Frontmatter parsing, placeholder replacement, YAML generation
    zip.js                        # ZIP bundle generation
    transformers/
      factory.js                  # createTransformer() -- generates transformer functions from config
      providers.js                # PROVIDERS config map -- one entry per provider
      index.js                    # Re-exports factory-generated transformer functions

Adding a New Provider

  1. Add a placeholder config to PROVIDER_PLACEHOLDERS in scripts/lib/utils.js:

    'my-provider': {
      model: 'MyModel',
      config_file: 'CONFIG.md',
      ask_instruction: 'ask the user directly to clarify.',
      command_prefix: '/'
    }
    
  2. Add a provider config to PROVIDERS in scripts/lib/transformers/providers.js:

    'my-provider': {
      provider: 'my-provider',
      configDir: '.my-provider',
      displayName: 'My Provider',
      frontmatterFields: ['user-invocable', 'argument-hint', 'license'],
    }
    
  3. Run bun run build -- the provider is automatically picked up by the build loop.

  4. Update HARNESSES.md with the provider's capabilities.

Provider Config Options

FieldDescription
providerKey for output directory and placeholder lookup
configDirDot-directory name (e.g., .claude)
displayNameHuman-readable name for build logs
frontmatterFieldsWhich optional fields to emit (see factory.js FIELD_SPECS)
bodyTransformOptional (body, skill) => body function for post-processing
placeholderProviderOverride which PROVIDER_PLACEHOLDERS key to use (for variants sharing config)

Key Functions

  • createTransformer(config): Factory that returns a transformer function from a provider config
  • parseFrontmatter(): Extracts YAML frontmatter and body from SKILL.md files
  • readSourceFiles(): Reads skill/SKILL.src.md plus its reference/ and scripts/ siblings
  • replacePlaceholders(): Substitutes {{model}}, {{config_file}}, etc. per provider
  • generateYamlFrontmatter(): Serializes objects to YAML frontmatter (auto-quotes values starting with [ or {)

Testing

bun run test                  # Default suite — unit + static fixtures (no API keys needed)
bun run test:live-e2e         # Opt-in — full-cycle live-mode E2E across framework fixtures (~2 min, needs `npx playwright install chromium` once)
bun run test:skill-behavior   # Opt-in — LLM-backed checks that the SKILL.md Setup flow actually drives the agent (~5 min, costs cents, needs `.env`)

The skill-behavior suite runs three providers (claude-haiku-4-5, gpt-5.4-mini, gemini-3.1-flash-lite — the cheapest tier of each, every run) with the source skill/SKILL.src.md inlined as the system prompt and a workspace-scoped bash/read/write/list tool set. It then asserts on the tool-call trace, not on free-form output. Use it whenever you edit skill/SKILL.src.md's Setup section, skill/scripts/context.mjs, or any Setup-touching reference (teach.md, document.md, brand.md, product.md, sub-command refs). Per-scenario assertions and the current baseline (21-22/24) live in tests/skill-behavior/README.md. Provider keys live in repo-root .env (gitignored); missing keys skip cleanly.

Best Practices

Skill Writing

  1. Focused scope: One clear domain per skill
  2. Clear descriptions: Make purpose obvious
  3. Clear instructions: LLM should understand exactly what to do
  4. Include examples: Where they clarify intent
  5. State constraints: What NOT to do as clearly as what to do
  6. Test across providers: Verify it works in multiple contexts. For Setup-related edits to skill/, bun run test:skill-behavior automates this across three providers.

Reference Documentation

Repository Structure

impeccable/
  skill/                           # Edit these! Source of truth
    SKILL.src.md                   # Frontmatter, shared design laws, command router
    reference/                     # One <command>.md per command + domain references
    scripts/                       # Skill runtime scripts (hooks, context.mjs, etc.)
    agents/                        # Nested subagent definitions
  cli/                             # Standalone `impeccable` CLI (npm package)
  site/                            # impeccable.style (Astro)
  extension/                       # Chrome extension
  functions/                       # Cloudflare Pages Functions
  plugin/                          # Committed Claude Code plugin build output
  dist/                            # Generated provider output (gitignored)
  scripts/
    build.js                       # Main orchestrator
    lib/
      utils.js                     # Shared utilities
      zip.js                       # ZIP generation
      transformers/
        factory.js                 # Config-driven transformer factory
        providers.js               # Provider config map
        index.js                   # Re-exports
  tests/                           # Bun test suite
  docs/
    HARNESSES.md                   # Provider capabilities reference
    STYLE.md                       # Editorial style guide
    adr-live-variant-mode.md       # Live mode architecture decision record
    DEVELOP.md                     # This file
  README.md                        # User documentation

Troubleshooting

Build fails with YAML parsing errors

  • Check frontmatter indentation (YAML is indent-sensitive)
  • Ensure --- delimiters are on their own lines
  • Values starting with [ or { are auto-quoted; other special YAML chars may need manual quoting

Output doesn't match expectations

  • Check the provider config in scripts/lib/transformers/providers.js
  • Verify source file has correct frontmatter structure
  • Run bun run rebuild to ensure clean build

Provider doesn't recognize the files

  • Check installation path for your provider
  • Verify file naming matches provider requirements
  • Consult HARNESSES.md for provider-specific details

Questions?

Open an issue first. If a maintainer approves the direction, offer to follow up with a PR. Regular contributors pbakaus and abdulwahabone may open PRs directly.