# solverforge-cli
Default entry point for new SolverForge projects.
Use this CLI to scaffold, grow, and validate SolverForge applications. The CLI
is its own versioned product: `solverforge --version` reports the CLI package
version and the scaffold dependency targets separately.
Current CLI package version: `3.2.0`.
Required Rust version: `1.95` or later.
New projects currently target these crate versions:
- `solverforge 0.19.7`
- `solverforge-ui 0.9.0` for the default web shell
- `solverforge-maps 2.1.4` for the default web shell
- `rmcp 3.4.0` for the MCP shell
```bash
cargo install solverforge-cli
solverforge new my-scheduler
```
## Current Contract
Public scaffold path:
- `solverforge new <name>`
That command creates a neutral app shell. The default shell is `web`; use
`--shell api` for an HTTP API without frontend assets, `--shell cli` for a
Clap command-line app without Axum, or `--shell mcp` for an MCP server that
exposes the solver to any MCP-capable agent harness. Users shape the app
afterward through facts, entities, solution/score metadata, variables,
constraints, generated data, and `solverforge.app.toml`. Shell choice is
recorded as `[app].shell`; it is not a modeling family selector. The current
public shell set is exactly `web`, `api`, `cli`, and `mcp`; a Tauri shell is
intentionally deferred and is not generated by this release line.
Planning variable kinds are canonical:
- `scalar` for single-value assignment variables backed by either
`--range <facts>` or a half-open `--countable-range <from..to>`
- `list` for sequence variables backed by `--elements <facts>`
Countable ranges are validated as non-negative `usize` values with `from < to`,
are projected into `solverforge.app.toml` and the web UI model, and render as
numeric value lanes in the generated frontend:
```bash
solverforge generate variable hour --entity Shift --kind scalar --countable-range 0..24
```
Scalar variables can also carry opt-in SolverForge hook metadata for
model-owned candidate selection, nearby candidate selection, distance meters,
and construction ordering:
```bash
solverforge generate variable resource_idx --entity Task --kind scalar --range resources \
--candidate-values resource_candidates \
--nearby-value-candidates nearby_resources \
--nearby-entity-candidates nearby_tasks \
--nearby-value-distance-meter resource_distance \
--nearby-entity-distance-meter task_distance \
--construction-entity-order-key task_priority \
--construction-value-order-key resource_priority
```
Those flags only write `#[planning_variable(...)]` metadata and project it into
`solverforge.app.toml`. Web-shell projects also project that metadata into
`static/generated/ui-model.json`; users still own the Rust hook functions.
Ordered sequences and routes use list variables. List variables expose the full
current SolverForge list metadata surface. Use
the stock CVRP profile when the solution implements the runtime's CVRP
contract:
```bash
solverforge generate variable visit_order --entity Route --kind list --elements visits --domain cvrp
```
For custom list domains, use `--distance-meter`, `--intra-distance-meter`,
`--route-hooks`, `--savings-hooks`, `--savings-metric-class-fn`,
`--element-owner-fn`, `--construction-element-order-key`,
`--precedence-duration-fn`, `--precedence-successors-fn`, and
`--solution-trait`. These values are preserved in `solverforge.app.toml` and
the web UI projection. They name user-owned Rust implementations. The stock
CVRP profile already owns its meters, route/savings hooks, metric class, and
solution trait, so those entries cannot be overridden alongside
`--domain cvrp`.
Scalar groups and conflict repairs are opt-in modeling resources. They are
identified only by exact IDs: scalar-group names and snake_case constraint IDs.
Unless `--skip-solver-config` is passed, `solverforge generate scalar-group`
writes both grouped construction and grouped local-search `solver.toml` refs
for assignment-backed and candidate-backed groups. `solverforge check` validates
those refs across the solver config graph, including construction phases,
top-level selectors, neighborhoods, nested selector children, and partition
child phases. The CLI writes those generated refs inside one
`# @solverforge:begin solver-config` / `# @solverforge:end solver-config`
region with exact-ID owner comments for each generated phase; generated apps
still consume plain `solver.toml` through the umbrella `solverforge` crate.
`standard` is only a demo dataset size label in `solverforge.app.toml`; it is
not a variable kind.
Basic domain flow:
```bash
solverforge new my-scheduler
cd my-scheduler
solverforge generate fact resource --field category:String --field load:i32
solverforge generate entity task --field label:String --field priority:i32
solverforge generate variable resource_idx --entity Task --kind scalar --range resources --allows-unassigned
solverforge generate data --size large
solverforge server
```
Shell variants:
```bash
solverforge new batch-scheduler --shell cli
solverforge new service-scheduler --shell api
solverforge new agent-scheduler --shell mcp
```
Generated projects use managed block markers as the canonical CLI edit points.
Domain exports, solution collections, entity variables, constraint modules, and
constraint calls must retain their `@solverforge:begin ...` /
`@solverforge:end ...` regions for later `generate` and `destroy` commands.
Project-local `.solverforge/templates/entity.rs.tmpl` and
`.solverforge/templates/solution.rs.tmpl` overrides are supported only when
they emit those same canonical managed blocks.
`solverforge generate data` owns the generated data pipeline. It keeps
`src/data/mod.rs` as the stable import wrapper, rewrites
`src/data/data_seed.rs` with deterministic sample builders, and persists
dataset size defaults in `solverforge.app.toml`. `sample` is the default mode;
`stub` is available for shape-only data. The supported demo size labels are
`small`, `standard`, and `large`. Generated values are structurally useful
rather than domain-specific fake business data.
The default web-shell frontend is intentionally thin. It composes shipped
`solverforge-ui 0.9.0` primitives such as `SF.createBackend(...)`,
`SF.createSolver(...)`, and `SF.rail.createTimeline(...)` instead of vendoring
app-specific UI frameworks. Domain-specific examples belong in quickstarts, not
in the built-in scaffold catalog. API-shell, CLI-shell, and MCP-shell projects
do not generate `static/`, `static/generated/ui-model.json`, or `ui_source`;
later domain mutations keep that shell boundary intact.
## Command Surface
Core commands:
- `solverforge new <name>` creates the neutral scaffold.
`--shell web|api|cli|mcp` selects the generated app shell; `web` is the
default. `--skip-git` skips the initial Git repository/commit, and
`--skip-readme` skips the generated project README.
- `solverforge generate fact|entity|variable|constraint|solution|score|data`
mutates the current project through the canonical generated surfaces.
- `solverforge generate scalar-group|conflict-repair` wires opt-in model
resources by exact ID.
- `solverforge destroy fact|entity|variable|constraint|solution|scalar-group|conflict-repair`
removes generated resources and rewrites the app spec/UI projection.
- `solverforge check`, `solverforge info`, and `solverforge routes` inspect the
generated project. `routes` applies only to web/API shells.
- `solverforge connect` prints ready-to-paste MCP client configuration for an
MCP-shell project (stdio command plus Streamable HTTP URL for Claude Code,
Claude Desktop, Cursor, VS Code, and other clients). `--write vscode` merges
the project entry into `.vscode/mcp.json`; global client files are printed
with their path instead of being modified.
- `solverforge config show|set` reads and writes non-phase `solver.toml`
settings. Ordered `phases` edits are manual. Generated model-resource refs in
that file are exact-ID graph references, not aliases; destroy re-renders the
CLI-managed solver config region and blocks when nested or user-authored
solver config still references the resource.
- `solverforge config set candidate_trace.max_entries <N>` enables the
bounded candidate-pull diagnostics and rejects zero/non-integer capacities.
- `solverforge server` runs web/API generated apps through Cargo, and boots the
MCP-shell project's stateless Streamable HTTP transport. CLI-shell projects
run directly with `cargo run -- demo-data`; MCP-shell projects default to the
stdio transport with `cargo run`.
- `solverforge test` delegates to `cargo test`.
- `solverforge completions <shell>` emits shell completions.
Generated project manifests include `rust-version = "1.95"` and dependencies
for the selected shell. The web shell includes the current direct web/runtime
support dependencies:
- `axum 0.8.9`
- `tokio 1.52.3`
- `tokio-stream 0.1.18`
- `tower-http 0.6.11`
- `tower 0.5.3`
- `serde 1.0.228`
- `serde_json 1.0.150`
- `uuid 1.23.5`
- `parking_lot 0.12.5`
The API shell keeps `solverforge`, Axum, Tokio, SSE, `tower-http` CORS,
serialization, and `parking_lot`, but excludes `solverforge-ui`,
`solverforge-maps`, and static file serving. The CLI shell keeps
`solverforge`, Clap, Tokio, serialization, and `parking_lot`, but excludes
Axum, `tower-http`, `tokio-stream`, `solverforge-ui`, `solverforge-maps`, and
`static/`. The MCP shell keeps `solverforge`, `rmcp`, Axum, Tokio,
`tracing`, `tracing-subscriber`, serialization, and `parking_lot`, but
excludes `solverforge-ui`, `solverforge-maps`, and `static/`; it also keeps
the runtime `console` feature off because the runtime banner writes to stdout,
which is the stdio MCP transport channel. Every shell declares `schemars` as
an optional dependency behind a `schema` feature; only the MCP shell enables it
to publish typed tool schemas over the shared DTO contract.
Every shell shares one generated core (`domain/`, `constraints/`, `solver/`,
`data/`, and the DTO contract), so `solverforge generate` and
`solverforge destroy` mutate an MCP-shell project exactly like any other shell.
## MCP Shell
`--shell mcp` produces a planning application whose delivery surface is an MCP
server. The retained solver job lifecycle is exposed as annotated, schema-typed
tools: `list_demo_data`, `get_demo_data`, `solve`, `get_status`,
`get_best_solution`, `analyze_solution`, `get_telemetry`, `get_candidate_trace`,
`pause`, `resume`, `cancel`, and `delete`.
`solve` returns an MCP task handle with the retained `jobId` in result metadata
to task-capable clients (MCP 2026-07-28), and an immediate job summary to every
other client. Either client can therefore drive the lifecycle through the
polling and control tools while the solve runs. MCP tasks have no TTL, so task
expiry cannot orphan an active solve; task records remain available for the
server process lifetime. The server speaks stdio by default and serves
stateless Streamable HTTP at `/mcp` with `--http`; HTTP binds
the loopback interface unless `--host` selects a concrete IP address. Wildcard
binds (`0.0.0.0` and `::`) are rejected so rmcp Host validation remains active.
One solver service and one task store are shared by every request so jobs and
tasks survive individual negotiations.
> **The MCP HTTP transport has no authentication and no per-caller isolation.**
> Every connection shares one solver and task store, so a non-loopback `--host`
> exposes job start, status, snapshot, analysis, candidate trace, pause, resume,
> cancel, and delete to anyone who can reach the port. Host validation only
> rejects DNS-rebinding `Host` headers; it is not access control. Keep the bind
> on loopback unless the network is trusted, or put an authenticating proxy in
> front. stdio has no such exposure because the client owns the process.
```bash
solverforge new agent-scheduler --shell mcp
cd agent-scheduler
solverforge generate fact resource
solverforge generate entity task
solverforge generate variable resource_idx --entity Task --kind scalar --range resources
solverforge generate data
cargo run --release # stdio MCP server
cargo run --release -- --http # Streamable HTTP on http://127.0.0.1:7860/mcp
solverforge connect # client configs for Claude Code, Claude Desktop, Cursor, VS Code
```
Persistent `.solverforgerc` files are loaded from the project root first and
then from `~/.solverforgerc`. Recognized preferences are intentionally narrow:
`port`, `no_color`, and `quiet`.
## Generated Runtime Diagnostics
Generated web/API apps expose every compact `SolverTelemetry` aggregate,
including applied/not-doable/rejected moves, hard-score direction counts,
conflict-repair counters, construction counters, current phase detail,
per-selector and per-move breakdowns, and the bounded applied-move trace. These
fields are carried consistently by status, snapshot, and typed SSE payloads.
Candidate-pull traces are intentionally not copied into those ordinary
control-plane payloads. After enabling `[candidate_trace]`, fetch the retained
diagnostic publication from `GET /jobs/{id}/telemetry`. The response includes
the full bounded pull prefix, canonical identities and dispositions, digests,
resolved phase plan, execution policy, input provenance, and qualification
status paired with the exact retained job status.
`POST /jobs/qualified` starts the same retained lifecycle with externally
attested SHA-256 schema, instance, initial-state, core-tree, and loaded-build
digests. Existing clients continue to use `POST /jobs`; the qualified route is
an additive diagnostic entry point. Tracing remains opt-in because candidate
pull detail can be large.
## Agent Skills
The repository ships two portable, harness-agnostic Agent Skills:
`skills/solverforge-modeling/` (turn a described planning problem into a runnable
SolverForge app with this CLI) and `skills/solverforge-ui/` (extend a generated
web shell with the shipped `solverforge-ui` components). The same folders are
discovered by opencode, Claude Code, Codex, and other Agent Skills harnesses.
The installer is agent-centric: name the harnesses you use and it resolves each
harness's own skills directory. It never installs into a directory you did not
ask for, never assumes `~/.agents`, and refuses duplicate discovery.
```bash
./scripts/install-skill --agent opencode # one copy, opencode
./scripts/install-skill --agent opencode --agent claude --layout covering
./scripts/install-skill --agent opencode --link # symlink instead of copy
./scripts/install-skill --agent opencode --project <dir> # project scope
./scripts/install-skill --agent opencode --list
./scripts/install-skill --agent opencode --uninstall
```
From the repository root, `make install-skill ARGS='--agent opencode'` runs the
same installer with the same arguments. It updates or removes only copies it
owns (tracked by the `.solverforge-skill` marker it writes at install time) and
leaves foreign entries untouched. Because opencode scans the opencode, Claude,
and Agent Skills directories, `{opencode, claude, codex}` has no duplicate-free
placement; the installer reports that instead of silently duplicating, and
`--layout per-harness --force` installs all three copies while accepting the
duplicate discovery.
The bundled `scripts/solve-smoke-test.sh <app-dir>` verifies a generated app:
for web/API it builds, boots, starts a real solve, and requires a clean
`COMPLETED` result with published scores; for MCP it proves only that the HTTP
transport boots with a healthy, panic-free `/health` — it does not negotiate
MCP or call tools, so drive those through a real MCP client or the
`runtime_mcp_pipeline_test` suite; for CLI it validates `demo-data`
serialization. It is a development aid, not a replacement for the
generated-app suites below.
## Validation Flow
End-to-end validation is split into explicit phases so the real production
pipeline stays readable:
- `cargo test`
Rust unit tests, scaffold contract tests, and generated-app runtime pipeline tests
- `make test-support`
installer behavior and skill reference-integrity tests
- `make test-runtime`
phase-marked runtime and MCP pipeline tests against ephemeral generated apps only
- `make test-e2e`
Playwright browser tests against ephemeral generated apps only
- `make install-e2e`
install Playwright Chromium locally before the first browser run
- `make test-full`
full pipeline: binary/unit tests, installer and skill-integrity tests,
scaffold contract tests, runtime and MCP pipelines, then Playwright
The runtime and browser suites both scaffold fresh temp apps, mutate them
through the real CLI, boot the generated servers on random ports, and clean up
automatically. Failure artifacts are written under `target/test-artifacts/`.
By default, end-to-end validation preserves the generated registry dependency
declarations and applies no local patches. During a coordinated runtime
prerelease whose target is not yet on crates.io, set `SF_USE_LOCAL_PATCHES=1`;
the harness writes a temporary `.cargo/config.toml` with explicit
`[patch.crates-io]` entries only for dependencies present in the generated
manifest. Generated manifests are not rewritten. Repeat the registry-only gate
after the target is published.
Current scenario coverage:
- neutral shell: scaffold, boot, and verify the empty production shell
- mixed app: scaffold mixed shape, seed non-empty mixed demo data, start a real
retained solve, verify required scalar assignment and complete list placement,
then cancel and delete the job through the generated runtime/browser surface
- scalar-only app: seed non-empty data and run it through the real generated
solver, including typed SSE, status, analysis, checkpointed Pause/Resume,
user-facing Stop as runtime cancel, terminal-only Delete, and status/snapshot
reconnect bootstrap; the runtime pipeline also verifies full aggregate
telemetry, bounded candidate detail, and qualified trace provenance
- MCP app: model a mixed scalar-plus-list problem, generate the sample data,
build, and drive the generated MCP server with the official `rmcp` client over
both stdio and stateless Streamable HTTP, including the annotated tool
surface, task-backed solve to a terminal snapshot with scalar assignment and
complete list placement, the retained lifecycle through a legacy client, and a
byte-level stdio check that the transport channel stays pure JSON-RPC
SolverForge `0.19.7` supports mixed scalar/list construction, canonical mixed
local-search defaults, and list-based sequence modeling. The CLI suite proves
the mixed path in both runtime and browser pipelines; the scalar-only scenario keeps the deeper
pause/resume, analysis, reconnect, and qualified-trace checks.
For solver and domain extension guidance after scaffolding, see the runtime docs
in [solverforge](https://github.com/SolverForge/solverforge):
[Extend the solver](https://github.com/SolverForge/solverforge/blob/main/docs/extend-solver.md)
and [Extend the domain](https://github.com/SolverForge/solverforge/blob/main/docs/extend-domain.md).
## License
Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE).