wedow/harness/extend-harness

Claude Code and agent skill by wedow/harness

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Use whenever the user asks the running agent to gain a new capability — phrasings like "build a tool", "add a hook", "give yourself X", "let me <do thing> through you", or anything that extends the agent itself at runtime. Defines the plugin protocols, where to install them (.harness/ scopes), and the required test-through-tool-call iteration pattern. CRITICAL: "tool" here means a plugin the agent invokes via tool-calls — install it to a .harness/ directory; do NOT write a standalone CLI script in the working directory and do NOT modify the harness codebase under plugins/. Load this skill before acting on such a request rather than reverse-engineering the protocol from source.

Install

npx vskill@latest install wedow/harness/extend-harness

SKILL.md

Extending harness

Harness is a state follower; everything else is plugins discovered at runtime from .harness/ directories. To extend the running agent — yourself — drop an executable into the right place. The next loop iteration picks it up; no restart needed (_refresh_sources runs every turn).

Where to put extensions

Discovery walks from CWD up to /, collecting every .harness/ directory. For same-named files, most-local wins. Pick scope based on lifetime and audience:

ScopeLocationUse when
User-global~/.harness/Available everywhere; personal preferences, secrets
Project<repo>/.harness/Committed with the repo, scoped to that project
Subdir<dir>/.harness/Narrower scope inside a monorepo

Inside any .harness/:

