mailpilot-crm 0.18.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. mailpilot_crm-0.18.0/PKG-INFO +101 -0
  2. mailpilot_crm-0.18.0/README.md +80 -0
  3. mailpilot_crm-0.18.0/pyproject.toml +130 -0
  4. mailpilot_crm-0.18.0/src/mailpilot/SKILL.md +265 -0
  5. mailpilot_crm-0.18.0/src/mailpilot/__init__.py +1 -0
  6. mailpilot_crm-0.18.0/src/mailpilot/__main__.py +5 -0
  7. mailpilot_crm-0.18.0/src/mailpilot/_filters.py +216 -0
  8. mailpilot_crm-0.18.0/src/mailpilot/agent/__init__.py +54 -0
  9. mailpilot_crm-0.18.0/src/mailpilot/agent/classify.py +185 -0
  10. mailpilot_crm-0.18.0/src/mailpilot/agent/invoke.py +1132 -0
  11. mailpilot_crm-0.18.0/src/mailpilot/agent/retry.py +111 -0
  12. mailpilot_crm-0.18.0/src/mailpilot/agent/templates.py +316 -0
  13. mailpilot_crm-0.18.0/src/mailpilot/agent/tools.py +788 -0
  14. mailpilot_crm-0.18.0/src/mailpilot/cadence.py +150 -0
  15. mailpilot_crm-0.18.0/src/mailpilot/calendar.py +189 -0
  16. mailpilot_crm-0.18.0/src/mailpilot/cli.py +4181 -0
  17. mailpilot_crm-0.18.0/src/mailpilot/database.py +5885 -0
  18. mailpilot_crm-0.18.0/src/mailpilot/drive.py +236 -0
  19. mailpilot_crm-0.18.0/src/mailpilot/email_ops.py +238 -0
  20. mailpilot_crm-0.18.0/src/mailpilot/email_renderer.py +167 -0
  21. mailpilot_crm-0.18.0/src/mailpilot/exceptions.py +55 -0
  22. mailpilot_crm-0.18.0/src/mailpilot/gmail.py +895 -0
  23. mailpilot_crm-0.18.0/src/mailpilot/migrations/001_initial_schema.sql +240 -0
  24. mailpilot_crm-0.18.0/src/mailpilot/migrations/002_company_disabled_reason.sql +5 -0
  25. mailpilot_crm-0.18.0/src/mailpilot/migrations/003_tag_controlled_vocabulary.sql +67 -0
  26. mailpilot_crm-0.18.0/src/mailpilot/migrations/004_enrollment_status_collapse.sql +38 -0
  27. mailpilot_crm-0.18.0/src/mailpilot/migrations/005_drop_tag_disabled_activity_type.sql +24 -0
  28. mailpilot_crm-0.18.0/src/mailpilot/migrations/006_rename_workflow_objective_to_goal.sql +21 -0
  29. mailpilot_crm-0.18.0/src/mailpilot/migrations/007_add_meeting_tables.sql +36 -0
  30. mailpilot_crm-0.18.0/src/mailpilot/migrations/008_lowercase_contact_email.sql +111 -0
  31. mailpilot_crm-0.18.0/src/mailpilot/migrations/009_workflow_name_global_kebab.sql +31 -0
  32. mailpilot_crm-0.18.0/src/mailpilot/migrations/010_workflow_touch_cadence.sql +29 -0
  33. mailpilot_crm-0.18.0/src/mailpilot/models.py +787 -0
  34. mailpilot_crm-0.18.0/src/mailpilot/operator_log.py +121 -0
  35. mailpilot_crm-0.18.0/src/mailpilot/pubsub.py +289 -0
  36. mailpilot_crm-0.18.0/src/mailpilot/routing.py +396 -0
  37. mailpilot_crm-0.18.0/src/mailpilot/run.py +388 -0
  38. mailpilot_crm-0.18.0/src/mailpilot/schema.sql +305 -0
  39. mailpilot_crm-0.18.0/src/mailpilot/settings.py +190 -0
  40. mailpilot_crm-0.18.0/src/mailpilot/sync.py +1518 -0
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.3
2
+ Name: mailpilot-crm
3
+ Version: 0.18.0
4
+ Summary: Agent-operated CRM with Gmail as the comms layer
5
+ Author: Konstantin Borovik
6
+ Author-email: Konstantin Borovik <github@lab5.ca>
7
+ Requires-Dist: click>=8.1.0,<8.4.0
8
+ Requires-Dist: google-api-python-client>=2.170.0,<3.0.0
9
+ Requires-Dist: google-auth>=2.40.0,<3.0.0
10
+ Requires-Dist: google-cloud-pubsub>=2.29.0,<3.0.0
11
+ Requires-Dist: httpx>=0.28.0,<1.0.0
12
+ Requires-Dist: logfire>=4.0.0,<5.0.0
13
+ Requires-Dist: mistune>=3.1.0,<4.0.0
14
+ Requires-Dist: psycopg[binary]>=3.2.0,<4.0.0
15
+ Requires-Dist: pydantic-ai-slim[anthropic]>=2.2.0,<3.0.0
16
+ Requires-Dist: pydantic-settings>=2.7.0,<3.0.0
17
+ Requires-Python: >=3.14
18
+ Project-URL: Homepage, https://lab5.ca
19
+ Project-URL: Repository, https://github.com/kborovik/mailpilot
20
+ Description-Content-Type: text/markdown
21
+
22
+ # MailPilot
23
+
24
+ Agent-operated CRM with Gmail as the communication layer.
25
+
26
+ **[See it in action](https://lab5.ca/mailpilot//)**
27
+
28
+ ## Overview
29
+
30
+ MailPilot manages contacts, companies, and communication workflows through Gmail API. It is designed to be operated by AI agents -- Claude Code as the strategic orchestrator and an internal Pydantic AI agent for real-time reactive work.
31
+
32
+ ### Two-Layer Intelligence
33
+
34
+ 1. **Claude Code** -- strategic orchestrator. Creates workflows, assigns contacts, reviews outcomes, generates reports. Operates the system via CLI.
35
+ 2. **Internal Pydantic AI agent** -- subordinate tactical executor. Handles inbound email classification, auto-replies, and follow-up scheduling within workflows.
36
+
37
+ ### Key Capabilities
38
+
39
+ - **Contact and company management** -- track relationships, tag for segmentation, annotate with notes
40
+ - **Activity timeline** -- unified chronological log of all interactions per contact
41
+ - **Email workflows** -- inbound auto-reply and outbound campaigns via Gmail API with service account delegation
42
+ - **Task scheduling** -- deferred agent work with scheduled execution for long-running processes
43
+ - **Reporting** -- Claude Code queries the database and generates activity summaries, relationship health, and campaign effectiveness reports
44
+
45
+ ## Architecture
46
+
47
+ - **CLI-first** -- JSON output, meaningful exit codes, actionable errors. Designed for LLM agent consumption.
48
+ - **PostgreSQL** -- contacts, companies, workflows, emails, activities, tags, notes. Raw SQL via psycopg, no ORM.
49
+ - **Gmail API** -- service account domain-wide delegation. Pub/Sub for real-time notifications. History API for incremental sync.
50
+ - **Pydantic AI** -- stateless agent invocations with tool access. Per-contact advisory locks for concurrency.
51
+ - **Observability** -- Pydantic Logfire (OpenTelemetry-based) for tracing and logging.
52
+
53
+ ## Tech Stack
54
+
55
+ - Python 3.14
56
+ - PostgreSQL 18
57
+ - Gmail API (`google-api-python-client`)
58
+ - Pydantic AI (agent framework)
59
+ - Pydantic Logfire (observability)
60
+ - Click (CLI)
61
+ - basedpyright (strict type checking)
62
+ - ruff (formatting and linting)
63
+ - pytest (testing)
64
+
65
+ ## Quick Start
66
+
67
+ ```bash
68
+ # Install dependencies
69
+ uv sync
70
+
71
+ # Configure
72
+ mailpilot config set database_url postgresql://localhost/mailpilot
73
+ mailpilot config set google_application_credentials /path/to/service-account.json
74
+ mailpilot config set anthropic_api_key sk-ant-...
75
+
76
+ # Create an account
77
+ mailpilot account create --email user@example.com --display-name "User Name"
78
+
79
+ # Sync emails
80
+ mailpilot account sync
81
+
82
+ # Start the sync loop
83
+ mailpilot run
84
+ ```
85
+
86
+ ## Development
87
+
88
+ ```bash
89
+ make check # lint + tests
90
+ make lint # ruff format + ruff check + basedpyright
91
+ make py-test # pytest -x
92
+ ```
93
+
94
+ ## Documentation
95
+
96
+ - [SPEC.md](SPEC.md) -- single source of truth (goals, constraints, invariants, tasks, bugs).
97
+ - [CLAUDE.md](CLAUDE.md) -- operator/agent guide.
98
+
99
+ ## License
100
+
101
+ Private.
@@ -0,0 +1,80 @@
1
+ # MailPilot
2
+
3
+ Agent-operated CRM with Gmail as the communication layer.
4
+
5
+ **[See it in action](https://lab5.ca/mailpilot//)**
6
+
7
+ ## Overview
8
+
9
+ MailPilot manages contacts, companies, and communication workflows through Gmail API. It is designed to be operated by AI agents -- Claude Code as the strategic orchestrator and an internal Pydantic AI agent for real-time reactive work.
10
+
11
+ ### Two-Layer Intelligence
12
+
13
+ 1. **Claude Code** -- strategic orchestrator. Creates workflows, assigns contacts, reviews outcomes, generates reports. Operates the system via CLI.
14
+ 2. **Internal Pydantic AI agent** -- subordinate tactical executor. Handles inbound email classification, auto-replies, and follow-up scheduling within workflows.
15
+
16
+ ### Key Capabilities
17
+
18
+ - **Contact and company management** -- track relationships, tag for segmentation, annotate with notes
19
+ - **Activity timeline** -- unified chronological log of all interactions per contact
20
+ - **Email workflows** -- inbound auto-reply and outbound campaigns via Gmail API with service account delegation
21
+ - **Task scheduling** -- deferred agent work with scheduled execution for long-running processes
22
+ - **Reporting** -- Claude Code queries the database and generates activity summaries, relationship health, and campaign effectiveness reports
23
+
24
+ ## Architecture
25
+
26
+ - **CLI-first** -- JSON output, meaningful exit codes, actionable errors. Designed for LLM agent consumption.
27
+ - **PostgreSQL** -- contacts, companies, workflows, emails, activities, tags, notes. Raw SQL via psycopg, no ORM.
28
+ - **Gmail API** -- service account domain-wide delegation. Pub/Sub for real-time notifications. History API for incremental sync.
29
+ - **Pydantic AI** -- stateless agent invocations with tool access. Per-contact advisory locks for concurrency.
30
+ - **Observability** -- Pydantic Logfire (OpenTelemetry-based) for tracing and logging.
31
+
32
+ ## Tech Stack
33
+
34
+ - Python 3.14
35
+ - PostgreSQL 18
36
+ - Gmail API (`google-api-python-client`)
37
+ - Pydantic AI (agent framework)
38
+ - Pydantic Logfire (observability)
39
+ - Click (CLI)
40
+ - basedpyright (strict type checking)
41
+ - ruff (formatting and linting)
42
+ - pytest (testing)
43
+
44
+ ## Quick Start
45
+
46
+ ```bash
47
+ # Install dependencies
48
+ uv sync
49
+
50
+ # Configure
51
+ mailpilot config set database_url postgresql://localhost/mailpilot
52
+ mailpilot config set google_application_credentials /path/to/service-account.json
53
+ mailpilot config set anthropic_api_key sk-ant-...
54
+
55
+ # Create an account
56
+ mailpilot account create --email user@example.com --display-name "User Name"
57
+
58
+ # Sync emails
59
+ mailpilot account sync
60
+
61
+ # Start the sync loop
62
+ mailpilot run
63
+ ```
64
+
65
+ ## Development
66
+
67
+ ```bash
68
+ make check # lint + tests
69
+ make lint # ruff format + ruff check + basedpyright
70
+ make py-test # pytest -x
71
+ ```
72
+
73
+ ## Documentation
74
+
75
+ - [SPEC.md](SPEC.md) -- single source of truth (goals, constraints, invariants, tasks, bugs).
76
+ - [CLAUDE.md](CLAUDE.md) -- operator/agent guide.
77
+
78
+ ## License
79
+
80
+ Private.
@@ -0,0 +1,130 @@
1
+ [project]
2
+ # PyPI distribution name; "mailpilot" is owned by an unrelated PyPI project.
3
+ # The import package and CLI command stay "mailpilot" (module-name below).
4
+ name = "mailpilot-crm"
5
+ version = "0.18.0"
6
+ description = "Agent-operated CRM with Gmail as the comms layer"
7
+ readme = "README.md"
8
+ authors = [
9
+ { name = "Konstantin Borovik", email = "github@lab5.ca" }
10
+ ]
11
+ requires-python = ">=3.14"
12
+ dependencies = [
13
+ "click>=8.1.0,<8.4.0",
14
+ "google-api-python-client>=2.170.0,<3.0.0",
15
+ "google-auth>=2.40.0,<3.0.0",
16
+ "google-cloud-pubsub>=2.29.0,<3.0.0",
17
+ "httpx>=0.28.0,<1.0.0",
18
+ "logfire>=4.0.0,<5.0.0",
19
+ "mistune>=3.1.0,<4.0.0",
20
+ "psycopg[binary]>=3.2.0,<4.0.0",
21
+ "pydantic-ai-slim[anthropic]>=2.2.0,<3.0.0",
22
+ "pydantic-settings>=2.7.0,<3.0.0",
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://lab5.ca"
27
+ Repository = "https://github.com/kborovik/mailpilot"
28
+
29
+ [project.scripts]
30
+ mailpilot = "mailpilot.cli:main"
31
+
32
+ [build-system]
33
+ requires = ["uv_build>=0.11.2,<0.12.0"]
34
+ build-backend = "uv_build"
35
+
36
+ [tool.uv.build-backend]
37
+ module-name = "mailpilot"
38
+
39
+ [dependency-groups]
40
+ dev = [
41
+ "basedpyright>=1.37.0",
42
+ "pytest>=8.4.0,<9.0.0",
43
+ "pytest-httpx>=0.35.0",
44
+ "ruff>=0.13.0",
45
+ "uv>=0.8.17",
46
+ ]
47
+
48
+ [tool.basedpyright]
49
+ typeCheckingMode = "strict"
50
+ pythonVersion = "3.14"
51
+ include = ["src/mailpilot/", "tests/"]
52
+ reportMissingTypeStubs = false
53
+ reportUnknownVariableType = false
54
+ reportUnknownMemberType = false
55
+ reportUnknownArgumentType = false
56
+ reportUnknownParameterType = false
57
+ reportUnknownLambdaType = false
58
+ reportUnusedFunction = "warning"
59
+
60
+ [tool.ruff]
61
+ target-version = "py314"
62
+ src = ["src", "tests"]
63
+
64
+ [tool.ruff.format]
65
+ docstring-code-format = true
66
+
67
+ [tool.ruff.lint]
68
+ fixable = ["ALL"]
69
+ select = [
70
+ "B", # flake8-bugbear
71
+ "C4", # flake8-comprehensions
72
+ "C90", # mccabe
73
+ "COM", # flake8-commas
74
+ "D", # pydocstyle
75
+ "E", # pycodestyle
76
+ "F", # pyflakes
77
+ "I", # isort
78
+ "N", # pep8-naming
79
+ "PL", # Pylint
80
+ "PT", # flake8-pytest-style
81
+ "RUF", # Ruff-specific rules
82
+ "SIM", # flake8-simplify
83
+ "TID", # flake8-tidy-imports
84
+ "UP", # pyupgrade
85
+ "W", # pycodestyle warnings
86
+ ]
87
+ ignore = [
88
+ "PLR2004", # magic values in comparisons
89
+ "PLC0415", # import not at top-level (lazy imports)
90
+ "D100", # Missing docstring in public module
91
+ "D104", # Missing docstring in public package
92
+ "D105", # Missing docstring in magic method
93
+ "D107", # Missing docstring in __init__
94
+ "COM812", # trailing commas
95
+ ]
96
+
97
+ [tool.ruff.lint.pydocstyle]
98
+ convention = "google"
99
+
100
+ [tool.ruff.lint.isort]
101
+ known-first-party = ["mailpilot"]
102
+
103
+ [tool.ruff.lint.per-file-ignores]
104
+ "src/mailpilot/cli.py" = [
105
+ "PLR0913", # Click commands have many parameters by design
106
+ ]
107
+ "src/mailpilot/database.py" = [
108
+ "PLR0913", # Create functions map to table columns
109
+ ]
110
+ "src/mailpilot/gmail.py" = [
111
+ "PLR0913", # Gmail API functions have many parameters
112
+ ]
113
+ "tests/**/*.py" = [
114
+ "D", # No docstrings required in tests
115
+ "PLR2004", # Magic values OK in tests
116
+ "PLR0913", # Too many arguments OK in tests
117
+ "E501", # Line too long
118
+ ]
119
+ ".claude/**/*.py" = [
120
+ "D", # No docstrings required in operator skill scripts
121
+ "E501", # Line too long
122
+ "PLR2004", # Magic values OK in scripts
123
+ ]
124
+
125
+ [tool.pytest.ini_options]
126
+ filterwarnings = [
127
+ "ignore::DeprecationWarning",
128
+ "ignore::PendingDeprecationWarning",
129
+ "ignore::logfire._internal.config.LogfireNotConfiguredWarning",
130
+ ]
@@ -0,0 +1,265 @@
1
+ # mailpilot CLI skill
2
+
3
+ External LLM-agent reference for the `mailpilot` CLI. Audience: agents that
4
+ have `mailpilot` installed as a dependency and need to drive it from a shell.
5
+ Scope: command grammar, JSON envelope shape, exit codes, common task recipes,
6
+ settings. Out of scope: database schema, internal agent / template wiring.
7
+
8
+ ## Grammar
9
+
10
+ ```
11
+ mailpilot <noun> <verb> [args]
12
+ mailpilot run | status | config get|set
13
+ mailpilot --version | --help | --completion <shell> | --skill | --debug
14
+ ```
15
+
16
+ Nouns: `account`, `company`, `contact`, `workflow`, `enrollment`, `task`,
17
+ `email`, `activity`, `tag`, `note`, `template`, `db`.
18
+
19
+ Verbs: `list`, `search`, `view`, `create`, `update`, `disable`, `enable`,
20
+ `add`, `remove`, `reply`, `send`, `start`, `stop`, `cancel`, `retry`, `run`,
21
+ `sync`, `export`, `import`, `init`, `migrate`, `check`. Not every verb applies
22
+ to every noun -- use
23
+ `mailpilot <noun> --help` to enumerate. `config` exposes the `get` and `set`
24
+ subverbs for reading and writing persistent configuration.
25
+
26
+ ## JSON envelope
27
+
28
+ Every noun-verb command writes a single JSON document to stdout. Operator
29
+ diagnostics go to stderr and never to stdout.
30
+
31
+ - `list`, `search`, `sync`, `export`, `import`:
32
+ `{"<plural>": [...], "record_count": <int>, "ok": true}`
33
+ - `view`, `create`, `update`, `disable`, `enable`, `add`, `remove`,
34
+ `reply`, `send`, `start`, `stop`, `cancel`, `retry`, `init`, `migrate`,
35
+ `check`:
36
+ `{"<singular>": {...}, "record_count": 1, "ok": true}`
37
+ - error: `{"error": "<code>", "message": "<text>", "ok": false}`
38
+
39
+ Every `ok: true` envelope carries a top-level integer `record_count`: the
40
+ array length for array payloads, `1` for single-object payloads. Error
41
+ envelopes omit it.
42
+
43
+ Plural keys mirror the noun (`accounts`, `companies`, `contacts`,
44
+ `workflows`, `enrollments`, `tasks`, `emails`, `activities`, `tags`, `notes`,
45
+ `templates`). Singular keys are the noun itself (`account`, `company`, ...).
46
+
47
+ Soft-disable verbs such as `contact disable`, `enrollment disable`, and
48
+ `tag disable` return the full updated entity under the singular envelope
49
+ since the row is retained.
50
+
51
+ ## Exit codes
52
+
53
+ - `0` -- success. `ok: true` payload on stdout.
54
+ - `1` -- failure. `ok: false` envelope on stderr; stdout stays empty.
55
+
56
+ The top-level `--skill`, `--version`, `--help`, `--completion` flags emit
57
+ plain text (not JSON) and exit `0`.
58
+
59
+ ## Settings
60
+
61
+ Persistent config lives in `~/.mailpilot/config.json`. Read and write with:
62
+
63
+ ```
64
+ mailpilot config get [KEY]
65
+ mailpilot config set KEY VALUE
66
+ ```
67
+
68
+ `config get` with no key returns `{"config": {...}, "ok": true}`. With a key
69
+ it returns `{"key": ..., "value": ..., "ok": true}` or an `invalid_key`
70
+ error envelope.
71
+
72
+ Every key may also be overridden via an environment variable of the form
73
+ `MAILPILOT_<UPPERCASE_KEY>` (for example, `MAILPILOT_DATABASE_URL`,
74
+ `MAILPILOT_ANTHROPIC_API_KEY`, `MAILPILOT_RUN_INTERVAL`). Priority is
75
+ constructor kwargs (tests only), then `MAILPILOT_*` env vars, then the
76
+ config file, then field defaults.
77
+
78
+ Keys:
79
+
80
+ - `database_url` -- PostgreSQL DSN. Default `postgresql://localhost/mailpilot`.
81
+ - `anthropic_api_key` -- required for agent invocations.
82
+ - `anthropic_model` -- e.g. `claude-sonnet-4-6`.
83
+ - `anthropic_base_url` -- Anthropic-compatible API endpoint. Default
84
+ `https://api.anthropic.com`; point it at e.g. `https://api.novita.ai/anthropic`
85
+ to route the same call to another vendor.
86
+ - `anthropic_thinking` -- workflow-agent extended thinking. Default `adaptive`
87
+ (on); set to empty to turn it off. Classifier never reads this key.
88
+ - `anthropic_effort` -- workflow-agent reasoning effort. Default `high`; one of
89
+ `low`, `medium`, `high`, `xhigh`, `max`, or empty to send no effort key.
90
+ `xhigh` needs Opus 4.7 or newer. Classifier never reads this key.
91
+ - `anthropic_max_tokens` -- workflow-agent output-token budget. Default `16384`;
92
+ always sent so default-active thinking cannot exhaust the provider-default
93
+ budget before any reply text. Classifier never reads this key.
94
+ - `google_application_credentials` -- path to service-account JSON. Optional
95
+ when running on a platform that exposes Application Default Credentials (GCE
96
+ attached service account, GKE Workload Identity, Cloud Run identity); leave
97
+ unset to use ADC. Set explicitly otherwise. Domain-wide delegation works in
98
+ both modes; ADC mode signs JWTs via the IAM Credentials API and requires the
99
+ active service account to hold `roles/iam.serviceAccountTokenCreator` on
100
+ itself.
101
+ - `google_pubsub_topic` -- default `mailpilot-topic-dev`.
102
+ - `google_pubsub_subscription` -- default `mailpilot-sub-dev`.
103
+ - `logfire_token` -- optional. Enables cloud telemetry.
104
+ - `logfire_environment` -- `development` or `production`.
105
+ - `run_interval` -- fallback poll interval for the sync loop, in seconds.
106
+ Default `60`.
107
+ - `max_concurrent_tasks` -- bound on the worker pool that drains the task
108
+ queue. Default `10`.
109
+
110
+ ## Recipes
111
+
112
+ ### Inspect state
113
+
114
+ ```
115
+ mailpilot status
116
+ mailpilot account list
117
+ mailpilot workflow list --account-email <ACCOUNT_REF>
118
+ mailpilot enrollment list --workflow-id <ID>
119
+ mailpilot task list --status pending
120
+ mailpilot email list --account-email <ACCOUNT_REF> --limit 50
121
+ ```
122
+
123
+ ### Provision and migrate the schema
124
+
125
+ ```
126
+ mailpilot db init
127
+ mailpilot db migrate
128
+ mailpilot db check
129
+ ```
130
+
131
+ `db init` provisions an empty database from the bundled schema; it refuses a
132
+ populated database (no destructive re-init) and is an idempotent no-op once
133
+ current. `db migrate` applies pending forward migrations, one transaction each.
134
+ `db check` reports the schema verdict and exits non-zero on `pending`/`drift`,
135
+ so it doubles as a deploy gate.
136
+
137
+ ### Onboard an account
138
+
139
+ ```
140
+ mailpilot account create --email outbound@example.com --display-name "Outbound"
141
+ mailpilot account sync --account-email <ACCOUNT_REF>
142
+ ```
143
+
144
+ `account sync` performs a one-shot Gmail sync; omit `--account-email` to sync
145
+ every account. The long-running `mailpilot run` loop handles ongoing
146
+ Pub/Sub deltas.
147
+
148
+ ### Create a contact and company
149
+
150
+ ```
151
+ mailpilot company create --domain example.com --name "Example Co"
152
+ mailpilot contact create --email lead@example.com \
153
+ --first-name "Ada" --last-name "Lovelace" --company-domain <COMPANY_REF>
154
+ ```
155
+
156
+ Soft-disable a contact (preserves audit history) with:
157
+
158
+ ```
159
+ mailpilot contact disable <CONTACT_REF> --reason "left company"
160
+ ```
161
+
162
+ ### Define a workflow declaratively
163
+
164
+ Workflow definitions are one TOML file per workflow. Export/import is TOML-only
165
+ and idempotent; round-trip is keyed on the globally unique `name`.
166
+
167
+ ```
168
+ mailpilot workflow export --account-email <ACCOUNT_REF> --out-dir workflows/
169
+ mailpilot workflow import --account-email <ACCOUNT_REF> --file workflows/
170
+ ```
171
+
172
+ `workflow export` writes one `*.toml` per workflow into `--out-dir` and prints a
173
+ JSON status envelope of the paths written (TOML never goes to stdout). `workflow
174
+ import` takes a single `.toml` file or a directory of them (`*.toml` glob). Each
175
+ file carries `name`, `template`, `goal`, `instructions`, `theme`, with
176
+ `instructions` as a TOML multi-line literal string. Available templates:
177
+
178
+ ```
179
+ mailpilot template list
180
+ mailpilot template view <NAME>
181
+ ```
182
+
183
+ `template` is immutable on update -- changing it requires deleting and
184
+ recreating the workflow. Import reports a per-row `template_immutable` error
185
+ when the value differs and continues with the rest of the batch.
186
+
187
+ Every import envelope carries top-level integer `applied` and `rejected`
188
+ counts beside the `workflows` rows. An import that applies zero rows (every
189
+ row rejected, or no `*.toml` found) fails loudly: `import_failed` error
190
+ envelope on stderr with the per-row rows inlined, exit 1. A partial import
191
+ stays `ok: true` (exit 0) with per-row errors inline, so check `rejected`
192
+ before trusting a batch.
193
+
194
+ ### Enroll a contact
195
+
196
+ `enrollment add` constructs the binding from `--workflow-id` + `--contact-email`
197
+ and returns the freshly-minted scalar `id`; every other verb takes that id
198
+ as a single positional argument.
199
+
200
+ ```
201
+ mailpilot enrollment add --workflow-id <WID> --contact-email <CONTACT_REF>
202
+ mailpilot enrollment run <ENROLLMENT_ID> # manual kick
203
+ mailpilot enrollment view <ENROLLMENT_ID>
204
+ mailpilot enrollment disable <ENROLLMENT_ID> --reason "left company"
205
+ mailpilot enrollment enable <ENROLLMENT_ID>
206
+ ```
207
+
208
+ Pass `--scheduled-at <ISO>` on `enrollment add` against an outbound workflow
209
+ to queue a first-touch send for that time; the run loop dispatches it when
210
+ due.
211
+
212
+ Enrollment status is `active` or `disabled`. `disabled` is the operator halt
213
+ (set via `enrollment disable`, reversed via `enrollment enable`); the agent
214
+ never re-enables an enrollment. Terminal outcomes (`completed`, `failed`) are
215
+ recorded as activity-log entries by the agent, not as enrollment status
216
+ changes.
217
+
218
+ ### Send and reply by hand
219
+
220
+ ```
221
+ mailpilot email send --account-email <ADDR> --to lead@example.com \
222
+ --subject "Hello" --body "..."
223
+ mailpilot email reply --account-email <ADDR> --email-id <EMAIL_ID> --body "..."
224
+ ```
225
+
226
+ ### Tag, note, and audit
227
+
228
+ ```
229
+ mailpilot tag create vip
230
+ mailpilot tag add --tag vip --contact-email <ADDR>
231
+ mailpilot tag remove --tag vip --contact-email <ADDR>
232
+ mailpilot tag disable vip --reason "<text>"
233
+ mailpilot note add --contact-email <ADDR> --body "Met at conf 2026."
234
+ mailpilot activity list --contact-email <ADDR>
235
+ ```
236
+
237
+ Tags are a controlled vocabulary: `tag create` defines a name, `tag add`
238
+ links it to a contact or company, `tag remove` unlinks, and `tag disable`
239
+ retires the name. A note attaches to exactly one of `contact_id` or
240
+ `company_id`. Activities may attach to either, both, or neither.
241
+
242
+ ### Task queue
243
+
244
+ ```
245
+ mailpilot task list --status pending
246
+ mailpilot task view <TID>
247
+ mailpilot task cancel <TID>
248
+ mailpilot task retry <TID> # only on failed or cancelled rows
249
+ ```
250
+
251
+ ### Run the sync loop
252
+
253
+ ```
254
+ mailpilot run
255
+ ```
256
+
257
+ Foreground process. Drives Gmail Pub/Sub delivery, runs queued tasks,
258
+ invokes the agent on routed inbound mail. Stderr emits one
259
+ `HH:MM:SS event=<name> ...` line per operator event. `Ctrl-C` stops cleanly.
260
+
261
+ ## Discovery
262
+
263
+ Every command supports `--help`. The top-level `--help` lists noun groups;
264
+ `mailpilot <noun> --help` lists verbs; `mailpilot <noun> <verb> --help`
265
+ lists flags. When uncertain, prefer `--help` over guessing.
@@ -0,0 +1 @@
1
+ """MailPilot -- CRM application for cold email outreach via Gmail."""
@@ -0,0 +1,5 @@
1
+ """Allow running as `python -m mailpilot`."""
2
+
3
+ from mailpilot.cli import main
4
+
5
+ main()