This section documents the smallFactory CLI (sf).
Use python3 sf.py ... or, if you installed a launcher, sf ....
- Initialize a datarepo
python3 sf.py init datarepos/my-repo
# or clone and initialize from GitHub
python3 sf.py init --github-url https://github.com/you/your-repo.git --name my-repo- Start the Web UI
python3 sf.py web --port 8080 --host 0.0.0.0 --debug- Print stickers (batch PDF)
python3 sf.py stickers --sfids p_widget,p_case --out labels.pdf --fields=name,rev --size=2x1 --dpi=300 --text-size=24- -R, --repo PATH: override datarepo path (defaults to user config or
SF_REPO) - -F, --format {human,json,yaml}: output format (default from
SF_FORMATorhuman) - -q, --quiet: decrease verbosity (repeatable)
- -v, --verbose: increase verbosity (repeatable)
- --version: print CLI version
Environment variables:
- SF_REPO: default datarepo path
- SF_FORMAT: default output format:
human,json, oryaml
- init — Initialize a new datarepo or clone an existing one
- repo — Repository compatibility and upgrade operations
- status, upgrade, validate
- web — Start the web UI server
- inventory — Inventory journal and reports
- post, onhand, rebuild
- entities — Canonical metadata operations
- add, ls, show, set, retire
- build: serial, datetime
- revision: bump, release
- files: ls, mkdir, rmdir, add, rm, mv
- events: ls, append, update, tags, link-file
- bom — Bill of Materials operations
- ls, add, rm (remove), set, alt-add, alt-rm
- stickers — Generate a PDF of QR labels (batch)
Use python3 sf.py <command> --help for built-in help on any command.
Initialize a local datarepo directory, or clone from GitHub and scaffold it.
# Local repo at datarepos/my-repo
python3 sf.py init datarepos/my-repo
# Clone from GitHub, auto-scaffold, and set default
python3 sf.py init --github-url https://github.com/you/your-repo.git --name my-repoNotes:
- Creates or clones the repo, writes
sfdatarepo.yml, sets it as default in user config, and makes an initial commit. - Ensures default inventory location
l_inboxexists and is configured insfdatarepo.yml.
Inspect compatibility/migration state and apply repository upgrades.
# Show compat + pending migration status
python3 sf.py repo status
# Preview migration plan only
python3 sf.py repo upgrade --dry-run
# Apply all pending migrations and commit changes
python3 sf.py repo upgrade
# Validate datarepo against PLM SPEC
python3 sf.py repo validateNotes:
repo upgradealways runs post-upgrade validation (normal workflow).
Start the Flask-based web UI. By default this also starts the read-only MCP
server in the same runtime on the same port under /mcp, using the same resolved datarepo.
python3 sf.py web --port 8080 --host 0.0.0.0 --debug- Flags:
--port,--host,--debug(auto-reload when MCP integration is disabled) - MCP is enabled by default:
- Web UI:
http://<host>:<port> - MCP (streamable HTTP):
http://<host>:<port>/mcp
- Web UI:
- MCP env controls:
SF_WEB_ENABLE_MCP(default1)SF_MCP_PATH(default/mcp)
Windsurf MCP config example:
{
"mcpServers": {
"smallfactory": {
"serverUrl": "http://127.0.0.1:8080/mcp"
}
}
}Default available tools:
server_statusrepo_infodata_model_guideentities_searchentity_getinventory_onhandparts_inventory_list(bulk part table; supportsquery,status_bucket,qty_gte/qty_lte,sort_by,sort_dir, cursor pagination)bom_resolvedbuild_events_listanalytics_query
Compatibility resources (for resource-first MCP clients):
mcp://smallfactorysmallfactory://status(alias:mcp://smallfactory/status)smallfactory://repo_info(alias:mcp://smallfactory/repo_info)smallfactory://data_model_guide(alias:mcp://smallfactory/data_model_guide)smallfactory://inventory/summary(alias:mcp://smallfactory/inventory_summary)smallfactory://parts/quantities(alias:mcp://smallfactory/parts_quantities)
Validate the datarepo against the PLM SPEC.
python3 sf.py repo validate
python3 sf.py repo validate --strict # non-zero exit on warnings too
python3 sf.py repo validate --no-git # skip commit metadata checks
python3 sf.py repo validate --no-entities --no-inventory # only run git checks
python3 sf.py repo validate --git-commits 50 # scan only the last 50 commitsOutputs human/json/yaml based on -F/--format. With --strict, warnings trigger non-zero exit.
Flags:
--strict— exit non-zero on warnings as well as errors.--no-entities— skip validation ofentities/.--no-inventory— skip validation ofinventory/.--no-git— skip Git commit metadata checks.--git-commits <N>— limit number of recent commits scanned for required::sfid::<SFID>tokens (0= scan all).
Inventory is modeled as an append-only journal; on-hand is computed from journals.
Append a journal entry for a part.
python3 sf.py inventory post \
--part p_m3x10 \
--qty-delta +5 \
--l_sfid l_inbox \
--reason "cycle count adjustment"--partis required.--l_sfidoptional; defaults toinventory.default_locationinsfdatarepo.yml.
Report on-hand quantities. Filter by part or location.
# Summary across all parts and locations
python3 sf.py inventory onhand
# For a specific part
python3 sf.py inventory onhand --part p_m3x10
# For a specific location
python3 sf.py inventory onhand --l_sfid l_inbox
# Machine-readable
python3 sf.py -F json inventory onhand --part p_m3x10
# Read-only computation (no cache writes)
python3 sf.py inventory onhand --readonly
# Read-only for a specific part
python3 sf.py inventory onhand --part p_m3x10 --readonlyNote: By default, computing on-hand may write cache files. Use --readonly to prevent any writes (pure read).
Rebuild on-hand caches from the journal (idempotent).
python3 sf.py inventory rebuildCanonical metadata operations for entities (parts, builds, locations, etc.).
python3 sf.py entities add p_widget name="Widget" uom=eapython3 sf.py entities lspython3 sf.py entities show p_widgetUpdate fields on an entity.
# General fields
python3 sf.py entities set p_widget description="A handy widget" category=fastener
# Build metadata
python3 sf.py entities set b_2024_0001 serialnumber=SN12345 datetime=2024-06-01T12:00:00Zpython3 sf.py entities retire p_widget --reason "obsolete"Set build-specific fields.
python3 sf.py entities build serial b_2024_0001 SN12345
python3 sf.py entities build datetime b_2024_0001 2024-06-01T12:00:00ZManage part revisions (PLM SPEC-compliant). bump cuts and immediately releases a revision (default: next numeric label); release releases a specific label and flips the released pointer.
# Create and release next revision with optional notes
python3 sf.py entities revision bump p_widget --notes "Initial release"
# Create and release a specific custom label
python3 sf.py entities revision bump p_widget --rev A01 --notes "Alpha release"
# Release a specific revision label
python3 sf.py entities revision release p_widget 1 --notes "Hotfix"Additional timing examples:
# Bump and set a specific released-at timestamp
python3 sf.py entities revision bump p_widget --released-at 2024-06-01T09:00:00Z --notes "Production cutover"
# Release label 2 with an explicit timestamp
python3 sf.py entities revision release p_widget 2 --released-at 2024-06-15T17:30:00Z --notes "ECN-42"Manage working files under an entity's files/ folder.
# List
python3 sf.py entities files ls p_widget --path drawings --recursive --glob "**/*.pdf"
# Create/remove folders
python3 sf.py entities files mkdir p_widget drawings
python3 sf.py entities files rmdir p_widget drawings
# Upload/delete/move files
python3 sf.py entities files add p_widget ./local/file.pdf drawings/file.pdf --overwrite
python3 sf.py entities files rm p_widget drawings/file.pdf
python3 sf.py entities files mv p_widget drawings/file.pdf drawings/file_rename.pdf --overwrite
# Move a directory
python3 sf.py entities files mv p_widget drawings drawings_v2 --dir --overwriteManage build events for b_* entities.
# List events
python3 sf.py entities events ls b_2024_0001
# Append an event with tags
python3 sf.py entities events append b_2024_0001 --message "Bench test failed" --tags repair,task_open
# Append and link existing files/ paths
python3 sf.py entities events append b_2024_0001 --message "Added evidence" \
--file "event_attachments/evt_demo/log.txt" \
--file "event_attachments/evt_demo/scope.png"
# Append and upload local files in one command
python3 sf.py entities events append b_2024_0001 --message "Created with uploads" \
--upload ./captures/log.txt \
--upload ./captures/scope.png
# Update fields on an event
python3 sf.py entities events update b_2024_0001 evt_20260227193308 --message "Reworked and retested"
# Replace tags on an event
python3 sf.py entities events tags b_2024_0001 evt_20260227193308 --tags repair,measurement
# Link a files/ path to an existing event
python3 sf.py entities events link-file b_2024_0001 evt_20260227193308 "event_attachments/evt_20260227193308/photo.png"Notes:
- Event storage is
entities/<b_sfid>/events.jsonl(JSON Lines). - Allowed event fields are fixed:
id,ts,tags,message,files.
Operate on a parent part's BOM.
# Full tree
python3 sf.py bom ls p_widget
# Limit recursion depth
python3 sf.py bom ls p_widget --max-depth 1python3 sf.py bom add p_widget --use p_screw --qty 4 --rev released \
--index 0 --alt p_screw_alt --alternates-group screwsNotes:
- Use
--no-check-existsonbom addto skip verifying that the child and alternates exist (advanced).
# By index
python3 sf.py bom rm p_widget --index 0
# By child SFID (first match)
python3 sf.py bom rm p_widget --use p_screw
# Remove all matching uses
python3 sf.py bom rm p_widget --use p_screw --allpython3 sf.py bom set p_widget --index 0 --qty 2 --rev released --alternates-group screwsNotes:
bom setalso supports--no-check-existsto bypass child existence checks when changing--use.
python3 sf.py bom alt-add p_widget --index 0 --use p_screw_alt
python3 sf.py bom alt-rm p_widget --index 0 --alt-index 0
# or by alternate SFID
python3 sf.py bom alt-rm p_widget --index 0 --alt-use p_screw_altNotes:
bom alt-addsupports--no-check-existsto skip verifying the alternate exists.
Generate a multi-page PDF, one sticker per page, with QR codes and optional text fields.
Dependencies: qrcode[pil], pillow, reportlab.
# Provide SFIDs directly
python3 sf.py stickers --sfids p_widget,p_case --out labels.pdf --fields=name,rev --size=2x1 --dpi=300 --text-size=24
# From a file (one-per-line or comma-separated)
python3 sf.py stickers --file sfids.txt --out labels.pdf
# From stdin
cat sfids.txt | python3 sf.py stickers --sfids - --out labels.pdfOptions (same for stickers and stickers batch):
--sfidsComma/newline separated SFIDs (use-to read from stdin)--fileFile containing SFIDs--fieldsExtra fields to print (comma-separated), in addition to name/SFID--sizeSticker size in inchesWIDTHxHEIGHT(default2x1)--dpiDots per inch (default300)--text-sizeBase text size in px (default24)-o, --outOutput PDF filename (defaultstickers.pdf)
- Use
-F jsonor-F yamlto get machine-readable outputs. - Human mode prints friendly summaries; some commands print YAML by default.
- To target a specific datarepo, use
-R /path/to/repoor setSF_REPO. - Default inventory location is configured in
sfdatarepo.ymlunderinventory.default_location(scaffolded asl_inbox).