Skip to content

Pull & Push Workflow

The pull-push workflow is the core of how you edit agents with SCRAPI. This page explains each step in detail — what happens under the hood, what you can and can't change locally, and how to handle edge cases like the --to flag and drift.


Overview

The platform is always the source of truth. When you run cxas pull, SCRAPI exports the app from the platform as a ZIP archive and unpacks it into a structured local directory. When you run cxas push, SCRAPI reads that directory and sends an app import request.

This means:

  • You never edit resources by calling the API directly for every field — you edit files locally and push in one go
  • The local directory is a snapshot. If someone else pushes a change while you're editing locally, your next push will overwrite their change (this is intentional — treat it like a Git branch)
  • Not every file on disk corresponds to a separate API call. The app's files are packaged and imported together.

Pulling an app

cxas pull <app> [--target-dir DIR] [--project-id PROJECT] [--location LOCATION]

Identifying the app

You can identify the app in two ways:

By full resource name (no --project-id or --location needed):

cxas pull projects/my-project/locations/us-central1/apps/my-app --target-dir ./cxas_app

By display name (requires --project-id and --location):

cxas pull "My Support Agent" \
  --project-id my-project \
  --location us-central1 \
  --target-dir ./cxas_app

Using the full resource name is slightly more robust — display names are case-sensitive and must be unique within a project.

Target directory

By default, the app is pulled into an <AppName>/ subdirectory inside your current working directory. You can change its parent directory with --target-dir:

cxas pull "My Support Agent" \
  --project-id my-project \
  --location us-central1 \
  --target-dir ./local-agents

This would create ./local-agents/<AppName>/.

What gets downloaded

The pull downloads everything in the app:

  • app.json — app-level settings including the root agent reference
  • agents/<name>/instruction.txt — the agent's instruction text
  • agents/<name>/<name>.json — agent configuration (tools, callbacks, child agents)
  • agents/<name>/before_model_callbacks/<cb>/python_code.py — callback code
  • agents/<name>/after_model_callbacks/<cb>/python_code.py
  • (and so on for other callback types)
  • tools/<name>/<name>.json — tool configuration
  • tools/<name>/python_function/python_code.py — tool implementation
  • evaluations/ — exported platform evaluations
  • guardrails/ — guardrail configuration

Editing locally

Once you've pulled, you can edit any file in the directory. Here's what each file type controls:

instruction.txt

The most frequently edited file. This is the agent's natural language instruction — the text the LLM uses to decide what to do. The linter enforces the required XML structure (<role>, <persona>, <taskflow>).

Edit this directly in your text editor or IDE. Run cxas lint after editing to check for structural problems before pushing.

<agent_name>.json

Controls which tools the agent can use, which callbacks are attached, and which child agents it can transfer to. Example:

{
  "displayName": "Support Root Agent",
  "tools": ["lookup_order", "end_session"],
  "beforeModelCallbacks": [
    {
      "name": "inject_context",
      "pythonCode": "agents/support-root/before_model_callbacks/inject_context/python_code.py"
    }
  ],
  "childAgents": []
}

The pythonCode field is a relative path to the callback file. SCRAPI resolves this path at push time and sends the file contents to the platform.

python_code.py (tools and callbacks)

Plain Python files. Edit them in your IDE with full syntax highlighting and linting. The linter rules T001-T011 and C001-C010 catch common mistakes before you push.

app.json

App-level configuration including the root agent reference. You generally only edit this when changing the root agent.


Pushing changes

cxas push [--app-dir DIR] [--to RESOURCE_NAME]

Basic push

cxas push --app-dir cxas_app/My_Support_Agent \
  --to projects/my-project/locations/us-central1/apps/my-app

SCRAPI reads the directory, packages the supported files into a ZIP archive, and sends it to the app import API.

The --to flag

The --to flag lets you push a local directory to a different app on the platform. This is the mechanism behind environment promotion:

# Push your local staging config to the production app
cxas push --app-dir cxas_app/My_Support_Agent \
  --to projects/my-project/locations/us-central1/apps/my-app-production

This is particularly useful when you have a staging app and a production app with the same structure. You develop on staging, test, and then push the same local directory to production using --to.

The --to app must already exist

The target app must already exist on the platform. cxas push --to does not create a new app — it updates an existing one. If you want to clone an app, use cxas branch instead.


Understanding drift

Drift happens when the platform state has changed since you last pulled. If you've been editing locally for a while, someone else may have pushed changes to the same app in the meantime.

SCRAPI's pre-agent-push.sh hook (used in the skills system) performs drift detection before a push. It compares the local app against the current platform state and warns you if they've diverged.

To handle drift manually:

  1. Pull the latest platform state into a temporary directory
  2. Compare with your local directory using diff or a git diff
  3. Reconcile the differences
  4. Push
# Pull the latest into a temp directory
cxas pull "My Support Agent" \
  --project-id my-project \
  --location us-central1 \
  --target-dir /tmp/platform-state

# Diff against your working copy
diff -r cxas_app/My_Support_Agent /tmp/platform-state/My_Support_Agent

Treating your local cxas_app/ directory as a Git repository gives you the full benefits of version control:

# Initial setup
git init
echo "cxas_app/" >> .gitignore  # or track it — your choice
git add .
git commit -m "Initial project setup"

# Development loop
cxas pull "My Support Agent" --project-id my-project --location us-central1 --target-dir ./cxas_app
# ... edit files ...
cxas lint --app-dir cxas_app/My_Support_Agent
git diff  # review changes
git commit -m "Improve order lookup instruction"
cxas push --app-dir cxas_app/My_Support_Agent \
  --to projects/my-project/locations/us-central1/apps/my-app

If you track cxas_app/ in Git, you get:

  • Full history of every instruction change
  • Easy rollback if a push breaks something
  • Code review via pull requests before pushing to production

What cxas push does internally

Understanding what happens during a push helps you reason about partial failures:

  1. SCRAPI copies supported app files and resource directories into a temporary directory.
  2. If --env-file is provided, its contents replace environment.json in that temporary copy.
  3. It creates a ZIP archive and calls the app import API, targeting an existing app or creating a new one.
  4. It waits for the import operation and reports any resources rejected by the server.

If a push fails partway through, some resources may already have been imported. Review any warnings, fix the rejected resources, and re-push.


Common issues

"App not found" after pull
The display name is case-sensitive. Try using the full resource name instead.
Push succeeds but changes aren't reflected
Check that you're pushing to the right app. Confirm with cxas apps list --project-id my-project --location us-central1 or apps.list_apps().
Callback changes aren't picked up
The pythonCode path in the agent JSON must match the actual file location. After a pull, these paths are set correctly; if you've moved files manually, update the JSON to match.
The instruction lint rules show errors you can't reproduce
The linter runs against the local files. Make sure you've saved your changes before running cxas lint.