Foundry CLI (fnd)
fnd (also installable as foundry) is the unified command-line interface for the Foundry platform. It is intentionally small: a few core utility commands plus a universal fnd api <method> <path> executor so new backend features do not require new CLI code.
Install
Section titled “Install”npm install -g @saas-maker/cli# or one-shot:npx @saas-maker/cli --helpFirst run
Section titled “First run”fnd login # browser OAuth → stores session in ~/.foundry/config.jsonfnd init # link the current directory to a project (writes foundry.json)fnd doctor # verify auth, linked project, and standards complianceCommand index
Section titled “Command index”| Command | Purpose |
|---|---|
fnd login | Browser OAuth login. Stores a session token in ~/.foundry/config.json. |
fnd whoami | Show the logged-in user and any linked project for this directory. |
fnd keys | Print the session token and the linked project key. |
fnd init | Link the current directory to a project (creates foundry.json). |
fnd doctor (alias fnd audit) | Check Foundry compliance and configuration drift. |
fnd status | Snapshot of feature counts and health for the linked project. |
fnd projects list|create|update|delete | Project management. |
fnd feedback, fnd roadmap, fnd changelog, fnd testimonials, fnd waitlist | Per-service helpers. |
fnd forge | Scaffold a new Foundry-compliant project. |
fnd fleet … | Fleet-wide automation (see below). |
fnd examples | Print copy-paste API recipes. |
fnd completions [bash|zsh|fish] | Print a shell completion script. |
fnd api <method> <path> | Universal API client — covers everything else. |
Run any command with --help for full flag documentation.
Universal API command
Section titled “Universal API command”fnd api <method> <path> [options]fnd api is the recommended way to use Foundry from scripts and agents. It validates the request against the bundled OpenAPI spec before sending.
Auth modes
Section titled “Auth modes”--auth session— sendsAuthorization: Bearer <token>fromfnd login.--auth project— sendsX-Project-Keyfrom the linkedfoundry.json.--auth auto(default) — uses whichever auth context is available.--auth none— no auth (public endpoints).
Common flags
Section titled “Common flags”| Flag | Effect |
|---|---|
--body '<json>' | Inline JSON body. |
--body-file ./payload.json | Read body from disk. |
--query key=value | Append a query param (repeatable). |
--header key=value | Add a request header (repeatable). |
--output json|table | Pick output format. |
--select field1,field2 | Project specific fields (supports dotted paths). |
--raw | Compact JSON, suitable for piping. |
--quiet | Suppress request/status logs. |
--token <t> / --project-key <k> | Override stored credentials. |
--no-validate | Skip OpenAPI enforcement (for experimental routes). |
Recipes
Section titled “Recipes”Health check
Section titled “Health check”fnd api GET /health --auth noneProject metadata
Section titled “Project metadata”# List projects you ownfnd api GET /v1/projects --auth session --output table
# Update the dashboard README / notes for a projectfnd api PATCH /v1/projects/<projectId> --auth session \ --body '{"readme":"Internal launch notes and owner context."}'Tasks (Symphony)
Section titled “Tasks (Symphony)”# List your tasks, filter by projectfnd api GET /v1/tasks --auth session --query project_slug=my-app --output table
# Create a taskfnd api POST /v1/tasks --auth session \ --body '{"title":"Ship invite flow","project_slug":"my-app","priority":"high"}'
# Move a task forwardfnd api PATCH /v1/tasks/<taskId> --auth session \ --body '{"status":"in_progress"}'
# Add a commentfnd api POST /v1/tasks/<taskId>/comments --auth session \ --body '{"body":"PR opened — waiting on review.","author_type":"agent"}'Feedback (project key)
Section titled “Feedback (project key)”fnd api POST /v1/feedback --auth project \ --body '{"title":"Bug","description":"Broken CTA","submitter_email":"me@example.com","type":"bug"}'
fnd api GET /v1/feedback --auth project --query type=feature --output tableAI Gateway (BYOK)
Section titled “AI Gateway (BYOK)”# Configure provider for a project (key is masked on read)fnd api PUT /v1/ai/config --auth session --query project_id=<projectId> \ --body '{"ai_base_url":"https://api.openai.com/v1","ai_model":"gpt-4o-mini","ai_api_key":"sk-..."}'
# Proxy a chat completion through the linked projectfnd api POST /v1/ai/chat/completions --auth project \ --body '{"messages":[{"role":"user","content":"Write release notes"}]}'
# Inspect usage and request logsfnd api GET /v1/ai/usage --auth session --query project_id=<projectId> --output tableSymphony memory, audit, and run ledger
Section titled “Symphony memory, audit, and run ledger”fnd api GET /v1/symphony/memory --auth sessionfnd api PUT /v1/symphony/memory --auth session \ --body '{"content":"Prefer Gemini for bounded cheap asks. Keep CI fixes surgical."}'
fnd api GET /v1/symphony/audit --auth session --output tablefnd api GET /v1/symphony/runs --auth session --output tableMarketing queue
Section titled “Marketing queue”Agents should add publishable marketing ideas directly to the queue, then the owner accepts/rejects and marks accepted ideas as sent after posting.
fnd api POST /v1/marketing/posts --auth session \ --body '{"project_slug":"linkchat","channel":"tiktok","status":"generated","source_type":"task","source_id":"<taskId>","task_id":"<taskId>","title":"Short AI video idea title","hook":"0-2s visual hook","body":"AI video brief: scene-by-scene script, shot list, voiceover, caption text, asset prompts, and edit notes.","cta":"One concrete next step."}'
fnd api GET /v1/marketing/posts --auth session --query status=generated --output table
fnd api PATCH /v1/marketing/posts/<postId> --auth session \ --body '{"status":"accepted"}'Default marketing queue ideas to AI-generated reel/video briefs for tiktok,
instagram_reels, or youtube_shorts. Avoid LinkedIn entirely. Use x and
reddit only for non-promotional discussion prompts.
Fleet events (system-of-record)
Section titled “Fleet events (system-of-record)”Spokes publish results and telemetry up to saas-maker via /v1/events — the
append-only fleet system-of-record. Send one event or a batch (array, max 100);
idempotency_key makes outbox retries safe. Only the hub reads the union.
fnd api POST /v1/events --auth session \ --body '{"product":"reel-pipeline","type":"reel.rendered","idempotency_key":"<uuid>","payload":{"reel_id":"r1","result_url":"https://..."}}'
fnd api GET /v1/events --auth session --query product=reel-pipeline --output tableTask queue (workers claim work on wake)
Section titled “Task queue (workers claim work on wake)”The Symphony task board doubles as a polling work-queue. Producers enqueue tasks
with a capability; workers claim by capability — claims are atomic and leased,
so two workers never run the same task, and a worker that dies mid-task has its
lease expire and the work reclaimed.
# Enqueue (producer)fnd api POST /v1/tasks --auth session --body '{"title":"Audit homepage","capability":"audit","project_slug":"linkchat"}'
# Worker wake-loop: claim → do → reportfnd api POST /v1/tasks/claim --auth session --body '{"worker":"psi-swarm@laptop","capability":"audit","lease_seconds":900}' # 204 = emptyfnd api POST /v1/tasks/<taskId>/complete --auth session --body '{"worker":"psi-swarm@laptop","result":"p75 LCP 1.8s"}'fnd api POST /v1/tasks/<taskId>/fail --auth session --body '{"worker":"psi-swarm@laptop","error":"timeout"}'The @saas-maker/sdk client.worker.drain({ worker, capability, handler }) wraps
this loop and degrades gracefully — a missing token or unreachable hub ends the
drain quietly, so each service still works standalone.
Fleet automation
Section titled “Fleet automation”The fnd fleet commands operate across a fleet of repositories on disk (default: ~/Desktop/Fleet). They are designed for the maintainer working many repos in parallel — most product users won’t need them.
| Command | What it does |
|---|---|
fnd fleet list | List discovered projects with status and slugs. |
fnd fleet run "<cmd>" | Run a shell command across every project. |
fnd fleet audit | Standards + code health pass. |
fnd fleet fix | Apply standards drift fixes automatically. |
fnd fleet versions [list|fix] | Surface and resolve dependency-version drift. |
fnd fleet secrets-sync | Push shared env values to per-project .env.local files. |
fnd fleet clean [--deep] | Reclaim disk space by purging build caches. |
fnd fleet provision | Provision a project’s cloud surfaces. |
fnd fleet apply <skill> | Run an evolutionary refactor across every repo. |
fnd fleet supervise | Watch the global error feed and dispatch fixes via agents. |
Configuration
Section titled “Configuration”~/.foundry/config.json
Section titled “~/.foundry/config.json”Created by fnd login:
{ "apiKey": "sm_...", "apiBaseUrl": "https://api.sassmaker.com"}foundry.json
Section titled “foundry.json”Created by fnd init in each linked project:
{ "slug": "my-app", "projectId": "uuid-project-id", "projectKey": "pk_..."}Older foundry.json files that store only projectId as a pk_... key are still accepted.
Environment overrides
Section titled “Environment overrides”FND_API_URL— point the CLI at a different API base URL (useful for staging or local development).
OpenAPI enforcement
Section titled “OpenAPI enforcement”fnd api validates method and path against the bundled OpenAPI spec by default. After changing or adding routes in workers/api, regenerate the spec so the CLI accepts the new endpoints:
pnpm generate:openapiThis refreshes packages/cli/src/openapi.json, apps/docs/public/openapi.json, and docs/openapi/openapi.json. To send a request that isn’t in the spec yet, pass --no-validate.