pulse-agent 2.0.1__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 (101) hide show
  1. pulse_agent-2.0.1/LICENSE +21 -0
  2. pulse_agent-2.0.1/PKG-INFO +287 -0
  3. pulse_agent-2.0.1/README.md +249 -0
  4. pulse_agent-2.0.1/pyproject.toml +58 -0
  5. pulse_agent-2.0.1/setup.cfg +4 -0
  6. pulse_agent-2.0.1/src/pulse/__init__.py +1 -0
  7. pulse_agent-2.0.1/src/pulse/analysis/__init__.py +0 -0
  8. pulse_agent-2.0.1/src/pulse/analysis/discovery.py +320 -0
  9. pulse_agent-2.0.1/src/pulse/analysis/event_summarizer.py +100 -0
  10. pulse_agent-2.0.1/src/pulse/analysis/preprocessor.py +481 -0
  11. pulse_agent-2.0.1/src/pulse/analysis/prompts.py +338 -0
  12. pulse_agent-2.0.1/src/pulse/analysis/source_summarizer.py +208 -0
  13. pulse_agent-2.0.1/src/pulse/analysis/vault_memory.py +439 -0
  14. pulse_agent-2.0.1/src/pulse/app/__init__.py +0 -0
  15. pulse_agent-2.0.1/src/pulse/app/api.py +129 -0
  16. pulse_agent-2.0.1/src/pulse/app/auth.py +44 -0
  17. pulse_agent-2.0.1/src/pulse/app/cli.py +3864 -0
  18. pulse_agent-2.0.1/src/pulse/app/cli_ui.py +191 -0
  19. pulse_agent-2.0.1/src/pulse/app/config.py +82 -0
  20. pulse_agent-2.0.1/src/pulse/app/config_loader.py +112 -0
  21. pulse_agent-2.0.1/src/pulse/app/corrections_webhook.py +71 -0
  22. pulse_agent-2.0.1/src/pulse/app/dependencies.py +14 -0
  23. pulse_agent-2.0.1/src/pulse/app/home_actions.py +147 -0
  24. pulse_agent-2.0.1/src/pulse/app/homepage.py +378 -0
  25. pulse_agent-2.0.1/src/pulse/app/main.py +244 -0
  26. pulse_agent-2.0.1/src/pulse/app/paths.py +30 -0
  27. pulse_agent-2.0.1/src/pulse/connectors/__init__.py +212 -0
  28. pulse_agent-2.0.1/src/pulse/connectors/browser.py +156 -0
  29. pulse_agent-2.0.1/src/pulse/connectors/calendar.py +134 -0
  30. pulse_agent-2.0.1/src/pulse/connectors/companion.py +62 -0
  31. pulse_agent-2.0.1/src/pulse/connectors/feeds.py +105 -0
  32. pulse_agent-2.0.1/src/pulse/connectors/github.py +130 -0
  33. pulse_agent-2.0.1/src/pulse/connectors/github_auth.py +58 -0
  34. pulse_agent-2.0.1/src/pulse/connectors/gitlab.py +103 -0
  35. pulse_agent-2.0.1/src/pulse/connectors/gitlab_auth.py +85 -0
  36. pulse_agent-2.0.1/src/pulse/connectors/gmail.py +81 -0
  37. pulse_agent-2.0.1/src/pulse/connectors/google_auth.py +111 -0
  38. pulse_agent-2.0.1/src/pulse/connectors/linear.py +189 -0
  39. pulse_agent-2.0.1/src/pulse/connectors/microsoft_auth.py +115 -0
  40. pulse_agent-2.0.1/src/pulse/connectors/microsoft_calendar.py +101 -0
  41. pulse_agent-2.0.1/src/pulse/connectors/microsoft_mail.py +96 -0
  42. pulse_agent-2.0.1/src/pulse/connectors/notion.py +320 -0
  43. pulse_agent-2.0.1/src/pulse/connectors/oauth.py +48 -0
  44. pulse_agent-2.0.1/src/pulse/connectors/oura.py +325 -0
  45. pulse_agent-2.0.1/src/pulse/connectors/oura_auth.py +75 -0
  46. pulse_agent-2.0.1/src/pulse/connectors/plaid_client.py +35 -0
  47. pulse_agent-2.0.1/src/pulse/connectors/plaid_connector.py +130 -0
  48. pulse_agent-2.0.1/src/pulse/connectors/plaid_link.py +219 -0
  49. pulse_agent-2.0.1/src/pulse/connectors/registry.py +67 -0
  50. pulse_agent-2.0.1/src/pulse/connectors/spotify.py +180 -0
  51. pulse_agent-2.0.1/src/pulse/connectors/spotify_auth.py +76 -0
  52. pulse_agent-2.0.1/src/pulse/connectors/youtube.py +117 -0
  53. pulse_agent-2.0.1/src/pulse/domain/connectors.py +37 -0
  54. pulse_agent-2.0.1/src/pulse/domain/correction_applications.py +16 -0
  55. pulse_agent-2.0.1/src/pulse/domain/corrections.py +10 -0
  56. pulse_agent-2.0.1/src/pulse/domain/event_types.py +107 -0
  57. pulse_agent-2.0.1/src/pulse/domain/events.py +13 -0
  58. pulse_agent-2.0.1/src/pulse/domain/llm.py +5 -0
  59. pulse_agent-2.0.1/src/pulse/domain/notifications.py +35 -0
  60. pulse_agent-2.0.1/src/pulse/domain/pattern_statuses.py +19 -0
  61. pulse_agent-2.0.1/src/pulse/jobs/runners.py +54 -0
  62. pulse_agent-2.0.1/src/pulse/jobs/scheduler.py +199 -0
  63. pulse_agent-2.0.1/src/pulse/llm/__init__.py +0 -0
  64. pulse_agent-2.0.1/src/pulse/llm/anthropic.py +19 -0
  65. pulse_agent-2.0.1/src/pulse/llm/anthropic_errors.py +106 -0
  66. pulse_agent-2.0.1/src/pulse/llm/factory.py +182 -0
  67. pulse_agent-2.0.1/src/pulse/llm/gemini.py +41 -0
  68. pulse_agent-2.0.1/src/pulse/llm/openai_compat.py +42 -0
  69. pulse_agent-2.0.1/src/pulse/mcp/__init__.py +0 -0
  70. pulse_agent-2.0.1/src/pulse/mcp/context.py +52 -0
  71. pulse_agent-2.0.1/src/pulse/mcp/server.py +293 -0
  72. pulse_agent-2.0.1/src/pulse/notifications/broadcast.py +19 -0
  73. pulse_agent-2.0.1/src/pulse/notifications/discord.py +28 -0
  74. pulse_agent-2.0.1/src/pulse/notifications/factory.py +73 -0
  75. pulse_agent-2.0.1/src/pulse/notifications/fcm.py +85 -0
  76. pulse_agent-2.0.1/src/pulse/notifications/gotify.py +27 -0
  77. pulse_agent-2.0.1/src/pulse/notifications/ntfy.py +26 -0
  78. pulse_agent-2.0.1/src/pulse/notifications/pushover.py +33 -0
  79. pulse_agent-2.0.1/src/pulse/notifications/slack.py +18 -0
  80. pulse_agent-2.0.1/src/pulse/notifications/smtp.py +61 -0
  81. pulse_agent-2.0.1/src/pulse/notifications/telegram.py +36 -0
  82. pulse_agent-2.0.1/src/pulse/notifications/webhook.py +22 -0
  83. pulse_agent-2.0.1/src/pulse/services/correction_interpreter.py +219 -0
  84. pulse_agent-2.0.1/src/pulse/services/corrections.py +388 -0
  85. pulse_agent-2.0.1/src/pulse/store/analytics.py +284 -0
  86. pulse_agent-2.0.1/src/pulse/store/correction_applications.py +96 -0
  87. pulse_agent-2.0.1/src/pulse/store/corrections.py +23 -0
  88. pulse_agent-2.0.1/src/pulse/store/db.py +19 -0
  89. pulse_agent-2.0.1/src/pulse/store/device_tokens.py +41 -0
  90. pulse_agent-2.0.1/src/pulse/store/events.py +85 -0
  91. pulse_agent-2.0.1/src/pulse/store/schema.py +130 -0
  92. pulse_agent-2.0.1/src/pulse/store/sync_state.py +30 -0
  93. pulse_agent-2.0.1/src/pulse/vault/__init__.py +5 -0
  94. pulse_agent-2.0.1/src/pulse/vault/obsidian_meta.py +63 -0
  95. pulse_agent-2.0.1/src/pulse/vault/onboarding.py +105 -0
  96. pulse_agent-2.0.1/src/pulse_agent.egg-info/PKG-INFO +287 -0
  97. pulse_agent-2.0.1/src/pulse_agent.egg-info/SOURCES.txt +99 -0
  98. pulse_agent-2.0.1/src/pulse_agent.egg-info/dependency_links.txt +1 -0
  99. pulse_agent-2.0.1/src/pulse_agent.egg-info/entry_points.txt +3 -0
  100. pulse_agent-2.0.1/src/pulse_agent.egg-info/requires.txt +25 -0
  101. pulse_agent-2.0.1/src/pulse_agent.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rupan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,287 @@
