AI-powered design document generator
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
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
``bashnpm
npm install spec-snake@beta
Requirements
- Node.js: 18 or higher
- Claude Code: Claude Code must be installed
CLI Commands
$3
Creates a new config file.
`bash
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:
`bash
Create with default filename
npx spec-snake-beta initCreate with custom filename
npx spec-snake-beta init -o my-config.tsOverwrite existing file
npx spec-snake-beta init -f
`$3
Loads the config file and starts the Web UI server.
`bash
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:
`bash
Start with default settings
npx spec-snake-beta startStart with custom config and port
npx spec-snake-beta start -c my-config.ts -p 8080Listen on all interfaces
npx spec-snake-beta start --host 0.0.0.0
`Config File
$3
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.
$3
`typescript
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
});
`$3
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 |$3
#### InputField - Text input
`typescript
{
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
`typescript
{
type: 'textarea',
id: 'description',
label: 'Description',
description: 'Field description',
rows: 4
}
`#### SelectField - Dropdown select
`typescript
{
type: 'select',
id: 'priority',
label: 'Priority',
description: 'Field description',
options: [
{ value: 'high', label: 'High' },
{ value: 'medium', label: 'Medium' },
{ value: 'low', label: 'Low' }
]
}
`#### CheckboxField - Checkbox
`typescript
{
type: 'checkbox',
id: 'agree',
label: 'I agree',
description: 'Field description'
}
`#### GridField - Layout for arranging fields in columns
`typescript
{
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.
`typescript
// 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.
`typescript
{
type: 'group',
fields: [
{ type: 'input', id: 'firstName', label: 'First Name', description: '' },
{ type: 'input', id: 'lastName', label: 'Last Name', description: '' }
]
}
// formData: { firstName: '...', lastName: '...' }
`$3
Fields can be conditionally shown/hidden based on other field values using the
when property.#### Single field conditions
`typescript
// 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
`typescript
// 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)
`typescript
// 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).
$3
| 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 | 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__ format$3
`typescript
{
// 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});
}
}
`$3
`typescript
// Static filename
filename: 'design-doc.md'// Or dynamic filename
filename: ({ formData, timestamp }) =>
${formData.project_name}-${timestamp}.md
`$3
Prompts are defined as functions that receive
formData and aiContext parameters.`typescript
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.
`typescript`
// 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.
`typescript`
// 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:
`typescriptGenerate a design document based on:
const prompt = ({ formData, aiContext }) =>
${JSON.stringify({ formData, aiContext }, null, 2)};``
MIT