> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metabind.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Flat-File Sync

> Edit a project as files on disk, then sync changes with the CLI

Flat-file sync lets you work on a project as files instead of only through Studio. Pull a project into a directory, edit the files with any editor or coding agent, and push the changes back. It's a good fit for keeping a project in version control alongside the application that consumes it, or for pointing a coding agent at a project the same way it would work on source code.

New to the file workflow? Walk through it once in [Sync Your First Project](/cli/flat-file-tutorial), then come back here for conflicts, renames, and repairing local state.

## How It Fits With Studio

* The server is always the source of truth. A local directory is a checkout that can drift and needs to be reconciled.
* Nothing is written back until a push is planned and applied.
* Edits are detected by content, not by timestamp, so an untouched directory always reports zero changes — background recompiles and other server-side updates never show up as an edit.
* A push that would overwrite a change made in Studio is refused, not merged. You choose how to resolve it.

## The Core Loop

| Step | Command | Effect |
| - | - | - |
| Check out | `metabind pull` | Writes the project to a directory and records a baseline. |
| Edit | any editor or agent | Ordinary file edits. |
| Preview | `metabind push --plan` | Reports what would change. Writes nothing. |
| Apply | `metabind push` | Writes the changes to the server. |
| Release | `metabind publish` | Promotes drafts to published, same as in Studio. |

Component, tool, and content changes that `push` writes are drafts. Your published MCP app doesn't serve them until you run `publish` — the file workflow has the same draft/published separation as Studio. Project settings (`metabind.jsonc`, `mcp-instructions.md`, and `agent/`) have no draft state, so a pushed settings change takes effect without a publish.

## Project Layout

| Path | Contents | Hand-editable |
| - | - | - |
| `metabind.jsonc` | Project configuration and settings | Yes |
| `mcp-instructions.md` | Instructions surfaced to MCP clients | Yes |
| `agent/` | Agent configuration | Yes |
| `components/view/` | View components | Yes |
| `components/data/` | Data components | Yes |
| `tools/` | Tool definitions, one directory per kind (`tools/view/`, `tools/layout/`, `tools/data/`) | Yes |
| `content/` | Content items, one file per item | Yes |
| `assets/` | Asset metadata and references | No — edit assets in Studio |
| `metabind.resolved` | Publish record: the latest package version, its component versions, and tool and content pins | No — generated |
| `.metabind/` | Local sync state the CLI depends on | No |

Assets are pulled so the rest of the project can reference them, but they stay out of the default push scope — an edit to `assets/assets.json` is discarded rather than sent. Studio remains the place to manage asset binaries.

`.metabind/` holds the baseline the CLI uses to tell a local edit from a remote one. It's not meant to be hand-edited. Commit `.metabind/state.json`: without it, a teammate who clones the repository, or a CI job, has no baseline and `push` refuses to run. `metabind.resolved` is safe — and useful — to commit; it gives you a readable record of project state over time.

## Resolving Conflicts

`metabind push --plan` shows, per entity, whether it would be created, updated, or left unchanged, and flags anything in conflict before it writes anything.

An entity that changed both in your local files and on the server — someone edited it in Studio while you were editing the same file, for example — is refused rather than merged. To resolve it:

* Re-pull into a clean directory and reapply your edit, or
* Run `metabind push --theirs` to keep the server's version for every conflicting entity in that push.

If any entity is in conflict when `push` plans, the whole push is refused before anything is written. An entity can still fail after others are written — it changed on the server mid-push, or the server rejected it — and `push` then exits with an error. Run `metabind push --plan` to see what's left: entities that were already written show as matching the server.

### Renaming a file

If you rename a file without changing its contents, `push` detects the rename on its own. If you also changed the contents, `push` can't tell a rename from a delete-and-create, so it stops and asks. Use `metabind mv` to make the rename explicit instead of guessing:

```bash theme={null}
metabind mv components/view/Card.ts components/view/ProductCard.ts
```

`mv` moves the file and updates the local sync state in the same step, so the entity keeps its id, its version history, and any published references to it. In a script, move the file yourself, then pass `push --rename <old>=<new>` so `push` treats it as a rename.

Renaming a tool changes its public name — anything calling it by the old name breaks, and if it's already published, its slug won't change to match.

## Repairing Local State

If `.metabind/state.json` goes missing or gets corrupted — a bad merge, a partial checkout, a stray edit — `push` stops rather than guess which file is which entity. Rebuild it without losing local edits:

```bash theme={null}
metabind sync repair
```

`sync repair` matches files to server entities by name and rebuilds the mapping, reporting anything it can't match rather than guessing: a file with no server match, or a server entity with no local file. Add `--plan` to see what it would do first. A fresh `metabind pull` also fixes this, but it discards every uncommitted local edit, so reach for `sync repair` first.

## Sharing a Project as Files

Two more commands round out the loop for working with a project written somewhere else — a template, a starter project, or one shared by a teammate.

### Check what a directory would do

Before pushing to or installing a directory you didn't write yourself, see what it asks for:

```bash theme={null}
metabind inspect --dir ./cloned-project
```

`inspect` reads a directory offline — no project context, no network call — and reports the secrets it references, the external hosts it would call, and the server-side handlers it declares. Use it before trusting a project tree from outside your organization.

### Turn a directory into a project

```bash theme={null}
metabind install --dir ./cloned-project --yes
```

`install` creates a new project from a directory and mints fresh ids for everything in it, so a tree written in another organization can't accidentally reference something there. It refuses to run until you pass `--yes` to accept the capabilities `inspect` would show you. Pass `--project <id>` to install into an existing project instead of creating a new one, and `--source-repo` / `--source-commit` to record where the tree came from. `install` only creates drafts — publish when you're ready to serve it.

## Next Steps

<CardGroup cols={2}>
  <Card title="Workflow Patterns" icon="list-checks" href="/cli/workflows">
    Build, publish, and roll back changes with the CLI.
  </Card>

  <Card title="CI Publish Lane" icon="github" href="/cli/ci-publish">
    Push drafts from GitHub Actions and review the current publishing limitation.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.