Skip to content
Local environment Preproduction — not production data

GitHub & source control

Factorial Code stores your code in the cloud, and the workspace console is a full editor — you never have to use Git. But a team that wants pull requests, code review, branch history or CI can have all of it: fcode team:clone lays every App and global workspace the team owns out on disk in one predictable structure, which is exactly what a repository needs.

Install the CLI and clone your development team:

Terminal window
$ pnpm install -g @factorialco/fcode-cli
$ fcode login
$ fcode team:clone

You get one folder per App, each holding a checkout of that App’s development workspace, and the global workspaces the team owns under global-workspaces/:

📦 acme-payroll
┣ 📂 .fcode
┃ ┗ 📜 team.json # the team, the Apps and the global workspaces cloned into it
┣ 📂 .claude
┃ ┗ 📂 skills # agent skills, installed once for the whole team
┣ 📂 global-workspaces
┃ ┗ 📂 acme-base # a global workspace the team owns: processes, modules, variables…
┣ 📂 payroll-sync
┃ ┣ 📜 settings.json # the App — name, description, id
┃ ┗ 📂 app # the App's workspace: processes, modules, variables…
┗ 📂 time-off-sync
┗ ...

One repository for the whole team folder is the simplest thing that works: a single clone gives a new developer every App, one CI configuration covers them all, and a change touching two Apps is one pull request.

Terminal window
$ cd acme-payroll
$ git init
$ git add .
$ git commit -m "Import Acme Payroll apps from Factorial Code"
$ git remote add origin [email protected]:acme/factorial-code-apps.git
$ git push -u origin main

Add a .gitignore at the team root first — the agent skills are a local install, not source:

.claude/skills
.agents
skills-lock.json

What each App’s workspace already excludes for you (its .gitignore is written by the CLI): variables.local.env, datastore.json, .env, storage/*, and every inherited file, which the parent workspace owns and fcode pull regenerates.

Keep .fcode/ in the repository. It records which cloud workspace each folder points at, so a teammate who clones the repository gets folders fcode already recognises — fcode pull in an App’s app folder just works, with no second setup step. The cost is that its per-resource sync hashes can conflict when two people push around the same time; resolve by taking either side and running fcode team:pull, which rewrites them from the cloud.

The cloud is the source of truth for what actually runs. A process executes the version stored in its workspace, not the one on your branch — merging a pull request changes nothing in Factorial Code until someone pushes. Treat the repository as history, review and backup, and keep one habit: pull before you work, push when you are done.

Terminal window
$ fcode team:pull # from the team root: clone new Apps, pull every workspace
$ fcode team:status # what differs from the cloud, App by App
$ git diff # …and what that changed in the repository

fcode team:pull also picks up Apps created since your last run and refreshes each App’s settings.json. An App removed from the team is reported but never deleted — the folder stays until you remove it yourself.

Pushing is deliberately per App. There is no team:push: you run fcode push from inside the App’s app folder, so a deploy is always one App at a time.

Terminal window
$ cd payroll-sync/app
$ fcode push

Automating a push to the dev workspace is rarely what you want: dev is where you and the console edit interactively, and a job pushing on every merge would fight it. The push worth automating is the promotion to production, which is exactly the fcode push the release flow leaves for a human to run.

Two things have to be true before a job can do it:

  • The release exists. Requesting a release in the console is what creates the prod-{app} workspace, snapshots dev as a workspace version carrying that release, and grants write access to the qualifying members of the owning team. Until then there is nothing to push to.
  • The identity is authorized. Only operators and the App’s qualifying team members may write to a prod workspace. Create the API credential in the prod workspace — someone who cannot promote by hand cannot create one either — and store its Client ID and Secret as repository secrets.

The checkout points at the dev workspace it was cloned from, so the job has to re-point it before pushing, or it would promote onto dev. The prod slug is per App, so keep it in a repository variable:

name: Promote to production
on:
push:
branches: [main]
jobs:
promote:
runs-on: ubuntu-latest
defaults:
run:
working-directory: payroll-sync/app
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm install -g @factorialco/fcode-cli
- run: fcode login -c "$CLIENT_ID" -s "$CLIENT_SECRET"
env:
CLIENT_ID: ${{ secrets.FCODE_CLIENT_ID }}
CLIENT_SECRET: ${{ secrets.FCODE_CLIENT_SECRET }}
# Without this the push would land on the dev workspace the folder was cloned from.
- run: fcode remote:set "$PROD_WORKSPACE"
env:
PROD_WORKSPACE: ${{ vars.FCODE_PROD_WORKSPACE }} # e.g. prod-b5hiralj5hpupls4otok6vxah
- run: fcode add
- run: fcode push

fcode add registers the resources against the newly targeted remote, which starts out tracking nothing — the same sequence the promotion in the CLI page performs by hand.

Three things to know before you rely on this:

  • Everything is per App. The prod slug, the API credential and the working-directory all belong to one App, so a repository holding several needs one job (or matrix entry) each.
  • Prod code is meant to be read-only. This automates the promotion; it does not make the repository authoritative over dev. Changes still start in the dev workspace and reach production through a release.
  • A diverged prod fails the job, which is what you want: it stops rather than discarding something promoted another way. Resolve it by pulling prod into the branch rather than reaching for --force.

Publishing the workspace version and moving the stable alias stays a separate, deliberate step — see workspace versioning.