Skip to content

feat(skills): salvage airtable skill + wire skill API keys into .env (#15838)#16291

Merged
teknium1 merged 5 commits into
mainfrom
hermes/hermes-d8968594
Apr 27, 2026
Merged

feat(skills): salvage airtable skill + wire skill API keys into .env (#15838)#16291
teknium1 merged 5 commits into
mainfrom
hermes/hermes-d8968594

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Closes #15838. Salvages @Sonoyunchu's bundled Airtable skill onto current main and layers on the follow-up work needed to make skill credentials stop falling out of .env.

What this PR makes true

Bundled skills that use API keys (airtable, linear, notion, gif-search) now have their credentials registered in Hermes' OPTIONAL_ENV_VARS catalog. The user sets the key once — via hermes setup, hermes config set, or by hand in ~/.hermes/.env — and every subsequent session can see it. Previously these keys were only mentioned in each SKILL.md's setup section and left as the user's problem to remember, with no persistence hook.

Commits

  1. feat(skills): add bundled Airtable productivity skill@Sonoyunchu's original (cherry-picked, authorship preserved).
  2. fix(skills/airtable): use .env credential pattern matching notion/linear — SKILL.md rewrite. Original used metadata.hermes.config.airtable.api_key (wrong bucket for a secret per references/skill-config-interface.md — that interface is for non-secret skill settings; secrets belong in .env). Also added version/author/license frontmatter, prerequisites.commands: [curl], PAT-specific setup (legacy key... keys were deprecated Feb 2024), the three required token scopes, pagination cap, PATCH vs PUT clarification, and a deterministic /v0/meta/bases health check for verification.
  3. feat(config): register bundled-skill API keys in OPTIONAL_ENV_VARS — adds NOTION_API_KEY, LINEAR_API_KEY, TENOR_API_KEY, AIRTABLE_API_KEY. Uses a new category: "skill" so the sandbox env blocklist in tools/environments/local.py does NOT auto-block them — skills legitimately need curl to read Authorization: Bearer $KEY from the subprocess environment, which is the opposite of provider/tool credentials (GHSA-rhgp-j443-p4rf). All four entries are advanced=True so they don't nag users who have never touched those skills.
  4. chore: docs + attribution — AUTHOR_MAP entry for sonoyuncudmr@gmail.com, skills-catalog.md row.

Validation

Before After
airtable SKILL.md frontmatter custom skills.config (wrong) prerequisites.env_vars: [AIRTABLE_API_KEY]
AIRTABLE_API_KEY in OPTIONAL_ENV_VARS registered, category=skill, advanced=True
LINEAR_API_KEY, NOTION_API_KEY, TENOR_API_KEY un-tracked (user had to hand-edit .env) persisted via save_env_value, auto-loaded via reload_env
_HERMES_PROVIDER_ENV_BLOCKLIST contains skill keys correctly excluded (verified)
_HERMES_PROVIDER_ENV_BLOCKLIST contains provider/tool keys Yes Yes (unchanged)
env_passthrough.is_env_passthrough(AIRTABLE_API_KEY) after skill_view would have been False (blocklisted) True

Tests: 171 passed across tests/hermes_cli/test_config.py, tests/tools/test_skills_tool.py, tests/tools/test_skill_env_passthrough.py, tests/tools/test_env_passthrough.py, tests/tools/test_local_env_blocklist.py, tests/hermes_cli/test_env_sanitize_on_load.py.

E2E verified the full round-trip for all four skills: save_env_valuereload_envos.environ populated → skill_view reports setup_needed=Falseenv_passthrough registers the key for subprocess inheritance.

Credit to @Sonoyunchu for the original Airtable skill.

Sonoyunchu and others added 5 commits April 26, 2026 18:32
Convert the airtable skill from 'skills.config.airtable.api_key'
(config.yaml, wrong bucket for a secret) to 'prerequisites.env_vars:
[AIRTABLE_API_KEY]' (~/.hermes/.env), matching every other bundled
skill that authenticates with an API token.

Why the original shape was wrong:
- metadata.hermes.config is for non-secret skill settings (paths,
  preferences) per references/skill-config-interface.md. Storing a
  bearer token under skills.config.* also triggered the documented
  'hermes config migrate' nag-on-every-run problem.
- The Quick Reference's 'AIRTABLE_API_KEY=...' bash line couldn't
  read skills.config.airtable.api_key anyway — it's a yaml path, not
  an env var.

Follow-up polish on the same pass:
- Added version/author/license frontmatter to match notion/linear.
- Added prerequisites.commands: [curl].
- Setup section now specifies the PAT format (pat...) that replaced
  legacy 'key...' API keys in Feb 2024, plus the three required scopes
  (data.records:read/write, schema.bases:read) and the per-base Access
  list requirement.
- Clarified PATCH vs PUT and pagination (100 records/page cap).
- Swapped verification from 'hermes -q ...' (non-deterministic) to a
  curl /v0/meta/bases call that returns a verifiable HTTP status code.
Adds NOTION_API_KEY, LINEAR_API_KEY, TENOR_API_KEY, and AIRTABLE_API_KEY
to OPTIONAL_ENV_VARS so:

- They persist to ~/.hermes/.env via save_env_value like every other
  key Hermes knows about, instead of being ad-hoc variables the user
  has to hand-edit the dotfile for.
- load_env() / reload_env() populate os.environ from .env on every
  startup — the user sets the key once, skills keep working across
  restarts without losing access.
- hermes setup / hermes config show surface them as known optional
  vars with the correct signup URL (linear.app/settings/api,
  airtable.com/create/tokens, etc.).

These four entries use category="skill" (new) rather than "tool".
tools/environments/local.py auto-adds every category=tool/messaging
entry to _HERMES_PROVIDER_ENV_BLOCKLIST, which stops env passthrough
from leaking provider credentials into the execute_code sandbox
(GHSA-rhgp-j443-p4rf). Skill API keys are the opposite case — the
point is for the agent's subprocess to see them so curl can read
Authorization headers — so they must be outside the blocklist. The
new category is inert for that check.

All four entries are advanced=True: they show up in 'hermes config'
and 'hermes status' displays, but do not nag users who have never
touched those skills during setup checklists.

E2E verified: save_env_value → reload_env → os.environ populated →
skill_view reports setup_needed=False → env_passthrough registers
the key for subprocess inheritance.
- scripts/release.py: map sonoyuncudmr@gmail.com -> Sonoyunchu so the
  check-attribution CI job and release notes credit Soynchu correctly.
- website/docs/reference/skills-catalog.md: add the airtable row to
  the productivity bundled-skills table.
Expand the airtable skill from bare CRUD to a full Hermes-shaped
cookbook matching the linear/notion neighbors, and trim the
description to fit the 60-char system-prompt cutoff.

Hermes-specific additions:
- Explicit 'use the terminal tool with curl — not web_extract or
  browser_navigate' guidance, matching the same note in linear.
- Note that AIRTABLE_API_KEY flows from ~/.hermes/.env into the
  subprocess automatically via env_passthrough, so curl calls don't
  need to re-export it.
- Prefer 'python3 -m json.tool' (always present) over jq (optional)
  for pretty-printing, with -s on every curl to keep output clean.
- Read-before-write workflow that resolves record IDs via
  filterByFormula instead of guessing.

Cookbook expansion (new vs original):
- Field-type reference table (text, select, multi-select, attachment,
  linked record, user) with the exact write-shape Airtable expects.
- typecast flag for auto-coercing values / auto-creating select options.
- performUpsert PATCH for idempotent sync by merge field.
- Batch create/delete endpoints (10-record cap per call).
- Sort + fields query params with URL-encoding (%5B / %5D).
- Named-view query that applies saved filter/sort server-side.
- Full pagination loop template (while loop with offset).
- Common filterByFormula patterns (exact match, contains, AND/OR,
  date comparison, NOT empty).
- Rate-limit backoff guidance (Retry-After header, per-base budget).
- Airtable error-code reference (AUTHENTICATION_REQUIRED,
  INVALID_PERMISSIONS, MODEL_ID_NOT_FOUND,
  INVALID_MULTIPLE_CHOICE_OPTIONS) so the agent can map failures to
  user-actionable fixes instead of just retrying.

Also: description trimmed from 183 chars (truncated to 60 in system
prompt, losing 'filter/upsert/delete' trigger terms) down to 59 chars
that render whole: 'Airtable REST API via curl. Records CRUD, filters,
upserts.' Catalog row updated to match.

SKILL.md grew from 115 to 228 lines — still under the 500-line soft
cap and below the linear skill (297 lines) which serves the same
role for GraphQL.
@teknium1 teknium1 merged commit 7e3c8a3 into main Apr 27, 2026
12 of 13 checks passed
@teknium1 teknium1 deleted the hermes/hermes-d8968594 branch April 27, 2026 01:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant