Commit Graph
32 Commits
Author SHA1 Message Date
Claude cb78b90332 docs(openspec): add design for implement-web-chat change
Selects Chainlit as the chat UI library (AI-native, Python, handles
WebSocket/streaming/session out of the box). Establishes Python as the
project language. Documents accessibility gap mitigations (ARIA live
regions, focus management, reduced-motion, contrast overrides). Defines
project layout and Chainlit configuration.

https://claude.ai/code/session_01SUWzMzFvSfWiHXA2p6rPg9
2026-02-22 05:15:25 +00:00
Claude eb2b69557e docs(openspec): add proposal for implement-web-chat change
Defines why the web chat is being built now, what changes (chat-interface
capability moving from spec to implementation), and adds accessibility as
a first-class requirement (WCAG 2.1 AA, keyboard nav, ARIA live regions).

https://claude.ai/code/session_01SUWzMzFvSfWiHXA2p6rPg9
2026-02-22 05:12:17 +00:00
Claude 3b32652d3e chore: scaffold implement-web-chat OpenSpec change
Creates the change directory for the web chat implementation with
.openspec.yaml metadata. First artifact (proposal.md) is pending.

https://claude.ai/code/session_01SUWzMzFvSfWiHXA2p6rPg9
2026-02-22 05:10:29 +00:00
Markus GrafandGitHub bab0e5fe5e Merge pull request #4 from gurix/claude/clarify-api-key-docs-McxCw
Update documentation for multi-language support and API keys
2026-02-21 23:14:29 +01:00
Claude 905debb48e Clarify API key docs and language support in README
- Remove ANTHROPIC_API_KEY and OPENAI_API_KEY from the Required
  variables table; they are provider-specific, not universally
  required. Add a note pointing readers to the Switching AI providers
  section instead.
- Replace "Supports German and English; defaults to German" with
  "Responds in any language the parent uses; defaults to German" to
  accurately reflect that the agent is fully language-agnostic.

https://claude.ai/code/session_01F9RoUQYKktPrmsvemSYrPk
2026-02-21 22:13:33 +00:00
Markus GrafandGitHub 2fac05c4ee Merge pull request #2 from gurix/claude/email-agent-multi-model-c3ShZ
Implement Python email agent with multi-model AI support
2026-02-21 23:06:40 +01:00
Claude eba450c5a5 Fix email quoting to include full conversation history
fetch_unread_messages now returns both `body` (stripped, for the LLM)
and `raw_body` (full with nested quotes, for the outgoing reply).
main.py passes raw_body as quoted_text so each reply carries the
complete conversation thread, not just the single last message.

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-21 21:54:35 +00:00
Claude 8217b33f38 Remove hardcoded language list from greeting prompt
Replace the explicit enumeration of German, English, French, Italian,
and Spanish with "any human language" to be inclusive of all parents
(Arabic, Turkish, etc.).

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-21 21:44:38 +00:00
Claude db97a357c9 Restore per-leader routing with configurable email addresses
Previously routing was hardcoded (Andrea for indoor, Barbara for outdoor).
Then it was replaced with a flat ADMIN_EMAILS list which lost the routing.
This commit restores routing via three separate env vars:

  ADMIN_EMAIL_INDOOR  — indoor leader, To when indoor days are booked
  ADMIN_EMAIL_OUTDOOR — outdoor leader, To when outdoor days are booked
  ADMIN_EMAIL_CC      — always Cc'd (comma-separated for multiple)

For testing, set all three to your own address so no real leader gets mail.

Changes:
- Config: replaced admin_emails with admin_email_indoor/outdoor/cc fields
- AdminNotifier: replaced admin_emails param with indoor_email/outdoor_email/
  cc_emails; _recipients_for() restored as an instance method using these
- main.py: wires the three new config fields into AdminNotifier
- .env.example: documents the three new variables with production defaults
- Tests: fixture updated to use new params

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-21 21:31:49 +00:00
Claude 7fb1d1fa0f Make admin notification recipients configurable via ADMIN_EMAILS
Previously the To/Cc addresses were hardcoded in notifier.py (Andrea,
Barbara, Markus). This caused accidental emails to production contacts
during testing.

