Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
name: claude
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]
# Manual fallback for ad-hoc runs from the Actions tab (default branch only).
workflow_dispatch:
inputs:
prompt:
description: Instruction for Claude (free text).
required: true
default: Summarise the repository structure and call out anything unusual.

# Least-privilege. id-token: write is required by the action even with
# OAuth-token auth — it calls the GitHub OIDC endpoint to mint a
# short-lived GitHub token regardless of how Anthropic auth is set up.
permissions:
contents: write
pull-requests: write
issues: write
id-token: write

# Serialise per-actor-per-issue. The actor key matters: when Claude
# posts its "I'm working…" ack comment, that comment fires another
# issue_comment event from `claude[bot]`. Without partitioning by actor,
# `cancel-in-progress` would cancel Claude's own in-flight reply run.
concurrency:
group: claude-${{ github.actor }}-${{ github.event.issue.number || github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

jobs:
claude:
# Only run when @claude is explicitly mentioned, to avoid spend on unrelated activity.
if: |
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
Comment on lines +39 to +45
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Run Claude Code
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# Empty for comment-triggered runs (auto-detect from @claude mention).
# Populated for workflow_dispatch runs from the operator's input.
prompt: ${{ github.event.inputs.prompt || '' }}
# Sign commits via the GitHub API so Claude's pushes are "Verified".
use_commit_signing: true
# Cap turns + pin model. Bump model when a newer GA Sonnet ships.
claude_args: |
--max-turns 10
--model claude-sonnet-4-6
80 changes: 80 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# CLAUDE.md

Guidance for Claude (Code Action, managed Code Review, local Claude Code
sessions) when working in this repository.

## What this repo is

An open-source **MCP (Model Context Protocol) server** providing
comprehensive email capabilities over IMAP and SMTP. It exposes tools,
prompts, and resources to AI assistants for reading, sending,
scheduling, organising, and analysing email across multiple accounts.

- **Language / runtime**: TypeScript (ESM), Node.js ≥ 24.
- **Package manager**: pnpm 9 (do not introduce npm or yarn).
- **Transport modes**: stdio (default), Streamable HTTP.
Comment on lines +12 to +15
- **License**: LGPL-3.0-or-later.
- **Public repo**: be mindful that issue and PR comments are world-readable.

See `README.md` for the feature list and `docs/` for deeper guides.

## Stack and tooling

| Concern | Tool |
|---|---|
| Format / import organisation | Biome (`pnpm format`, `pnpm format:check`) |
| Lint | ESLint (Airbnb Extended + TS strict) (`pnpm lint`) |
| Combined static checks | `pnpm check` (Biome + ESLint) |
| Type-check | `pnpm typecheck` |
| Unit tests | Vitest (`pnpm test`) |
| Integration tests | Vitest with `vitest.config.integration.ts` (`pnpm test:integration`) — uses testcontainers |
| Pre-commit hooks | lefthook |
| Versioning / changelog | cocogitto (`cog`) |
| Release | goreleaser |
Comment on lines +29 to +33

Always run `pnpm check && pnpm typecheck && pnpm test` before declaring
work done. For changes touching IMAP/SMTP behaviour, run
`pnpm test:integration` as well.

## Conventions to follow

- **Commits**: Conventional Commits (`feat:`, `fix:`, `docs:`, `refactor:`,
`test:`, `chore:`, `ci:`). cocogitto enforces this. Use `pnpm commit`
if unsure.
- **Branches**: topic branches off `develop`. PRs target `develop`.
`main` is the release line, fed by `develop` → `main` merges.
- **Files layout**: business logic in `src/services/`, MCP wiring in
`src/tools/`, `src/prompts/`, `src/resources/`. Keep them decoupled —
services must be unit-testable without mocking MCP transports.
- **Workflows**: lowercase kebab-case `name:`, explicit `permissions:`
block per workflow, prefer the shared workflows under
`codefuturist/shared-workflows` over re-implementing common steps.

## What to do / not do

- **Prefer editing existing files** over creating new ones.
- **Do not** hardcode credentials, API keys, OAuth client secrets, or
example email addresses with real domains in source or tests.
- **Do not** log passwords, OAuth tokens, or full message bodies at
`info` or above — they may end up in user-shared logs.
- **Do not** bump `engines.node` below 24 (existing baseline).
- **Do not** add a new transport without updating both `README.md` and
the MCP capability negotiation.
Comment on lines +58 to +62
- **TypeScript**: keep `strict` on, no `any` without an explicit
comment justifying it.
- **Async correctness**: IMAP IDLE, watcher, scheduler, and rate-limiter
code is concurrency-sensitive. Don't fire-and-forget promises; await
or attach `.catch(...)`.
- **Public API**: tool names, parameter schemas, and resource URIs are
part of the MCP API surface. Renames or schema changes are breaking
changes and need a major version bump (see `cog.toml`).

## Validation before you call it done

1. `pnpm check` — Biome + ESLint clean.
2. `pnpm typecheck` — no type errors.
3. `pnpm test` — unit tests green.
4. `pnpm test:integration` — only if touching IMAP/SMTP, watcher,
scheduler, or transport code.
5. For Docker-affecting changes: `pnpm docker:build` succeeds.
6. For workflow changes: `actionlint` clean (`pnpm report` includes it).
Comment on lines +72 to +80
79 changes: 79 additions & 0 deletions REVIEW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Review instructions

Used by Anthropic's managed Code Review service
(<https://claude.ai/admin-settings/claude-code>). Inert until that
service is enabled for this repo. Read alongside `CLAUDE.md`, which is
shared context for all Claude tooling.

## What Important (🔴) means here

Reserve 🔴 for findings that would, if merged:

- **Leak credentials**: hardcoded passwords, OAuth client secrets, API
keys, or app-specific passwords committed to source/tests/fixtures;
log statements at `info` or above that include passwords, OAuth
tokens, refresh tokens, or full message bodies.
- **Break MCP protocol compliance**: tool/resource/prompt schemas that
violate the MCP SDK contracts, malformed JSON-RPC responses, missing
required capability declarations.
- **Cause data loss or corruption**: destructive IMAP operations
(`EXPUNGE`, `STORE \Deleted`, move-to-Trash) without explicit
confirmation gating; SMTP send paths that bypass the audit trail or
rate limiter.
- **Introduce a silent concurrency bug**: unawaited promises in the
IDLE watcher / scheduler / rate-limiter; shared mutable state across
account connections without synchronisation; race conditions on
reconnect logic.
- **Break the public API**: tool rename, parameter schema change, or
resource URI change without a major-version bump per `cog.toml`.
- **Open a TLS hole**: any code path that disables certificate
verification, allows downgrade from STARTTLS, or accepts plaintext
auth on non-loopback connections.

Style, naming, refactoring, and "could be DRYer" are 🟡 Nit at most.

## Cap the nits

Report at most **five** 🟡 Nits per review. If you found more, say
"plus N similar items" in the summary. Lead the summary with
"No blocking issues" when nothing 🔴 was found.

## Skip these

- Anything CI already enforces: Biome formatting, ESLint, TypeScript
errors, Vitest failures, actionlint, the shared `ci-node.yml`
workflow checks.
Comment on lines +44 to +45
- Generated artefacts: `dist/`, `node_modules/`, `coverage/`, `reports/`.
- Lockfiles: `pnpm-lock.yaml`, `package-lock.json`.
- Dependabot/Renovate-only PRs: comment only on logic regressions, not
changelogs or version bumps.
- `CHANGELOG.md` — generated by cocogitto.

## Always check

- New tool / prompt / resource registrations have matching unit tests
under `src/**/__tests__/` or `tests/`.
- New IMAP-touching code paths have an integration test added under
`tests/integration/`.
- New environment variables and config keys are documented in
`README.md` (Install / Usage) and reflected in any `.env.example` or
config template.
- Error responses do not embed raw credentials, OAuth tokens, or
internal stack traces.
- Public exports in `src/index.ts` (or equivalent entry) have not
changed shape without a corresponding `cog.toml` major bump entry.

## Verification bar

Behaviour claims need a `file:line` citation in the diff or surrounding
code. Do not flag based on naming alone — open the file, confirm.

## Re-review convergence

After the first review on a PR, suppress new 🟡 Nits and post only 🔴
Important findings. The author already saw the style notes once.

## Summary shape

Open the review body with a one-line tally:
`N important, M nits` (or `No blocking issues` when N=0).
Loading