fix(skills): widen HERMES_HOME-aware .env resolution to all sibling skills

Follow-up to the GitHub-skills fix: the same hardcoded ~/.hermes/.env
pattern existed across other bundled and optional skills. Under the
official Docker setup (HERMES_HOME=/opt/data, subprocess HOME=/opt/data/home)
those paths point at a nonexistent file.

- kanban-video-orchestrator setup.sh.tmpl + docs: resolve via
  ${HERMES_HOME:-$HOME/.hermes}/.env in check_key()
- telephony.py / canvas_api.py / hyperliquid_client.py: error and
  save messages now report the real resolved env path instead of a
  hardcoded literal (path resolution itself was already correct)
- godmode SKILL.md: load_dotenv snippet resolves via HERMES_HOME
- watch_github.py + ~20 SKILL.md prose mentions: document the env file
  as ${HERMES_HOME:-~/.hermes}/.env so Docker users edit the right file
This commit is contained in:
teknium1 2026-06-10 14:46:21 -07:00 committed by Teknium
parent 0a593f132c
commit d1383a6b14
25 changed files with 59 additions and 52 deletions

View File

@ -36,7 +36,7 @@ Read-only — no API key, no signing, no order placement.
Stdlib only — no external packages, no API key.
The script reads `~/.hermes/.env` for two optional defaults:
The script reads `${HERMES_HOME:-~/.hermes}/.env` for two optional defaults:
- `HYPERLIQUID_API_URL` — defaults to `https://api.hyperliquid.xyz`. Set to
`https://api.hyperliquid-testnet.xyz` for testnet.
@ -80,7 +80,7 @@ hyperliquid_client.py export <coin> [--interval 1h] [--hours N] [--output PATH]
```
For `state`, `spot-balances`, `fills`, `orders`, and `review`, the address is
optional when `HYPERLIQUID_USER_ADDRESS` is set in `~/.hermes/.env`.
optional when `HYPERLIQUID_USER_ADDRESS` is set in `${HERMES_HOME:-~/.hermes}/.env`.
---

View File

@ -115,7 +115,7 @@ def _resolve_user(user: Optional[str]) -> str:
sys.exit(
"Missing Hyperliquid address. Pass <address> explicitly or set "
f"{DEFAULT_USER_ENV} in your environment or ~/.hermes/.env."
f"{DEFAULT_USER_ENV} in your environment or {_hermes_home() / '.env'}."
)

View File

@ -182,7 +182,7 @@ task graphs. See **[references/examples.md](references/examples.md)**.
right human-review gates.
8. **Verify API keys BEFORE firing.** External APIs (TTS, image-gen,
image-to-video) need keys in `~/.hermes/.env` or the user's secret store.
image-to-video) need keys in `${HERMES_HOME:-~/.hermes}/.env` or the user's secret store.
A worker that hits a missing-key error wastes a task slot. The setup
script's `check_key` helper aborts cleanly if a required key is missing.

View File

@ -23,8 +23,9 @@ check_key() {
local var="$1"
local kc_account="${2:-hermes}"
local kc_service="${3:-$1}"
if grep -q "^${var}=" "$HOME/.hermes/.env" 2>/dev/null && \
[ -n "$(grep "^${var}=" "$HOME/.hermes/.env" | cut -d= -f2-)" ]; then
local _hermes_env="${HERMES_HOME:-$HOME/.hermes}/.env"
if grep -q "^${var}=" "$_hermes_env" 2>/dev/null && \
[ -n "$(grep "^${var}=" "$_hermes_env" | cut -d= -f2-)" ]; then
echo " ✓ ${var} (env)"
return 0
fi
@ -33,7 +34,7 @@ check_key() {
echo " ✓ ${var} (Keychain ${kc_account}/${kc_service})"
return 0
fi
echo " ✗ ${var} not set in ~/.hermes/.env or Keychain (${kc_account}/${kc_service})"
echo " ✗ ${var} not set in ${_hermes_env} or Keychain (${kc_account}/${kc_service})"
return 1
}

View File

@ -218,22 +218,24 @@ The director turns this into actual `kanban_create` calls.
## API-key prerequisites check
Before firing the kanban, verify required keys are available. Check both
`~/.hermes/.env` and macOS Keychain (if on macOS):
the Hermes `.env` (`${HERMES_HOME:-$HOME/.hermes}/.env`) and macOS Keychain
(if on macOS):
```bash
check_key() {
local var="$1"
local kc_account="$2"
local kc_service="$3"
if grep -q "^${var}=" ~/.hermes/.env 2>/dev/null && \
[ -n "$(grep "^${var}=" ~/.hermes/.env | cut -d= -f2-)" ]; then
local _hermes_env="${HERMES_HOME:-$HOME/.hermes}/.env"
if grep -q "^${var}=" "$_hermes_env" 2>/dev/null && \
[ -n "$(grep "^${var}=" "$_hermes_env" | cut -d= -f2-)" ]; then
return 0
fi
if command -v security >/dev/null 2>&1 && \
security find-generic-password -a "${kc_account}" -s "${kc_service}" -w >/dev/null 2>&1; then
return 0
fi
echo "ERROR: ${var} not set in ~/.hermes/.env or Keychain (${kc_account}/${kc_service})"
echo "ERROR: ${var} not set in ${_hermes_env} or Keychain (${kc_account}/${kc_service})"
return 1
}

