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@betaRequirements
- 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 initOptions:
| 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 -fstart - Start the server
Loads the config file and starts the Web UI server.
npx spec-snake-beta startOptions:
| 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.0Config File
Config Examples
See the examples/ directory for config file examples.
examples/local/spec-snake.config.ts- Basic config example (English)examples/local/spec-snake-ja.config.ts- Basic config example (Japanese)
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
