formhaus 0.19.1 → 0.20.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.
Files changed (2) hide show
  1. package/dist/cli.js +9 -5
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -7,13 +7,13 @@ import { randomBytes, createHash } from 'crypto';
7
7
  import { existsSync, statSync, accessSync, constants, realpathSync, readFileSync, mkdirSync, copyFileSync, renameSync, rmSync, lstatSync, writeFileSync, chmodSync } from 'fs';
8
8
 
9
9
  // package.json
10
- var version = "0.19.1";
10
+ var version = "0.20.0";
11
11
 
12
12
  // ../../.cursor/rules/formhaus.mdc
13
- var formhaus_default = '---\ndescription: Formhaus form definitions. Use when creating, editing or reviewing a Formhaus JSON form definition, or code that passes one to FormEngine or FormRenderer.\nalwaysApply: false\n---\n\n# Formhaus form definitions\n\nA Formhaus form is a JSON definition rendered by `@formhaus/core` with `@formhaus/react` or `@formhaus/vue`. One definition drives the web form and the Figma plugin.\n\n- Start each definition file with `"$schema": "https://formhaus.dev/schema/form-definition.json"`.\n- Required keys: `id` (kebab-case), `title`, `submit: { "label": "..." }`.\n- Use `fields` for a single page or `steps` for a multi-step form, never both.\n- Field `key` values are camelCase and unique across all steps.\n- `show` and `showAny` conditions reference existing field keys and use `eq`, `neq`, `in`, `notIn` or `notEmpty`.\n- Step `routes` only point to later steps. `to: null` ends the form without auto-submission.\n\n## Checking definitions\n\nWhen the `formhaus` MCP server is available (`npx -y @formhaus/mcp`):\n\n- Call `capabilities` for the current field types, validation rules and step semantics.\n- Call `example_definitions` for working examples.\n- Call `validate_definition` after every change and fix all `errors` before finishing.\n- Call `simulate_path` with sample answers to check branching steps and conditions.\n\nReference: https://formhaus.dev/api/definition.html\n';
13
+ var formhaus_default = '---\ndescription: Formhaus form definitions. Use when creating, editing or reviewing a Formhaus JSON form definition, or code that passes one to FormEngine or FormRenderer.\nalwaysApply: false\n---\n\n# Formhaus form definitions\n\nA Formhaus form is a JSON definition rendered by `@formhaus/core` with `@formhaus/react` or `@formhaus/vue`. One definition drives the web form and the Figma plugin.\n\n- Start each definition file with `"$schema": "https://formhaus.dev/schema/form-definition.json"`.\n- Required keys: `id` (kebab-case), `title`, `submit: { "label": "..." }`.\n- Use `fields` for a single page or `steps` for a multi-step form, never both.\n- Field `key` values are camelCase and unique across all steps.\n- `show` and `showAny` conditions reference existing field keys and use `eq`, `neq`, `in`, `notIn`, `contains`, `notContains` or `notEmpty`. A `multiselect` holds a list: test it with `contains` or `notContains`.\n- Step `routes` only point to later steps. `to: null` ends the form without auto-submission.\n\n## Checking definitions\n\nWhen the `formhaus` MCP server is available (`npx -y @formhaus/mcp`):\n\n- Call `capabilities` for the current field types, validation rules and step semantics.\n- Call `example_definitions` for working examples.\n- Call `validate_definition` after every change and fix all `errors` before finishing.\n- Call `simulate_path` with sample answers to check branching steps and conditions.\n\nReference: https://formhaus.dev/api/definition.html\n';
14
14
 
15
15
  // ../../plugins/formhaus/skills/formhaus-create-form/SKILL.md
16
- var SKILL_default = '---\nname: formhaus-create-form\ndescription: |\n Generate @formhaus/core form definitions from any input: text description,\n CSV/table, or screenshot of a form. Interactive step/condition builder.\n Output pastes directly into the Formhaus Figma plugin or FormRenderer.\n Use when: "generate form", "form definition", "form JSON", "create a form",\n "formhaus", or when user describes form fields in any format.\nallowed-tools:\n - Read\n - Write\n - Glob\n - Grep\n - AskUserQuestion\n - Agent\n - mcp__plugin_formhaus_formhaus__validate_definition\n - mcp__plugin_formhaus_formhaus__simulate_path\n - mcp__plugin_formhaus_formhaus__capabilities\n - mcp__plugin_formhaus_formhaus__example_definitions\n - mcp__plugin_formhaus_formhaus__publish_form\n - mcp__plugin_formhaus_formhaus__list_forms\n - mcp__formhaus__validate_definition\n - mcp__formhaus__simulate_path\n - mcp__formhaus__capabilities\n - mcp__formhaus__example_definitions\n - mcp__formhaus__publish_form\n - mcp__formhaus__list_forms\n - Edit\n - Bash\n---\n\n# Form Definition Generator\n\nYou generate valid `@formhaus/core` form definitions from any input.\nThe output is ready to paste into the Formhaus Figma plugin or use with FormRenderer.\nThe formhaus repo does not need to be cloned. The JSON Schema is published at\n`https://formhaus.dev/schema/form-definition.json`.\n\n## Formhaus MCP tools\n\nCheck whether the `formhaus` MCP server tools are available (`validate_definition`,\n`simulate_path`, `capabilities`, `example_definitions`, `publish_form`). The Formhaus Claude Code plugin\nstarts the server. Without the plugin, it is added with\n`claude mcp add formhaus -- npx -y @formhaus/mcp`.\n\n- `capabilities` returns the current field types, validation rules, condition operators\n and step semantics. Prefer it over the reference below when they differ.\n- `example_definitions` lists bundled example definitions and returns one by `id`.\n- `validate_definition` checks a definition against the JSON Schema and the engine.\n- `simulate_path` walks a multi-step definition with sample answers and reports the step path.\n- `publish_form` publishes a definition to Formhaus Cloud and returns a live endpoint, a hosted page URL, an embed snippet and a React snippet.\n\nIf the tools are not available, validate by hand against the types below and tell the\nuser that `@formhaus/mcp` can check the result.\n\n## Types Reference\n\nThese are the exact TypeScript types. Your output MUST conform to them.\n\n```typescript\ninterface FormDefinition {\n id: string; // kebab-case identifier\n formatVersion?: string; // "1.3" when the form uses scale, rating, option images or a matrix\n title: string; // human-readable form title\n description?: string; // short intro under the title, up to 1000 characters\n submit: FormAction; // submit button config (required)\n cancel?: FormAction; // cancel button (optional)\n fields?: FormField[]; // single-step form\n steps?: FormStep[]; // multi-step form (use steps OR fields, not both)\n}\n\ninterface FormStep {\n id: string;\n title: string;\n description?: string;\n fields: (FormField | FormSection)[]; // may be empty for a welcome or statement screen\n show?: ShowCondition[]; // step visible when ALL conditions true\n showAny?: ShowCondition[]; // step visible when ANY condition true\n routes?: StepRoute[]; // ordered forward destinations\n next?: FormAction | false; // custom "next" or false to hide it\n back?: FormAction | false; // custom "back" or false to hide it\n skip?: FormAction; // Skip button: resets the step, moves on without validation\n}\n\ninterface StepRoute {\n to: string | null; // later step id, or terminal without auto-submission\n show?: ShowCondition[];\n showAny?: ShowCondition[];\n}\n\ninterface FormField {\n key: string; // camelCase field identifier\n type: FieldType;\n label: string;\n placeholder?: string;\n helperText?: string;\n defaultValue?: unknown;\n autoAdvance?: boolean; // explicit activation advances; built-in radio, scale and rating support it\n show?: ShowCondition[]; // field visible when ALL conditions true\n showAny?: ShowCondition[]; // field visible when ANY condition true\n validation?: FieldValidation;\n options?: FieldOption[]; // for select, multiselect, radio; anchor labels on the first, middle and last point of scale and rating\n layout?: \'stack\' | \'inline\'; // radio/multiselect; inline only for a few short options\n optionsFrom?: string; // dynamic options loaded at runtime\n optionsDependsOn?: string[];\n accept?: string; // for file type: "image/*", ".pdf"\n rows?: number; // for textarea\n mask?: string; // input mask\n inputMode?: \'text\' | \'numeric\' | \'decimal\' | \'tel\' | \'email\' | \'url\' | \'search\';\n}\n\ntype FieldType =\n | \'text\' | \'email\' | \'phone\' | \'number\' | \'password\'\n | \'select\' | \'autocomplete\' | \'multiselect\'\n | \'checkbox\' | \'radio\' | \'switch\'\n | \'file\' | \'date\' | \'datetime\' | \'time\' | \'textarea\' | \'hidden\'\n | \'scale\' // whole number from validation.min (0 or 1, default 1) to validation.max (default 5)\n | \'rating\'; // stars from 1 to validation.max (default 5, 3 to 10)\n\ninterface FormSection {\n key: string;\n type: \'section\';\n title: string;\n description?: string;\n layout?: \'matrix\'; // grid: each field is a radio row with the same options\n show?: ShowCondition[];\n fields: FormField[]; // may be empty for a statement between questions\n}\n\ninterface ShowCondition {\n field: string; // references another field\'s key\n eq?: string | number | boolean; // equals\n neq?: string | number | boolean; // not equals\n in?: (string | number)[]; // one of\n notIn?: (string | number)[]; // not one of\n notEmpty?: boolean; // has a value\n gt?: number; gte?: number; // numeric comparisons, such as an NPS score\n lt?: number; lte?: number;\n}\n\ninterface FieldValidation {\n required?: boolean | string; // true or custom message\n minLength?: number;\n minLengthMessage?: string;\n maxLength?: number;\n maxLengthMessage?: string;\n min?: number;\n minMessage?: string;\n max?: number;\n maxMessage?: string;\n pattern?: string; // regex\n patternMessage?: string;\n matchField?: string; // must match another field (e.g. confirmPassword)\n matchFieldMessage?: string;\n validator?: string; // custom validator name\n}\n\ninterface FieldOption {\n value: string; // on scale and rating anchors: the point number, such as "0" or "10"\n label: string;\n image?: string; // image URL for radio and multiselect (picture choice)\n}\ninterface FormAction {\n label: string;\n variant?: \'primary\' | \'secondary\' | \'text\';\n action?: string;\n disabled?: ShowCondition[];\n}\n```\n\n## Workflow\n\n### Step 1: Detect input format\n\nLook at the user\'s input and classify:\n\n- **Text description**: "Registration form with name, email, password"\n- **CSV/table**: structured data with columns like key, type, label, required\n- **Screenshot**: user provides a path to an image file or says "look at this screenshot"\n- **Existing definition**: user references a bundled example (`example_definitions`)\n\n### Step 2: Extract fields\n\nFrom the input, extract a flat list of fields with: key, type, label, and any\nobvious validation (required, email format, etc).\n\n**Type inference rules:**\n- Email addresses \u2192 `email`\n- Phone numbers \u2192 `phone`\n- Passwords, PINs \u2192 `password`\n- "Agree to terms", "Subscribe" \u2192 `checkbox`\n- "Account type", "Gender" with 2-4 options \u2192 `radio`\n- Dropdowns, "Select..." with 5+ options \u2192 `select`\n- Searchable dropdowns, typeahead \u2192 `autocomplete`\n- Country, city, currency \u2192 `select`\n- Long text, "Description", "Notes", "Bio" \u2192 `textarea`\n- Dates, birthdays, "When" \u2192 `date`\n- Date and time together \u2192 `datetime`\n- Time of day only, "Start time", "Opening hours" \u2192 `time`\n- Tracking values the user never sees, "Source", "Campaign", "Referral code from the link" \u2192 `hidden`\n- File upload, "Attach", "Upload" \u2192 `file`\n- Toggle, "Enable/Disable", notifications \u2192 `switch`\n- Numbers, amounts, quantities \u2192 `number`\n- "How likely are you to recommend" with 0 to 10 \u2192 `scale` with `validation: { min: 0, max: 10 }` and anchors `"0": "Not at all likely"`, `"10": "Extremely likely"` (NPS)\n- "Rate", "stars", satisfaction from 1 to 5 \u2192 `rating`\n- "Strongly disagree \u2026 Strongly agree" or "1 to 7" for one statement \u2192 `scale` with start and end anchors\n- Several statements with the same agree/disagree answers \u2192 a `section` with `layout: "matrix"` and one `radio` per statement, all with the same options\n- Options shown as pictures \u2192 `radio` or `multiselect` with `image` on each option\n- An intro, welcome or "thank you for joining" screen \u2192 a step with a title, a description, `next: { label: "Start" }` and `fields: []`\n- Everything else \u2192 `text`\n\n### Step 3: Interactive refinement\n\nUse AskUserQuestion for each decision point. ONE question at a time.\n\n**3a: Steps**\n\nIf the form has 5+ fields, ask:\n\n> "This form has N fields. Should it be a single page or split into steps?\n> If steps, describe how to group them (e.g. \'Personal info, Address, Payment\')."\n\nOptions:\n- A) Single step (all fields on one page)\n- B) Multi-step (I\'ll describe the groups)\n- C) Auto-group (you decide based on field types)\n\nIf C: group by semantic similarity (personal info, address, account, payment, etc.).\n\n**3b: Conditions**\n\nIf any field logically depends on another (e.g. "Bank name" only makes sense\nwhen payment method is "bank"), ask:\n\n> "I noticed [field X] probably only shows when [field Y] = [value].\n> Should I add show conditions?"\n\nOptions:\n- A) Yes, add the conditions you suggested\n- B) No conditions, show everything always\n- C) Let me specify which fields depend on what\n\n**3c: Validation**\n\nFor each field, infer validation:\n- Fields marked "required" or with * \u2192 `required: true`\n- Email fields \u2192 pattern for email format\n- Password fields \u2192 minLength: 8\n- Phone fields \u2192 inputMode: \'tel\'\n- Number fields \u2192 inputMode: \'numeric\'\n\nAsk only if there\'s ambiguity: "Should [field] be required?"\n\n### Step 4: Generate JSON\n\nOutput the complete form definition JSON. Follow these rules:\n\n1. `id` is kebab-case derived from the title\n2. `key` for each field is camelCase\n3. Every form MUST have `submit: { label: "..." }`\n4. Use `fields` for single-step, `steps` for multi-step\n5. Only include optional properties when they add value (no empty strings, no `required: false`)\n6. Options for select/radio must have both `value` and `label`\n7. Set `"formatVersion": "1.3"` when the form has a `scale`, a `rating`, an option `image` or a matrix section. Set at least `"1.1"` when it has sections or groups\n8. Conditions on `scale` and `rating` compare numbers: `{ "field": "nps", "lte": 6 }`. A range is two conditions: `gte` and `lte`\n\nPut `"$schema": "https://formhaus.dev/schema/form-definition.json"` as the first key\nso editors validate and autocomplete the file.\n\n### Step 5: Validate\n\nIf the MCP tools are available:\n\n1. Call `validate_definition` with the definition. Fix every entry in `errors` and\n re-run until `errors` is empty. Review `warnings` and fix the ones that are real mistakes.\n2. For multi-step definitions with conditions or routes, call `simulate_path` with answers\n that take each branch and check that the path matches the user\'s intent.\n\n### Step 6: Present and confirm\n\nShow the generated JSON in a code block. Ask:\n\n> "Here\'s your form definition. You can paste this directly into the\n> Formhaus Figma plugin or use with FormRenderer."\n\nOptions:\n- A) Looks good, copy and done\n- B) Make changes (specify what)\n- C) Publish it and get a live endpoint\n\nIf B: apply changes and re-present\n\n### Step 7: Publish and wire into the project\n\nOnly when the user chose C, or asked for a working form, a hosted form or an endpoint.\nRequires the `publish_form` tool and a definition that passed `validate_definition`.\n\n1. Call `publish_form` with the definition. Hosted forms do not support `password`, `file`,\n `autocomplete`, `optionsFrom` and custom field types. If the tool reports an error, fix every\n listed path and call it again.\n2. Tell the user the `hosted_url` and `endpoint`. Without an API key the form is unclaimed\n (7 days, 100 submissions): give them the `claim_url` from the result.\n3. Detect the project: React or Vue in `package.json`, or plain HTML. Install the matching\n package (`@formhaus/react` or `@formhaus/vue`, plus `@formhaus/core` for `style.css`) with\n the project\'s package manager.\n4. Add the `react_snippet` from the result where the user wants the form. It imports\n `FormhausForm` from `@formhaus/react/cloud`. For Vue use `FormhausForm` from\n `@formhaus/vue/cloud` with the same `id`. For plain HTML use `embed_snippet`.\n5. Publishing again with the same `form_id` ships a new version at the same endpoint, so\n later edits to the definition do not change the project code.\n\n## Example Definitions\n\nCall `example_definitions` to list the bundled examples and fetch one by `id` when you\nneed to verify a pattern or show the user an example. They cover common field types,\nvalidation, show conditions, linear steps, and branching steps with routes.\n\n## Rules\n\n- NEVER call `publish_form` unless the user asked for a hosted form or Formhaus Cloud. If the project has its own backend, render with `FormRenderer` and submit to that backend.\n- NEVER output invalid JSON. Validate before presenting, with `validate_definition` when available.\n- NEVER invent field types not in the FieldType union.\n- NEVER use `fields` AND `steps` in the same definition. Pick one.\n- Keys must be unique within a definition (across all steps).\n- ShowCondition.field must reference an existing field key.\n- For multi-step: each step needs `id`, `title`, and `fields`.\n- Submit label should match the form\'s purpose ("Submit", "Create transfer", "Register", etc.).\n';
16
+ var SKILL_default = '---\nname: formhaus-create-form\ndescription: |\n Generate @formhaus/core form definitions from any input: text description,\n CSV/table, or screenshot of a form. Interactive step/condition builder.\n Output pastes directly into the Formhaus Figma plugin or FormRenderer.\n Use when: "generate form", "form definition", "form JSON", "create a form",\n "formhaus", or when user describes form fields in any format.\nallowed-tools:\n - Read\n - Write\n - Glob\n - Grep\n - AskUserQuestion\n - Agent\n - mcp__plugin_formhaus_formhaus__validate_definition\n - mcp__plugin_formhaus_formhaus__simulate_path\n - mcp__plugin_formhaus_formhaus__capabilities\n - mcp__plugin_formhaus_formhaus__example_definitions\n - mcp__plugin_formhaus_formhaus__publish_form\n - mcp__plugin_formhaus_formhaus__list_forms\n - mcp__formhaus__validate_definition\n - mcp__formhaus__simulate_path\n - mcp__formhaus__capabilities\n - mcp__formhaus__example_definitions\n - mcp__formhaus__publish_form\n - mcp__formhaus__list_forms\n - Edit\n - Bash\n---\n\n# Form Definition Generator\n\nYou generate valid `@formhaus/core` form definitions from any input.\nThe output is ready to paste into the Formhaus Figma plugin or use with FormRenderer.\nThe formhaus repo does not need to be cloned. The JSON Schema is published at\n`https://formhaus.dev/schema/form-definition.json`.\n\n## Formhaus MCP tools\n\nCheck whether the `formhaus` MCP server tools are available (`validate_definition`,\n`simulate_path`, `capabilities`, `example_definitions`, `publish_form`). The Formhaus Claude Code plugin\nstarts the server. Without the plugin, it is added with\n`claude mcp add formhaus -- npx -y @formhaus/mcp`.\n\n- `capabilities` returns the current field types, validation rules, condition operators\n and step semantics. Prefer it over the reference below when they differ.\n- `example_definitions` lists bundled example definitions and returns one by `id`.\n- `validate_definition` checks a definition against the JSON Schema and the engine.\n- `simulate_path` walks a multi-step definition with sample answers and reports the step path.\n- `publish_form` publishes a definition to Formhaus Cloud and returns a live endpoint, a hosted page URL, an embed snippet and a React snippet.\n\nIf the tools are not available, validate by hand against the types below and tell the\nuser that `@formhaus/mcp` can check the result.\n\n## Types Reference\n\nThese are the exact TypeScript types. Your output MUST conform to them.\n\n```typescript\ninterface FormDefinition {\n id: string; // kebab-case identifier\n formatVersion?: string; // "1.4" with contains or notContains, "1.3" with scale, rating, option images or a matrix\n title: string; // human-readable form title\n description?: string; // short intro under the title, up to 1000 characters\n submit: FormAction; // submit button config (required)\n cancel?: FormAction; // cancel button (optional)\n fields?: FormField[]; // single-step form\n steps?: FormStep[]; // multi-step form (use steps OR fields, not both)\n}\n\ninterface FormStep {\n id: string;\n title: string;\n description?: string;\n fields: (FormField | FormSection)[]; // may be empty for a welcome or statement screen\n show?: ShowCondition[]; // step visible when ALL conditions true\n showAny?: ShowCondition[]; // step visible when ANY condition true\n routes?: StepRoute[]; // ordered forward destinations\n next?: FormAction | false; // custom "next" or false to hide it\n back?: FormAction | false; // custom "back" or false to hide it\n skip?: FormAction; // Skip button: resets the step, moves on without validation\n}\n\ninterface StepRoute {\n to: string | null; // later step id, or terminal without auto-submission\n show?: ShowCondition[];\n showAny?: ShowCondition[];\n}\n\ninterface FormField {\n key: string; // camelCase field identifier\n type: FieldType;\n label: string;\n placeholder?: string;\n helperText?: string;\n defaultValue?: unknown;\n autoAdvance?: boolean; // explicit activation advances; built-in radio, scale and rating support it\n show?: ShowCondition[]; // field visible when ALL conditions true\n showAny?: ShowCondition[]; // field visible when ANY condition true\n validation?: FieldValidation;\n options?: FieldOption[]; // for select, multiselect, radio; anchor labels on the first, middle and last point of scale and rating\n layout?: \'stack\' | \'inline\'; // radio/multiselect; inline only for a few short options\n optionsFrom?: string; // dynamic options loaded at runtime\n optionsDependsOn?: string[];\n accept?: string; // for file type: "image/*", ".pdf"\n rows?: number; // for textarea\n mask?: string; // input mask\n inputMode?: \'text\' | \'numeric\' | \'decimal\' | \'tel\' | \'email\' | \'url\' | \'search\';\n}\n\ntype FieldType =\n | \'text\' | \'email\' | \'phone\' | \'number\' | \'password\'\n | \'select\' | \'autocomplete\' | \'multiselect\'\n | \'checkbox\' | \'radio\' | \'switch\'\n | \'file\' | \'date\' | \'datetime\' | \'time\' | \'textarea\' | \'hidden\'\n | \'scale\' // whole number from validation.min (0 or 1, default 1) to validation.max (default 5)\n | \'rating\'; // stars from 1 to validation.max (default 5, 3 to 10)\n\ninterface FormSection {\n key: string;\n type: \'section\';\n title: string;\n description?: string;\n layout?: \'matrix\'; // grid: each field is a radio row with the same options\n show?: ShowCondition[];\n fields: FormField[]; // may be empty for a statement between questions\n}\n\ninterface ShowCondition {\n field: string; // references another field\'s key\n eq?: string | number | boolean; // equals\n neq?: string | number | boolean; // not equals\n in?: (string | number)[]; // one of\n notIn?: (string | number)[]; // not one of\n contains?: string | number; // multiselect answer includes this option\n notContains?: string | number; // multiselect answer does not include it\n notEmpty?: boolean; // has a value\n gt?: number; gte?: number; // numeric comparisons, such as an NPS score\n lt?: number; lte?: number;\n}\n\ninterface FieldValidation {\n required?: boolean | string; // true or custom message\n minLength?: number;\n minLengthMessage?: string;\n maxLength?: number;\n maxLengthMessage?: string;\n min?: number;\n minMessage?: string;\n max?: number;\n maxMessage?: string;\n pattern?: string; // regex\n patternMessage?: string;\n matchField?: string; // must match another field (e.g. confirmPassword)\n matchFieldMessage?: string;\n validator?: string; // custom validator name\n}\n\ninterface FieldOption {\n value: string; // on scale and rating anchors: the point number, such as "0" or "10"\n label: string;\n image?: string; // image URL for radio and multiselect (picture choice)\n}\ninterface FormAction {\n label: string;\n variant?: \'primary\' | \'secondary\' | \'text\';\n action?: string;\n disabled?: ShowCondition[];\n}\n```\n\n## Workflow\n\n### Step 1: Detect input format\n\nLook at the user\'s input and classify:\n\n- **Text description**: "Registration form with name, email, password"\n- **CSV/table**: structured data with columns like key, type, label, required\n- **Screenshot**: user provides a path to an image file or says "look at this screenshot"\n- **Existing definition**: user references a bundled example (`example_definitions`)\n\n### Step 2: Extract fields\n\nFrom the input, extract a flat list of fields with: key, type, label, and any\nobvious validation (required, email format, etc).\n\n**Type inference rules:**\n- Email addresses \u2192 `email`\n- Phone numbers \u2192 `phone`\n- Passwords, PINs \u2192 `password`\n- "Agree to terms", "Subscribe" \u2192 `checkbox`\n- "Account type", "Gender" with 2-4 options \u2192 `radio`\n- Dropdowns, "Select..." with 5+ options \u2192 `select`\n- Searchable dropdowns, typeahead \u2192 `autocomplete`\n- Country, city, currency \u2192 `select`\n- Long text, "Description", "Notes", "Bio" \u2192 `textarea`\n- Dates, birthdays, "When" \u2192 `date`\n- Date and time together \u2192 `datetime`\n- Time of day only, "Start time", "Opening hours" \u2192 `time`\n- Tracking values the user never sees, "Source", "Campaign", "Referral code from the link" \u2192 `hidden`\n- File upload, "Attach", "Upload" \u2192 `file`\n- Toggle, "Enable/Disable", notifications \u2192 `switch`\n- Numbers, amounts, quantities \u2192 `number`\n- "How likely are you to recommend" with 0 to 10 \u2192 `scale` with `validation: { min: 0, max: 10 }` and anchors `"0": "Not at all likely"`, `"10": "Extremely likely"` (NPS)\n- "Rate", "stars", satisfaction from 1 to 5 \u2192 `rating`\n- "Strongly disagree \u2026 Strongly agree" or "1 to 7" for one statement \u2192 `scale` with start and end anchors\n- Several statements with the same agree/disagree answers \u2192 a `section` with `layout: "matrix"` and one `radio` per statement, all with the same options\n- Options shown as pictures \u2192 `radio` or `multiselect` with `image` on each option\n- An intro, welcome or "thank you for joining" screen \u2192 a step with a title, a description, `next: { label: "Start" }` and `fields: []`\n- Everything else \u2192 `text`\n\n### Step 3: Interactive refinement\n\nUse AskUserQuestion for each decision point. ONE question at a time.\n\n**3a: Steps**\n\nIf the form has 5+ fields, ask:\n\n> "This form has N fields. Should it be a single page or split into steps?\n> If steps, describe how to group them (e.g. \'Personal info, Address, Payment\')."\n\nOptions:\n- A) Single step (all fields on one page)\n- B) Multi-step (I\'ll describe the groups)\n- C) Auto-group (you decide based on field types)\n\nIf C: group by semantic similarity (personal info, address, account, payment, etc.).\n\n**3b: Conditions**\n\nIf any field logically depends on another (e.g. "Bank name" only makes sense\nwhen payment method is "bank"), ask:\n\n> "I noticed [field X] probably only shows when [field Y] = [value].\n> Should I add show conditions?"\n\nOptions:\n- A) Yes, add the conditions you suggested\n- B) No conditions, show everything always\n- C) Let me specify which fields depend on what\n\n**3c: Validation**\n\nFor each field, infer validation:\n- Fields marked "required" or with * \u2192 `required: true`\n- Email fields \u2192 pattern for email format\n- Password fields \u2192 minLength: 8\n- Phone fields \u2192 inputMode: \'tel\'\n- Number fields \u2192 inputMode: \'numeric\'\n\nAsk only if there\'s ambiguity: "Should [field] be required?"\n\n### Step 4: Generate JSON\n\nOutput the complete form definition JSON. Follow these rules:\n\n1. `id` is kebab-case derived from the title\n2. `key` for each field is camelCase\n3. Every form MUST have `submit: { label: "..." }`\n4. Use `fields` for single-step, `steps` for multi-step\n5. Only include optional properties when they add value (no empty strings, no `required: false`)\n6. Options for select/radio must have both `value` and `label`\n7. Set `"formatVersion": "1.4"` when a condition uses `contains` or `notContains`, else `"1.3"` when the form has a `scale`, a `rating`, an option `image` or a matrix section. Set at least `"1.1"` when it has sections or groups\n8. Conditions on `scale` and `rating` compare numbers: `{ "field": "nps", "lte": 6 }`. A range is two conditions: `gte` and `lte`\n9. A `multiselect` answer is a list. Test one option with `{ "field": "interests", "contains": "other" }`, never with `eq` or `in`\n\nPut `"$schema": "https://formhaus.dev/schema/form-definition.json"` as the first key\nso editors validate and autocomplete the file.\n\n### Step 5: Validate\n\nIf the MCP tools are available:\n\n1. Call `validate_definition` with the definition. Fix every entry in `errors` and\n re-run until `errors` is empty. Review `warnings` and fix the ones that are real mistakes.\n2. For multi-step definitions with conditions or routes, call `simulate_path` with answers\n that take each branch and check that the path matches the user\'s intent.\n\n### Step 6: Present and confirm\n\nShow the generated JSON in a code block. Ask:\n\n> "Here\'s your form definition. You can paste this directly into the\n> Formhaus Figma plugin or use with FormRenderer."\n\nOptions:\n- A) Looks good, copy and done\n- B) Make changes (specify what)\n- C) Publish it and get a live endpoint\n\nIf B: apply changes and re-present\n\n### Step 7: Publish and wire into the project\n\nOnly when the user chose C, or asked for a working form, a hosted form or an endpoint.\nRequires the `publish_form` tool and a definition that passed `validate_definition`.\n\n1. Call `publish_form` with the definition. Hosted forms do not support `password`, `file`,\n `autocomplete`, `optionsFrom` and custom field types. If the tool reports an error, fix every\n listed path and call it again.\n2. Tell the user the `hosted_url` and `endpoint`. Without an API key the form is unclaimed\n (7 days, 100 submissions): give them the `claim_url` from the result.\n3. Detect the project: React or Vue in `package.json`, or plain HTML. Install the matching\n package (`@formhaus/react` or `@formhaus/vue`, plus `@formhaus/core` for `style.css`) with\n the project\'s package manager.\n4. Add the `react_snippet` from the result where the user wants the form. It imports\n `FormhausForm` from `@formhaus/react/cloud`. For Vue use `FormhausForm` from\n `@formhaus/vue/cloud` with the same `id`. For plain HTML use `embed_snippet`.\n5. Publishing again with the same `form_id` ships a new version at the same endpoint, so\n later edits to the definition do not change the project code.\n\n## Example Definitions\n\nCall `example_definitions` to list the bundled examples and fetch one by `id` when you\nneed to verify a pattern or show the user an example. They cover common field types,\nvalidation, show conditions, linear steps, and branching steps with routes.\n\n## Rules\n\n- NEVER call `publish_form` unless the user asked for a hosted form or Formhaus Cloud. If the project has its own backend, render with `FormRenderer` and submit to that backend.\n- NEVER output invalid JSON. Validate before presenting, with `validate_definition` when available.\n- NEVER invent field types not in the FieldType union.\n- NEVER use `fields` AND `steps` in the same definition. Pick one.\n- Keys must be unique within a definition (across all steps).\n- ShowCondition.field must reference an existing field key.\n- For multi-step: each step needs `id`, `title`, and `fields`.\n- Submit label should match the form\'s purpose ("Submit", "Create transfer", "Register", etc.).\n';
17
17
 
