Skip to content

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.

Terminal window
npm install -g @saas-maker/cli
# or one-shot:
npx @saas-maker/cli --help
Terminal window
fnd login # browser OAuth → stores session in ~/.foundry/config.json
fnd init # link the current directory to a project (writes foundry.json)
fnd doctor # verify auth, linked project, and standards compliance
CommandPurpose
fnd loginBrowser OAuth login. Stores a session token in ~/.foundry/config.json.
fnd whoamiShow the logged-in user and any linked project for this directory.
fnd keysPrint the session token and the linked project key.
fnd initLink the current directory to a project (creates foundry.json).
fnd doctor (alias fnd audit)Check Foundry compliance and configuration drift.
fnd statusSnapshot of feature counts and health for the linked project.
fnd projects list|create|update|deleteProject management.
fnd feedback, fnd roadmap, fnd changelog, fnd testimonials, fnd waitlistPer-service helpers.
fnd forgeScaffold a new Foundry-compliant project.
fnd fleet …Fleet-wide automation (see below).
fnd examplesPrint 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.

Terminal window
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 session — sends Authorization: Bearer <token> from fnd login.
  • --auth project — sends X-Project-Key from the linked foundry.json.
  • --auth auto (default) — uses whichever auth context is available.
  • --auth none — no auth (public endpoints).
FlagEffect
--body '<json>'Inline JSON body.
--body-file ./payload.jsonRead body from disk.
--query key=valueAppend a query param (repeatable).
--header key=valueAdd a request header (repeatable).
--output json|tablePick output format.
--select field1,field2Project specific fields (supports dotted paths).
--rawCompact JSON, suitable for piping.
--quietSuppress request/status logs.
--token <t> / --project-key <k>Override stored credentials.
--no-validateSkip OpenAPI enforcement (for experimental routes).
Terminal window
fnd api GET /health --auth none
Terminal window
# List projects you own
fnd api GET /v1/projects --auth session --output table
# Update the dashboard README / notes for a project
fnd api PATCH /v1/projects/<projectId> --auth session \
--body '{"readme":"Internal launch notes and owner context."}'
Terminal window
# List your tasks, filter by project
fnd api GET /v1/tasks --auth session --query project_slug=my-app --output table
# Create a task
fnd api POST /v1/tasks --auth session \
--body '{"title":"Ship invite flow","project_slug":"my-app","priority":"high"}'
# Move a task forward
fnd api PATCH /v1/tasks/<taskId> --auth session \
--body '{"status":"in_progress"}'
# Add a comment
fnd api POST /v1/tasks/<taskId>/comments --auth session \
--body '{"body":"PR opened — waiting on review.","author_type":"agent"}'
Terminal window
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 table
Terminal window
# 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 project
fnd api POST /v1/ai/chat/completions --auth project \
--body '{"messages":[{"role":"user","content":"Write release notes"}]}'
# Inspect usage and request logs
fnd api GET /v1/ai/usage --auth session --query project_id=<projectId> --output table
Terminal window
fnd api GET /v1/symphony/memory --auth session
fnd 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 table
fnd api GET /v1/symphony/runs --auth session --output table

Agents should add publishable marketing ideas directly to the queue, then the owner accepts/rejects and marks accepted ideas as sent after posting.

Terminal window
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.

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.

Terminal window
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 table

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.

Terminal window
# Enqueue (producer)
fnd api POST /v1/tasks --auth session --body '{"title":"Audit homepage","capability":"audit","project_slug":"linkchat"}'
# Worker wake-loop: claim → do → report
fnd api POST /v1/tasks/claim --auth session --body '{"worker":"psi-swarm@laptop","capability":"audit","lease_seconds":900}' # 204 = empty
fnd 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.

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.

CommandWhat it does
fnd fleet listList discovered projects with status and slugs.
fnd fleet run "<cmd>"Run a shell command across every project.
fnd fleet auditStandards + code health pass.
fnd fleet fixApply standards drift fixes automatically.
fnd fleet versions [list|fix]Surface and resolve dependency-version drift.
fnd fleet secrets-syncPush shared env values to per-project .env.local files.
fnd fleet clean [--deep]Reclaim disk space by purging build caches.
fnd fleet provisionProvision a project’s cloud surfaces.
fnd fleet apply <skill>Run an evolutionary refactor across every repo.
fnd fleet superviseWatch the global error feed and dispatch fixes via agents.

Created by fnd login:

{
"apiKey": "sm_...",
"apiBaseUrl": "https://api.sassmaker.com"
}

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.

  • FND_API_URL — point the CLI at a different API base URL (useful for staging or local development).

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:

Terminal window
pnpm generate:openapi

This 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.