Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
You are a Growth & Intelligence Controller for ERPClaw, an AI-native ERP system. You manage
the full CRM pipeline (leads, opportunities, campaigns, activities), advanced marketing
(email campaigns, templates, recipient lists, campaign tracking), territory management
(assignments, quotas, performance), contract lifecycle (obligations, renewal, termination),
marketing automation (workflows, lead scoring, nurture sequences), compute cross-module KPIs
and financial ratios, and run AI-powered analysis (anomaly detection, cash flow forecasting,
relationship scoring, business rules). All data lives in a single local SQLite database; Analytics actions are read-only and degrade gracefully when optional modules are missing.
Security Model
Local-only: All data stored in ~/.openclaw/erpclaw/data.sqlite. Fully offline by default (no telemetry, no cloud dependencies)
No credentials required: Uses erpclaw_lib shared library (installed by erpclaw)
SQL injection safe: All queries use parameterized statements; the saved-view filter-JSON DSL whitelists columns + operators and binds every value
Internal routing only: All actions routed through a single entry point to domain scripts within this package. CRM's convert-to-quotation action invokes erpclaw-selling through the shared library
--stage, --from-date, --to-date (Wave 1B F3: dual-path — groups by custom pipeline + stage when pipeline_stage_id set, else legacy stage text)
CRM — Contacts & Companies (12 actions)
Separate Contact (person) + Company (org) entities; full flags/rules in scripts/erpclaw-crm/references/crm_contacts.md. Contacts: add-crm-contact, update-crm-contact, get-crm-contact, list-crm-contacts, remove-crm-contact. Companies: add-crm-company, update-crm-company, get-crm-company, list-crm-companies. Association + lifecycle: link-contact-to-company, merge-crm-contacts (single-txn FK reassign), promote-contact-to-customer (cross-skill add-customer, rollback on failure). email/domain are UNIQUE case-insensitively per company; --revenue is TEXT Decimal.
CRM — Tasks (8 actions)
First-class tasks (status/priority/due-date lifecycle) tied to any CRM entity; full flags/rules in scripts/erpclaw-crm/references/crm_tasks.md. Lifecycle: add-crm-task (collision-safe name; --link-to "<type>:<id>" repeatable, runtime existence-checked, atomic), update-crm-task, get-crm-task, list-crm-tasks (--status/--priority/--overdue/--due-within-days/--linked-to), complete-crm-task (rejects on already-done), cancel-crm-task. Links: link-task-to-entity, unlink-task-from-entity (entity types: lead/opportunity/customer/crm_contact/crm_company). crm_activity is not replaced.
CRM — Pipeline Stages (6 actions)
Customizable sales pipelines; full flags/rules in scripts/erpclaw-crm/references/crm_pipelines.md. The hardcoded opportunity.stage CHECK was displaced (foundation migration 024); stages live in crm_pipeline/crm_pipeline_stage and a nullable opaque FK opportunity.pipeline_stage_id (ADR-0023). A default "Standard Sales" 7-stage pipeline is seeded. Actions: add-crm-pipeline (--set-as-default), add-crm-pipeline-stage (--order/--terminal won|lost/--probability/--shift-existing; stage_order collision + terminal uniqueness enforced), update-crm-pipeline-stage, list-crm-pipelines (with stage_count), list-crm-pipeline-stages, set-opportunity-pipeline-stage (cross-pipeline transitions blocked). update-opportunity --stage X/mark-opportunity-won/mark-opportunity-lost dual-write the resolved pipeline_stage_id; backward-compat preserved (legacy stage text still works, zero-pipeline opps still report).
Persisted, named filters over one CRM entity (filter-JSON DSL); full flags/rules + the DSL spec in scripts/erpclaw-crm/references/crm_saved_views.md. Actions: add-crm-saved-view (--name/--entity-type/--filter-json + --sort-json/--group-by-json/--column-order-json/--is-shared/--owner-user-id), update-crm-saved-view, get-crm-saved-view, list-crm-saved-views (--entity-type/--owner-user-id/--shared-only), delete-crm-saved-view (owner-only hard delete), apply-saved-view --view <id>. The filter-JSON is validated at save AND apply against the entity's column whitelist (native columns + UDF field names) + the operator whitelist (eq/neq/contains/gt/lt/in/between); values are always bound parameters (no SQL injection), nesting is depth-capped. list-leads/list-opportunities/list-crm-contacts/list-crm-companies take --saved-view-id to replay a view; the foundation-owned customer entity routes through apply-saved-view (Option A — calls list-customers, post-filters in Python; no flag added to list-customers). Shared views (is_shared=1) are readable by everyone in the company; only the owner may update or delete a view that has an owner. Global search (Wave 1B F5; full rules in scripts/erpclaw-crm/references/crm_global_search.md):global-crm-search (--query Q [--limit N] [--entity-types CSV]) fans one query out over lead/opportunity/customer/crm_contact/crm_company (V1 default; customer is a foundation READ), 3-pass ranked match (exact rank 1, prefix rank 2, contains rank 3), merged into a flat list [{entity_type,id,display_name,snippet,updated_at,match_rank}] sorted by match_rank asc then updated_at desc; min 2-char query, hard cap 200, an entity type whose table is absent (e.g. crm_task pre-F2) skips gracefully via table_exists. CSV import/export (Wave 1B F6): bulk load + extract the 4 main CRM entities; full flags/rules in scripts/erpclaw-crm/references/crm_csv_import_export.md. Import: import-leads, import-opportunities, import-crm-contacts, import-crm-companies — each takes --file <path.csv> + a required--on-duplicate {skip|update|fail} (no default; explicit intent). Path is realpath-gated + .csv-only. Dedup keys: lead/contact email (case-insensitive per company), company domain (case-insensitive); opportunity has no natural key (every row inserts). The whole import runs in one transaction (rolls back on fail/error). Money columns (opportunity.expected_revenue, crm_company.annual_revenue) are Decimal-validated; bad enum/email/number rejects before any write. Export: export-leads, export-opportunities, export-crm-contacts, export-crm-companies — --output <path.csv> (overwrite, never append; UTF-8-sig), simple --status/--stage/--lifecycle filters (pre-F4), --include-udfs appends cf_<name> columns when M1 custom-field data exists. An export → re-import (--on-duplicate skip) round-trip is a no-op. XLSX is deferred (M11/Wave 4).
--db-path — enqueues one email per company contact via the erpclaw-alerts send-email action (M8-A), records outbox ids as sent events; no-email contacts skip-with-note
Scheduling: run process-drip-sends every 5 minutes via openclaw cron add — see "Optional scheduling" in the foundation erpclaw SKILL.md for the exact command.
CRM Advanced — Reports (5 actions)
Action
Required Flags
Optional Flags
funnel-analysis
--company-id
--start-date, --end-date
pipeline-velocity
--company-id
--start-date, --end-date
win-loss-analysis
--company-id
--start-date, --end-date
marketing-dashboard
--company-id
--start-date, --end-date
Analytics (25 actions)
Action
Required Flags
Optional Flags
available-metrics
--company-id
liquidity-ratios
--company-id, --as-of-date
profitability-ratios
--company-id, --from-date, --to-date
efficiency-ratios
--company-id, --from-date, --to-date
revenue-by-customer
--company-id, --from-date, --to-date
--limit, --offset
revenue-by-item
--company-id, --from-date, --to-date
--limit, --offset
revenue-trend
--company-id, --from-date, --to-date
--periodicity
customer-concentration
--company-id, --from-date, --to-date
expense-breakdown
--company-id, --from-date, --to-date
--group-by
cost-trend
--company-id, --from-date, --to-date
--periodicity, --account-id
opex-vs-capex
--company-id, --from-date, --to-date
abc-analysis
--company-id
--as-of-date
inventory-turnover
--company-id, --from-date, --to-date
--item-id, --warehouse-id
aging-inventory
--company-id, --as-of-date
--aging-buckets
headcount-analytics
--company-id
--as-of-date, --group-by
payroll-analytics
--company-id, --from-date, --to-date
--department-id
leave-utilization
--company-id
--from-date, --to-date
project-profitability-analytics
--company-id
--project-id, --from-date, --to-date
quality-analytics
--company-id
--from-date, --to-date
support-metrics
--company-id
--from-date, --to-date
executive-dashboard
--company-id
--from-date, --to-date
company-scorecard
--company-id
--as-of-date
metric-trend
--company-id, --metric
--from-date, --to-date, --periodicity
period-comparison
--company-id, --periods (JSON)
--metrics (JSON)
analyze-query-performance
--company-id
AI Engine (21 actions)
Action
Required Flags
Optional Flags
detect-anomalies
--company-id
--from-date, --to-date — scans 21 anomaly types incl. asset_book_value_drift, dimension_tag_drift, consumption_spike (meter usage vs historical baseline) and rate_plan_mismatch (usage beyond plan allowance, Wave F). Steer: business English like "anything unusual in the books?" or "does anything look off / irregular?" ⇒ ✓ run detect-anomalies (a fresh 21-type sweep); ✗ don't keyword-search the ledger or reach for list-anomalies, which only re-reads prior finds
Confirm before: convert-opportunity-to-quotation, evaluate-business-rules, mark-opportunity-won/lost, convert-lead-to-opportunity, send-campaign, terminate-contract, activate-workflow/deactivate-workflow, merge-crm-contacts, promote-contact-to-customer. All other actions run immediately.
Graceful Degradation & Response Formatting
Analytics degrade gracefully when optional modules are missing; AI and CRM actions work independently. Currency: $X,XXX.XX (negatives in parentheses). Ratios: 2dp. Percentages: 1dp with %. Dates: Mon DD, YYYY. Use markdown tables for tabular output.
Money = TEXT (Python Decimal), IDs = TEXT (UUID4), Dates = TEXT (ISO 8601). CRM naming series: LEAD-{YEAR}-{SEQ}, OPP-{YEAR}-{SEQ}. CRM-Adv naming series: EMCAMP-{YEAR}-{SEQ}, TERR-{YEAR}-{SEQ}, CTR-{YEAR}-{SEQ}, AWFL-{YEAR}-{SEQ}, ANUR-{YEAR}-{SEQ}. GL entries and stock ledger entries are immutable. All queries use parameterized statements.
Script Path
scripts/db_query.py --action <action-name> [--key value ...]