block-creation — Skill instructions
When to invoke
Invoke this skill when the builder expresses intent to create a new block. Trigger phrases include:
- "build me a block that ..."
- "scaffold a block for ..."
- "create a new block named ..."
- "I need a block that does ..."
Do NOT invoke when the builder is asking to:
- Install an existing block into a workspace (that's the SeldonFrame
MCP's
install_*tools). - Modify an existing block (refer to direct file edits).
- Generate UI components or pages (out-of-scope for this slice; future SLICE 4 scope).
Framing — this is code-authoring, not workspace-admin
The scaffold writes files to the builder's repo working tree. It does NOT:
- commit to git
- push to a remote
- install the block into a running workspace
- modify any workspace's database
Those are the builder's follow-up actions (git add / git commit /
git push, then install_block against a workspace). Claude Code
is a code-gen assistant here, not a platform admin.
Workflow
1. Intent → BlockSpec
Claude Code reads the builder's natural-language intent and
constructs a BlockSpec JSON object matching the shape below.
Before constructing the spec, Claude should Read:
- Reference anatomies — the composition-contract shape of two
existing blocks. These show the canonical layout for produces /
consumes / tools / subscriptions:
packages/crm/src/blocks/notes.block.md(simple tool-only block — the PR 1 C7 smoke-test)packages/crm/src/blocks/crm.block.md(real-world anatomy with## Subscriptionsdeclaring a reactive handler)
- Canonical NL → BlockSpec examples (these are hand-curated
translation lessons in code form):
packages/crm/src/lib/scaffolding/nl/example-specs.ts— EXAMPLE_SPECS export. Two examples: tool-only (contact-notes)- reactive-with-subscription (auto-activity-log).
- Hard constraints on BlockSpec shape (the schema Claude must
honor):
packages/crm/src/lib/scaffolding/spec.ts— BlockSpecSchema- field patterns (slug, tool name, handler name, event name, subscription event).
Reading order: anatomies first → schema → examples. Each reads in under 30 seconds; together they give Claude the concrete pattern needed to translate NL confidently.
Required fields:
slug: kebab-case lowercase, e.g."notes","client-satisfaction". Cannot collide with reserved core blocks:crm,caldiy-booking,email,sms,payments,formbricks-intake,landing-pages.title: human-readable title, e.g."Notes","Client Satisfaction".description: one-line builder-facing description.triggerPhrases: 2-5 natural-language phrases that would activate this block.frameworks: array of framework strings, e.g.["universal"].
Optional fields (defaulted if absent):
produces: array of{ name, fields }event declarations.consumes: array of{ kind, ... }discriminated-union entries (kind: "event" | "soul_field" | "trigger_payload").tools: array of tool definitions:{ name, description, args: [...], returns: [...], emits: [...] }.subscriptions: array of{ event, handlerName, description, idempotencyKey }reactive handlers.eventis fully-qualified:"<source-block>:<event.name>".
2. Pre-classify the intent (deterministic safety check)
Before translating NL → BlockSpec, call the deterministic classifier once to sanity-check the intent:
import { classifyIntent } from "@/lib/scaffolding/nl/intent-classifier";
const classification = classifyIntent(nlIntent);
// { tier: 1 | 2 | 3, issues: string[], suggestedAction: string }
Behavior by tier:
- Tier 3 — the intent is destructive / modifies an existing core block. Refuse by default. If the builder explicitly confirms (e.g., "yes, this is an admin-only tool with safeguards"), the scaffold may proceed; otherwise relay the specific risk and stop.
- Tier 1 — the intent is empty, trivially short, or
self-contradictory. Ask ONE focused clarifying question
(
suggestedActionpoints at what to ask). Wait for a reply. Re-classify after the reply; proceed only when classification is no longer tier 1. - Tier 2 — the common case. Proceed with scaffold generation.
Fill sensible defaults for under-specified fields; mark each
default with
TODO (scaffold-default)comments the builder can grep post-scaffold.
The classifier's heuristics are loose — false-negative tier-2 intents are acceptable (downstream BlockSpec validation catches structural problems). False-positive tier-3 decisions are preferred over false-negative — better to refuse once and require confirmation than scaffold a destructive tool silently.
3. Clarify rarely (three-tier policy per audit G-4)
- Tier 1 — Ask once only when the intent is genuinely meaningless (zero description, or internally contradictory type declarations). Ask ONE focused question; proceed.
- Tier 2 — Default with TODO markers for everything else. When
the intent doesn't name tools, scaffold
create_<entity>/list_<entity>s/get_<entity>as defaults. Every scaffolded default lands withTODO (scaffold-default)markers the builder can grep. - Tier 3 — Fail on dangerous output. Refuse to scaffold:
- Destructive tools without explicit confirmation.
- Modifications to existing blocks (scope limits to NEW blocks).
4. Run the scaffold
Once the BlockSpec is constructed, write it to a temporary file and invoke the scaffold CLI:
# Claude writes /tmp/spec.json with the BlockSpec JSON
pnpm scaffold:block --spec /tmp/spec.json
The scaffold will:
- Validate the BlockSpec against the Zod schema.
- Render BLOCK.md, tools.ts, subscription handler stubs (if any), and test stubs.
- Write all files to
packages/crm/src/blocks/<slug>.*andpackages/crm/tests/unit/blocks/<slug>.spec.ts. - Run the validation gate:
parseBlockMdround-trip on the new BLOCK.mdtsc --noEmitacross the CRM packagepnpm emit:blocks:check(after the builder adds the block to the emit TARGETS registry on next step)
- On success, print the created-files list and next-step hints.
- On failure, print the orphan report with
git cleanrecovery commands.
5. Relay results
After the scaffold succeeds, tell the builder:
- Files created (from the scaffold's stdout).
- Next-step checklist (also from the scaffold's stdout):
- Review TODO (scaffold-default) markers — they're the fill-in points.
- Add the block to
scripts/emit-block-tools.impl.tsTARGETS list so emit:blocks:check covers it. - Run
pnpm emit:blocksto populate the TOOLS block in the BLOCK.md. - Run
pnpm test:unit— new test stubs appear as todos. git diff/git add/git commitwhen satisfied.
On failure, relay the orphan report verbatim so the builder has the exact recovery commands.
BlockSpec example — "notes" block
{
"slug": "notes",
"title": "Notes",
"description": "Simple note-taking on contacts.",
"triggerPhrases": [
"Add a notes block",
"Install notes",
"Let me jot notes on contacts"
],
"frameworks": ["universal"],
"produces": [
{
"name": "note.created",
"fields": [
{ "name": "noteId", "type": "string", "nullable": false },
{ "name": "contactId", "type": "string", "nullable": false }
]
}
],
"consumes": [],
"tools": [
{
"name": "create_note",
"description": "Create a note on a contact.",
"args": [
{ "name": "contactId", "type": "string", "nullable": false, "required": true },
{ "name": "body", "type": "string", "nullable": false, "required": true }
],
"returns": [
{ "name": "noteId", "type": "string", "nullable": false, "required": true }
],
"emits": ["note.created"]
}
],
"subscriptions": []
}
Dry-run mode
Pass --dry-run to see what would be created without writing:
pnpm scaffold:block --spec /tmp/spec.json --dry-run
Useful for previewing before committing to the scaffold.
Error recovery
If the scaffold fails mid-pipeline:
- Files that landed before the failure remain on disk (by design — orphan detection, not transactional rollback).
- The error message lists every orphan + concrete
git cleancommands to remove them. - Fix the underlying issue and re-run. The scaffold refuses to overwrite existing files, so the builder must remove orphans first (or pick a different slug).
Out of scope for this skill
- NL intent parsing without an explicit BlockSpec construction step (future PR will layer this on; this PR requires Claude Code to translate intent → BlockSpec JSON inline).
- UI scaffolding (pages, Puck components) — future SLICE 4.
- Database schema generation (builder authors Drizzle schemas manually if their block needs persistence).
- Marketplace publishing (out of slice).