Initial skills documentation — 25 categories, all SKILL.md + references + scripts
This commit is contained in:
+77
@@ -0,0 +1,77 @@
|
||||
# Direct IMAP triage implementation pattern
|
||||
|
||||
Use this reference when Himalaya is not available or a minimal Python stdlib path is preferable.
|
||||
|
||||
## Credential handling
|
||||
|
||||
- Store the mailbox password outside the script, e.g. `/root/.config/himalaya/<account>.pass` or a secret-manager command.
|
||||
- Set password-file mode to `0600` and verify existence/mode before login.
|
||||
- Never hardcode passwords into the script or skill.
|
||||
|
||||
## IMAP verification
|
||||
|
||||
Use `imaplib.IMAP4_SSL(host, 993, ssl_context=ssl.create_default_context())`, then:
|
||||
|
||||
1. `login(user, password)`
|
||||
2. `list()` to verify folder names
|
||||
3. `select('INBOX', readonly=True)`
|
||||
4. `uid('SEARCH', None, 'UNSEEN')` for unread-only triage, or `ALL` when requested
|
||||
|
||||
## Folder names with spaces
|
||||
|
||||
Quote mailbox names explicitly. Some IMAP servers will interpret an unquoted `Suspected Spam` as separate atoms and create a folder named `Suspected`.
|
||||
|
||||
```python
|
||||
def q(name: str) -> str:
|
||||
return '"' + name.replace('\\', '\\\\').replace('"', '\\"') + '"'
|
||||
|
||||
M._simple_command('CREATE', q('Suspected Spam'))
|
||||
M.uid('COPY', uid, q('Suspected Spam'))
|
||||
```
|
||||
|
||||
After any create operation, re-run `list()` and remove accidental test folders only after selecting them and confirming they contain zero messages.
|
||||
|
||||
## Moving without deleting permanently
|
||||
|
||||
For high-confidence spam/phishing only:
|
||||
|
||||
```python
|
||||
M.select('INBOX', readonly=False)
|
||||
M.uid('COPY', uid, q('Suspected Spam'))
|
||||
M.uid('STORE', uid, '+FLAGS', '(\\Deleted)')
|
||||
M.expunge()
|
||||
```
|
||||
|
||||
This moves by copy-then-delete-original. It is not permanent deletion because the message exists in the quarantine folder first. Log every move.
|
||||
|
||||
## State and audit log
|
||||
|
||||
Keep local state under `~/.hermes/email_triage/`:
|
||||
|
||||
- `state.json`: processed UIDs keyed by UID with decision, reason, moved_to, timestamp.
|
||||
- `actions.jsonl`: append-only audit log with timestamp, UID, sender/subject if available, decision, and reason.
|
||||
|
||||
Use UID or Message-ID to avoid reprocessing. UID is adequate for one mailbox/folder; Message-ID is safer across folders.
|
||||
|
||||
## Unlimited collection semantics
|
||||
|
||||
If the user controls the mail server or explicitly authorizes full processing, do not impose arbitrary caps such as 10 messages per run. Let `--max 0` mean unlimited:
|
||||
|
||||
```python
|
||||
selected = list(reversed(uids)) if max_messages <= 0 else list(reversed(uids))[:max_messages]
|
||||
```
|
||||
|
||||
If the resulting JSON is too large for an LLM run, the issue is model context, not IMAP server load. Use chunking or local deterministic pre-classification while preserving the user's permission to process the full mailbox.
|
||||
|
||||
## Cron integration
|
||||
|
||||
Use a small collection wrapper in `~/.hermes/scripts/` and pass it to Hermes cron by relative filename, not absolute path:
|
||||
|
||||
```bash
|
||||
# ~/.hermes/scripts/imap_triage_collect.sh
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
python3 /root/.hermes/scripts/imap_triage.py --collect
|
||||
```
|
||||
|
||||
Cron create/update expects `script="imap_triage_collect.sh"`; absolute script paths are rejected for scripts already under `~/.hermes/scripts/`.
|
||||
Reference in New Issue
Block a user