@flome/cli
v0.0.1
Published
A configuration-driven decision runtime: a small gate-controlled FSM that advances one state at a time. The CLI is designed for repeated subprocess use by LLM workflows and never parses or rewrites target-language source code.
Maintainers
Readme
flome
An LLM-friendly, configuration-driven workflow runtime for business Skills, with explicit FSMs, structured subtasks, fail-closed gates, and machine-readable CLI orchestration.
Install
npm install -g @flome/cli
flome --helpOr run one-off via npx:
npx @flome/cli lint --flow examples/support-ticket-triage/flow.yamlAfter installation, the CLI binary is named flome.
flome runs a declared workflow one transition at a time. It can execute mechanical commands, dispatch structured work to a human or model, validate the returned JSON, and route the run through explicit gates.
The runtime does not parse or rewrite target-language source code. A business Skill owns domain reasoning and edits; flome owns workflow state, contracts, transitions, persistence, and audit facts.
When to use flome
Use flome when a business process needs all of the following:
- A small, explicit state machine that an LLM can inspect and follow.
- Structured input and output contracts instead of free-form model text.
- Mechanical verification commands whose exit codes become gate inputs.
- Fail-closed routing when evidence is missing or a result is invalid.
- Durable runs, revision checks, idempotent submissions, and an audit trail.
Typical processes include support-ticket triage, code-review routing, content approval, release checks, and incident response.
The LLM contract
The normal caller loop is deliberately small:
init a run
-> next
-> if a semantic task is returned, do the Skill work
-> submit exactly one JSON result that matches the declared schema
-> next again
-> stop when terminal=true and done=truenext and submit return one machine-readable CliResult JSON envelope. An LLM or an outer Skill should parse JSON fields, not human-oriented logs.
The boundary is:
business Skill / LLM
| reads NextDispatch.subject and result_schema
v
detent next ---> semantic task dispatch
^ |
| | structured JSON result
+------ detent submit <---+
|
v
gate -> declared transition -> auditThe complete integration contract is in docs/LLM-INTEGRATION.md.
Quickstart: run a business Skill
This repository includes a runnable support-ticket triage example. It models a real integration boundary without pretending that flome understands ticket content: the Skill decides the ticket outcome, while the runtime validates and routes it.
npm install
npm run build
cd examples/support-ticket-triage
FLOME="node ../../dist/cli/main.js"
$FLOME lint
RUN_JSON="$($FLOME init --plugin . --target .)"
RUN_ID="$(node -e 'const r = JSON.parse(process.argv[1]); console.log(r.data.run_id)' "$RUN_JSON")"
# Mechanical INTAKE runs and dispatches CLASSIFY for ticket-1001.
DISPATCH="$($FLOME next --run "$RUN_ID")"
printf '%s\n' "$DISPATCH"
# The Skill performs the classification and returns only schema-valid JSON.
printf '%s\n' '{"decision":"resolve","reason":"The request is well-scoped and has a documented resolution path."}' > result.json
$FLOME submit classify_ticket --result @result.json --run "$RUN_ID"
# Mechanical RESOLVE runs, then the run reaches the RESOLVED terminal.
$FLOME next --run "$RUN_ID"The example's source files are intentionally visible:
- flow.yaml declares states, terminals, and edges.
- detent.plugin.json binds the flow, gate, task, and schema directories.
- classify_ticket.yaml is the Skill task contract.
- ticket_decision.json constrains the model result.
- detent.plugin.js contains a pure, fail-closed routing gate.
Author a flow
A flow contains an initial state, state definitions, terminal states, and explicit transitions:
name: document-approval
version: 0.1.0
initial: REVIEW
states:
- id: REVIEW
kind: semantic
subtasks: [review_document]
- id: PUBLISH
kind: mechanical
cmd: "npm run publish:check"
terminal_states:
- id: PUBLISHED
scope: global
- id: REJECTED
scope: global
transitions:
- from: REVIEW
to: [PUBLISH, REJECTED]
gate: route_review
- from: PUBLISH
to: [PUBLISHED, REJECTED]
gate: exit_okState kinds:
| Kind | Runtime behavior | Typical owner | | --- | --- | --- | | mechanical | Run cmd, capture exit code and optional JSON stdout, evaluate a gate | Compiler, test runner, analyzer, deploy command | | semantic | Return a structured task and wait for submit | LLM or human Skill | | mixed | Combine structured work with mechanical verification | Review-and-verify stages |
Transition rules are intentionally strict:
- true selects to[0].
- false selects to[1]; if it does not exist, the transition fails closed.
- A string selects that state only when it is explicitly listed in to.
- A gate can read structured run state, candidate results, the previous exit code, and parsed stdout, but never the filesystem.
- A flow change invalidates existing runs. Reset the run or create a new run after changing flow.yaml.
Use detent lint before creating a run. It checks reachability, terminal convergence, gate references, task references, fanout, adapter references, and recommendation bounds.
Integrate a business Skill
Create a plugin directory with this shape:
my-skill/
detent.plugin.json
flow.yaml
detent.plugin.js # optional pure gates
adapter.json # optional project-specific values
subtasks/*.yaml # questions and context scopes
schemas/*.json # JSON Schema for Skill resultsThe Skill integration responsibilities are:
- Run flome next --run .
- If terminal is true, stop and report the terminal facts.
- If task_id is present, read question, subject, candidate, and result_schema.
- Perform the domain work using only the dispatched context and the Skill's allowed tools.
- Return JSON that exactly matches the result schema.
- Write the JSON to a file and call flome submit --result @result.json --run .
- On revision_conflict, call next again and reconcile the current dispatch before retrying.
- Use a stable --submission when the caller may retry the same result.
flome does not discover, prompt, or execute an external Skill registry. The caller owns that integration. flome supplies a typed boundary and makes the result auditable.
See docs/LLM-INTEGRATION.md for the full contract and docs/USE-CASES.md for code-review, support, content-approval, and incident-response patterns.
CLI reference
detent init [--plugin <dir>] [--flow <path>] [--target <dir>] [--project <name>]
detent lint [--flow <path>]
detent state --run <id>
detent next --run <id> [--session <id>]
detent submit <task-id> --result @result.json --run <id> [--session <id>] [--submission <id>]
detent expand <task-id> --file <path> [--run <id>]
detent reset --run <id>
detent runsEvery command writes one CliResult JSON object to stdout and exits with code 0 on success or 1 on failure. The public shape is:
{"ok":true,"data":{}}or:
{"ok":false,"error":"shape_check_failed","details":{}}Important errors for callers include shape_check_failed, flow_changed, revision_conflict, and self-propagation exceeded step budget.
Storage
flome stores runs as JSON files and needs no native dependency:
- State: .detent/runs/<run_id>/state.json.
- Atomic writes, locking, and revision-based compare-and-swap.
- Idempotent submissions keyed by submission_id.
Invariants
- flome never parses or rewrites target-language source code.
- Declared compilers, tests, analyzers, and commands remain the verification authority.
- Domain decisions and edits belong to the business Skill; the runtime supplies transitions, contracts, and structured context.
- Gates receive structured state only and must fail closed when they cannot establish a safe route.
Development
npm install
npm run typecheck
npm run test
npm run buildPublishing
The package is published from GitHub Actions with npm OIDC Trusted Publishing. Push a matching tag such as v0.0.1 after configuring the npm trusted publisher. See docs/NPM-PUBLISHING.md for the release flow and bootstrap requirements.
License
MIT. See LICENSE.