commands/ # CLI subcommands (hs <name>)
tools/ # tools the model can call
hooks.d/<stage>/ # pipeline hooks; sorted by NN- prefix
providers/ # LLM API adapters (binary or .conf variant)
prompts/*.md # appended to the system prompt
skills/<name>/ # on-demand instruction packs (SKILL.md)
plugins/<name>/ # plugin packs with the same layout

AGENTS.md follows the agents.md standard — placed at the parent of the .harness/ directory, not inside it.

Tools (the most common extension)

A tool is an executable responding to three flags. Drop it at .harness/tools/<name> and chmod +x.

bash
#!/usr/bin/env bash
set -euo pipefail
case "${1:-}" in
--schema) cat <<'JSON'
{"name":"web_get","description":"GET a URL and return body",
"input_schema":{"type":"object",
"properties":{"url":{"type":"string"}},
"required":["url"]}}
JSON
;;
--describe) echo "GET a URL" ;;
--exec)
url="$(jq -r '.url' < /dev/stdin)"
curl -fsS "$url"
;;
esac
  • Stdout becomes the tool result. Stderr → HARNESS_LOG. Non-zero exit marks the result as error: true.
  • Tools run in HARNESS_CWD (the session's original working directory).
  • Any language: see `../../../../examples/tools/web_fetch` for a Python example.

Verify: hs tools lists what's discovered.

Hooks (the agent loop is a pipeline)

Each stage dispatches its hooks as a chain — each hook reads the previous one's stdout and writes JSON. The numeric NN- prefix sets order. Local same-basename overrides global, which lets you replace a bundled hook by name.

start → assemble → send → receive → done
↑ │
│ tool_exec → tool_done
│ │
└─────────────────┘

The transition is not hardcoded — each hook may emit next_state and the loop follows it. Empty next_state ends the loop. output (string) is what's printed when the loop exits.

StageStdinDefault nextUse for
sources{}(n/a)Discover/filter plugin source dirs
resolve{provider,model}(n/a)Pick provider/model before the loop
start{}assembleSession init (e.g. set HARNESS_CWD)
assemble{}sendBuild payload (messages, tools, system)
sendpayloadreceiveCall provider
receiveAPI responsedoneSave assistant msg, extract tool calls
tool_exec{tool_calls:[…]}tool_doneExecute one tool call
tool_donetool resulttool_exec / assembleSave result, route
errorerror context(empty=stop)Recover or report
donefinal context(empty=stop)Cleanup, terminal

Pipeline rules:

  • All hooks for a stage run in basename-sorted order (use NN- prefix).
  • Non-zero exit aborts the chain → routes to error.
  • Pass-through hooks (gates, observers) just cat stdin to stdout after side effects, leaving the pipeline payload intact.
  • The hook that wants to drive a transition emits {"next_state": "..."} (usually the last hook in the stage).

Example — confirm bash before execution (gate hook on tool_exec):

bash
#!/usr/bin/env bash
# .harness/hooks.d/tool_exec/05-confirm
set -euo pipefail
tc="$(cat)"
[[ "$(echo "$tc" | jq -r '.name')" == "bash" ]] || { echo "$tc"; exit 0; }
cmd="$(echo "$tc" | jq -r '.input.command')"
read -p "run: $cmd ? [y/N] " r </dev/tty
[[ "$r" =~ ^[Yy] ]] || exit 1
echo "$tc"

Example — log token usage after each response (observer hook on receive):

bash
#!/usr/bin/env bash
# .harness/hooks.d/receive/20-cost
set -euo pipefail
r="$(cat)"
in="$(echo "$r" | jq -r '.usage.input_tokens // 0')"
out="$(echo "$r" | jq -r '.usage.output_tokens // 0')"
echo "$(date -Iseconds) in=$in out=$out" >> "${HARNESS_SESSION}/cost.log"
echo "$r" # pass through

Verify: hs hooks <stage> lists what's discovered.

Skills (on-demand instruction packs)

A skill is a directory under .harness/skills/<name>/ containing SKILL.md with YAML frontmatter (name, description). The 35-skills assemble hook injects only the catalog (name + description) into the system prompt — the model loads the full body via the skill tool when relevant. Optional sibling dirs (references/, scripts/, assets/) are listed so the model knows what auxiliary files exist.

.harness/skills/my-workflow/
SKILL.md # frontmatter + body
references/details.md # listed in <skill-resources>
scripts/setup.sh # listed in <skill-resources>

Use a skill when instructions would bloat the system prompt but only matter for specific tasks. The description field is what triggers the model — make it concrete about when to load it.

Providers

A provider is an executable in providers/<name> reading payload JSON on stdin and writing the raw API response. Optional flags: --describe, --ready (exit 0 if creds set; used for auto-select), --defaults (model=… lines), --env, --stream.

A directory whose basename matches a binary in its providers/ (e.g. plugins/openai/providers/openai) is treated as a provider plugin — its hooks/tools/prompts only participate when that provider is active.

For OpenAI-compatible or Anthropic-compatible services, write a <name>.conf instead of a full binary:

protocol=openai
description=My Endpoint
model=foo-1
url=https://api.example.com/v1/chat/completions
auth_env=MY_API_KEY

Drop in any providers/ dir; hs auth set <name> stores credentials under that name.

Prompts

AGENTS.md files (at each .harness/ parent) are concatenated into the system prompt, global → local. For composable fragments inside .harness/, use prompts/*.md (sorted by filename, all loaded). Local fragments come last so they can refine global ones.

Workflow for self-extension

  1. Decide scope (~/.harness/ for personal, <repo>/.harness/ for shared).
  2. Write the executable with the right protocol — use write_file, not heredoc-via-bash, so the file is clean and easy to edit later.
  3. `chmod +x` — non-executable files are silently ignored. This is the most common omission.
  4. Verify by invoking the new extension as a real tool call on your next turn. Harness rediscovers tools every loop iteration, so the new tool is already registered — make a tool call to it the same way you call read_file or bash. Listing it with hs tools only proves discovery, not that the protocol implementation is correct; you must actually invoke it. For hooks, trigger the relevant stage (e.g. emit a tool call to exercise tool_exec). For commands, run hs <name> from bash. Never test a tool by shelling out (./tool --exec <<< '{...}') or by exiting back to the user with "ready to use" — both bypass the JSON dispatcher and silently skip protocol bugs (bad schema, wrong stdout shape, schema/exec mismatch, forgotten chmod +x).
  5. Iterate on failure. If the result is an error or unexpected output, read it, edit the file with edit_file/write_file, and call again. Keep iterating until a real tool call returns the expected result. The task is not done until you have personally invoked the new extension through the loop and seen the right result — do not return control to the user with the extension untested.

Going deeper

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
169
Repository forks
12
7-day trend score
26

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/wedow/harness/extend-harness/badge)](https://verified-skill.com/skills/wedow/harness/extend-harness)

Something wrong with this skill or its page? Tell us.