A literal "~/..." in EMCLI_DB has no shell to expand it, so SQLite opened
it relative to the cwd and silently created a stray "~" directory tree.
Expand a leading "~" or "~/" to the user's home dir; "~user", mid-path
tildes, and absolute/relative paths are left untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve final-review findings: commandRole is now the single source of
truth (Run resolves role once and threads it to handlers, replacing
hardcoded openStore roles). Tighten crypto/SKILL/SPEC/USER-MANUAL wording
and document init's agent-key-on-first-init-only semantics.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Open() now opens LOCKED; InitKeys generates a DEK sealed under both KEKs;
Unlock loads it from the role's slot (admin slot has no agent fallback).
s.key becomes the DEK, so account/mail crypto is unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the admin/diagnostics surface from SPEC §7.2:
- doctor [--account]: per-account IMAP + (RW) SMTP connectivity/auth checks via
new mail.CheckIMAP/CheckSMTP (connect+auth only, no mail). Exit non-zero on any
failure; secrets never printed.
- store.UpdateAccount: partial edit, re-encrypts password/secrets only when a
non-empty value is supplied (blank keeps existing). RecentAuditFor(account).
- config set/get (validates audit_retention_days), audit list [--account][--limit],
account edit (flag partial-update) / remove [--yes].
- internal/tui: bubbletea AccountForm with pure, fully-tested Fields (validation +
store.Account assembly + edit prefill). init / bare `account add` / `account edit
--name X` drop into the TUI; flag forms remain for scripting.
Built test-first; full suite green incl -race. Validated live against the mxlogin
(password) and Gmail (app-password) accounts. Live validation caught a real bug:
doctor authenticated with empty passwords because it iterated ListAccounts (which
strips secrets) — fixed to re-fetch via GetAccount, locked in by a regression test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SQLite PRAGMAs are connection-scoped, but database/sql uses a connection
pool. Without pinning to one connection, new pooled connections won't have
foreign_keys enabled, breaking ON DELETE CASCADE enforcement.
Also mark modernc.org/sqlite as a direct dependency in go.mod.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>