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

# Quickstart

> Create a repository and write your first change.

In this quickstart, you will create a repo, read and write files with MesaFS, and experiment with versioning. The goal is to show the primitives Mesa provides that can be used for any of your agent workflows.

<Steps>
  <Step title="Sign up and create an API key">
    1. Create an account at [app.mesa.dev](https://app.mesa.dev).
    2. Create an organization for your product (example: `acme`).
    3. Generate an API key with `admin` scope.

    Store the key as an environment variable:

    ```bash theme={null}
    export MESA_API_KEY="mesa_..."
    ```

    <Note>
      API keys are only shown once. Save them in your secrets manager before leaving the dashboard.
    </Note>

    <Tip>
      See [Authentication](/content/concepts/authentication) for key management and scope details.
    </Tip>
  </Step>

  <Step title="Install the SDK or CLI">
    <CodeGroup>
      ```bash TypeScript theme={null}
      npm install @mesadev/sdk
      ```

      ```bash Python theme={null}
      pip install mesa-sdk
      ```

      ```bash CLI theme={null}
      curl -fsSL https://mesa.dev/install.sh | sh
      ```
    </CodeGroup>

    <Note>
      Using MesaFS through the CLI requires FUSE which is not a straightforward process on macOS (yet). If testing with the CLI we recommend
      using a FUSE-enabled sandbox. See [Sandboxes](/content/integrations/sandboxes/daytona) for more.
    </Note>
  </Step>

  <Step title="Create a repo">
    In Mesa, you start by creating a special kind of folder called a `repository`. Each repository has its own version history and permissions.

    Repositories are free to create. Use them liberally to isolate resources that belong to different customers or projects.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      import { Mesa } from "@mesadev/sdk";

      const mesa = new Mesa({ apiKey: process.env.MESA_API_KEY, org: "acme" });

      const repo = await mesa.repos.create({ name: "my-project" });
      ```

      ```python Python theme={null}
      import os
      from mesa_sdk import Mesa

      mesa = Mesa(api_key=os.environ["MESA_API_KEY"], org="acme")

      repo = await mesa.repos.create(name="my-project")
      ```

      ```bash CLI theme={null}
      mesa repo create my-project --org acme
      ```
    </CodeGroup>

    To retrieve an existing repo later:

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const repo = await mesa.repos.get({ repo: "my-project" });
      ```

      ```python Python theme={null}
      repo = await mesa.repos.get(repo="my-project")
      ```

      ```bash CLI theme={null}
      mesa repo list --all --org acme
      ```
    </CodeGroup>
  </Step>

  <Step title="Read and write your first files">
    Once created, the easiest way to read and write to a repository is by mounting MesaFS, either through our SDKs or through a FUSE mount with our CLI.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      import { Mesa } from "@mesadev/sdk";

      const mesa = new Mesa({ apiKey: process.env.MESA_API_KEY, org: "acme" });
      const repo = await mesa.repos.create({ name: "my-project" });

      // Open the repository locally as a virtual filesystem
      const fs = await mesa.fs.mount({
        repos: [{ name: repo.name, bookmark: "main" }],
      });

      // Use explicit filesystem operations
      await fs.mkdir("/acme/my-project/memories", { recursive: true });
      await fs.writeFile("/acme/my-project/memories/run-1.md", "Hello, world!");
      const content = await fs.readFile("/acme/my-project/memories/run-1.md", "utf8");

      // Or use the emulated bash environment
      const { stdout } = await fs.bash({ cwd: "/acme/my-project" }).exec("echo memories/run-1.md");
      ```

      ```python Python theme={null}
      import os
      from mesa_sdk import Mesa

      mesa = Mesa(api_key=os.environ["MESA_API_KEY"], org="acme")
      repo = await mesa.repos.create(name="my-project")

      # Open the repository locally as a virtual filesystem
      async with mesa.fs.mount(repos=[repo.name]) as fs:
          # Use explicit filesystem operations
          await fs.mkdir("/acme/my-project/memories", recursive=True)
          await fs.write("/acme/my-project/memories/run-1.md", b"Hello, world!")
          content = await fs.read("/acme/my-project/memories/run-1.md")

          # Or use the emulated bash environment
          result = await fs.bash(cwd="/acme/my-project").exec("echo memories/run-1.md")
          print(result.stdout.decode())
      ```

      ```bash CLI theme={null}
      # Open the repository locally as a virtual filesystem
      export MESA_ORG=acme
      export MESA_API_KEY="mesa_..."
      mesa repo create my-project --org acme
      mesa mount --daemonize --non-interactive

      # Use explicit filesystem operations
      mkdir -p ~/.local/share/mesa/mnt/acme/my-project/memories
      printf "Hello, world!" > ~/.local/share/mesa/mnt/acme/my-project/memories/run-1.md
      cat ~/.local/share/mesa/mnt/acme/my-project/memories/run-1.md

      # Or use the mounted directory with regular shell commands
      cd ~/.local/share/mesa/mnt/acme/my-project
      echo memories/run-1.md
      ```
    </CodeGroup>
  </Step>

  <Step title="Experiment with versioning">
    A repository is a directed acyclic graph of `Changes`: snapshots of the repository at that point in time. See more in [Versioning](/content/concepts/versioning).

    Every file write is part of specific change. Your first change is created automatically
    when you mount an empty repo in MesaFS. All subsequent changes must be explicitly created.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      // The first Change that holds our previous writes
      const firstChange = await fs.change.current({ repo: "my-project" });

      // Create a new Change on top of the first
      await fs.change.new({ repo: "my-project", changeId: firstChange.changeId });

      // All writes go to the new change
      await fs.bash({ cwd: "/acme/my-project" }).exec('echo "Hello, Mesa!" > memories/run-1.md');
      const secondChange = await fs.change.current({ repo: "my-project" });
      ```

      ```python Python theme={null}
      # The first Change that holds our previous writes
      first_change = await fs.changes.current("my-project")

      # Create a new Change on top of the first
      await fs.changes.new("my-project", change_id=first_change.change_id)

      # All writes go to the new change
      await fs.bash(cwd="/acme/my-project").exec('echo "Hello, Mesa!" > memories/run-1.md')
      second_change = await fs.changes.current("my-project")
      ```

      ```bash CLI theme={null}
      # Create a new Change on top of main
      cd ~/.local/share/mesa/mnt/acme/my-project
      mesa new main

      # All writes go to the new change
      echo "Hello, Mesa!" > memories/run-1.md

      # Inspect the new change
      mesa show --summary
      ```
    </CodeGroup>

    You can easily roll back to a previous version by switching to an old Change.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      // Roll back to the first change
      await fs.change.edit({ repo: "my-project", changeId: firstChange.changeId });
      const original = await fs.readFile("/acme/my-project/memories/run-1.md", "utf8");
      console.log("Content:", original); // "Hello, world!"

      // Continue with the new change
      await fs.change.edit({ repo: "my-project", changeId: secondChange.changeId });
      const updated = await fs.readFile("/acme/my-project/memories/run-1.md", "utf8");
      console.log("Content:", updated); // "Hello, Mesa!"
      ```

      ```python Python theme={null}
      # Roll back to the first change
      await fs.changes.edit("my-project", change_id=first_change.change_id)
      original = await fs.read("/acme/my-project/memories/run-1.md")
      print("Content:", original.decode())  # "Hello, world!"

      # Continue with the new change
      await fs.changes.edit("my-project", change_id=second_change.change_id)
      updated = await fs.read("/acme/my-project/memories/run-1.md")
      print("Content:", updated.decode())  # "Hello, Mesa!"
      ```

      ```bash CLI theme={null}
      # Roll back to the first change
      mesa edit "$FIRST_CHANGE" --repo acme/my-project
      cat ~/.local/share/mesa/mnt/acme/my-project/memories/run-1.md

      # Continue with the new change
      mesa edit "$SECOND_CHANGE" --repo acme/my-project
      cat ~/.local/share/mesa/mnt/acme/my-project/memories/run-1.md
      ```
    </CodeGroup>
  </Step>

  <Step title="Diff two changes">
    Now you have two changes in your repo.

    You can imagine having many different changes, each representing the concurrent work of a different agent. Once an agent is done working,
    you may want to show a UI for users to review the changes and approve or reject them.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const diffResult = await mesa.diffs.get({
        repo: repo.name,
        base_change_id: firstChange.changeId,
        head_change_id: secondChange.changeId,
      });

      diffResult.entries.forEach((entry) => {
        // render file diff
      });
      ```

      ```python Python theme={null}
      diff_result = await mesa.diffs.get(
          repo=repo.name,
          base_change_id=first_change.change_id,
          head_change_id=second_change.change_id,
      )

      for entry in diff_result.entries:
          # render file diff
          pass
      ```

      ```bash CLI theme={null}
      # Show the diff for the second change
      mesa show "$SECOND_CHANGE" --repo acme/my-project --git
      ```
    </CodeGroup>
  </Step>

  <Step title="Bookmark changes">
    By default, changes have random, alphanumeric identifiers. However, you will often want to assign a human-readable name to a change. You can do this with bookmarks.
    By default, the first change on a repo is bookmarked `main`.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      await mesa.bookmarks.create({
        repo: repo.name,
        name: "my-feature",
        change_id: secondChange.changeId,
      });
      ```

      ```python Python theme={null}
      await mesa.bookmarks.create(
          repo=repo.name,
          name="my-feature",
          change_id=second_change.change_id,
      )
      ```

      ```bash CLI theme={null}
      mesa bookmark create my-feature --repo acme/my-project --revision "$SECOND_CHANGE"
      ```
    </CodeGroup>
  </Step>

  <Step title="Merge two changes">
    You will eventually have lots of different changes and bookmarks and you'll want to merge the changes from one bookmark into another.

    Merge your source bookmark (`my-feature`) into your target bookmark (`main`). This process creates a new Change on top of
    `main` that contains the merged changes and moves the `main` bookmark to the new Change.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const mergedChange = await mesa.bookmarks.merge({
        repo: repo.name,
        target: "main",
        source: "my-feature",
      });

      console.log("Merged Change:", mergedChange.change_id);
      ```

      ```python Python theme={null}
      merged_change = await mesa.bookmarks.merge(
          repo=repo.name,
          target="main",
          source="my-feature",
      )

      print("Merged Change:", merged_change.change_id)
      ```
    </CodeGroup>

    Now you have 3 changes in your repo.

    Conventionally, the `main` bookmark points to the canonical
    version of your documents, and other bookmarks represent "draft" work. A typical pattern is rendering diff UI for human-in-the-loop
    approvals, then merging approved changes into your `main` line of Change history.
  </Step>
</Steps>

You're now ready to start building complex agent workflows with Mesa.

## Next steps

* [Versioning](/content/concepts/versioning) for repos, bookmarks, and changes
* [Authentication](/content/concepts/authentication) for API key scopes
* [TypeScript SDK Reference](/content/reference/ts/index) for the primary TypeScript client
* [Python SDK Reference](/content/reference/py/index) if you're working in Python
* [Rust SDK Reference](/content/reference/sdk-rust) if you're working in Rust