View File

@ -284,7 +284,7 @@ skills:
## API key requirements
Track these in the project setup. The setup script should verify each required
key is present in `~/.hermes/.env` (or macOS Keychain) before firing the kanban.
key is present in `${HERMES_HOME:-~/.hermes}/.env` (or macOS Keychain) before firing the kanban.
| Service | Env var | Used by |
|---------|---------|---------|
@ -301,7 +301,7 @@ key is present in `~/.hermes/.env` (or macOS Keychain) before firing the kanban.
| Anthropic | `ANTHROPIC_API_KEY` | every Hermes profile (Claude) |
If a key is missing, prompt the user to add it. Storage methods, in order of
preference: macOS Keychain → `~/.hermes/.env` → environment variable.
preference: macOS Keychain → `${HERMES_HOME:-~/.hermes}/.env` → environment variable.
## Skill version pinning

View File

@ -62,7 +62,7 @@ python $HERMES_HOME/skills/devops/watchers/scripts/watch_rss.py \
--name hn --url https://news.ycombinator.com/rss --max 5
```
Watch a GitHub repo (set `GITHUB_TOKEN` in `~/.hermes/.env` to avoid the 60 req/hr anonymous rate limit):
Watch a GitHub repo (set `GITHUB_TOKEN` in `${HERMES_HOME:-~/.hermes}/.env` to avoid the 60 req/hr anonymous rate limit):
```bash
python $HERMES_HOME/skills/devops/watchers/scripts/watch_github.py \

View File

@ -8,7 +8,8 @@ Usage (via cron with --no-agent):
--script "$HERMES_HOME/skills/devops/watchers/scripts/watch_github.py" \\
--script-args "--name hermes-issues --repo NousResearch/hermes-agent --scope issues"
Set GITHUB_TOKEN (or GH_TOKEN) in ~/.hermes/.env to avoid the 60 req/hr
Set GITHUB_TOKEN (or GH_TOKEN) in the Hermes .env file
(``${HERMES_HOME:-~/.hermes}/.env``) to avoid the 60 req/hr
anonymous rate limit.
Scopes: issues | pulls | releases | commits. Or pass --search QUERY to

View File

@ -26,7 +26,7 @@ Read-only access to Canvas LMS for listing courses and assignments.
2. Go to **Account → Settings** (click your profile icon, then Settings)
3. Scroll to **Approved Integrations** and click **+ New Access Token**
4. Name the token (e.g., "Hermes Agent"), set an optional expiry, and click **Generate Token**
5. Copy the token and add to `~/.hermes/.env`:
5. Copy the token and add to `${HERMES_HOME:-~/.hermes}/.env`:
```
CANVAS_API_TOKEN=your_token_here

View File

@ -28,9 +28,12 @@ def _check_config():
if not CANVAS_BASE_URL:
missing.append("CANVAS_BASE_URL")
if missing:
hermes_env = os.path.join(
os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes")), ".env"
)
print(
f"Missing required environment variables: {', '.join(missing)}\n"
"Set them in ~/.hermes/.env or export them in your shell.\n"
f"Set them in {hermes_env} or export them in your shell.\n"
"See the canvas skill SKILL.md for setup instructions.",
file=sys.stderr,
)

View File

@ -36,7 +36,7 @@ The REST Admin API is legacy since 2024-04 and only receives security fixes. **U
1. In Shopify admin: **Settings → Apps and sales channels → Develop apps → Create an app**.
2. Click **Configure Admin API scopes**, select what you need (examples below), save.
3. **Install app** → the Admin API access token appears ONCE. Copy it immediately — Shopify will never show it again. Tokens start with `shpat_`.
4. Save to `~/.hermes/.env`:
4. Save to `${HERMES_HOME:-~/.hermes}/.env`:
```
SHOPIFY_ACCESS_TOKEN=shpat_xxxxxxxxxxxxxxxxxxxx
SHOPIFY_STORE_DOMAIN=my-store.myshopify.com

