npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

spec-snake

v0.0.1-beta.15

Published

AI-powered design document generator

Downloads

1,710

Readme

Spec Snake Beta

AI-powered design document generator

日本語版

  • Config Base
    • Define scenarios, forms, and prompts in TypeScript config files
  • Multi Step Form
    • Collect required information step by step through wizard-style forms
  • AI Generation
    • Generate high-quality documents from collected information using Claude Agent SDK
  • MCP Integration
    • Connect with external tools like Figma to generate more detailed documents

Concept

Documents are managed based on three core concepts:

  • Scenario
    • A unit representing the type of document. Define scenarios for each purpose such as technical design docs, implementation plans, etc.
  • Step
    • Form input steps within a scenario. Collect information progressively through multiple steps
  • Document
    • The generated markdown document. Created from form input and AI

Installation

# npm
npm install spec-snake@beta

# yarn
yarn add spec-snake@beta

# pnpm
pnpm add spec-snake@beta

Requirements

  • Node.js: 18 or higher
  • Claude Code: Claude Code must be installed

CLI Commands

init - Initialize config file

Creates a new config file.

npx spec-snake-beta init

Options:

| Option | Alias | Default | Description | | ---------- | ----- | ---------------------- | ------------------------ | | --output | -o | spec-snake.config.ts | Output file path | | --force | -f | false | Overwrite existing files |

Examples:

# Create with default filename
npx spec-snake-beta init

# Create with custom filename
npx spec-snake-beta init -o my-config.ts

# Overwrite existing file
npx spec-snake-beta init -f

start - Start the server

Loads the config file and starts the Web UI server.

npx spec-snake-beta start

Options:

| Option | Alias | Default | Description | | ---------- | ----- | ---------------------- | ---------------- | | --config | -c | spec-snake.config.ts | Config file path | | --port | -p | 3000 | Server port | | --host | - | localhost | Host to bind |

Examples:

# Start with default settings
npx spec-snake-beta start

# Start with custom config and port
npx spec-snake-beta start -c my-config.ts -p 8080

# Listen on all interfaces
npx spec-snake-beta start --host 0.0.0.0

Config File

Config Examples

See the examples/ directory for config file examples.

Also refer to src/types.ts for configurable options.

Config Structure

import { defineConfig, defineScenario } from "spec-snake";

export default defineConfig({
  scenarios: [
    defineScenario({
      id: "design-doc",
      name: "Design Document",
      steps: [
        {
          slug: "overview",
          title: "Overview",
          description: "Project overview",
          name: "overview",
          fields: [
            { id: "title", type: "input", label: "Title", description: "" },
          ],
        },
      ],
      prompt: "...",
      outputDir: "docs",
      filename: ({ formData, timestamp }) =>
        `${formData.overview?.title ?? "untitled"}-${timestamp}.md`,
    }),
  ],
  permissions: {
    allowSave: true,
  },
  // AI mode: 'stream' (default), 'sync', or 'mock'
  // ai: 'mock', // Use mock mode for development without AI
});

Type Definitions

Config - Root config object

| Property | Type | Required | Description | | ------------- | ------------- | -------- | ----------------------------------------------------------------- | | scenarios | Scenario[] | Yes | Array of scenarios | | permissions | Permissions | Yes | Global permission config | | ai | AiMode | No | AI mode ('stream' | 'sync' | 'mock'). Default: 'stream' |

AiMode - AI mode for document generation

| Value | Description | | ---------- | -------------------------------------------------------- | | 'stream' | AI enabled with SSE streaming (default) | | 'sync' | AI enabled, returns full response at once | | 'mock' | AI disabled, returns fixed mock response for development |

Permissions - Permission settings

| Property | Type | Description | | ----------- | --------- | ---------------------------- | | allowSave | boolean | Whether to allow saving docs |

Scenario - Scenario definition. Each scenario represents one document type

| Property | Type | Required | Description | | ------------ | -------------------- | -------- | ------------------------------ | | id | string | Yes | Unique identifier used in URL | | name | string | Yes | Display name | | steps | Step[] | Yes | Form wizard steps | | prompt | Function | Yes | Prompt function sent to Claude | | outputDir | string | No | Directory for saving documents | | filename | string \| Function | No | Custom filename for documents | | aiSettings | AiSettings | No | Claude Agent SDK settings | | hooks | ScenarioHooks | No | Lifecycle hooks |

