← Marketplace
Productivity

harness-sharev0.3.0

Compile, export, import, and sync harness configurations across Claude Code, Cursor, and GitHub Copilot

By harnessprotocolApache-2.0Source ↗
OfficialCaution

Install

Add the marketplace once, then install the plugin:

/plugin marketplace add harnessprotocol/harness-kit
/plugin install harness-share@harness-kit
configurationexportimportyamlcross-platform

Security & permissions

Caution2 warnings, 5 info

Declared capabilities

Network accessYes
File writesNo
Environment variablesNone
External URLs3
Filesystem patternsNone

Scan observations

  • infoskills/harness-export/README.md:23

    External URL detected: https://harnessprotocol.io/schema/v1/harness.schema.json

    Verify this URL is necessary and trustworthy. Consider if this data should be fetched at install time or runtime.

  • infoskills/harness-export/README.md:55

    External URL detected: https://raw.githubusercontent.com/harnessprotocol/harness-kit/main/harness-restore.sh

    Verify this URL is necessary and trustworthy. Consider if this data should be fetched at install time or runtime.

  • warningskills/harness-export/README.md:55

    Suspicious pattern detected: Piped bash execution

    Review this code carefully. This pattern can be dangerous if not properly controlled.

  • infoskills/harness-export/SKILL.md:171

    External URL detected: https://harnessprotocol.io/schema/v1/harness.schema.json

    Verify this URL is necessary and trustworthy. Consider if this data should be fetched at install time or runtime.

  • infoskills/harness-import/README.md:53

    External URL detected: https://raw.githubusercontent.com/harnessprotocol/harness-kit/main/harness-restore.sh

    Verify this URL is necessary and trustworthy. Consider if this data should be fetched at install time or runtime.

  • warningskills/harness-import/README.md:53

    Suspicious pattern detected: Piped bash execution

    Review this code carefully. This pattern can be dangerous if not properly controlled.

  • infoskills/harness-validate/SKILL.md:52

    External URL detected: https://raw.githubusercontent.com/harnessprotocol/harness-protocol/spec/v1-foundation/schema/draft/harness.schema.json

    Verify this URL is necessary and trustworthy. Consider if this data should be fetched at install time or runtime.

Scanned at build time from source. How trust signals work →

Skills5

harness-compileskills/harness-compile/SKILL.md
harness-compile

Compile a Harness Configuration

You are compiling a harness.yaml file into native configuration files for one or more AI coding tools. Each target tool gets its own idiomatic output: instruction files, MCP server configs, skill files, and permission settings.

This skill implements the Harness Protocol compiler mapping defined at harnessprotocol.io. The output is deterministic — the same harness.yaml always produces the same files.

Workflow Order (MANDATORY)

Follow these steps in order. Do not skip any step.


Step 1: Find harness.yaml

Check for the harness file in this order:

  1. A path provided by the user after /harness-compile (e.g., /harness-compile ~/dotfiles/harness.yaml)
  2. ./harness.yaml in the current directory

If no file is found at either location, tell the user:

"No harness.yaml found. Specify a path: /harness-compile path/to/harness.yaml"

Read and parse the file. Extract metadata.name (use default if absent). Extract version to confirm v1 format. Note the instructions.import-mode value (default: merge if not specified).

If version is absent, is the integer 1 (legacy format), or is any value other than the string "1", stop and tell the user: "This harness.yaml uses an unsupported format. Run /harness-validate to check the file and see upgrade instructions."

Also check for any flags the user passed:

  • --target <claude-code|cursor|copilot|all> — restricts which targets are compiled
  • --dry-run — preview output without writing any files
  • --clean — remove orphaned marker blocks in addition to current update
  • --verbose — show skipped slots and extra detail in the compilation report

Step 2: Platform detection

If --target was provided, skip interactive detection and use those targets directly. all means compile for all three: claude-code, cursor, copilot.

Otherwise, scan the working directory for platform indicators:

Claude Code is present if any of these exist: CLAUDE.md, .claude/, .mcp.json Cursor is present if any of these exist: .cursor/, .cursor/rules/, .cursor/mcp.json, .cursor/skills/ Copilot is present if any of these exist: .github/, .vscode/mcp.json, .github/skills/

Never assume a platform is present. Detect each independently. If .github/ is the only Copilot indicator, ask the user: "I found a .github/ directory but no other Copilot indicators. Are you using GitHub Copilot in this project?"

Present the detected platforms as a multi-select and ask the user to confirm or adjust:

"I detected these targets in your project: [x] Claude Code (found: CLAUDE.md, .claude/) [x] Cursor (found: .cursor/) [ ] Copilot (not detected)

Which targets should I compile for? (Confirm, add, or remove any.)"

Wait for confirmation before proceeding. If no platforms are detected, ask:

"No AI tool config directories found. Which targets should I compile for? (claude-code, cursor, copilot)"


Step 3: Compile instructions

For each confirmed target, write the instruction slots using this mapping:

Harness slotClaude CodeCopilotCursor
instructions.operationalCLAUDE.md.github/copilot-instructions.md.cursor/rules/harness.mdc
instructions.behavioralAGENT.md.github/instructions/behavioral.instructions.md.cursor/rules/behavioral.mdc
instructions.identitySOUL.md(omit — not supported)(omit — not supported)

Section markers (exact format — do not deviate):

Every generated block must be wrapped in markers. The {name} value is metadata.name from harness.yaml, or default if absent. The {slot} value is operational, behavioral, or identity.

<!-- BEGIN harness:{name}:{slot} -->
...generated content...
<!-- END harness:{name}:{slot} -->

Example with metadata.name: data-engineer and slot operational:

<!-- BEGIN harness:data-engineer:operational -->
## Commands
- Build: `dbt run`
<!-- END harness:data-engineer:operational -->

Import-mode behavior:

  • merge (default): If the target file already exists and contains matching markers, update only the content between those markers. If no markers exist yet, append the marker block at the end of the file (creating the file if it does not exist).
  • replace: Warn the user that existing content will be overwritten and require explicit confirmation before proceeding:

    "Warning: import-mode 'replace' will overwrite CLAUDE.md. Existing content (42 lines) will be replaced with generated content. Your manual customizations outside the markers will be lost.

    Proceed? [y/N]" After confirmation, write the generated content wrapped in markers as the entire file. Skip the confirmation prompt on re-compilation if the file already contains only harness marker blocks.

  • skip: Do not write or modify this slot's file at all. Leave it untouched.

Cursor .mdc frontmatter (mandatory):

Cursor rules files require YAML frontmatter. Always prepend this block before the instruction content for .cursor/rules/harness.mdc (operational slot):

---
description: Harness operational instructions
globs: **/*
alwaysApply: true
---

For .cursor/rules/behavioral.mdc (behavioral slot):

---
description: Harness behavioral preferences
globs: **/*
alwaysApply: true
---

The frontmatter goes before the <!-- BEGIN ... --> marker block.

Copilot .instructions.md frontmatter:

Copilot instructions files require YAML frontmatter. Always prepend this block:

---
applyTo: "**"
---

The frontmatter goes before the <!-- BEGIN ... --> marker block.

Directory creation: Create parent directories before writing if they don't exist (.cursor/rules/, .github/instructions/).


Step 4: Compile MCP servers

If the harness has a mcp-servers: section, write MCP JSON config files for each confirmed target.

TargetFile path
Claude Code.mcp.json
Cursor.cursor/mcp.json
Copilot / VS Code.vscode/mcp.json

All three files use identical JSON structure. Translate from harness YAML:

  • YAML key transport → JSON key type
  • Omit any harness-specific YAML keys that have no MCP JSON equivalent
{
  "mcpServers": {
    "postgres": {
      "type": "stdio",
      "command": "uvx",
      "args": ["mcp-server-postgres", "--connection-string", "${DB_CONNECTION_STRING}"]
    }
  }
}

Note the casing: mcpServers (camelCase) — not mcp-servers, not mcp_servers.

Merging: If the target MCP file already exists, merge: add new servers defined in the harness, but do not overwrite existing server configurations. Servers already present in the file take precedence.

If a server name in harness.yaml already exists in the target config file, do not overwrite but print a warning (substitute the actual target file path and server name):

Warning: <target-config-file> already defines server '<server-name>'. Existing config kept.
  To update it, edit <target-config-file> directly or remove the entry and re-run.

For example: Warning: .cursor/mcp.json already defines server 'postgres'. Existing config kept.

Create parent directories if they don't exist.


Step 5: Compile skills

This step only runs if both conditions are true:

  1. The harness has a plugins: section
  2. At least one plugin has a SKILL.md discoverable via the source lookup below

For each plugin in plugins:, locate its SKILL.md by checking these locations in order:

  1. ~/.claude/skills/<name>/SKILL.md — Claude Code global install
  2. .cursor/skills/<name>/SKILL.md — project-local Cursor
  3. .agents/skills/<name>/SKILL.md — agentskills.io shared location

Use the first SKILL.md found. If no SKILL.md is found in any of these locations, skip that plugin and note it in the compilation report as: <name>: skipped (no SKILL.md found in ~/.claude/skills/, .cursor/skills/, or .agents/skills/).

For each plugin that has a SKILL.md, copy it to each confirmed target's skill directory:

TargetSkill directory
Claude Code(skip — uses plugin install system, not file copy)
Cursor.cursor/skills/<name>/SKILL.md or .agents/skills/<name>/SKILL.md
Copilot.github/skills/<name>/SKILL.md or .agents/skills/<name>/SKILL.md

Ask the user which location to write to if multiple are applicable, or write to the platform-specific location by default (.cursor/skills/<name>/ for Cursor, .github/skills/<name>/ for Copilot).

Frontmatter adaptation when copying to Cursor/Copilot:

  • If the source SKILL.md frontmatter has a dependencies field, rename it to compatibility
  • Enforce that the name field matches the folder name: lowercase letters and hyphens only, max 64 characters. Truncate and slugify if needed.
  • If description exceeds 1024 characters, truncate at the last word boundary before 1024 characters and append .

Create parent directories before writing.


Step 6: Compile permissions

Claude Code:

If the harness has a permissions: section, write or update .claude/settings.json with the permissions data:

{
  "permissions": {
    "allow": ["Read", "Glob", "Grep", "Write", "Edit"],
    "deny": ["mcp__postgres__drop_*"],
    "additionalDirectories": ["sql/", "migrations/"]
  }
}

The keys map as follows:

  • permissions.tools.allowpermissions.allow
  • permissions.tools.denypermissions.deny
  • permissions.pathspermissions.additionalDirectories

If .claude/settings.json already exists, merge: update or add the permissions key without touching other keys in the file.

Cursor and Copilot:

These tools do not support machine-enforceable permissions via a JSON config. Instead, append a human-readable permission description inside markers to the operational instructions file for that target (e.g., appended to the marker block in .cursor/rules/harness.mdc or .github/copilot-instructions.md):

## Tool Permissions

This harness specifies the following tool permissions:
- **Allowed**: Read, Glob, Grep, Write, Edit, Bash
- **Denied**: Any tool matching `mcp__*__drop_*` or `mcp__*__delete_*`

Please configure your tool's permission settings to match these constraints.

Print a warning for each non-Claude-Code target that has a deny list:

Warning: permissions.tools.deny is not machine-enforceable for target 'cursor'.
  Denied tools: mcp__*__drop_*
  Instructions have been updated to describe the intended permissions,
  but manual tool configuration is required.

Step 7: Handle flags

--dry-run:

Do not write any files. Instead, for each file that would be written, print its path and full content:

[DRY RUN] Would write: CLAUDE.md
----------------------------------------
<!-- BEGIN harness:data-engineer:operational -->
## Commands
- Build: `dbt run`
<!-- END harness:data-engineer:operational -->
----------------------------------------

[DRY RUN] Would write: .mcp.json
----------------------------------------
{
  "mcpServers": {
    "postgres": { ... }
  }
}
----------------------------------------

End with:

"Dry run complete. No files were written. Remove --dry-run to apply."

--clean:

In addition to normal compilation, scan all target files for orphaned marker blocks — markers whose {name} no longer matches metadata.name in the current harness.yaml.

Example: if a file contains <!-- BEGIN harness:old-harness:operational --> but the current harness name is data-engineer, that block is orphaned.

Before deleting any orphaned blocks, the agent must:

  1. List every orphaned block found: profile name, slot, file, and line numbers
  2. Show the user exactly what will be deleted
  3. Ask for confirmation: "Remove these N orphaned blocks? [y/N]"
  4. Only proceed with deletion if the user confirms

This confirmation is required every time. --clean is a destructive operation with no recovery path — never skip this step.


Step 8: Print compilation report

After all files are written (or previewed in dry-run mode), print a compilation report:

Compiled harness: data-engineer (v1.2.0)
Targets: claude-code, cursor

  CLAUDE.md                        operational   merge    48 lines added
  AGENT.md                         behavioral    merge    12 lines added
  .mcp.json                        mcp-servers   ——       2 servers
  .claude/settings.json            permissions   ——       4 allowed, 1 denied
  .cursor/rules/harness.mdc        operational   merge    48 lines added
  .cursor/rules/behavioral.mdc     behavioral    merge    12 lines added
  .cursor/mcp.json                 mcp-servers   ——       2 servers

  Warnings:
    permissions.tools.deny is not machine-enforceable for target 'cursor'.

Format rules:

  • Group by target: claude-code entries first, then cursor, then copilot
  • Within each target group, list files in slot order: operational, behavioral, identity, mcp-servers, permissions, skills
  • Skipped slots are omitted unless --verbose is passed
  • Show line counts for instruction files where calculable
  • Show server counts for MCP files
  • Show allowed/denied counts for permissions

Common Mistakes

MistakeFix
Using wrong marker formatMarkers must be exact: <!-- BEGIN harness:{name}:{slot} --> — any deviation breaks re-compilation merge logic
Using mcp_servers or mcp-servers in JSONJSON key must be mcpServers (camelCase)
Omitting Cursor .mdc frontmatterAlways add frontmatter to .mdc files — it is mandatory for Cursor to recognize the file
Omitting Copilot .instructions.md frontmatterAlways add applyTo: "**" frontmatter to Copilot instructions files
Overwriting existing MCP server configs on mergeMerge means add new servers only; existing server definitions win
Silently skipping MCP server key collisionsAlways warn when a server name already exists in the target file — never skip silently
Expecting import-mode to be per-slotimport-mode is a single scalar under instructions: that applies to all slots. Per-slot override is not supported in v1
Writing SOUL.md for non-Claude-Code targetsidentity slot is Claude Code-only — omit it for cursor and copilot
Assuming platforms are presentAlways detect independently; never assume a platform exists
Applying --clean without the flagOnly remove orphaned markers when --clean is explicitly passed
Skipping replace confirmationAlways confirm with the user before overwriting existing file content
Skipping parent directory creationAlways create parent dirs (.cursor/rules/, .github/instructions/, .vscode/) before writing
Using {name} as literal string in markers{name} must be replaced with the actual metadata.name value, or default if absent
harness-exportskills/harness-export/SKILL.md
harness-export

Export Your Harness Configuration

You are helping the user capture their current harness-kit setup into a harness.yaml file they can share with teammates or commit to their dotfiles repo.

This file follows the Harness Protocol v1 format — the open spec at harnessprotocol.io. It is backward-compatible with harness-import (which handles both old and new formats).

Workflow Order (MANDATORY)

Follow these steps in order. Do not skip any step.


Step 1: Detect installed skills

Scan all four skill directories. Each subdirectory inside these directories is an installed skill:

  • ~/.claude/skills/ — Claude Code global skills
  • .cursor/skills/ — Cursor project-local skills
  • .github/skills/ — Copilot project-local skills
  • .agents/skills/ — agentskills.io standard shared location
ls ~/.claude/skills/ 2>/dev/null
ls .cursor/skills/ 2>/dev/null
ls .github/skills/ 2>/dev/null
ls .agents/skills/ 2>/dev/null

Collect the directory names from each location. Deduplicate by skill name across locations — if the same skill name appears in multiple directories, count it once. Track which platforms each skill was found in.

Show the user the combined result:

Found skills across AI tools:

  Claude Code (~/.claude/skills/):  research, explain, orient
  Cursor (.cursor/skills/):         research, explain
  Copilot (.github/skills/):        research
  Shared (.agents/skills/):         (none)

  Unique skills: research, explain, orient

Use this deduplicated list as your list of installed plugin names for the steps that follow.

If all four directories are empty or missing, tell the user: "No installed skills found in any supported location. Nothing to export." and stop.


Step 2: Ask about sources and metadata

Tell the user what skills you found, then ask:

"I found these installed skills: [list]. A couple of quick questions:

  1. For each plugin, which repo is it from? Format: owner/repo — for example harnessprotocol/harness-kit. If a plugin is from harness-kit, just say so and I'll fill it in.

  2. What name and description should I give this harness profile? (optional — press enter to skip)

  3. Do you have any MCP servers, env variables, or CLAUDE.md instructions you'd like to include? (optional)

    Note: MCP server detection is automatic — Step 2.6 will scan .mcp.json, .cursor/mcp.json, and .vscode/mcp.json and ask you separately. You only need to answer about MCP here if you have MCP servers in a non-standard location not covered by those files.

If you've only added harness-kit plugins, just say so."

Wait for the user's response before proceeding.


Step 2.5: Detect cross-platform instruction content

Check whether any cross-platform instruction files exist and contain harness-generated marker blocks:

grep -l "<!-- BEGIN harness:" .cursor/rules/harness.mdc .github/copilot-instructions.md 2>/dev/null

Claude Code instruction files (CLAUDE.md, AGENT.md, SOUL.md) are intentionally excluded — they are managed by the user directly and are not cross-platform sources for export.

If any matching files are found, tell the user what was found and ask. List all slots found in each file — a file may contain multiple harness marker blocks:

"I also found harness-generated instruction content in these files:

  • .cursor/rules/harness.mdc (contains my-harness:operational and my-harness:behavioral blocks)
  • .github/copilot-instructions.md (contains my-harness:operational block)

Would you like me to include the operational and behavioral instruction content in the export?"

(List only the files that actually exist and contain marker blocks — substitute the real profile name from the markers in place of my-harness.)

If the user says yes:

  • Extract the content between each <!-- BEGIN harness:{name}:{slot} --> and <!-- END harness:{name}:{slot} --> marker from those files.
  • If the same profile + slot block appears in multiple files with identical content, use it once.
  • If the same profile + slot block appears in multiple files with different content, show the user both versions and ask which to use.
  • Store the extracted content to include as the instructions: section in harness.yaml (Step 4).

If the user says no, skip — do not include instruction content from cross-platform files.

If no harness marker blocks are found in any cross-platform file, skip this step silently.


Step 2.6: Detect cross-platform MCP servers

Scan these three files for MCP server definitions:

cat .mcp.json 2>/dev/null
cat .cursor/mcp.json 2>/dev/null
cat .vscode/mcp.json 2>/dev/null

Each file uses the same JSON structure with a top-level mcpServers key. Merge all mcpServers entries across all files found. Deduplicate by server name:

  • If the same server name appears in multiple files with the same config, note that it is shared and count it once.
  • If the same server name appears in multiple files with different configs, show the user both configs and ask which to keep.

Show the user a summary:

Found MCP servers:

  postgres   (in .mcp.json and .cursor/mcp.json — same config)
  filesystem (in .mcp.json only)

"Would you like to include these in the export? (all / pick / none)"

If the user selects all or pick (and picks at least one), store the chosen servers to write to the mcp-servers: section in harness.yaml (Step 4). Convert from JSON format back to harness YAML format: JSON key type → YAML key transport.

If the user selects none, or if no MCP config files are found, skip this step.


Step 3: Build the plugin entries

For each installed skill, determine its source repo:

Known harness-kit plugins (source: harnessprotocol/harness-kit):

PluginDescription
explainLayered explanations of files, functions, directories, or concepts
researchProcess any source into a structured, compounding knowledge base
lineageColumn-level lineage tracing through SQL, Kafka, Spark, and JDBC
orientTopic-focused session orientation across graph, knowledge, and research
captureCapture session information into a staging file for later reflection
reviewCode review for a branch, PR, or path — severity labels and cross-file analysis
docgenGenerate or update README, API docs, architecture overview, or changelog
harness-exportExport your installed plugins to a shareable harness.yaml
harness-importImport a harness.yaml and interactively select plugins to install
harness-validateValidate a harness.yaml file against the Harness Protocol v1 JSON Schema
harness-compileCompile harness.yaml to native config files for Claude Code, Cursor, and Copilot
harness-syncSync AI tool configuration across Claude Code, Cursor, and Copilot
open-prPre-flight checks and PR creation: run tests, open a PR, code review, and check CI
merge-prPR merge workflow: verify CI and review status, sync with base, confirm, squash merge, and clean up
pr-sweepCross-repo PR sweep: triage all open PRs, run code reviews, merge what's ready, fix quick CI blockers, and report

For any installed skill not in this table, ask the user:

"I see [name] installed but don't recognize it. What owner/repo is it from, and what does it do in one sentence?"


Step 4: Write harness.yaml

Write harness.yaml to the current directory (or a path the user specifies). Use the Harness Protocol v1 format:

$schema: https://harnessprotocol.io/schema/v1/harness.schema.json
version: "1"

# Profile identity (optional but recommended)
metadata:
  name: my-harness
  description: My personal harness configuration.

plugins:
  - name: explain
    source: harnessprotocol/harness-kit
    description: Layered explanations of files, functions, directories, or concepts
  # additional plugins follow the same structure

With MCP servers (include only if user provided them):

mcp-servers:
  postgres:
    transport: stdio
    command: uvx
    args:
      - mcp-server-postgres
      - ${DB_CONNECTION_STRING}

With env declarations (include only if user has env vars):

env:
  - name: DB_CONNECTION_STRING
    description: PostgreSQL connection string.
    required: true
    sensitive: true

With instructions (include only if user wants to bundle CLAUDE.md/AGENT.md content):

instructions:
  operational: |
    Your operational instructions here.
  import-mode: merge

Rules:

  • version must be the string "1" (quoted), not the integer 1
  • source is owner/repo — no marketplace: key, no marketplaces: section
  • Only include mcp-servers, env, instructions, and permissions sections if the user provided content for them
  • If instruction content was collected in Step 2.5, include it as the instructions: section
  • If MCP servers were collected in Step 2.6, include them as the mcp-servers: section (using YAML transport: key, not JSON type:)
  • Omit metadata if the user skipped the name/description questions
  • Do NOT include harness-export or harness-import in the output unless the user explicitly asks

Step 5: Confirm and suggest next steps

Tell the user where the file was written:

"Saved to harness.yaml. To compile it to Cursor and Copilot config files, run /harness-compile.

To share with teammates: commit it to your dotfiles repo. They can import it with /harness-import inside Claude Code, or with the shell fallback:

curl -fsSL https://raw.githubusercontent.com/harnessprotocol/harness-kit/main/harness-restore.sh | bash -s -- harness.yaml
```"

Common Mistakes

MistakeFix
Using version: 1 (integer)Must be version: "1" (string) — this is what distinguishes the protocol format
Using marketplace: harness-kitProtocol format uses source: harnessprotocol/harness-kit — no marketplaces: section
Including harness-export and harness-import in the outputOnly include plugins the user actually uses
Writing to a path without confirmingWrite to ./harness.yaml by default. If user specified a path in the invocation, use that
Adding mcp-servers: / env: / instructions: as empty sectionsOnly include these sections when the user has actual content to put in them
harness-importskills/harness-import/SKILL.md
harness-import

Import a Harness Configuration

You are helping the user install plugins from a harness.yaml file — either all of them or a subset they choose.

This skill handles both format versions:

  • v1 protocol format (version: "1" string) — uses source: owner/repo per plugin, may include mcp-servers, env, and instructions sections
  • Legacy format (version: 1 integer) — uses marketplace: key per plugin with a marketplaces: section

Workflow Order (MANDATORY)

Follow these steps in order. Do not skip any step.


Step 1: Find and read the config

Check for harness.yaml in this order:

  1. A path provided by the user after /harness-import (e.g., /harness-import ~/dotfiles/harness.yaml)
  2. ./harness.yaml in the current directory

If no file is found at either location, tell the user:

"No harness.yaml found. Get one from a teammate or generate your own with /harness-export."

Read and parse the file. Detect which format version it uses:

  • version: "1" (string) → Protocol v1 format
  • version: 1 (integer) → Legacy format

Step 2: Show what's available

Display the plugin list clearly before asking anything:

Plugins in this config:

  1. explain (harnessprotocol/harness-kit) — Layered explanations of files, functions, directories, or concepts
  2. research (harnessprotocol/harness-kit) — Process any source into a structured, compounding knowledge base
  3. superpowers (obra/superpowers-marketplace) — Structured dev workflows — TDD, systematic debugging, subagent delegation

For legacy format, resolve the source by looking up plugins[].marketplacemarketplaces[key].

Then, if the file contains any of the following sections, mention them briefly:

  • mcp-servers: — "This config also declares MCP servers. I'll ask about those after plugins."
  • env: — "This config declares environment variables. I'll surface those after plugins."
  • instructions: — "This config includes harness instructions. I'll offer to apply those after plugins."

Then ask:

"Would you like to install all of these, pick a subset, or get more details on any before deciding?"


Step 3: Handle the user's choice

"All" — mark all plugins as selected, skip to Step 5.

"Details" — for each plugin the user asks about, explain what it does based on its description. You can supplement with your knowledge of harness-kit plugins. Then return to this question.

"Pick" or "subset" — proceed to Step 4.


Step 4: Individual selection

Walk through each plugin one at a time:

"explain — Layered explanations of files, functions, directories, or concepts. Include? (yes/no)"

Wait for the answer before moving to the next plugin. Collect the selection list.

After all plugins: "Got it — you've selected: [list]. Ready to generate the install commands?"


Step 5: Generate install commands

Output the complete Claude Code command sequence. Marketplace adds come first, plugin installs second.

For protocol v1 format (source: owner/repo), derive marketplace info from the source field:

Run these in Claude Code:

/plugin marketplace add harnessprotocol/harness-kit
/plugin marketplace add obra/superpowers-marketplace

/plugin install explain@harness-kit
/plugin install superpowers@obra

The marketplace short name to use in /plugin install commands is the repo name (last segment of owner/repo). Example: harnessprotocol/harness-kit → short name harness-kit.

For legacy format (marketplace: key + marketplaces: section), resolve as before:

  • marketplace: harness-kit + marketplaces.harness-kit: harnessprotocol/harness-kit/plugin marketplace add harnessprotocol/harness-kit

Only include marketplaces for plugins actually selected.

Close with:

"Paste these into Claude Code and run them in order. Restart Claude Code after the installs complete."


Step 5.5: Offer cross-platform setup

After generating the Claude Code install commands, scan the current directory for other AI tool indicators.

Cursor is present if any of these exist: .cursor/, .cursor/rules/, .cursor/mcp.json, .cursor/skills/ GitHub Copilot is present if any of these exist: .github/, .github/skills/, .vscode/mcp.json

If .github/ is the only Copilot indicator present, ask the user: "I found a .github/ directory but no other Copilot indicators. Are you using GitHub Copilot in this project?" Only include Copilot in the multi-select menu if the user confirms.

If neither platform is detected, skip this step silently — do not ask.

If one or more platforms are detected, show them and ask which to include:

"I detected the following other AI tools in this project: Cursor, GitHub Copilot. Which would you like to set up?

  1. Both
  2. Cursor only
  3. Copilot only
  4. Neither (skip)"

For each confirmed platform, copy installed skill SKILL.md files:

  • Only copy skills that have an installed SKILL.md at ~/.claude/skills/<name>/SKILL.md
  • Only copy skill files for the plugins the user selected in Steps 3–4. Do not copy all installed skills — only the selected subset.
  • Cursor: copy to .cursor/skills/<name>/SKILL.md
  • Copilot: copy to .github/skills/<name>/SKILL.md

Frontmatter adaptation when copying:

  • If the source SKILL.md frontmatter has a dependencies field, rename it to compatibility
  • Enforce that the name field is lowercase letters and hyphens only, max 64 characters. Truncate and slugify if needed.
  • If description exceeds 1024 characters, truncate at the last word boundary before 1024 characters and append

Create parent directories before writing (.cursor/skills/<name>/, .github/skills/<name>/, .github/instructions/).

Record which platforms were confirmed — Steps 6 and 8 will use this.


Step 6: Handle MCP servers (if present)

If the config has a mcp-servers: section, walk through each server:

"This config declares an MCP server: postgres (stdio, uvx mcp-server-postgres). Would you like me to add this to your .mcp.json?"

If yes, write or update .mcp.json in the current directory with the server definition.

If platforms were confirmed in Step 5.5, also write to platform-specific MCP configs:

  • Cursor confirmed: also write .cursor/mcp.json
  • Copilot confirmed: also write .vscode/mcp.json

All three files use the same mcpServers JSON structure:

{
  "mcpServers": {
    "postgres": {
      "type": "stdio",
      "command": "uvx",
      "args": ["mcp-server-postgres", "--connection-string", "${DB_CONNECTION_STRING}"]
    }
  }
}

Note the casing: mcpServers (camelCase) — not mcp-servers, not mcp_servers.

Merge behavior (applies to all target files): If the target file already exists, add new servers but do not overwrite existing server configurations. If a server name already exists in the target file, warn:

Warning: <target-config-file> already defines server '<server-name>'. Existing config kept.
  To update it, edit <target-config-file> directly or remove the entry and re-run.

For example: Warning: .cursor/mcp.json already defines server 'postgres'. Existing config kept.

Create parent directories before writing (.cursor/, .vscode/).

If the server command contains ${VAR} references, note which env vars are needed (the env: section will cover them in Step 7).


Step 7: Surface env declarations (if present)

If the config has an env: section, display them clearly:

Environment variables required by this harness:

  DB_CONNECTION_STRING (required, sensitive) — PostgreSQL connection string for the project database.
  ANALYTICS_API_KEY (optional, sensitive) — API key for the analytics MCP server.

Tell the user:

"These environment variables need to be set before using the MCP servers or skills that depend on them. Set them in your shell profile, .env file, or secret manager."

Do NOT ask for the values — sensitive vars must never be stored in the harness file or conversation.


Step 8: Offer to apply instructions (if present)

If the config has an instructions: section and import-mode: merge (or if import-mode is absent, default to merge), offer:

"This config includes harness instructions (operational guidance for the AI). Would you like me to append them to your CLAUDE.md or AGENT.md?"

If yes, append to the file the user specifies. If import-mode: replace is set, warn:

"This config requests full replacement of existing instructions. That will overwrite your current CLAUDE.md/AGENT.md. Are you sure?"

If Cursor or Copilot was confirmed in Step 5.5, also compile instructions to those platforms using the same import-mode from harness.yaml. Use metadata.name from the harness.yaml as {name} (or default if absent). Wrap all generated content in section markers (exact format — do not deviate):

<!-- BEGIN harness:{name}:{slot} -->
...generated content...
<!-- END harness:{name}:{slot} -->

Cursor operational — write .cursor/rules/harness.mdc with mandatory frontmatter before the marker block:

---
description: Harness operational instructions
globs: **/*
alwaysApply: true
---

Cursor behavioral — if instructions.behavioral is present in harness.yaml, write .cursor/rules/behavioral.mdc with mandatory frontmatter before the marker block:

---
description: Harness behavioral preferences
globs: **/*
alwaysApply: true
---

Then content from instructions.behavioral in section markers <!-- BEGIN harness:{name}:behavioral --> / <!-- END harness:{name}:behavioral -->.

GitHub Copilot operational — write .github/copilot-instructions.md with mandatory frontmatter before the marker block:

---
applyTo: "**"
---

GitHub Copilot behavioral — if instructions.behavioral is present in harness.yaml, write .github/instructions/behavioral.instructions.md with mandatory frontmatter before the marker block:

---
applyTo: "**"
---

Then content from instructions.behavioral in section markers <!-- BEGIN harness:{name}:behavioral --> / <!-- END harness:{name}:behavioral -->.

Identity slot: Omit for both Cursor and Copilot — the identity slot is Claude Code-only.

Apply the same import-mode behavior for all Cursor and Copilot files:

  • merge (default): If the file exists and contains matching markers, update the content between them. If no markers exist yet, append the marker block at the end (creating the file if it does not exist).
  • replace: Before overwriting any Cursor or Copilot instruction file, warn the user and require confirmation, the same as for CLAUDE.md:

    "This config requests full replacement of existing [file]. That will overwrite your current [file] content. Are you sure?" Never apply replace without confirmation.

  • skip: Do not write or modify the file.

Create parent directories before writing (.cursor/rules/, .github/, .github/instructions/).


Step 9: Offer shell fallback

After all the above, add:

"If you'd rather install without Claude Code, use the shell script:

curl -fsSL https://raw.githubusercontent.com/harnessprotocol/harness-kit/main/harness-restore.sh | bash -s -- harness.yaml

This installs skill files directly (no scripts or hooks — those require the marketplace install)."


Step 9.5: Cross-platform compilation report

If cross-platform setup was performed (at least one platform confirmed in Step 5.5), print a summary after all steps complete:

Cross-platform setup complete:

  Cursor:
    Skills:  explain, research  (.cursor/skills/)
    MCP:     postgres  (.cursor/mcp.json)
    Instructions:  harness.mdc (operational), behavioral.mdc (behavioral)

  GitHub Copilot:
    Skills:  explain, research  (.github/skills/)
    MCP:     (none declared in harness.yaml)
    Instructions:  copilot-instructions.md (operational), behavioral.instructions.md (behavioral)

Omit any row where nothing was written for that category (e.g., omit MCP: if the harness has no mcp-servers: section). Omit a platform block entirely if that platform was not confirmed or nothing was written for it. If cross-platform setup was skipped (Step 5.5 produced no confirmations), do not print this report.


Common Mistakes

MistakeFix
Including marketplaces for plugins the user didn't selectOnly add marketplace add lines for marketplaces that have at least one selected plugin
Forgetting to explain the @name conventionThe @name in /plugin install must match the marketplace short name (repo name), not the owner
Skipping the marketplace add commandsThey must come before the plugin installs, or the installs will fail
Ignoring mcp-servers, env, instructions sectionsAlways check for these sections and handle them in the appropriate steps
Prompting the user for sensitive env var valuesNever ask for secret values — just tell the user which vars to set and where
Asking about cross-platform setup when no other platforms are detectedOnly prompt for cross-platform setup if Cursor or Copilot indicators are found; skip silently otherwise
Copying skill SKILL.md files that aren't installedOnly copy skills that have a SKILL.md at ~/.claude/skills/<name>/SKILL.md
Using mcp_servers or mcp-servers in MCP JSON filesJSON key must be mcpServers (camelCase) in all target files
Silently skipping MCP server key collisions in Cursor/Copilot configsAlways warn when a server name already exists in the target file — never skip silently
Omitting Cursor .mdc frontmatterAlways add description, globs, alwaysApply frontmatter — it is mandatory for Cursor to recognize the file
Omitting Copilot copilot-instructions.md frontmatterAlways add applyTo: "**" frontmatter to Copilot instructions files
Using wrong section marker formatMarkers must be exact: <!-- BEGIN harness:{name}:{slot} --> — any deviation breaks merge logic
Skipping parent directory creationAlways create parent dirs (.cursor/skills/<name>/, .github/skills/<name>/, .cursor/rules/, .vscode/) before writing
Printing the cross-platform report when nothing was set upOnly print Step 9.5 report if at least one platform was confirmed in Step 5.5
harness-syncskills/harness-sync/SKILL.md
harness-sync

Sync Harness Configurations Across Platforms

You are syncing AI tool configurations across Claude Code, Cursor, and GitHub Copilot. You will inventory what is installed and configured on each platform, report any divergence, and execute the sync direction the user chooses.

This skill depends on harness-compile: the push path reuses harness-compile's compilation logic to write the selected source platform's content to all target platforms.

Workflow Order (MANDATORY)

Follow these steps in order. Do not skip any step.


Step 1: Detect platforms

Check for any --target <platform> flag the user passed. If present, restrict the entire workflow to that platform only. Valid values: claude-code, cursor, copilot. For sync, --target <platform> means "treat this platform as the only target for push output." It does not restrict which platforms are scanned in Step 2 — all detected platforms are still inventoried so divergence can be identified.

Otherwise, scan the working directory for platform indicators:

Claude Code is present if any of these exist: CLAUDE.md, .claude/, .mcp.json Cursor is present if any of these exist: .cursor/, .cursor/rules/, .cursor/mcp.json, .cursor/skills/ Copilot is present if any of these exist: .github/, .vscode/mcp.json, .github/skills/

Never assume a platform is present. Detect each independently. If .github/ is the only Copilot indicator, ask the user:

"I found a .github/ directory but no other Copilot indicators. Are you using GitHub Copilot in this project?"

Also check global skill directories:

  • ~/.claude/skills/ — Claude Code global skill installs
  • ~/.cursor/skills/ — Cursor global skills (if the directory exists)

If no platforms are detected, tell the user:

"No AI tool config directories found. Which platforms are you using? (claude-code, cursor, copilot)"


Step 2: Inventory current state

For each detected platform, catalog three things:

Installed skills:

  • Claude Code: ~/.claude/skills/ (global) and any project-local skill symlinks
  • Cursor: .cursor/skills/ (project-local), ~/.cursor/skills/ (global if the directory exists)
  • Copilot: .github/skills/ (project-local)
  • Shared: .agents/skills/ (agentskills.io shared — available to both Cursor and Copilot)

Distinguish global vs. project-local installs with a (global) or (local) tag. Label skills found in .agents/skills/ as (shared) in the inventory display.

Instruction files: Read each platform's instruction files and identify:

  • Harness marker blocks: lines matching <!-- BEGIN harness: and <!-- END harness: -->
  • Manual content: any content outside marker blocks

Report marker blocks by their {name}:{slot} identifier.

MCP server configs: Parse JSON from:

  • Claude Code: .mcp.json
  • Cursor: .cursor/mcp.json
  • Copilot / VS Code: .vscode/mcp.json

List each server by name and transport type.

Present a unified inventory:

Current state:

Claude Code:
  Skills:  research (global), explain (global), orient (global)
  MCP:     postgres (stdio), filesystem (stdio)
  Harness blocks in CLAUDE.md: [my-harness:operational], [my-harness:behavioral]

Cursor:
  Skills:  research (local), explain (local)
  MCP:     postgres (stdio)
  Harness blocks in .cursor/rules/harness.mdc: [my-harness:operational]

Copilot:
  Skills:  (none)
  MCP:     (none)
  Harness blocks in .github/copilot-instructions.md: (none)

If a platform has no instruction file for a slot, show (none) for that slot's harness blocks.


Step 3: Detect divergence

Compare across all detected platforms:

Skills divergence: For each unique skill name found anywhere, note which platforms have it and which don't.

MCP divergence: For each unique MCP server name found anywhere, note which platforms have it and which don't. If a server exists on multiple platforms but with different configurations (different command, args, or type), flag it as a configuration conflict.

Instructions divergence: For each unique harness block identifier (e.g., my-harness:operational) found anywhere, compare the content between platforms. If the content differs between any two platforms, flag it.

Present a divergence report:

Divergence detected:

Skills:
  ✓ research  — all platforms
  ✓ explain   — all platforms
  ✗ orient    — Claude Code only (missing from Cursor, Copilot)

MCP:
  △ postgres  — Claude Code, Cursor (missing from Copilot)
  ✗ filesystem — Claude Code only

Instructions:
  CLAUDE.md ↔ .cursor/rules/harness.mdc: content differs (my-harness:operational block)
  .github/copilot-instructions.md: no harness blocks (Claude Code has my-harness:operational, my-harness:behavioral)

If no divergence is found across any dimension, tell the user:

"All platforms are in sync. Nothing to do."

And stop here.


Step 4: Propose sync direction

Ask the user:

"How would you like to sync?

  1. Push from Claude Code → other platforms
  2. Push from Cursor → other platforms
  3. Pull changes from Cursor/Copilot into harness.yaml, then recompile
  4. Show me the diff first

Which would you like?"

Wait for the user's answer before proceeding.

Note: Copilot cannot be a push source. To incorporate changes made directly in Copilot config files into harness.yaml, use option 3 (pull).

Option 1 or 2 (push): Note the source platform. Ask which targets to push to:

"Push to which platforms? (all detected platforms, or list specific ones)"

Wait for target confirmation before proceeding to Step 5.

Option 3 (pull): Note that the user wants to pull external changes back into harness.yaml and proceed to Step 5.

Option 4 (diff): Show a detailed diff of each divergent section:

  • For skills: list which skills are missing from which platforms
  • For MCP: list which servers are missing or differ between platforms, showing the full server config side by side if configs differ
  • For instructions: show the full content of each divergent harness block from each platform, labelled by platform and file path

After showing all diffs, return to this question.


Step 5: Conflict detection

Before executing any sync, check for conflicts: a conflict exists when the same harness block identifier (e.g., my-harness:operational) has different content on two or more platforms that the user is treating as peers (i.e., neither is designated the authoritative source).

For push operations: The source platform is authoritative. There are no conflicts — the source wins. Skip to Step 6.

For pull operations: Two kinds of conflicts must be resolved before writing to harness.yaml:

First, if multiple external platforms are selected as pull sources (e.g., both Cursor and Copilot), compare those platforms against each other. If the same harness block exists on both with different content, that is a cross-platform conflict that must be surfaced before either is merged into harness.yaml:

Cross-platform conflict: my-harness:operational

Cursor (.cursor/rules/harness.mdc):
  [content A]

Copilot (.github/copilot-instructions.md):
  [content B]

These platforms disagree. Which should I pull from?
1. Use Cursor version
2. Use Copilot version
3. Edit manually first, then re-run /harness-sync
4. Skip this block

Resolve all cross-platform conflicts first, then proceed to compare the surviving version against harness.yaml.

Second, any harness block that exists on the pull source and in harness.yaml with different content is a conflict.

For each conflict, show both versions side by side and ask for resolution:

Conflict: my-harness:operational

Claude Code (CLAUDE.md):
  [full content of the block from CLAUDE.md]

Cursor (.cursor/rules/harness.mdc):
  [full content of the block from .cursor/rules/harness.mdc]

How would you like to resolve this?
1. Use Claude Code version (overwrite Cursor)
2. Use Cursor version (overwrite Claude Code)
3. Edit manually first, then re-run /harness-sync
4. Skip this conflict for now

Never silently overwrite. Always show both versions. Always wait for the user's resolution choice before proceeding. Resolve all conflicts before moving to Step 6.

If the user chooses option 3 (edit manually), stop and tell them:

"Make your edits, then run /harness-sync again to pick up where we left off."

If the user chooses option 4 (skip), note the skipped conflict and continue resolving remaining conflicts. Skipped conflicts will not be synced.


Step 6: Execute

Push path:

Use harness-compile logic to write the source platform's content to all confirmed target platforms. Only update items that are divergent — do not touch content that is already in sync across platforms.

When reading skill SKILL.md files for the push source, locate them as follows:

  • Push source = Claude Code: read from ~/.claude/skills/<name>/SKILL.md
  • Push source = Cursor: check .cursor/skills/<name>/SKILL.md first; if not found, fall back to ~/.cursor/skills/<name>/SKILL.md
  • Skills in .agents/skills/<name>/SKILL.md are available to both platforms and may be used as a source if neither platform-specific path yields a SKILL.md

For each divergent item:

  • Skills: copy the skill's SKILL.md (located via the lookup above) to each target platform's skill directory (.cursor/skills/<name>/SKILL.md for Cursor, .github/skills/<name>/SKILL.md for Copilot). Apply frontmatter adaptation rules from harness-compile (rename dependencies to compatibility, enforce name constraints, truncate description if over 1024 characters).
  • MCP servers: add missing servers to target MCP config files using the source platform's server definition. Do not overwrite existing server entries in the target — if a server name already exists in the target file, print a warning and skip it.
  • Instructions: write the source platform's harness block content into each target platform's instruction file, following the slot mapping and marker format from harness-compile. Follow import-mode rules: if the target file already contains markers for that block, update between the markers; if no markers exist yet, append the block.

Create parent directories before writing if they don't exist.

Pull path:

  1. Read all content that exists outside harness marker blocks in the source platform's instruction files (Cursor or Copilot). This is native content the user added directly.
  2. Read all MCP servers that exist in source platform config files but are not in harness.yaml.

Skill files are not pulled. Skills in harness.yaml are plugin source references (source: owner/repo), not file content. If Cursor has a skill in .cursor/skills/ that is not in harness.yaml, it cannot be automatically added — the plugins: section requires knowing the plugin's source repo. Tell the user:

"I found skills in .cursor/skills/ not in your harness.yaml: [list]. To add these, run /harness-export and specify their source repos when prompted."

  1. Summarize what would be added to harness.yaml:
Changes to write to harness.yaml:

  instructions.operational: 14 lines of content from .cursor/rules/harness.mdc (outside harness blocks)
  mcp-servers: add "redis" server from .cursor/mcp.json
  1. Warn the user:

"This will modify your harness.yaml — your source of truth file. The changes above will be merged in."

"Proceed? [y/N]"

  1. If the user confirms, write the updated harness.yaml with the pulled content merged in.
  2. After writing harness.yaml, immediately run harness-compile logic to recompile all platforms from the updated harness.yaml, applying the new content consistently everywhere.

Step 7: Verify

Re-run the Step 2 inventory scan across all platforms that were modified. Compare the results to confirm platforms are now in sync.

Report the outcome:

Sync complete:

  Added:   .cursor/skills/orient/SKILL.md
  Updated: .cursor/mcp.json (added filesystem server)
  Updated: .cursor/rules/harness.mdc (my-harness:operational block)
  Skipped: .github/ (Copilot not detected)

All platforms are now in sync.

If any divergence remains (e.g., a skipped conflict, a server that already existed in the target), report it:

Remaining divergence:
  my-harness:operational block was skipped (conflict unresolved)
  Run /harness-sync again to address it.

Common Mistakes

MistakeFix
Silently overwriting divergent contentAlways show the conflict and ask — never write without the user's explicit choice
Treating .github/ alone as Copilot confirmationAsk the user if .github/ is the only indicator — it may exist for CI only
Conflating global and project-local skillsAlways label skills as (global) or (local) in the inventory — they are not equivalent
Skipping conflict detection on pushPush makes the source authoritative — but still check whether the user confirmed the target list before writing
Overwriting existing MCP server entries in targetsOn push, add missing servers only — warn and skip if a server name already exists in the target
Running pull without warning about harness.yaml modificationAlways warn that pull modifies harness.yaml before writing
Forgetting to recompile after pullPull must update harness.yaml and then run harness-compile logic — the two steps are always paired
Skipping Step 7 verificationAlways re-scan after sync to confirm platforms are in sync and report any remaining divergence
Reporting all items in the sync report, not just changed onesOnly report files that were actually added, updated, or skipped — do not list unchanged files
Applying frontmatter adaptation only for new skillsFrontmatter rules (rename dependencies, enforce name constraints, truncate description) apply on every copy to Cursor/Copilot, including updates
Showing only one version in a conflictAlways show both versions side by side with platform labels — never show just one
Skipping cross-platform comparison on pullWhen multiple platforms are pull sources, compare them against each other before merging into harness.yaml
Expecting Copilot to be a push sourceCopilot is a pull-only platform. Use option 3 to bring Copilot changes back into harness.yaml
harness-validateskills/harness-validate/SKILL.md
harness-validate

Validate a Harness Configuration

You are helping the user validate a harness.yaml file against the Harness Protocol v1 JSON Schema.

Workflow Order (MANDATORY)

Follow these steps in order. Do not skip any step.


Step 1: Find the file

Check for the harness file in this order:

  1. A path provided by the user after /harness-validate (e.g., /harness-validate ~/dotfiles/harness.yaml)
  2. ./harness.yaml in the current directory

If no file is found at either location, tell the user:

"No harness.yaml found. Specify a path: /harness-validate path/to/harness.yaml"

Read the file contents.


Step 2: Run validation

Install the validation tools if not present and run validation:

python3 -m venv /tmp/hk-validate-venv 2>/dev/null || true
/tmp/hk-validate-venv/bin/pip install jsonschema pyyaml -q 2>/dev/null || \
  (python3 -m venv /tmp/hk-validate-venv && /tmp/hk-validate-venv/bin/pip install jsonschema pyyaml -q)

Then run this validation script:

import json, sys, yaml

try:
    from jsonschema import validate, ValidationError, SchemaError
    from jsonschema.validators import validator_for
except ImportError:
    print("ERROR: jsonschema not installed")
    sys.exit(1)

SCHEMA_URL = "https://raw.githubusercontent.com/harnessprotocol/harness-protocol/spec/v1-foundation/schema/draft/harness.schema.json"
HARNESS_FILE = "harness.yaml"  # replace with actual path

# Fetch the schema
import urllib.request
try:
    with urllib.request.urlopen(SCHEMA_URL, timeout=5) as resp:
        schema = json.loads(resp.read())
except Exception as e:
    print(f"WARN: Could not fetch remote schema ({e}). Falling back to basic checks.")
    schema = None

# Load the harness file
try:
    with open(HARNESS_FILE) as f:
        doc = yaml.safe_load(f)
except yaml.YAMLError as e:
    print(f"FAIL: YAML parse error — {e}")
    sys.exit(1)

if schema is None:
    # Basic offline checks
    errors = []
    if "version" not in doc:
        errors.append("Missing required field: version")
    elif doc["version"] not in ("1", 1):
        errors.append(f"version must be \"1\" (string) or 1 (legacy integer), got: {doc['version']!r}")
    if "metadata" not in doc:
        errors.append("Missing required field: metadata")
    elif "name" not in doc.get("metadata", {}):
        errors.append("metadata.name is required")
    if errors:
        for e in errors:
            print(f"FAIL: {e}")
    else:
        print("PASS (basic checks only — schema fetch failed)")
    sys.exit(0)

# Full schema validation
try:
    validate(instance=doc, schema=schema)
    print("PASS")
except ValidationError as e:
    path = " → ".join(str(p) for p in e.absolute_path) or "(root)"
    print(f"FAIL: {path}: {e.message}")
except SchemaError as e:
    print(f"ERROR: Schema itself is invalid — {e.message}")

Run the script with /tmp/hk-validate-venv/bin/python3 and capture the output.


Step 3: Report results

On PASS:

"Your harness.yaml is valid — passes Harness Protocol v1 schema validation."

If the file uses version: 1 (integer, legacy format), add:

"Note: this is in the legacy format (version: 1 integer). Run /harness-export to regenerate in Harness Protocol v1 format (version: \"1\" string)."

On FAIL (validation errors):

Display errors with clear field paths and fix suggestions:

✗ harness.yaml failed validation:

  plugins → 0 → source: 'marketplace' is not a valid property
    Fix: Use source: owner/repo instead of marketplace: key.
    Example: source: harnessprotocol/harness-kit

  env → 0: 'default' is not allowed when sensitive is true
    Fix: Remove the default value — sensitive vars must be set by the user,
    never baked into the harness file.

  (root): version must be "1" (string), got 1 (integer)
    Fix: Change version: 1 to version: "1" (add quotes).

Common errors and their fixes:

ErrorFix
version must be string "1"Change version: 1 to version: "1"
source is not a valid propertyReplace marketplace: key with source: owner/repo
default not allowed when sensitive: trueRemove the default value
metadata.name is requiredAdd a metadata.name field
Unknown additional propertyCheck for typos in field names

After listing errors:

"Fix these issues and run /harness-validate again to confirm."

On YAML parse error:

"Your harness.yaml has a YAML syntax error: [error details]

Common causes: wrong indentation, missing quotes around special characters (like : in strings), or a tab used instead of spaces."


Common Mistakes

MistakeFix
Reporting only the first errorShow all errors, not just the first one
Giving up if schema fetch failsFall back to basic checks and tell the user the schema was unavailable
Suggesting marketplace: as a fixAlways recommend source: owner/repo — that's the v1 format