Block creation
Open-source AI front office for local service businesses: AI receptionist (voice/SMS/chat) + website + CRM + booking. Self-hostable or $29/mo flat. The open-source GoHighLevel alternative.
npx -y skills add seldonframe/seldonframe --skill block-creationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 16 stars16 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Scaffold a new SeldonFrame block from a natural-language intent. Use when the builder says "build me a block that ...", "scaffold a block that ...", or similar. Produces a real, validated block skeleton in packages/crm/src/blocks/ — BLOCK.md with composition contract, tools.ts with Zod schemas, subscription handler stubs (when reactive), and test.todo stubs per tool. Does NOT commit, does NOT install into any workspace. Builder owns review + git.
SKILL.md
9.3 KB, as published. Nobody here has run it
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).