Step - Each step in the multi-step form

| Property | Type | Required | Description | | ------------- | --------- | -------- | ------------------------------ | | slug | string | Yes | URL-friendly identifier | | title | string | Yes | Title displayed in step header | | description | string | Yes | Description shown below title | | name | string | Yes | Key used in formData | | fields | Field[] | Yes | Array of fields in this step |

Field Types

InputField - Text input

{
  type: 'input',
  id: 'title',
  label: 'Title',
  description: 'Field description',
  placeholder: 'Placeholder',
  required: true,
  inputType: 'text' | 'date' | 'url',
  suggestions: ['Option 1', 'Option 2']
}

TextareaField - Multi-line text

{
  type: 'textarea',
  id: 'description',
  label: 'Description',
  description: 'Field description',
  rows: 4
}

SelectField - Dropdown select

{
  type: 'select',
  id: 'priority',
  label: 'Priority',
  description: 'Field description',
  options: [
    { value: 'high', label: 'High' },
    { value: 'medium', label: 'Medium' },
    { value: 'low', label: 'Low' }
  ]
}

CheckboxField - Checkbox

{
  type: 'checkbox',
  id: 'agree',
  label: 'I agree',
  description: 'Field description'
}

GridField - Layout for arranging fields in columns

{
  type: 'grid',
  columns: 2,
  fields: [
    { type: 'input', id: 'firstName', label: 'First Name' },
    { type: 'input', id: 'lastName', label: 'Last Name' }
  ]
}

RepeatableLayout - Layout for repeating fields

Allows users to add multiple instances of a field or group.

// Single field repeatable
{
  type: 'repeatable',
  id: 'tags',
  minCount: 1,  // Minimum entries (optional)
  field: { type: 'input', id: 'name', label: 'Tag', description: '' }
}
// formData: { tags: [{ name: 'tag1' }, { name: 'tag2' }] }

// Group repeatable (multiple fields per entry)
{
  type: 'repeatable',
  id: 'libraries',
  minCount: 1,
  field: {
    type: 'group',
    fields: [
      { type: 'input', id: 'name', label: 'Library Name', description: '' },
      { type: 'input', id: 'url', label: 'URL', description: '', inputType: 'url' }
    ]
  }
}
// formData: { libraries: [{ name: 'React', url: 'https://...' }, ...] }

GroupLayout - Visual grouping of fields

Groups fields together visually (no repetition). To repeat a group, wrap it in a RepeatableLayout.

{
  type: 'group',
  fields: [
    { type: 'input', id: 'firstName', label: 'First Name', description: '' },
    { type: 'input', id: 'lastName', label: 'Last Name', description: '' }
  ]
}
// formData: { firstName: '...', lastName: '...' }

Conditional Field Display

Fields can be conditionally shown/hidden based on other field values using the when property.

Single field conditions

// Show when priority is 'high'
{ type: 'input', id: 'deadline', label: 'Deadline', when: { field: 'priority', is: 'high' } }

// Show when priority is 'high' or 'medium' (array match)
{ type: 'textarea', id: 'risk', label: 'Risk', when: { field: 'priority', is: ['high', 'medium'] } }

// Show when priority is NOT 'low'
{ type: 'input', id: 'reviewer', label: 'Reviewer', when: { field: 'priority', isNot: 'low' } }

// Show when checkbox is checked
{ type: 'input', id: 'date', label: 'Date', when: { field: 'has_deadline', is: true } }

// Show when field is not empty
{ type: 'textarea', id: 'notes', label: 'Notes', when: { field: 'title', isNotEmpty: true } }

// Show when field is empty
{ type: 'input', id: 'fallback', label: 'Fallback', when: { field: 'title', isEmpty: true } }

AND / OR conditions

// AND condition: Show when priority is 'high' AND title is not empty
{
  type: 'textarea',
  id: 'stakeholders',
  label: 'Stakeholders',
  when: {
    and: [
      { field: 'priority', is: 'high' },
      { field: 'title', isNotEmpty: true }
    ]
  }
}