Changes:
- New ADMIN_EMAILS env var: comma-separated list of addresses.
  First address → To; remaining addresses → Cc.
- AdminNotifier now accepts admin_emails list; warns and skips if empty.
- Removed hardcoded _INDOOR_EMAIL / _OUTDOOR_EMAIL / _ADMIN_CC_EMAIL
  constants and the _recipients_for() routing method.
- Config.from_env() parses ADMIN_EMAILS into a list.
- main.py passes config.admin_emails to AdminNotifier.
- .env.example documents the new variable with production example.
- Tests: fixture updated; TestRecipientsFor removed (routing gone).

For testing: ADMIN_EMAILS=you@example.com
For production: ADMIN_EMAILS=andrea.sigrist@gmx.net,baba.laeubli@gmail.com,spielgruppen@familien-verein.ch

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-21 21:23:51 +00:00
Claude af96c7a310 German admin emails and German-only registration data storage
notifier.py:
- All email body text translated to German (section headers, labels,
  day names, playgroup type names, age format, change diff labels)
- Subject lines changed to German: "Neue Anmeldung:" / "Anmeldung aktualisiert:"
- Channel label localised: "E-Mail" / "Chat"
- Fallback special needs label changed to "Keine"

prompts.py:
- New rule: always store free-text field values (especially specialNeeds)
  in German in `updates`, translating from the parent's language if needed;
  use "Keine" for no special needs

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-21 21:13:19 +00:00
Claude c488a0061e Improve email conversation efficiency: language hint + aggressive info collection
Two prompt changes:

1. Greeting step: explicitly tell parents they can write in any language
   (German, English, French, Italian, Spanish, …) and the agent will reply
   in the same language. Also kick off info collection immediately by asking
   for child name + DOB in the greeting reply.

2. Personality: replace the "1–2 questions at a time" rule with a strategy
   that gathers all relevant questions per step in one message (woven into
   natural sentences, not a form), and explicitly re-asks any unanswered
   questions before advancing — no open question is silently skipped.

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-21 21:01:01 +00:00
Claude 4440b00d91 Prepend email headers to message text passed to LLM
The LLM previously only received the stripped email body, giving it no
way to extract the sender's email address for the parentGuardian.email
field. Prepend Von:/Betreff: headers to every message so the LLM can
read the From address and subject without asking the parent for them.

The quoted_text sent back in the reply still uses only msg["body"] so
the quote block stays clean.

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-21 20:46:20 +00:00
Claude 9c33bafbf3 Prohibit markdown in email reply text
Tell the LLM explicitly that the reply field must be plain text with no
markdown (no bold, italic, headers, bullet points, or backticks). Email
clients display raw text so markdown syntax would appear as literal
characters rather than formatting.

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-21 20:10:41 +00:00
Claude fef0388534 Quote parent's message in email replies
Add standard > -prefixed quote block to outbound replies so parents can
see what they wrote in the previous message, matching natural email client
behaviour. The quote header uses the German "Am <date> schrieb <addr>:"
convention (matching Outlook/Thunderbird).

- email_channel.py: add _build_quoted_block() helper; extend send_reply()
  with optional quoted_text/quoted_from params
- main.py: pass msg["body"] and msg["from"] as quoted_text/quoted_from

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-21 20:05:12 +00:00
Claude 98a5f5b5b1 Resolve PR review comments
- prompts.py: correct age restrictions (indoor ≥2 yrs, outdoor ≥2.5 yrs)
  Previously had indoor ≥2.5 and outdoor ≥3, which was too restrictive
- README.md: add full setup and configuration guide covering prerequisites,
  installation, env var reference, provider switching, cron scheduling,
  running tests, and knowledge base editing

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-21 13:35:35 +00:00
gurix 60f056ece4 Merge branch 'main' into claude/email-agent-multi-model-c3ShZ 2026-02-21 14:24:33 +01:00
Markus GrafandGitHub cfa90174e3 Merge pull request #3 from gurix/claude/update-readme-purpose-MxY60
Update README with project purpose and capabilities
2026-02-21 14:22:10 +01:00
Claude 7f55cdd204 Add pytest test suite (92 tests, all passing)
Covers every module in src/ with unit tests:

