π§± Building Blocks¶
Marketplace-ready YAML presets β share, remix, and discover pipelines, agents, quality packs, and language packs.
What are Building Blocks?¶
Blocks are versioned, marketplace-ready YAML packages that define reusable configurations for the SLMCode pipeline. They come in five kinds:
| Kind | Schema | Purpose |
|---|---|---|
pipeline | PipelineBlock | Phase graph, loop agents, insertable slots |
agent | AgentBlock | Custom specialist definition or builtin override |
quality | QualityBlock | Format/lint/test/build commands per language |
pack | PackBlock | Composes pipeline + quality + agents + skills |
team | TeamBlock | A virtual dev team: what it owns, what proves it, who staffs it β see Teams |
Discovery Order¶
Blocks are discovered in a priority chain. First ID wins per kind:
- Project β
.slmcode/blocks/{pipelines,agents,quality,packs,teams}/*.yaml - User β
~/.slmcode/blocks/β¦or$XDG_CONFIG_HOME/slmcode/blocks/β¦ - Extra β
$SLMCODE_BLOCKSenv var, walk-upblocks/dirs - Builtin β embedded in
pkg/blocks/bundled/(compiled into binary)
This means project overrides always win. Drop a .slmcode/blocks/pipelines/go.yaml with your customizations, and it replaces the builtin Go pipeline for that project.
Common Schema (Meta)¶
Every block YAML file shares this header:
api_version: blocks/v1 # required β current schema version
kind: pipeline # pipeline | agent | quality | pack | team
id: my-block # lowercase kebab-case, 2-64 chars, [a-z][a-z0-9_-]+
name: My Block # human-readable display name
description: A reusable block
version: "1.0.0"
author: UnicoLab
license: MIT
language: go # lowercase language code
tags: [go, worker]
icon: "πΉ"
shareable: true # marketplace-ready flag (default: true)
Predefined Language Packs (Builtin)¶
SLMCode ships fifteen packs. Each one is a pack block composing a pipeline, a quality block and language-aware agents; slmcode init picks one automatically (see Detection).
| Pack | Language | Agents | Smoke | QA gate (qa_gate_command) |
|---|---|---|---|---|
πΉ go | go | go-worker go-tester go-reviewer | go test ./... -short | go test ./... -race -count=1 |
π python | python | python-worker python-tester python-reviewer | python -m pytest -q | python -m pytest -q |
βοΈ react | typescript | react-worker react-tester react-reviewer | npm test --silent | npm test --silent |
π¦ typescript | typescript | ts-worker ts-tester ts-reviewer | npm test --silent | npm test --silent |
π web | html | web-worker web-tester | test -s index.html \|\| test -s index.htm | same |
π¦ rust | rust | rust-worker rust-tester rust-reviewer | cargo check --quiet | cargo test --quiet |
β java | java | java-worker java-tester java-reviewer | mvn -q -B -DskipTests compile | mvn -q -B test |
πͺ kotlin | kotlin | kotlin-worker kotlin-tester | ./gradlew compileKotlin --console=plain | ./gradlew test --console=plain |
π£ dotnet | csharp | dotnet-worker dotnet-tester dotnet-reviewer | dotnet build --nologo --verbosity quiet | dotnet test --nologo --verbosity quiet |
π ruby | ruby | ruby-worker ruby-tester | bundle exec rspec --no-color | bundle exec rspec --no-color |
π php | php | php-worker php-tester | vendor/bin/phpunit --colors=never | vendor/bin/phpunit --colors=never |
ποΈ swift | swift | swift-worker swift-tester | swift build | swift test |
βοΈ cpp | cpp | cpp-worker cpp-tester | cmake --build build | ctest --test-dir build --output-on-failure |
π§© shadcn | typescript | shadcn-worker shadcn-reviewer react-tester | npx tsc --noEmit | npx tsc --noEmit |
π¨ untitledui | typescript | untitledui-worker untitledui-reviewer react-tester | npx tsc --noEmit | npx tsc --noEmit |
slmcode blocks list prints the live set; this table is a snapshot of it.
shadcn and untitledui are methods, not languages
The last two build React UI by installing components with the library's own CLI and wiring them up, rather than writing them by hand. You do not have to apply them: both are chosen automatically from the request and the project, and their agents ship registered. Applying one just pins the choice for good. See Frontend: assemble or write.
Every pack also pins skills (pin_skills: true) β always atomic-coding, specialist-worker and specialist-tester, plus language-specific ones: go adds go-table-tests and go-concurrency, typescript adds typescript-strict, react adds react-hooks and typescript-strict.
π Shell (agents only)¶
shell-worker / shell-tester β for Bash/shell scripts (bash -n + shellcheck). No standalone pack; the generic pipeline selects them when the workspace is shell.
Detection β how a pack is chosen¶
slmcode init calls blocks.DetectPack(root, root), and that is the only detection answer in the codebase. It is deterministic and precedence-ranked, and it scores each quality block's detect stanza:
| Signal | Score | Meaning |
|---|---|---|
detect.contains satisfied | +25 each | strongest: proof from a marker file's content |
detect.files marker present in the root | +12 each | strong |
a source file with a detect.extensions suffix | +2 each, capped at 3 | weak tiebreak |
detect.priority | added as-is | the pack author's ranking |
Two rules make it correct on real repositories:
- Nested sub-projects are skipped. The extension walk does not descend into a directory that carries its own project marker (
go.mod,package.json,Cargo.toml,pyproject.toml,pom.xml,build.gradle{,.kts}, β¦). A Go module with a Vite app inweb/is a Go project; the frontend's.tsfiles no longer out-vote the backend'sgo.mod. - Markers outweigh stray files. "This repo has a
pyproject.toml" is worth much more than "some.pyfile exists somewhere".
detect.contains β proving a language from file content¶
package.json alone cannot tell react from typescript, and a filename-only rule got it wrong in both directions. contains maps a root file onto substrings that prove the language; any one match satisfies the entry:
spec:
detect:
files: [package.json]
extensions: [.tsx, .jsx]
contains:
package.json: ['"react"', '"next"', '"preact"', '"react-dom"']
priority: 14
At most 256 KB of each named file is read. A contains entry that is declared but not satisfied scores nothing β it never counts against the block.
So: a package.json that declares React resolves to the react pack; one that does not resolves to typescript; a directory of index.html and .css with no package.json resolves to web.
CLI Commands¶
# List all available blocks, grouped by kind
slmcode blocks list
# Show details of a specific block
slmcode blocks show pipeline go
slmcode blocks show agent python-worker
slmcode blocks show pack react
# Apply a language pack (writes pipeline.yaml + config)
slmcode blocks apply go
slmcode blocks apply go --materialize-agents
# Validate all block YAML configs
slmcode blocks validate
Create, edit and delete project blocks (written to .slmcode/blocks/):
slmcode blocks new agent my-agent --file agent.yaml
slmcode blocks new agent my-agent --name "My Agent"
slmcode blocks edit agent my-agent --file agent.yaml
slmcode blocks delete agent my-agent
slmcode blocks apply go --force # overwrite existing agent files
In the TUI or the chat REPL:
/pack <pack-id> β apply a language pack (any of the thirteen)
/blocks β list all available blocks
/skills β list loaded skills
Creating Custom Blocks¶
Pipeline Block¶
Save as .slmcode/blocks/pipelines/my-pipeline.yaml:
api_version: blocks/v1
kind: pipeline
id: my-lang
name: My Language Pipeline
version: "1.0.0"
language: rust
tags: [rust, pipeline]
icon: "π¦"
spec:
version: 1
order: [init, skills, context, explore, plan, split, coord, execute, learn, test, memory, done]
groups:
- {id: prepare, label: Prepare, steps: [init, skills, context, explore]}
- {id: design, label: Design, steps: [plan, split]}
- {id: build, label: Build, steps: [coord, execute, learn]}
- {id: verify, label: Verify, steps: [test]}
- {id: finish, label: Finish, steps: [memory, done]}
phases:
init: {agent: "", when: always, label: Init}
context: {agent: context, when: always, label: Context}
explore: {agent: explorer, when: auto, label: Explore}
plan: {agent: planner, when: always, label: Plan}
split: {agent: splitter, when: always, label: Split}
coord: {agent: coordinator, when: always, label: Coord}
execute: {agent: worker, when: always, label: Execute}
test: {agent: tester, when: always, label: Test}
memory: {agent: memory, when: always, label: Memory}
done: {agent: "", when: always, label: Done}
execute:
default_role: worker
reviewer: reviewer
corrector: corrector
max_waves: 2
slots:
- id: quality-reminder
agent: tester
title: Quality reminder
before: execute
when: always
persist_to: scratch
fail_mode: continue
input: |
Remember quality bar: cargo clippy, cargo test, cargo build
Query: {{query}}
Agent Block¶
Save as .slmcode/blocks/agents/my-worker.yaml:
api_version: blocks/v1
kind: agent
id: my-worker
name: My Worker
version: "1.0.0"
language: rust
tags: [rust, worker]
icon: "π¦"
spec:
id: my-worker
title: My Worker
system_prompt: |
You are a Rust implementation specialist. Stay inside HARD SCOPE.
After edits, smoke with: cargo test -p <crate>
tools: true
max_iter: 16
temperature: 0.12
max_tokens: 3072
skills: [specialist-worker, atomic-coding]
Quality Block¶
Save as .slmcode/blocks/quality/my-lang.yaml:
api_version: blocks/v1
kind: quality
id: my-lang
name: My Lang Quality
version: "1.0.0"
language: rust
spec:
detect:
files: [Cargo.toml] # root marker files (+12 each; globs allowed)
extensions: [.rs] # source suffixes (+2 each, capped at 3)
contains: # content proof (+25 each) β any substring matches
Cargo.toml: ['[package]']
priority: 20 # author ranking, added to the score
lint:
- {cmd: cargo clippy -- -D warnings, label: clippy}
test:
- {cmd: cargo test, label: cargo test}
build:
- {cmd: cargo build, label: cargo build}
smoke: cargo test --quiet
qa_gate: cargo test
safe_prefixes:
- cargo test
- cargo build
- cargo clippy
Pack Block¶
Save as .slmcode/blocks/packs/my-lang.yaml:
api_version: blocks/v1
kind: pack
id: my-lang
name: My Language Pack
version: "1.0.0"
language: rust
spec:
pipeline: my-lang
quality: my-lang
agents: [my-worker, my-tester]
skills: [atomic-coding]
pin_skills: true
override_tester: my-tester
override_worker: my-worker
Studio Integration¶
The BlockManager page (navigate to Blocks in the sidebar) provides a visual browser for all available blocks:
- Tabbed interface: All, Packs, Pipelines, Agents, Quality
- Cards showing metadata: name, description, language, version, tags, source
- One-click Apply for packs and pipelines
- Active indicators showing which pack/pipeline is currently active
The PackSelector in Settings lets you switch language packs directly from the settings page, alongside the Stack Selector.
The PipelineEditor includes a preset selector listing every pipeline block the registry can see β the thirteen builtins plus anything under .slmcode/blocks/pipelines/ β with one-click switching.
Validation¶
Loads every block, calls Validate() on each, and for packs also verifies that all referenced pipelines, agents, and quality packs actually exist. Reports exact errors so you can fix YAML issues before running.
Marketplace-Ready¶
All blocks are designed for sharing:
- Versioned β Semantic versioning (
"1.0.0") - Authored β
authorandlicensefields - Tagged β
tagsfor discovery and filtering - Iconed β
iconfor visual recognition in UI - Shareable β
shareable: trueflag for marketplace listing
Drop your custom blocks into a GitHub repo's blocks/ directory, and anyone can use them by setting the SLMCODE_BLOCKS environment variable.