Skip to content

CLI Quickstart

The CLI is the easiest way to try Graft. graft sql opens an ordinary physical SQLite worktree file, while repository commands use Graft’s control-plane service directly. No SQLite extension or repository PRAGMA transport is involved.

  1. Create a scratch app data directory.

    Terminal window
    mkdir app-state-demo
    cd app-state-demo
  2. Initialize Graft.

    Terminal window
    graft init

    This creates .graft/. It does not create a default SQLite database.

  3. Write SQLite data.

    Terminal window
    graft sql --db data.sqlite "CREATE TABLE notes(id INTEGER PRIMARY KEY, body TEXT);"
    graft sql --db data.sqlite "INSERT INTO notes(body) VALUES ('first note');"
  4. Inspect status.

    Terminal window
    graft status

    data.sqlite appears as an unstaged SQLite database snapshot.

    At this point it is still a normal SQLite file. Graft reads it only when a repository command needs to inspect or stage the path.

  5. Commit the first app-state version.

    Terminal window
    graft add --all
    graft commit -m "seed notes"
  6. Change the database and add an app file.

    Terminal window
    graft sql --db data.sqlite "INSERT INTO notes(body) VALUES ('second note');"
    mkdir -p attachments
    printf 'draft\n' > attachments/readme.txt
  7. See how Graft classifies paths.

    Terminal window
    graft status --json

    Path entries include kind and storage, so apps can render database snapshots and files differently.

  8. Commit and inspect a row diff.

    Terminal window
    graft add --all
    graft commit -m "add second note and attachment"
    graft diff --rows HEAD~1 HEAD data.sqlite
  9. Restore the database from the latest commit.

    Terminal window
    graft sql --db data.sqlite "DELETE FROM notes;"
    graft restore --source HEAD -- data.sqlite
    graft status

    The final status should be clean.

If no SQL argument is passed, graft sql --db <path> reads stdin:

Terminal window
cat <<'SQL' | graft sql --db data.sqlite
CREATE TABLE IF NOT EXISTS notes(id INTEGER PRIMARY KEY, body TEXT);
INSERT INTO notes(body) VALUES ('hello from stdin');
SELECT * FROM notes;
SQL

Branch and merge commands operate on the repository discovered from the current directory:

Terminal window
graft switch main
graft --db data.sqlite merge feature/search
graft merge --continue -m "merge feature/search"

switch materializes the tracked worktree paths without a database selector. Pass --db to merge when it should open that SQLite database for row-aware analysis; finishing the repository merge does not need it.