View File

@ -30,7 +30,7 @@ Use the [SiYuan](https://github.com/siyuan-note/siyuan) kernel API via curl to s
1. Install and run SiYuan (desktop or Docker)
2. Get your API token: **Settings > About > API token**
3. Store it in `~/.hermes/.env`:
3. Store it in `${HERMES_HOME:-~/.hermes}/.env`:
```
SIYUAN_TOKEN=your_token_here
SIYUAN_URL=http://127.0.0.1:6806

View File

@ -17,7 +17,7 @@ metadata:
This optional skill gives Hermes practical phone capabilities while keeping telephony out of the core tool list.
It ships with a helper script, `scripts/telephony.py`, that can:
- save provider credentials into `~/.hermes/.env`
- save provider credentials into `${HERMES_HOME:-~/.hermes}/.env`
- search for and buy a Twilio phone number
- remember that owned number for later sessions
- send SMS / MMS from the owned number
@ -104,7 +104,7 @@ Why:
The skill persists telephony state in two places:
### `~/.hermes/.env`
### `${HERMES_HOME:-~/.hermes}/.env`
Used for long-lived provider credentials and owned-number IDs, for example:
- `TWILIO_ACCOUNT_SID`
- `TWILIO_AUTH_TOKEN`
@ -241,7 +241,7 @@ python3 "$SCRIPT" save-twilio AC... auth_token_here
python3 "$SCRIPT" twilio-search --country US --area-code 702 --limit 10
```
3. Buy it and save it into `~/.hermes/.env` + state:
3. Buy it and save it into `${HERMES_HOME:-~/.hermes}/.env` + state:
```bash
python3 "$SCRIPT" twilio-buy "+17025551234" --save-env
```
@ -403,7 +403,7 @@ After setup, you should be able to do all of the following with just this skill:
1. `diagnose` shows provider readiness and remembered state
2. search and buy a Twilio number
3. persist that number to `~/.hermes/.env`
3. persist that number to `${HERMES_HOME:-~/.hermes}/.env`
4. send an SMS from the owned number
5. poll inbound texts for the owned number later
6. place a direct Twilio call

View File

@ -2,7 +2,7 @@
"""Telephony helper for the Hermes optional telephony skill.
Capabilities:
- Persist telephony provider credentials to ~/.hermes/.env
- Persist telephony provider credentials to the Hermes .env file ($HERMES_HOME/.env)
- Search for, buy, and remember Twilio phone numbers
- Make direct Twilio calls (TwiML <Say> or <Play>)
- Send SMS / MMS via Twilio
@ -286,7 +286,7 @@ def _twilio_creds() -> tuple[str, str]:
if not sid or not token:
raise TelephonyError(
"Twilio credentials are not configured. Use 'save-twilio' or set "
"TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN in ~/.hermes/.env."
f"TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN in {_env_path()}."
)
return sid, token
@ -420,7 +420,7 @@ def _resolve_twilio_number(identifier: str | None = None) -> OwnedTwilioNumber:
raise TelephonyError(
"No default Twilio phone number is set. Use 'twilio-buy --save-env', "
"'twilio-set-default', or set TWILIO_PHONE_NUMBER in ~/.hermes/.env."
f"'twilio-set-default', or set TWILIO_PHONE_NUMBER in {_env_path()}."
)
@ -756,7 +756,7 @@ def _vapi_import_twilio_number(
api_key = _vapi_api_key()
if not api_key:
raise TelephonyError(
"Vapi is not configured. Use 'save-vapi' or set VAPI_API_KEY in ~/.hermes/.env first."
f"Vapi is not configured. Use 'save-vapi' or set VAPI_API_KEY in {_env_path()} first."
)
owned = _resolve_twilio_number(phone_identifier)
sid, token = _twilio_creds()
@ -803,7 +803,7 @@ def _bland_call(
api_key = _bland_api_key()
if not api_key:
raise TelephonyError(
"Bland.ai is not configured. Use 'save-bland' or set BLAND_API_KEY in ~/.hermes/.env."
f"Bland.ai is not configured. Use 'save-bland' or set BLAND_API_KEY in {_env_path()}."
)
normalized = _normalize_phone(phone_number)
if voice is None:
@ -881,13 +881,13 @@ def _vapi_call(
api_key = _vapi_api_key()
if not api_key:
raise TelephonyError(
"Vapi is not configured. Use 'save-vapi' or set VAPI_API_KEY in ~/.hermes/.env."
f"Vapi is not configured. Use 'save-vapi' or set VAPI_API_KEY in {_env_path()}."
)
phone_number_id = _vapi_phone_number_id()
if not phone_number_id:
raise TelephonyError(
"No Vapi phone number id is configured. Import an owned Twilio number with "
"'vapi-import-twilio --save-env' or set VAPI_PHONE_NUMBER_ID in ~/.hermes/.env."
f"'vapi-import-twilio --save-env' or set VAPI_PHONE_NUMBER_ID in {_env_path()}."
)
normalized = _normalize_phone(phone_number)
voice_provider = _env_or_config(
@ -1091,7 +1091,7 @@ def save_twilio(account_sid: str, auth_token: str, phone_number: str = "", phone
"provider": "twilio",
"saved_env_keys": sorted(updates),
"env_path": str(env_file),
"message": "Twilio credentials saved to ~/.hermes/.env.",
"message": f"Twilio credentials saved to {env_file}.",
}
if phone_number:
result.update(_remember_twilio_number(phone_number=updates["TWILIO_PHONE_NUMBER"], phone_sid=phone_sid.strip(), save_env=False))
@ -1111,7 +1111,7 @@ def save_bland(api_key: str, voice: str = BLAND_DEFAULT_VOICE) -> dict[str, Any]
"provider": "bland",
"saved_env_keys": ["BLAND_API_KEY", "BLAND_DEFAULT_VOICE", "PHONE_PROVIDER"],
"env_path": str(env_file),
"message": "Bland.ai configuration saved to ~/.hermes/.env.",
"message": f"Bland.ai configuration saved to {env_file}.",
}
@ -1138,7 +1138,7 @@ def save_vapi(
"provider": "vapi",
"saved_env_keys": sorted(updates),
"env_path": str(env_file),
"message": "Vapi configuration saved to ~/.hermes/.env.",
"message": f"Vapi configuration saved to {env_file}.",
}
if phone_number_id:
result.update(_remember_vapi_number(phone_number_id=phone_number_id.strip(), save_env=False))
@ -1151,17 +1151,17 @@ def _build_parser() -> argparse.ArgumentParser:
sub.add_parser("diagnose", help="Show saved telephony state and provider readiness")
p = sub.add_parser("save-twilio", help="Save Twilio credentials to ~/.hermes/.env")
p = sub.add_parser("save-twilio", help="Save Twilio credentials to the Hermes .env file")
p.add_argument("account_sid")
p.add_argument("auth_token")
p.add_argument("--phone-number", default="")
p.add_argument("--phone-sid", default="")
p = sub.add_parser("save-bland", help="Save Bland.ai settings to ~/.hermes/.env")
p = sub.add_parser("save-bland", help="Save Bland.ai settings to the Hermes .env file")
p.add_argument("api_key")
p.add_argument("--voice", default=BLAND_DEFAULT_VOICE)
p = sub.add_parser("save-vapi", help="Save Vapi settings to ~/.hermes/.env")
p = sub.add_parser("save-vapi", help="Save Vapi settings to the Hermes .env file")
p.add_argument("api_key")
p.add_argument("--phone-number-id", default="")
p.add_argument("--voice-provider", default=VAPI_DEFAULT_VOICE_PROVIDER)
@ -1312,7 +1312,7 @@ def _dispatch(args: argparse.Namespace) -> dict[str, Any]:
)
raise TelephonyError(
f"Unsupported AI call provider '{provider}'. Use --provider bland or --provider vapi, "
"or set PHONE_PROVIDER in ~/.hermes/.env."
f"or set PHONE_PROVIDER in {_env_path()}."
)
if cmd == "ai-status":
provider = (args.provider or _ai_provider()).lower().strip()
@ -1322,7 +1322,7 @@ def _dispatch(args: argparse.Namespace) -> dict[str, Any]:
return _bland_status(args.call_id, analyze=args.analyze or None)
raise TelephonyError(
f"Unsupported AI call provider '{provider}'. Use --provider bland or --provider vapi, "
"or set PHONE_PROVIDER in ~/.hermes/.env."
f"or set PHONE_PROVIDER in {_env_path()}."
)
raise TelephonyError(f"Unknown command: {cmd}")

View File

@ -41,7 +41,7 @@ Use this skill when the user wants secrets managed through 1Password instead of
### Service Account (recommended for Hermes)
Set `OP_SERVICE_ACCOUNT_TOKEN` in `~/.hermes/.env` (the skill will prompt for this on first load).
Set `OP_SERVICE_ACCOUNT_TOKEN` in `${HERMES_HOME:-~/.hermes}/.env` (the skill will prompt for this on first load).
No desktop app needed. Supports `op read`, `op inject`, `op run`.
```bash

View File

@ -400,4 +400,4 @@ Claude Sonnet 4 is robust against all current techniques for clearly harmful con
9. **Always use `load_godmode.py` in execute_code** — The individual scripts (`parseltongue.py`, `godmode_race.py`, `auto_jailbreak.py`) have argparse CLI entry points with `if __name__ == '__main__'` blocks. When loaded via `exec()` in execute_code, `__name__` is `'__main__'` and argparse fires, crashing the script. The `load_godmode.py` loader handles this by setting `__name__` to a non-main value and managing sys.argv.
10. **boundary_inversion is model-version specific** — Works on Claude 3.5 Sonnet but NOT Claude Sonnet 4 or Claude 4.6. The strategy order in auto_jailbreak tries it first for Claude models, but falls through to refusal_inversion when it fails. Update the strategy order if you know the model version.
11. **Gray-area vs hard queries** — Jailbreak techniques work much better on "dual-use" queries (lock picking, security tools, chemistry) than on overtly harmful ones (phishing templates, malware). For hard queries, skip directly to ULTRAPLINIAN or use Hermes/Grok models that don't refuse.
12. **execute_code sandbox has no env vars** — When Hermes runs auto_jailbreak via execute_code, the sandbox doesn't inherit `~/.hermes/.env`. Load dotenv explicitly: `from dotenv import load_dotenv; load_dotenv(os.path.expanduser("~/.hermes/.env"))`
12. **execute_code sandbox has no env vars** — When Hermes runs auto_jailbreak via execute_code, the sandbox doesn't inherit the Hermes `.env`. Load dotenv explicitly: `import os; from dotenv import load_dotenv; load_dotenv(os.path.join(os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes")), ".env"))`

View File

@ -397,7 +397,7 @@ class TestAPISmoke:
### Token handling
- Never log full tokens. Redact: `Bearer <REDACTED>`.
- Never hardcode tokens in scripts. Read from env (`os.environ["API_TOKEN"]`) or `~/.hermes/.env`.
- Never hardcode tokens in scripts. Read from env (`os.environ["API_TOKEN"]`) or `${HERMES_HOME:-~/.hermes}/.env`.
- Rotate immediately if a token surfaces in logs, error messages, or git history.
### Safe logging

View File

@ -343,7 +343,7 @@ The registry of record is `hermes_cli/commands.py` — every consumer
```
~/.hermes/config.yaml Main configuration
~/.hermes/.env API keys and secrets
~/.hermes/.env API keys and secrets (under $HERMES_HOME if set)
$HERMES_HOME/skills/ Installed skills
~/.hermes/sessions/ Gateway routing index, request dumps, *.jsonl transcripts (and optional per-session JSON snapshots when sessions.write_json_snapshots: true)
~/.hermes/state.db Canonical session store (SQLite + FTS5)
@ -908,7 +908,7 @@ hermes-agent/
└── website/ # Docusaurus docs site
```
Config: `~/.hermes/config.yaml` (settings), `~/.hermes/.env` (API keys).
Config: `~/.hermes/config.yaml` (settings), `~/.hermes/.env` (API keys) — both under `$HERMES_HOME` when it is set.
### Adding a Tool (3 files)

View File

@ -30,7 +30,7 @@ platforms:
```
### Option 3: Environment variables
Add to `~/.hermes/.env`:
Add to `${HERMES_HOME:-~/.hermes}/.env`:
```bash
WEBHOOK_ENABLED=true
WEBHOOK_PORT=8644

View File

@ -23,7 +23,7 @@ Useful for finding reaction GIFs, creating visual content, and sending GIFs in c
## Setup
Set your Tenor API key in your environment (add to `~/.hermes/.env`):
Set your Tenor API key in your environment (add to `${HERMES_HOME:-~/.hermes}/.env`):
```bash
TENOR_API_KEY=your_key_here

View File

@ -12,7 +12,7 @@ Use this skill for filesystem-first Obsidian vault work: reading notes, listing
Use a known or resolved vault path before calling file tools.
The documented vault-path convention is the `OBSIDIAN_VAULT_PATH` environment variable, for example from `~/.hermes/.env`. If it is unset, use `~/Documents/Obsidian Vault`.
The documented vault-path convention is the `OBSIDIAN_VAULT_PATH` environment variable, for example from `${HERMES_HOME:-~/.hermes}/.env`. If it is unset, use `~/Documents/Obsidian Vault`.
File tools do not expand shell variables. Do not pass paths containing `$OBSIDIAN_VAULT_PATH` to `read_file`, `write_file`, `patch`, or `search_files`; resolve the vault path first and pass a concrete absolute path. Vault paths may contain spaces, which is another reason to prefer file tools over shell commands.

View File

@ -26,7 +26,7 @@ Work with Airtable's REST API directly via `curl` using the `terminal` tool. No
- `data.records:write` — create / update / delete rows
- `schema.bases:read` — list bases and tables
3. **Important:** in the same token UI, add each base you want to access to the token's **Access** list. PATs are scoped per-base — a valid token on the wrong base returns `403`.
4. Store the token in `~/.hermes/.env` (or via `hermes setup`):
4. Store the token in `${HERMES_HOME:-~/.hermes}/.env` (or via `hermes setup`):
```
AIRTABLE_API_KEY=pat_your_token_here
```
@ -222,7 +222,7 @@ done
## Important Notes for Hermes
- **Always use the `terminal` tool with `curl`.** Do NOT use `web_extract` (it can't send auth headers) or `browser_navigate` (needs UI auth and is slow).
- **`AIRTABLE_API_KEY` flows from `~/.hermes/.env` into the subprocess automatically** when this skill is loaded — no need to re-export it before each `curl` call.
- **`AIRTABLE_API_KEY` flows from `${HERMES_HOME:-~/.hermes}/.env` into the subprocess automatically** when this skill is loaded — no need to re-export it before each `curl` call.
- **Escape curly braces in formulas carefully.** In a heredoc body, `{Status}` is literal. In a shell argument, `{Status}` is safe outside `{...}` brace-expansion context — but pass dynamic strings through `python3 urllib.parse.quote` before splicing into a URL.
- **Pretty-print with `python3 -m json.tool`** (always present) rather than `jq` (optional). Only reach for `jq` when you need filtering/projection.
- **Pagination is per-page, not global.** Airtable's 100-record cap is a hard limit; there is no way to bump it. Loop with `offset` until the field is absent.

View File

@ -26,7 +26,7 @@ Talk to Notion two ways. Same integration token works for both — pick by what'
1. Create an integration at https://notion.so/my-integrations
2. Copy the API key (starts with `ntn_` or `secret_`)
3. Store in `~/.hermes/.env`:
3. Store in `${HERMES_HOME:-~/.hermes}/.env`:
```
NOTION_API_KEY=ntn_your_key_here
```
@ -50,7 +50,7 @@ export NOTION_API_TOKEN=$NOTION_API_KEY # ntn reads NOTION_API_TOKEN
export NOTION_KEYRING=0 # don't try to use the OS keychain
```
Add those exports to your shell profile (or to `~/.hermes/.env`) so every session inherits them.
Add those exports to your shell profile (or to `${HERMES_HOME:-~/.hermes}/.env`) so every session inherits them.
### 3. Choose path at runtime

View File

@ -39,7 +39,7 @@ Multilingual trigger examples (not exhaustive):
## Prerequisites
Before using the pipeline, verify these are set in `~/.hermes/.env`:
Before using the pipeline, verify these are set in `${HERMES_HOME:-~/.hermes}/.env`:
```bash
MSGRAPH_TENANT_ID=...

View File

@ -35,7 +35,7 @@ Use this skill when the user:
## Wiki Location
**Location:** Set via `WIKI_PATH` environment variable (e.g. in `~/.hermes/.env`).
**Location:** Set via `WIKI_PATH` environment variable (e.g. in `${HERMES_HOME:-~/.hermes}/.env`).
If unset, defaults to `~/wiki`.