@waron97/prbot
v3.9.1
Published
CLI tool for managing PRs, changelogs, and Odoo workflow XML files in the addons repo.
Readme
prbot
CLI tool for managing PRs, changelogs, and Odoo workflow XML files in the addons repo.
Install
Requires Node >= 20.
npm install -g @waron97/prbotSetup
prbot initPrompts for all required config values and writes them to ~/.config/prbot/config. Run once, re-run anytime to update config.
Config keys
| Key | Description |
| ---------------------- | -------------------------------------------------------- |
| ADDONS_PATH | Path to local Odoo addons repo. Unset, empty, or . uses the current directory instead (e.g. a git worktree). Either way the resolved directory must be a git checkout containing config/ — commands refuse to run otherwise. |
| KC_URL | Keycloak token endpoint URL |
| KC_USER | Keycloak username |
| KC_PASSWORD | Keycloak password |
| KC_ID | Keycloak client ID |
| KC_SECRET | Keycloak client secret |
| RIP_URL | RIP API base URL |
| TRIDENT_URL | Trident (Odoo) instance URL |
| TRIDENT_UID | Trident user ID |
| TRIDENT_TOKEN | Trident API token |
| TRIDENT_DB | Trident database name |
| DEVOPS_TOKEN | Azure DevOps personal access token |
| DEVOPS_ORG | Azure DevOps organization |
| DEVOPS_PROJECT | Azure DevOps project |
| DEVOPS_REPO | Azure DevOps repository name |
| AUTOPR_TARGET_BRANCH | Target branch for auto-created PRs (default: 15.0-dev) |
| IMPORTEXPORT_URL | ImportExport API base URL |
Commands
prbot pr <module>
Fetches workflow XML for <module> from RIP, writes files into ADDONS_PATH/config/<module>/data/, and commits.
prbot pr config_wf_contestazioneOptions:
| Flag | Description |
| -------------------- | ------------------------------------------------------------------------- |
| -b, --bump <level> | Also bump manifest version after commit. Level: major, minor, patch |
prbot pr config_wf_contestazione -b minorprbot ver <module>
Bumps the version in __manifest__.py for <module> and commits.
prbot ver config_wf_contestazione --bump patchprbot changelog <pr>
Writes a changelog entry into CHANGELOG.md for a given PR number. Prompts to select the target section, detects existing indentation, and appends the entry with refs.
prbot changelog 42 -m "Fix invoice state race condition" -t 1234 -t 5678 -j TESTML-1 -j TESTML-2Options:
| Flag | Description |
| ---------------------- | --------------------------------------------- |
| -m, --message <text> | Changelog entry message (prompted if omitted) |
| -t, --trident <code> | Trident issue code (repeatable) |
| -j, --jira <code> | JIRA issue code (repeatable) |
prbot autopr
End-to-end PR automation: creates a branch, pushes it, opens a draft PR on Azure DevOps, appends the PR link to each Trident task's release checklist, writes a changelog entry, commits, and pushes.
# Single Trident task
prbot autopr -t 1234
# Multiple Trident tasks (all get PR link; first with work package drives section matching)
prbot autopr -t 1234 -t 5678
# Multiple Trident + multiple JIRA
prbot autopr -t 1234 -t 5678 -j TESTML-1 -j TESTML-2
# JIRA only — skips all Trident fetch/write operations
prbot autopr -j JIRA-99 --branch my-branchOptions:
| Flag | Description |
| ---------------------- | ------------------------------------------------------------------------ |
| -t, --trident <id> | Trident task ID (repeatable) |
| -j, --jira <code> | JIRA issue code (repeatable) |
| -m, --message <text> | Changelog entry message (prompted if omitted) |
| -b, --branch <name> | Branch name (default: autopr_<first-task-id> or autopr_<first-jira>) |
| -n, --name <text> | PR title (default: Trident task name) |
prbot commit
Interactive commit builder. Prompts for operation type ([IMP], [FIX], etc.), and a message. The destinatin module will be automatically detected. If nothing is staged, shows unstaged files and lets you select which to stage first. Previews the final commit message before confirming.
prbot commitprbot export workflow
Fetches workflow XML for an interactively selected module from RIP and commits. Prompts to select the module (from ADDONS_PATH/config/ directories) via fuzzy search.
prbot export workflow
prbot export workflow --no-commitOptions:
| Flag | Description |
| -------------- | ------------------------------------------------------------- |
| --no-commit | Skip the git commit step |
| -q, --quiet | Suppress informational output — errors still fail the command |
| -s, --silent | Deprecated alias of --quiet; no longer swallows errors |
prbot export pb
Exports a Process Builder process from the ImportExport API and writes the ZIP to ADDONS_PATH/.cloudbuild/pb/B2WA/processes/. Updates the file in place if it already exists, otherwise writes to the all/ subdirectory. Prompts to select the process via fuzzy search.
prbot export pb
prbot export pb --no-commitOptions:
| Flag | Description |
| -------------- | ------------------------------------------------------------- |
| --no-commit | Skip the git commit step |
| -q, --quiet | Suppress informational output — errors still fail the command |
| -s, --silent | Deprecated alias of --quiet; no longer swallows errors |
prbot export imperex
Exports a single Imperex record from Odoo via RIP and writes the resulting YAML into ADDONS_PATH/sorgenia_imperex_metadata/migrations/0.0.0/imperex/<model>/. Prompts first for the model (from local folder names), then for the record (fetched from API). Both prompts support fuzzy search.
prbot export imperex
prbot export imperex --no-commitOptions:
| Flag | Description |
| -------------- | ------------------------------------------------------------- |
| --no-commit | Skip the git commit step |
| -q, --quiet | Suppress informational output — errors still fail the command |
| -s, --silent | Deprecated alias of --quiet; no longer swallows errors |
prbot export email-templates
Fetches email templates for an interactively selected workflow from RIP and writes them as ADDONS_PATH/config/<module>/data/mail_templates.xml. Prompts first for the module (from ADDONS_PATH/config/ directories), then for the workflow. Both prompts support fuzzy search.
prbot export email-templates
prbot export email-templates --no-commitOptions:
| Flag | Description |
| -------------- | ------------------------------------------------------------- |
| --no-commit | Skip the git commit step |
| -q, --quiet | Suppress informational output — errors still fail the command |
| -s, --silent | Deprecated alias of --quiet; no longer swallows errors |
prbot init
Interactive setup: writes ~/.config/prbot/config.
prbot update
Reinstalls prbot from npm — latest by default, or a pinned version.
prbot update # latest
prbot update 3.4.0 # pin an exact versionagrippa
Syncs Odoo workflow phase Python code, MFA records, process-builder wizards, and long-running processes (LRPs) between the local filesystem and the RIP / Process Builder / Symphony APIs. Tracks changes via checksums and detects conflicts before overwriting.
Credentials are inherited from the global prbot config (~/.config/prbot/config). Override per-workspace in agrippa.yaml.
agrippa init
Creates agrippa.yaml in the current directory. Always writes pyproject.toml with the standard ruff builtins. Optionally writes pyrightconfig.json and copies type stubs into typings/.
agrippa initagrippa clone
Clones all from_code phases for a selected workflow, a single MFA, a process-builder wizard, or a long-running process (LRP), into the workspace. Writes files to disk and registers them in agrippa.yaml. With no flag, prompts for the object type (MFA / Phase / Process Builder / Long Running Process).
A wizard or LRP is downloaded and decomposed into editable files (structure.yaml, process.yaml, scripts/, pages/, manifest) — LRPs share the same layout minus pages/ (LRPs have no user tasks); see agrippa pb and agrippa-pb.md.
agrippa clone
agrippa clone --phase
agrippa clone --mfa
agrippa clone --phase --id 123 --path my-workflow/
agrippa clone --pb # select a wizard
agrippa clone --pb --name ml_review_billing --path my-wizard/
agrippa clone --lrp # live search LRPs by name
agrippa clone --lrp --name B2WA_ml_IFS_passive_trigger --path my-lrp/Options:
| Flag | Description |
| --------------- | -------------------------------------------------------------------------- |
| --phase | Clone a phase (select a workflow) |
| --mfa | Clone an MFA record |
| --pb | Clone a process-builder wizard |
| --lrp | Clone a long-running process |
| --id <id> | Skip selection, clone by ID (phase/mfa) |
| --name <name> | Skip selection: document_id (with --pb) or process name (with --lrp) |
| --path <path> | Destination path (base dir for phases/wizard/lrp, file for MFA) |
agrippa pull
Fetches remote code for all tracked entries and shows what changed. Classifies each as fast-forward (safe overwrite) or conflict (local edits would be lost). conflict entries are shown but not preselected — you opt in explicitly. Lets you select which to pull.
After pulling, also checks tracked workflows for newly added from_code phases and auto-clones any not yet present locally.
Tracked process-builder wizards and long-running processes are also refreshed from upstream: the local project is re-decomposed from the latest payload (orphan script files pruned; PB wizards also prune orphan pages), with the same fast-forward/conflict classification — a semantic checksum of the recomposed payload (not raw updated_date), canonicalized so cosmetic noise never triggers a false conflict: whitespace-only BPMN text nodes, format-only labelPos, page ordering, and the updated_date/modified_by audit fields Odoo/Symple bump on any server touch are all excluded from the comparison. The current local state is backed up to .backup/<timestamp>/<path>/local.json first.
Plain phase/MFA code is backed up the same way: the current local .py content is written to .backup/<timestamp>/<path> before it's overwritten with the pulled remote code.
agrippa pull
agrippa pull --non-interactive # no prompts: auto-select fast-forward, fail if any conflictagrippa push
Pushes local file changes back to RIP (phases/MFAs), the Process Builder API (wizards), and Symphony (long-running processes). Backs up current remote state to .backup/<timestamp>/ before overwriting. Same conflict detection as pull, with the concern inverted. LRP entries are located by name, not id (the Symphony id changes on every save), re-resolving the current id/tenantId immediately before saving.
Pushing a wizard saves it as a draft; publish it so live consumers see the change with --publish (auto) or answer the prompt. Page edits (pages/) are saved independently of the whole-wizard save, mirroring the UI. Pushing an LRP saves the BPMN via Symphony's tabulator PATCH; deploy it (the LRP analog of publish) the same way — --publish/--skip-publish double as auto-deploy/never-deploy for LRPs too.
agrippa push # prompts whether to publish/deploy each pushed wizard/LRP
agrippa push --publish # auto-publish wizards, auto-deploy LRPs
agrippa push --skip-publish # never publish/deploy (no prompt)
agrippa push --non-interactive # no prompts: auto-select fast-forward, fail if any conflict;
# publish/deploy defaults to skip unless --publish is also passedagrippa restore
Restores local workspace files from a .backup/<timestamp>/ snapshot written by an earlier push or pull. Local only — it never touches the remote and never modifies agrippa.yaml/checksum_at_pull; restored content is just local file state, classified against the existing baseline like any other local edit on the next push/pull/diff. No network calls.
Prompts for a snapshot (newest first), then a checkbox of which tracked resources have backup content in it (unchecked by default — this is a deliberate recovery action, not a routine safe one).
What's available to restore, by object type and which command wrote it:
| object_type | push backed up | pull backed up |
| ---------------------- | ------------------------------------- | ------------------------------------ |
| phase / mfa | remote code (about to be overwritten) | local code (about to be overwritten) |
| process_builder | full remote payload (upstream.json) | full local payload (local.json) |
| long_running_process | remote BPMN only (upstream.xml) | full local payload (local.json) |
agrippa restore
agrippa restore --timestamp 2026-08-05_14-30-00Z # skip the snapshot pickerOptions:
| Flag | Description |
| ------------------ | ----------------------------------------------------------- |
| --timestamp <ts> | Backup snapshot to restore from (skips the snapshot picker) |
agrippa diff [target]
Shows a diff between local files and remote code. [target] optionally narrows it to a single file, a workflow folder, a cloned project directory, or a document_id/name; omit it for the whole workspace.
For process-builder wizards and long-running processes, diffs the whole project tree against what the workspace would look like if the project were decomposed fresh from upstream right now (.backup/ and preview.svg are excluded as local-only artifacts).
agrippa diff
agrippa diff my-workflow/some-phase.py
agrippa diff ml_review_billing # a cloned wizard's directory
agrippa diff B2WA_M2C_passthrough # a cloned LRP, by directory or nameagrippa init-phase
Selects a workflow and any phase, then pushes a default code scaffold to that phase on RIP. Sets set_result_automatically to from_code, generates result variable constants from the phase's allowed results, and creates the corresponding result.code.configurator records.
agrippa init-phaseagrippa repair
Removes entries from agrippa.yaml whose local files no longer exist on disk.
agrippa repairagrippa pb
Local editing helpers for a cloned process-builder wizard or long-running process (no network — they edit the decomposed files in place; both share the same decomposed layout and BPMN engine). Each operates on one wizard/LRP, resolved by --pb <document_id_or_name> (name for LRPs, since they have no document_id), single-entry auto-select, or a fuzzy prompt.
These commands exist mainly so an AI agent can add/remove/connect blocks without hand-editing the multi-thousand-line structure.yaml. Human users typically edit blocks in the UI instead. Full agent-facing guide (also usable as a workspace CLAUDE.md): agrippa-pb.md.
Newly added blocks get placeholder geometry. pb format re-lays-out the whole diagram (discarding any hand-tuned layout), so running it is a deliberate human decision — the alternative is positioning the new blocks by hand in the UI. Agents are expected to make structural edits but not to run format or to pull/push; a human reviews and syncs.
| Command | Purpose |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| pb format | Re-lay-out the entire diagram (elkjs, left→right); overwrites existing layout |
| pb add | Add a node (--type, --name, --parent); scaffolds script/page files |
| pb rm | Remove a node (--id), its edges, and its script/page files |
| pb connect | Add a flow (--from, --to, --condition, --default); enforces the gateway rule |
| pb disconnect | Remove a flow (--id, or --from/--to); clears the source gateway's default if it pointed at that flow |
| pb lint | Check the structural rules: gateway defaults/conditions, branch names, incoming-flow limits, and defaults pointing at flows that no longer exist |
| pb ls | List nodes and edges with their ids (discover targets without reading the YAML) |
| pb preview | Render the diagram to an SVG (--out) for a quick visual check |
agrippa pb ls --pb ml_review_billing
agrippa pb add --type scriptTask --name "Check pod" --pb ml_review_billing
agrippa pb connect --from ScriptTask_x --to ExclusiveGateway_y --pb ml_review_billing
agrippa pb format --pb ml_review_billing
agrippa pb ls --pb B2WA_ml_IFS_passive_trigger # LRP, resolved by name
agrippa pb add --type serviceTask --name "Call SAP" --pb B2WA_ml_IFS_passive_triggeruserTask (pages) can't be added to an LRP project — LRPs never have user tasks.
