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:
parent
0a593f132c
commit
d1383a6b14
@ -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`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -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'}."
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}")
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"))`
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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=...
|
||||
|
||||
@ -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`.
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user