From ef9dbde8742b39d66c3236c23bf55c41b8384535 Mon Sep 17 00:00:00 2001 From: Travis Tidwell Date: Sun, 12 Jul 2026 11:42:45 -0500 Subject: [PATCH 1/7] Refactor README to be easier to follow and provide more realistic examples. --- README.md | 640 +++------------------- plugin/skills/formio-application/SKILL.md | 64 +-- 2 files changed, 90 insertions(+), 614 deletions(-) diff --git a/README.md b/README.md index ebaf87f..86e1066 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,29 @@ -# Form.io Agentic Coding Toolset +# The Form.io Agentic Coding Toolset -**Build compliance-ready apps with Form.io, in your agentic coding environment.** +`@formio/ai` is what brings Form.io into your agentic coding environment. It provides a series of tools that enable any developer to perform a number of complex actions against the Form.io Enterprise Server using their favorite Agentic Coding toolsets (starting with Claude Code). This turns the Form.io Enterprise Server into a **Composable Backend for Agentically Coded Applications**. -With Form.io, define a data model once and leverage it in multiple ways at runtime. A singular data model can be rendered as a form for humans to fill out, leveraged as the auto-generated API a system calls, or provide context for AI agents to execute workflows. One model, every surface. +## Getting Started +Getting started is easy. First install Claude Code as follows: -That's what makes Form.io the data standardization layer for enterprises in regulated industries around the world. Form.io enables developers and AI coding agents to build applications using forms as infrastructure for compliance-ready solutions in any industry. + 1. [Install Claude Code](https://github.com/anthropics/claude-code#get-started) + 2. Run `claude` in your Terminal. + 3. Run the following command within **Claude Code**. -`@formio/ai` is what brings Form.io into your agentic coding environment. A Claude Code plugin, an MCP server (`@formio/mcp`), and a skill library so your agent works in Form.io primitives: forms, resources, nested data structures, role-based access, group permissions, server-side actions. - -The result: every app your agent builds inherits the model, the governance, RBAC, and the audit trail by default. - -> **Note on scope.** This repo is for AI coding agents while writing code. Form.io also provides a Universal Agent Gateway (UAG) for governing agentic workflows running in production. [More on Form.io UAG here.](https://form.io/uag) +```bash +/plugin marketplace add https://github.com/formio/ai.git +/plugin install formio-ai@formio +``` -Official Documentation --------------------------- -For the latest documentation, release information, and guides, always refer to the official Form.io Help Documentation available here: +Claude Code prompts for the `Project URL` and the `Base URL` on install. These are described as follows: -**[https://help.form.io](https://help.form.io/dev/ai)** + - **Project URL**: This is the endpoint for your 'project'. If you are using our SaaS environment (https://portal.form.io), then your project URL will be a sub-domain, such https://myproject.form.io. However, for most use cases, when Form.io is deployed in your own enviornment, then the project url is typically a 'sub-directory' structure like https://forms.mysite.com/myproject. + - **Base URL**: This is the endpoint for the deployment. If you are using the SaaS environment (https://portal.form.io), then this is always going to be https://api.form.io. However, for most use cases, when Form.io is deployed in your own environment, then this is the URL of the deployment like https://forms.mysite.com. -## What you get + ## What you get - **Claude Code plugin: `@formio/ai`.** One-command install. Bundles the MCP server and skill library, registers them with Claude Code. - **MCP server: `@formio/mcp`.** Form.io operations (`form_*`, `role_*`, `action_*`, `project_*`) as MCP tools. Works with any MCP-aware client: Claude Code, Claude Desktop, VS Copilot, and whatever comes next. -- **Skills library:** Eight activatable skills covering app orchestration, resource planning, Angular scaffolding, JSON-schema authoring, action configuration, authentication & authorization, the `@formio/js` SDK surface, and the full Form.io REST surface. +- **Skills library:** Nine activatable skills covering app orchestration, resource planning, Form building, JSON-schema authoring, action configuration, authentication & authorization, the `@formio/js` SDK surface, and the full Form.io REST surface. ## Why this exists @@ -34,501 +35,97 @@ Form.io has been the data standardization layer for enterprise data for a decade **Governance built in.** RBAC, group permissions, change history, audit trails — all emitted on the first pass. Every app lands inside the same compliance envelope the enterprise already runs on. ---- - -## Table of contents - -- [What this library is for](#what-this-library-is-for) -- [Use cases](#use-cases) -- [Quickstart — Claude Code plugin](#quickstart--claude-code-plugin) -- [Quickstart — standalone MCP server](#quickstart--standalone-mcp-server) -- [How it works](#how-it-works) - - [Skill routing map](#skill-routing-map) - - [The `formio-application` flow](#the-formio-application-flow) - - [The `formio-resource-planner` two-phase flow](#the-formio-resource-planner-two-phase-flow) - - [The planner ↔ `formio-auth` handoff](#the-planner--formio-auth-handoff) - - [The `formio-angular` five-phase flow](#the-formio-angular-five-phase-flow) -- [Sample resource maps](#sample-resource-maps) - - [Task manager (group-via-join)](#task-manager-group-via-join) - - [CRM with transitive group access](#crm-with-transitive-group-access) -- [Skills library](#skills-library) -- [MCP server tools](#mcp-server-tools) -- [Authentication](#authentication) -- [Environment variables](#environment-variables) -- [Development](#development) -- [Spec-driven development with OpenSpec](#spec-driven-development-with-openspec) -- [License](#license) - ---- - -## What this library is for - -`@formio/ai` introduces Form.io to agentic coding workflows. The skills cover the full mental model an enterprise Form.io developer carries — and the MCP tools give the agent direct, authenticated access to the running platform — so the agent can: - -- **Reason about the data model** as resources, sub-resources, and join resources rather than generic tables. -- **Apply RBAC correctly** by default — Roles, Group Assignment actions, field-based `submissionAccess`, owner-vs-group-vs-role-vs-tenant access strategies, transitive group propagation through hidden calculated mirrors. -- **Wire authentication** via the built-in `user` resource or a custom user resource, with Login + Role Assignment actions, self-register vs admin-invite flows, and the right anonymous/authenticated/admin role layering. -- **Configure server-side behavior** through actions — email, webhook, save, login, role assignment, group assignment, reset password — with the right handler/method/priority/condition combinations. -- **Author and edit form JSON** — every component type, validation rule, conditional logic expression, calculated value, and field-based access block. -- **Operate the running platform** through first-party MCP tools (`form_*`, `role_*`, `action_*`, `project_*`) instead of brittle raw HTTP, with implicit JWT auth via the portal-login flow. -- **Navigate the full REST surface** — platform admin, project admin, runtime/end-user, PDF — when an MCP tool does not yet cover an operation. - -Agents loaded with `@formio/ai` produce enterprise-grade applications: every resource ships with a Save Submission action so submissions actually persist; every authenticated role inherits its access through documented Group / Field-based / Role mechanics; every front-end component is scaffolded against `@formio/angular` with `FormioAuthConfig` and `FormioResourceConfig` derived directly from the planner's `template.json`. - ---- - -## Use Cases - -What you can do with these tools: Five real prompts, paste-ready in Claude Code. Each one shows a different shape of work the agent handles natively. - -### Build a complete app from one prompt - -> "/formio-application Build me a CRM where sales reps only see accounts owned by the teams they belong to." - -The agent plans the data model, imports it into Form.io, and scaffolds an Angular front-end wired to the project. Approval gate at every step. At the end you have a running application — not a prototype. - -### Extend a running app - -> "/formio-application Also let customers leave reviews on each completed booking, and let providers reply to them." - -In a workspace that's already wired to a Form.io project, the agent plans only what's new and adds it without touching what works. Existing project content stays intact. - -### Plan a data model from plain language - -> "Plan the resource structure for a multi-tenant booking system where customers book services from providers, providers belong to one or more locations, and admins manage everything." - -The agent runs a structured interview, produces an ER diagram and access matrix for review, and emits a project template ready to import. You see the architectural shape before any code or schema gets written. - -### Tighten access controls on an existing resource - -> "Currently anyone authenticated can read every Account. Lock it down so reps only see Accounts owned by their Team." - -The agent reads the resource, modifies the access rules, and ensures the supporting role assignments are in place. The diff lands for your review before anything ships. - -### Inspect and operate a live project - -> "List every form in this project that doesn't have a Save Submission action attached." - -The agent queries the live project through the MCP server, surfaces what's missing, and offers to fix it. This is operational work — not building from scratch, but keeping a running system clean. - ---- - -## Quickstart — Claude Code plugin - -The fastest way to get started. From inside Claude Code: - -```text -/plugin marketplace add https://github.com/formio/ai.git -/plugin install formio-ai@formio -``` - -Claude Code prompts for `FORMIO_BASE_URL` and `FORMIO_PROJECT_URL` on install. Then describe what you want: - -> "/formio-application Build me a task manager where each project has its own team of users, and users only see tasks inside projects they belong to." - -The `formio-application` skill activates, runs the planner, captures URLs, imports the template, and hands off to `formio-angular` to scaffold the Angular front-end. Each step has an approval gate. +## Agentic Skill Library -To extend an existing app, just describe the new feature in the same workspace: +The plugin ships an activatable skill library. Claude loads the relevant skill on demand based on what you ask — you rarely need to name one explicitly. -> "Also let team members add comments to each task." +### Orchestration skills -The same skill switches to **modify-existing** mode — plans only the new resources (delta `template.json`), additively imports them, and hands off to the Angular extend sub-skill to scaffold modules for exactly the new resources. +Orchestration skills are special skills that serve as the **entry point** for most prompts. Rather than covering a single capability, they coordinate the other skills (planning, schema authoring, actions, deployment, framework scaffolding) to fulfill a broad, plain-language request end to end. When you describe *what you want* instead of *which tool to use*, an orchestration skill picks it up and drives the whole pipeline. ---- +| Skill | What it does | +| --- | --- | +| `formio-application` | Framework-agnostic "build me an app" orchestrator. Turns plain-language intent into a running application backed by a Form.io project — planning resources, importing the template, and handing off to a framework implementor. Also handles adding new features to an existing app. | +| `form-builder` *(coming soon)* | Orchestrates any "form building" prompt — complex forms, multi-page conditional wizards, and schema-compliant form definitions (e.g. FHIR) — from intent through creation in your deployment. | -## Quickstart — standalone MCP server +### All skills -The MCP server (`@formio/mcp`) is independently usable from any MCP-aware client. From a clone of this repo: +| Skill | What it does | +| --- | --- | +| `formio-application` | Orchestration entry point for building or extending an application on the Form.io platform (see above). | +| `formio-resource-planner` | Plans the resource structure, field configuration, and access/permission model from high-level requirements, then emits a ready-to-import `template.json`. | +| `formio-schema` | Reference for Form.io JSON schema — the document shapes for projects, forms/resources, and submissions. Used when constructing, editing, or interpreting any Form.io JSON. | +| `formio-actions` | Reference for configuring Form.io actions — the server-side behavior layer for email notifications, authentication, webhooks, role assignment, and form-to-form saves. | +| `formio-auth` | Authentication and authorization specialist — login/registration, RBAC, SSO (OIDC/SAML/LDAP), Token Swap, Custom JWT, passwordless email tokens, and JWT/session mechanics. | +| `formio-api` | Comprehensive Form.io REST API reference — every endpoint across platform admin, project admin, runtime, and PDF scopes. | +| `formio-sdk` | Reference for the `@formio/js` JavaScript SDK and `@formio/js/utils` Utilities — static and instance methods, VanillaJS rendering, plugins, and helpers. | +| `formio-angular` | Angular framework implementor. Turns an approved `template.json` plus a target project into a working Angular app using `@formio/angular`. Delegated to by `formio-application`. | + +## Examples and Use Cases +Once this is installed, you are ready to do the following: + + - [Build an Enterprise 'greenfield' form-based workflow application from a single prompt](#build-an-enterprise-greenfield-form-based-workdlow-application-from-a-single-prompt) + - [Introduce new Form.io features within existing applications](#introduce-new-formio-features-within-existing-applications) + - [Prompt your agent to build conditional forms and wizards with complex validations](#prompt-your-agent-to-build-conditional-forms-and-wizards-with-complex-validations) + - [Build forms using descriptions of data models and integration interfaces](#build-forms-using-descriptions-of-data-models-and-integration-interfaces) + - [Create new Resource / Model structures using the REST API's within your Form.io project via prompt](#create-new-resource--model-structures-using-the-rest-apis-within-your-formio-project-via-prompt) + - [Embed a simple form.io form within a bespoke application](#embed-a-simple-formio-form-within-a-bespoke-application) + +## Build an Enterprise 'greenfield' form-based workdlow application from a single prompt. +With this plugin, you can create a brand-new 'greenfield' form-based [application](#this-library-currently-only-supports-angular-other-frameworks-are-coming-soon) using the `formio-application` skill. To get started, simply create a new folder within your computer and then start claude code within that folder as follows: ```bash -pnpm install -pnpm --filter @formio/mcp dev -``` - -The server starts on port 3000. Override with the `PORT` env var. - -### Transports - -| Transport | Endpoint | Compatible with | -| --- | --- | --- | -| Streamable HTTP | `POST /mcp` | Claude Code, VS Copilot, modern MCP clients | -| SSE | `GET /sse` + `POST /messages` | Claude Desktop, legacy MCP clients | -| stdio | `node dist/stdio.js` | `.mcp.json` spawn-mode clients | - -### Connect to Claude Code - -```json -{ - "mcpServers": { - "formio-mcp": { - "type": "http", - "url": "http://localhost:3000/mcp" - } - } -} -``` - -### Connect to Claude Desktop - -`claude_desktop_config.json`: - -```json -{ - "mcpServers": { - "formio-mcp": { - "url": "http://localhost:3000/sse" - } - } -} -``` - -### Spawn via `.mcp.json` (stdio) - -```json -{ - "mcpServers": { - "formio-mcp": { - "command": "npx", - "args": ["-y", "@formio/mcp"], - "env": { - "FORMIO_BASE_URL": "https://api.form.io", - "FORMIO_PROJECT_URL": "https://your-project.form.io" - } - } - } -} +mkdir form-app +cd form-app +claude ``` -In standalone (non-plugin) mode, `FORMIO_BASE_URL` and `FORMIO_PROJECT_URL` are required env vars. In plugin mode, the plugin manages both via Claude Code's user-config + per-cwd `~/.formio/projects.json` mapping. +From this prompt, you can then perform the following to create a new application, like the following: ---- - -## How it works - -Three skills do the heavy lifting on a build: the orchestrator (`formio-application`) routes work, the planner (`formio-resource-planner`) designs the data model, and the framework implementor (`formio-angular`) scaffolds the front-end. Four more skills are pulled in on demand — `formio-auth` (authentication & authorization), `formio-schema` (project / form / submission JSON), `formio-actions` (server-side behavior), `formio-sdk` (the `@formio/js` runtime), and `formio-api` (raw REST) — whenever the build or a direct question touches their domain. - -### Skill routing map - -Claude activates the skill whose `description` matches the request. Build-an-app intent enters through `formio-application`, which delegates to the planner, hands off to `formio-auth` when the data model needs auth beyond default roles, and routes to `formio-angular` for the front-end. The reference skills (`formio-schema`, `formio-actions`, `formio-sdk`, `formio-api`) activate directly on a matching question, or are consulted mid-build. - -```mermaid -flowchart TD - User([User request]) --> Router{Claude routes
by skill description} - - Router -->|""build / extend an app""| App[formio-application
orchestrator] - Router -->|""plan / model the data""| Planner[formio-resource-planner] - Router -->|""login, SSO, RBAC, JWT""| Auth[formio-auth] - Router -->|""Angular front-end""| Ng[formio-angular] - Router -->|""form / submission / project JSON""| Schema[formio-schema] - Router -->|""email / webhook / login action""| Actions[formio-actions] - Router -->|""Formio.* / Utils.* in JS""| Sdk[formio-sdk] - Router -->|""call a REST endpoint""| Api[formio-api] - - App -->|Step 2: Plan| Planner - Planner -.->|"non-default auth
(SSO / Custom JWT / groups)"| Auth - App -->|Step 6: Framework| Ng - Ng -.->|per-resource modules| NgRes[formio-angular/resources] - - Planner -.->|consults| Schema - Planner -.->|emits| Actions - Ng -.->|runtime calls| Sdk - App -.->|MCP tools| MCP[("@formio/mcp
form_* role_* action_* project_*")] - Ng -.->|MCP tools| MCP - - style Router fill:#67b346,color:#fff - style App fill:#e8f5e4 - style Planner fill:#e8f5e4 - style Auth fill:#fff4e0 - style Ng fill:#e8f5e4 - style MCP fill:#fff4e0 +```bash +/formio-application I would like to create a story board application for my Sales Team to keep track of the sales status of leads. This application should allow each user create their own story board, and with those story boards create custom swim lanes, and then within those swim lanes create new "Leads". These Leads can then be dragged-and-dropped between the swim lanes to keep track of the status of the lead. Each user should then be able to create Teams, and then add new users to that team, and then add that team to the story board where that user would then have read/write access to the story board they have been added to. ``` -### The `formio-application` flow - -`formio-application` is the default "build me an app" entry point. It runs a six-step orchestration with approval gates at every write. **Build-new** runs all six. **Modify-existing** skips Deployment + MCP Config (URLs already known) but still plans (delta) and still imports (additive). - -```mermaid -flowchart TD - User([User intent:
"build me a CRM" OR "also track attendees"]) --> Intent{Step 1: Intent
build-new or modify-existing?} - - Intent -->|build-new| PlanFull[Step 2: Plan
formio-resource-planner
full-project template.json] - Intent -->|modify-existing| PlanDelta[Step 2: Plan
formio-resource-planner
delta template.json
only new resources] - - PlanFull --> Deploy[Step 3: Deployment
capture FORMIO_BASE_URL
+ FORMIO_PROJECT_URL] - Deploy --> MCPConfig[Step 4: MCP Config
write ./.mcp.json
approval gate] - MCPConfig --> Restart[/Halt — user restarts
Claude Code or /mcp reconnect/] - Restart --> ImportFull[Step 5: Import
project_import full template
first call triggers portal login] - - PlanDelta --> ReadURLs[Steps 3 + 4 SKIPPED
read URLs from workspace
FormioAppConfig] - ReadURLs --> ImportDelta[Step 5: Import
project_import delta template
additive merge into existing project] - - ImportFull --> FrameworkNew[Step 6: Framework routing
scaffold new app] - ImportDelta --> FrameworkExtend[Step 6: Framework routing
extend sub-skill
scaffolds only new resource modules] - - FrameworkNew --> Angular[formio-angular] - FrameworkExtend --> AngularResources[formio-angular/resources] - - Angular --> App([Running app]) - AngularResources --> App +The plugin will then use the `formio-application` skill as the orchestration skill to create this new application using the Form.io platform as the composable backend for the full application logic. - style Intent fill:#67b346,color:#fff - style PlanFull fill:#e8f5e4 - style PlanDelta fill:#e8f5e4 - style MCPConfig fill:#fff4e0 - style ImportFull fill:#fff4e0 - style ImportDelta fill:#fff4e0 - style FrameworkNew fill:#e8f5e4 - style FrameworkExtend fill:#e8f5e4 - style Angular fill:#e8f5e4 - style AngularResources fill:#e8f5e4 - style App fill:#67b346,color:#fff -``` +## Introduce new Form.io features within existing applications. +In addition to being able to create new 'greenfield' [applications](#this-library-currently-only-supports-angular-other-frameworks-are-coming-soon), it is also possible to use this plugin to introduce new complex form-based workflow features within existing [applications](#this-library-currently-only-supports-angular-other-frameworks-are-coming-soon). This can also be achieved using the `formio-application` skill as follows. -| Step | Build-new | Modify-existing | -| --- | --- | --- | -| 1. Intent | Identify build-new | Identify modify-existing | -| 2. Plan | Full-project `template.md` + `template.json` | Delta — only new resources/fields/actions | -| 3. Deployment | Capture URLs (one batched `AskUserQuestion`) | Skipped — read URLs from workspace `FormioAppConfig` | -| 4. MCP Config | Write `./.mcp.json`, halt for restart | Skipped — `.mcp.json` already in place | -| 5. Import | Additive `project_import` (first call triggers portal login) | Additive `project_import` of delta | -| 6. Framework | Scaffold full app (Angular today) | Hand list of new resources to extend sub-skill | - -Import is **additive**: existing project content is preserved; only matching machine-names are overwritten. Authentication is implicit — the first authenticated MCP tool call triggers the browser portal-login flow on a cache miss. - -### The `formio-resource-planner` two-phase flow - -The planner is interview-driven and emits two artifacts in lockstep — `template.md` (architectural intent, ER + Access Flow diagrams) and `template.json` (the structured Form.io export, every resource + role + form + action). Phase A always blocks on user approval before Phase B writes any file. - -```mermaid -flowchart TD - Req([User requirement:
"Task manager — projects, tasks, teams of users"]) --> Entities[Round 1
extract named entities] - Entities --> Rels[Round 2
determine relationships
1:1 / 1:N / N:N] - Rels --> Auth[Round 3
user / auth model
built-in user vs custom] - Auth --> Access[Round 4
access model
owner / group / role / tenant] - - Access --> PhaseA[Phase A
Resource Map
ASCII ER + Access Flow] - PhaseA --> Gate{User
approves?} - Gate -->|revise| Rels - Gate -->|approve| PhaseB[Phase B
Write template.md +
template.json paired set
Mermaid diagrams] - PhaseB --> Done([Ready for project_import]) - - style PhaseA fill:#fff4e0 - style Gate fill:#67b346,color:#fff - style PhaseB fill:#e8f5e4 - style Done fill:#67b346,color:#fff +```bash +/formio-application Within this EMR portal application, I would like to add the ability for each Clinic to create their own Patient Onboarding forms within the Form.io Platform portal, and then embed those patient onboarding forms within their own clinic websites. When a potential patient fills out the patient onboarding form, I would like to introduce a new section within this EMR application called "Patient Onboarding" that displays a table view of all the new patient applications (forms). The clinic administrator should be able to click on each patient onboarding submission, call that patient, and through their own phone call convert that into a scheduled patient appointment within the EMR portal." ``` -Every emitted `template.json` includes a top-level `actions` map with at minimum a `Save Submission` action per resource (omitting it produces a project where forms accept submissions but never store them — the planner treats that as a hard failure). - -### The planner ↔ `formio-auth` handoff +From this prompt, the `formio-application` should understand that this is a new 'form-based' feature that is being added within an exsting [application](#this-library-currently-only-supports-angular-other-frameworks-are-coming-soon), and then initiate the development process for introducing this new feature using the Form.io platform as the underlying platform for such feature. -The contract between the two auth-aware skills is explicit. `formio-resource-planner` owns the **data model** — roles, the `user` resource, login/registration forms, group joins — and emits the canonical `template.json` shapes for the Login, Role Assignment, and Group Assignment actions plus the `access` / `submissionAccess` arrays. `formio-auth` owns the **auth configuration that runs on top of that model** — SSO (OIDC / OAuth / SAML / LDAP) with provider role mapping, Token Swap, Custom JWT signed with `JWT_SECRET`, passwordless email-token auth, and JWT / session / 2FA / reCAPTCHA mechanics. Action JSON shapes are never duplicated; `formio-auth` references the planner's `references/template-json.md` by path. +## Prompt your agent to build conditional forms and wizards with complex validations +Using the `form-builder` skill, you can easily prompt your agent to create complex forms, as well as multi-page conditional wizards. Not only will the agent create the JSON for this form, but it will also automatically create the form within any stage of your deployment. -```mermaid -flowchart TD - Map[Resource Map approved
Users & Auth section] --> Q{Auth beyond
resource login + Role + Group?} - Q -->|no| Planner[formio-resource-planner
emits template.json
Login / Role / Group actions] - Q -->|"SSO ≠ none, Custom JWT,
Token Swap, passwordless, 2FA"| Auth[formio-auth
configures auth layer
on top of the model] - Planner --> Import([project_import]) - Auth -.->|references template-json.md| Planner - Auth --> Import - - style Q fill:#67b346,color:#fff - style Auth fill:#fff4e0 - style Planner fill:#e8f5e4 - style Import fill:#67b346,color:#fff +```bash +/form-builder I would like to create a new multi-page wizard form that is responsible for collecting college applications, which we will embed within our content management system. This wizard should have multiple sections where it first collects the applicants personal information, followed by scholastic achievements, followed by extra curricular activities. It should follow up with them selecting their desired program, and based on what they select, the following wizard pages should contain the specific onboarding questions for that program. ``` -Hand off to `formio-auth` immediately after the Resource Map is approved whenever its `Users & Auth` section emits a non-`none` `SSO` field, a `Custom JWT: yes`, or any auth concern beyond resource-backed login plus Role and Group Assignment. - -### The `formio-angular` five-phase flow - -Invoked either as a handoff from `formio-application` (URLs and `template.json` already in hand) or directly when the user names Angular explicitly. Each phase has its own approval gate. - -```mermaid -flowchart TD - Entry{Invoked by} -->|formio-application handoff| Handoff[SETUP confirms handoff context
URLs + template paths already known] - Entry -->|user names Angular directly| Direct[SETUP runs full URL interview] - - Handoff --> Bootstrap - Direct --> Bootstrap - - Bootstrap{Phase 2
BOOTSTRAP
angular.json present?} - Bootstrap -->|no| Install[Install angular/skills lib
delegate to angular-new-app
install @formio/angular + @formio/js
Bootstrap 5 + zone.js] - Bootstrap -->|yes| Config +## Build forms using descriptions of data models and integration interfaces +The `form-builder` skill is also capable of enabling an Agent to produce form definitions that are capable of producing submission data structures that adhere to well defined schemas. This is very useful when a form is needed to produce a data object that meets a very specific structure, such as when that data is to be used against an already defined integration or standard. For example, you can use this skill to produce forms that are [FHIR compliant](https://hl7.org/fhir). - Install --> Config[Phase 3
CONFIG
generate src/app/config.ts
FormioAppConfig] - Config --> AuthPhase[Phase 4
AUTH
AuthModule + login/register
route guards on protected routes
login-success notification] - AuthPhase --> Resources[Phase 5
Resources sub-skill
per-resource NgModule
FormioResourceConfig + guarded Routes] - Resources --> Done([Running Angular app]) - - style Bootstrap fill:#67b346,color:#fff - style Config fill:#e8f5e4 - style AuthPhase fill:#e8f5e4 - style Resources fill:#e8f5e4 - style Done fill:#67b346,color:#fff +```bash +/form-builder I am building a new patient onboarding application that must adhere to the FHIR Patient resource definitions described at https://build.fhir.org/patient.html. Can you create a form that will onboard a new patient and the data that it produces generate FHIR compliant data for Patients? ``` -Generated workspaces use NgModule-based components (`standalone: false`) to match the official `@formio/angular` demo, with Bootstrap 5 + Bootstrap Icons wired via `angular.json`. The AUTH phase wires a `FormioAuthService`-backed login/register experience and attaches route guards so protected resource routes redirect unauthenticated users to login (with a login-success notification on return). UI-touching files consult Claude's built-in `frontend-design` skill before authoring templates or styles. - ---- - -## Sample resource maps - -These are real outputs from `formio-resource-planner`'s `references/examples/`. Both ship with paired `template.md` and `template.json` artifacts under [`plugin/skills/formio-resource-planner/references/examples/`](./plugin/skills/formio-resource-planner/references/examples/). - -### Task manager (group-via-join) - -A multi-user task manager where each Project has a team of Users and users only see Tasks inside Projects they belong to. Demonstrates **group permissions via a join resource** (`ProjectUser`) and **field-based ACL inheritance** (`Task.project`). - -```mermaid -erDiagram - User ||--o{ ProjectUser : "member of" - Project ||--o{ ProjectUser : "has members" - Project ||--o{ Task : "contains" - - User { - string email "login identifier (unique)" - string password "login secret" - } - Project { - string name "required" - } - Task { - string description - select project "ref=Project, field-based access" - } - ProjectUser { - select project "ref=Project" - select user "ref=User" - action GroupAssignment "group=project, user=user" - } -``` +## Create new Resource / Model structures using the REST API's within your Form.io project via prompt +The `formio-api` skill is fully aware how to interface with your Form.io Enterprise Deployment via REST API's. This provides the ability to create any Resource, Form, Submission, or any other entity using the REST API's of that deployment. -```mermaid -flowchart TD - Admin[[administrator]] --> All[[every resource]] - Auth[[authenticated]] -->|"membership row"| PU[/ProjectUser/] - PU -->|"Group Assignment
group=project
user=user"| P[Project] - P -->|"field-based submissionAccess
on Task.project"| T[Task] - Auth -->|"Submission Access: read_own, update_own"| U[User] +```bash +/formio-api I would like to create a new Patient resource within my Form.io deployment. ``` -| Resource | Actor | create | read | update | delete | Notes | -| --- | --- | :-: | :-: | :-: | :-: | --- | -| Project | administrator | all | all | all | all | full admin | -| Project | authenticated | — | group | group | — | group-via-ProjectUser | -| Task | administrator | all | all | all | all | | -| Task | authenticated | group | group | group | — | inherits via `Task.project` | -| ProjectUser | administrator | all | all | all | all | admin-managed membership | -| ProjectUser | authenticated | — | own | — | — | user sees their own memberships | -| User | administrator | all | all | all | all | | -| User | authenticated | — | own | own | — | owner-level on own record | - -### CRM with transitive group access - -A multi-team CRM where the group (`Team`) sits **two levels above** the bottom of the hierarchy. The direct child (`Account`) uses a normal group-reference select; grandchildren (`Contact`, `Deal`, `Activity`) use a hidden, calculated `team` mirror field to propagate the ACL one more hop. - -```mermaid -erDiagram - User ||--o{ TeamUser : "member of" - Team ||--o{ TeamUser : "has members" - Team ||--o{ Account : "owns (direct group)" - Account ||--o{ Contact : "has" - Account ||--o{ Deal : "has" - Account ||--o{ Activity : "has" - Deal ||--o{ Activity : "optional back-ref" - - Team { string name "required" } - TeamUser { - select team "ref=Team" - select user "ref=User" - action GroupAssignment "group=team, user=user" - } - Account { - string name "required" - select team "ref=Team, field-based access (direct group)" - } - Contact { - string firstName - select account "ref=Account" - select team "HIDDEN calculated mirror: data.account.data.team" - } - Deal { - string title - select account "ref=Account" - select team "HIDDEN calculated mirror" - } - Activity { - string subject - select account "ref=Account" - select team "HIDDEN calculated mirror" - } -``` +## Embed a simple form.io form within a bespoke application. +In addition to building new 'greenfield' applications, the `formio-application` skill is also capable of embedding a form or wizard within any HTML-based application. -```mermaid -flowchart TD - Admin[[administrator]] --> All[[every resource]] - SR[[salesRep]] -->|"Role Assignment on userRegister"| SR - SR -->|"membership row (one per Team)"| TU[/TeamUser/] - TU -->|"Group Assignment
group=team
user=user"| Team[Team] - Team -->|"field-based submissionAccess
on Account.team"| Account[Account] - Account -->|"hidden calculated mirror
value = data.account.data.team"| Contact[Contact] - Account -->|"hidden calculated mirror"| Deal[Deal] - Account -->|"hidden calculated mirror"| Activity[Activity] +```bash +/formio-application I would like to create and embed a multi-page student onbaording wizard within my application. ``` -Runtime propagation: - -1. User self-registers on `userRegister` → Save writes into `user` resource → Role Assignment grants `salesRep` → Login issues JWT. -2. Admin creates a `TeamUser(team, user)` row → Group Assignment writes an ACL entry onto the Team submission. -3. `Account.team` carries field-based `submissionAccess` (4 entries, empty roles) → Form.io propagates the Team's ACL onto each Account whose `team` matches. -4. `Contact` / `Deal` / `Activity` each carry a hidden `team` select with `calculateValue: value = data.account.data.team;`, `refreshOn: account`, `hidden: true`, plus the same field-based `submissionAccess` block — propagating the ACL one more hop on every save. - ---- - -## Skills library - -Eight activatable skills under [`plugin/skills/`](./plugin/skills/). Claude routes between them based on what the user asked for (see the [skill routing map](#skill-routing-map)). - -### Orchestration & framework skills - -| Skill | Purpose | -| --- | --- | -| [`formio-application`](./plugin/skills/formio-application/) | Default "build me an app" orchestrator. Six-step pipeline: Intent → Plan → Deployment → MCP Config → Import → Framework routing. Build-new runs all six; modify-existing runs Plan (delta) + Import (additive) + Framework (extend sub-skill), skipping only Deployment + MCP Config. | -| [`formio-resource-planner`](./plugin/skills/formio-resource-planner/) | Turns plain-language requirements into a Form.io project `template.md` + `template.json` pair — resources, fields, roles, actions, access model. Two-phase: Resource Map (review) → paired artifacts (after explicit approval). Encodes when to model something as a resource vs a plain form, and the access-model anti-patterns to avoid. Hands non-default auth off to `formio-auth`. | -| [`formio-angular`](./plugin/skills/formio-angular/) | Angular framework implementor. Five-phase scaffold flow over `@formio/angular` (NgModule-based, `standalone: false`, Bootstrap 5). Sub-skill at [`resources/`](./plugin/skills/formio-angular/resources/) handles per-resource NgModule generation and modify-existing extension. | - -### Schema, action & SDK skills - -| Skill | Purpose | -| --- | --- | -| [`formio-schema`](./plugin/skills/formio-schema/) | Comprehensive JSON schema reference for **projects, forms (and resources), and submissions** — component types, validation, conditional logic, project settings/integrations, submission `data`/`access`/`metadata`/state. Consulted when reading, writing, or editing any Form.io JSON. (Absorbs the former `formio-form` skill.) | -| [`formio-actions`](./plugin/skills/formio-actions/) | Deep reference for configuring Form.io server-side actions — email, login, webhook, role assignment, save, reset password — including handler/method, priorities, and conditions. | -| [`formio-auth`](./plugin/skills/formio-auth/) | Authentication & authorization specialist — resource-backed login (Login + Role Assignment actions), the eight RBAC permission types, group permissions (single-level + transitive), SSO via OIDC/OAuth/SAML/LDAP with role mapping, Token Swap, Custom JWT (`JWT_SECRET`), passwordless email-token auth, and JWT/session/2FA/reCAPTCHA mechanics. Peer to the planner; owns the auth layer the planner's data model runs on. | -| [`formio-sdk`](./plugin/skills/formio-sdk/) | Source-derived reference for the `@formio/js` SDK, `@formio/js/utils` Utilities, and `@formio/core` helpers — static & instance `Formio.*` methods, VanillaJS rendering (`Formio.createForm`, `Formio.builder`), the plugin lifecycle, and the `Utils` surface (Evaluator, traversal, conditions, JSONLogic, mask/sanitize). Mandates ESM imports. | - -### API skill + reference library - -| Skill | Purpose | -| --- | --- | -| [`formio-api`](./plugin/skills/formio-api/) | Single activatable skill covering every endpoint in the [Form.io API Postman collection](https://documenter.getpostman.com/view/684631/2sBXiok9LB) — platform admin, project admin, runtime/end-user, PDF. Activates on any Form.io REST question and navigates into the relevant reference doc. | - -The endpoint detail lives under [`plugin/skills/formio-api/references/`](./plugin/skills/formio-api/references/) — one Markdown file per capability group (no frontmatter, not independently activatable, read via links from the router). The groups: - -- **Platform scope** (`${FORMIO_BASE_URL}/`): `platform-auth`, `platform-projects`, `platform-teams`, `platform-staging`, `platform-tenants`, `server-status` -- **Project scope** (`${FORMIO_PROJECT_URL}/`): `project-auth`, `project-roles`, `project-forms`, `project-form-revisions`, `project-actions` -- **Runtime scope** (`${FORMIO_PROJECT_URL}/`): `runtime-auth`, `runtime-custom-users`, `runtime-access-control`, `runtime-reports`, `runtime-submissions` -- **PDF scope** (`${FORMIO_PROJECT_URL}/pdf-proxy/`): `pdf-api` - -Two automated checks guard the skill library (both also run under the repo-wide `turbo run test`): - -- **Plugin bundle structure** — `pnpm --filter @formio/mcp test` runs [`packages/mcp-server/src/__tests__/plugin-build.test.ts`](./packages/mcp-server/src/__tests__/plugin-build.test.ts), which asserts the right skills ship in `dist/plugin/skills/` (e.g. `formio-api` and `formio-schema` present, the retired `formio-form` and the `openspec-*` / `tdd-*` skills excluded) and that `plugin.json` and the bundled stdio server are well-formed. (Run `pnpm build:plugin` first to populate `dist/plugin/`.) -- **SDK example execution** — `pnpm --filter @formio/skill-tests test` runs the [`@formio/skill-tests`](./packages/skill-tests/) package, which executes the code examples shipped in `formio-sdk`'s reference docs against the real `@formio/js` / `@formio/core` at runtime, so a doc that drifts from the SDK's actual export shape fails a test with a pointer to the bad example. - -Terminology in the skills is strict (a convention, not test-enforced): `baseUrl`/`base_url` always means `FORMIO_BASE_URL`; `projectUrl`/`project_url` always means `FORMIO_PROJECT_URL`. - ---- +#### ***This library currently only supports Angular application framework for new 'greenfield' applications. Other frameworks are coming soon*** ## MCP server tools @@ -615,89 +212,6 @@ The probe runs lazily — only when the local auth page is actually served. --- -## Development - -This is a [pnpm](https://pnpm.io/) + [Turborepo](https://turbo.build/) monorepo. - -### Prerequisites - -- Node.js >= 20 -- pnpm 10.x - -### Common commands - -```bash -pnpm install # install workspace deps -pnpm build # build all packages -pnpm dev # run all packages in watch mode -pnpm test # run all tests (Vitest across @formio/mcp + @formio/skill-tests + plugin build/smoke) -pnpm lint # type-check all packages -pnpm format # prettier --write . -pnpm clean # clean build artifacts -pnpm build:plugin # bundle the plugin into dist/plugin/ -pnpm test:plugin # run plugin smoke tests -``` - -### MCP server only - -```bash -pnpm --filter @formio/mcp dev # start with hot reload (port 3000) -pnpm --filter @formio/mcp test # run tests once -pnpm --filter @formio/mcp test:watch # run tests in watch mode -pnpm --filter @formio/mcp build # compile to dist/ -``` - -### Skill SDK tests - -```bash -pnpm --filter @formio/skill-tests test # run formio-sdk doc examples against the real @formio/js -``` - -[`@formio/skill-tests`](./packages/skill-tests/) executes the code examples in `formio-sdk`'s reference docs against the live SDK — a failing test means a reference doc has drifted from the SDK's actual surface. See [`packages/skill-tests/README.md`](./packages/skill-tests/README.md). - -### Definition of Done - -Work is not complete until all of the following pass: - -- **Tests** — `pnpm test` -- **Type-check** — `pnpm lint` -- **Formatting** — `pnpm format` - -TDD is mandatory. Write failing tests first, then implement the minimum code to make them pass. - -### Local plugin testing - -See [`examples/basic-app/README.md`](./examples/basic-app/README.md) for the full local-plugin loop — building the plugin, registering the local marketplace, and exercising it from inside Claude Code without publishing to npm. - -### Iterating on skills - -Some skills (`formio-resource-planner`, `formio-angular`) ship their own eval harness under `evals/`. The standard layout: - -- `skills//evals/evals.json` — test prompts + expected outputs -- `skills//evals/grade.py` — structural assertions; reads `.eval-artifacts//iteration-N/` and writes `grading.json` -- `skills//evals/README.md` — teammate-facing runbook (seed fixtures → spawn subagents → grade → aggregate) - -When iterating on, testing, or measuring a skill change, **read that skill's `evals/README.md` first** — don't improvise. The standard loop is the same across skills, but each README documents skill-specific add-ons. - ---- - -## Spec-driven development with OpenSpec - -This project uses [OpenSpec](https://openspec.dev/) for spec-driven development. Specs live in `openspec/specs/` organized by capability; change proposals are generated in `openspec/changes/`. - -```bash -npm install -g @fission-ai/openspec@latest -``` - -Workflow: - -1. **Explore** — read existing specs in `openspec/specs/` for context. -2. **Propose** — `/openspec:propose` plans a change with design rationale, specs, and tasks before implementing. -3. **Implement** — `/openspec:apply` works through the generated tasks. -4. **Archive** — `/openspec:archive` finalizes a completed change. - ---- - ## License [MIT](./LICENSE) diff --git a/plugin/skills/formio-application/SKILL.md b/plugin/skills/formio-application/SKILL.md index 282b73d..037f103 100644 --- a/plugin/skills/formio-application/SKILL.md +++ b/plugin/skills/formio-application/SKILL.md @@ -1,57 +1,19 @@ --- name: formio-application description: >- - Default "build me an app" skill for this library — framework-agnostic orchestrator - that turns plain-language intent into a running application backed by a Form.io project. - Use this skill whenever the user wants to build, create, spin up, scaffold, or stand up - an app, tool, system, portal, dashboard, tracker, or workflow around data, OR when the - user wants to extend an already-running app with a new feature — in plain language that - does NOT require the user to know anything about Form.io, Angular, React, resources, - NgModules, CRUD, or any framework terminology. - Example build-new triggers this skill claims include "build me an app to track - maintenance requests", "create a CRM for my consulting practice", "I need a tool to - track who signed up for each event", "spin up a help desk", and bare domain archetypes - ("task manager", "help desk", "CRM", "booking system", "member directory", "issue - tracker", "order management"). - Example extend triggers this skill claims include "also track attendees for each event", - "add a way to see line items for an order", "each project should have a list of - milestones", "let customers leave reviews on products", "let admins tag users". - Internally runs a six-step orchestration with approval gates — - (1) Intent — ask whether this is a new app or an existing app to extend; - (2) Plan — invoke `formio-resource-planner` to produce a `template.json` (full project - for build-new; additive delta containing ONLY the new resources, fields, or actions for - modify-existing); - (3) Deployment — in plugin mode, capture only the Form.io Project URL via one - `AskUserQuestion` (default offered from `FORMIO_DEFAULT_PROJECT_URL` when the - verify-project-url hook surfaces one) and persist via `project_set({ cwd, projectUrl })` - so future sessions in the same cwd skip the prompt — the Base URL comes from the - plugin's MCP server env (set by the user's plugin configuration), not from this - skill; in no-plugin mode, fall back to the batched Base URL + Project URL interview - with plain-language descriptions of each (build-new only; modify-existing reads both - URLs from the existing workspace's `FormioAppConfig`); - (4) MCP Config — skipped when the `@formio/ai` plugin provides the MCP server - (detected via `mcp__plugin_formio-ai_formio-mcp__*` tools or the verify-project-url - hook), since `project_set` already routes per-cwd via `~/.formio/projects.json`; - otherwise write (or merge into) `.mcp.json` in the workspace root with captured URLs - under `FORMIO_PROJECT_URL` and `FORMIO_BASE_URL`, then halt the invocation so the user - can restart Claude Code (or run `/mcp` to reconnect) — Claude Code only reads - `.mcp.json` at session start, so the flow must pause across one restart boundary - (build-new only; modify-existing keeps its existing `.mcp.json`); - (5) Import — call the `project_import` MCP tool to additively merge the - planner-generated `template.json` into the target Form.io project, with an explicit - approval gate citing the URLs and merge-overwrite warning (runs on BOTH branches — - import is additive, so adding new resources to an existing project is safe); - (6) Framework — consult the library's framework registry and route to the right - framework-specific skill (build-new: scaffold a new app; modify-existing: hand off to - the framework's extend sub-skill with the new resources). - Authentication is implicit — the first authenticated MCP tool call triggers the - portal-login flow in a browser on a cache miss and caches the JWT for subsequent calls. - Not for: framework-explicit Angular requests (the user names Angular or @formio/angular - verbatim) — see `formio-angular` for initial Angular scaffolding and - `formio-angular-resources` for Angular-explicit feature additions. - Not for: planning a data model without building an app around it — see - `formio-resource-planner`. - Not for: looking up Form.io REST endpoints — see the `formio-api` skill. + Default "build me an app" orchestrator. Either builds a new "greenfield" application + backed by a Form.io project from a plain-language idea, OR jumps into an existing + application to add a new feature — the two are independent entry paths, not sequential + steps (extending an app never requires building one first). + Use whenever the user wants to build, create, spin up, scaffold, or stand up an app, tool, + system, portal, dashboard, tracker, or workflow around data — or extend an existing app + with a new feature — without naming a UI framework. Example triggers: "build me an app to + track maintenance requests", "create a CRM for my consulting practice", "spin up a help + desk", bare archetypes ("task manager", "booking system", "issue tracker"), and extensions + like "also track attendees for each event" or "let customers leave reviews on products". + Not for: framework-explicit requests that name Angular or @formio/angular (see + `formio-angular`); planning a data model without building an app around it (see + `formio-resource-planner`); Form.io REST endpoint lookups (see `formio-api`). --- # Form.io Application Orchestrator From 96423d57c87f1818e9e5fc511e31a3a9fabfcfaa Mon Sep 17 00:00:00 2001 From: Travis Tidwell Date: Mon, 13 Jul 2026 10:09:39 -0500 Subject: [PATCH 2/7] Adding a formio-form skill to help with embedding forms using the core renderer embedding strategies. --- .claude/skills/formio-form | 1 + .../add-formio-form-skill/.openspec.yaml | 2 + .../changes/add-formio-form-skill/design.md | 100 +++++++ .../changes/add-formio-form-skill/proposal.md | 47 ++++ .../specs/claude-plugin-packaging/spec.md | 34 +++ .../specs/formio-angular-skill/spec.md | 44 ++++ .../specs/formio-application-skill/spec.md | 55 ++++ .../specs/formio-form-skill/spec.md | 246 ++++++++++++++++++ .../specs/formio-schema-skill/spec.md | 25 ++ .../specs/formio-sdk-skill/spec.md | 30 +++ .../changes/add-formio-form-skill/tasks.md | 122 +++++++++ .../src/__tests__/plugin-build.test.ts | 7 +- .../src/formio-form/calculated-values.test.ts | 41 +++ .../src/formio-form/conditionals.test.ts | 55 ++++ .../src/formio-form/external-data.test.ts | 52 ++++ .../src/formio-form/field-logic.test.ts | 36 +++ .../src/formio-form/fixtures/logic.ts | 137 ++++++++++ .../src/formio-form/fixtures/rendering.ts | 34 +++ .../formio-form/fixtures/wizard-external.ts | 69 +++++ .../src/formio-form/rendering.test.ts | 82 ++++++ .../src/formio-form/skill-structure.test.ts | 180 +++++++++++++ .../src/formio-form/validation.test.ts | 43 +++ .../src/formio-form/wizards.test.ts | 53 ++++ plugin/skills/formio-angular/SKILL.md | 2 +- plugin/skills/formio-application/SKILL.md | 5 +- plugin/skills/formio-form/SKILL.md | 73 ++++++ .../references/calculated-values.md | 62 +++++ .../formio-form/references/conditionals.md | 80 ++++++ .../formio-form/references/external-data.md | 120 +++++++++ .../formio-form/references/field-logic.md | 96 +++++++ .../formio-form/references/javascript-api.md | 95 +++++++ .../formio-form/references/json-logic.md | 86 ++++++ .../skills/formio-form/references/options.md | 61 +++++ .../formio-form/references/rendering.md | 120 +++++++++ plugin/skills/formio-form/references/setup.md | 66 +++++ .../formio-form/references/validation.md | 85 ++++++ .../skills/formio-form/references/wizards.md | 95 +++++++ plugin/skills/formio-sdk/SKILL.md | 2 +- 38 files changed, 2536 insertions(+), 7 deletions(-) create mode 120000 .claude/skills/formio-form create mode 100644 openspec/changes/add-formio-form-skill/.openspec.yaml create mode 100644 openspec/changes/add-formio-form-skill/design.md create mode 100644 openspec/changes/add-formio-form-skill/proposal.md create mode 100644 openspec/changes/add-formio-form-skill/specs/claude-plugin-packaging/spec.md create mode 100644 openspec/changes/add-formio-form-skill/specs/formio-angular-skill/spec.md create mode 100644 openspec/changes/add-formio-form-skill/specs/formio-application-skill/spec.md create mode 100644 openspec/changes/add-formio-form-skill/specs/formio-form-skill/spec.md create mode 100644 openspec/changes/add-formio-form-skill/specs/formio-schema-skill/spec.md create mode 100644 openspec/changes/add-formio-form-skill/specs/formio-sdk-skill/spec.md create mode 100644 openspec/changes/add-formio-form-skill/tasks.md create mode 100644 packages/skill-tests/src/formio-form/calculated-values.test.ts create mode 100644 packages/skill-tests/src/formio-form/conditionals.test.ts create mode 100644 packages/skill-tests/src/formio-form/external-data.test.ts create mode 100644 packages/skill-tests/src/formio-form/field-logic.test.ts create mode 100644 packages/skill-tests/src/formio-form/fixtures/logic.ts create mode 100644 packages/skill-tests/src/formio-form/fixtures/rendering.ts create mode 100644 packages/skill-tests/src/formio-form/fixtures/wizard-external.ts create mode 100644 packages/skill-tests/src/formio-form/rendering.test.ts create mode 100644 packages/skill-tests/src/formio-form/skill-structure.test.ts create mode 100644 packages/skill-tests/src/formio-form/validation.test.ts create mode 100644 packages/skill-tests/src/formio-form/wizards.test.ts create mode 100644 plugin/skills/formio-form/SKILL.md create mode 100644 plugin/skills/formio-form/references/calculated-values.md create mode 100644 plugin/skills/formio-form/references/conditionals.md create mode 100644 plugin/skills/formio-form/references/external-data.md create mode 100644 plugin/skills/formio-form/references/field-logic.md create mode 100644 plugin/skills/formio-form/references/javascript-api.md create mode 100644 plugin/skills/formio-form/references/json-logic.md create mode 100644 plugin/skills/formio-form/references/options.md create mode 100644 plugin/skills/formio-form/references/rendering.md create mode 100644 plugin/skills/formio-form/references/setup.md create mode 100644 plugin/skills/formio-form/references/validation.md create mode 100644 plugin/skills/formio-form/references/wizards.md diff --git a/.claude/skills/formio-form b/.claude/skills/formio-form new file mode 120000 index 0000000..a4588d2 --- /dev/null +++ b/.claude/skills/formio-form @@ -0,0 +1 @@ +../../plugin/skills/formio-form \ No newline at end of file diff --git a/openspec/changes/add-formio-form-skill/.openspec.yaml b/openspec/changes/add-formio-form-skill/.openspec.yaml new file mode 100644 index 0000000..976c122 --- /dev/null +++ b/openspec/changes/add-formio-form-skill/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-tdd +created: 2026-07-12 diff --git a/openspec/changes/add-formio-form-skill/design.md b/openspec/changes/add-formio-form-skill/design.md new file mode 100644 index 0000000..3d120b9 --- /dev/null +++ b/openspec/changes/add-formio-form-skill/design.md @@ -0,0 +1,100 @@ +# Design: `formio-form` skill + +## Context + +The skills library covers building whole apps (`formio-application` → `formio-angular`), planning data models (`formio-resource-planner`), REST endpoints (`formio-api`), and the raw SDK/Utils API surface (`formio-sdk`). There is no task-oriented skill for the most common developer job: embedding a single form into an existing page or application with the Vanilla JS renderer. + +The renderer surface to document is well-bounded: `Formio.createForm(element, srcOrJson, options)`, submission pre-fill, the form instance's event/method API, renderer options, and the component-JSON logic vocabulary (`logic`, `conditional.json`, `calculateValue`, `validate.json`) driven by JSON Logic. + +Source material (authoring inputs, not runtime dependencies): + +- help.form.io Form Renderer guide (page requirements, render URL, render JSON, submissions, controlling with JavaScript, options) +- formio.js example pages: `fieldLogic`, `conditions`, `external`, `calculated`, `externalload`, `conditionalwizard`, `customwizard` +- jsonlogic.com operations reference + +`packages/skill-tests` already runs `formio-sdk` doc examples against the real `@formio/js` in a DOM test environment, so renderer examples are testable. + +## Goals / Non-Goals + +**Goals:** + +- One activatable skill, `plugin/skills/formio-form/`, that owns framework-agnostic "embed/render a form" work end to end. +- Unambiguous routing: Angular-explicit phrasing goes to `formio-angular`; app-building goes to `formio-application`; everything embed-shaped lands here. +- Every JSON Logic shape the renderer consumes (`validate.json`, `conditional.json`, `calculateValue` with JSON variants, `logic` actions) documented with canonical examples. +- Doc examples runnable and verified by Vitest against the real `@formio/js` (same guarantee `formio-sdk` gives — no drift-prone prose). + +**Non-Goals:** + +- Form building UI (`Formio.builder`) — out of scope; mention only as a pointer. +- Framework wrappers (`@formio/angular`, `@formio/react`) — those are framework skills. +- Server-side/API concerns (actions, roles, submissions via REST) — `formio-api` / MCP tools own those. +- An eval harness (`evals/`) for this skill — can be added later following the repo convention; not part of this change. +- Custom components / plugin authoring — `formio-sdk` territory. + +## Decisions + +### D1: Stand-alone peer skill, not a `formio-sdk` reference doc + +`formio-sdk` is an API reference ("what does `Formio.request` take?"); `formio-form` is a task guide ("put this form on my page and make field B appear when field A is X"). Embedding has its own trigger vocabulary and needs component-JSON logic docs that are form-definition concerns, not SDK concerns. Alternative — folding into `formio-sdk/references/rendering.md` — rejected: activation precision would suffer and the doc would balloon past one capability group. + +### D2: Reference-doc layout (SKILL.md router + `references/*.md`, no frontmatter on references) + +Same pattern as `formio-api` and `formio-sdk`. Files: + +| Reference | Covers | +|---|---| +| `setup.md` | HTML page requirements: CDN `` + matching CSS ``, global `Formio`. +- ESM: `import { Formio } from '@formio/js';` (identical to the `formio-sdk` canonical import). + +Forbidden everywhere: `@formio/core` imports, `@formio/js/lib/` deep imports, `require()`. Terminology stays strict: `baseUrl` ⇒ `FORMIO_BASE_URL`, `projectUrl` ⇒ `FORMIO_PROJECT_URL`. + +### D5: Doc examples verified by `packages/skill-tests/src/formio-form/` + +Same contract as `formio-sdk`: reference-doc code examples are extracted into Vitest tests running against the real `@formio/js` in the existing DOM test environment. Coverage targets: `createForm` from JSON, submission pre-fill, `change`/`submit` events, `validate.json` (invalid value yields the JSON Logic error string), `calculateValue`, `conditional.json` show/hide, and wizard page navigation. TDD order per repo rule: failing tests first (red), then author the reference docs whose examples make them pass (green). + +Rendering-by-URL examples are documented but network-dependent; tests stub `Formio.setBaseUrl`/fetch rather than hit a live server. + +### D6: MCP tool preference + +`SKILL.md` includes an `## MCP Tool Preference` section (mirroring `formio-api` reference convention): when the form definition must be fetched or modified during an embed task, prefer `form_get` / `form_update` MCP tools over ad-hoc `curl`/fetch; authentication goes through the portal-login `authenticate` flow, never PKCE or API keys. + +### D7: Dev symlink + +Add `.claude/skills/formio-form -> ../../plugin/skills/formio-form`, matching the existing dev symlinks (`formio-angular`, `formio-application`, `formio-resource-planner`) so the skill activates in this repo during development. + +## Risks / Trade-offs + +- [Renderer behavior in a DOM test environment diverges from real browsers (focus, attach timing, wizard transitions)] → Keep tested examples to data/logic behavior (`submission`, validation results, conditional visibility flags, calculated values) rather than pixel/DOM assertions; the `formio-sdk` suite already proves this works. +- [Overlap with `formio-sdk`'s `rendering.md` reference causes double-activation or contradictory guidance] → `formio-form` owns the task-level embed guidance; `formio-sdk/references/rendering.md` stays the API-surface reference. The `Not for:` clauses on both descriptions point at each other; no example is duplicated with different content. +- [help.form.io / example pages drift after authoring] → Tests against the real package are the drift guard for code behavior; doc URLs are cited as sources in the change, not linked as living dependencies inside the skill. +- [JSON Logic primer duplicates jsonlogic.com] → Scope the primer to operations the renderer actually evaluates plus Form.io-specific `var` data resolution; link out for the rest. + +## Open Questions + +None — routing boundaries, file layout, and test scope are settled above. diff --git a/openspec/changes/add-formio-form-skill/proposal.md b/openspec/changes/add-formio-form-skill/proposal.md new file mode 100644 index 0000000..a33b34c --- /dev/null +++ b/openspec/changes/add-formio-form-skill/proposal.md @@ -0,0 +1,47 @@ +# Add `formio-form` skill + +## Why + +The skills library has no framework-agnostic "embed a form in my application" skill. Today, embed requests either fall through to `formio-sdk` (an API reference, not a task guide) or get mis-routed to `formio-angular` (Angular-only) / `formio-application` (full app orchestration). Users who just want to drop a Form.io form into an existing page — render by URL or JSON, pre-fill a submission, wire up events, add conditional logic, calculated values, or JSON Logic validation — have no dedicated skill. + +## What Changes + +- New activatable skill at `plugin/skills/formio-form/` — the library's default "embed a form" entry point, built on the Vanilla JS renderer `@formio/js` (`Formio.createForm` / `Formio.builder`). +- Skill routes to `formio-angular` ONLY when the user explicitly names Angular or `@formio/angular`; otherwise `formio-form` claims all embed/render triggers. +- Reference documents covering: + - HTML page requirements (script/CSS includes, target `
`) + - Rendering a form by URL (`Formio.createForm(el, url)`) + - Rendering a form by JSON definition + - Rendering submissions / pre-filling forms (`submission` set, `src` with submission URL) + - Controlling the form with JavaScript (events, `form.submission`, `form.getComponent`, methods) + - Form renderer options (`readOnly`, `noAlerts`, `hooks`, `i18n`, `sanitizeConfig`, etc.) + - Field Logic (component `logic` arrays) + - Conditional forms with JSON Logic (`conditional.json`, simple conditionals) + - Calculated values with JSON Logic (`calculateValue`) + - Custom validations with JSON Logic (`validate.json` returning `true` or an error string) + - External data sources and cascading/conditional selects (make → model → year) + - Worked examples: external data load + submission set, conditional wizards, custom wizards +- Doc examples in the skill are runnable and covered by tests in `packages/skill-tests/src/formio-form/`, executed against the real `@formio/js` in jsdom (same pattern as `formio-sdk`). +- Sibling skill descriptions updated so routing is unambiguous: `formio-sdk`, `formio-application`, and `formio-angular` gain `Not for:` pointers at `formio-form` for framework-agnostic embed requests. + +## Capabilities + +### New Capabilities + +- `formio-form-skill`: The `plugin/skills/formio-form/` skill — directory layout, three-clause description with embed triggers and Angular/Sdk/Application negative triggers, required reference docs, canonical `@formio/js` import + CDN usage conventions, JSON Logic shapes (`validate.json`, `conditional.json`, `calculateValue`), and runnable doc examples verified by `packages/skill-tests/src/formio-form/`. + +### Modified Capabilities + +- `formio-sdk-skill`: description's `Not for:` clause additionally names `formio-form` for task-oriented "embed/render a form in my page/app" requests (formio-sdk stays the API reference). +- `formio-application-skill`: description's `Not for:` clauses additionally point embed-a-form-in-an-existing-page requests (no app build/orchestration) at `formio-form`. +- `formio-angular-skill`: description's negative-trigger guidance additionally states that framework-agnostic embed requests route to `formio-form`, not `formio-angular`. +- `formio-schema-skill`: the name `formio-form` was previously a form-schema reference skill merged into `formio-schema`; the spec's blanket prohibition ("the repository SHALL NOT contain a separate `formio-form` skill") is lifted — the name is reused for the NEW embed skill, whose scope (rendering/embedding) does not overlap `formio-schema`'s (JSON schema authoring). +- `claude-plugin-packaging`: the plugin bundle SHALL now include the `formio-form` embed skill; the old exclusion assertions (bundle and smoke test) are updated accordingly. + +## Impact + +- New: `plugin/skills/formio-form/SKILL.md` + `plugin/skills/formio-form/references/*.md`. +- New: `packages/skill-tests/src/formio-form/*.test.ts` (jsdom, real `@formio/js`). +- Modified: `plugin/skills/formio-sdk/SKILL.md`, `plugin/skills/formio-application/SKILL.md`, `plugin/skills/formio-angular/SKILL.md` (description `Not for:` clauses only). +- No changes to the MCP server, plugin packaging manifest beyond the new skill directory, or existing reference docs' content. +- Source material: help.form.io Form Renderer docs, formio.js example pages (fieldLogic, conditions, external, calculated, externalload, conditionalwizard, customwizard), jsonlogic.com operations. diff --git a/openspec/changes/add-formio-form-skill/specs/claude-plugin-packaging/spec.md b/openspec/changes/add-formio-form-skill/specs/claude-plugin-packaging/spec.md new file mode 100644 index 0000000..b784c8c --- /dev/null +++ b/openspec/changes/add-formio-form-skill/specs/claude-plugin-packaging/spec.md @@ -0,0 +1,34 @@ +## MODIFIED Requirements + +### Requirement: Plugin bundles the skills library + +The plugin source tree SHALL include, and the build SHALL copy to `dist/plugin/skills/`, the full `formio-api` router skill, every `formio-api-` capability-group skill, and the `formio-schema`, `formio-resource-planner`, and `formio-form` skills. (`formio-form` here is the `@formio/js` embed skill introduced by the `formio-form-skill` capability — not the historical schema-reference skill of the same name, which remains merged into `formio-schema`.) + +#### Scenario: Installed plugin exposes all skills + +- **WHEN** a user installs `@formio/ai` in a Claude Code project +- **THEN** Claude Code discovers every `formio-api`, `formio-schema`, `formio-resource-planner`, and `formio-form` skill from the plugin's `skills/` directory + +#### Scenario: Bundled formio-form is the embed skill + +- **WHEN** the bundled `skills/formio-form/SKILL.md` is inspected +- **THEN** its frontmatter `name` is `formio-form` and its description claims embed/render triggers for `@formio/js` + +### Requirement: Smoke test validates the built plugin over stdio + +A `scripts/test-plugin.ts` script SHALL validate that `dist/plugin/` exists, that `plugin.json` contains required fields (`name`, `version`, `description`, at least one `mcpServers` entry), that required skill directories (at minimum `formio-api` and `formio-schema`) are present, and that spawning the bundled server and sending a JSON-RPC `tools/list` request returns a well-formed response. + +#### Scenario: Smoke test fails when build is missing + +- **WHEN** the smoke test runs without `dist/plugin/` present +- **THEN** it exits non-zero with a message instructing the user to run `pnpm build:plugin` + +#### Scenario: Smoke test verifies live MCP server + +- **WHEN** the smoke test spawns `dist/plugin/server/stdio.mjs` and sends a `tools/list` JSON-RPC request +- **THEN** the server responds with a `result.tools` array and the test exits zero + +#### Scenario: Smoke test asserts on the consolidated schema skill + +- **WHEN** the smoke test inspects the bundled `skills/` directory +- **THEN** it SHALL assert `formio-schema` is present diff --git a/openspec/changes/add-formio-form-skill/specs/formio-angular-skill/spec.md b/openspec/changes/add-formio-form-skill/specs/formio-angular-skill/spec.md new file mode 100644 index 0000000..5cd9720 --- /dev/null +++ b/openspec/changes/add-formio-form-skill/specs/formio-angular-skill/spec.md @@ -0,0 +1,44 @@ +## MODIFIED Requirements + +### Requirement: Parent skill description and trigger surface + +The parent `formio-angular` `SKILL.md` frontmatter `description` SHALL claim ONLY framework-explicit Angular triggers — phrases that explicitly name Angular or request Angular-specific behavior. The description MUST include at least the following positive triggers: + +- "build it in Angular" +- "Angular front-end for this Form.io project" +- "use Angular" +- "the Angular skill" +- Invocation from `formio-application` via handoff context (the orchestrator passes the framework choice explicitly). + +The description MUST drop all plain-language "build me an app" triggers (those now belong to `formio-application`). The description MUST NOT contain generic phrases like "build me an app", "build me a tool", "spin up an app", "I need a tool to track", or bare domain archetypes like "task manager", "help desk", "CRM", "booking system". + +The description MUST include a `Not for:` clause pointing at `formio-application` for generic build-an-app requests and for framework-agnostic "I want to build X" requests. + +The description MUST include a `Not for:` clause pointing at `formio-form` for framework-agnostic embed/render-a-form requests that do not name Angular or `@formio/angular`. + +The description MUST continue to include the existing `Not for:` clause pointing at `formio-angular-resources` for add-a-feature-to-an-existing-app requests. + +#### Scenario: formio-angular only fires on Angular-explicit phrasing + +- **WHEN** the user says "build it in Angular" or "I want an Angular front-end for my Form.io project" +- **THEN** the `formio-angular` skill activates +- **AND** `formio-application` does not activate + +#### Scenario: formio-angular does not fire on generic build-an-app phrasing + +- **WHEN** the user says "build me a CRM" (no mention of Angular) +- **THEN** the `formio-angular` skill does NOT activate +- **AND** `formio-application` activates instead + +#### Scenario: formio-angular does not fire on generic embed phrasing + +- **WHEN** the user says "embed this form in my web page" (no mention of Angular) +- **THEN** the `formio-angular` skill does NOT activate +- **AND** `formio-form` activates instead +- **AND** the `formio-angular` description contains a `Not for:` clause with the literal substring `formio-form` + +#### Scenario: formio-angular description Not-for clause names the orchestrator + +- **WHEN** the `formio-angular` `SKILL.md` frontmatter is inspected +- **THEN** its `description` contains the literal substring `formio-application` +- **AND** it contains a `Not for:` clause pointing at `formio-application` diff --git a/openspec/changes/add-formio-form-skill/specs/formio-application-skill/spec.md b/openspec/changes/add-formio-form-skill/specs/formio-application-skill/spec.md new file mode 100644 index 0000000..d160a39 --- /dev/null +++ b/openspec/changes/add-formio-form-skill/specs/formio-application-skill/spec.md @@ -0,0 +1,55 @@ +## MODIFIED Requirements + +### Requirement: formio-application description claims plain-language triggers and names the framework skills + +The `formio-application` `SKILL.md` frontmatter `description` SHALL claim plain-language build-an-app and extend-an-app triggers WITHOUT requiring the user to know any UI framework or Form.io terminology. Example triggers the description MUST claim include: + +- Build-new: "build me an app", "create a CRM", "I need a tool to track X", "spin up a system for Y", bare domain archetypes ("task manager", "help desk"). +- Extend-existing: "also track X", "add a way to see Y", "each Z should have a list of W", "let users also submit V". + +The description MUST state that this skill is the library's default build-an-app entry point; that it picks a UI framework automatically when only one is installed and asks the user when multiple are installed; that the user does NOT need to mention "Angular", "React", "framework", "resource", or "NgModule". + +The description MUST include `Not for:` clauses pointing at: + +- `formio-angular` for framework-explicit Angular requests. +- `formio-angular-resources` for framework-explicit Angular extension requests. +- `formio-resource-planner` for data-model-only planning requests without building an app. +- `formio-api` for endpoint lookups. +- `formio-form` for embedding or rendering a single form in an existing page or application (no app build or orchestration). + +The description MUST also state that the skill writes a `.mcp.json` file in the workspace root as part of the flow (so users know a file will be created and why) and that the flow pauses for a Claude Code restart after the `.mcp.json` write. + +#### Scenario: formio-application claims generic build-new triggers + +- **WHEN** the user says "build me a tool to track maintenance requests" (no framework or Form.io terminology) +- **THEN** the `formio-application` skill activates +- **AND** neither `formio-angular` nor `formio-angular-resources` activates + +#### Scenario: formio-application claims generic extend triggers + +- **WHEN** the user says "also let customers leave reviews on products" in a workspace that already has a framework app wired +- **THEN** the `formio-application` skill activates +- **AND** `formio-angular-resources` does not activate directly + +#### Scenario: Framework-explicit phrasing does NOT route through formio-application + +- **WHEN** the user says "build it in Angular" or "add an Angular module for X" +- **THEN** `formio-angular` (or `formio-angular-resources`) activates directly +- **AND** `formio-application` does not activate + +#### Scenario: Embed-a-form phrasing does NOT route through formio-application + +- **WHEN** the user says "embed this form in my existing site" or "render this form on my page" (no app build requested) +- **THEN** `formio-form` activates +- **AND** `formio-application` does not activate +- **AND** the `formio-application` description's `Not for:` clauses contain the literal substring `formio-form` + +#### Scenario: Description mentions the .mcp.json write + +- **WHEN** the `formio-application` `SKILL.md` frontmatter is inspected +- **THEN** its `description` contains the literal substring `.mcp.json` + +#### Scenario: Description mentions the restart pause + +- **WHEN** the `formio-application` `SKILL.md` frontmatter is inspected +- **THEN** its `description` mentions restarting Claude Code (or an equivalent "pause and restart" phrasing) so the user knows the flow is multi-invocation diff --git a/openspec/changes/add-formio-form-skill/specs/formio-form-skill/spec.md b/openspec/changes/add-formio-form-skill/specs/formio-form-skill/spec.md new file mode 100644 index 0000000..5d397af --- /dev/null +++ b/openspec/changes/add-formio-form-skill/specs/formio-form-skill/spec.md @@ -0,0 +1,246 @@ +## ADDED Requirements + +### Requirement: New skill `formio-form` exists as the library's embed-a-form entry point + +The skills library SHALL contain a new skill at `plugin/skills/formio-form/SKILL.md` with frontmatter `name: formio-form`. The skill directory SHALL contain a `references/` directory with exactly the following reference documents, none of which begin with a YAML frontmatter block: + +- `setup.md` — HTML page requirements and library inclusion +- `rendering.md` — rendering by form URL, by form JSON, and with submissions (pre-fill) +- `javascript-api.md` — controlling the form instance with JavaScript (events and methods) +- `options.md` — form renderer options +- `json-logic.md` — JSON Logic primer scoped to renderer evaluation +- `field-logic.md` — component `logic` arrays (triggers and actions) +- `conditionals.md` — simple and JSON Logic conditionals +- `calculated-values.md` — `calculateValue` +- `validation.md` — custom validation via `validate.json` +- `external-data.md` — external data sources and cascading/conditional selects +- `wizards.md` — wizard display, conditional wizards, custom wizard navigation + +A symlink `.claude/skills/formio-form` SHALL exist and resolve to `plugin/skills/formio-form/`. + +#### Scenario: formio-form directory layout + +- **WHEN** the repository is inspected +- **THEN** `plugin/skills/formio-form/SKILL.md` exists with frontmatter `name: formio-form` and a non-empty `description` +- **AND** all eleven reference documents listed above exist under `plugin/skills/formio-form/references/` and are non-empty +- **AND** no reference document's first line is `---` +- **AND** `.claude/skills/formio-form` resolves to `plugin/skills/formio-form/` + +### Requirement: formio-form description uses the three-clause template and claims framework-agnostic embed triggers + +The `formio-form` `SKILL.md` frontmatter `description` SHALL contain three clauses: + +1. A capability statement naming the Vanilla JS renderer `@formio/js` and asserting the skill covers embedding forms in any web application — rendering by URL or JSON, submission pre-fill, JavaScript control, renderer options, conditional logic, calculated values, and JSON Logic validation. +2. A trigger clause beginning with the substring `Use when the user asks to` claiming framework-agnostic embed phrasing. Example triggers the description MUST claim include: "embed a form", "render a form", "add this form to my page/site/app", "pre-fill a form", "show or hide a field based on another field", "calculate a field value", "add custom validation to a field", "conditional wizard". +3. A negative-trigger clause beginning with the substring `Not for:` that names ALL of: `formio-angular` (user explicitly names Angular or `@formio/angular`), `formio-application` (building a whole app, portal, or tracker around data), `formio-resource-planner` (designing the data model), `formio-api` (REST endpoint lookups), and `formio-sdk` (raw SDK/Utils API reference beyond embedding). + +#### Scenario: formio-form claims framework-agnostic embed phrasing + +- **WHEN** the user says "embed this form in my web page" or "make the model field depend on the make field" with no framework named +- **THEN** the `formio-form` skill activates +- **AND** neither `formio-angular` nor `formio-application` activates + +#### Scenario: Angular-explicit phrasing does NOT route through formio-form + +- **WHEN** the user says "embed this form in my Angular app" or names `@formio/angular` +- **THEN** `formio-angular` activates +- **AND** `formio-form` does not activate + +#### Scenario: Negative clause names every sibling + +- **WHEN** the `formio-form` `SKILL.md` frontmatter is inspected +- **THEN** its `description` contains a `Not for:` clause containing the literal substrings `formio-angular`, `formio-application`, `formio-resource-planner`, `formio-api`, and `formio-sdk` + +### Requirement: Canonical inclusion modes are CDN and ESM only + +Across `plugin/skills/formio-form/SKILL.md` and every `plugin/skills/formio-form/references/*.md`, form-renderer inclusion SHALL be documented in exactly two canonical modes: + +- CDN: a ` + +
+ + +``` + +The CDN build exposes the global `Formio`. + +## ESM inclusion (bundled applications) + +```js +import { Formio } from '@formio/js'; +import '@formio/js/dist/formio.full.min.css'; + +const form = await Formio.createForm( + document.getElementById('formio'), + 'https://examples.form.io/example' +); +``` + +These are the only two supported inclusion modes. Never import from +`@formio/core`, never deep-import from `@formio/js/lib/`, and never use CommonJS +`require`. + +## Target element + +`Formio.createForm` renders into any block element you hand it — conventionally +an empty `
`. The renderer owns that element's contents; do not manage its +children from your own code. + +## Hosted vs SaaS URL configuration + +- SaaS: `FORMIO_BASE_URL` is `https://api.form.io`; `FORMIO_PROJECT_URL` is `https://.form.io`. +- Self-hosted: `FORMIO_BASE_URL` is your server root (e.g. `https://forms.mysite.com`); `FORMIO_PROJECT_URL` appends the project path (e.g. `https://forms.mysite.com/myproject`). + +```js +import { Formio } from '@formio/js'; + +Formio.setBaseUrl('https://forms.mysite.com'); // FORMIO_BASE_URL +Formio.setProjectUrl('https://forms.mysite.com/myproject'); // FORMIO_PROJECT_URL +``` + +Form URLs passed to `Formio.createForm` are `{FORMIO_PROJECT_URL}/{formPath}`. + +## See also + +- [rendering.md](./rendering.md) — the three input shapes `createForm` accepts. +- [options.md](./options.md) — the options object. diff --git a/plugin/skills/formio-form/references/validation.md b/plugin/skills/formio-form/references/validation.md new file mode 100644 index 0000000..9ee79c7 --- /dev/null +++ b/plugin/skills/formio-form/references/validation.md @@ -0,0 +1,85 @@ +# Custom Validation with JSON Logic + +## Overview + +A component's `validate.json` property holds a JSON Logic expression the +renderer evaluates on every value change and on submit. The contract: + +- The expression result is **`true`** → the value is valid. +- The expression result is a **string** → the value is invalid and that string + is the validation error message shown on the component. + +Expression syntax and the operations vocabulary live in +[json-logic.md](./json-logic.md). Inside `validate.json`, `{"var": "input"}` +resolves to the value of the component being validated, and +`{"var": "data."}` reaches any other field for cross-field rules. + +## Canonical example + +An `if` that returns `true` for the valid case and the error string otherwise: + +```json +{ + "type": "textfield", + "key": "name", + "label": "Name", + "input": true, + "validate": { + "json": { + "if": [ + { "===": [{ "var": "input" }, "Bob"] }, + true, + "Your name must be 'Bob'!" + ] + } + } +} +``` + +Typing anything but `Bob` marks the field invalid with exactly +`Your name must be 'Bob'!`; typing `Bob` clears the error. A failed rule also +blocks `form.submit()` — the promise rejects and the messages land on +`form.errors` (see [javascript-api.md](./javascript-api.md)). + +## Cross-field rules + +Reach other fields through `data`: + +```json +{ + "validate": { + "json": { + "if": [ + { ">=": [{ "var": "input" }, { "var": "data.minimumBid" }] }, + true, + "Your bid must be at least the minimum bid." + ] + } + } +} +``` + +## Composing with standard validation + +`validate.json` runs alongside the declarative `validate` keys — it does not +replace them. All configured rules must pass: + +```json +{ + "validate": { + "required": true, + "minLength": 2, + "pattern": "[A-Za-z]+", + "json": { "if": [{ "===": [{ "var": "input" }, "Bob"] }, true, "Your name must be 'Bob'!"] } + } +} +``` + +`required`, `minLength`/`maxLength`, `min`/`max`, `pattern`, and +`customMessage` keep their standard meanings; `json` adds the escape hatch for +everything they cannot express. + +## See also + +- [json-logic.md](./json-logic.md) — operations and `var` resolution. +- [field-logic.md](./field-logic.md) — reacting to values beyond validity (disable, hide, mutate). diff --git a/plugin/skills/formio-form/references/wizards.md b/plugin/skills/formio-form/references/wizards.md new file mode 100644 index 0000000..1eb51ba --- /dev/null +++ b/plugin/skills/formio-form/references/wizards.md @@ -0,0 +1,95 @@ +# Wizards — Conditional Pages and Custom Navigation + +## Overview + +Set `display: "wizard"` on the form definition and every top-level `panel` +component becomes a page: + +```json +{ + "display": "wizard", + "components": [ + { "type": "panel", "key": "basics", "title": "Basics", "components": [ ... ] }, + { "type": "panel", "key": "extras", "title": "Extras", "components": [ ... ] }, + { "type": "panel", "key": "confirmation", "title": "Confirmation", "components": [ ... ] } + ] +} +``` + +`Formio.createForm` returns a Wizard instance — everything in +[javascript-api.md](./javascript-api.md) applies, plus the page API below. + +## Page API + +| Member | Meaning | +| --- | --- | +| `wizard.page` | current page index (0-based) | +| `wizard.pages` | the currently visible pages (conditional pages excluded) | +| `wizard.nextPage()` | validate the current page, then advance; returns a promise | +| `wizard.prevPage()` | go back; returns a promise | +| `wizard.setPage(index)` | jump straight to a visible page | + +```js +const wizard = await Formio.createForm(el, wizardDefinition); +await wizard.nextPage(); // page 0 → 1 (validates page 0 first) +await wizard.prevPage(); // back to 0 +``` + +`nextPage()` rejects when the current page fails validation — the wizard stays +put and errors render on the offending components. + +## Conditional wizard pages + +A page is a `panel`, so it takes the same `conditional` shapes as any +component (see [conditionals.md](./conditionals.md)). Skip the Extras page +unless the user wants it: + +```json +{ + "type": "panel", + "key": "extras", + "title": "Extras", + "components": [ + { "type": "textfield", "key": "extraDetails", "label": "Extra Details", "input": true } + ], + "conditional": { + "json": { "!==": [{ "var": "data.wantsExtras" }, "no"] } + } +} +``` + +Hidden pages drop out of `wizard.pages` and the breadcrumb entirely — +navigation flows straight from the page before to the page after, and the +page's components stop validating. Flip the driving value back and the page +rejoins in place. + +## Custom navigation + +Hide the built-in buttons and drive the wizard from your own UI: + +```js +const wizard = await Formio.createForm(el, wizardDefinition, { + buttonSettings: { + showPrevious: false, + showNext: false, + showCancel: false, + showSubmit: false, + }, +}); + +document.getElementById('myNext').addEventListener('click', () => wizard.nextPage()); +document.getElementById('myBack').addEventListener('click', () => wizard.prevPage()); +document.getElementById('myFinish').addEventListener('click', () => wizard.submit()); +``` + +Track progress from wizard events: `wizard.on('nextPage')`, +`wizard.on('prevPage')`, and `wizard.on('wizardPageSelected')` fire as the user +moves; pair with `wizard.page`/`wizard.pages.length` to render a progress bar. +`breadcrumbSettings: { clickable: false }` locks the breadcrumb when users must +move strictly forward. + +## See also + +- [conditionals.md](./conditionals.md) — the conditional shapes pages accept. +- [options.md](./options.md) — `buttonSettings`, `breadcrumbSettings`. +- [javascript-api.md](./javascript-api.md) — events shared with plain forms. diff --git a/plugin/skills/formio-sdk/SKILL.md b/plugin/skills/formio-sdk/SKILL.md index 1dffffb..b31290d 100644 --- a/plugin/skills/formio-sdk/SKILL.md +++ b/plugin/skills/formio-sdk/SKILL.md @@ -1,7 +1,7 @@ --- name: formio-sdk description: >- - Reference for the Form.io JavaScript SDK (`@formio/js`), the Form.io Utilities (`@formio/js/utils`), and the small set of helpers exposed only by `@formio/core` (jsonLogic, dom, I18n, override, unwind, the logic processor). Covers static SDK methods (`setBaseUrl`, `setProjectUrl`, `setToken`, `currentUser`, `logout`, `ssoInit`, `accessInfo`, `pluginAlter`, `pluginGet`, `pluginWait`), instance SDK methods on `new Formio(url)` (`loadForm`, `saveForm`, `deleteForm`, `loadForms`, `loadSubmission`, `saveSubmission`, `deleteSubmission`, `loadSubmissions`, `availableActions`, `userPermissions`, `getDownloadUrl`, `getTempToken`, `uploadFile`, `downloadFile`, `deleteFile`), VanillaJS form rendering (`Formio.createForm`, `Formio.builder`, `form.on(…)`, `form.submission` prefill), the plugin lifecycle (`preRequest`, `request`, `staticRequest`, `wrapRequestPromise`, `wrapStaticRequestPromise`, `requestOptions`, `requestResponse`), the `Utils` surface (Evaluator, form traversal, conditions, mask, sanitize, date helpers, fastCloneDeep), and the @formio/core-only surfaces (`jsonLogic`, `dom`, `I18n`, `override`, `unwind`, `logicProcessSync`/`logicProcessInfo`). Token decoding goes through `Formio.getToken({ decode: true })`; logout and token clearing through `Formio.logout()` or `Formio.setToken(null)`. Use when the user asks to call any `Formio.*` static method, work with a `new Formio(...)` instance, render a Form.io form in a VanillaJS or non-Angular consumer, invoke a `Utils.*` helper, evaluate JSONLogic, run component-logic actions, manipulate the DOM via Form.io's helpers, register a plugin, configure `setBaseUrl`/`setProjectUrl` for a Hosted or SaaS deployment, traverse component trees, mask or sanitize input, or decode the cached JWT. Not for: documenting Form.io REST endpoint shape (see formio-api); orchestrating a full application build (see formio-application); planning Resource schemas (see formio-resource-planner); Angular `@formio/angular` wrappers, `FormioAppConfig`, or `FormioModule` (see formio-angular). + Reference for the Form.io JavaScript SDK (`@formio/js`), the Form.io Utilities (`@formio/js/utils`), and the small set of helpers exposed only by `@formio/core` (jsonLogic, dom, I18n, override, unwind, the logic processor). Covers static SDK methods (`setBaseUrl`, `setProjectUrl`, `setToken`, `currentUser`, `logout`, `ssoInit`, `accessInfo`, `pluginAlter`, `pluginGet`, `pluginWait`), instance SDK methods on `new Formio(url)` (`loadForm`, `saveForm`, `deleteForm`, `loadForms`, `loadSubmission`, `saveSubmission`, `deleteSubmission`, `loadSubmissions`, `availableActions`, `userPermissions`, `getDownloadUrl`, `getTempToken`, `uploadFile`, `downloadFile`, `deleteFile`), VanillaJS form rendering (`Formio.createForm`, `Formio.builder`, `form.on(…)`, `form.submission` prefill), the plugin lifecycle (`preRequest`, `request`, `staticRequest`, `wrapRequestPromise`, `wrapStaticRequestPromise`, `requestOptions`, `requestResponse`), the `Utils` surface (Evaluator, form traversal, conditions, mask, sanitize, date helpers, fastCloneDeep), and the @formio/core-only surfaces (`jsonLogic`, `dom`, `I18n`, `override`, `unwind`, `logicProcessSync`/`logicProcessInfo`). Token decoding goes through `Formio.getToken({ decode: true })`; logout and token clearing through `Formio.logout()` or `Formio.setToken(null)`. Use when the user asks to call any `Formio.*` static method, work with a `new Formio(...)` instance, look up the raw `Formio.createForm`/`Formio.builder` API surface, invoke a `Utils.*` helper, evaluate JSONLogic, run component-logic actions, manipulate the DOM via Form.io's helpers, register a plugin, configure `setBaseUrl`/`setProjectUrl` for a Hosted or SaaS deployment, traverse component trees, mask or sanitize input, or decode the cached JWT. Not for: documenting Form.io REST endpoint shape (see formio-api); orchestrating a full application build (see formio-application); planning Resource schemas (see formio-resource-planner); Angular `@formio/angular` wrappers, `FormioAppConfig`, or `FormioModule` (see formio-angular); task-oriented "embed/render a form in my page or app" requests — conditionals, calculated values, `validate.json`, wizards, cascading selects (see formio-form; this skill stays the raw SDK/Utils API reference). --- # Form.io SDK Skills From fc26164728608f616a10df33a262ec7945e8244f Mon Sep 17 00:00:00 2001 From: Travis Tidwell Date: Mon, 13 Jul 2026 12:44:35 -0500 Subject: [PATCH 3/7] Adding a formio-form-builder orchestrator skill for building single forms. --- .claude/skills/formio-form-builder | 1 + .../.openspec.yaml | 0 .../design.md | 109 +++++++ .../proposal.md | 40 +++ .../specs/claude-plugin-packaging/spec.md | 21 ++ .../specs/formio-application-skill/spec.md | 75 +++++ .../specs/formio-form-builder-skill/spec.md | 169 +++++++++++ .../specs/formio-form-skill/spec.md | 49 ++++ .../formio-resource-planner-skill/spec.md | 22 ++ .../tasks.md | 99 +++++++ .../.openspec.yaml | 2 + .../design.md | 0 .../proposal.md | 0 .../specs/claude-plugin-packaging/spec.md | 0 .../specs/formio-angular-skill/spec.md | 0 .../specs/formio-application-skill/spec.md | 0 .../specs/formio-form-skill/spec.md | 0 .../specs/formio-schema-skill/spec.md | 0 .../specs/formio-sdk-skill/spec.md | 0 .../tasks.md | 0 .../specs/claude-plugin-packaging/spec.md | 19 +- openspec/specs/formio-angular-skill/spec.md | 9 + .../specs/formio-application-skill/spec.md | 26 ++ .../specs/formio-form-builder-skill/spec.md | 169 +++++++++++ openspec/specs/formio-form-skill/spec.md | 267 ++++++++++++++++++ .../formio-resource-planner-skill/spec.md | 22 ++ openspec/specs/formio-schema-skill/spec.md | 14 +- openspec/specs/formio-sdk-skill/spec.md | 8 +- .../src/__tests__/plugin-build.test.ts | 3 +- .../skill-structure.test.ts | 139 +++++++++ .../src/formio-form-builder/step-docs.test.ts | 144 ++++++++++ plugin/skills/formio-application/SKILL.md | 6 +- plugin/skills/formio-form-builder/EMBED.md | 24 ++ .../skills/formio-form-builder/FORM_TYPES.md | 81 ++++++ plugin/skills/formio-form-builder/INTENT.md | 74 +++++ plugin/skills/formio-form-builder/SAVE.md | 52 ++++ plugin/skills/formio-form-builder/SKILL.md | 74 +++++ plugin/skills/formio-form/SKILL.md | 13 +- .../skills/formio-resource-planner/SKILL.md | 2 +- 39 files changed, 1716 insertions(+), 17 deletions(-) create mode 120000 .claude/skills/formio-form-builder rename openspec/changes/{add-formio-form-skill => archive/2026-07-12-add-formio-form-builder-skill}/.openspec.yaml (100%) create mode 100644 openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/design.md create mode 100644 openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/proposal.md create mode 100644 openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/claude-plugin-packaging/spec.md create mode 100644 openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-application-skill/spec.md create mode 100644 openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-form-builder-skill/spec.md create mode 100644 openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-form-skill/spec.md create mode 100644 openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-resource-planner-skill/spec.md create mode 100644 openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/tasks.md create mode 100644 openspec/changes/archive/2026-07-12-add-formio-form-skill/.openspec.yaml rename openspec/changes/{add-formio-form-skill => archive/2026-07-12-add-formio-form-skill}/design.md (100%) rename openspec/changes/{add-formio-form-skill => archive/2026-07-12-add-formio-form-skill}/proposal.md (100%) rename openspec/changes/{add-formio-form-skill => archive/2026-07-12-add-formio-form-skill}/specs/claude-plugin-packaging/spec.md (100%) rename openspec/changes/{add-formio-form-skill => archive/2026-07-12-add-formio-form-skill}/specs/formio-angular-skill/spec.md (100%) rename openspec/changes/{add-formio-form-skill => archive/2026-07-12-add-formio-form-skill}/specs/formio-application-skill/spec.md (100%) rename openspec/changes/{add-formio-form-skill => archive/2026-07-12-add-formio-form-skill}/specs/formio-form-skill/spec.md (100%) rename openspec/changes/{add-formio-form-skill => archive/2026-07-12-add-formio-form-skill}/specs/formio-schema-skill/spec.md (100%) rename openspec/changes/{add-formio-form-skill => archive/2026-07-12-add-formio-form-skill}/specs/formio-sdk-skill/spec.md (100%) rename openspec/changes/{add-formio-form-skill => archive/2026-07-12-add-formio-form-skill}/tasks.md (100%) create mode 100644 openspec/specs/formio-form-builder-skill/spec.md create mode 100644 openspec/specs/formio-form-skill/spec.md create mode 100644 openspec/specs/formio-resource-planner-skill/spec.md create mode 100644 packages/skill-tests/src/formio-form-builder/skill-structure.test.ts create mode 100644 packages/skill-tests/src/formio-form-builder/step-docs.test.ts create mode 100644 plugin/skills/formio-form-builder/EMBED.md create mode 100644 plugin/skills/formio-form-builder/FORM_TYPES.md create mode 100644 plugin/skills/formio-form-builder/INTENT.md create mode 100644 plugin/skills/formio-form-builder/SAVE.md create mode 100644 plugin/skills/formio-form-builder/SKILL.md diff --git a/.claude/skills/formio-form-builder b/.claude/skills/formio-form-builder new file mode 120000 index 0000000..f89005e --- /dev/null +++ b/.claude/skills/formio-form-builder @@ -0,0 +1 @@ +../../plugin/skills/formio-form-builder \ No newline at end of file diff --git a/openspec/changes/add-formio-form-skill/.openspec.yaml b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/.openspec.yaml similarity index 100% rename from openspec/changes/add-formio-form-skill/.openspec.yaml rename to openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/.openspec.yaml diff --git a/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/design.md b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/design.md new file mode 100644 index 0000000..340fa45 --- /dev/null +++ b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/design.md @@ -0,0 +1,109 @@ +# Design: `formio-form-builder` skill + +## Context + +The library has two orchestrators today: `formio-application` (build a whole app) and, beneath it, `formio-resource-planner` (plan the data model). Single-form creation has no owner. The building blocks all exist — `formio-schema` authors form JSON, the MCP server's `form_create` persists it (and already instructs use of `formio-schema`), `formio-form` embeds a saved form — but no skill chains them into a pipeline, and no skill knows how to choose between the three Form.io form types (webform, wizard, PDF form). + +Source material for the form-type reference (authoring inputs, not runtime dependencies): + +- https://help.form.io/form-building/form-types — webform vs wizard vs PDF form +- https://help.form.io/form-building/pdf-forms — PDF form capabilities and when to use them +- https://help.form.io/how/nested-form-workflows/nested-wizard-workflow#create-child-wizard — child wizards / complex multi-page flows + +Repo state that constrains this change: + +- The `add-formio-form-skill` change is implemented but not archived, so the `formio-form-skill` capability spec lives in that change's `specs/` directory, not yet in `openspec/specs/`. This change's `formio-form-skill` delta layers on top of the pending version. +- `formio-resource-planner` has no capability spec at all; this change introduces a narrow one covering only the routing boundary being added. +- `plugin-build.test.ts` assertion 1.3 enumerates bundled skills; `add-formio-form-skill` hit a stale exclusion there. This change updates the assertion in the same change that adds the skill. +- The `formio-schema-skill` spec forbids the string `formio-form` in `formio-schema`'s description — and `formio-form-builder` contains that substring — so `formio-schema` gets NO reverse pointer; the reference is one-way. + +## Goals / Non-Goals + +**Goals:** + +- One orchestrator skill, `plugin/skills/formio-form-builder/`, owning "build me a form" end to end: INTENT → SCHEMA → SAVE → EMBED (conditional). +- A crisp form-vs-resource routing boundary, stated in the description and enforced by reverse pointers on `formio-application`, `formio-resource-planner`, and `formio-form`. +- Zero duplication: form JSON shapes stay in `formio-schema`; embed guidance stays in `formio-form`; this skill references both by name. +- Skill ships in the `@formio/ai` plugin bundle with the packaging spec and build-test assertions updated in the same change. +- Structural tests guarantee the authoring contract (same pattern as `packages/skill-tests/src/formio-form/skill-structure.test.ts`). + +**Non-Goals:** + +- No MCP server changes — `form_create`, `form_get`, and `authenticate` already exist. +- No renderer behavior tests — the flow is prose/orchestration; there is no runtime surface to drive. +- No changes to `formio-schema` content or description. +- No eval harness (`evals/`) for this skill — can follow later per repo convention. +- No PDF-upload tooling — the form-types reference documents what PDF forms are and when to choose them; server-side PDF conversion/upload is out of scope. + +## Decisions + +### D1: Orchestrator tier, `formio-application` shape — root-level step docs, not `references/` + +`formio-form-builder` mirrors `formio-application`'s layout: `SKILL.md` router plus root-level uppercase step docs (the INTENT.md/IMPORT.md pattern), NOT the `references/` layout used by reference skills (`formio-api`, `formio-form`). Rationale: this is a flow with approval gates, not a lookup library; each doc scripts one step. Files: + +| Doc | Covers | +|---|---| +| `SKILL.md` | Router: three-clause description, stance, the four steps with links, SCHEMA delegation to `formio-schema`, MCP Tool Preference, URL terminology | +| `FORM_TYPES.md` | What each form type is (webform / wizard / PDF form), capabilities, when to choose it, how INTENT distinguishes them; wizard section covers nested/child wizard workflows | +| `INTENT.md` | The single batched `AskUserQuestion` interview script: form type (infer-and-confirm vs ask) + embed intent (embed handoff fires only on explicit yes) | +| `SAVE.md` | `form_create` invocation, the saved-form confirmation (path/URL), and error branches — auth failure routes through the `authenticate` portal-login flow | +| `EMBED.md` | Conditional handoff contract to `formio-form` (form URL, framework rules: unnamed framework → `formio-form`; Angular-explicit → `formio-angular`) | + +No YAML frontmatter on the step docs (repo convention for non-SKILL.md docs). + +SCHEMA gets no step doc: the step is "invoke `formio-schema` with the confirmed form type and the user's field requirements" — a delegation statement in `SKILL.md`, not a script. Writing a SCHEMA.md would invite duplicating component guidance that must live only in `formio-schema`. + +### D2: INTENT is one batched interview capturing form type AND embed intent + +Same pattern as `formio-application`'s INTENT step: one `AskUserQuestion` call, two questions. + +- **Form type** — infer from phrasing when unambiguous ("multi-page form" ⇒ wizard, "pdf form" ⇒ pdf) and present the inference as the recommended option to confirm; ask open when ambiguous. `FORM_TYPES.md` holds the distinguishing signals. +- **Embed intent** — captured up front so a standalone "make me a survey" stays fast. The EMBED step fires ONLY on an explicit yes; "no" and "unsure/later" both mean the flow ends at SAVE with the form URL in hand (the user can invoke `formio-form` later). + +Alternative — ask embed intent after SAVE — rejected: it adds a second interview round-trip to every run, and the answer changes nothing before EMBED anyway; batching keeps the fast path fast. + +### D3: Description routing — the form-vs-resource boundary is stated verbatim + +Three-clause template: + +- **Capability clause**: orchestrator that builds a single Form.io form end to end — determines form type (webform, wizard, or PDF form), delegates schema authoring, saves via the MCP server, and optionally hands off to embedding. +- **Trigger clause** (`Use when the user asks to …`): "build a form", "create a form", "I would like a new form", "multi-page form", "build a wizard", "create a survey / contact form / intake form / registration form / questionnaire", "pdf form" — single-form creation intents. +- **Boundary rule**, stated explicitly in the description: "build a form to collect X" (standalone form) = this skill; "track X / manage X / build an app around X" (data model, CRUD, resources) = `formio-application` / `formio-resource-planner`. +- **Negative clause** (`Not for:`): embedding an EXISTING form (see `formio-form`); building a whole app, portal, or tracker (see `formio-application`); designing resources/data models/permissions (see `formio-resource-planner`); raw form JSON schema lookups without the build-and-save flow (see `formio-schema`); Form.io REST endpoint lookups (see `formio-api`). + +Reverse pointers: `formio-application` and `formio-resource-planner` descriptions gain `Not for:` pointers at `formio-form-builder` for standalone single-form creation; `formio-form`'s description gains one for create-a-new-form requests (it stays embed-only). `formio-schema` gets none (spec-forbidden string, see Context). + +### D4: Inbound handoffs are requirements on the siblings, not prose in this skill + +Two siblings route TO `formio-form-builder`: + +- `formio-application`: when the user asks for a standalone FORM (not a resource, not a data model, not an app) — an ADDED requirement on `formio-application-skill`. +- `formio-form`: when an embed request reveals the form does not exist yet, or the request needs form-type determination before embedding — an ADDED requirement on `formio-form-skill`. + +Encoding these on the sibling capabilities (rather than only describing them in `formio-form-builder`'s docs) keeps each skill's spec the single source of truth for its own routing behavior, matching how the library's other reverse pointers are specified. + +### D5: SAVE uses `form_create` with the canonical auth story + +`SAVE.md` scripts: an approval gate showing the form title/path/type and target project before the call; the `form_create` invocation; on success, confirming the saved form path and full form URL (`{FORMIO_PROJECT_URL}/{formPath}`) back to the user; on auth error, routing through the `authenticate` portal-login flow (browser opens, JWT captured, attached as `x-jwt-token` via `formioFetch` — never PKCE, never API keys) and retrying. `SKILL.md` carries the `## MCP Tool Preference` section naming `form_create`, `form_get`, and `authenticate` with the canonical portal-login JWT paragraph. Terminology stays strict: `baseUrl` ⇒ `FORMIO_BASE_URL` only; `projectUrl` ⇒ `FORMIO_PROJECT_URL` only. + +### D6: Structural tests must be substring-collision-safe + +`formio-form-builder` contains the substring `formio-form`. Naive `toContain('formio-form')` assertions cannot distinguish a pointer at the embed skill from a pointer at this skill. The structural tests (and the sibling reverse-pointer assertions) therefore match backtick-delimited names — `` `formio-form` `` vs `` `formio-form-builder` `` — or an equivalent word-boundary regex. This also protects the existing `formio-form` structural test suite, whose sibling-pointer assertions would otherwise pass vacuously against `formio-form-builder` mentions. + +Test file: `packages/skill-tests/src/formio-form-builder/skill-structure.test.ts`, following the `formio-form` pattern: frontmatter (`name: formio-form-builder`, non-empty description), three clauses with all five Not-for names, boundary rule present, the four step docs exist / non-empty / no frontmatter, MCP Tool Preference section (`form_create`, `form_get`, `authenticate`, `x-jwt-token`), dev symlink resolution, and sibling reverse-pointer assertions for `formio-application`, `formio-resource-planner`, and `formio-form`. + +### D7: Plugin packaging updated in-change + +`plugin-build.test.ts` assertion 1.3 gains `formio-form-builder` in its inclusion list, and the `claude-plugin-packaging` spec's "Plugin bundles the skills library" requirement adds the skill to the bundled set. The build script copies `plugin/skills/` wholesale, so no build-script change is expected — the test update is the guard. This is done in this change deliberately: `add-formio-form-skill` shipped its skill against a stale exclusion assertion and had to fix it after the fact. + +## Risks / Trade-offs + +- [Trigger overlap with `formio-application` on ambiguous phrasing ("I need something to collect maintenance requests")] → The boundary rule is stated verbatim in BOTH descriptions (this skill's positive form and the siblings' `Not for:` pointers); when genuinely ambiguous, the INTENT interview surfaces the distinction ("just a form, or an app around the data?") before any work happens. +- [Substring collision `formio-form` / `formio-form-builder` weakens description-based routing and tests] → Backtick-delimited name matching in all new assertions (D6); descriptions always write the full backticked skill name. +- [`formio-form-skill` base spec is in an unarchived change; archive order could conflict] → This change's delta copies the pending version's full requirement text and layers the addition on top; archiving `add-formio-form-skill` first keeps the merge clean. Noted in tasks. +- [PDF form creation may not be fully achievable via `form_create` alone (PDF upload is a hosted-server concern)] → `FORM_TYPES.md` documents the capability honestly: choosing "pdf" sets `display: "pdf"` and notes the PDF-document upload prerequisite; the skill never promises server-side PDF conversion. +- [help.form.io source docs drift after authoring] → Docs are cited as authoring sources in the change, not linked as living dependencies inside the skill; structural tests guard the contract that matters (layout, routing, tool preference). + +## Open Questions + +None — layout, routing boundaries, handoff contracts, and test scope are settled above. diff --git a/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/proposal.md b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/proposal.md new file mode 100644 index 0000000..0648aa0 --- /dev/null +++ b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/proposal.md @@ -0,0 +1,40 @@ +# Add `formio-form-builder` skill + +## Why + +The skills library has no orchestrator for the single most common Form.io request: "build me a form." Today a standalone form-creation intent ("create a survey", "I need a contact form", "build a multi-page wizard") has no owner — it mis-routes to `formio-application` (full app orchestration), `formio-resource-planner` (data-model planning), or `formio-schema` (a JSON reference, not a pipeline). Nothing determines the form type (webform vs wizard vs PDF form), authors the definition, persists it via `form_create`, and optionally hands off to embedding. + +## What Changes + +- New orchestrator skill at `plugin/skills/formio-form-builder/` — same tier and shape as `formio-application` — owning the full "build me a form" pipeline: INTENT → SCHEMA → SAVE → EMBED (conditional). + - **INTENT** — one batched `AskUserQuestion` interview (mirroring `formio-application`'s INTENT step) capturing (a) form type: `webform` (single-page), `wizard` (multi-page), or `pdf` form — inferred from phrasing when unambiguous and confirmed, asked when ambiguous; and (b) embed intent: embed in an application afterward, or just create in the Form.io project. Embed handoff fires ONLY on an explicit yes. + - **SCHEMA** — delegate component selection and full form JSON authoring to the existing `formio-schema` skill for the chosen form type. No component/schema documentation is duplicated; `formio-schema` is referenced by name only. + - **SAVE** — persist the definition via the MCP server's `form_create` tool (which already instructs use of `formio-schema`; no MCP tool changes). Auth errors route through the `authenticate` portal-login flow (`x-jwt-token`; never PKCE or API keys). Confirm the saved form path/URL back to the user. + - **EMBED** (conditional) — only on an explicit yes at INTENT: hand off to `formio-form` to embed the saved form by its form URL; Angular-explicit requests route through `formio-angular` per existing rules. +- Reference docs (no YAML frontmatter) mirroring `formio-application`'s INTENT.md/IMPORT.md pattern: a form-types reference authored from the official help.form.io docs (form types, PDF forms, nested wizard workflows), the INTENT interview script, the SAVE `form_create` invocation + error handling, and the EMBED handoff contract. +- Routing boundaries encoded in the three-clause description. Sharpest boundary — form vs resource/data model: "build a form to collect X" (standalone form) = `formio-form-builder`; "track X / manage X / app around X" (data model, CRUD, resources) = `formio-application` / `formio-resource-planner`. The description states this rule explicitly. +- Reverse `Not for:` pointers added to sibling descriptions: `formio-application`, `formio-resource-planner`, and `formio-form` each point standalone create-a-new-form requests at `formio-form-builder`. `formio-form` also gains the inbound handoff: when an embed request reveals the form does not exist yet, route to `formio-form-builder` first. `formio-schema`'s description is NOT touched — its spec forbids the string `formio-form` in its description; `formio-form-builder` references `formio-schema` one-way. +- The skill SHIPS in the `@formio/ai` plugin bundle — the `claude-plugin-packaging` spec's bundled-skills list and the `packages/mcp-server/src/__tests__/plugin-build.test.ts` inclusion assertions are updated in this change (the add-formio-form-skill change hit a stale exclusion here; not repeated). +- Dev symlink `.claude/skills/formio-form-builder` like the other orchestrators. +- Structural tests at `packages/skill-tests/src/formio-form-builder/` following the `formio-form` skill-structure.test.ts pattern (frontmatter, three-clause description with all Not-for names, reference docs present/no-frontmatter, MCP Tool Preference section, sibling reverse-pointer assertions). Flow behavior itself is prose/orchestration — no renderer behavior tests. + +## Capabilities + +### New Capabilities + +- `formio-form-builder-skill`: The `plugin/skills/formio-form-builder/` orchestrator — directory layout and dev symlink, three-clause description with single-form-creation triggers and the form-vs-resource boundary rule, the four-step INTENT/SCHEMA/SAVE/EMBED flow with its reference docs, no-duplication constraints (defer to `formio-schema` and `formio-form` by name), MCP Tool Preference (`form_create` / `form_get` / `authenticate`), strict URL terminology, and structural tests. +- `formio-resource-planner-skill`: New (narrow) spec for the existing `formio-resource-planner` skill's routing boundary — its description gains a `Not for:` pointer at `formio-form-builder` for standalone single-form creation requests. (The planner has no spec today; this capability starts with the routing requirement only.) + +### Modified Capabilities + +- `formio-application-skill`: description's `Not for:` clauses additionally point standalone single-form creation requests ("build/create a form", surveys, contact forms) at `formio-form-builder`; the orchestrator hands off to `formio-form-builder` when the user asks to create a standalone form rather than a resource/data model or app. +- `formio-form-skill`: description gains a `Not for:` pointer at `formio-form-builder` for create-a-new-form requests (`formio-form` stays embed-only); the skill routes to `formio-form-builder` when the form to embed does not exist yet or the request needs form-type determination. (Base spec currently lives in the unarchived `add-formio-form-skill` change; this delta layers on top of it.) +- `claude-plugin-packaging`: the plugin bundle SHALL include the `formio-form-builder` skill; build/smoke test inclusion assertions updated accordingly. + +## Impact + +- New: `plugin/skills/formio-form-builder/SKILL.md` + reference docs; `.claude/skills/formio-form-builder` symlink. +- New: `packages/skill-tests/src/formio-form-builder/skill-structure.test.ts`. +- Modified: `plugin/skills/formio-application/SKILL.md`, `plugin/skills/formio-resource-planner/SKILL.md`, `plugin/skills/formio-form/SKILL.md` (description clauses + handoff prose only); `packages/mcp-server/src/__tests__/plugin-build.test.ts` (inclusion assertion). +- No MCP server changes — `form_create`, `form_get`, and `authenticate` already exist and `form_create` already instructs use of `formio-schema`. +- Source material (authoring inputs, not runtime dependencies): help.form.io Form Types, PDF Forms, and Nested Wizard Workflow docs. diff --git a/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/claude-plugin-packaging/spec.md b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/claude-plugin-packaging/spec.md new file mode 100644 index 0000000..26a0ab2 --- /dev/null +++ b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/claude-plugin-packaging/spec.md @@ -0,0 +1,21 @@ +## MODIFIED Requirements + +### Requirement: Plugin bundles the skills library + +The plugin source tree SHALL include, and the build SHALL copy to `dist/plugin/skills/`, the full `formio-api` router skill, every `formio-api-` capability-group skill, and the `formio-schema`, `formio-resource-planner`, `formio-form`, and `formio-form-builder` skills. (`formio-form` is the `@formio/js` embed skill; `formio-form-builder` is the build-a-form orchestrator introduced by the `formio-form-builder-skill` capability.) The plugin build test's skill-inclusion assertions (`packages/mcp-server/src/__tests__/plugin-build.test.ts`) SHALL assert `formio-form-builder` is present in the bundled `skills/` directory. + +#### Scenario: Installed plugin exposes all skills + +- **WHEN** a user installs `@formio/ai` in a Claude Code project +- **THEN** Claude Code discovers every `formio-api`, `formio-schema`, `formio-resource-planner`, `formio-form`, and `formio-form-builder` skill from the plugin's `skills/` directory + +#### Scenario: Bundled formio-form-builder is the orchestrator skill + +- **WHEN** the bundled `skills/formio-form-builder/SKILL.md` is inspected +- **THEN** its frontmatter `name` is `formio-form-builder` and its description claims single-form creation triggers + +#### Scenario: Build test asserts inclusion, not a stale exclusion + +- **WHEN** the plugin build test suite runs against a built `dist/plugin/` +- **THEN** it asserts `formio-form-builder` is present in `dist/plugin/skills/` +- **AND** no assertion excludes `formio-form-builder` from the bundle diff --git a/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-application-skill/spec.md b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-application-skill/spec.md new file mode 100644 index 0000000..0e24561 --- /dev/null +++ b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-application-skill/spec.md @@ -0,0 +1,75 @@ +## MODIFIED Requirements + +### Requirement: formio-application description claims plain-language triggers and names the framework skills + +The `formio-application` `SKILL.md` frontmatter `description` SHALL claim plain-language build-an-app and extend-an-app triggers WITHOUT requiring the user to know any UI framework or Form.io terminology. Example triggers the description MUST claim include: + +- Build-new: "build me an app", "create a CRM", "I need a tool to track X", "spin up a system for Y", bare domain archetypes ("task manager", "help desk"). +- Extend-existing: "also track X", "add a way to see Y", "each Z should have a list of W", "let users also submit V". + +The description MUST state that this skill is the library's default build-an-app entry point; that it picks a UI framework automatically when only one is installed and asks the user when multiple are installed; that the user does NOT need to mention "Angular", "React", "framework", "resource", or "NgModule". + +The description MUST include `Not for:` clauses pointing at: + +- `formio-angular` for framework-explicit Angular requests. +- `formio-angular-resources` for framework-explicit Angular extension requests. +- `formio-resource-planner` for data-model-only planning requests without building an app. +- `formio-api` for endpoint lookups. +- `formio-form` for embedding or rendering a single form in an existing page or application (no app build or orchestration). +- `formio-form-builder` for standalone single-form creation requests — "build/create a form", surveys, contact forms, intake forms, registration forms, questionnaires, wizards, PDF forms — where the user wants one form, not a data model, resources, or an app around the data. + +The description MUST also state that the skill writes a `.mcp.json` file in the workspace root as part of the flow (so users know a file will be created and why) and that the flow pauses for a Claude Code restart after the `.mcp.json` write. + +#### Scenario: formio-application claims generic build-new triggers + +- **WHEN** the user says "build me a tool to track maintenance requests" (no framework or Form.io terminology) +- **THEN** the `formio-application` skill activates +- **AND** neither `formio-angular` nor `formio-angular-resources` activates + +#### Scenario: formio-application claims generic extend triggers + +- **WHEN** the user says "also let customers leave reviews on products" in a workspace that already has a framework app wired +- **THEN** the `formio-application` skill activates +- **AND** `formio-angular-resources` does not activate directly + +#### Scenario: Framework-explicit phrasing does NOT route through formio-application + +- **WHEN** the user says "build it in Angular" or "add an Angular module for X" +- **THEN** `formio-angular` (or `formio-angular-resources`) activates directly +- **AND** `formio-application` does not activate + +#### Scenario: Embed-a-form phrasing does NOT route through formio-application + +- **WHEN** the user says "embed this form in my existing site" or "render this form on my page" (no app build requested) +- **THEN** `formio-form` activates +- **AND** `formio-application` does not activate +- **AND** the `formio-application` description's `Not for:` clauses contain the literal substring `formio-form` + +#### Scenario: Standalone form-creation phrasing does NOT route through formio-application + +- **WHEN** the user says "build me a form to collect customer feedback" or "create a survey" (a standalone form, not an app or data model) +- **THEN** `formio-form-builder` activates +- **AND** `formio-application` does not activate +- **AND** the `formio-application` description's `Not for:` clauses name the backtick-delimited `` `formio-form-builder` `` + +#### Scenario: Description mentions the .mcp.json write + +- **WHEN** the `formio-application` `SKILL.md` frontmatter is inspected +- **THEN** its `description` contains the literal substring `.mcp.json` + +#### Scenario: Description mentions the restart pause + +- **WHEN** the `formio-application` `SKILL.md` frontmatter is inspected +- **THEN** its `description` mentions restarting Claude Code (or an equivalent "pause and restart" phrasing) so the user knows the flow is multi-invocation + +## ADDED Requirements + +### Requirement: formio-application hands standalone form creation off to formio-form-builder + +When, during or at the start of an orchestration, the user's request turns out to be a standalone FORM — one form to collect responses, not a resource, not a data model, not an app — the `formio-application` skill SHALL hand off to the `formio-form-builder` skill rather than running the planner/import pipeline. A standalone form that may later be embedded in an application still belongs to `formio-form-builder` (its own flow captures embed intent). + +#### Scenario: Mid-orchestration standalone form request hands off + +- **WHEN** `formio-application` is active and the user's clarified intent is a single standalone form (e.g., "actually I just need a feedback form, not a whole app") +- **THEN** `formio-application` hands off to `formio-form-builder` +- **AND** the planner/import pipeline does not run for that request diff --git a/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-form-builder-skill/spec.md b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-form-builder-skill/spec.md new file mode 100644 index 0000000..90d6f07 --- /dev/null +++ b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-form-builder-skill/spec.md @@ -0,0 +1,169 @@ +## ADDED Requirements + +### Requirement: New skill `formio-form-builder` exists as the library's build-a-form orchestrator + +The skills library SHALL contain a new orchestrator skill at `plugin/skills/formio-form-builder/SKILL.md` with frontmatter `name: formio-form-builder`. The skill directory SHALL follow the `formio-application` orchestrator layout — root-level uppercase step docs, NOT a `references/` directory — containing exactly the following step docs, none of which begin with a YAML frontmatter block: + +- `FORM_TYPES.md` — the form-type reference (webform vs wizard vs PDF form) +- `INTENT.md` — the batched intent interview script +- `SAVE.md` — the `form_create` invocation and error handling +- `EMBED.md` — the conditional embed handoff contract + +A symlink `.claude/skills/formio-form-builder` SHALL exist and resolve to `plugin/skills/formio-form-builder/`. + +#### Scenario: formio-form-builder directory layout + +- **WHEN** the repository is inspected +- **THEN** `plugin/skills/formio-form-builder/SKILL.md` exists with frontmatter `name: formio-form-builder` and a non-empty `description` +- **AND** `FORM_TYPES.md`, `INTENT.md`, `SAVE.md`, and `EMBED.md` exist in the skill directory and are non-empty +- **AND** no step doc's first line is `---` +- **AND** the skill directory contains no `references/` directory +- **AND** `.claude/skills/formio-form-builder` resolves to `plugin/skills/formio-form-builder/` + +### Requirement: formio-form-builder description uses the three-clause template and states the form-vs-resource boundary + +The `formio-form-builder` `SKILL.md` frontmatter `description` SHALL contain three clauses: + +1. A capability statement: an orchestrator that builds a single Form.io form end to end — determines the form type (webform, wizard, or PDF form), delegates schema authoring, saves the form into the user's Form.io project via the MCP server, and optionally hands off to embedding. +2. A trigger clause beginning with the substring `Use when the user asks to` claiming single-form creation intents. Example triggers the description MUST claim include: "build a form", "create a form", "I would like a new form", "multi-page form", "build a wizard", "create a survey", "contact form", "intake form", "registration form", "questionnaire", "pdf form". +3. A negative-trigger clause beginning with the substring `Not for:` that names ALL of: `formio-form` (embedding an EXISTING form), `formio-application` (building a whole app, portal, or tracker), `formio-resource-planner` (designing resources, data models, or permissions), `formio-schema` (raw form JSON schema lookups without the build-and-save flow), and `formio-api` (Form.io REST endpoint lookups). + +The description SHALL additionally state the form-vs-resource boundary rule explicitly: "build a form to collect X" (a standalone form) belongs to this skill, while "track X / manage X / build an app around X" (a data model, CRUD, resources) belongs to `formio-application` / `formio-resource-planner`. + +#### Scenario: formio-form-builder claims single-form creation phrasing + +- **WHEN** the user says "build me a form to collect customer feedback" or "create a multi-page registration wizard" +- **THEN** the `formio-form-builder` skill activates +- **AND** neither `formio-application` nor `formio-resource-planner` activates + +#### Scenario: App and data-model phrasing does NOT route through formio-form-builder + +- **WHEN** the user says "build me an app to track maintenance requests" or "model customers and orders" +- **THEN** `formio-application` (or `formio-resource-planner`) activates +- **AND** `formio-form-builder` does not activate + +#### Scenario: Negative clause names every sibling + +- **WHEN** the `formio-form-builder` `SKILL.md` frontmatter is inspected +- **THEN** its `description` contains a `Not for:` clause containing the backtick-delimited names `` `formio-form` ``, `` `formio-application` ``, `` `formio-resource-planner` ``, `` `formio-schema` ``, and `` `formio-api` `` + +#### Scenario: Boundary rule stated verbatim + +- **WHEN** the `formio-form-builder` `SKILL.md` frontmatter is inspected +- **THEN** its `description` states that standalone form-collection requests belong to this skill and data-model / app-around-data requests belong to `formio-application` / `formio-resource-planner` + +### Requirement: INTENT runs a single batched interview capturing form type and embed intent + +`INTENT.md` SHALL script a single batched `AskUserQuestion` interview (one call, mirroring `formio-application`'s INTENT step) that captures: + +1. **Form type** — `webform` (single-page form), `wizard` (multi-page form), or `pdf` form. The script SHALL instruct the agent to infer the type from phrasing when unambiguous (e.g., "multi-page form" ⇒ wizard, "pdf form" ⇒ pdf) and present the inference as the recommended option to confirm, and to ask openly when ambiguous. The distinguishing signals SHALL be sourced from `FORM_TYPES.md`, referenced by file path. +2. **Embed intent** — whether the user wants the form embedded in an application afterward, or only created in their Form.io project. The EMBED step SHALL fire ONLY on an explicit yes; any other answer ends the flow at SAVE. + +#### Scenario: One batched question, two intents + +- **WHEN** `INTENT.md` is inspected +- **THEN** it scripts a single `AskUserQuestion` call capturing both the form type and the embed intent +- **AND** it instructs inferring the form type from unambiguous phrasing and confirming, asking only when ambiguous + +#### Scenario: Standalone request stays fast + +- **WHEN** the user asks "make me a survey" and answers that no embedding is wanted +- **THEN** the flow runs INTENT → SCHEMA → SAVE and ends after confirming the saved form URL +- **AND** the EMBED step does not run + +#### Scenario: Embed fires only on explicit yes + +- **WHEN** the user's embed answer is anything other than an explicit yes +- **THEN** the EMBED handoff does not fire + +### Requirement: FORM_TYPES.md documents the three form types from the official docs + +`FORM_TYPES.md` SHALL document, for each of the three Form.io form types — webform (single-page form), wizard (multi-page form), and PDF form — what it is, what it can do, when to choose it, and the phrasing signals the INTENT step uses to distinguish them. The wizard section SHALL cover nested wizard workflows (child wizards for complex multi-page flows). The PDF form section SHALL state the PDF-document prerequisite (a PDF form renders over an uploaded/hosted PDF document) rather than promising server-side PDF conversion. Content SHALL be authored from the official help.form.io documentation (form types, PDF forms, nested wizard workflow). + +#### Scenario: All three form types covered + +- **WHEN** `FORM_TYPES.md` is inspected +- **THEN** webform, wizard, and PDF form are each documented with capabilities, when-to-choose guidance, and INTENT distinguishing signals + +#### Scenario: Nested wizards covered + +- **WHEN** `FORM_TYPES.md` is inspected +- **THEN** the wizard section documents nested/child wizard workflows + +### Requirement: SCHEMA delegates entirely to formio-schema with no duplication + +The SCHEMA step SHALL delegate component selection and authoring of the complete form JSON definition for the requested form type to the `formio-schema` skill, referenced by name. No `formio-form-builder` document SHALL duplicate component or schema documentation (component JSON shapes, component property tables, or schema authoring guidance) — those live only in `formio-schema`. Likewise, embed guidance SHALL NOT be duplicated — `EMBED.md` references the `formio-form` skill by name for all embedding mechanics. + +#### Scenario: Schema authoring routes through formio-schema + +- **WHEN** the flow reaches the SCHEMA step +- **THEN** the agent invokes the `formio-schema` skill to select components and author the form JSON for the confirmed form type + +#### Scenario: No duplicated shapes + +- **WHEN** any `formio-form-builder` document is inspected +- **THEN** it contains no component JSON shape documentation and no embed mechanics, referencing `formio-schema` and `formio-form` by name instead + +### Requirement: SAVE persists via form_create and handles auth through the portal-login flow + +`SAVE.md` SHALL script the SAVE step: an approval gate summarizing the form (title, path, type) and target project before the call; invoking the MCP server's `form_create` tool with the authored definition; on success, confirming the saved form path and full form URL (`{FORMIO_PROJECT_URL}/{formPath}`) back to the user. On an authentication error, the script SHALL direct the flow through the `authenticate` portal-login flow — the browser-based login that captures a JWT attached as `x-jwt-token` — and retry; PKCE and API keys SHALL NOT be used or mentioned as alternatives. No MCP server changes are required — `form_create` already exists and already instructs use of `formio-schema`. + +#### Scenario: Saved form confirmed by URL + +- **WHEN** `form_create` succeeds +- **THEN** the user is shown the saved form's path and its full form URL under `FORMIO_PROJECT_URL` + +#### Scenario: Auth error routes through authenticate + +- **WHEN** `form_create` returns an authentication error +- **THEN** `SAVE.md` directs the agent through the `authenticate` portal-login flow (`x-jwt-token`) and retries the save +- **AND** neither PKCE nor API keys appear as an auth mechanism in any `formio-form-builder` document + +### Requirement: EMBED conditionally hands off to formio-form + +`EMBED.md` SHALL define the conditional handoff contract, executed ONLY when the user answered an explicit yes to embed intent at INTENT: hand off to the `formio-form` skill to embed the saved form by its form URL in the user's application. When no framework was named, `formio-form` handles the embedding; Angular-explicit requests route through `formio-angular` per the library's existing routing rules. The handoff SHALL pass at minimum the saved form URL. + +#### Scenario: Embed handoff on yes + +- **WHEN** the user answered yes to embed intent and SAVE succeeded +- **THEN** the flow hands off to `formio-form` with the saved form URL + +#### Scenario: Angular-explicit embed routes to formio-angular + +- **WHEN** the user answered yes to embed intent and explicitly named Angular or `@formio/angular` +- **THEN** the handoff routes through `formio-angular` instead of `formio-form` + +### Requirement: SKILL.md includes an MCP Tool Preference section + +`plugin/skills/formio-form-builder/SKILL.md` SHALL include a `## MCP Tool Preference` section instructing Claude to prefer the MCP server's first-party tools — `form_create`, `form_get`, and `authenticate` — over ad-hoc HTTP requests, and stating that authentication uses the browser-based portal-login flow that captures a JWT attached as `x-jwt-token` on every request (never PKCE or API keys). + +#### Scenario: MCP Tool Preference present + +- **WHEN** `plugin/skills/formio-form-builder/SKILL.md` is inspected +- **THEN** it contains a `## MCP Tool Preference` heading +- **AND** the section names `form_create`, `form_get`, and `authenticate` +- **AND** it contains the canonical portal-login JWT auth guidance (`x-jwt-token`, no PKCE, no API keys) + +### Requirement: Terminology for URLs is strict + +Across all `formio-form-builder` documents, `baseUrl`/`base_url` SHALL refer only to `FORMIO_BASE_URL` and `projectUrl`/`project_url` SHALL refer only to `FORMIO_PROJECT_URL`. + +#### Scenario: URL terminology consistent + +- **WHEN** any `formio-form-builder` document mentions `baseUrl` or `projectUrl` +- **THEN** the usage is consistent with `FORMIO_BASE_URL` / `FORMIO_PROJECT_URL` respectively + +### Requirement: Structural tests verify the authoring contract with substring-safe matching + +The repository SHALL contain Vitest tests under `packages/skill-tests/src/formio-form-builder/` following the `formio-form` skill-structure.test.ts pattern, asserting: the frontmatter (`name: formio-form-builder`, non-empty description); the three-clause description including all five `Not for:` names and the form-vs-resource boundary rule; the four step docs present, non-empty, and frontmatter-free; the `## MCP Tool Preference` section naming `form_create`, `form_get`, `authenticate`, and `x-jwt-token`; the dev symlink resolution; and the sibling reverse pointers (`formio-application`, `formio-resource-planner`, and `formio-form` descriptions each name `formio-form-builder` in a `Not for:` clause). Because `formio-form-builder` contains the substring `formio-form`, every assertion distinguishing the two names SHALL match backtick-delimited names (`` `formio-form` `` vs `` `formio-form-builder` ``) or an equivalent word-boundary pattern — plain substring matching SHALL NOT be used where the two names could be confused. Flow behavior itself is prose/orchestration; no renderer behavior tests are required. + +#### Scenario: Structural test suite exists and passes + +- **WHEN** `pnpm test` runs +- **THEN** `packages/skill-tests/src/formio-form-builder/` contains structural tests covering the assertions above +- **AND** all tests pass + +#### Scenario: Name matching is substring-safe + +- **WHEN** a structural assertion distinguishes `formio-form` from `formio-form-builder` +- **THEN** it matches backtick-delimited names or a word-boundary pattern, not a plain substring diff --git a/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-form-skill/spec.md b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-form-skill/spec.md new file mode 100644 index 0000000..0ad043e --- /dev/null +++ b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-form-skill/spec.md @@ -0,0 +1,49 @@ +## MODIFIED Requirements + +### Requirement: formio-form description uses the three-clause template and claims framework-agnostic embed triggers + +The `formio-form` `SKILL.md` frontmatter `description` SHALL contain three clauses: + +1. A capability statement naming the Vanilla JS renderer `@formio/js` and asserting the skill covers embedding forms in any web application — rendering by URL or JSON, submission pre-fill, JavaScript control, renderer options, conditional logic, calculated values, and JSON Logic validation. +2. A trigger clause beginning with the substring `Use when the user asks to` claiming framework-agnostic embed phrasing. Example triggers the description MUST claim include: "embed a form", "render a form", "add this form to my page/site/app", "pre-fill a form", "show or hide a field based on another field", "calculate a field value", "add custom validation to a field", "conditional wizard". +3. A negative-trigger clause beginning with the substring `Not for:` that names ALL of: `formio-angular` (user explicitly names Angular or `@formio/angular`), `formio-application` (building a whole app, portal, or tracker around data), `formio-resource-planner` (designing the data model), `formio-api` (REST endpoint lookups), `formio-sdk` (raw SDK/Utils API reference beyond embedding), and `formio-form-builder` (creating a NEW form — `formio-form` stays embed-only). + +#### Scenario: formio-form claims framework-agnostic embed phrasing + +- **WHEN** the user says "embed this form in my web page" or "make the model field depend on the make field" with no framework named +- **THEN** the `formio-form` skill activates +- **AND** neither `formio-angular` nor `formio-application` activates + +#### Scenario: Angular-explicit phrasing does NOT route through formio-form + +- **WHEN** the user says "embed this form in my Angular app" or names `@formio/angular` +- **THEN** `formio-angular` activates +- **AND** `formio-form` does not activate + +#### Scenario: Create-a-new-form phrasing does NOT route through formio-form + +- **WHEN** the user says "build a form" or "create a new survey" (no existing form to embed) +- **THEN** `formio-form-builder` activates +- **AND** `formio-form` does not activate + +#### Scenario: Negative clause names every sibling + +- **WHEN** the `formio-form` `SKILL.md` frontmatter is inspected +- **THEN** its `description` contains a `Not for:` clause containing the literal substrings `formio-angular`, `formio-application`, `formio-resource-planner`, `formio-api`, `formio-sdk`, and the backtick-delimited `` `formio-form-builder` `` + +## ADDED Requirements + +### Requirement: formio-form routes not-yet-existing forms to formio-form-builder + +When an embed request reveals that the form to embed does not exist yet in the user's Form.io project, or the user describes a complex form needing form-type determination (webform vs wizard vs PDF form) before any embedding can happen, the `formio-form` skill SHALL route to the `formio-form-builder` skill to create the form first. After `formio-form-builder` saves the form, embedding proceeds via its EMBED handoff back to `formio-form`. + +#### Scenario: Embed request for a nonexistent form + +- **WHEN** `formio-form` is active and the form the user wants to embed does not exist in the project +- **THEN** `formio-form` routes to `formio-form-builder` to create and save the form +- **AND** embedding resumes with the saved form URL + +#### Scenario: Complex form described inside an embed request + +- **WHEN** the user asks to embed a form they describe from scratch (e.g., "embed a multi-step intake wizard on my page" with no existing form) +- **THEN** `formio-form` routes to `formio-form-builder` for form-type determination and creation before any embedding diff --git a/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-resource-planner-skill/spec.md b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-resource-planner-skill/spec.md new file mode 100644 index 0000000..e920f97 --- /dev/null +++ b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/specs/formio-resource-planner-skill/spec.md @@ -0,0 +1,22 @@ +## ADDED Requirements + +### Requirement: formio-resource-planner description routes standalone single-form creation to formio-form-builder + +The `formio-resource-planner` `SKILL.md` frontmatter `description` SHALL contain a `Not for:` pointer at `` `formio-form-builder` `` for standalone single-form creation requests — building or creating one form (a survey, contact form, intake form, registration form, questionnaire, wizard, or PDF form) without designing a data model, resources, or permissions. The planner keeps claiming data-model, resource, and app-planning triggers; only the single-form creation intent is excluded. + +#### Scenario: Planner description names formio-form-builder + +- **WHEN** the `formio-resource-planner` `SKILL.md` frontmatter is inspected +- **THEN** its `description` contains a `Not for:` pointer naming the backtick-delimited `` `formio-form-builder` `` for standalone single-form creation requests + +#### Scenario: Single-form phrasing does NOT route through the planner + +- **WHEN** the user says "build me a contact form" or "create a survey" (no data model, resources, or app requested) +- **THEN** `formio-form-builder` activates +- **AND** `formio-resource-planner` does not activate + +#### Scenario: Data-model phrasing still routes through the planner + +- **WHEN** the user says "design the resources for a task manager" or "plan the schema for my CRM" +- **THEN** `formio-resource-planner` activates +- **AND** `formio-form-builder` does not activate diff --git a/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/tasks.md b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/tasks.md new file mode 100644 index 0000000..a888799 --- /dev/null +++ b/openspec/changes/archive/2026-07-12-add-formio-form-builder-skill/tasks.md @@ -0,0 +1,99 @@ +# Tasks: add `formio-form-builder` skill + +## 1. Skill structure — directory, description, symlink + + +### Red + +- [x] 1.1 Write failing structural tests in `packages/skill-tests/src/formio-form-builder/skill-structure.test.ts` (mirror `packages/skill-tests/src/formio-form/skill-structure.test.ts`): `plugin/skills/formio-form-builder/SKILL.md` exists with frontmatter `name: formio-form-builder` and a non-empty `description`; step docs `FORM_TYPES.md`, `INTENT.md`, `SAVE.md`, `EMBED.md` exist, are non-empty, and do not start with `---`; the skill directory has no `references/` directory; `.claude/skills/formio-form-builder` is a symlink resolving to `plugin/skills/formio-form-builder/` +- [x] 1.2 Write failing tests for the three-clause description: contains `Use when the user asks to`; contains `Not for:` naming all five siblings; states the form-vs-resource boundary rule (standalone form vs data model/app). Use backtick-delimited matching (`` `formio-form` `` vs `` `formio-form-builder` ``) or word-boundary regex — no plain substring where the two names could be confused + +### Green + +- [x] 1.3 Create `plugin/skills/formio-form-builder/SKILL.md` — orchestrator router modeled on `plugin/skills/formio-application/SKILL.md`: three-clause frontmatter description (triggers: "build a form", "create a form", "I would like a new form", "multi-page form", "build a wizard", "create a survey / contact form / intake form / registration form / questionnaire", "pdf form"; boundary rule verbatim; `Not for:` naming `formio-form`, `formio-application`, `formio-resource-planner`, `formio-schema`, `formio-api`), stance, the four-step flow (INTENT → SCHEMA → SAVE → EMBED) with links to the step docs, SCHEMA delegation to `formio-schema` by name, URL terminology section (`baseUrl` = `FORMIO_BASE_URL` only; `projectUrl` = `FORMIO_PROJECT_URL` only), and the `## MCP Tool Preference` section (`form_create`, `form_get`, `authenticate`, canonical portal-login `x-jwt-token` paragraph, no PKCE/API keys) +- [x] 1.4 Create the dev symlink `.claude/skills/formio-form-builder -> ../../plugin/skills/formio-form-builder` and placeholder-free step docs so the layout tests pass (content lands in groups 2–3) + +### Refactor + +- [x] 1.5 Review implementation and refactor as needed + +## 2. Step docs — FORM_TYPES, INTENT + + +### Red + +- [x] 2.1 Write failing tests: `FORM_TYPES.md` documents all three form types (webform, wizard, PDF form) with when-to-choose guidance and INTENT distinguishing signals; the wizard section covers nested/child wizard workflows; the PDF section states the PDF-document prerequisite +- [x] 2.2 Write failing tests: `INTENT.md` scripts a single batched `AskUserQuestion` capturing form type AND embed intent; instructs infer-and-confirm for unambiguous phrasing; references `FORM_TYPES.md` by path; states the EMBED step fires only on an explicit yes + +### Green + +- [x] 2.3 Author `FORM_TYPES.md` from the official docs — https://help.form.io/form-building/form-types, https://help.form.io/form-building/pdf-forms, https://help.form.io/how/nested-form-workflows/nested-wizard-workflow#create-child-wizard — covering what each type is, capabilities, when to choose it, and the phrasing signals INTENT uses (e.g., "multi-page form" ⇒ wizard) +- [x] 2.4 Author `INTENT.md` — the batched interview script mirroring `plugin/skills/formio-application/INTENT.md`'s shape: one `AskUserQuestion` with the form-type question (inferred recommendation first when unambiguous) and the embed-intent question (explicit yes gates EMBED; "no"/"later" ends the flow at SAVE) + +### Refactor + +- [x] 2.5 Review implementation and refactor as needed + +## 3. Step docs — SAVE, EMBED + + +### Red + +- [x] 3.1 Write failing tests: `SAVE.md` scripts the approval gate (form title/path/type + target project), the `form_create` invocation, the saved-form confirmation including the full form URL under `FORMIO_PROJECT_URL`, and the auth-error branch routing through `authenticate` (`x-jwt-token`); no `formio-form-builder` doc mentions PKCE or API keys as auth mechanisms +- [x] 3.2 Write failing tests: `EMBED.md` defines the conditional handoff — only on explicit yes from INTENT — to `formio-form` with the saved form URL, and routes Angular-explicit requests through `formio-angular`; no `formio-form-builder` doc duplicates component JSON shapes or embed mechanics (defers to `formio-schema` / `formio-form` by name) + +### Green + +- [x] 3.3 Author `SAVE.md` — approval gate, `form_create` call (no MCP server changes; the tool already instructs `formio-schema` usage), success confirmation with `{FORMIO_PROJECT_URL}/{formPath}`, and error branches (auth failure → `authenticate` portal-login flow and retry; validation failure surfaced with retry/bail choice) +- [x] 3.4 Author `EMBED.md` — the handoff contract: fires only on the INTENT explicit yes, passes the saved form URL, `formio-form` handles unnamed-framework embedding, Angular-explicit routes through `formio-angular` + +### Refactor + +- [x] 3.5 Review implementation and refactor as needed + +## 4. Sibling reverse pointers and handoffs + + +### Red + +- [x] 4.1 Write failing structural tests (in the group 1 test file): the `formio-application`, `formio-resource-planner`, and `formio-form` `SKILL.md` frontmatter descriptions each contain a `Not for:` clause naming the backtick-delimited `` `formio-form-builder` ``; assert `formio-schema`'s description does NOT contain the substring `formio-form` (the spec-forbidden string — no reverse pointer there) + +### Green + +- [x] 4.2 Update `plugin/skills/formio-application/SKILL.md`: add the `Not for:` pointer at `formio-form-builder` for standalone single-form creation, and add the mid-orchestration handoff note (standalone-form intent hands off to `formio-form-builder` instead of running the planner/import pipeline) +- [x] 4.3 Update `plugin/skills/formio-resource-planner/SKILL.md`: add the `Not for:` pointer at `formio-form-builder` for standalone single-form creation requests +- [x] 4.4 Update `plugin/skills/formio-form/SKILL.md`: add `formio-form-builder` to the `Not for:` clause (create-a-new-form requests; `formio-form` stays embed-only) and add the inbound-handoff guidance (form doesn't exist yet, or complex form needing form-type determination → route to `formio-form-builder` first, embedding resumes with the saved form URL) + +### Refactor + +- [x] 4.5 Review implementation and refactor as needed + +## 5. Plugin packaging + + +### Red + +- [x] 5.1 Update `packages/mcp-server/src/__tests__/plugin-build.test.ts` assertion 1.3 to expect `formio-form-builder` in `dist/plugin/skills/` (fails until the skill directory exists and the plugin is rebuilt); verify no assertion excludes `formio-form-builder` + +### Green + +- [x] 5.2 Run `pnpm build:plugin` and confirm the bundled tree contains `skills/formio-form-builder/` with all step docs; make the updated build test pass (the build script copies `plugin/skills/` wholesale — no script change expected) + +### Refactor + +- [x] 5.3 Review implementation and refactor as needed + +## 6. Definition of Done + + +### Red + +- [x] 6.1 Run the full suite (`pnpm test`) and capture any remaining failures across skill-tests and mcp-server tests — including the pre-existing `formio-form` structural suite, whose sibling-pointer assertions must not be weakened or vacuously satisfied by `formio-form-builder` mentions + +### Green + +- [x] 6.2 Fix any failures; then `pnpm test`, `pnpm lint`, and `pnpm format` all pass clean + +### Refactor + +- [x] 6.3 Review implementation and refactor as needed diff --git a/openspec/changes/archive/2026-07-12-add-formio-form-skill/.openspec.yaml b/openspec/changes/archive/2026-07-12-add-formio-form-skill/.openspec.yaml new file mode 100644 index 0000000..976c122 --- /dev/null +++ b/openspec/changes/archive/2026-07-12-add-formio-form-skill/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-tdd +created: 2026-07-12 diff --git a/openspec/changes/add-formio-form-skill/design.md b/openspec/changes/archive/2026-07-12-add-formio-form-skill/design.md similarity index 100% rename from openspec/changes/add-formio-form-skill/design.md rename to openspec/changes/archive/2026-07-12-add-formio-form-skill/design.md diff --git a/openspec/changes/add-formio-form-skill/proposal.md b/openspec/changes/archive/2026-07-12-add-formio-form-skill/proposal.md similarity index 100% rename from openspec/changes/add-formio-form-skill/proposal.md rename to openspec/changes/archive/2026-07-12-add-formio-form-skill/proposal.md diff --git a/openspec/changes/add-formio-form-skill/specs/claude-plugin-packaging/spec.md b/openspec/changes/archive/2026-07-12-add-formio-form-skill/specs/claude-plugin-packaging/spec.md similarity index 100% rename from openspec/changes/add-formio-form-skill/specs/claude-plugin-packaging/spec.md rename to openspec/changes/archive/2026-07-12-add-formio-form-skill/specs/claude-plugin-packaging/spec.md diff --git a/openspec/changes/add-formio-form-skill/specs/formio-angular-skill/spec.md b/openspec/changes/archive/2026-07-12-add-formio-form-skill/specs/formio-angular-skill/spec.md similarity index 100% rename from openspec/changes/add-formio-form-skill/specs/formio-angular-skill/spec.md rename to openspec/changes/archive/2026-07-12-add-formio-form-skill/specs/formio-angular-skill/spec.md diff --git a/openspec/changes/add-formio-form-skill/specs/formio-application-skill/spec.md b/openspec/changes/archive/2026-07-12-add-formio-form-skill/specs/formio-application-skill/spec.md similarity index 100% rename from openspec/changes/add-formio-form-skill/specs/formio-application-skill/spec.md rename to openspec/changes/archive/2026-07-12-add-formio-form-skill/specs/formio-application-skill/spec.md diff --git a/openspec/changes/add-formio-form-skill/specs/formio-form-skill/spec.md b/openspec/changes/archive/2026-07-12-add-formio-form-skill/specs/formio-form-skill/spec.md similarity index 100% rename from openspec/changes/add-formio-form-skill/specs/formio-form-skill/spec.md rename to openspec/changes/archive/2026-07-12-add-formio-form-skill/specs/formio-form-skill/spec.md diff --git a/openspec/changes/add-formio-form-skill/specs/formio-schema-skill/spec.md b/openspec/changes/archive/2026-07-12-add-formio-form-skill/specs/formio-schema-skill/spec.md similarity index 100% rename from openspec/changes/add-formio-form-skill/specs/formio-schema-skill/spec.md rename to openspec/changes/archive/2026-07-12-add-formio-form-skill/specs/formio-schema-skill/spec.md diff --git a/openspec/changes/add-formio-form-skill/specs/formio-sdk-skill/spec.md b/openspec/changes/archive/2026-07-12-add-formio-form-skill/specs/formio-sdk-skill/spec.md similarity index 100% rename from openspec/changes/add-formio-form-skill/specs/formio-sdk-skill/spec.md rename to openspec/changes/archive/2026-07-12-add-formio-form-skill/specs/formio-sdk-skill/spec.md diff --git a/openspec/changes/add-formio-form-skill/tasks.md b/openspec/changes/archive/2026-07-12-add-formio-form-skill/tasks.md similarity index 100% rename from openspec/changes/add-formio-form-skill/tasks.md rename to openspec/changes/archive/2026-07-12-add-formio-form-skill/tasks.md diff --git a/openspec/specs/claude-plugin-packaging/spec.md b/openspec/specs/claude-plugin-packaging/spec.md index b2f63b0..6aa7f52 100644 --- a/openspec/specs/claude-plugin-packaging/spec.md +++ b/openspec/specs/claude-plugin-packaging/spec.md @@ -29,13 +29,23 @@ The `plugin/package.json` SHALL set `name` to `@formio/ai`, `publishConfig.acces ### Requirement: Plugin bundles the skills library -The plugin source tree SHALL include, and the build SHALL copy to `dist/plugin/skills/`, the full `formio-api` router skill, every `formio-api-` capability-group skill, and the `formio-schema` and `formio-resource-planner` skills. The bundled set SHALL NOT include `formio-form`. +The plugin source tree SHALL include, and the build SHALL copy to `dist/plugin/skills/`, the full `formio-api` router skill, every `formio-api-` capability-group skill, and the `formio-schema`, `formio-resource-planner`, `formio-form`, and `formio-form-builder` skills. (`formio-form` is the `@formio/js` embed skill; `formio-form-builder` is the build-a-form orchestrator introduced by the `formio-form-builder-skill` capability.) The plugin build test's skill-inclusion assertions (`packages/mcp-server/src/__tests__/plugin-build.test.ts`) SHALL assert `formio-form-builder` is present in the bundled `skills/` directory. #### Scenario: Installed plugin exposes all skills - **WHEN** a user installs `@formio/ai` in a Claude Code project -- **THEN** Claude Code discovers every `formio-api`, `formio-schema`, and `formio-resource-planner` skill from the plugin's `skills/` directory -- **AND** no skill named `formio-form` is present in the bundled `skills/` directory +- **THEN** Claude Code discovers every `formio-api`, `formio-schema`, `formio-resource-planner`, `formio-form`, and `formio-form-builder` skill from the plugin's `skills/` directory + +#### Scenario: Bundled formio-form-builder is the orchestrator skill + +- **WHEN** the bundled `skills/formio-form-builder/SKILL.md` is inspected +- **THEN** its frontmatter `name` is `formio-form-builder` and its description claims single-form creation triggers + +#### Scenario: Build test asserts inclusion, not a stale exclusion + +- **WHEN** the plugin build test suite runs against a built `dist/plugin/` +- **THEN** it asserts `formio-form-builder` is present in `dist/plugin/skills/` +- **AND** no assertion excludes `formio-form-builder` from the bundle ### Requirement: Build script produces a self-contained plugin tree @@ -53,7 +63,7 @@ A `scripts/build-plugin.ts` script SHALL clean `dist/plugin/`, copy the `plugin/ ### Requirement: Smoke test validates the built plugin over stdio -A `scripts/test-plugin.ts` script SHALL validate that `dist/plugin/` exists, that `plugin.json` contains required fields (`name`, `version`, `description`, at least one `mcpServers` entry), that required skill directories (at minimum `formio-api` and `formio-schema`) are present, and that spawning the bundled server and sending a JSON-RPC `tools/list` request returns a well-formed response. The smoke test SHALL NOT require `formio-form` to be present. +A `scripts/test-plugin.ts` script SHALL validate that `dist/plugin/` exists, that `plugin.json` contains required fields (`name`, `version`, `description`, at least one `mcpServers` entry), that required skill directories (at minimum `formio-api` and `formio-schema`) are present, and that spawning the bundled server and sending a JSON-RPC `tools/list` request returns a well-formed response. #### Scenario: Smoke test fails when build is missing @@ -69,7 +79,6 @@ A `scripts/test-plugin.ts` script SHALL validate that `dist/plugin/` exists, tha - **WHEN** the smoke test inspects the bundled `skills/` directory - **THEN** it SHALL assert `formio-schema` is present -- **AND** it SHALL NOT assert `formio-form` is present ### Requirement: Plugin ships a README documenting environment variables diff --git a/openspec/specs/formio-angular-skill/spec.md b/openspec/specs/formio-angular-skill/spec.md index 94b938e..c8c681b 100644 --- a/openspec/specs/formio-angular-skill/spec.md +++ b/openspec/specs/formio-angular-skill/spec.md @@ -44,6 +44,8 @@ The description MUST drop all plain-language "build me an app" triggers (those n The description MUST include a `Not for:` clause pointing at `formio-application` for generic build-an-app requests and for framework-agnostic "I want to build X" requests. +The description MUST include a `Not for:` clause pointing at `formio-form` for framework-agnostic embed/render-a-form requests that do not name Angular or `@formio/angular`. + The description MUST continue to include the existing `Not for:` clause pointing at `formio-angular-resources` for add-a-feature-to-an-existing-app requests. #### Scenario: formio-angular only fires on Angular-explicit phrasing @@ -58,6 +60,13 @@ The description MUST continue to include the existing `Not for:` clause pointing - **THEN** the `formio-angular` skill does NOT activate - **AND** `formio-application` activates instead +#### Scenario: formio-angular does not fire on generic embed phrasing + +- **WHEN** the user says "embed this form in my web page" (no mention of Angular) +- **THEN** the `formio-angular` skill does NOT activate +- **AND** `formio-form` activates instead +- **AND** the `formio-angular` description contains a `Not for:` clause with the literal substring `formio-form` + #### Scenario: formio-angular description Not-for clause names the orchestrator - **WHEN** the `formio-angular` `SKILL.md` frontmatter is inspected diff --git a/openspec/specs/formio-application-skill/spec.md b/openspec/specs/formio-application-skill/spec.md index 66b1115..61ba491 100644 --- a/openspec/specs/formio-application-skill/spec.md +++ b/openspec/specs/formio-application-skill/spec.md @@ -35,6 +35,8 @@ The description MUST include `Not for:` clauses pointing at: - `formio-angular-resources` for framework-explicit Angular extension requests. - `formio-resource-planner` for data-model-only planning requests without building an app. - `formio-api` for endpoint lookups. +- `formio-form` for embedding or rendering a single form in an existing page or application (no app build or orchestration). +- `formio-form-builder` for standalone single-form creation requests — "build/create a form", surveys, contact forms, intake forms, registration forms, questionnaires, wizards, PDF forms — where the user wants one form, not a data model, resources, or an app around the data. The description MUST also state that the skill writes a `.mcp.json` file in the workspace root as part of the flow (so users know a file will be created and why) and that the flow pauses for a Claude Code restart after the `.mcp.json` write. @@ -56,6 +58,20 @@ The description MUST also state that the skill writes a `.mcp.json` file in the - **THEN** `formio-angular` (or `formio-angular-resources`) activates directly - **AND** `formio-application` does not activate +#### Scenario: Embed-a-form phrasing does NOT route through formio-application + +- **WHEN** the user says "embed this form in my existing site" or "render this form on my page" (no app build requested) +- **THEN** `formio-form` activates +- **AND** `formio-application` does not activate +- **AND** the `formio-application` description's `Not for:` clauses contain the literal substring `formio-form` + +#### Scenario: Standalone form-creation phrasing does NOT route through formio-application + +- **WHEN** the user says "build me a form to collect customer feedback" or "create a survey" (a standalone form, not an app or data model) +- **THEN** `formio-form-builder` activates +- **AND** `formio-application` does not activate +- **AND** the `formio-application` description's `Not for:` clauses name the backtick-delimited `` `formio-form-builder` `` + #### Scenario: Description mentions the .mcp.json write - **WHEN** the `formio-application` `SKILL.md` frontmatter is inspected @@ -314,3 +330,13 @@ The MCP server SHALL register a new tool named `authenticate` at `packages/mcp-s - **WHEN** `authenticate` succeeds but the subsequent `GET /current` call fails (network error, 404, etc.) - **THEN** the tool still returns `authenticated: true` with no `userEmail` field - **AND** the tool call itself does NOT error + +### Requirement: formio-application hands standalone form creation off to formio-form-builder + +When, during or at the start of an orchestration, the user's request turns out to be a standalone FORM — one form to collect responses, not a resource, not a data model, not an app — the `formio-application` skill SHALL hand off to the `formio-form-builder` skill rather than running the planner/import pipeline. A standalone form that may later be embedded in an application still belongs to `formio-form-builder` (its own flow captures embed intent). + +#### Scenario: Mid-orchestration standalone form request hands off + +- **WHEN** `formio-application` is active and the user's clarified intent is a single standalone form (e.g., "actually I just need a feedback form, not a whole app") +- **THEN** `formio-application` hands off to `formio-form-builder` +- **AND** the planner/import pipeline does not run for that request diff --git a/openspec/specs/formio-form-builder-skill/spec.md b/openspec/specs/formio-form-builder-skill/spec.md new file mode 100644 index 0000000..90d6f07 --- /dev/null +++ b/openspec/specs/formio-form-builder-skill/spec.md @@ -0,0 +1,169 @@ +## ADDED Requirements + +### Requirement: New skill `formio-form-builder` exists as the library's build-a-form orchestrator + +The skills library SHALL contain a new orchestrator skill at `plugin/skills/formio-form-builder/SKILL.md` with frontmatter `name: formio-form-builder`. The skill directory SHALL follow the `formio-application` orchestrator layout — root-level uppercase step docs, NOT a `references/` directory — containing exactly the following step docs, none of which begin with a YAML frontmatter block: + +- `FORM_TYPES.md` — the form-type reference (webform vs wizard vs PDF form) +- `INTENT.md` — the batched intent interview script +- `SAVE.md` — the `form_create` invocation and error handling +- `EMBED.md` — the conditional embed handoff contract + +A symlink `.claude/skills/formio-form-builder` SHALL exist and resolve to `plugin/skills/formio-form-builder/`. + +#### Scenario: formio-form-builder directory layout + +- **WHEN** the repository is inspected +- **THEN** `plugin/skills/formio-form-builder/SKILL.md` exists with frontmatter `name: formio-form-builder` and a non-empty `description` +- **AND** `FORM_TYPES.md`, `INTENT.md`, `SAVE.md`, and `EMBED.md` exist in the skill directory and are non-empty +- **AND** no step doc's first line is `---` +- **AND** the skill directory contains no `references/` directory +- **AND** `.claude/skills/formio-form-builder` resolves to `plugin/skills/formio-form-builder/` + +### Requirement: formio-form-builder description uses the three-clause template and states the form-vs-resource boundary + +The `formio-form-builder` `SKILL.md` frontmatter `description` SHALL contain three clauses: + +1. A capability statement: an orchestrator that builds a single Form.io form end to end — determines the form type (webform, wizard, or PDF form), delegates schema authoring, saves the form into the user's Form.io project via the MCP server, and optionally hands off to embedding. +2. A trigger clause beginning with the substring `Use when the user asks to` claiming single-form creation intents. Example triggers the description MUST claim include: "build a form", "create a form", "I would like a new form", "multi-page form", "build a wizard", "create a survey", "contact form", "intake form", "registration form", "questionnaire", "pdf form". +3. A negative-trigger clause beginning with the substring `Not for:` that names ALL of: `formio-form` (embedding an EXISTING form), `formio-application` (building a whole app, portal, or tracker), `formio-resource-planner` (designing resources, data models, or permissions), `formio-schema` (raw form JSON schema lookups without the build-and-save flow), and `formio-api` (Form.io REST endpoint lookups). + +The description SHALL additionally state the form-vs-resource boundary rule explicitly: "build a form to collect X" (a standalone form) belongs to this skill, while "track X / manage X / build an app around X" (a data model, CRUD, resources) belongs to `formio-application` / `formio-resource-planner`. + +#### Scenario: formio-form-builder claims single-form creation phrasing + +- **WHEN** the user says "build me a form to collect customer feedback" or "create a multi-page registration wizard" +- **THEN** the `formio-form-builder` skill activates +- **AND** neither `formio-application` nor `formio-resource-planner` activates + +#### Scenario: App and data-model phrasing does NOT route through formio-form-builder + +- **WHEN** the user says "build me an app to track maintenance requests" or "model customers and orders" +- **THEN** `formio-application` (or `formio-resource-planner`) activates +- **AND** `formio-form-builder` does not activate + +#### Scenario: Negative clause names every sibling + +- **WHEN** the `formio-form-builder` `SKILL.md` frontmatter is inspected +- **THEN** its `description` contains a `Not for:` clause containing the backtick-delimited names `` `formio-form` ``, `` `formio-application` ``, `` `formio-resource-planner` ``, `` `formio-schema` ``, and `` `formio-api` `` + +#### Scenario: Boundary rule stated verbatim + +- **WHEN** the `formio-form-builder` `SKILL.md` frontmatter is inspected +- **THEN** its `description` states that standalone form-collection requests belong to this skill and data-model / app-around-data requests belong to `formio-application` / `formio-resource-planner` + +### Requirement: INTENT runs a single batched interview capturing form type and embed intent + +`INTENT.md` SHALL script a single batched `AskUserQuestion` interview (one call, mirroring `formio-application`'s INTENT step) that captures: + +1. **Form type** — `webform` (single-page form), `wizard` (multi-page form), or `pdf` form. The script SHALL instruct the agent to infer the type from phrasing when unambiguous (e.g., "multi-page form" ⇒ wizard, "pdf form" ⇒ pdf) and present the inference as the recommended option to confirm, and to ask openly when ambiguous. The distinguishing signals SHALL be sourced from `FORM_TYPES.md`, referenced by file path. +2. **Embed intent** — whether the user wants the form embedded in an application afterward, or only created in their Form.io project. The EMBED step SHALL fire ONLY on an explicit yes; any other answer ends the flow at SAVE. + +#### Scenario: One batched question, two intents + +- **WHEN** `INTENT.md` is inspected +- **THEN** it scripts a single `AskUserQuestion` call capturing both the form type and the embed intent +- **AND** it instructs inferring the form type from unambiguous phrasing and confirming, asking only when ambiguous + +#### Scenario: Standalone request stays fast + +- **WHEN** the user asks "make me a survey" and answers that no embedding is wanted +- **THEN** the flow runs INTENT → SCHEMA → SAVE and ends after confirming the saved form URL +- **AND** the EMBED step does not run + +#### Scenario: Embed fires only on explicit yes + +- **WHEN** the user's embed answer is anything other than an explicit yes +- **THEN** the EMBED handoff does not fire + +### Requirement: FORM_TYPES.md documents the three form types from the official docs + +`FORM_TYPES.md` SHALL document, for each of the three Form.io form types — webform (single-page form), wizard (multi-page form), and PDF form — what it is, what it can do, when to choose it, and the phrasing signals the INTENT step uses to distinguish them. The wizard section SHALL cover nested wizard workflows (child wizards for complex multi-page flows). The PDF form section SHALL state the PDF-document prerequisite (a PDF form renders over an uploaded/hosted PDF document) rather than promising server-side PDF conversion. Content SHALL be authored from the official help.form.io documentation (form types, PDF forms, nested wizard workflow). + +#### Scenario: All three form types covered + +- **WHEN** `FORM_TYPES.md` is inspected +- **THEN** webform, wizard, and PDF form are each documented with capabilities, when-to-choose guidance, and INTENT distinguishing signals + +#### Scenario: Nested wizards covered + +- **WHEN** `FORM_TYPES.md` is inspected +- **THEN** the wizard section documents nested/child wizard workflows + +### Requirement: SCHEMA delegates entirely to formio-schema with no duplication + +The SCHEMA step SHALL delegate component selection and authoring of the complete form JSON definition for the requested form type to the `formio-schema` skill, referenced by name. No `formio-form-builder` document SHALL duplicate component or schema documentation (component JSON shapes, component property tables, or schema authoring guidance) — those live only in `formio-schema`. Likewise, embed guidance SHALL NOT be duplicated — `EMBED.md` references the `formio-form` skill by name for all embedding mechanics. + +#### Scenario: Schema authoring routes through formio-schema + +- **WHEN** the flow reaches the SCHEMA step +- **THEN** the agent invokes the `formio-schema` skill to select components and author the form JSON for the confirmed form type + +#### Scenario: No duplicated shapes + +- **WHEN** any `formio-form-builder` document is inspected +- **THEN** it contains no component JSON shape documentation and no embed mechanics, referencing `formio-schema` and `formio-form` by name instead + +### Requirement: SAVE persists via form_create and handles auth through the portal-login flow + +`SAVE.md` SHALL script the SAVE step: an approval gate summarizing the form (title, path, type) and target project before the call; invoking the MCP server's `form_create` tool with the authored definition; on success, confirming the saved form path and full form URL (`{FORMIO_PROJECT_URL}/{formPath}`) back to the user. On an authentication error, the script SHALL direct the flow through the `authenticate` portal-login flow — the browser-based login that captures a JWT attached as `x-jwt-token` — and retry; PKCE and API keys SHALL NOT be used or mentioned as alternatives. No MCP server changes are required — `form_create` already exists and already instructs use of `formio-schema`. + +#### Scenario: Saved form confirmed by URL + +- **WHEN** `form_create` succeeds +- **THEN** the user is shown the saved form's path and its full form URL under `FORMIO_PROJECT_URL` + +#### Scenario: Auth error routes through authenticate + +- **WHEN** `form_create` returns an authentication error +- **THEN** `SAVE.md` directs the agent through the `authenticate` portal-login flow (`x-jwt-token`) and retries the save +- **AND** neither PKCE nor API keys appear as an auth mechanism in any `formio-form-builder` document + +### Requirement: EMBED conditionally hands off to formio-form + +`EMBED.md` SHALL define the conditional handoff contract, executed ONLY when the user answered an explicit yes to embed intent at INTENT: hand off to the `formio-form` skill to embed the saved form by its form URL in the user's application. When no framework was named, `formio-form` handles the embedding; Angular-explicit requests route through `formio-angular` per the library's existing routing rules. The handoff SHALL pass at minimum the saved form URL. + +#### Scenario: Embed handoff on yes + +- **WHEN** the user answered yes to embed intent and SAVE succeeded +- **THEN** the flow hands off to `formio-form` with the saved form URL + +#### Scenario: Angular-explicit embed routes to formio-angular + +- **WHEN** the user answered yes to embed intent and explicitly named Angular or `@formio/angular` +- **THEN** the handoff routes through `formio-angular` instead of `formio-form` + +### Requirement: SKILL.md includes an MCP Tool Preference section + +`plugin/skills/formio-form-builder/SKILL.md` SHALL include a `## MCP Tool Preference` section instructing Claude to prefer the MCP server's first-party tools — `form_create`, `form_get`, and `authenticate` — over ad-hoc HTTP requests, and stating that authentication uses the browser-based portal-login flow that captures a JWT attached as `x-jwt-token` on every request (never PKCE or API keys). + +#### Scenario: MCP Tool Preference present + +- **WHEN** `plugin/skills/formio-form-builder/SKILL.md` is inspected +- **THEN** it contains a `## MCP Tool Preference` heading +- **AND** the section names `form_create`, `form_get`, and `authenticate` +- **AND** it contains the canonical portal-login JWT auth guidance (`x-jwt-token`, no PKCE, no API keys) + +### Requirement: Terminology for URLs is strict + +Across all `formio-form-builder` documents, `baseUrl`/`base_url` SHALL refer only to `FORMIO_BASE_URL` and `projectUrl`/`project_url` SHALL refer only to `FORMIO_PROJECT_URL`. + +#### Scenario: URL terminology consistent + +- **WHEN** any `formio-form-builder` document mentions `baseUrl` or `projectUrl` +- **THEN** the usage is consistent with `FORMIO_BASE_URL` / `FORMIO_PROJECT_URL` respectively + +### Requirement: Structural tests verify the authoring contract with substring-safe matching + +The repository SHALL contain Vitest tests under `packages/skill-tests/src/formio-form-builder/` following the `formio-form` skill-structure.test.ts pattern, asserting: the frontmatter (`name: formio-form-builder`, non-empty description); the three-clause description including all five `Not for:` names and the form-vs-resource boundary rule; the four step docs present, non-empty, and frontmatter-free; the `## MCP Tool Preference` section naming `form_create`, `form_get`, `authenticate`, and `x-jwt-token`; the dev symlink resolution; and the sibling reverse pointers (`formio-application`, `formio-resource-planner`, and `formio-form` descriptions each name `formio-form-builder` in a `Not for:` clause). Because `formio-form-builder` contains the substring `formio-form`, every assertion distinguishing the two names SHALL match backtick-delimited names (`` `formio-form` `` vs `` `formio-form-builder` ``) or an equivalent word-boundary pattern — plain substring matching SHALL NOT be used where the two names could be confused. Flow behavior itself is prose/orchestration; no renderer behavior tests are required. + +#### Scenario: Structural test suite exists and passes + +- **WHEN** `pnpm test` runs +- **THEN** `packages/skill-tests/src/formio-form-builder/` contains structural tests covering the assertions above +- **AND** all tests pass + +#### Scenario: Name matching is substring-safe + +- **WHEN** a structural assertion distinguishes `formio-form` from `formio-form-builder` +- **THEN** it matches backtick-delimited names or a word-boundary pattern, not a plain substring diff --git a/openspec/specs/formio-form-skill/spec.md b/openspec/specs/formio-form-skill/spec.md new file mode 100644 index 0000000..02a3c27 --- /dev/null +++ b/openspec/specs/formio-form-skill/spec.md @@ -0,0 +1,267 @@ +## ADDED Requirements + +### Requirement: New skill `formio-form` exists as the library's embed-a-form entry point + +The skills library SHALL contain a new skill at `plugin/skills/formio-form/SKILL.md` with frontmatter `name: formio-form`. The skill directory SHALL contain a `references/` directory with exactly the following reference documents, none of which begin with a YAML frontmatter block: + +- `setup.md` — HTML page requirements and library inclusion +- `rendering.md` — rendering by form URL, by form JSON, and with submissions (pre-fill) +- `javascript-api.md` — controlling the form instance with JavaScript (events and methods) +- `options.md` — form renderer options +- `json-logic.md` — JSON Logic primer scoped to renderer evaluation +- `field-logic.md` — component `logic` arrays (triggers and actions) +- `conditionals.md` — simple and JSON Logic conditionals +- `calculated-values.md` — `calculateValue` +- `validation.md` — custom validation via `validate.json` +- `external-data.md` — external data sources and cascading/conditional selects +- `wizards.md` — wizard display, conditional wizards, custom wizard navigation + +A symlink `.claude/skills/formio-form` SHALL exist and resolve to `plugin/skills/formio-form/`. + +#### Scenario: formio-form directory layout + +- **WHEN** the repository is inspected +- **THEN** `plugin/skills/formio-form/SKILL.md` exists with frontmatter `name: formio-form` and a non-empty `description` +- **AND** all eleven reference documents listed above exist under `plugin/skills/formio-form/references/` and are non-empty +- **AND** no reference document's first line is `---` +- **AND** `.claude/skills/formio-form` resolves to `plugin/skills/formio-form/` + +### Requirement: formio-form description uses the three-clause template and claims framework-agnostic embed triggers + +The `formio-form` `SKILL.md` frontmatter `description` SHALL contain three clauses: + +1. A capability statement naming the Vanilla JS renderer `@formio/js` and asserting the skill covers embedding forms in any web application — rendering by URL or JSON, submission pre-fill, JavaScript control, renderer options, conditional logic, calculated values, and JSON Logic validation. +2. A trigger clause beginning with the substring `Use when the user asks to` claiming framework-agnostic embed phrasing. Example triggers the description MUST claim include: "embed a form", "render a form", "add this form to my page/site/app", "pre-fill a form", "show or hide a field based on another field", "calculate a field value", "add custom validation to a field", "conditional wizard". +3. A negative-trigger clause beginning with the substring `Not for:` that names ALL of: `formio-angular` (user explicitly names Angular or `@formio/angular`), `formio-application` (building a whole app, portal, or tracker around data), `formio-resource-planner` (designing the data model), `formio-api` (REST endpoint lookups), `formio-sdk` (raw SDK/Utils API reference beyond embedding), and `formio-form-builder` (creating a NEW form — `formio-form` stays embed-only). + +#### Scenario: formio-form claims framework-agnostic embed phrasing + +- **WHEN** the user says "embed this form in my web page" or "make the model field depend on the make field" with no framework named +- **THEN** the `formio-form` skill activates +- **AND** neither `formio-angular` nor `formio-application` activates + +#### Scenario: Angular-explicit phrasing does NOT route through formio-form + +- **WHEN** the user says "embed this form in my Angular app" or names `@formio/angular` +- **THEN** `formio-angular` activates +- **AND** `formio-form` does not activate + +#### Scenario: Create-a-new-form phrasing does NOT route through formio-form + +- **WHEN** the user says "build a form" or "create a new survey" (no existing form to embed) +- **THEN** `formio-form-builder` activates +- **AND** `formio-form` does not activate + +#### Scenario: Negative clause names every sibling + +- **WHEN** the `formio-form` `SKILL.md` frontmatter is inspected +- **THEN** its `description` contains a `Not for:` clause containing the literal substrings `formio-angular`, `formio-application`, `formio-resource-planner`, `formio-api`, `formio-sdk`, and the backtick-delimited `` `formio-form-builder` `` + +### Requirement: Canonical inclusion modes are CDN and ESM only + +Across `plugin/skills/formio-form/SKILL.md` and every `plugin/skills/formio-form/references/*.md`, form-renderer inclusion SHALL be documented in exactly two canonical modes: + +- CDN: a `