A .NET CLI tool for LLM-based coding agents to interact with the Quotation Factory API via Bearer token authentication. Includes 87 agent skill documents covering the full quoting workflow, configuration, interactive UI guides, per-page Settings references, and 656 validated API endpoint references.
- API Discovery —
qfc api discoverlists all endpoints grouped by tag, with--search,--tag,--method, and--compactfilters. Compact mode outputs one line per endpoint for minimal context window usage. - Endpoint Inspection —
qfc api describe <path>shows parameters, request/response schemas, idempotency hints, and a cross-platform example call. Use--expand-refsto inline referenced schema types. - Generic REST commands —
qfc api get|post|put|delete <path>with--param,--query,--body, and--body-fileoptions.--body-filereads JSON from a file, avoiding PowerShell 5.1 quote-stripping issues with--body. - Response filtering —
--fields a,b,ckeeps only selected fields,--max-items Ntruncates arrays with metadata,--quietsuppresses output on success. - Batch operations —
qfc api batch --ops '[...]'executes multiple API calls in a single invocation with optional--continue-on-error. - Nesting summary —
qfc api nesting-summaryaggregates cutting plan utilization and remnant strategies for a project in one command. Auto-detects partyId/projectId from navigation context whenqfc serveis running. - Dry-run validation —
--dry-runon POST/PUT validates request body against the OpenAPI schema without sending. - File uploads —
--fileand--formoptions for multipart/form-data uploads (geometry files, engineering drawings). - Bearer token auth — persistent storage via
qfc auth set-token(survives across shell sessions) orQFC_ACCESS_TOKENenv var. - Live UI preview —
qfc servereverse-proxies the Rhodium24 web app through localhost with automatic Auth0 authentication. Factory mode (full UI) or portal mode (--party-idfor self-service buyer view). Multi-layer caching (persistent disk cache, HTML cache, speculative API prefetch) — page loads in ~500ms with warm cache. - LLM-optimized output — pretty-printed JSON to stdout, null fields omitted, no unicode escapes, auto-generated summaries for undocumented endpoints,
_nextguidance fields, error body truncation (500 chars), and fuzzy tag suggestions on empty results. - Cross-platform — all output, examples, and commands work identically on Windows (PowerShell, cmd), macOS, Linux, and Git Bash. No platform-specific syntax in generated commands.
- Global .NET tool — install once, use everywhere as
qfc. - 87 agent skills — workflow recipes, reference docs, and interactive UI guides in
.claude/commands/(available as/slash-commands) covering the full quoting lifecycle, configuration, and troubleshooting. Skills include machine-readablerequires:/next:dependency frontmatter for automated workflow planning. - Updatable skills —
qfc skills updateinstalls verified skills from the latest GitHub release without reinstalling the CLI.
The recommended workflow for an AI agent using this CLI:
# 1. Authenticate (persistent — survives across sessions, works on all platforms)
qfc auth set-token eyJ...
# 2. Discover available endpoints (--compact saves context window tokens)
qfc api discover --compact # one-line-per-endpoint text output
qfc api discover --search projects # search by keyword
qfc api discover --tag Project # filter by tag (exact tag name)
qfc api discover --method POST # filter by HTTP method
# 3. Inspect an endpoint before calling it (includes idempotency hint)
qfc api describe /api/parties/{partyId}/projects --method POST
qfc api describe /api/parties/{partyId}/projects --method POST --expand-refs
# 4. Validate before sending (--dry-run checks body against schema)
qfc api post /api/parties/{partyId}/projects \
--param partyId=<uuid> --body "{\"name\":\"Test\"}" --dry-run
# 5. Call the endpoint (double-quoted body works on all platforms)
qfc api get /api/parties/{partyId}/projects \
--param partyId=550e8400-e29b-41d4-a716-446655440000 \
--query Page=1 --query PageSize=25
# 6. Filter large responses to save context window
qfc api get /api/parties/{partyId}/projects/getProjectsPaged \
--param partyId=<uuid> --query Page=1 \
--max-items 5 --fields id,name,status
# 7. Batch multiple calls in one invocation
qfc api batch --ops '[{"method":"GET","path":"/api/parties/{partyId}/projects","params":{"partyId":"<uuid>"}}]'Persistent storage (recommended):
qfc auth set-token eyJ...Or via environment variable (current session only):
# PowerShell
$env:QFC_ACCESS_TOKEN = "eyJ..."# Bash
export QFC_ACCESS_TOKEN="eyJ..."To get a token:
- Open https://rhodium24.io/app in your browser
- Open Developer Tools (F12) → Network tab
- Search for
getProjectsPagedin the filter - Click the request → Headers → copy the
Authorizationvalue (after "Bearer ")
Run qfc auth login for step-by-step instructions, or qfc auth status to check.
# Set the API base URL (default already points to https://api.rhodium24.io/backend)
qfc config set api-base-url https://api.rhodium24.io/backend
# (Optional) Point to a local swagger.json to override the auto-cached spec
qfc config set swagger-path /path/to/swagger.json
# Show current config
qfc config showThe API spec (swagger.json) is fetched automatically from the server on first use and cached locally for 24 hours. No manual configuration needed. If the server is unreachable, the stale cache is used as fallback.
Or use environment variables (also supported via a .env file in the project root):
| Variable | Description |
|---|---|
QFC_ACCESS_TOKEN |
Bearer token (takes precedence over stored token). Use qfc auth set-token for persistent storage instead. |
QFC_API_BASE_URL |
API base URL |
QFC_SWAGGER_PATH |
Path to a local swagger.json to override auto-caching |
Configuration precedence (highest to lowest): environment variables > .env file > settings.json > auto-cache > remote fetch.
# List all endpoints — compact mode for LLMs (3x smaller output)
qfc api discover --compact
# Full JSON output with all metadata
qfc api discover
# Search endpoints by keyword
qfc api discover --search quotation
# Filter by tag and/or HTTP method
qfc api discover --tag Project --method GET
# Get full details for a specific endpoint
qfc api describe /api/parties/{partyId}/projects --method GET
# Expand referenced types inline (shows AddressDto fields etc.)
qfc api describe /api/parties/{partyId}/projects --method POST --expand-refs
# The describe output includes:
# - Path and query parameters (null fields omitted)
# - Request body schema with required fields highlighted
# - Response schemas
# - Cross-platform example call (double-quoted, no single quotes)
# - _next field with a ready-to-run command
# - Auto-generated summary when the API spec has none# GET with path parameter substitution
qfc api get /api/parties/{partyId}/projects \
--param partyId=550e8400-e29b-41d4-a716-446655440000
# POST with body (double-quoted — works on bash, PowerShell, cmd, and Git Bash)
qfc api post /api/parties/{partyId}/projects \
--param partyId=550e8400-e29b-41d4-a716-446655440000 \
--body "{\"name\":\"New Project\"}"
# With query parameters
qfc api get /api/parties/{partyId}/projects \
--param partyId=550e8400-e29b-41d4-a716-446655440000 \
--query Page=1 --query PageSize=25 --query Search=steel
# File upload (multipart/form-data)
qfc api post /api/parties/{partyId}/projects/{projectId}/uploadGeometryWithDetails \
--param partyId=<uuid> --param projectId=<uuid> \
--file "geometryFiles[0][email protected]" \
--form "geometryFiles[0].quantity=4"
# DELETE
qfc api delete /api/parties/{partyId}/projects/{projectId} \
--param partyId=<uuid> --param projectId=<uuid>
# Suppress output on success (--quiet)
qfc api put /api/parties/{partyId}/projects/{projectId}/status \
--param partyId=<uuid> --param projectId=<uuid> \
--body "{\"status\":\"Quoted\"}" --quiet
# Truncate large arrays (--max-items) and filter fields (--fields)
qfc api get /api/parties/{partyId}/projects/getProjectsPaged \
--param partyId=<uuid> --max-items 10 --fields id,name,status
# Validate body against schema without sending (--dry-run)
qfc api post /api/parties/{partyId}/projects \
--param partyId=<uuid> --body "{\"name\":\"Test\"}" --dry-run
# Execute multiple operations in one invocation (batch)
qfc api batch --ops '[
{"method":"GET","path":"/api/parties/{partyId}/projects","params":{"partyId":"<uuid>"}},
{"method":"GET","path":"/api/user"}
]'
qfc api batch --ops '[...]' --continue-on-error
# Health check
qfc healthqfc config show| Document | Location | Purpose |
|---|---|---|
| Setup & Configuration | dist/SETUP-PROMPT.md |
Installation, authentication, skill index — paste into Claude Desktop as a project prompt |
| Prompt Examples | dist/PROMPT-EXAMPLES.md |
25+ example prompts showing how to use each skill |
| Skill Map | .claude/commands/qf-cli.md |
Central skill index with "I want to..." lookup table, quoting workflow steps, configuration prerequisites, and troubleshooting guide |
| Prerequisites | .claude/commands/qf-prerequisites.md |
Shared warnings and conventions referenced by all skills |
| Glossary | .claude/commands/qf-glossary.md |
Canonical definitions for BOM item, working step, resource, nesting, etc. |
| Enums | .claude/commands/qf-enums.md |
Centralized project statuses, working step types (84), issue codes (28), rule editor models (13) |
src/QuotationFactory.Cli/
├── Authentication/ # Bearer token provider (persistent file + env var, 30s cache)
├── Commands/ # CLI command handlers
│ ├── ApiCommands.cs # get/post/put/delete with --quiet, --max-items, --fields, --dry-run
│ ├── BatchCommand.cs # qfc api batch — multi-operation sequential execution
│ ├── NestingSummaryCommand.cs # qfc api nesting-summary — cutting plan utilization + remnant settings
│ ├── DiscoverCommand.cs # qfc api discover — endpoint listing with mtime-cached spec
│ ├── DescribeCommand.cs # qfc api describe — endpoint detail with idempotency hints
│ └── ServeCommand.cs # qfc serve — orchestrator for the reverse proxy server
├── Serve/ # Reverse proxy server components
│ ├── InjectedScripts.cs # Auth injection, context tracking, guide engine, PDF polyfill JS
│ ├── TtsHandler.cs # TTS endpoints, cache, ElevenLabs/OpenAI integration
│ ├── GatewayHandler.cs # Gateway API endpoints (discover, describe, tags, schema)
│ ├── GuideHandler.cs # Guide overlay + selector validation HTTP endpoints
│ ├── WebSocketRelay.cs # WebSocket bidirectional proxy with proper cleanup
│ └── AssetCache.cs # Gzip compression, disk cache, CachedAsset/CachedHtml records
├── Configuration/ # Settings storage & environment overrides (.env walk capped to 5 levels)
├── Gateway/ # GatewayClient with persistent down-cache (60s TTL)
├── OpenApi/ # OpenAPI spec parsing
│ ├── OpenApiSpecProvider.cs # Spec loader and endpoint extraction
│ ├── SchemaFlattener.cs # JSON Schema to flat property list
│ └── EndpointInfo.cs # Data records for endpoints, params, schemas
├── Output/ # JSON output helpers (error body truncation at 500 chars)
└── Program.cs # Entry point
.claude/commands/ # 87 slash commands (agent skill documents)
├── qf-cli.md # Core CLI usage + Skill Map + "I want to..." selection table
├── qf-prerequisites.md # Shared warnings & conventions (referenced by all skills)
├── qf-glossary.md # Canonical domain term definitions
├── qf-enums.md # Centralized enum values (statuses, working steps, issue codes)
├── qf-auth.md # Authentication setup
├── qf-context.md # Navigation context (partyId, projectId, bomItemId)
├── qf-create-project.md # Project creation workflow
├── qf-upload-geometry.md # Geometry file upload
├── qf-analyze-drawing.md # Drawing analysis (OCR)
├── qf-detect-purchasing-part.md # Purchasing part detection
├── qf-set-material.md # Material assignment
├── qf-add-working-step.md # Working step addition
├── qf-lookup-customer.md # Customer lookup
├── qf-projects.md # Project management & status
├── qf-bom-item.md # BOM item details & overrides
├── qf-quotations.md # Quotations, RFQ, price requests
├── qf-business-rules.md # 13 business rule models
├── qf-resources.md # Machines & Excel estimators
├── qf-materials.md # Articles, standards, keywords
├── qf-financial.md # Currency, terms, calculation
├── qf-delivery.md # Delivery calendar & units
├── qf-production.md # Routes, nesting, machining
├── qf-suppliers.md # Address book & suppliers
├── qf-portal.md # Portal & email config
├── qf-party.md # Company profile & agents
├── qf-party-pricing.md # Party-level pricing rules
├── qf-users.md # Users, roles, access
├── qf-imnoo.md # Imnoo CNC integration
├── qf-analytics.md # Segment.io CDP events
├── qf-guide-author.md # Meta-skill: authoring interactive UI guides
├── qf-guide-create-project.md # Guide: create a new project walkthrough
├── qf-guide-project-states.md # Guide: project lifecycle states tour
├── qf-guide-upload-geometry.md # Guide: upload CAD files walkthrough
├── qf-guide-cad-drawings.md # Guide: CAD + engineering drawings tour
├── qf-guide-dynamic.md # Dynamic guide generation from user questions
└── understand*.md # 8 codebase understanding skills
All 25 workflow/configuration skills include requires: and next: YAML frontmatter fields, forming a machine-readable dependency graph for automated workflow planning. The 6 guide skills define interactive UI walkthroughs with DOM selectors, narration text, and step-by-step playback instructions.
The cross-platform qfc-cli.zip contains:
bin/win-x64/qfc.exe— Windows (x64) self-contained binarybin/linux-x64/qfc— Linux (x64) self-contained binarybin/osx-x64/qfc— macOS (Intel) self-contained binarybin/osx-arm64/qfc— macOS (Apple Silicon) self-contained binaryskills/— all 87 agent skill documents (with dependency frontmatter)README.md— project overview, architecture, and LLM integration tipsSETUP-PROMPT.md— installation and configuration instructionsPROMPT-EXAMPLES.md— 25+ example prompts for using the skills.env.example— configuration template (copy to.envand fill in)
CLAUDE.md is intentionally not included — it is the internal development guide for working on the CLI, not end-user material. The zip is assembled by build/pack.ps1, which stages exactly this manifest; keep the script and this list in sync.
Releases also provide qfc-skills.zip. Install or refresh its contents in the current project with:
qfc skills update
qfc skills update --prerelease
qfc skills update --version 0.0.1-beta
qfc skills update --skills-dir .claude/commandsThe updater verifies the release checksum and preserves files it did not install.
No .NET runtime required on any platform.
# Build
dotnet build
# Run tests
dotnet test
# Run locally (development)
dotnet run --project src/QuotationFactory.Cli -- auth status
# Publish self-contained binaries (all platforms)
dotnet publish src/QuotationFactory.Cli -c Release -r win-x64 --self-contained -o dist/bin/win-x64
dotnet publish src/QuotationFactory.Cli -c Release -r linux-x64 --self-contained -o dist/bin/linux-x64
dotnet publish src/QuotationFactory.Cli -c Release -r osx-x64 --self-contained -o dist/bin/osx-x64
dotnet publish src/QuotationFactory.Cli -c Release -r osx-arm64 --self-contained -o dist/bin/osx-arm64The CLI is designed for unattended agent use where silent failures are worse than loud ones:
- Atomic file writes — config, token, and swagger cache files use write-to-temp-then-rename to prevent corruption from crashes or concurrent access.
- Corrupted config recovery — malformed
settings.jsonor token files fall back to defaults with a warning instead of crashing. - Swagger cache validation — the cached spec is validated on load (must be a JSON object with a
pathsproperty). Corrupted caches are auto-deleted and re-fetched. Cross-invocation mtime caching skips re-parsing when the file hasn't changed. - Circular
$refprotection — the OpenAPI schema flattener detects circular references and broken$refpointers, preventing infinite loops on malformed specs. - HTTP timeouts — all outbound HTTP calls have explicit timeouts (30s for spec fetch, 2min for proxy, 30s for TTS).
- Gateway down cache — when
qfc serveis not running, the failed availability check is cached to disk for 60 seconds, skipping the 150ms probe on subsequent cold invocations. - Error body truncation — error response bodies are capped at 500 characters to prevent context window overflow, with a
fullErrorTruncatedflag when content was trimmed. - Expired env var auto-fallback — when
QFC_ACCESS_TOKENholds an expired token but a valid stored token exists, the CLI automatically uses the stored token. Prevents stale env vars (baked into Windows User environment or inherited by long-running parent processes) from permanently breaking auth. - Bounded
.envsearch — the upward directory walk for.envfiles is capped at 5 levels to prevent pathological traversal on deeply nested paths. - Gzip-compressed proxy responses — HTML, JS, and CSS proxied from the upstream are gzip-compressed before serving. The upstream response is decompressed for URL rewriting and auth injection, then re-compressed. Reduces the 3.1 MB JS bundle to ~1 MB on the wire.
- Persistent disk cache — processed JS/CSS assets (gzip-compressed, URL-rewritten) are saved to
%LOCALAPPDATA%\QuotationFactory.Cli\asset-cache\and loaded into memory on server startup. Assets survive server restarts — only the very first launch ever fetches from upstream. - Startup prefetch — a background task fetches the SPA HTML page, discovers asset URLs, caches any missing assets, and pre-warms
/api/user— all before the browser connects. - HTML page cache — processed HTML (auth-injected, CSP-stripped) cached for 5 minutes with token-based invalidation. Eliminates ~1s upstream round-trip on repeated page loads.
- Speculative API prefetch —
/api/useris fetched in background when HTML is first requested. By the time the SPA's JS loads and calls it (~1-2s later), the response is already cached (2ms instead of 1000ms). - Deferred server init — OpenAPI spec loading and ElevenLabs voice resolution run in background tasks. The server starts accepting requests within ~900ms; gateway endpoints return 503 until spec loads (~200ms later).
- Preview-compatible readiness — root
/returns 200 (with JS redirect) instead of 302, so the Claude Code preview UI's readiness poll detects the server immediately rather than timing out. - Unused script stripping — Appcues script tags are stripped from proxied HTML to eliminate blocked network requests.
- Resource leak prevention — file streams in multipart uploads are tracked and disposed on validation errors; TTS audio cache is bounded (500 entries / 200MB).
- Stable cache keys — TTS cache uses SHA256 hashing instead of
GetHashCode()for deterministic, collision-resistant keys. - PowerShell 5.1 quote-stripping detection —
--bodyarguments with nested JSON lose their inner double quotes in PS 5.1 (a known platform bug). The CLI detects mangled bodies (JSON without quotes) and returns aBODY_QUOTES_STRIPPEDerror pointing to--body-fileas the fix. --body-fileoption — reads request body from a JSON file instead of a command-line argument, bypassing shell quoting issues entirely.--fieldsempty-match warning — when--fieldsmatches zero properties, a stderr warning lists the available property names so the caller can correct without a round-trip.- WebSocket relay cleanup — bidirectional WebSocket proxy properly cancels and awaits both relay directions before disposing sockets, preventing orphaned tasks.
- Gateway decompression — the gateway client handles gzip-compressed responses from the proxy, preventing raw binary output.
234 unit tests cover these behaviors, including dedicated robustness tests for each resilience mechanism. All tests use isolated temp directories — they never read or write real user data (tokens, config).
- Use
--compactfor discovery —qfc api discover --compactoutputs one line per endpoint (~90KB vs ~276KB full JSON). Use full JSON only when you need parameter details. - Use
--expand-refsfor describe —qfc api describe <path> --expand-refsinlines referenced schema types so you can see all fields without extra calls. The output includes anidempotentfield so agents know which calls are safe to retry. - Filter large responses —
--max-items 10truncates arrays withtotalCount/truncatedmetadata.--fields id,name,statuskeeps only the fields you need. Both reduce context window usage dramatically. - Use
--quietin pipelines — suppress success output when you only care about errors (e.g. status updates, deletes). - Validate before sending —
--dry-runon POST/PUT checks your JSON body against the OpenAPI schema and reports missing required fields without making the actual API call. - Batch operations —
qfc api batch --ops '[...]'executes multiple API calls sequentially in one invocation. Use--continue-on-errorto process all operations even if some fail. Returns an array of results with success/failure per operation. - Follow the
_nextfield —describeandhealthoutput includes a_nextfield with a ready-to-run command. - Use
--body-filefor JSON bodies — PowerShell 5.1 strips inner double quotes from--bodyarguments. Write JSON to a temp file and pass--body-file path.jsoninstead. The CLI detects mangled bodies and suggests this fix. - Use
--paramfor path parameters —{partyId}in the path is replaced by--param partyId=<value>. Missing params produce aMISSING_PATH_PARAMSerror with the exact param names needed. - Check
didYouMeanon empty results — if--tagreturns nothing, the output includes fuzzy suggestions and the fullavailableTagslist. - Null fields are omitted — output only includes fields with values. No
"format": nullor"refName": nullnoise. - Errors are structured JSON on stderr — pretty-printed with readable
<angle brackets>, not unicode escapes. Error bodies are truncated to 500 chars to prevent context window overflow. - Use skill dependency frontmatter — each skill's YAML frontmatter has
requires:andnext:fields. Agents can parse these to build a workflow graph and determine prerequisites before executing a skill. - Reference skills for shared knowledge —
qf-prerequisites(shared warnings),qf-glossary(domain terms), andqf-enums(project statuses, working step types, issue codes) are centralized references. Load them once instead of hunting through individual skills. - Swagger auto-caching — the API spec is fetched once and cached for 24 hours with mtime-based revalidation. Set
QFC_SWAGGER_PATHonly if you need to override this with a specific local file.