- tests/conftest.py        shared fixtures (complete_registration, fresh_state, …)
- tests/test_models.py     RegistrationData.is_complete(), to_dict/from_dict round-trips
- tests/test_storage.py    normalize_email, _diff_registrations, ConversationStore CRUD,
                           registration versioning
- tests/test_llm.py        litellm wrapper — message construction, model passthrough,
                           error propagation
- tests/test_agent.py      EmailAgent — new/existing conversations, registration
                           completion, admin notification, fallback on LLM error,
                           JSON parsing, _apply_updates
- tests/test_notifier.py   AdminNotifier routing, fee calculation, SMTP dispatch
- tests/test_knowledge_base.py  KnowledgeBase loading and reload

All external I/O (litellm, SMTP, filesystem) is mocked. Tests run fast (~6s)
with no network access required.

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-21 08:07:31 +00:00
Claude 431847a8b7 Replace custom provider abstraction with litellm
Drops the src/providers/ package (base class, AnthropicProvider,
OpenAIProvider, factory) in favour of a single src/llm.py that calls
litellm.completion() directly. litellm handles provider routing,
authentication, and SDK differences for 100+ providers without any
code we need to maintain.

Changes:
- Delete src/providers/ entirely
- Add src/llm.py — one complete() function wrapping litellm
- src/agent/core.py: EmailAgent takes model: str instead of LLMProvider
- src/config.py: ai_provider + api key fields → single ai_model string
  in litellm format (e.g. "anthropic/claude-opus-4-6")
- main.py: remove provider factory wiring; pass config.ai_model to agent
- .env.example: simplify AI section, show litellm model string examples
- pyproject.toml: replace anthropic + openai deps with litellm>=1.0.0
- uv.lock: regenerated

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-21 07:35:37 +00:00
Claude 1ba42f9497 docs: document flock + cron approach for email agent scheduling
https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-21 06:41:57 +00:00
Claude 05d4b51e7a Upgrade to Python 3.13
- .python-version: 3.11 → 3.13
- pyproject.toml: requires-python = ">=3.13"
- uv.lock: regenerated against Python 3.13.12

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-20 22:30:43 +00:00
Claude a174023ee5 Switch to uv as dependency manager
- Add pyproject.toml with project metadata and pinned dependency ranges
- Add uv.lock (generated by `uv lock`) for reproducible installs
- Add .python-version pinning Python 3.11
- Remove requirements.txt (superseded by pyproject.toml)

Install: `uv sync`
Run: `uv run python main.py`

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-20 22:29:37 +00:00
Claude 0c3b5a9033 Implement email-address-based conversation matching
Closes the gap where parents sending a new email (instead of replying)
would lose their registration progress. All changes follow the
email-based-conversation-matching OpenSpec change.

Key changes
-----------
storage/json_store.py
  - normalize_email() helper (lowercase + trim)
  - Conversations now keyed by sender email address, not thread ID
  - Versioned registration storage: data/registrations/<email>/v<N>_<ts>.json
  - current.json always reflects the latest version
  - save_registration() returns (email_key, version) tuple
  - save_registration_version() for updates with change_summary
  - get_registration_history() returns all versions in order

models/conversation.py
  - Added last_inbound_message_id field for reply threading (not matching)

channels/email_channel.py
  - fetch_unread_messages() no longer exposes thread_id
  - Conversation matching removed from channel layer (now in agent)
  - Removed _resolve_thread_id() — threading headers kept for SMTP only

agent/core.py
  - process_message() takes parent_email + inbound_message_id (no thread ID)
  - Looks up conversation by normalized email address
  - Post-completion handler: detects intent (question / update / new_child)
  - Registration updates: diffs old vs new, versions storage, notifies admin

agent/prompts.py
  - build_system_prompt() dispatches to registration or post-completion prompt
  - Post-completion prompt guides LLM to return intent field
  - Reminder language updated: no expiration threats

notifications/notifier.py
  - notify_admin() accepts version parameter
  - notify_registration_update() sends "Registration Updated" emails with diff
  - _build_update_body() includes field-level old→new change summary

