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
┃ ┣ 📜 README.md # the App's README
┃ ┣ 📜 MARKETPLACE.md # its marketplace listing
┃ ┣ 📜 CHANGELOG.md # its releases, generated
┃ ┣ 📂 marketplace
┃ ┃ ┗ 🖼️ logo.png # its logo
┃ ┗ 📂 app # the App's workspace: processes, modules, variables…
┗ 📂 time-off-sync
┗ ...

Each App folder is a small repository of its own around the workspace: a README.md for the people who own and operate the App, the marketplace listing as MARKETPLACE.md, a CHANGELOG.md generated from the App’s releases, and the logo. That is what an internal integration catalogue or a handover needs, and it lives next to the code it describes.

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, CHANGELOG.md and logo. 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

The App’s README.md and MARKETPLACE.md are the one thing you edit in the repository and push up as documentation rather than code. They are also editable in the dashboard, so a pull never overwrites a local edit you have not pushed — it keeps the file and tells you. From the App folder:

Terminal window
$ cd payroll-sync
$ fcode app:push # uploads README.md and MARKETPLACE.md if they changed

See Push an App’s README and marketplace listing for the MARKETPLACE.md format and how conflicts are resolved.

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: "22.23"
- 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.