View source on GitHub
clone-and-attach, which prepares a sandbox
(clone + setup.sh) and then attaches a conversation. Here we prepare the
sandbox by uploading skills instead. Read start-sandbox
first if the sandbox/agent-server split is new to you.
Why Would I Do This?
You have skills authored locally (a folder ofSKILL.md files) and you want an
agent to use them without committing them to a repo or installing them by
hand. This script drops them into the sandbox’s user skills directory before
the conversation starts, so the brand-new conversation loads them automatically.
Where do the skills go, and why there?
When OpenHands starts a conversation it loads skills from several sources and merges them. User skills come from the sandbox user’s home:~/.openhands/skills/← this example’s default target~/.agents/skills/← newer AgentSkills location (use--remote-skills-dir)~/.openhands/microagents/(legacy)
SETTING_UP_SKILLS phase.) Putting them under the user home — rather
than a repo’s .openhands/skills/ — means they load regardless of whether a
repository is selected.
Already-running conversations are not re-scanned. Upload skills first,
then start the conversation — which is exactly what this script does.
How It Works
X-Session-API-Key: <OH_API_KEY>). Steps 3–4 use the sandbox’s agent server
(auth header X-Session-API-Key: <session_api_key>, returned by the create
call). The agent server is where file upload and shell execution live — the
Cloud app server has no file-upload route.
Recursive copy without per-file round-trips
Rather than upload files one at a time, the script tars the contents of your local directory (arcname=".") into a single .tar.gz, uploads that one
archive, and extracts it with tar -xzf ... -C <target>. So a local layout
like:
~/.openhands/skills/code-review/SKILL.md and
~/.openhands/skills/deploy-helper/SKILL.md in the sandbox.
Run It
hello-openhands skill.
Point It at Your Own Skills
Every input is a flag with an environment-variable fallback, so the script is safe to drop into your own automation unchanged:Reusing the sandbox so new conversations load these skills automatically
Once this script runs, the uploaded skills live in the sandbox’s home directory, so the sandbox is now a ready-made, skills-loaded environment. Whether new conversations reuse it — and therefore inherit those skills for free — is controlled by the Sandbox Grouping Strategy setting under Settings → Application.- Default —
No Grouping (new sandbox per conversation): every new conversation gets its own fresh sandbox, so conversations you start later (in the UI or via the API) will not see the skills uploaded here. - Any grouping strategy (e.g.
Group by Newest,Add to Any,Least Recently Used,Fewest Conversations): new conversations are added to an existing, still-running sandbox instead of a fresh one. Because every conversation reloads skills from the sandbox home when it starts, the conversations you create straight from the web UI land on this skills-loaded sandbox and pick up the uploaded skills automatically — no re-running this script needed.
--sandbox-id <sandbox_id> / SANDBOX_ID instead of relying on the
grouping setting — this resumes the sandbox if it is paused.)
Skill format
Each skill is a directory containing aSKILL.md with YAML frontmatter:
triggers: list to auto-activate a skill when keywords appear
in a user message. See the bundled example-skills/ for a
working sample.
Cleanup
The sandbox is intentionally left running because a live conversation is now attached to it — deleting the sandbox ends that conversation. Delete it from the conversation UI, or via the API (the id goes in both the path and a requiredsandbox_id query parameter):