// OR condition: Show when priority is 'high' OR description is not empty
{
  type: 'input',
  id: 'review_date',
  label: 'Review Date',
  when: {
    or: [
      { field: 'priority', is: 'high' },
      { field: 'description', isNotEmpty: true }
    ]
  }
}

// Nested AND/OR: Show when priority is 'high' OR (priority is 'medium' AND deadline is set)
{
  type: 'textarea',
  id: 'escalation_plan',
  label: 'Escalation Plan',
  when: {
    or: [
      { field: 'priority', is: 'high' },
      {
        and: [
          { field: 'priority', is: 'medium' },
          { field: 'deadline', isNotEmpty: true }
        ]
      }
    ]
  }
}

Cross-section field references (dot notation)

// Reference fields from other steps using dot notation
{ type: 'input', id: 'design_link', label: 'Design', when: { field: 'overview.priority', is: 'high' } }

Note: Hidden fields are automatically excluded from validation and form submission. Function-based conditions are not supported (conditions must be JSON-serializable).

AiSettings - Claude Agent SDK settings

| Property | Type | Description | | --------------------------------- | --------------------------------- | ----------------------------------------------------------- | | model | string | Model to use (e.g., claude-sonnet-4-5-20250929) | | fallbackModel | string | Fallback model | | maxTurns | number | Maximum turns | | maxBudgetUsd | number | Budget limit in USD | | tools | object | Tool settings ({ type: 'preset', preset: 'claude_code' }) | | allowedTools | string[] | Allowed tools | | disallowedTools | string[] | Disallowed tools | | permissionMode | PermissionMode | Permission mode | | allowDangerouslySkipPermissions | boolean | Skip permission checks | | mcpServers | Record<string, McpServerConfig> | MCP server config |

Available Tools

  • File operations: Read, Write, Edit, Glob, Grep, NotebookEdit
  • Command execution: Bash
  • Web: WebSearch, WebFetch
  • Agents: Task, TodoWrite
  • Code completion: LSP
  • MCP tools: mcp__<server>__<tool> format

scenario.hooks - Lifecycle hooks

{
  // Called after preview generation
  onPreview: async ({ formData, content }) => {
    console.log('Preview generated');
  },
  // Called after document save
  onSave: async ({ content, filename, outputPath, formData }) => {
    console.log(`Saved to ${outputPath}`);
  }
}

scenario.filename - Custom document filename

// Static filename
filename: 'design-doc.md'

// Or dynamic filename
filename: ({ formData, timestamp }) =>
  `${formData.project_name}-${timestamp}.md`

Prompt Function

Prompts are defined as functions that receive formData and aiContext parameters.

const prompt = ({ formData, aiContext }) => `Generate a design document.

${JSON.stringify({ formData, aiContext }, null, 2)}`;

formData

Raw form data from UI, keyed by step name. Contains actual values entered by user.

// Example formData structure
{
  overview: {
    title: "My Project",
    description: "Project description",
    priority: "high"
  },
  modules: {
    items: [
      {
        name: "Auth Module",
        features: [
          { feature_name: "Login", feature_description: "User login" }
        ]
      }
    ]
  }
}

aiContext

Field metadata (labels, descriptions) organized by step. Helps AI understand the structure without duplicating values.

// Example aiContext structure
{
  overview: {
    _step: { title: "Overview", description: "Project overview" },
    title: { label: "Title", description: "Project title" },
    description: { label: "Description", description: "Project description" },
    priority: { label: "Priority", description: "Priority level" }
  },
  modules: {
    _step: { title: "Modules", description: "Module structure" },
    items: {
      name: { label: "Module Name", description: "Name of the module" },
      features: {
        feature_name: { label: "Feature Name", description: "Name of feature" },
        feature_description: { label: "Feature Description", description: "Feature details" }
      }
    }
  }
}

Usage example:

const prompt = ({ formData, aiContext }) => `Generate a design document based on:

${JSON.stringify({ formData, aiContext }, null, 2)}`;

License

MIT