inbox-to-action 0.2.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.
- inbox_to_action-0.2.0/.env.example +62 -0
- inbox_to_action-0.2.0/.github/ISSUE_TEMPLATE/bug_report.md +20 -0
- inbox_to_action-0.2.0/.github/ISSUE_TEMPLATE/feature_request.md +14 -0
- inbox_to_action-0.2.0/.github/workflows/ci.yml +50 -0
- inbox_to_action-0.2.0/.gitignore +36 -0
- inbox_to_action-0.2.0/.serena/.gitignore +2 -0
- inbox_to_action-0.2.0/.serena/project.local.yml +5 -0
- inbox_to_action-0.2.0/.serena/project.yml +133 -0
- inbox_to_action-0.2.0/CLAUDE.md +60 -0
- inbox_to_action-0.2.0/CONTRIBUTING.md +45 -0
- inbox_to_action-0.2.0/Dockerfile +27 -0
- inbox_to_action-0.2.0/LICENSE +21 -0
- inbox_to_action-0.2.0/PKG-INFO +320 -0
- inbox_to_action-0.2.0/README.md +281 -0
- inbox_to_action-0.2.0/SETUP.md +199 -0
- inbox_to_action-0.2.0/config.example.json +14 -0
- inbox_to_action-0.2.0/conftest.py +72 -0
- inbox_to_action-0.2.0/glama.json +6 -0
- inbox_to_action-0.2.0/inbox_to_action/__init__.py +3 -0
- inbox_to_action-0.2.0/inbox_to_action/agent.py +166 -0
- inbox_to_action-0.2.0/inbox_to_action/config.py +108 -0
- inbox_to_action-0.2.0/inbox_to_action/fixtures/sample_inbox.json +42 -0
- inbox_to_action-0.2.0/inbox_to_action/llm_client.py +360 -0
- inbox_to_action-0.2.0/inbox_to_action/mailboxes/__init__.py +14 -0
- inbox_to_action-0.2.0/inbox_to_action/mailboxes/base.py +104 -0
- inbox_to_action-0.2.0/inbox_to_action/mailboxes/gmail.py +66 -0
- inbox_to_action-0.2.0/inbox_to_action/mailboxes/outlook.py +18 -0
- inbox_to_action-0.2.0/inbox_to_action/main.py +214 -0
- inbox_to_action-0.2.0/inbox_to_action/mcp_server.py +69 -0
- inbox_to_action-0.2.0/inbox_to_action/models.py +75 -0
- inbox_to_action-0.2.0/inbox_to_action/reasoner.py +60 -0
- inbox_to_action-0.2.0/inbox_to_action/report.py +111 -0
- inbox_to_action-0.2.0/inbox_to_action/tools/__init__.py +6 -0
- inbox_to_action-0.2.0/inbox_to_action/tools/calendar_flag.py +39 -0
- inbox_to_action-0.2.0/inbox_to_action/tools/classifier.py +58 -0
- inbox_to_action-0.2.0/inbox_to_action/tools/gmail.py +239 -0
- inbox_to_action-0.2.0/inbox_to_action/tools/notify.py +102 -0
- inbox_to_action-0.2.0/inbox_to_action/tools/summarizer.py +32 -0
- inbox_to_action-0.2.0/inbox_to_action/tools/tasks.py +118 -0
- inbox_to_action-0.2.0/pyproject.toml +57 -0
- inbox_to_action-0.2.0/pytest.ini +3 -0
- inbox_to_action-0.2.0/requirements.txt +4 -0
- inbox_to_action-0.2.0/server.json +24 -0
- inbox_to_action-0.2.0/setup_cron.sh +33 -0
- inbox_to_action-0.2.0/skills/inbox-to-action/SKILL.md +45 -0
- inbox_to_action-0.2.0/smithery.yaml +37 -0
- inbox_to_action-0.2.0/tests/test_agent.py +136 -0
- inbox_to_action-0.2.0/tests/test_classifier.py +54 -0
- inbox_to_action-0.2.0/tests/test_config.py +89 -0
- inbox_to_action-0.2.0/tests/test_gmail.py +122 -0
- inbox_to_action-0.2.0/tests/test_llm_client.py +324 -0
- inbox_to_action-0.2.0/tests/test_mailboxes.py +90 -0
- inbox_to_action-0.2.0/tests/test_main.py +101 -0
- inbox_to_action-0.2.0/tests/test_mcp_server.py +32 -0
- inbox_to_action-0.2.0/tests/test_notify.py +95 -0
- inbox_to_action-0.2.0/tests/test_report.py +53 -0
- inbox_to_action-0.2.0/tests/test_summarizer.py +19 -0
- inbox_to_action-0.2.0/tests/test_tasks.py +89 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# ── inbox-to-action configuration ──────────────────────────────────────────
|
|
2
|
+
# Copy to `.env` and fill in only the provider you use. Free-first defaults
|
|
3
|
+
# require NO paid keys.
|
|
4
|
+
|
|
5
|
+
# Which LLM backend the standalone CLI uses.
|
|
6
|
+
# openrouter | ollama | nim | openai | anthropic | claude | host
|
|
7
|
+
# `claude` = keyless via the local `claude` CLI (Claude Code login) — fastest
|
|
8
|
+
# no-key option; great for testing. Needs `claude` on PATH.
|
|
9
|
+
# `host` = no LLM here; reasoning is supplied by Claude Code (MCP / Skill mode).
|
|
10
|
+
PROVIDER=openrouter
|
|
11
|
+
|
|
12
|
+
# Model id (provider-specific). Defaults shown.
|
|
13
|
+
# openrouter -> google/gemma-4-31b-it:free (auto-falls back across free models)
|
|
14
|
+
# ollama -> llama3.1 (e.g. llama3.1:8b)
|
|
15
|
+
# anthropic -> claude-opus-4-8 (cheap: claude-haiku-4-5)
|
|
16
|
+
# Note: OpenRouter free models are often "rate-limited upstream" (429); the
|
|
17
|
+
# client retries with backoff and rotates the fallback list automatically.
|
|
18
|
+
# MODEL=
|
|
19
|
+
|
|
20
|
+
# ── Triage configuration (optional) ─────────────────────────────────────────
|
|
21
|
+
# Rules + freeform instructions live in config.json (see config.example.json),
|
|
22
|
+
# overridable per-run with `--config PATH`. This env var overrides the file's
|
|
23
|
+
# freeform instructions (handy for quick experiments):
|
|
24
|
+
# TRIAGE_INSTRUCTIONS=I'm job hunting — treat relevant job alerts as action_needed.
|
|
25
|
+
# INBOX_TO_ACTION_CONFIG=./config.json
|
|
26
|
+
|
|
27
|
+
# ── OpenAI-compatible providers ─────────────────────────────────────────────
|
|
28
|
+
# OpenRouter (free models available). https://openrouter.ai/keys
|
|
29
|
+
OPENROUTER_API_KEY=
|
|
30
|
+
|
|
31
|
+
# NVIDIA NIM. https://build.nvidia.com (NVIDIA_API_KEY also accepted)
|
|
32
|
+
# Default model: meta/llama-3.3-70b-instruct (override with MODEL=…).
|
|
33
|
+
NIM_API_KEY=
|
|
34
|
+
|
|
35
|
+
# OpenAI (paid BYOK fallback).
|
|
36
|
+
OPENAI_API_KEY=
|
|
37
|
+
|
|
38
|
+
# Ollama needs no key; just run `ollama serve` locally.
|
|
39
|
+
# OLLAMA_BASE_URL=http://localhost:11434/v1
|
|
40
|
+
|
|
41
|
+
# ── Anthropic ───────────────────────────────────────────────────────────────
|
|
42
|
+
# Optional. If unset, the client uses your `ant auth login` OAuth profile
|
|
43
|
+
# (fully keyless). Set only to force a specific key.
|
|
44
|
+
# ANTHROPIC_API_KEY=
|
|
45
|
+
|
|
46
|
+
# ── Tasks ───────────────────────────────────────────────────────────────────
|
|
47
|
+
# Optional Todoist integration (--todoist flag). Free tier.
|
|
48
|
+
# https://todoist.com/app/settings/integrations/developer
|
|
49
|
+
TODOIST_API_TOKEN=
|
|
50
|
+
|
|
51
|
+
# ── Telegram summary (--telegram flag) ──────────────────────────────────────
|
|
52
|
+
# Push a concise run summary to your phone. Create a bot with @BotFather → token;
|
|
53
|
+
# message the bot once, then GET https://api.telegram.org/bot<token>/getUpdates
|
|
54
|
+
# to read your chat id. Off by default. Notification only — never sends email.
|
|
55
|
+
# TELEGRAM_TOKEN is also accepted as an alias for TELEGRAM_BOT_TOKEN.
|
|
56
|
+
TELEGRAM_BOT_TOKEN=
|
|
57
|
+
TELEGRAM_CHAT_ID=
|
|
58
|
+
|
|
59
|
+
# ── Gmail ───────────────────────────────────────────────────────────────────
|
|
60
|
+
# Path to OAuth client secrets downloaded from Google Cloud Console.
|
|
61
|
+
# Only readonly + compose scopes are requested. Drafts only — never sends.
|
|
62
|
+
GMAIL_CLIENT_SECRETS=client_secret.json
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Something went wrong
|
|
4
|
+
title: "[bug] "
|
|
5
|
+
labels: bug
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
**Command run**
|
|
9
|
+
```
|
|
10
|
+
inbox-to-action run ...
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**What happened** (paste the error output — mask any secrets)
|
|
14
|
+
|
|
15
|
+
**Expected**
|
|
16
|
+
|
|
17
|
+
**Environment**
|
|
18
|
+
- `PROVIDER`:
|
|
19
|
+
- inbox-to-action version (`pip show inbox-to-action`):
|
|
20
|
+
- Python + OS:
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest an improvement
|
|
4
|
+
title: "[feat] "
|
|
5
|
+
labels: enhancement
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
**What you want**
|
|
9
|
+
|
|
10
|
+
**Why it helps**
|
|
11
|
+
|
|
12
|
+
**Notes** (provider/mailbox/config involved, if any)
|
|
13
|
+
|
|
14
|
+
> Reminder: the tool never sends email — proposals must keep the drafts-only invariant.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [main]
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
tags: ["v*"]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
cache: pip
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: |
|
|
27
|
+
python -m pip install --upgrade pip
|
|
28
|
+
pip install -e '.[dev,mcp]'
|
|
29
|
+
|
|
30
|
+
- name: Run tests
|
|
31
|
+
run: pytest --cov=. --cov-report=term-missing
|
|
32
|
+
|
|
33
|
+
# Publish to PyPI on a version tag (e.g. v0.2.0), after tests pass.
|
|
34
|
+
# Uses OIDC Trusted Publishing — no API token stored. One-time setup:
|
|
35
|
+
# configure the PyPI project's Trusted Publisher (repo tarunlnmiit/inbox-to-action,
|
|
36
|
+
# workflow ci.yml). See SETUP.md.
|
|
37
|
+
publish:
|
|
38
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
39
|
+
needs: test
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
permissions:
|
|
42
|
+
id-token: write
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/checkout@v4
|
|
45
|
+
- uses: actions/setup-python@v5
|
|
46
|
+
with:
|
|
47
|
+
python-version: "3.12"
|
|
48
|
+
- run: pip install hatch
|
|
49
|
+
- run: hatch build
|
|
50
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Secrets & tokens
|
|
2
|
+
.env
|
|
3
|
+
client_secret*.json
|
|
4
|
+
token.json
|
|
5
|
+
*.token
|
|
6
|
+
|
|
7
|
+
# Generated output
|
|
8
|
+
triage-report.md
|
|
9
|
+
tasks.md
|
|
10
|
+
|
|
11
|
+
# Python
|
|
12
|
+
__pycache__/
|
|
13
|
+
*.py[cod]
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
*.egg-info/
|
|
17
|
+
build/
|
|
18
|
+
dist/
|
|
19
|
+
|
|
20
|
+
# Personal triage config (ship config.example.json instead)
|
|
21
|
+
config.json
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.coverage
|
|
24
|
+
htmlcov/
|
|
25
|
+
coverage.xml
|
|
26
|
+
|
|
27
|
+
# OS / editor
|
|
28
|
+
.DS_Store
|
|
29
|
+
.idea/
|
|
30
|
+
.vscode/
|
|
31
|
+
|
|
32
|
+
# Internal planning/spec docs — never shared to the public repo
|
|
33
|
+
*-spec.md
|
|
34
|
+
*-plan*.md
|
|
35
|
+
plans/
|
|
36
|
+
inbox-to-action-claude-code-spec.md
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# This file allows you to locally override settings in project.yml for development purposes.
|
|
2
|
+
#
|
|
3
|
+
# Use the same keys as in project.yml here. Any setting you specify will override the corresponding
|
|
4
|
+
# setting in project.yml, allowing you to customise the configuration for your local development environment
|
|
5
|
+
# without affecting the project configuration in project.yml (which is intended to be versioned).
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# the name by which the project can be referenced within Serena
|
|
2
|
+
project_name: "inbox-to-action"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# list of languages for which language servers are started; choose from:
|
|
6
|
+
# al angular ansible bash clojure
|
|
7
|
+
# cpp cpp_ccls crystal csharp csharp_omnisharp
|
|
8
|
+
# dart elixir elm erlang fortran
|
|
9
|
+
# fsharp go groovy haskell haxe
|
|
10
|
+
# hlsl html java json julia
|
|
11
|
+
# kotlin lean4 lua luau markdown
|
|
12
|
+
# matlab msl nix ocaml pascal
|
|
13
|
+
# perl php php_phpactor powershell python
|
|
14
|
+
# python_jedi python_ty r rego ruby
|
|
15
|
+
# ruby_solargraph rust scala scss solidity
|
|
16
|
+
# svelte swift systemverilog terraform toml
|
|
17
|
+
# typescript typescript_vts vue yaml zig
|
|
18
|
+
# (This list may be outdated. For the current list, see values of Language enum here:
|
|
19
|
+
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
|
|
20
|
+
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
|
|
21
|
+
# Note:
|
|
22
|
+
# - For C, use cpp
|
|
23
|
+
# - For JavaScript, use typescript
|
|
24
|
+
# - For Angular projects, use angular (subsumes typescript+html; requires `npm install` in the project root)
|
|
25
|
+
# - For Svelte projects, use svelte (subsumes typescript/javascript for .svelte projects; requires npm)
|
|
26
|
+
# - For SCSS / Sass / plain CSS, use scss (some-sass-language-server handles all three)
|
|
27
|
+
# - For Free Pascal/Lazarus, use pascal
|
|
28
|
+
# Special requirements:
|
|
29
|
+
# Some languages require additional setup/installations.
|
|
30
|
+
# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers
|
|
31
|
+
# When using multiple languages, the first language server that supports a given file will be used for that file.
|
|
32
|
+
# The first language is the default language and the respective language server will be used as a fallback.
|
|
33
|
+
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
|
|
34
|
+
languages:
|
|
35
|
+
- python
|
|
36
|
+
|
|
37
|
+
# the encoding used by text files in the project
|
|
38
|
+
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
|
|
39
|
+
encoding: "utf-8"
|
|
40
|
+
|
|
41
|
+
# line ending convention to use when writing source files.
|
|
42
|
+
# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default)
|
|
43
|
+
# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings.
|
|
44
|
+
line_ending:
|
|
45
|
+
|
|
46
|
+
# The language backend to use for this project.
|
|
47
|
+
# If not set, the global setting from serena_config.yml is used.
|
|
48
|
+
# Valid values: LSP, JetBrains
|
|
49
|
+
# Note: the backend is fixed at startup. If a project with a different backend
|
|
50
|
+
# is activated post-init, an error will be returned.
|
|
51
|
+
language_backend:
|
|
52
|
+
|
|
53
|
+
# whether to use project's .gitignore files to ignore files
|
|
54
|
+
ignore_all_files_in_gitignore: true
|
|
55
|
+
|
|
56
|
+
# advanced configuration option allowing to configure language server-specific options.
|
|
57
|
+
# Maps the language key to the options.
|
|
58
|
+
# The settings are considered only if the project is trusted (see global configuration to define trusted projects).
|
|
59
|
+
# See https://oraios.github.io/serena/02-usage/050_configuration.html#language-server-specific-settings
|
|
60
|
+
ls_specific_settings: {}
|
|
61
|
+
|
|
62
|
+
# list of additional workspace folder paths for cross-package reference support (e.g. in monorepos).
|
|
63
|
+
# Paths can be absolute or relative to the project root.
|
|
64
|
+
# Each folder is registered as an LSP workspace folder, enabling language servers to discover
|
|
65
|
+
# symbols and references across package boundaries.
|
|
66
|
+
# Currently supported for: TypeScript.
|
|
67
|
+
# Example:
|
|
68
|
+
# additional_workspace_folders:
|
|
69
|
+
# - ../sibling-package
|
|
70
|
+
# - ../shared-lib
|
|
71
|
+
additional_workspace_folders: []
|
|
72
|
+
|
|
73
|
+
# list of additional paths to ignore in this project.
|
|
74
|
+
# Same syntax as gitignore, so you can use * and **.
|
|
75
|
+
# Note: global ignored_paths from serena_config.yml are also applied additively.
|
|
76
|
+
ignored_paths: []
|
|
77
|
+
|
|
78
|
+
# whether the project is in read-only mode
|
|
79
|
+
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
|
|
80
|
+
# Added on 2025-04-18
|
|
81
|
+
read_only: false
|
|
82
|
+
|
|
83
|
+
# list of tool names to exclude.
|
|
84
|
+
# This extends the existing exclusions (e.g. from the global configuration)
|
|
85
|
+
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
|
|
86
|
+
excluded_tools: []
|
|
87
|
+
|
|
88
|
+
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default).
|
|
89
|
+
# This extends the existing inclusions (e.g. from the global configuration).
|
|
90
|
+
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
|
|
91
|
+
included_optional_tools: []
|
|
92
|
+
|
|
93
|
+
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
|
|
94
|
+
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
|
|
95
|
+
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
|
|
96
|
+
fixed_tools: []
|
|
97
|
+
|
|
98
|
+
# list of mode names that are to be activated by default, overriding the setting in the global configuration.
|
|
99
|
+
# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes.
|
|
100
|
+
# If the setting is undefined/empty, the default_modes from the global configuration (serena_config.yml) apply.
|
|
101
|
+
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
|
|
102
|
+
# Therefore, you can set this to [] if you do not want the default modes defined in the global config to apply
|
|
103
|
+
# for this project.
|
|
104
|
+
# This setting can, in turn, be overridden by CLI parameters (--mode).
|
|
105
|
+
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
|
|
106
|
+
default_modes:
|
|
107
|
+
|
|
108
|
+
# list of mode names to be activated additionally for this project, e.g. ["query-projects"]
|
|
109
|
+
# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes.
|
|
110
|
+
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
|
|
111
|
+
added_modes:
|
|
112
|
+
|
|
113
|
+
# initial prompt for the project. It will always be given to the LLM upon activating the project
|
|
114
|
+
# (contrary to the memories, which are loaded on demand).
|
|
115
|
+
initial_prompt: ""
|
|
116
|
+
|
|
117
|
+
# time budget (seconds) per tool call for the retrieval of additional symbol information
|
|
118
|
+
# such as docstrings or parameter information.
|
|
119
|
+
# This overrides the corresponding setting in the global configuration; see the documentation there.
|
|
120
|
+
# If null or missing, use the setting from the global configuration.
|
|
121
|
+
symbol_info_budget:
|
|
122
|
+
|
|
123
|
+
# list of regex patterns which, when matched, mark a memory entry as read‑only.
|
|
124
|
+
# Extends the list from the global configuration, merging the two lists.
|
|
125
|
+
read_only_memory_patterns: []
|
|
126
|
+
|
|
127
|
+
# list of regex patterns for memories to completely ignore.
|
|
128
|
+
# Matching memories will not appear in list_memories or activate_project output
|
|
129
|
+
# and cannot be accessed via read_memory or write_memory.
|
|
130
|
+
# To access ignored memory files, use the read_file tool on the raw file path.
|
|
131
|
+
# Extends the list from the global configuration, merging the two lists.
|
|
132
|
+
# Example: ["_archive/.*", "_episodes/.*"]
|
|
133
|
+
ignored_memory_patterns: []
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# CLAUDE.md — project context for Claude Code
|
|
2
|
+
|
|
3
|
+
One-pass agentic inbox triage: fetch unread mail → classify → summarize → extract
|
|
4
|
+
tasks → draft replies (**never sends**) → flag calendar → write `triage-report.md`.
|
|
5
|
+
|
|
6
|
+
## Commands
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
inbox-to-action run --since 24h [--no-drafts] [--max N] [--telegram] [--todoist] [--config PATH] [--mock]
|
|
10
|
+
inbox-to-action auth [--account <id>] [--config PATH] [--client-secrets PATH]
|
|
11
|
+
inbox-to-action mcp # stdio MCP server (also: python -m inbox_to_action.mcp_server)
|
|
12
|
+
pytest --cov=. # tests (LLM + Gmail mocked)
|
|
13
|
+
ruff check inbox_to_action/
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Security invariant (do not break)
|
|
17
|
+
|
|
18
|
+
Gmail scopes = `gmail.readonly` + `gmail.compose` ONLY. Outlook = `Mail.Read` +
|
|
19
|
+
`Mail.ReadWrite` ONLY. No send scope, no send call anywhere. Drafts only. Enforced by
|
|
20
|
+
`tests/test_gmail.py`. `--mock` never writes to real Gmail.
|
|
21
|
+
|
|
22
|
+
## Package layout
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
inbox_to_action/
|
|
26
|
+
main.py Typer CLI (run/auth/mcp)
|
|
27
|
+
agent.py per-email trajectory (classify→summarize→tasks→draft→calendar)
|
|
28
|
+
llm_client.py providers: claude, ollama, openrouter, nim, openai, anthropic, host
|
|
29
|
+
reasoner.py ProviderReasoner vs HostReasoner injection seam
|
|
30
|
+
config.py Settings: triage_instructions, rules[], accounts[]
|
|
31
|
+
models.py Email, Task, TriageResult, CATEGORIES
|
|
32
|
+
report.py triage-report.md renderer
|
|
33
|
+
mcp_server.py FastMCP: fetch_emails, save_gmail_draft, append_tasks, write_report
|
|
34
|
+
mailboxes/ MailAccount protocol; gmail.py (works), outlook.py (stub)
|
|
35
|
+
tools/ classifier, summarizer, tasks (+ Todoist), gmail (+ is_noreply),
|
|
36
|
+
calendar_flag, notify (Telegram)
|
|
37
|
+
fixtures/ sample_inbox.json (--mock)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Config & env
|
|
41
|
+
|
|
42
|
+
- `config.json` (gitignored; example: `config.example.json`): `triage_instructions`,
|
|
43
|
+
`rules[]` (field: sender|subject|body|any → category), `accounts[]`
|
|
44
|
+
(id, kind: gmail|outlook, label, client_secret?, client_id?, tenant?).
|
|
45
|
+
- `.env` (gitignored; example: `.env.example`): `PROVIDER`, `MODEL`, provider keys
|
|
46
|
+
(`OPENROUTER_API_KEY`, `OPENAI_API_KEY`, `NIM_API_KEY`/`NVIDIA_API_KEY`,
|
|
47
|
+
`ANTHROPIC_API_KEY`, `OLLAMA_BASE_URL`), `TRIAGE_INSTRUCTIONS`,
|
|
48
|
+
`INBOX_TO_ACTION_CONFIG`, `TODOIST_API_TOKEN`, `TELEGRAM_BOT_TOKEN`/`TELEGRAM_TOKEN`
|
|
49
|
+
+ `TELEGRAM_CHAT_ID`, `GMAIL_CLIENT_SECRETS`, `INBOX_TO_ACTION_TOKEN`,
|
|
50
|
+
`INBOX_TO_ACTION_HTTP_TIMEOUT`.
|
|
51
|
+
|
|
52
|
+
## Categories
|
|
53
|
+
|
|
54
|
+
`action_needed` (→ tasks + draft + calendar) · `fyi` · `newsletter` · `noise`.
|
|
55
|
+
|
|
56
|
+
## Conventions
|
|
57
|
+
|
|
58
|
+
Every change via the git loop: branch → PR to `main` → squash-merge → delete branch.
|
|
59
|
+
Conventional commits. Small modules, type hints, tests for new behavior (mock the LLM
|
|
60
|
+
with `FakeReasoner`, network with `respx`).
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for helping improve `inbox-to-action`.
|
|
4
|
+
|
|
5
|
+
## The one hard rule
|
|
6
|
+
|
|
7
|
+
**The tool must never send email.** It requests only `gmail.readonly` +
|
|
8
|
+
`gmail.compose` (drafts) and, for Outlook, `Mail.Read` + `Mail.ReadWrite`. No PR may
|
|
9
|
+
add a send scope or a send call (`messages().send`, `sendMail`, etc.). This is
|
|
10
|
+
enforced by tests — keep them passing.
|
|
11
|
+
|
|
12
|
+
## Dev setup
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
git clone https://github.com/tarunlnmiit/inbox-to-action && cd inbox-to-action
|
|
16
|
+
python -m venv .venv && source .venv/bin/activate
|
|
17
|
+
pip install -e '.[dev,mcp]'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Before opening a PR
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
ruff check inbox_to_action/ # lint
|
|
24
|
+
pytest --cov=. --cov-report=term-missing # all tests pass, coverage stays high
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- One focused change per PR.
|
|
28
|
+
- Add tests for new behavior (see `tests/`). Provider/network code is mocked with
|
|
29
|
+
`respx`; the LLM is mocked with `FakeReasoner` (see `conftest.py`).
|
|
30
|
+
- Don't commit secrets — `.env` and `config.json` are gitignored. Ship
|
|
31
|
+
`.env.example` / `config.example.json` changes instead.
|
|
32
|
+
- Match the existing style: small focused modules, type hints, no mutation of shared
|
|
33
|
+
state.
|
|
34
|
+
|
|
35
|
+
## High-value contributions
|
|
36
|
+
|
|
37
|
+
- New mailbox providers (finish Outlook / Microsoft Graph — read + draft only).
|
|
38
|
+
- New LLM providers in `llm_client.py` (OpenAI-compatible ones are easy).
|
|
39
|
+
- Better classification/extraction prompts.
|
|
40
|
+
- New outbound notifiers (mirror `tools/notify.py`).
|
|
41
|
+
|
|
42
|
+
## Reporting bugs
|
|
43
|
+
|
|
44
|
+
Open an issue with: the exact command, the error output, your `PROVIDER`, and
|
|
45
|
+
Python + OS versions.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Dockerfile for Glama MCP listing + general container use.
|
|
2
|
+
# Builds the inbox-to-action MCP server (stdio transport).
|
|
3
|
+
FROM python:3.11-slim
|
|
4
|
+
|
|
5
|
+
LABEL org.opencontainers.image.source="https://github.com/tarunlnmiit/inbox-to-action" \
|
|
6
|
+
org.opencontainers.image.description="One-pass agentic inbox triage — MCP server" \
|
|
7
|
+
org.opencontainers.image.licenses="MIT"
|
|
8
|
+
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
|
|
11
|
+
# Install build deps first for layer caching.
|
|
12
|
+
COPY pyproject.toml README.md ./
|
|
13
|
+
COPY inbox_to_action ./inbox_to_action
|
|
14
|
+
|
|
15
|
+
# Example config read from the working dir at runtime (real config.json is mounted).
|
|
16
|
+
COPY config.example.json ./
|
|
17
|
+
|
|
18
|
+
# Install the package with the MCP extra.
|
|
19
|
+
RUN pip install --no-cache-dir --upgrade pip \
|
|
20
|
+
&& pip install --no-cache-dir ".[mcp]"
|
|
21
|
+
|
|
22
|
+
# The MCP server exposes IO-only tools; Claude Code (the host) is the LLM, so
|
|
23
|
+
# no provider key is required. Gmail credentials, if used, are mounted/supplied
|
|
24
|
+
# at runtime. The server starts and answers introspection without any secrets.
|
|
25
|
+
|
|
26
|
+
# FastMCP serves over stdio.
|
|
27
|
+
CMD ["python", "-m", "inbox_to_action.mcp_server"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tarun Gupta
|
|
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.
|