spec-snake 0.0.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +333 -0
- package/README.md +335 -0
- package/dist/cli.js +783 -0
- package/dist/cli.js.map +7 -0
- package/dist/client/assets/ChevronRightIcon-BcBiiChF.js +1 -0
- package/dist/client/assets/DocumentIcon-jB6zASay.js +1 -0
- package/dist/client/assets/Header-DN39nNWE.js +1 -0
- package/dist/client/assets/PlusIcon-DqYOBije.js +1 -0
- package/dist/client/assets/index-CSjjObwb.css +1 -0
- package/dist/client/assets/index-CT_VQ59w.js +1 -0
- package/dist/client/assets/index-D6usLrOW.js +26 -0
- package/dist/client/assets/index-DQxQHK-0.js +1 -0
- package/dist/client/assets/index-DYnzYTb1.js +1 -0
- package/dist/client/assets/index-Q_AobJB0.js +1 -0
- package/dist/client/assets/index-pyqED_5G.js +1 -0
- package/dist/client/assets/useStepFormStore-CBcs2c-9.js +41 -0
- package/dist/client/index.html +13 -0
- package/dist/types.d.ts +848 -0
- package/dist/types.js +99 -0
- package/package.json +85 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,848 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core type definitions for spec-snake
|
|
3
|
+
*
|
|
4
|
+
* This module defines all TypeScript types used throughout the application,
|
|
5
|
+
* including form fields, sections, steps, scenarios, and configuration.
|
|
6
|
+
*
|
|
7
|
+
* @module types
|
|
8
|
+
*/
|
|
9
|
+
import type { McpHttpServerConfig, McpSSEServerConfig, McpStdioServerConfig, Options, PermissionMode } from '@anthropic-ai/claude-agent-sdk';
|
|
10
|
+
/**
|
|
11
|
+
* Option for select/dropdown fields
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const option: SelectOption = {
|
|
16
|
+
* value: 'high',
|
|
17
|
+
* label: 'High Priority'
|
|
18
|
+
* };
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export type SelectOption = {
|
|
22
|
+
/** Internal value used in form data */
|
|
23
|
+
value: string;
|
|
24
|
+
/** Display label shown to users */
|
|
25
|
+
label: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Text input field configuration
|
|
29
|
+
*
|
|
30
|
+
* Supports various input types including text, date, and URL.
|
|
31
|
+
* Can include autocomplete suggestions.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* const field: InputField = {
|
|
36
|
+
* id: 'project_name',
|
|
37
|
+
* type: 'input',
|
|
38
|
+
* label: 'Project Name',
|
|
39
|
+
* description: 'Enter the name of your project',
|
|
40
|
+
* placeholder: 'My Awesome Project',
|
|
41
|
+
* required: true,
|
|
42
|
+
* inputType: 'text',
|
|
43
|
+
* suggestions: ['Project A', 'Project B']
|
|
44
|
+
* };
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export type InputField = {
|
|
48
|
+
/** Unique identifier for the field (used as form data key) */
|
|
49
|
+
id: string;
|
|
50
|
+
/** Display label shown above the input */
|
|
51
|
+
label: string;
|
|
52
|
+
/** Help text describing the field's purpose */
|
|
53
|
+
description: string;
|
|
54
|
+
/** Placeholder text shown when input is empty */
|
|
55
|
+
placeholder?: string;
|
|
56
|
+
/** Whether the field must be filled before submission */
|
|
57
|
+
required?: boolean;
|
|
58
|
+
/** Field type discriminator */
|
|
59
|
+
type: 'input';
|
|
60
|
+
/** HTML input type - affects keyboard and validation */
|
|
61
|
+
inputType?: 'text' | 'date' | 'url';
|
|
62
|
+
/** Autocomplete suggestions shown as datalist options */
|
|
63
|
+
suggestions?: string[];
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Multi-line text area field configuration
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* const field: TextareaField = {
|
|
71
|
+
* id: 'description',
|
|
72
|
+
* type: 'textarea',
|
|
73
|
+
* label: 'Description',
|
|
74
|
+
* description: 'Provide a detailed description',
|
|
75
|
+
* rows: 5
|
|
76
|
+
* };
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export type TextareaField = {
|
|
80
|
+
/** Unique identifier for the field */
|
|
81
|
+
id: string;
|
|
82
|
+
/** Display label shown above the textarea */
|
|
83
|
+
label: string;
|
|
84
|
+
/** Help text describing the field's purpose */
|
|
85
|
+
description: string;
|
|
86
|
+
/** Placeholder text shown when textarea is empty */
|
|
87
|
+
placeholder?: string;
|
|
88
|
+
/** Whether the field must be filled before submission */
|
|
89
|
+
required?: boolean;
|
|
90
|
+
/** Field type discriminator */
|
|
91
|
+
type: 'textarea';
|
|
92
|
+
/** Number of visible text rows (affects initial height) */
|
|
93
|
+
rows?: number;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Dropdown select field configuration
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* const field: SelectField = {
|
|
101
|
+
* id: 'priority',
|
|
102
|
+
* type: 'select',
|
|
103
|
+
* label: 'Priority',
|
|
104
|
+
* description: 'Select the priority level',
|
|
105
|
+
* options: [
|
|
106
|
+
* { value: 'low', label: 'Low' },
|
|
107
|
+
* { value: 'medium', label: 'Medium' },
|
|
108
|
+
* { value: 'high', label: 'High' }
|
|
109
|
+
* ]
|
|
110
|
+
* };
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export type SelectField = {
|
|
114
|
+
/** Unique identifier for the field */
|
|
115
|
+
id: string;
|
|
116
|
+
/** Display label shown above the select */
|
|
117
|
+
label: string;
|
|
118
|
+
/** Help text describing the field's purpose */
|
|
119
|
+
description: string;
|
|
120
|
+
/** Placeholder text (shown as first disabled option) */
|
|
121
|
+
placeholder?: string;
|
|
122
|
+
/** Whether a selection must be made before submission */
|
|
123
|
+
required?: boolean;
|
|
124
|
+
/** Field type discriminator */
|
|
125
|
+
type: 'select';
|
|
126
|
+
/** Available options for selection */
|
|
127
|
+
options: SelectOption[];
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Boolean checkbox field configuration
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```ts
|
|
134
|
+
* const field: CheckboxField = {
|
|
135
|
+
* id: 'agree_terms',
|
|
136
|
+
* type: 'checkbox',
|
|
137
|
+
* label: 'I agree to the terms',
|
|
138
|
+
* description: 'You must agree to continue',
|
|
139
|
+
* required: true
|
|
140
|
+
* };
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export type CheckboxField = {
|
|
144
|
+
/** Unique identifier for the field */
|
|
145
|
+
id: string;
|
|
146
|
+
/** Display label shown next to the checkbox */
|
|
147
|
+
label: string;
|
|
148
|
+
/** Help text describing the field's purpose */
|
|
149
|
+
description: string;
|
|
150
|
+
/** Placeholder (not typically used for checkboxes) */
|
|
151
|
+
placeholder?: string;
|
|
152
|
+
/** Whether the checkbox must be checked before submission */
|
|
153
|
+
required?: boolean;
|
|
154
|
+
/** Field type discriminator */
|
|
155
|
+
type: 'checkbox';
|
|
156
|
+
};
|
|
157
|
+
export type FormField = InputField | TextareaField | SelectField | CheckboxField;
|
|
158
|
+
/**
|
|
159
|
+
* Grid layout for arranging multiple fields in columns
|
|
160
|
+
*
|
|
161
|
+
* Allows organizing form fields horizontally within a section.
|
|
162
|
+
* Supports nested fields including other grids (recursive).
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```ts
|
|
166
|
+
* const layout: GridLayout = {
|
|
167
|
+
* type: 'grid',
|
|
168
|
+
* columns: 2,
|
|
169
|
+
* fields: [
|
|
170
|
+
* { id: 'first_name', type: 'input', ... },
|
|
171
|
+
* { id: 'last_name', type: 'input', ... }
|
|
172
|
+
* ]
|
|
173
|
+
* };
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
export type GridLayout = {
|
|
177
|
+
/** Layout type discriminator */
|
|
178
|
+
type: 'grid';
|
|
179
|
+
/** Number of columns (fields per row) */
|
|
180
|
+
columns: number;
|
|
181
|
+
/** Fields to arrange in the grid (can include nested layouts) */
|
|
182
|
+
fields: Field[];
|
|
183
|
+
};
|
|
184
|
+
export type LayoutField = GridLayout;
|
|
185
|
+
export type Field = FormField | LayoutField;
|
|
186
|
+
/**
|
|
187
|
+
* Single-instance section containing one set of fields
|
|
188
|
+
*
|
|
189
|
+
* Used when the user fills out the fields exactly once.
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```ts
|
|
193
|
+
* const section: SingleSection = {
|
|
194
|
+
* type: 'single',
|
|
195
|
+
* name: 'project_info',
|
|
196
|
+
* fields: [...]
|
|
197
|
+
* };
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
export type SingleSection = {
|
|
201
|
+
/** Section type discriminator */
|
|
202
|
+
type: 'single';
|
|
203
|
+
/** Section name (used as key in form data output) */
|
|
204
|
+
name: string;
|
|
205
|
+
/** Fields contained in this section */
|
|
206
|
+
fields: Field[];
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Array section allowing multiple instances of the same fields
|
|
210
|
+
*
|
|
211
|
+
* Used when the user can add multiple entries (e.g., team members, features).
|
|
212
|
+
* Each entry contains the same field structure.
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```ts
|
|
216
|
+
* const section: ArraySection = {
|
|
217
|
+
* type: 'array',
|
|
218
|
+
* name: 'team_members',
|
|
219
|
+
* fields: [
|
|
220
|
+
* { id: 'name', type: 'input', ... },
|
|
221
|
+
* { id: 'role', type: 'select', ... }
|
|
222
|
+
* ],
|
|
223
|
+
* minFieldCount: 1
|
|
224
|
+
* };
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
export type ArraySection = {
|
|
228
|
+
/** Section type discriminator */
|
|
229
|
+
type: 'array';
|
|
230
|
+
/** Section name (used as key in form data output) */
|
|
231
|
+
name: string;
|
|
232
|
+
/** Fields template for each array entry */
|
|
233
|
+
fields: Field[];
|
|
234
|
+
/** Minimum number of entries required (default: 0) */
|
|
235
|
+
minFieldCount?: number;
|
|
236
|
+
};
|
|
237
|
+
export type Section = SingleSection | ArraySection;
|
|
238
|
+
/**
|
|
239
|
+
* Step definition for multi-step form wizard
|
|
240
|
+
*
|
|
241
|
+
* Each step represents one page in the form wizard, containing
|
|
242
|
+
* a section with fields for the user to fill out.
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* ```ts
|
|
246
|
+
* const step: Step = {
|
|
247
|
+
* slug: 'basic-info',
|
|
248
|
+
* title: 'Basic Information',
|
|
249
|
+
* description: 'Enter the basic details of your project',
|
|
250
|
+
* section: {
|
|
251
|
+
* type: 'single',
|
|
252
|
+
* name: 'basic',
|
|
253
|
+
* fields: [...]
|
|
254
|
+
* }
|
|
255
|
+
* };
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
export type Step = {
|
|
259
|
+
/** URL-friendly identifier (used in routing) */
|
|
260
|
+
slug: string;
|
|
261
|
+
/** Display title shown in step header */
|
|
262
|
+
title: string;
|
|
263
|
+
/** Description text shown below the title */
|
|
264
|
+
description: string;
|
|
265
|
+
/** Section containing the step's fields */
|
|
266
|
+
section: Section;
|
|
267
|
+
};
|
|
268
|
+
/**
|
|
269
|
+
* MCP (Model Context Protocol) Server Configuration
|
|
270
|
+
*
|
|
271
|
+
* Defines how to connect to an MCP server. Supports three transport types:
|
|
272
|
+
* - stdio: Local process started with a command
|
|
273
|
+
* - sse: Remote server via Server-Sent Events
|
|
274
|
+
* - http: Remote server via HTTP
|
|
275
|
+
*
|
|
276
|
+
* Re-exported from `@anthropic-ai/claude-agent-sdk` for convenience.
|
|
277
|
+
* Note: The SDK also supports an 'sdk' type for in-memory servers,
|
|
278
|
+
* but that requires runtime instances and is not serializable to config files.
|
|
279
|
+
*
|
|
280
|
+
* @see https://docs.anthropic.com/en/docs/claude-code/mcp
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* ```ts
|
|
284
|
+
* // stdio server (local process)
|
|
285
|
+
* const stdioConfig: McpServerConfig = {
|
|
286
|
+
* command: 'npx',
|
|
287
|
+
* args: ['@modelcontextprotocol/server-filesystem'],
|
|
288
|
+
* env: { ALLOWED_PATHS: '/home/user/projects' }
|
|
289
|
+
* };
|
|
290
|
+
*
|
|
291
|
+
* // SSE server (remote)
|
|
292
|
+
* const sseConfig: McpServerConfig = {
|
|
293
|
+
* type: 'sse',
|
|
294
|
+
* url: 'https://api.example.com/mcp/sse',
|
|
295
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
296
|
+
* };
|
|
297
|
+
*
|
|
298
|
+
* // HTTP server (remote)
|
|
299
|
+
* const httpConfig: McpServerConfig = {
|
|
300
|
+
* type: 'http',
|
|
301
|
+
* url: 'https://api.example.com/mcp',
|
|
302
|
+
* headers: { 'Authorization': 'Bearer token' }
|
|
303
|
+
* };
|
|
304
|
+
* ```
|
|
305
|
+
*/
|
|
306
|
+
export type McpServerConfig = McpStdioServerConfig | McpSSEServerConfig | McpHttpServerConfig;
|
|
307
|
+
export type { PermissionMode, McpStdioServerConfig, McpSSEServerConfig, McpHttpServerConfig, };
|
|
308
|
+
/**
|
|
309
|
+
* AI Settings for Claude Agent SDK query options
|
|
310
|
+
*
|
|
311
|
+
* Derived from the SDK's `Options` type using `Pick` to select only the
|
|
312
|
+
* fields relevant for config file serialization. This ensures type safety
|
|
313
|
+
* and automatic updates when the SDK changes.
|
|
314
|
+
*
|
|
315
|
+
* This is a simplified subset focused on model selection, tools, MCP, and permissions.
|
|
316
|
+
* Other SDK options (session, environment, output format, etc.) are managed by the server.
|
|
317
|
+
*
|
|
318
|
+
* @see https://docs.anthropic.com/en/docs/claude-code/sdk
|
|
319
|
+
* @see https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* ```ts
|
|
323
|
+
* const aiSettings: AiSettings = {
|
|
324
|
+
* model: 'claude-sonnet-4-5-20250929',
|
|
325
|
+
* maxTurns: 10,
|
|
326
|
+
* maxBudgetUsd: 1.0,
|
|
327
|
+
* permissionMode: 'bypassPermissions',
|
|
328
|
+
* allowDangerouslySkipPermissions: true,
|
|
329
|
+
* mcpServers: {
|
|
330
|
+
* filesystem: {
|
|
331
|
+
* command: 'npx',
|
|
332
|
+
* args: ['@modelcontextprotocol/server-filesystem']
|
|
333
|
+
* }
|
|
334
|
+
* }
|
|
335
|
+
* };
|
|
336
|
+
* ```
|
|
337
|
+
*
|
|
338
|
+
* **Available Tools:**
|
|
339
|
+
*
|
|
340
|
+
* File Operations:
|
|
341
|
+
* - `Read` - Read file contents
|
|
342
|
+
* - `Write` - Write/create files
|
|
343
|
+
* - `Edit` - Edit existing files
|
|
344
|
+
* - `Glob` - Find files by pattern
|
|
345
|
+
* - `Grep` - Search file contents
|
|
346
|
+
* - `NotebookEdit` - Edit Jupyter notebooks
|
|
347
|
+
*
|
|
348
|
+
* Command Execution:
|
|
349
|
+
* - `Bash` - Execute shell commands
|
|
350
|
+
*
|
|
351
|
+
* Web:
|
|
352
|
+
* - `WebSearch` - Search the web
|
|
353
|
+
* - `WebFetch` - Fetch content from URLs
|
|
354
|
+
*
|
|
355
|
+
* Agent & Task:
|
|
356
|
+
* - `Task` - Launch sub-agents
|
|
357
|
+
* - `TodoWrite` - Manage task lists
|
|
358
|
+
*
|
|
359
|
+
* Code Intelligence:
|
|
360
|
+
* - `LSP` - Language Server Protocol operations
|
|
361
|
+
*
|
|
362
|
+
* MCP Tools:
|
|
363
|
+
* - Format: `mcp__<server>__<tool>` (e.g., `mcp__filesystem__read_file`)
|
|
364
|
+
*/
|
|
365
|
+
export type AiSettings = Omit<Pick<Options, 'model' | 'fallbackModel' | 'maxThinkingTokens' | 'maxTurns' | 'maxBudgetUsd' | 'allowedTools' | 'disallowedTools' | 'tools' | 'permissionMode' | 'allowDangerouslySkipPermissions' | 'mcpServers' | 'strictMcpConfig'>, 'mcpServers'> & {
|
|
366
|
+
/**
|
|
367
|
+
* MCP server configurations keyed by server name
|
|
368
|
+
*
|
|
369
|
+
* Each server provides additional tools and capabilities to the model.
|
|
370
|
+
* Only serializable transport types (stdio, sse, http) are supported in config files.
|
|
371
|
+
*
|
|
372
|
+
* @see https://docs.anthropic.com/en/docs/claude-code/mcp
|
|
373
|
+
*/
|
|
374
|
+
mcpServers?: Record<string, McpServerConfig>;
|
|
375
|
+
};
|
|
376
|
+
/**
|
|
377
|
+
* Base scenario definition (serializable to JSON)
|
|
378
|
+
*
|
|
379
|
+
* Contains the core fields that can be validated by Valibot schema.
|
|
380
|
+
* Extended by `Scenario` with function-based fields.
|
|
381
|
+
*/
|
|
382
|
+
export type ScenarioBase = {
|
|
383
|
+
/** Unique identifier for the scenario (used in URLs) */
|
|
384
|
+
id: string;
|
|
385
|
+
/** Display name shown in the scenario list */
|
|
386
|
+
name: string;
|
|
387
|
+
/** Steps that make up the scenario's form wizard */
|
|
388
|
+
steps: Step[];
|
|
389
|
+
/** Prompt template sent to Claude (use {{INPUT_JSON}} placeholder) */
|
|
390
|
+
prompt: string;
|
|
391
|
+
/**
|
|
392
|
+
* AI settings for Claude Agent SDK
|
|
393
|
+
* @see AiSettings
|
|
394
|
+
*/
|
|
395
|
+
aiSettings?: AiSettings;
|
|
396
|
+
};
|
|
397
|
+
/**
|
|
398
|
+
* InputData type (transformed form data sent to Claude)
|
|
399
|
+
*
|
|
400
|
+
* This is the structure of `inputData` passed to hooks, prompts, and overrides.
|
|
401
|
+
* It's the same as `{{INPUT_JSON}}` in prompts.
|
|
402
|
+
*/
|
|
403
|
+
export type InputData = {
|
|
404
|
+
items: Array<{
|
|
405
|
+
title: string;
|
|
406
|
+
description: string;
|
|
407
|
+
values: Array<{
|
|
408
|
+
label: string;
|
|
409
|
+
description: string;
|
|
410
|
+
value: unknown;
|
|
411
|
+
}> | Array<Array<{
|
|
412
|
+
label: string;
|
|
413
|
+
description: string;
|
|
414
|
+
value: unknown;
|
|
415
|
+
}>>;
|
|
416
|
+
}>;
|
|
417
|
+
};
|
|
418
|
+
/**
|
|
419
|
+
* Lifecycle hooks for scenario events
|
|
420
|
+
*
|
|
421
|
+
* Allows custom behavior during preview and save operations.
|
|
422
|
+
*
|
|
423
|
+
* @typeParam TFormData - Type of the raw form data from UI
|
|
424
|
+
*/
|
|
425
|
+
export type ScenarioHooks<TFormData extends Record<string, unknown> = Record<string, unknown>> = {
|
|
426
|
+
/**
|
|
427
|
+
* Called after preview is generated but before displaying to user
|
|
428
|
+
*
|
|
429
|
+
* Use for logging, analytics, or transforming the preview content.
|
|
430
|
+
*
|
|
431
|
+
* @param params.formData - The raw form data from the UI
|
|
432
|
+
* @param params.inputData - The transformed data sent to Claude (same as {{INPUT_JSON}})
|
|
433
|
+
* @param params.content - The generated markdown content
|
|
434
|
+
*/
|
|
435
|
+
onPreview?: (params: {
|
|
436
|
+
formData: TFormData;
|
|
437
|
+
inputData: InputData;
|
|
438
|
+
content: string;
|
|
439
|
+
}) => Promise<void>;
|
|
440
|
+
/**
|
|
441
|
+
* Called after document is saved to disk
|
|
442
|
+
*
|
|
443
|
+
* Use for post-processing, notifications, or integrations.
|
|
444
|
+
*
|
|
445
|
+
* @param params.content - The saved markdown content
|
|
446
|
+
* @param params.filename - The filename that was used
|
|
447
|
+
* @param params.outputPath - Full path to the saved file
|
|
448
|
+
* @param params.formData - The raw form data from the UI
|
|
449
|
+
* @param params.inputData - The transformed data sent to Claude (same as {{INPUT_JSON}})
|
|
450
|
+
*/
|
|
451
|
+
onSave?: (params: {
|
|
452
|
+
content: string;
|
|
453
|
+
filename: string;
|
|
454
|
+
outputPath: string;
|
|
455
|
+
formData: TFormData;
|
|
456
|
+
inputData: InputData;
|
|
457
|
+
}) => Promise<void>;
|
|
458
|
+
};
|
|
459
|
+
/**
|
|
460
|
+
* Override options for scenario behavior
|
|
461
|
+
*
|
|
462
|
+
* @typeParam TFormData - Type of the raw form data from UI
|
|
463
|
+
*/
|
|
464
|
+
export type ScenarioOverrides<TFormData extends Record<string, unknown> = Record<string, unknown>> = {
|
|
465
|
+
/**
|
|
466
|
+
* Custom filename for saved documents
|
|
467
|
+
*
|
|
468
|
+
* Can be a static string or a function for dynamic naming.
|
|
469
|
+
* Default format: `{scenarioId}-{timestamp}.md`
|
|
470
|
+
*
|
|
471
|
+
* @example
|
|
472
|
+
* ```ts
|
|
473
|
+
* // Static filename
|
|
474
|
+
* filename: 'design-doc.md'
|
|
475
|
+
*
|
|
476
|
+
* // Dynamic filename based on form data
|
|
477
|
+
* filename: ({ formData, timestamp }) =>
|
|
478
|
+
* `${formData.project_name}-${timestamp}.md`
|
|
479
|
+
* ```
|
|
480
|
+
*/
|
|
481
|
+
filename?: string | ((params: {
|
|
482
|
+
scenarioId: string;
|
|
483
|
+
timestamp: string;
|
|
484
|
+
content: string;
|
|
485
|
+
formData: TFormData;
|
|
486
|
+
inputData: InputData;
|
|
487
|
+
}) => string);
|
|
488
|
+
};
|
|
489
|
+
/**
|
|
490
|
+
* Prompt template type
|
|
491
|
+
*
|
|
492
|
+
* Can be a static string or a function for dynamic prompt generation.
|
|
493
|
+
* Use `{{INPUT_JSON}}` in the template to inject form data.
|
|
494
|
+
*
|
|
495
|
+
* @typeParam TFormData - Type of the raw form data from UI
|
|
496
|
+
*
|
|
497
|
+
* @example
|
|
498
|
+
* ```ts
|
|
499
|
+
* // Static prompt
|
|
500
|
+
* prompt: `Generate a design doc based on: {{INPUT_JSON}}`
|
|
501
|
+
*
|
|
502
|
+
* // Dynamic prompt
|
|
503
|
+
* prompt: ({ formData, inputData }) =>
|
|
504
|
+
* `Create a ${formData.doc_type} document for: {{INPUT_JSON}}`
|
|
505
|
+
* ```
|
|
506
|
+
*/
|
|
507
|
+
export type ScenarioPrompt<TFormData extends Record<string, unknown> = Record<string, unknown>> = string | ((params: {
|
|
508
|
+
formData: TFormData;
|
|
509
|
+
inputData: InputData;
|
|
510
|
+
}) => string);
|
|
511
|
+
/**
|
|
512
|
+
* Complete scenario definition
|
|
513
|
+
*
|
|
514
|
+
* Extends ScenarioBase with function-based fields that cannot be
|
|
515
|
+
* serialized to JSON (prompt can be a function, hooks, overrides).
|
|
516
|
+
*
|
|
517
|
+
* @typeParam TFormData - Type of the raw form data from UI (inferred from steps)
|
|
518
|
+
*
|
|
519
|
+
* @example
|
|
520
|
+
* ```ts
|
|
521
|
+
* const scenario: Scenario = {
|
|
522
|
+
* id: 'design-doc',
|
|
523
|
+
* name: 'Design Document',
|
|
524
|
+
* steps: [...],
|
|
525
|
+
* prompt: 'Generate a design doc based on: {{INPUT_JSON}}',
|
|
526
|
+
* outputDir: './docs/designs',
|
|
527
|
+
* aiSettings: {
|
|
528
|
+
* model: 'claude-sonnet-4-5'
|
|
529
|
+
* },
|
|
530
|
+
* hooks: {
|
|
531
|
+
* onSave: async ({ filename }) => {
|
|
532
|
+
* console.log(`Saved: ${filename}`);
|
|
533
|
+
* }
|
|
534
|
+
* }
|
|
535
|
+
* };
|
|
536
|
+
* ```
|
|
537
|
+
*/
|
|
538
|
+
export type Scenario<TFormData extends Record<string, unknown> = Record<string, unknown>> = Omit<ScenarioBase, 'prompt'> & {
|
|
539
|
+
/** Prompt template (static string or dynamic function) */
|
|
540
|
+
prompt: ScenarioPrompt<TFormData>;
|
|
541
|
+
/** Directory where generated documents are saved */
|
|
542
|
+
outputDir?: string;
|
|
543
|
+
/** Lifecycle hooks for custom behavior */
|
|
544
|
+
hooks?: ScenarioHooks<TFormData>;
|
|
545
|
+
/** Override default behaviors */
|
|
546
|
+
overrides?: ScenarioOverrides<TFormData>;
|
|
547
|
+
};
|
|
548
|
+
/**
|
|
549
|
+
* Helper type to get the value type for a form field
|
|
550
|
+
*/
|
|
551
|
+
type FieldValueType<F> = F extends {
|
|
552
|
+
type: 'checkbox';
|
|
553
|
+
} ? boolean : F extends {
|
|
554
|
+
type: 'select';
|
|
555
|
+
options: readonly {
|
|
556
|
+
value: infer V;
|
|
557
|
+
}[];
|
|
558
|
+
} ? V : string;
|
|
559
|
+
/**
|
|
560
|
+
* Helper type to create an object type from FormField array (not supporting nested GridLayout)
|
|
561
|
+
*/
|
|
562
|
+
type FormFieldsToObject<Fields extends readonly FormField[]> = {
|
|
563
|
+
[F in Fields[number] as F['id']]: FieldValueType<F>;
|
|
564
|
+
};
|
|
565
|
+
/**
|
|
566
|
+
* Helper type to merge union to intersection
|
|
567
|
+
*/
|
|
568
|
+
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
569
|
+
/**
|
|
570
|
+
* Infer the formData type from a Scenario's steps
|
|
571
|
+
*
|
|
572
|
+
* Use this utility type to get type-safe access to raw form data
|
|
573
|
+
* in hooks, prompts, and overrides.
|
|
574
|
+
*
|
|
575
|
+
* **Note**: This utility works with FormField arrays only (not GridLayout).
|
|
576
|
+
* Define fields with `as const` for literal type inference.
|
|
577
|
+
*
|
|
578
|
+
* @example
|
|
579
|
+
* ```ts
|
|
580
|
+
* const scenario = {
|
|
581
|
+
* id: 'design-doc',
|
|
582
|
+
* name: 'Design Document',
|
|
583
|
+
* steps: [
|
|
584
|
+
* {
|
|
585
|
+
* slug: 'overview',
|
|
586
|
+
* title: 'Overview',
|
|
587
|
+
* description: 'Project overview',
|
|
588
|
+
* section: {
|
|
589
|
+
* type: 'single',
|
|
590
|
+
* name: 'overview',
|
|
591
|
+
* fields: [
|
|
592
|
+
* { id: 'title', type: 'input', label: 'Title', description: '' },
|
|
593
|
+
* { id: 'description', type: 'textarea', label: 'Description', description: '' },
|
|
594
|
+
* ] as const,
|
|
595
|
+
* },
|
|
596
|
+
* },
|
|
597
|
+
* {
|
|
598
|
+
* slug: 'requirements',
|
|
599
|
+
* title: 'Requirements',
|
|
600
|
+
* description: 'List requirements',
|
|
601
|
+
* section: {
|
|
602
|
+
* type: 'array',
|
|
603
|
+
* name: 'requirements',
|
|
604
|
+
* fields: [
|
|
605
|
+
* { id: 'name', type: 'input', label: 'Name', description: '' },
|
|
606
|
+
* ] as const,
|
|
607
|
+
* },
|
|
608
|
+
* },
|
|
609
|
+
* ],
|
|
610
|
+
* prompt: '...',
|
|
611
|
+
* } as const satisfies Scenario;
|
|
612
|
+
*
|
|
613
|
+
* type MyFormData = InferFormData<typeof scenario>;
|
|
614
|
+
* // Result:
|
|
615
|
+
* // {
|
|
616
|
+
* // overview: { title: string; description: string };
|
|
617
|
+
* // requirements: Array<{ name: string }>;
|
|
618
|
+
* // }
|
|
619
|
+
* ```
|
|
620
|
+
*/
|
|
621
|
+
export type InferFormData<T extends Scenario> = T['steps'] extends readonly (infer S)[] ? S extends {
|
|
622
|
+
section: infer Sec;
|
|
623
|
+
} ? Sec extends {
|
|
624
|
+
type: 'single';
|
|
625
|
+
name: infer N;
|
|
626
|
+
fields: readonly FormField[];
|
|
627
|
+
} ? N extends string ? {
|
|
628
|
+
[K in N]: FormFieldsToObject<Sec['fields']>;
|
|
629
|
+
} : never : Sec extends {
|
|
630
|
+
type: 'array';
|
|
631
|
+
name: infer N;
|
|
632
|
+
fields: readonly FormField[];
|
|
633
|
+
} ? N extends string ? {
|
|
634
|
+
[K in N]: Array<FormFieldsToObject<Sec['fields']>>;
|
|
635
|
+
} : never : never : never : never;
|
|
636
|
+
/**
|
|
637
|
+
* Infer the merged formData type from a Scenario
|
|
638
|
+
*
|
|
639
|
+
* This flattens all steps' sections into a single object type.
|
|
640
|
+
*
|
|
641
|
+
* @example
|
|
642
|
+
* ```ts
|
|
643
|
+
* type MyFormData = InferFormDataMerged<typeof scenario>;
|
|
644
|
+
* // {
|
|
645
|
+
* // overview: { title: string; description: string };
|
|
646
|
+
* // requirements: Array<{ name: string }>;
|
|
647
|
+
* // }
|
|
648
|
+
* ```
|
|
649
|
+
*/
|
|
650
|
+
export type InferFormDataMerged<T extends Scenario> = UnionToIntersection<InferFormData<T>>;
|
|
651
|
+
/**
|
|
652
|
+
* Helper type to extract FormField from Field (handling one level of GridLayout)
|
|
653
|
+
*/
|
|
654
|
+
type ExtractFormFields<F> = F extends FormField ? F : F extends {
|
|
655
|
+
type: 'grid';
|
|
656
|
+
fields: readonly (infer GF)[];
|
|
657
|
+
} ? GF extends FormField ? GF : never : never;
|
|
658
|
+
/**
|
|
659
|
+
* Helper type to create an object type from Field array (supporting GridLayout)
|
|
660
|
+
*/
|
|
661
|
+
type FieldsToObject<Fields extends readonly Field[]> = {
|
|
662
|
+
[F in ExtractFormFields<Fields[number]> as F extends {
|
|
663
|
+
id: infer ID extends string;
|
|
664
|
+
} ? ID : never]?: F extends FormField ? FieldValueType<F> : unknown;
|
|
665
|
+
};
|
|
666
|
+
/**
|
|
667
|
+
* Infer the formData type directly from a steps array
|
|
668
|
+
*
|
|
669
|
+
* Use this utility type when defining scenarios with `defineScenario`
|
|
670
|
+
* for type-safe access to formData in hooks, prompts, and overrides.
|
|
671
|
+
*
|
|
672
|
+
* **Note**: For best results, define steps with `as const satisfies Step[]`
|
|
673
|
+
* to preserve literal types for section names and field IDs.
|
|
674
|
+
*
|
|
675
|
+
* @example
|
|
676
|
+
* ```ts
|
|
677
|
+
* const steps = [
|
|
678
|
+
* {
|
|
679
|
+
* slug: 'basic-info',
|
|
680
|
+
* title: 'Basic Info',
|
|
681
|
+
* description: 'Enter basic information',
|
|
682
|
+
* section: {
|
|
683
|
+
* type: 'single',
|
|
684
|
+
* name: 'basicInfo',
|
|
685
|
+
* fields: [
|
|
686
|
+
* { id: 'title', type: 'input', label: 'Title', description: '' },
|
|
687
|
+
* ],
|
|
688
|
+
* },
|
|
689
|
+
* },
|
|
690
|
+
* {
|
|
691
|
+
* slug: 'items',
|
|
692
|
+
* title: 'Items',
|
|
693
|
+
* description: 'Add items',
|
|
694
|
+
* section: {
|
|
695
|
+
* type: 'array',
|
|
696
|
+
* name: 'items',
|
|
697
|
+
* fields: [
|
|
698
|
+
* { id: 'name', type: 'input', label: 'Name', description: '' },
|
|
699
|
+
* ],
|
|
700
|
+
* },
|
|
701
|
+
* },
|
|
702
|
+
* ] as const satisfies Step[];
|
|
703
|
+
*
|
|
704
|
+
* type FormData = InferFormDataFromSteps<typeof steps>;
|
|
705
|
+
* // {
|
|
706
|
+
* // basicInfo?: { title?: string };
|
|
707
|
+
* // items?: Array<{ name?: string }>;
|
|
708
|
+
* // }
|
|
709
|
+
* ```
|
|
710
|
+
*/
|
|
711
|
+
export type InferFormDataFromSteps<TSteps extends readonly Step[]> = UnionToIntersection<TSteps[number] extends infer S ? S extends {
|
|
712
|
+
section: infer Sec;
|
|
713
|
+
} ? Sec extends {
|
|
714
|
+
type: 'single';
|
|
715
|
+
name: infer N extends string;
|
|
716
|
+
fields: readonly Field[];
|
|
717
|
+
} ? {
|
|
718
|
+
[K in N]?: FieldsToObject<Sec['fields']>;
|
|
719
|
+
} : Sec extends {
|
|
720
|
+
type: 'array';
|
|
721
|
+
name: infer N extends string;
|
|
722
|
+
fields: readonly Field[];
|
|
723
|
+
} ? {
|
|
724
|
+
[K in N]?: Array<FieldsToObject<Sec['fields']>>;
|
|
725
|
+
} : never : never : never> & Record<string, unknown>;
|
|
726
|
+
/**
|
|
727
|
+
* Permission settings for the application
|
|
728
|
+
*/
|
|
729
|
+
export type Permissions = {
|
|
730
|
+
/** Whether users can save generated documents to disk */
|
|
731
|
+
allowSave: boolean;
|
|
732
|
+
};
|
|
733
|
+
/**
|
|
734
|
+
* Root configuration object
|
|
735
|
+
*
|
|
736
|
+
* This is the type for the config file exported from `config.ts`.
|
|
737
|
+
*
|
|
738
|
+
* @example
|
|
739
|
+
* ```ts
|
|
740
|
+
* // config.ts
|
|
741
|
+
* import type { Config } from '@anthropic-ai/claude-agent-sdk';
|
|
742
|
+
*
|
|
743
|
+
* export default {
|
|
744
|
+
* scenarios: [
|
|
745
|
+
* { id: 'design-doc', name: 'Design Doc', ... }
|
|
746
|
+
* ],
|
|
747
|
+
* permissions: {
|
|
748
|
+
* allowSave: true
|
|
749
|
+
* }
|
|
750
|
+
* } satisfies Config;
|
|
751
|
+
* ```
|
|
752
|
+
*/
|
|
753
|
+
export type Config = {
|
|
754
|
+
/** Available scenarios (each represents a document type) */
|
|
755
|
+
scenarios: Scenario[];
|
|
756
|
+
/** Global permission settings */
|
|
757
|
+
permissions: Permissions;
|
|
758
|
+
};
|
|
759
|
+
/**
|
|
760
|
+
* Helper function to define a scenario with type-safe formData
|
|
761
|
+
*
|
|
762
|
+
* This function uses TypeScript's const type parameters to infer
|
|
763
|
+
* literal types from the steps array, enabling type-safe access
|
|
764
|
+
* to formData in hooks, prompts, and overrides.
|
|
765
|
+
*
|
|
766
|
+
* **Usage**: Define your steps with `as const satisfies Step[]` for best results.
|
|
767
|
+
*
|
|
768
|
+
* @example
|
|
769
|
+
* ```ts
|
|
770
|
+
* import { defineScenario, type Step, type Config } from '@cut0/spec-snake';
|
|
771
|
+
*
|
|
772
|
+
* const steps = [
|
|
773
|
+
* {
|
|
774
|
+
* slug: 'basic-info',
|
|
775
|
+
* title: 'Basic Info',
|
|
776
|
+
* description: 'Enter basic information',
|
|
777
|
+
* section: {
|
|
778
|
+
* type: 'single',
|
|
779
|
+
* name: 'basicInfo',
|
|
780
|
+
* fields: [
|
|
781
|
+
* { id: 'projectName', type: 'input', label: 'Project Name', description: '' },
|
|
782
|
+
* { id: 'overview', type: 'textarea', label: 'Overview', description: '' },
|
|
783
|
+
* ],
|
|
784
|
+
* },
|
|
785
|
+
* },
|
|
786
|
+
* {
|
|
787
|
+
* slug: 'features',
|
|
788
|
+
* title: 'Features',
|
|
789
|
+
* description: 'List features',
|
|
790
|
+
* section: {
|
|
791
|
+
* type: 'array',
|
|
792
|
+
* name: 'features',
|
|
793
|
+
* fields: [
|
|
794
|
+
* { id: 'name', type: 'input', label: 'Feature Name', description: '' },
|
|
795
|
+
* ],
|
|
796
|
+
* },
|
|
797
|
+
* },
|
|
798
|
+
* ] as const satisfies Step[];
|
|
799
|
+
*
|
|
800
|
+
* const scenario = defineScenario({
|
|
801
|
+
* id: 'my-scenario',
|
|
802
|
+
* name: 'My Scenario',
|
|
803
|
+
* steps,
|
|
804
|
+
* prompt: '...',
|
|
805
|
+
* outputDir: 'docs',
|
|
806
|
+
* overrides: {
|
|
807
|
+
* filename: ({ formData }) => {
|
|
808
|
+
* // formData is now type-safe!
|
|
809
|
+
* // formData.basicInfo?.projectName is typed as string | undefined
|
|
810
|
+
* return `${formData.basicInfo?.projectName ?? 'untitled'}.md`;
|
|
811
|
+
* },
|
|
812
|
+
* },
|
|
813
|
+
* });
|
|
814
|
+
*
|
|
815
|
+
* const config: Config = {
|
|
816
|
+
* scenarios: [scenario],
|
|
817
|
+
* permissions: { allowSave: true },
|
|
818
|
+
* };
|
|
819
|
+
* ```
|
|
820
|
+
*/
|
|
821
|
+
export declare function defineScenario<const TSteps extends readonly Step[]>(scenario: Omit<ScenarioBase, 'steps' | 'prompt'> & {
|
|
822
|
+
steps: TSteps;
|
|
823
|
+
prompt: ScenarioPrompt<InferFormDataFromSteps<TSteps>>;
|
|
824
|
+
outputDir?: string;
|
|
825
|
+
hooks?: ScenarioHooks<InferFormDataFromSteps<TSteps>>;
|
|
826
|
+
overrides?: ScenarioOverrides<InferFormDataFromSteps<TSteps>>;
|
|
827
|
+
}): Scenario;
|
|
828
|
+
/**
|
|
829
|
+
* Helper function to define a config object
|
|
830
|
+
*
|
|
831
|
+
* This is a simple identity function that provides better type inference
|
|
832
|
+
* and editor support when defining config files.
|
|
833
|
+
*
|
|
834
|
+
* @example
|
|
835
|
+
* ```ts
|
|
836
|
+
* import { defineConfig, defineScenario } from '@cut0/spec-snake';
|
|
837
|
+
*
|
|
838
|
+
* export default defineConfig({
|
|
839
|
+
* scenarios: [
|
|
840
|
+
* defineScenario({ ... }),
|
|
841
|
+
* ],
|
|
842
|
+
* permissions: {
|
|
843
|
+
* allowSave: true,
|
|
844
|
+
* },
|
|
845
|
+
* });
|
|
846
|
+
* ```
|
|
847
|
+
*/
|
|
848
|
+
export declare function defineConfig(config: Config): Config;
|