Hexagonal (ports and adapters) architecture for clean separation of concerns.
Quick Links: README | Commands | Development
cmd/nylas/ # Entry point (main.go)
internal/
domain/ # Business entities
ports/ # Interface contracts
adapters/ # Implementations
nylas/ # Nylas API client
ai/ # AI providers (Claude, OpenAI, Groq, Ollama)
analytics/ # Focus optimizer, meeting scorer
keyring/ # Secret storage
grantcache/ # Non-secret local grant metadata/default cache
config/ # Configuration validation
mcp/ # MCP proxy server
utilities/ # Timezone, scheduling, contacts services
oauth/ # OAuth callback server
browser/ # Browser automation
tunnel/ # Cloudflare tunnel
webhookserver/ # Webhook server
cli/ # CLI commands
common/ # Shared helpers (client, context, errors, flags, format, html, timeutil)
admin/ # API key management
ai/ # AI commands
auth/ # Authentication
calendar/ # Calendar & events
contacts/ # Contact management
email/ # Email operations
integration/ # CLI integration tests
mcp/ # MCP server command
notetaker/ # Meeting notetaker
otp/ # OTP extraction
scheduler/ # Booking pages
setup/ # First-time setup wizard (nylas init)
timezone/ # Timezone utilities
update/ # Self-update
webhook/ # Webhook management
tui/ # Terminal UI
app/ # Shared app logic (auth, otp)
testutil/ # Test utilities
util/ # General utilities
docs/ # Documentation
commands/ # Command guides
ai/ # AI docs
security/ # Security docs
troubleshooting/ # Troubleshooting
development/ # Dev guides
.claude/ # Claude configuration
commands/ # Skills/commands
agents/ # Agent definitions
rules/ # Project rules
shared/patterns/ # Code patterns
| Looking for | Location |
|---|---|
| CLI | |
| CLI entry & registration | cmd/nylas/main.go |
| CLI helpers (context, config) | internal/cli/common/ |
| CLI integration tests | internal/cli/integration/ |
| Adapters | |
| Nylas HTTP client | internal/adapters/nylas/client.go |
| AI providers | internal/adapters/ai/ |
| MCP server | internal/adapters/mcp/ |
| Timezone service | internal/adapters/utilities/timezone/ |
| User Interfaces | |
| TUI terminal client | internal/tui/ |
| Tests | |
| CLI integration tests | internal/cli/integration/*_test.go |
| Test utilities | internal/testutil/ |
| Docs | |
| Documentation index | docs/INDEX.md |
| Command reference | docs/COMMANDS.md |
| Claude rules | .claude/rules/ |
| Layer | Location | Purpose |
|---|---|---|
| App Services | internal/app/ |
Orchestrates adapters for workflows (auth login, OTP extraction) |
| CLI Helpers | internal/cli/common/ |
Reusable utilities (context, format, colors, pagination) |
| Adapter Helpers | internal/adapters/nylas/client_helpers.go |
HTTP helpers, request building, response handling |
Key difference: App services coordinate multiple adapters. CLI helpers are stateless utilities. Adapter helpers handle API specifics.
| File | Helpers | Purpose |
|---|---|---|
client.go |
GetNylasClient(), GetCachedNylasClient(), ResetCachedClient(), GetAPIKey(), GetGrantID() |
Nylas client creation and credential access |
colors.go |
Bold, BoldWhite, Dim, Cyan, Green, Yellow, Red, Blue |
Shared color definitions |
config.go |
GetConfigStore(), GetConfigPath() |
Config store access from commands |
context.go |
CreateContext(), CreateContextWithTimeout() |
Context creation with timeouts |
errors.go |
WrapError(), FormatError(), PrintFormattedError(), NewUserError(), NewInputError(), WrapGetError(), WrapFetchError(), WrapCreateError(), WrapUpdateError(), WrapDeleteError(), WrapSendError() |
Error wrapping and formatting |
flags.go |
AddLimitFlag(), AddFormatFlag(), AddIDFlag(), AddPageTokenFlag(), AddForceFlag(), AddYesFlag(), AddVerboseFlag() |
Common CLI flag definitions |
format.go |
ParseFormat(), NewFormatter(), NewTable(), FormatParticipant(), FormatParticipants(), FormatSize(), PrintEmptyState(), PrintEmptyStateWithHint(), PrintListHeader(), PrintSuccess(), PrintError(), PrintWarning(), PrintInfo(), Confirm() |
Display formatting and output helpers |
html.go |
StripHTML(), RemoveTagWithContent() |
HTML-to-text conversion |
logger.go |
InitLogger(), GetLogger(), IsDebug(), IsQuiet(), Debug(), Info(), Warn(), Error(), DebugHTTP(), DebugAPI() |
Logging utilities |
pagination.go |
FetchAllPages(), FetchAllWithProgress(), NewPaginatedDisplay(), PageResult[T] |
Pagination helpers |
path.go |
ValidateExecutablePath(), FindExecutableInPath(), SafeCommand() |
Safe executable path handling |
progress.go |
NewSpinner(), NewProgressBar(), NewCounter() |
Progress indicators |
retry.go |
WithRetry(), DefaultRetryConfig(), NoRetryConfig(), IsRetryable(), IsRetryableStatusCode() |
Retry logic with backoff |
string.go |
Truncate() |
String utilities |
time.go |
FormatTimeAgo(), ParseTimeOfDay(), ParseTimeOfDayInLocation(), ParseDuration() |
Time formatting and parsing |
timeutil.go |
ParseDate(), ParseTime(), FormatDate(), FormatDisplayDate() + constants |
Date parsing and formatting |
| Helper | Purpose |
|---|---|
doGet(ctx, url, &result) |
GET request with JSON decoding |
doGetWithNotFound(ctx, url, &result, notFoundErr) |
GET with 404 handling |
doDelete(ctx, url) |
DELETE request (accepts 200/204) |
ListResponse[T] |
Generic paginated response type |
QueryBuilder |
Fluent URL query parameter builder |
QueryBuilder methods:
NewQueryBuilder()- Create new builderAdd(key, value)- Add string value (if non-empty)AddInt(key, value)- Add int value (if > 0)AddInt64(key, value)- Add int64 value (if > 0)AddBool(key, value)- Add bool value (if true)AddBoolPtr(key, value)- Add bool pointer (if non-nil)AddSlice(key, values)- Add multiple values with same keyEncode()- Get encoded query stringValues()- Get underlying url.ValuesBuildURL(baseURL)- Append query to URL
QueryBuilder usage:
qb := NewQueryBuilder().
Add("limit", "50").
AddInt("offset", params.Offset).
AddBoolPtr("unread", params.Unread)
url := qb.BuildURL(baseURL)Three layers:
-
Domain (
internal/domain/)- Pure business logic, no external dependencies
- Core types: Message, Email, Calendar, Event, Contact, Grant, Webhook
- Feature types: AI, Analytics, Admin, Scheduler, Notetaker, Agent
- Support types: Config, Errors, Provider, Utilities
- Shared interfaces:
interfaces.go(Paginated, QueryParams, Resource, Timestamped, Validator)
Key type relationships:
Person- Base type with Name/Email (incalendar.go)EmailParticipant- Type alias forPerson(inemail.go)Participant- EmbedsPerson, adds Status/Comment for calendar events
-
Ports (
internal/ports/)One file per interface; the main ones:
nylas.go- NylasClient interface (main API operations)secrets.go- SecretStore interface (credential storage)llm.go- LLM interface (AI providers)config.go- Config interfacewebhook_server.go- Webhook server interface
-
Adapters (
internal/adapters/)Adapter Purpose nylas/Nylas API client (messages, calendars, contacts, events) ai/AI clients (Claude, OpenAI, Groq, Ollama), email analyzer analytics/Focus optimizer, conflict resolver, meeting scorer keyring/Secret storage (system keyring, encrypted file fallback) grantcache/Non-secret local grant metadata/default cache mcp/MCP proxy server for AI assistants config/Configuration validation oauth/OAuth callback server utilities/Services (contacts, email, scheduling, timezone, webhook) browser/Browser automation tunnel/Cloudflare tunnel webhookserver/Webhook server
Benefits:
- Testability (mock adapters)
- Flexibility (swap implementations)
- Clean separation of concerns
Calendar enforces working hours (soft warnings) and break blocks (hard constraints).
Domain models:
WorkingHoursConfig- Per-day working hours with break periodsDaySchedule- Working hours for specific weekdayBreakBlock- Break periods (lunch, coffee) with hard constraints
Configuration: ~/.nylas/config.yaml
Implementation: internal/cli/calendar/helpers.go (checkBreakViolation())
Tests: internal/cli/calendar/helpers_test.go
Details: See commands/timezone.md
Shared interfaces in internal/domain/interfaces.go enable generic programming:
| Interface | Purpose | Implemented By |
|---|---|---|
Paginated |
Resources with pagination info | MessageListResponse, EventListResponse, etc. |
QueryParams |
Query parameter types | MessageQueryParams, EventQueryParams, etc. |
Resource |
Resources with ID and GrantID | Message, Event, Contact, etc. |
Timestamped |
Resources with timestamps | Message, Event, Draft, etc. |
Validator |
Self-validating types | EventWhen, SendMessageRequest, BreakBlock |
Type embedding example:
// Person is the base type for email/calendar participants
type Person struct {
Name string `json:"name,omitempty"`
Email string `json:"email"`
}
// Participant embeds Person and adds calendar-specific fields
type Participant struct {
Person
Status string `json:"status,omitempty"`
Comment string `json:"comment,omitempty"`
}
// EmailParticipant is an alias for Person (backward compatibility)
type EmailParticipant = PersonEach feature follows consistent structure:
internal/cli/<feature>/
├── <feature>.go # Main command
├── list.go # List subcommand
├── create.go # Create subcommand
├── update.go # Update subcommand
├── delete.go # Delete subcommand
└── helpers.go # Shared helpers
The CLI provides a terminal-based interface:
| Interface | Command | Port | Purpose | Location |
|---|---|---|---|---|
| TUI | nylas tui |
N/A | Terminal-based email/calendar client | internal/tui/ |
For detailed implementation, see CLAUDE.md and docs/DEVELOPMENT.md