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¶
Identifying the app¶
You can identify the app in two ways:
By full resource name (no --project-id or --location needed):
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 referenceagents/<name>/instruction.txt— the agent's instruction textagents/<name>/<name>.json— agent configuration (tools, callbacks, child agents)agents/<name>/before_model_callbacks/<cb>/python_code.py— callback codeagents/<name>/after_model_callbacks/<cb>/python_code.py- (and so on for other callback types)
tools/<name>/<name>.json— tool configurationtools/<name>/python_function/python_code.py— tool implementationevaluations/— exported platform evaluationsguardrails/— 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¶
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:
- Pull the latest platform state into a temporary directory
- Compare with your local directory using
diffor a git diff - Reconcile the differences
- 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
Recommended workflow with Git¶
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:
- SCRAPI copies supported app files and resource directories into a temporary directory.
- If
--env-fileis provided, its contents replaceenvironment.jsonin that temporary copy. - It creates a ZIP archive and calls the app import API, targeting an existing app or creating a new one.
- 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-central1orapps.list_apps(). - Callback changes aren't picked up
- The
pythonCodepath 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.