[Upstream sync] K-Dense-AI/scientific-agent-skills (github) — 45 added, 56 modified #33

Merged
promptadmin merged 101 commits from upstream-sync/scientific-agent-skills-20260808-991bd9-sesb into main 2026-08-09 18:31:46 +00:00
Showing only changes of commit bfedd081cb - Show all commits
@@ -2,9 +2,9 @@
title: "Contributing Skills"
task: ""
lineage_type: import
upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/9c9bd2e9/CONTRIBUTING.md
upstream_sha: 9c9bd2e9
imported_at: 2026-06-26
upstream_source: https://github.com/K-Dense-AI/scientific-agent-skills/blob/991bd993/CONTRIBUTING.md
upstream_sha: 991bd993
imported_at: 2026-08-08
prompt_class: catalogue
upstream_changes: accepted
author: upstream
@@ -15,11 +15,14 @@ validated: false
Thanks for helping improve Scientific Agent Skills. This guide explains how to add or update a skill in this repository while following the open [Agent Skills specification](https://agentskills.io/specification).
Participation in this project is governed by our [Code of Conduct](CODE_OF_CONDUCT.md).
## Ways to Contribute
- Add a new scientific package, database, platform, workflow, or research method skill.
- Improve an existing skill with clearer instructions, current APIs, better examples, references, or scripts.
- Fix outdated examples, broken install steps, security issues, or documentation gaps.
- Add or extend a skill's tests under `tests/<skill-name>/` (see [Tests](#tests)).
- Report bugs or request new skills through GitHub Issues.
## Skill Location
@@ -41,8 +44,12 @@ Only `SKILL.md` is required. Use optional directories when they make the skill e
- `scripts/` for executable helpers, validators, or reusable workflow code.
- `assets/` for templates, static resources, or example data.
Those four are the only directories a skill may contain. Anything else — tests, fixtures, scratch data, generated output — belongs outside `skills/`.
Keep references one level deep from `SKILL.md` where possible, and keep the main `SKILL.md` concise. The Agent Skills specification recommends keeping `SKILL.md` under 500 lines and using progressive disclosure for longer material.
A skill directory holds only what an agent loads, so tests do not belong there. They live in the repository-level suite under `tests/<skill-name>/`, mirroring the skill directory name, with any fixtures in `tests/<skill-name>/fixtures/`. See [Tests](#tests).
## Required Skill Format
Every skill must be a directory containing a `SKILL.md` file with YAML frontmatter followed by Markdown instructions.
@@ -53,7 +60,9 @@ Use this minimum template:
---
name: skill-name
description: Clear description of what the skill does and when an agent should use it.
metadata: {"version": "1.0", "skill-author": "Your Name"}
metadata:
version: "1.0"
skill-author: Your Name
---
# Skill Title
@@ -83,19 +92,33 @@ Follow the [Agent Skills specification](https://agentskills.io/specification) an
- `description` should explain both what the skill does and when an agent should use it.
- `metadata.version` is required in this repository, even though `metadata` is optional in the upstream spec.
- Version values must be quoted numeric strings, such as `"1.0"` or `"1.1"`.
- **Write `metadata` as a single-line JSON object** (flow style), for example `metadata: {"version": "1.0", "skill-author": "K-Dense Inc."}`. This is valid YAML — so it parses identically in Claude Code, Cursor, Codex, Hermes, Pi, and any Agent Skills-compliant host — and it is the only form OpenClaw's line-based frontmatter reader can parse (a multi-line block `metadata:` is silently dropped there). Do not use a nested `metadata:` block.
- **Only the six fields defined by the specification are allowed** at the top level: `name`, `description`, `license`, `compatibility`, `allowed-tools`, and `metadata`. The spec defines a closed set and the reference validator rejects any other top-level key, so everything else belongs under `metadata`.
- **Write `metadata` as a block mapping, not single-line JSON.** The reference validator parses frontmatter with `strictyaml`, which rejects JSON-style flow mappings. A flow mapping does not merely fail one check — the entire frontmatter fails to parse, so `name` and `description` become unreadable and the skill does not register.
```yaml
# Wrong -- breaks the reference validator
metadata: {"version": "1.0", "skill-author": "K-Dense Inc."}
# Right
metadata:
version: "1.0"
skill-author: K-Dense Inc.
```
Optional frontmatter fields from the specification may be used when relevant:
- `license`: the license for the individual skill, if different or worth stating explicitly.
- `compatibility`: environment requirements such as Python version, system packages, agent host, or network access.
- `metadata`: additional metadata. Must be a single-line JSON object (see above). Common keys: `version` (required), `skill-author`, an optional `openclaw` block, and an optional `hermes` block (see below).
- `allowed-tools`: space-separated tool permissions for hosts that support this experimental field.
- `required_environment_variables`: top-level Hermes credential declarations (see below). Other hosts ignore it.
- `compatibility`: environment requirements such as Python version, system packages, agent host, or network access. Maximum 500 characters.
- `metadata`: additional metadata, as a block mapping of string keys to string values. Quote any value that would otherwise parse as a number, boolean, or date (`version: "1.0"`, `last-reviewed: "2026-07-23"`). Common keys: `version` (required), `skill-author`, and an optional nested `openclaw` or `hermes` block (see below).
- `allowed-tools`: a **space-separated string** of tool permissions for hosts that support this experimental field, for example `allowed-tools: Read Write Edit Bash`. Not a YAML list.
### OpenClaw gating (`metadata.openclaw`)
OpenClaw reads an optional `openclaw` object nested inside `metadata` for dependency gating, credential injection, and display. Because it lives under `metadata`, the Agent Skills spec permits it and other hosts ignore it. It is only needed for skills with external requirements (credentials, daemons, specific binaries) — most skills omit it entirely. Supported keys:
OpenClaw reads an optional `openclaw` object nested inside `metadata` for dependency gating, credential injection, and display. Because it lives under `metadata`, the Agent Skills spec permits it and other hosts ignore it. It is only needed for skills with external requirements (credentials, daemons, specific binaries) — most skills omit it entirely.
**Keep this block a nested mapping — never a JSON string.** OpenClaw's `resolveOpenClawManifestBlock()` requires `typeof candidate === "object"`, so a stringified block silently disables gating and credential injection with no error. This is the one documented exception to the string-values rule for `metadata`, and it still passes `skills-ref validate`.
Supported keys:
- `requires`: hard eligibility gates — `{"bins": [...]}` (all must be on `PATH`), `{"anyBins": [...]}` (at least one), `{"env": [...]}` (vars that must be set), `{"config": [...]}`. A failed gate hides the skill from the agent, so only gate on things the skill genuinely cannot run without.
- `primaryEnv`: the main credential variable; OpenClaw injects it from its config (`skills.entries.<name>.apiKey`).
@@ -106,29 +129,57 @@ OpenClaw reads an optional `openclaw` object nested inside `metadata` for depend
Example (an API-key skill that stays available even without the key set, so it gates nothing and only declares the credential):
```yaml
metadata: {"version": "1.0", "skill-author": "K-Dense Inc.", "openclaw": {"primaryEnv": "EXA_API_KEY", "envVars": [{"name": "EXA_API_KEY", "required": true, "description": "Exa search API key."}]}}
metadata:
version: "1.0"
skill-author: K-Dense Inc.
openclaw:
primaryEnv: EXA_API_KEY
envVars:
- name: EXA_API_KEY
required: true
description: Exa search API key.
```
### Hermes compatibility (`required_environment_variables` and `metadata.hermes`)
[Hermes](https://hermes-agent.nousresearch.com/docs) is Agent Skills-compatible, so every skill in this repository already loads and runs there with no changes. Two optional fields make credentialed skills first-class on Hermes:
- **`required_environment_variables`** (top level): the credentials Hermes should prompt for. Write it as a single-line JSON array — `[{"name": "X_API_KEY", "prompt": "What it is", "required_for": "full functionality"}]`. This is the one Hermes-specific field that is *not* nested under `metadata`, because Hermes reads secrets at the top level. Writing it as single-line JSON keeps it valid YAML for every host and lets OpenClaw's line-based reader skip it cleanly; Claude Code, Cursor, and Codex ignore the unknown key. Mirror the same variables you declare in `metadata.openclaw.envVars`, using `required_for: "full functionality"` for required vars and `"optional features"` for optional ones.
- **`metadata.hermes`** (nested, spec-safe like `openclaw`): optional classification and gating — `tags`, `category`, `requires_toolsets`, `fallback_for_toolsets`. A failed `requires_toolsets` gate *hides* the skill, so only gate on a tool the skill genuinely cannot run without; prefer leaving it unset so the skill stays available.
- **`metadata.hermes`** (nested, spec-safe like `openclaw`): optional classification and gating — `tags`, `category`, `requires_toolsets`, `fallback_for_toolsets`. A failed `requires_toolsets` gate *hides* the skill, so only gate on a tool the skill genuinely cannot run without; prefer leaving it unset so the skill stays available. Keep it a nested mapping, not a JSON string.
Example (an API-key skill, declaring its credential for Hermes alongside the OpenClaw block):
Example (an API-key skill, declaring its credential for OpenClaw and classifying itself for Hermes):
```yaml
required_environment_variables: [{"name": "EXA_API_KEY", "prompt": "Exa search API key.", "required_for": "full functionality"}]
metadata: {"version": "1.0", "skill-author": "Exa", "openclaw": {"primaryEnv": "EXA_API_KEY", "envVars": [{"name": "EXA_API_KEY", "required": true, "description": "Exa search API key."}]}}
metadata:
version: "1.0"
skill-author: Exa
openclaw:
primaryEnv: EXA_API_KEY
envVars:
- name: EXA_API_KEY
required: true
description: Exa search API key.
hermes:
category: research
```
### `required_environment_variables` is not used in this repository
Hermes also reads a **top-level** `required_environment_variables` array to prompt for credentials. That field cannot coexist with spec conformance: the specification defines a closed set of six top-level fields, so the reference validator rejects it outright — and because `strictyaml` fails the whole frontmatter block on an unknown-shaped document, the failure is not confined to that one key.
This repository therefore does not use it. Declare credentials in two spec-legal places instead:
- `compatibility` — a human- and agent-readable sentence naming the variables the skill needs.
- `metadata.openclaw.envVars` — the machine-readable declaration, which ClawHub's security analysis also checks against the variables your scripts actually reference.
Skills still load and run on Hermes; only its automatic credential prompt is unavailable, and the required variables remain discoverable from the two fields above.
## Versioning
Every `SKILL.md` must include a quoted `version` inside the single-line `metadata` object:
Every `SKILL.md` must include a quoted `version` inside the `metadata` mapping:
```yaml
metadata: {"version": "1.0"}
metadata:
version: "1.0"
```
For a new skill, start at `"1.0"`.
@@ -171,9 +222,17 @@ Good skills are specific, practical, and easy for an agent to apply.
5. Test any commands, code examples, and scripts included in the skill.
6. Update related documentation if the new skill changes repository-level lists, examples, or setup guidance.
6. If the skill ships `scripts/`, add their tests in the repository-level suite, not in the skill directory:
7. Run validation and security checks before opening a pull request.
```text
tests/skill-name/
```
See [Tests](#tests) for the layout, the path anchor to use, and how to run them.
7. Update related documentation if the new skill changes repository-level lists, examples, or setup guidance.
8. Run validation and security checks before opening a pull request.
## Updating an Existing Skill
@@ -182,17 +241,22 @@ Good skills are specific, practical, and easy for an agent to apply.
3. Make the smallest useful change that fixes or improves the skill.
4. Increment `metadata.version`.
5. Test changed examples, commands, and scripts.
6. Note any behavior changes in the pull request description.
6. Run the skill's suite if it has one: `uv run --with pytest python -m pytest tests/skill-name -q`. Suites check that `metadata.version` is present and quoted, not what it equals, so a version bump never needs a matching test edit.
7. Note any behavior changes in the pull request description.
## Validation
Validate Agent Skills format with the reference validator:
Validate Agent Skills format with the reference validator, which is already a dev dependency:
```bash
skills-ref validate ./skills/skill-name
uv sync
uv run skills-ref validate ./skills/skill-name
# or check every skill at once, the same way CI does
for d in skills/*/; do uv run skills-ref validate "$d"; done
```
If `skills-ref` is not installed, follow the installation instructions from the [skills-ref reference library](https://github.com/agentskills/agentskills/tree/main/skills-ref).
CI runs this on every pull request that touches `skills/`, along with the repo-specific checks in `.github/workflows/skill-spec-validation.yml` (a required `metadata.version`, `allowed-tools` as a string, quoted `metadata` scalars, and a warning above 500 lines).
Security-scan new or substantially changed skills:
@@ -203,17 +267,100 @@ skill-scanner scan ./skills/skill-name --use-behavioral
A clean scan reduces review noise but does not replace manual review.
## Tests
**Tests never live under `skills/`.** A skill directory ships only what an agent loads, so tests go in the repository-level suite instead — one directory per skill, named exactly after the skill directory:
```text
tests/
└── skill-name/ # matches skills/skill-name/
├── test_scripts.py
└── fixtures/ # optional test data
```
A test reaches the skill it covers through an explicit anchor rather than a relative walk:
```python
SKILL_ROOT = Path(__file__).resolve().parents[2] / "skills" / "skill-name"
```
Anything the CLIs under test resolve relative to the working directory should be repo-root relative, since the suite runs from the repository root — `tests/skill-name/fixtures/manifest.json`, not `fixtures/manifest.json`.
Run one skill's suite, or the whole tree:
```bash
uv run --with pytest python -m pytest tests/skill-name -q
# every skill, in a separate process each, after the repo-wide guard
uv run --with pytest python tests/run_all.py
```
Each skill's suite must run in its own process. Skills' `scripts/` directories own plain top-level module names — 32 skills ship a `scripts/_common.py`, and names like `cluster.py` and `validate_manifest.py` recur — so collecting two skills into one interpreter would resolve those imports to whichever skill was imported first and silently test the wrong files. `tests/conftest.py` rejects a multi-skill session, and `tests/run_all.py` forks per skill.
### The repo-wide guard, and what you no longer have to write
```bash
uv run --with pytest python -m pytest tests/_meta -q
```
`tests/_meta` is the check to run first and the one CI blocks on. It needs no scientific packages and finishes in seconds. It spans every skill at once — safe, because it parses scripts with `ast` and never imports them — and it enforces the rule this whole layout exists for: **a skill that ships `scripts/` must have a suite at `tests/<name>/` and a `[skills.<name>]` entry in `skill-requirements.toml`.** It also runs the shared structural contract over every skill: frontmatter conformance, the 500-line `SKILL.md` limit, no tests or compiled bytecode under `skills/`, every local link resolving, every script parsing, no `eval`/`exec`/`os.system`, no script shadowing a standard-library module, no hardcoded local path, and valid shell scripts.
Because `tests/_meta` already covers all of that repo-wide, a per-skill suite should not repeat it. Write only what is specific to the skill, and pull the shared pieces from `tests/_contract/`, which `tests/conftest.py` registers as the importable module `skill_contract`:
```python
import skill_contract
# every argparse script answers --help; skips when the skill's packages are
# absent, and runs for real under --isolated
CliHelpTests = skill_contract.cli.help_test_case(SKILL_ROOT)
# for scripts that are importable libraries with a worked example under
# `if __name__ == "__main__":` rather than argparse CLIs
DemoBlockTests = skill_contract.cli.demo_test_case(SKILL_ROOT, ("doe_designs.py",))
```
`skill_contract.office` and `skill_contract.schematic` cover files that several skills ship byte-identical copies of — the OOXML `office/` tree under `docx`/`pptx`/`xlsx`, and the AI schematic generator under five skills. Instantiate them against your skill root rather than writing the tests again; `tests/_meta` separately fails if the copies drift apart, so those files have to be changed together.
Guard heavy imports at module scope so a suite degrades to skips rather than a collection error when a package is missing:
```python
np = pytest.importorskip("numpy", reason="skill-name needs numpy")
```
### One environment per skill
Four suites fail on this repository's default environment because their scientific dependencies are not installed (`exa-search`, `qutip`, `scikit-survival`, `simpy`), and installing them all into one environment is not possible: the skills' upstream pins contradict each other. `opentrons` requires `numpy<2`; `esm` caps `transformers` below the release the `transformers` skill targets; `geniml` and `spikeinterface` pin `zarr<3` while the `zarr-python` skill targets 3.x; `bioservices` caps `lxml<6` while `matchms` requires 6.0.2+; and `pytdc`, `molfeat`, `deepchem`, `histolab`, `vaex`, and `ete3` each need an interpreter older than 3.13.
`--isolated` therefore gives each skill its own throwaway `uv` environment, built from [`tests/skill-requirements.toml`](tests/skill-requirements.toml):
```bash
python tests/run_all.py --isolated # every suite, one env each
python tests/run_all.py --isolated qutip exa-search # just these
```
Nothing is installed into the project environment, so `uv sync` is unaffected. Each `[skills.<name>]` entry lists the packages that skill documents and, where needed, a `python` version for that skill alone — uv downloads the interpreter on demand. Packages that cannot be installed at all (a GitHub-only SDK, a conda-forge-only library, a CUDA build) are listed under `[unavailable]` with the reason, and the runner prints them so the gap appears in the test output.
A new skill that ships `scripts/` needs a `[skills.<name>]` entry — `tests/_meta` fails without one. Use `packages = []` when its bundled tooling is standard-library only — the skill still gets a clean environment with just pytest. uv caches wheels globally, so repeat runs create each environment in milliseconds.
`.github/workflows/skill-tests.yml` runs `tests/_meta` plus every `packages = []` suite on each pull request, which is fast and needs no wheels beyond pytest. The full `--isolated` sweep is not run in CI: it builds an environment per skill, and several of them need a CUDA toolchain, a JDK, or a local MATLAB install that a runner does not have. Run it locally before a release, and whenever you change anything under `tests/_contract/`.
## Pull Request Checklist
Before submitting a pull request, confirm:
- The skill directory name and `name` frontmatter match exactly.
- The skill directory contains only `SKILL.md`, `references/`, `scripts/`, and `assets/` — no `tests/` directory and no `test_*.py` files. Tests live in `tests/<skill-name>/`.
- `SKILL.md` has valid YAML frontmatter and Markdown body content.
- `metadata` is a single-line JSON object (not a multi-line block), so it parses on OpenClaw as well as Claude Code, Cursor, Codex, Hermes, and Pi.
- If the skill needs credentials, `required_environment_variables` is present as a single-line JSON array and mirrors the variables in `metadata.openclaw.envVars`.
- `uv run skills-ref validate ./skills/<name>` passes.
- Only the six spec-defined top-level fields are present; anything else lives under `metadata`.
- `metadata` is a block mapping, not single-line JSON, and its scalar values are quoted where needed.
- Any `metadata.openclaw` or `metadata.hermes` block is a nested mapping, not a JSON string.
- If the skill needs credentials, they are named in `compatibility` and declared in `metadata.openclaw.envVars`.
- `metadata.version` exists and is quoted.
- Existing skills have a version bump when changed.
- The `description` clearly says what the skill does and when to use it.
- `uv run --with pytest python -m pytest tests/_meta -q` passes. This is what CI blocks on, and it catches a missing suite, a missing `skill-requirements.toml` entry, a broken local link, a leaked local path, and a `SKILL.md` over 500 lines.
- If the skill ships `scripts/`: a suite exists at `tests/<skill-name>/`, a `[skills.<skill-name>]` entry exists in `tests/skill-requirements.toml`, and `python tests/run_all.py --isolated <skill-name>` passes.
- Examples and scripts have been tested or clearly marked as illustrative.
- No secrets, credentials, private data, or unsafe instructions are included.
- Relevant official documentation is linked where useful.