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.
- mailpilot_crm-0.18.0/PKG-INFO +101 -0
- mailpilot_crm-0.18.0/README.md +80 -0
- mailpilot_crm-0.18.0/pyproject.toml +130 -0
- mailpilot_crm-0.18.0/src/mailpilot/SKILL.md +265 -0
- mailpilot_crm-0.18.0/src/mailpilot/__init__.py +1 -0
- mailpilot_crm-0.18.0/src/mailpilot/__main__.py +5 -0
- mailpilot_crm-0.18.0/src/mailpilot/_filters.py +216 -0
- mailpilot_crm-0.18.0/src/mailpilot/agent/__init__.py +54 -0
- mailpilot_crm-0.18.0/src/mailpilot/agent/classify.py +185 -0
- mailpilot_crm-0.18.0/src/mailpilot/agent/invoke.py +1132 -0
- mailpilot_crm-0.18.0/src/mailpilot/agent/retry.py +111 -0
- mailpilot_crm-0.18.0/src/mailpilot/agent/templates.py +316 -0
- mailpilot_crm-0.18.0/src/mailpilot/agent/tools.py +788 -0
- mailpilot_crm-0.18.0/src/mailpilot/cadence.py +150 -0
- mailpilot_crm-0.18.0/src/mailpilot/calendar.py +189 -0
- mailpilot_crm-0.18.0/src/mailpilot/cli.py +4181 -0
- mailpilot_crm-0.18.0/src/mailpilot/database.py +5885 -0
- mailpilot_crm-0.18.0/src/mailpilot/drive.py +236 -0
- mailpilot_crm-0.18.0/src/mailpilot/email_ops.py +238 -0
- mailpilot_crm-0.18.0/src/mailpilot/email_renderer.py +167 -0
- mailpilot_crm-0.18.0/src/mailpilot/exceptions.py +55 -0
- mailpilot_crm-0.18.0/src/mailpilot/gmail.py +895 -0
- mailpilot_crm-0.18.0/src/mailpilot/migrations/001_initial_schema.sql +240 -0
- mailpilot_crm-0.18.0/src/mailpilot/migrations/002_company_disabled_reason.sql +5 -0
- mailpilot_crm-0.18.0/src/mailpilot/migrations/003_tag_controlled_vocabulary.sql +67 -0
- mailpilot_crm-0.18.0/src/mailpilot/migrations/004_enrollment_status_collapse.sql +38 -0
- mailpilot_crm-0.18.0/src/mailpilot/migrations/005_drop_tag_disabled_activity_type.sql +24 -0
- mailpilot_crm-0.18.0/src/mailpilot/migrations/006_rename_workflow_objective_to_goal.sql +21 -0
- mailpilot_crm-0.18.0/src/mailpilot/migrations/007_add_meeting_tables.sql +36 -0
- mailpilot_crm-0.18.0/src/mailpilot/migrations/008_lowercase_contact_email.sql +111 -0
- mailpilot_crm-0.18.0/src/mailpilot/migrations/009_workflow_name_global_kebab.sql +31 -0
- mailpilot_crm-0.18.0/src/mailpilot/migrations/010_workflow_touch_cadence.sql +29 -0
- mailpilot_crm-0.18.0/src/mailpilot/models.py +787 -0
- mailpilot_crm-0.18.0/src/mailpilot/operator_log.py +121 -0
- mailpilot_crm-0.18.0/src/mailpilot/pubsub.py +289 -0
- mailpilot_crm-0.18.0/src/mailpilot/routing.py +396 -0
- mailpilot_crm-0.18.0/src/mailpilot/run.py +388 -0
- mailpilot_crm-0.18.0/src/mailpilot/schema.sql +305 -0
- mailpilot_crm-0.18.0/src/mailpilot/settings.py +190 -0
- 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."""
|