1
+ Metadata-Version: 2.4
2
+ Name: pulse-agent
3
+ Version: 2.0.1
4
+ Summary: Self-hosted personal intelligence agent
5
+ License-Expression: MIT
6
+ Keywords: personal-intelligence,data-ingestion,self-hosted,mcp
7
+ Classifier: Development Status :: 5 - Production/Stable
8
+ Classifier: Programming Language :: Python :: 3.12
9
+ Classifier: Programming Language :: Python :: 3.13
10
+ Classifier: Topic :: Office/Business
11
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
12
+ Requires-Python: >=3.12
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: rich>=13.7
16
+ Requires-Dist: rich-argparse>=1.5
17
+ Requires-Dist: fastapi
18
+ Requires-Dist: pydantic
19
+ Requires-Dist: aiosqlite
20
+ Requires-Dist: apscheduler
21
+ Requires-Dist: httpx
22
+ Requires-Dist: feedparser>=6.0.11
23
+ Requires-Dist: mcp[cli]
24
+ Requires-Dist: google-auth-oauthlib
25
+ Requires-Dist: google-api-python-client
26
+ Requires-Dist: plaid-python>=24
27
+ Requires-Dist: anthropic
28
+ Requires-Dist: uvicorn[standard]
29
+ Requires-Dist: questionary>=2.1.1
30
+ Provides-Extra: openai
31
+ Requires-Dist: openai>=1.0; extra == "openai"
32
+ Provides-Extra: gemini
33
+ Requires-Dist: google-genai>=1.0; extra == "gemini"
34
+ Provides-Extra: all-llm
35
+ Requires-Dist: openai>=1.0; extra == "all-llm"
36
+ Requires-Dist: google-genai>=1.0; extra == "all-llm"
37
+ Dynamic: license-file
38
+
39
+ # Pulse - Personal Intelligence Agent
40
+
41
+ A self-hosted, push-first personal intelligence agent. Pulse continuously ingests data from your digital life — email, calendar, purchases, health, media — and proactively surfaces insights through notifications. No app to open. No daily check-ins.
42
+
43
+ The core philosophy: big data companies already collect and exploit your personal data. Pulse reclaims it, running entirely on your own infrastructure with full transparency into what the agent knows and how it reasons.
44
+
45
+ ## How it works
46
+
47
+ ```
48
+ Data Sources (Gmail, Calendar, Notion, Linear, Oura, …)
49
+
50
+ Event Store (SQLite)
51
+
52
+ Analysis Engine ──→ Vault (Obsidian-compatible Markdown)
53
+
54
+ Notifications (Telegram)
55
+
56
+ User Corrections
57
+ ```
58
+
59
+ 1. **Connectors** pull data from your accounts and normalize it into timestamped events
60
+ 2. **Event Store** persists everything in a local SQLite database
61
+ 3. **Analysis Engine** runs scheduled insight discovery and writes patterns to your vault
62
+ 4. **Vault** writes human-readable markdown files you can browse in Obsidian
63
+ 5. **Notifications** push insights via Telegram, [ntfy](https://ntfy.sh), [Gotify](https://gotify.net/), email (SMTP), generic JSON webhooks, [Discord](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) / [Slack](https://api.slack.com/messaging/webhooks) incoming webhooks, [Pushover](https://pushover.net/)—configure one or more
64
+ 6. **Corrections** let you reply to fix anything the agent gets wrong
65
+
66
+ ## Two ways to run
67
+
68
+ **Standalone** — Pulse runs as its own service with FastAPI, APScheduler, and Telegram notifications. Good for `docker run` deployments.
69
+
70
+ **Agent integration** — Pulse exposes an [MCP server](https://modelcontextprotocol.io/) so any compatible agent (Claude Code, OpenClaw, etc.) can query your events, run discovery, inspect patterns, and record corrections using its own scheduling and LLM capabilities.
71
+
72
+ ## Installation
73
+
74
+ Install `pulse-agent` from PyPI. The package provides `pulse` and `pulse-mcp` commands.
75
+
76
+ **Quick install** (bootstraps [pipx](https://pipx.pypa.io/) if needed; runs `pulse onboard` when the session is interactive):
77
+
78
+ ```bash
79
+ curl -fsSL https://pulseagent.dev/install.sh | bash
80
+ ```
81
+
82
+ Manual install (recommended if you already use pipx):
83
+
84
+ ```bash
85
+ pipx install pulse-agent # isolated environment
86
+ # or
87
+ uv tool install pulse-agent
88
+ # or
89
+ pip install pulse-agent
90
+ ```
91
+
92
+ Config defaults to `~/.config/pulse` (config files, `pulse.toml`) and `~/.local/share/pulse` (database, vault, token files). Override the config directory with `PULSE_CONFIG_DIR`.
93
+
94
+ ## Developer setup
95
+
96
+ **With [uv](https://docs.astral.sh/uv/) (recommended)**
97
+
98
+ ```bash
99
+ uv sync
100
+ ```
101
+
102
+ Include dev tools (pytest): `uv sync --group dev`.
103
+
104
+ **With Nix** — from the repo root, `nix develop` drops you into a shell with Python, uv, and a `.venv` kept in sync via `uv sync --group dev`.
105
+
106
+ **Classic venv** — `python3 -m venv .venv`, activate, then `pip install -e .` (and `pip install pytest` if you run tests).
107
+
108
+ **Configuration file** — keep paths, secrets, connector blocks, and `[llm]` in **`pulse.toml`**, usually at **`.config/pulse.toml`** in your project (new default). A repo-root **`pulse.toml`** is still read if present and `.config/pulse.toml` does not exist. Override with **`PULSE_CONFIG_FILE`** (path to the TOML file) or **`PULSE_CONFIG_DIR`** (directory containing `pulse.toml`). See `pulse.toml.example`. Run `pulse configure` to edit the resolved file. Environment variables override the file: any `PULSE_*` name maps to the same snake_case root key (for example `PULSE_DATABASE_PATH` → `database_path`). `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and `GEMINI_API_KEY` are also applied when the corresponding field is empty. `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and `GEMINI_API_KEY` are also applied when the corresponding field is empty.
109
+
110
+ For Docker and shell exports, the same settings are often passed as env vars:
111
+
112
+ | Variable | Runtime use | Default |
113
+ |----------|-------------|---------|
114
+ | `PULSE_DATABASE_PATH` | SQLite database path; also determines where OAuth token files are stored | `data/pulse.db` |
115
+ | `PULSE_VAULT_PATH` | Markdown vault output directory | `Pulse-Vault` |
116
+ | `PULSE_TIMEZONE` | Timezone used for current-day resolution and day boundaries inside jobs | `UTC` |
117
+ | `PULSE_TELEGRAM_BOT_TOKEN` | Enables outbound Telegram notifications when paired with chat ID | _(optional)_ |
118
+ | `PULSE_TELEGRAM_CHAT_ID` | Destination chat for Telegram notifications | _(optional)_ |
119
+ | `PULSE_CORRECTIONS_WEBHOOK_SECRET` | Enables `POST /webhooks/corrections` (Bearer or `X-Pulse-Signature` HMAC); omit to disable the route | _(optional)_ |
120
+ | `PULSE_NTFY_TOPIC` | ntfy topic name (public `ntfy.sh` or self-hosted server) | _(optional)_ |
121
+ | `PULSE_NTFY_BASE_URL` | ntfy server root URL (default `https://ntfy.sh`) | _(optional)_ |
122
+ | `PULSE_NOTIFICATION_WEBHOOK_URL` | POST JSON payloads for any compatible receiver | _(optional)_ |
123
+ | `PULSE_DISCORD_WEBHOOK_URL` | Discord server incoming webhook URL | _(optional)_ |
124
+ | `PULSE_SLACK_WEBHOOK_URL` | Slack incoming webhook URL | _(optional)_ |
125
+ | `PULSE_PUSHOVER_USER_KEY` | Pushover user key (use with API token) | _(optional)_ |
126
+ | `PULSE_PUSHOVER_API_TOKEN` | Pushover application API token | _(optional)_ |
127
+ | `PULSE_GOTIFY_URL` | Gotify server base URL | _(optional)_ |
128
+ | `PULSE_GOTIFY_APP_TOKEN` | Gotify application token | _(optional)_ |
129
+ | `PULSE_SMTP_HOST` | SMTP server for email notifications | _(optional)_ |
130
+ | `PULSE_SMTP_PORT` | SMTP port (default `587`) | `587` |
131
+ | `PULSE_SMTP_USER` / `PULSE_SMTP_PASSWORD` | SMTP auth (optional for local relay) | _(optional)_ |
132
+ | `PULSE_SMTP_FROM` / `PULSE_SMTP_TO` | From address and recipient(s); `PULSE_SMTP_TO` can be comma-separated | _(optional)_ |
133
+ | `PULSE_SMTP_USE_TLS` | Use STARTTLS after connect (typical for port 587) | `true` |
134
+ | `PULSE_SMTP_USE_SSL` | Use implicit TLS (typical for port 465) | `false` |
135
+ | `PULSE_GOOGLE_CLIENT_ID` | Google OAuth client ID for enabled Google connectors | _(optional)_ |
136
+ | `PULSE_GOOGLE_CLIENT_SECRET` | Google OAuth client secret | _(optional)_ |
137
+ | `PULSE_SPOTIFY_CLIENT_ID` | Spotify OAuth client ID for the Spotify connector | _(optional)_ |
138
+ | `PULSE_SPOTIFY_CLIENT_SECRET` | Spotify OAuth client secret | _(optional)_ |
139
+ | `PULSE_MICROSOFT_CLIENT_ID` / `PULSE_MICROSOFT_CLIENT_SECRET` | Microsoft Graph (Outlook mail / calendar) OAuth | _(optional)_ |
140
+ | `PULSE_MICROSOFT_TENANT_ID` | Azure AD tenant (`common` if omitted) | _(optional)_ |
141
+ | `PULSE_GITHUB_CLIENT_ID` / `PULSE_GITHUB_CLIENT_SECRET` | GitHub OAuth for the GitHub connector | _(optional)_ |
142
+ | `PULSE_GITLAB_CLIENT_ID` / `PULSE_GITLAB_CLIENT_SECRET` | GitLab OAuth (or set `PULSE_GITLAB_TOKEN` for a PAT) | _(optional)_ |
143
+ | `PULSE_GITLAB_TOKEN` | GitLab personal access token (skips OAuth when set) | _(optional)_ |
144
+ | `PULSE_PLAID_CLIENT_ID` / `PULSE_PLAID_SECRET` | Plaid API credentials for bank transactions | _(optional)_ |
145
+ | `PULSE_PLAID_ENV` | `sandbox`, `development`, or `production` | _(optional)_ |
146
+ | `PULSE_OURA_CLIENT_ID` / `PULSE_OURA_CLIENT_SECRET` | Oura Cloud API OAuth (or set `PULSE_OURA_PERSONAL_ACCESS_TOKEN`) | _(optional)_ |
147
+ | `PULSE_OURA_PERSONAL_ACCESS_TOKEN` | Oura personal access token (skips OAuth when set) | _(optional)_ |
148
+ | `PULSE_NOTION_TOKEN` | Notion internal integration secret for the Notion connector | _(optional)_ |
149
+ | `PULSE_LINEAR_API_KEY` | Linear personal API key for assigned-issue sync | _(optional)_ |
150
+ | `PULSE_ANTHROPIC_API_KEY` | Anthropic API key for `[llm.*]` with `provider = "anthropic"` (or use `anthropic_api_key` in `pulse.toml`) | _(optional)_ |
151
+
152
+ Connector toggles and nested connector settings live under `[connectors.*]` in `pulse.toml` (not separate per-connector files).
153
+
154
+ LLM features require **`[llm.summarization]`**, **`[llm.discovery]`**, and/or **`[llm.corrections]`** in `pulse.toml` (see `pulse.toml.example`). Set `[llm] provider` once and use different `model` values per role (e.g. fast summarization + stronger discovery), or set `provider` on each block when mixing vendors. Put API keys in `pulse.toml` (`anthropic_api_key`, `openai_api_key`, `gemini_api_key`) or use `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and `GEMINI_API_KEY` in the environment. For **up-to-date model id examples** (Claude 4.6 family, GPT-5.4 variants, Gemini 2.5, etc.) and links to each vendor’s model list, see [**LLM provider configuration** in the configuration reference](docs/reference/configuration.md#llm-provider-configuration).
155
+
156
+ Discovery (scheduled, `pulse discover`, homepage **run discovery**, and MCP `pulse_discovery`) aggregates stats for the target window, then runs the LLM insight pipeline with your configured summarization and discovery models.
157
+
158
+ The full runtime config reference is in [`docs/reference/configuration.md`](docs/reference/configuration.md).
159
+
160
+ Standalone app, CLI commands, and the MCP server use `PULSE_DATABASE_PATH`. They resolve the rest of config via `load_config()` (default `.config/pulse.toml`, else repo-root `pulse.toml`, plus process environment overrides).
161
+
162
+ ## Docs
163
+
164
+ Documentation lives under [docs/index.md](docs/index.md). The deployed site serves the same guides at [`/docs/`](/docs/).
165
+
166
+ - [Self-hosting quickstart](docs/self-hosting/quickstart.md)
167
+ - [Configuration reference](docs/reference/configuration.md)
168
+ - [Operations runbook](docs/operations/runbook.md)
169
+ - [Connectors index](docs/connectors/index.md)
170
+ - [Contributing](CONTRIBUTING.md)
171
+
172
+ ## Run tests
173
+
174
+ ```bash
175
+ uv sync --group dev
176
+ uv run pytest
177
+ ```
178
+
179
+ Continuous integration (`.github/workflows/ci.yml`) runs `uv sync --group dev --locked` and `uv run pytest` on pushes and pull requests to `main`, on Python 3.12 and 3.13.
180
+
181
+ ## Start the standalone server
182
+
183
+ ```bash
184
+ uv run uvicorn --app-dir src pulse.app.main:create_app --factory
185
+ ```
186
+
187
+ ## Use as an MCP server
188
+
189
+ Pulse ships an [MCP](https://modelcontextprotocol.io/) server so you can use your **existing AI agents**—**Claude Code**, **OpenClaw**, Cursor, and any other MCP-capable client—to read events, run discovery, list patterns, check connectors, and record corrections through the same SQLite store and vault as the standalone app. The agent brings scheduling and models; Pulse brings your personal data and insight pipeline.
190
+
191
+ **Before you wire MCP**
192
+
193
+ 1. Complete a normal Pulse setup at least once: a real **`pulse.toml`** must exist where `load_config()` looks (default `~/.config/pulse/pulse.toml`, or repo-root `pulse.toml`, or the path from **`PULSE_CONFIG_FILE`** / **`PULSE_CONFIG_DIR`**). The MCP entrypoint uses `load_config(require_files=True)` and **exits on startup** if that file is missing—setting only `PULSE_DATABASE_PATH` / `PULSE_VAULT_PATH` in JSON is not enough without a TOML on disk. Run `pulse configure` if you have not created config yet.
194
+ 2. Put **`database_path`** and **`vault_path`** in `pulse.toml` (or override with **`PULSE_DATABASE_PATH`** and **`PULSE_VAULT_PATH`** in the MCP `env` block) so the agent process hits the same DB and vault as `pulse serve` / your scheduler.
195
+ 3. If the agent starts the server with a working directory where Pulse would not find your config, set **`PULSE_CONFIG_FILE`** (absolute path to `pulse.toml`) or **`PULSE_CONFIG_DIR`** in the MCP `env` block.
196
+
197
+ Add a server entry to your agent’s MCP settings (location and shape differ by product—Claude Code, OpenClaw, Cursor, etc.; check their docs for `"mcpServers"` or equivalent).
198
+
199
+ **Example: `pulse-mcp` on `PATH`** (after `pipx install pulse-agent` or `uv tool install pulse-agent`):
200
+
201
+ ```json
202
+ {
203
+ "mcpServers": {
204
+ "pulse": {
205
+ "command": "pulse-mcp",
206
+ "env": {
207
+ "PULSE_CONFIG_FILE": "/absolute/path/to/pulse.toml"
208
+ }
209
+ }
210
+ }
211
+ }
212
+ ```
213
+
214
+ Omit `PULSE_CONFIG_FILE` when default resolution already finds your `pulse.toml` (typical layout: `~/.config/pulse/pulse.toml`).
215
+
216
+ **Example: run from a git clone with uv**
217
+
218
+ ```json
219
+ {
220
+ "mcpServers": {
221
+ "pulse": {
222
+ "command": "uv",
223
+ "args": ["run", "python", "-m", "pulse.mcp.server"],
224
+ "cwd": "/absolute/path/to/pulse/repo",
225
+ "env": {
226
+ "PULSE_DATABASE_PATH": "/absolute/path/to/pulse.db",
227
+ "PULSE_VAULT_PATH": "/absolute/path/to/Pulse-Vault"
228
+ }
229
+ }
230
+ }
231
+ }
232
+ ```
233
+
234
+ When you rely on repo-root **`pulse.toml`**, set a server **`cwd`** to that repo *if your agent’s MCP config supports it*; otherwise use **`PULSE_CONFIG_FILE`** pointing at that file.
235
+
236
+ ### Available tools
237
+
238
+ | Tool | Description |
239
+ |------|-------------|
240
+ | `pulse_events_for_day` | Query events for a specific date, optionally filtered by source |
241
+ | `pulse_ingest_event` | Manually push an event into the store |
242
+ | `pulse_correct` | Record a correction or feedback about an insight |
243
+ | `pulse_discovery` | Run LLM insight discovery for a cadence and date |
244
+ | `pulse_insights` | List discovery patterns from the database |
245
+ | `pulse_read_pattern` | Read a pattern markdown file from the vault |
246
+ | `pulse_connector_status` | Check sync state of all connectors |
247
+
248
+ ### Available resources
249
+
250
+ | Resource | URI |
251
+ |----------|-----|
252
+ | Today's events | `pulse://events/today` |
253
+ | Connector status | `pulse://connectors/status` |
254
+
255
+ ## Mobile companion (optional)
256
+
257
+ The **Flutter** app under [`companion_app/`](companion_app/README.md) talks to the same server with `X-Pulse-Token` / `companion_token`. Enable **`[connectors.companion]`** to mount the webhook and API routes. Pattern content uses **`GET /api/insights`** and **`GET /api/insights/{id}`** (replacing removed digest endpoints).
258
+
259
+ ## Project structure
260
+
261
+ ```
262
+ src/pulse/
263
+ ├── app/ # FastAPI server, config, dependencies
264
+ ├── analysis/ # Preprocessing, source summaries, discovery engine
265
+ ├── connectors/ # Gmail, Calendar, YouTube, Spotify, M365, GitHub, GitLab, Plaid, browser, feeds, …
266
+ ├── domain/ # Core types and protocols
267
+ ├── jobs/ # Scheduled tasks (aggregation, discovery)
268
+ ├── mcp/ # MCP server for agent integration
269
+ ├── notifications/ # Telegram channel
270
+ ├── services/ # Business logic (corrections)
271
+ ├── store/ # SQLite repositories (events, sync state, corrections)
272
+ └── vault/ # Vault onboarding and Obsidian helpers
273
+ ```
274
+
275
+ ## Design principles
276
+
277
+ 1. **Push-first** — insights come to you as notifications
278
+ 2. **Zero-effort integration** — connecting data sources takes minutes
279
+ 3. **Full transparency** — all data stored as human-readable markdown in Obsidian
280
+ 4. **Extensible** — clean interfaces for connectors, LLM providers, and notification channels
281
+ 5. **Self-hosted** — runs on your hardware, data stays local
282
+
283
+ ## Releases and versioning
284
+
285
+ - **PyPI package:** `pulse-agent` (CLI entry points `pulse` and `pulse-mcp`).
286
+ - **Versioning:** [Semantic Versioning](https://semver.org/) — **MAJOR** for incompatible changes (including config or behavior you must act on), **MINOR** for backward-compatible features, **PATCH** for fixes. Review **`CHANGELOG.md`** before upgrading.
287
+ - **Shipping a release:** tag `v*` (for example `v1.0.0`); CI builds, publishes to PyPI, and builds Docker as defined in `.github/workflows/release-publish.yml`. Copy **`CHANGELOG.md`** into the GitHub release notes for that tag.
@@ -0,0 +1,249 @@
1
+ # Pulse - Personal Intelligence Agent
2
+
3
+ A self-hosted, push-first personal intelligence agent. Pulse continuously ingests data from your digital life — email, calendar, purchases, health, media — and proactively surfaces insights through notifications. No app to open. No daily check-ins.
4
+
5
+ The core philosophy: big data companies already collect and exploit your personal data. Pulse reclaims it, running entirely on your own infrastructure with full transparency into what the agent knows and how it reasons.
6
+
7
+ ## How it works
8
+
9
+ ```
10
+ Data Sources (Gmail, Calendar, Notion, Linear, Oura, …)
11
+
12
+ Event Store (SQLite)
13
+
14
+ Analysis Engine ──→ Vault (Obsidian-compatible Markdown)
15
+
16
+ Notifications (Telegram)
17
+
18
+ User Corrections
19
+ ```
20
+
21
+ 1. **Connectors** pull data from your accounts and normalize it into timestamped events
22
+ 2. **Event Store** persists everything in a local SQLite database
23
+ 3. **Analysis Engine** runs scheduled insight discovery and writes patterns to your vault
24
+ 4. **Vault** writes human-readable markdown files you can browse in Obsidian
25
+ 5. **Notifications** push insights via Telegram, [ntfy](https://ntfy.sh), [Gotify](https://gotify.net/), email (SMTP), generic JSON webhooks, [Discord](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) / [Slack](https://api.slack.com/messaging/webhooks) incoming webhooks, [Pushover](https://pushover.net/)—configure one or more
26
+ 6. **Corrections** let you reply to fix anything the agent gets wrong
27
+
28
+ ## Two ways to run
29
+
30
+ **Standalone** — Pulse runs as its own service with FastAPI, APScheduler, and Telegram notifications. Good for `docker run` deployments.
31
+
32
+ **Agent integration** — Pulse exposes an [MCP server](https://modelcontextprotocol.io/) so any compatible agent (Claude Code, OpenClaw, etc.) can query your events, run discovery, inspect patterns, and record corrections using its own scheduling and LLM capabilities.
33
+
34
+ ## Installation
35
+
36
+ Install `pulse-agent` from PyPI. The package provides `pulse` and `pulse-mcp` commands.
37
+
38
+ **Quick install** (bootstraps [pipx](https://pipx.pypa.io/) if needed; runs `pulse onboard` when the session is interactive):
39
+
40
+ ```bash
41
+ curl -fsSL https://pulseagent.dev/install.sh | bash
42
+ ```
43
+
44
+ Manual install (recommended if you already use pipx):
45
+
46
+ ```bash
47
+ pipx install pulse-agent # isolated environment
48
+ # or
49
+ uv tool install pulse-agent
50
+ # or
51
+ pip install pulse-agent
52
+ ```
53
+
54
+ Config defaults to `~/.config/pulse` (config files, `pulse.toml`) and `~/.local/share/pulse` (database, vault, token files). Override the config directory with `PULSE_CONFIG_DIR`.
55
+
56
+ ## Developer setup
57
+
58
+ **With [uv](https://docs.astral.sh/uv/) (recommended)**
59
+
60
+ ```bash
61
+ uv sync
62
+ ```
63
+
64
+ Include dev tools (pytest): `uv sync --group dev`.
65
+
66
+ **With Nix** — from the repo root, `nix develop` drops you into a shell with Python, uv, and a `.venv` kept in sync via `uv sync --group dev`.
67
+
68
+ **Classic venv** — `python3 -m venv .venv`, activate, then `pip install -e .` (and `pip install pytest` if you run tests).
69
+
70
+ **Configuration file** — keep paths, secrets, connector blocks, and `[llm]` in **`pulse.toml`**, usually at **`.config/pulse.toml`** in your project (new default). A repo-root **`pulse.toml`** is still read if present and `.config/pulse.toml` does not exist. Override with **`PULSE_CONFIG_FILE`** (path to the TOML file) or **`PULSE_CONFIG_DIR`** (directory containing `pulse.toml`). See `pulse.toml.example`. Run `pulse configure` to edit the resolved file. Environment variables override the file: any `PULSE_*` name maps to the same snake_case root key (for example `PULSE_DATABASE_PATH` → `database_path`). `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and `GEMINI_API_KEY` are also applied when the corresponding field is empty. `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and `GEMINI_API_KEY` are also applied when the corresponding field is empty.
71
+
72
+ For Docker and shell exports, the same settings are often passed as env vars:
73
+
74
+ | Variable | Runtime use | Default |
75
+ |----------|-------------|---------|
76
+ | `PULSE_DATABASE_PATH` | SQLite database path; also determines where OAuth token files are stored | `data/pulse.db` |
77
+ | `PULSE_VAULT_PATH` | Markdown vault output directory | `Pulse-Vault` |
78
+ | `PULSE_TIMEZONE` | Timezone used for current-day resolution and day boundaries inside jobs | `UTC` |
79
+ | `PULSE_TELEGRAM_BOT_TOKEN` | Enables outbound Telegram notifications when paired with chat ID | _(optional)_ |
80
+ | `PULSE_TELEGRAM_CHAT_ID` | Destination chat for Telegram notifications | _(optional)_ |
81
+ | `PULSE_CORRECTIONS_WEBHOOK_SECRET` | Enables `POST /webhooks/corrections` (Bearer or `X-Pulse-Signature` HMAC); omit to disable the route | _(optional)_ |
82
+ | `PULSE_NTFY_TOPIC` | ntfy topic name (public `ntfy.sh` or self-hosted server) | _(optional)_ |
83
+ | `PULSE_NTFY_BASE_URL` | ntfy server root URL (default `https://ntfy.sh`) | _(optional)_ |
84
+ | `PULSE_NOTIFICATION_WEBHOOK_URL` | POST JSON payloads for any compatible receiver | _(optional)_ |
85
+ | `PULSE_DISCORD_WEBHOOK_URL` | Discord server incoming webhook URL | _(optional)_ |
86
+ | `PULSE_SLACK_WEBHOOK_URL` | Slack incoming webhook URL | _(optional)_ |
87
+ | `PULSE_PUSHOVER_USER_KEY` | Pushover user key (use with API token) | _(optional)_ |
88
+ | `PULSE_PUSHOVER_API_TOKEN` | Pushover application API token | _(optional)_ |
89
+ | `PULSE_GOTIFY_URL` | Gotify server base URL | _(optional)_ |
90
+ | `PULSE_GOTIFY_APP_TOKEN` | Gotify application token | _(optional)_ |
91
+ | `PULSE_SMTP_HOST` | SMTP server for email notifications | _(optional)_ |
92
+ | `PULSE_SMTP_PORT` | SMTP port (default `587`) | `587` |
93
+ | `PULSE_SMTP_USER` / `PULSE_SMTP_PASSWORD` | SMTP auth (optional for local relay) | _(optional)_ |
94
+ | `PULSE_SMTP_FROM` / `PULSE_SMTP_TO` | From address and recipient(s); `PULSE_SMTP_TO` can be comma-separated | _(optional)_ |
95
+ | `PULSE_SMTP_USE_TLS` | Use STARTTLS after connect (typical for port 587) | `true` |
96
+ | `PULSE_SMTP_USE_SSL` | Use implicit TLS (typical for port 465) | `false` |
97
+ | `PULSE_GOOGLE_CLIENT_ID` | Google OAuth client ID for enabled Google connectors | _(optional)_ |
98
+ | `PULSE_GOOGLE_CLIENT_SECRET` | Google OAuth client secret | _(optional)_ |
99
+ | `PULSE_SPOTIFY_CLIENT_ID` | Spotify OAuth client ID for the Spotify connector | _(optional)_ |
100
+ | `PULSE_SPOTIFY_CLIENT_SECRET` | Spotify OAuth client secret | _(optional)_ |
101
+ | `PULSE_MICROSOFT_CLIENT_ID` / `PULSE_MICROSOFT_CLIENT_SECRET` | Microsoft Graph (Outlook mail / calendar) OAuth | _(optional)_ |
102
+ | `PULSE_MICROSOFT_TENANT_ID` | Azure AD tenant (`common` if omitted) | _(optional)_ |
103
+ | `PULSE_GITHUB_CLIENT_ID` / `PULSE_GITHUB_CLIENT_SECRET` | GitHub OAuth for the GitHub connector | _(optional)_ |
104
+ | `PULSE_GITLAB_CLIENT_ID` / `PULSE_GITLAB_CLIENT_SECRET` | GitLab OAuth (or set `PULSE_GITLAB_TOKEN` for a PAT) | _(optional)_ |
105
+ | `PULSE_GITLAB_TOKEN` | GitLab personal access token (skips OAuth when set) | _(optional)_ |
106
+ | `PULSE_PLAID_CLIENT_ID` / `PULSE_PLAID_SECRET` | Plaid API credentials for bank transactions | _(optional)_ |
107
+ | `PULSE_PLAID_ENV` | `sandbox`, `development`, or `production` | _(optional)_ |
108
+ | `PULSE_OURA_CLIENT_ID` / `PULSE_OURA_CLIENT_SECRET` | Oura Cloud API OAuth (or set `PULSE_OURA_PERSONAL_ACCESS_TOKEN`) | _(optional)_ |
109
+ | `PULSE_OURA_PERSONAL_ACCESS_TOKEN` | Oura personal access token (skips OAuth when set) | _(optional)_ |
110
+ | `PULSE_NOTION_TOKEN` | Notion internal integration secret for the Notion connector | _(optional)_ |
111
+ | `PULSE_LINEAR_API_KEY` | Linear personal API key for assigned-issue sync | _(optional)_ |
112
+ | `PULSE_ANTHROPIC_API_KEY` | Anthropic API key for `[llm.*]` with `provider = "anthropic"` (or use `anthropic_api_key` in `pulse.toml`) | _(optional)_ |
113
+
114
+ Connector toggles and nested connector settings live under `[connectors.*]` in `pulse.toml` (not separate per-connector files).
115
+
116
+ LLM features require **`[llm.summarization]`**, **`[llm.discovery]`**, and/or **`[llm.corrections]`** in `pulse.toml` (see `pulse.toml.example`). Set `[llm] provider` once and use different `model` values per role (e.g. fast summarization + stronger discovery), or set `provider` on each block when mixing vendors. Put API keys in `pulse.toml` (`anthropic_api_key`, `openai_api_key`, `gemini_api_key`) or use `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and `GEMINI_API_KEY` in the environment. For **up-to-date model id examples** (Claude 4.6 family, GPT-5.4 variants, Gemini 2.5, etc.) and links to each vendor’s model list, see [**LLM provider configuration** in the configuration reference](docs/reference/configuration.md#llm-provider-configuration).
117
+
118
+ Discovery (scheduled, `pulse discover`, homepage **run discovery**, and MCP `pulse_discovery`) aggregates stats for the target window, then runs the LLM insight pipeline with your configured summarization and discovery models.
119
+
120
+ The full runtime config reference is in [`docs/reference/configuration.md`](docs/reference/configuration.md).
121
+
122
+ Standalone app, CLI commands, and the MCP server use `PULSE_DATABASE_PATH`. They resolve the rest of config via `load_config()` (default `.config/pulse.toml`, else repo-root `pulse.toml`, plus process environment overrides).
123
+
124
+ ## Docs
125
+
126
+ Documentation lives under [docs/index.md](docs/index.md). The deployed site serves the same guides at [`/docs/`](/docs/).
127
+
128
+ - [Self-hosting quickstart](docs/self-hosting/quickstart.md)
129
+ - [Configuration reference](docs/reference/configuration.md)
130
+ - [Operations runbook](docs/operations/runbook.md)
131
+ - [Connectors index](docs/connectors/index.md)
132
+ - [Contributing](CONTRIBUTING.md)
133
+
134
+ ## Run tests
135
+
136
+ ```bash
137
+ uv sync --group dev
138
+ uv run pytest
139
+ ```
140
+
141
+ Continuous integration (`.github/workflows/ci.yml`) runs `uv sync --group dev --locked` and `uv run pytest` on pushes and pull requests to `main`, on Python 3.12 and 3.13.
142
+
143
+ ## Start the standalone server
144
+
145
+ ```bash
146
+ uv run uvicorn --app-dir src pulse.app.main:create_app --factory
147
+ ```
148
+
149
+ ## Use as an MCP server
150
+
151
+ Pulse ships an [MCP](https://modelcontextprotocol.io/) server so you can use your **existing AI agents**—**Claude Code**, **OpenClaw**, Cursor, and any other MCP-capable client—to read events, run discovery, list patterns, check connectors, and record corrections through the same SQLite store and vault as the standalone app. The agent brings scheduling and models; Pulse brings your personal data and insight pipeline.
152
+
153
+ **Before you wire MCP**
154
+
155
+ 1. Complete a normal Pulse setup at least once: a real **`pulse.toml`** must exist where `load_config()` looks (default `~/.config/pulse/pulse.toml`, or repo-root `pulse.toml`, or the path from **`PULSE_CONFIG_FILE`** / **`PULSE_CONFIG_DIR`**). The MCP entrypoint uses `load_config(require_files=True)` and **exits on startup** if that file is missing—setting only `PULSE_DATABASE_PATH` / `PULSE_VAULT_PATH` in JSON is not enough without a TOML on disk. Run `pulse configure` if you have not created config yet.
156
+ 2. Put **`database_path`** and **`vault_path`** in `pulse.toml` (or override with **`PULSE_DATABASE_PATH`** and **`PULSE_VAULT_PATH`** in the MCP `env` block) so the agent process hits the same DB and vault as `pulse serve` / your scheduler.
157
+ 3. If the agent starts the server with a working directory where Pulse would not find your config, set **`PULSE_CONFIG_FILE`** (absolute path to `pulse.toml`) or **`PULSE_CONFIG_DIR`** in the MCP `env` block.
158
+
159
+ Add a server entry to your agent’s MCP settings (location and shape differ by product—Claude Code, OpenClaw, Cursor, etc.; check their docs for `"mcpServers"` or equivalent).
160
+
161
+ **Example: `pulse-mcp` on `PATH`** (after `pipx install pulse-agent` or `uv tool install pulse-agent`):
162
+
163
+ ```json
164
+ {
165
+ "mcpServers": {
166
+ "pulse": {
167
+ "command": "pulse-mcp",
168
+ "env": {
169
+ "PULSE_CONFIG_FILE": "/absolute/path/to/pulse.toml"
170
+ }
171
+ }
172
+ }
173
+ }
174
+ ```
175
+
176
+ Omit `PULSE_CONFIG_FILE` when default resolution already finds your `pulse.toml` (typical layout: `~/.config/pulse/pulse.toml`).
177
+
178
+ **Example: run from a git clone with uv**
179
+
180
+ ```json
181
+ {
182
+ "mcpServers": {
183
+ "pulse": {
184
+ "command": "uv",
185
+ "args": ["run", "python", "-m", "pulse.mcp.server"],
186
+ "cwd": "/absolute/path/to/pulse/repo",
187
+ "env": {
188
+ "PULSE_DATABASE_PATH": "/absolute/path/to/pulse.db",
189
+ "PULSE_VAULT_PATH": "/absolute/path/to/Pulse-Vault"
190
+ }
191
+ }
192
+ }
193
+ }
194
+ ```
195
+
196
+ When you rely on repo-root **`pulse.toml`**, set a server **`cwd`** to that repo *if your agent’s MCP config supports it*; otherwise use **`PULSE_CONFIG_FILE`** pointing at that file.
197
+
198
+ ### Available tools
199
+
200
+ | Tool | Description |
201
+ |------|-------------|
202
+ | `pulse_events_for_day` | Query events for a specific date, optionally filtered by source |
203
+ | `pulse_ingest_event` | Manually push an event into the store |
204
+ | `pulse_correct` | Record a correction or feedback about an insight |
205
+ | `pulse_discovery` | Run LLM insight discovery for a cadence and date |
206
+ | `pulse_insights` | List discovery patterns from the database |
207
+ | `pulse_read_pattern` | Read a pattern markdown file from the vault |
208
+ | `pulse_connector_status` | Check sync state of all connectors |
209
+
210
+ ### Available resources
211
+
212
+ | Resource | URI |
213
+ |----------|-----|
214
+ | Today's events | `pulse://events/today` |
215
+ | Connector status | `pulse://connectors/status` |
216
+
217
+ ## Mobile companion (optional)
218
+
219
+ The **Flutter** app under [`companion_app/`](companion_app/README.md) talks to the same server with `X-Pulse-Token` / `companion_token`. Enable **`[connectors.companion]`** to mount the webhook and API routes. Pattern content uses **`GET /api/insights`** and **`GET /api/insights/{id}`** (replacing removed digest endpoints).
220
+
221
+ ## Project structure
222
+
223
+ ```
224
+ src/pulse/
225
+ ├── app/ # FastAPI server, config, dependencies
226
+ ├── analysis/ # Preprocessing, source summaries, discovery engine
227
+ ├── connectors/ # Gmail, Calendar, YouTube, Spotify, M365, GitHub, GitLab, Plaid, browser, feeds, …
228
+ ├── domain/ # Core types and protocols
229
+ ├── jobs/ # Scheduled tasks (aggregation, discovery)
230
+ ├── mcp/ # MCP server for agent integration
231
+ ├── notifications/ # Telegram channel
232
+ ├── services/ # Business logic (corrections)
233
+ ├── store/ # SQLite repositories (events, sync state, corrections)
234
+ └── vault/ # Vault onboarding and Obsidian helpers
235
+ ```
236
+
237
+ ## Design principles
238
+
239
+ 1. **Push-first** — insights come to you as notifications
240
+ 2. **Zero-effort integration** — connecting data sources takes minutes
241
+ 3. **Full transparency** — all data stored as human-readable markdown in Obsidian
242
+ 4. **Extensible** — clean interfaces for connectors, LLM providers, and notification channels
243
+ 5. **Self-hosted** — runs on your hardware, data stays local
244
+
245
+ ## Releases and versioning
246
+
247
+ - **PyPI package:** `pulse-agent` (CLI entry points `pulse` and `pulse-mcp`).
248
+ - **Versioning:** [Semantic Versioning](https://semver.org/) — **MAJOR** for incompatible changes (including config or behavior you must act on), **MINOR** for backward-compatible features, **PATCH** for fixes. Review **`CHANGELOG.md`** before upgrading.
249
+ - **Shipping a release:** tag `v*` (for example `v1.0.0`); CI builds, publishes to PyPI, and builds Docker as defined in `.github/workflows/release-publish.yml`. Copy **`CHANGELOG.md`** into the GitHub release notes for that tag.
@@ -0,0 +1,58 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pulse-agent"
7
+ version = "2.0.1"
8
+ description = "Self-hosted personal intelligence agent"
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ license = "MIT"
12
+ keywords = ["personal-intelligence", "data-ingestion", "self-hosted", "mcp"]
13
+ classifiers = [
14
+ "Development Status :: 5 - Production/Stable",
15
+ "Programming Language :: Python :: 3.12",
16
+ "Programming Language :: Python :: 3.13",
17
+ "Topic :: Office/Business",
18
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
19
+ ]
20
+ dependencies = [
21
+ "rich>=13.7",
22
+ "rich-argparse>=1.5",
23
+ "fastapi",
24
+ "pydantic",
25
+ "aiosqlite",
26
+ "apscheduler",
27
+ "httpx",
28
+ "feedparser>=6.0.11",
29
+ "mcp[cli]",
30
+ "google-auth-oauthlib",
31
+ "google-api-python-client",
32
+ "plaid-python>=24",
33
+ "anthropic",
34
+ "uvicorn[standard]",
35
+ "questionary>=2.1.1",
36
+ ]
37
+
38
+ [project.scripts]
39
+ pulse-mcp = "pulse.mcp.server:main"
40
+ pulse = "pulse.app.cli:main"
41
+
42
+ [project.optional-dependencies]
43
+ openai = ["openai>=1.0"]
44
+ gemini = ["google-genai>=1.0"]
45
+ all-llm = ["openai>=1.0", "google-genai>=1.0"]
46
+
47
+ [tool.setuptools]
48
+ package-dir = {"" = "src"}
49
+
50
+ [tool.setuptools.packages.find]
51
+ where = ["src"]
52
+
53
+ [dependency-groups]
54
+ dev = ["pytest>=8"]
55
+
56
+ [tool.pytest.ini_options]
57
+ pythonpath = ["src"]
58
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ """Pulse package."""
File without changes