18
18
  // src/context.ts
19
19
  var SetupError = class extends Error {
@@ -203,7 +203,8 @@ var NAME = "Claude Code";
203
203
  var PLUGIN_PREFIX = `${SERVER_NAME}@`;
204
204
  var SHIPPED_SKILLS = [
205
205
  "e929afc9f58a267647341d962283d84749a18028e5ec2c4bd770a214c26a7c25",
206
- "7288b74973a7668592f0baef836793aaf8591f3468546c87ff0c2335c8380065"
206
+ "7288b74973a7668592f0baef836793aaf8591f3468546c87ff0c2335c8380065",
207
+ "4638f4303a3907af4f6c077dd52251cb2e909875fe6010641e9932333ce0f351"
207
208
  ];
208
209
  var CLAUDE_ADD_ARGS = ["mcp", "add", "--scope", "user", SERVER_NAME, "--", SERVER.command, ...SERVER.args];
209
210
  function configDir(ctx) {
@@ -1088,7 +1089,10 @@ function claudeDesktopDir(ctx) {
1088
1089
  function found(ctx, directory, bin) {
1089
1090
  return isDirectory(directory) || findOnPath(ctx, bin) !== void 0;
1090
1091
  }
1091
- var SHIPPED_RULES = ["1bb79502ba88f1d38bef0bb3f34ede713f94487643496eae94ca6be7d2c8a32c"];
1092
+ var SHIPPED_RULES = [
1093
+ "1bb79502ba88f1d38bef0bb3f34ede713f94487643496eae94ca6be7d2c8a32c",
1094
+ "2be2512e6127d311d5b887a6688e1d71067033126355ee9c797b77c857dbd6b1"
1095
+ ];
1092
1096
  var codex = {
1093
1097
  name: "Codex",
1094
1098
  detect: (ctx) => found(ctx, codexHome(ctx), "codex"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "formhaus",
3
- "version": "0.19.1",
3
+ "version": "0.20.0",
4
4
  "description": "Set up the Formhaus MCP server and agent instructions in Claude Code, Codex, Cursor and Claude Desktop with one command.",
5
5
  "type": "module",
6
6
  "engines": {