From d1383a6b1450c6c139720b1b01f8b99cc130453f Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Wed, 10 Jun 2026 14:46:21 -0700 Subject: [PATCH] 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 --- .../blockchain/hyperliquid/SKILL.md | 4 +-- .../hyperliquid/scripts/hyperliquid_client.py | 2 +- .../kanban-video-orchestrator/SKILL.md | 2 +- .../assets/setup.sh.tmpl | 7 +++-- .../references/kanban-setup.md | 10 ++++--- .../references/tool-matrix.md | 4 +-- optional-skills/devops/watchers/SKILL.md | 2 +- .../devops/watchers/scripts/watch_github.py | 3 +- optional-skills/productivity/canvas/SKILL.md | 2 +- .../productivity/canvas/scripts/canvas_api.py | 5 +++- optional-skills/productivity/shopify/SKILL.md | 2 +- optional-skills/productivity/siyuan/SKILL.md | 2 +- .../productivity/telephony/SKILL.md | 8 ++--- .../telephony/scripts/telephony.py | 30 +++++++++---------- optional-skills/security/1password/SKILL.md | 2 +- optional-skills/security/godmode/SKILL.md | 2 +- .../rest-graphql-debug/SKILL.md | 2 +- .../hermes-agent/SKILL.md | 4 +-- .../hermes-agent/references/webhooks.md | 2 +- skills/media/gif-search/SKILL.md | 2 +- skills/note-taking/obsidian/SKILL.md | 2 +- skills/productivity/airtable/SKILL.md | 4 +-- skills/productivity/notion/SKILL.md | 4 +-- .../teams-meeting-pipeline/SKILL.md | 2 +- skills/research/llm-wiki/SKILL.md | 2 +- 25 files changed, 59 insertions(+), 52 deletions(-) diff --git a/optional-skills/blockchain/hyperliquid/SKILL.md b/optional-skills/blockchain/hyperliquid/SKILL.md index ec0671e050..51843bbf1b 100644 --- a/optional-skills/blockchain/hyperliquid/SKILL.md +++ b/optional-skills/blockchain/hyperliquid/SKILL.md @@ -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 [--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`. --- diff --git a/optional-skills/blockchain/hyperliquid/scripts/hyperliquid_client.py b/optional-skills/blockchain/hyperliquid/scripts/hyperliquid_client.py index 1079f6b626..be2a95d5f9 100644 --- a/optional-skills/blockchain/hyperliquid/scripts/hyperliquid_client.py +++ b/optional-skills/blockchain/hyperliquid/scripts/hyperliquid_client.py @@ -115,7 +115,7 @@ def _resolve_user(user: Optional[str]) -> str: sys.exit( "Missing Hyperliquid address. Pass
explicitly or set " - f"{DEFAULT_USER_ENV} in your environment or ~/.hermes/.env." + f"{DEFAULT_USER_ENV} in your environment or {_hermes_home() / '.env'}." ) diff --git a/optional-skills/creative/kanban-video-orchestrator/SKILL.md b/optional-skills/creative/kanban-video-orchestrator/SKILL.md index f06972abd5..c5ac2a8c96 100644 --- a/optional-skills/creative/kanban-video-orchestrator/SKILL.md +++ b/optional-skills/creative/kanban-video-orchestrator/SKILL.md @@ -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. diff --git a/optional-skills/creative/kanban-video-orchestrator/assets/setup.sh.tmpl b/optional-skills/creative/kanban-video-orchestrator/assets/setup.sh.tmpl index 01d836def8..3f7629d629 100644 --- a/optional-skills/creative/kanban-video-orchestrator/assets/setup.sh.tmpl +++ b/optional-skills/creative/kanban-video-orchestrator/assets/setup.sh.tmpl @@ -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 } diff --git a/optional-skills/creative/kanban-video-orchestrator/references/kanban-setup.md b/optional-skills/creative/kanban-video-orchestrator/references/kanban-setup.md index ab449a0b0a..53e4f26999 100644 --- a/optional-skills/creative/kanban-video-orchestrator/references/kanban-setup.md +++ b/optional-skills/creative/kanban-video-orchestrator/references/kanban-setup.md @@ -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 } diff --git a/optional-skills/creative/kanban-video-orchestrator/references/tool-matrix.md b/optional-skills/creative/kanban-video-orchestrator/references/tool-matrix.md index 5a52d15ddd..b5e59c3147 100644 --- a/optional-skills/creative/kanban-video-orchestrator/references/tool-matrix.md +++ b/optional-skills/creative/kanban-video-orchestrator/references/tool-matrix.md @@ -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 diff --git a/optional-skills/devops/watchers/SKILL.md b/optional-skills/devops/watchers/SKILL.md index 628f340b4c..7c326ae7e4 100644 --- a/optional-skills/devops/watchers/SKILL.md +++ b/optional-skills/devops/watchers/SKILL.md @@ -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 \ diff --git a/optional-skills/devops/watchers/scripts/watch_github.py b/optional-skills/devops/watchers/scripts/watch_github.py index bb4a3ca6f3..4b42d4ed3e 100755 --- a/optional-skills/devops/watchers/scripts/watch_github.py +++ b/optional-skills/devops/watchers/scripts/watch_github.py @@ -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 diff --git a/optional-skills/productivity/canvas/SKILL.md b/optional-skills/productivity/canvas/SKILL.md index fbcfec5853..68d6402e55 100644 --- a/optional-skills/productivity/canvas/SKILL.md +++ b/optional-skills/productivity/canvas/SKILL.md @@ -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 diff --git a/optional-skills/productivity/canvas/scripts/canvas_api.py b/optional-skills/productivity/canvas/scripts/canvas_api.py index 13599c5755..2390d5ff51 100644 --- a/optional-skills/productivity/canvas/scripts/canvas_api.py +++ b/optional-skills/productivity/canvas/scripts/canvas_api.py @@ -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, ) diff --git a/optional-skills/productivity/shopify/SKILL.md b/optional-skills/productivity/shopify/SKILL.md index 0062674069..4dc8dc93ad 100644 --- a/optional-skills/productivity/shopify/SKILL.md +++ b/optional-skills/productivity/shopify/SKILL.md @@ -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 diff --git a/optional-skills/productivity/siyuan/SKILL.md b/optional-skills/productivity/siyuan/SKILL.md index 0417ba6c4c..3f19977643 100644 --- a/optional-skills/productivity/siyuan/SKILL.md +++ b/optional-skills/productivity/siyuan/SKILL.md @@ -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 diff --git a/optional-skills/productivity/telephony/SKILL.md b/optional-skills/productivity/telephony/SKILL.md index b3d1d5884e..f0d2861491 100644 --- a/optional-skills/productivity/telephony/SKILL.md +++ b/optional-skills/productivity/telephony/SKILL.md @@ -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 diff --git a/optional-skills/productivity/telephony/scripts/telephony.py b/optional-skills/productivity/telephony/scripts/telephony.py index 188b6be2ad..291fd8629a 100644 --- a/optional-skills/productivity/telephony/scripts/telephony.py +++ b/optional-skills/productivity/telephony/scripts/telephony.py @@ -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 or ) - 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}") diff --git a/optional-skills/security/1password/SKILL.md b/optional-skills/security/1password/SKILL.md index 2a6cc8e18b..152cd13e60 100644 --- a/optional-skills/security/1password/SKILL.md +++ b/optional-skills/security/1password/SKILL.md @@ -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 diff --git a/optional-skills/security/godmode/SKILL.md b/optional-skills/security/godmode/SKILL.md index 27751e93ec..deda347178 100644 --- a/optional-skills/security/godmode/SKILL.md +++ b/optional-skills/security/godmode/SKILL.md @@ -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"))` diff --git a/optional-skills/software-development/rest-graphql-debug/SKILL.md b/optional-skills/software-development/rest-graphql-debug/SKILL.md index 78f90f2a91..64b96b3cdd 100644 --- a/optional-skills/software-development/rest-graphql-debug/SKILL.md +++ b/optional-skills/software-development/rest-graphql-debug/SKILL.md @@ -397,7 +397,7 @@ class TestAPISmoke: ### Token handling - Never log full tokens. Redact: `Bearer `. -- 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 diff --git a/skills/autonomous-ai-agents/hermes-agent/SKILL.md b/skills/autonomous-ai-agents/hermes-agent/SKILL.md index 08a4fd2b43..d02ac7933c 100644 --- a/skills/autonomous-ai-agents/hermes-agent/SKILL.md +++ b/skills/autonomous-ai-agents/hermes-agent/SKILL.md @@ -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) diff --git a/skills/autonomous-ai-agents/hermes-agent/references/webhooks.md b/skills/autonomous-ai-agents/hermes-agent/references/webhooks.md index a1758d64f0..0af935ea23 100644 --- a/skills/autonomous-ai-agents/hermes-agent/references/webhooks.md +++ b/skills/autonomous-ai-agents/hermes-agent/references/webhooks.md @@ -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 diff --git a/skills/media/gif-search/SKILL.md b/skills/media/gif-search/SKILL.md index 1a28b8b293..5416290a9d 100644 --- a/skills/media/gif-search/SKILL.md +++ b/skills/media/gif-search/SKILL.md @@ -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 diff --git a/skills/note-taking/obsidian/SKILL.md b/skills/note-taking/obsidian/SKILL.md index 1581090088..e3a9872309 100644 --- a/skills/note-taking/obsidian/SKILL.md +++ b/skills/note-taking/obsidian/SKILL.md @@ -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. diff --git a/skills/productivity/airtable/SKILL.md b/skills/productivity/airtable/SKILL.md index 547e2a14b7..3fa1b0ab97 100644 --- a/skills/productivity/airtable/SKILL.md +++ b/skills/productivity/airtable/SKILL.md @@ -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. diff --git a/skills/productivity/notion/SKILL.md b/skills/productivity/notion/SKILL.md index 83222ffd93..22010c6241 100644 --- a/skills/productivity/notion/SKILL.md +++ b/skills/productivity/notion/SKILL.md @@ -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 diff --git a/skills/productivity/teams-meeting-pipeline/SKILL.md b/skills/productivity/teams-meeting-pipeline/SKILL.md index 4ad37c4758..11960aa320 100644 --- a/skills/productivity/teams-meeting-pipeline/SKILL.md +++ b/skills/productivity/teams-meeting-pipeline/SKILL.md @@ -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=... diff --git a/skills/research/llm-wiki/SKILL.md b/skills/research/llm-wiki/SKILL.md index 839c2f682a..7dc708c9a5 100644 --- a/skills/research/llm-wiki/SKILL.md +++ b/skills/research/llm-wiki/SKILL.md @@ -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`.