main.py
  - Poll loop passes parent_email + inbound_message_id to agent (no thread_id)

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-20 22:15:49 +00:00
Claude 968475804c Update README with project purpose and capabilities
Replaces the placeholder heading with a concise description of what
Meister-Eder does, its channels, and current status.

https://claude.ai/code/session_01XinJbQWij74daNnyZ9grSw
2026-02-20 22:14:45 +00:00
gurix c37862b75e Merge branch 'main' into claude/email-agent-multi-model-c3ShZ 2026-02-20 22:30:15 +01:00
gurixandClaude Opus 4.5 b10ff7a4fe Add change spec: email-based conversation matching
Replace thread-ID-based conversation matching with email-address-based
matching for more reliable conversation continuity. Key changes:

- One conversation per email address (simpler model)
- No data expiration (conversations persist indefinitely)
- Post-completion support (questions and registration updates)
- Versioned storage for registration updates (audit trail)
- Admin notifications for registration changes

This addresses the gap where parents sending new emails (instead of
replying) would lose their registration progress.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-20 22:00:21 +01:00
Claude b82ff27efd Implement Python email agent with multi-model AI support
Adds a complete email-based registration agent for Spielgruppe Pumuckl
based on the OpenSpec define-project-scope specifications.

Architecture
- Channel-agnostic EmailAgent core — no email-specific code in business logic
- Pluggable AI provider layer: Anthropic (Claude) and OpenAI (GPT) supported
  via a shared LLMProvider interface; switch with AI_PROVIDER env var
- IMAP polling for inbound emails with thread-tracking via email headers
  (Message-ID / In-Reply-To / References)
- SMTP for outbound replies and admin notifications
- File-based JSON storage for conversation state and completed registrations
- Admin-editable knowledge-base loaded from markdown files at startup

Key files
  src/config.py                  — env-var configuration
  src/providers/base.py          — abstract LLMProvider
  src/providers/anthropic_provider.py — Claude backend
  src/providers/openai_provider.py    — OpenAI backend
  src/agent/core.py              — EmailAgent orchestrator
  src/agent/prompts.py           — system prompt builder (KB + registration state)
  src/models/registration.py     — RegistrationData matching the JSON schema
  src/models/conversation.py     — ConversationState persisted per thread
  src/channels/email_channel.py  — IMAP/SMTP I/O + quoted-text stripping
  src/storage/json_store.py      — conversation & registration persistence
  src/notifications/notifier.py  — admin notification routing by playgroup type
  main.py                        — polling entry point
  requirements.txt               — anthropic, openai, python-dotenv, jsonschema
  .env.example                   — configuration template

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-20 20:17:00 +00:00
Markus GrafandGitHub 4d44d4ee58 Merge pull request #1 from gurix/claude/claude-md-mlv4qouppkw29jdy-GvDt5
Add CLAUDE.md with comprehensive codebase documentation
2026-02-20 18:00:08 +01:00
Claude f24525e691 Add CLAUDE.md with comprehensive codebase documentation
Documents project overview, repository structure, OpenSpec workflow,
system architecture decisions, domain knowledge (fees, schedule,
registration schema, conversation flow), and conventions for AI
assistants working in this repo.

https://claude.ai/code/session_01A6JAs2Pbg1hF1aLs7bcKhx
2026-02-20 16:58:08 +00:00
gurixandClaude Opus 4.5 145947c520 Add project scope documentation for AI playgroup registration system
Define complete non-technical scope for replacing Google Forms registration
with an AI-powered conversational agent. Includes:

- Registration data requirements (13 fields with validation rules)
- Knowledge base content (fees, regulations, schedule, FAQ from official PDFs)
- Conversation flow design with bilingual support (German/English)
- Agent personality guidelines (warm & friendly tone)
- Admin notification routing by playgroup type
- Email reminder system for incomplete registrations
- Data export formats (CSV/JSON)
- Channel configuration (email + chat)
- Release priorities (MVP scope defined)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-20 17:52:13 +01:00
Markus GrafandGitHub 685bfbd03f Initial commit 2026-02-20 14:12:45 +01:00