An open API service indexing awesome lists of open source software.

https://github.com/jeangnc/harness-kit

Framework for building multi-agent harnesses — author plugins once, target every vendor (Claude Code, Codex, …).
https://github.com/jeangnc/harness-kit

agent claude claude-code codex harness markdown marketplace multi-agent plugin skills typescript

Last synced: 25 days ago
JSON representation

Framework for building multi-agent harnesses — author plugins once, target every vendor (Claude Code, Codex, …).

Awesome Lists containing this project

README

          

# Harness Kit

`harness-kit` is a framework for building multi-agent harnesses — author plugins once, target every vendor.

## Quickstart

```sh
pnpm add @jean.gnc/harness-kit
harness init --marketplace my-harness --vendors claude,codex
# author src/plugins//... (see Authoring below)
harness compile
harness install
```

`harness` is the canonical bin; `harness-kit` is an alias.

## How it works

### `harness.yaml`

A declarative repo config with two required fields:

```yaml
marketplace: my-harness
vendors:
- claude
- codex
```

Created by `harness init`. Read automatically by `compile`, `install`, and `uninstall` via `--repo` (default `.`).

### Vendors

A vendor is a named target with a home directory, a per-vendor manifest path, an `emitPluginManifest` hook, `install`/`uninstall` hooks, and an optional `aliases` hook for fan-out symlink destinations. Built-in:

- **claude** — registers plugins via `claude plugin install`; creates a `CLAUDE.md` symlink whenever an `AGENTS.md` config file is linked.
- **codex** — primes the local cache by copying compiled plugins, then runs `codex plugin marketplace add`.

Writing your own vendor: see [docs/vendors.md](./docs/vendors.md).

### Source layout

```text
src/
/configs/ # vendor-specific config files (e.g. AGENTS.md, settings.json)
.fragments/ # source-only snippets; never emitted to dist (see Fragments vs companions)
plugins// # shared across every declared vendor
.claude-plugin/plugin.json # or PLUGIN.ts
skills//SKILL.md # or SKILL.ts + body.md
agents/.md # optional
commands/.md # optional
hooks/.json # optional
.claude-plugin/marketplace.json # lists the plugins to compile
```

→ Manifest fields, passthrough behavior, and plugin extensions: [docs/marketplace.md](./docs/marketplace.md).

### Compile pipeline

`harness compile` reads vendors from `harness.yaml`, validates the marketplace manifest, discovers plugins, and validates each `hookRequires` against the local artifact IDs. For each declared vendor it emits everything under a single top-level `dist//` subtree:

- `dist//.-plugin/marketplace.json` — the per-vendor marketplace manifest.
- `dist//plugins//` — compiled skills/agents/commands plus the per-vendor plugin manifest.
- `dist//configs/` — vendor-specific config files (everything under `src//configs/` minus dot-prefixed entries).

A top-level `dist/configs.json` link manifest enumerates the symlinks `harness install` will create.

### Install pipeline

`harness install` reads `harness.yaml`, then:

1. Applies links from `dist/configs.json`. Existing symlinks are replaced. Regular files are renamed to `.backup` (incrementing to `.backup.2`, `.backup.3`, …) before the symlink is created. Orphan symlinks pointing back into the repo are swept before applying.
2. For each declared vendor, discovers compiled plugins under `dist//plugins/` and calls the vendor's `install` hook.

`--mode` selects where Claude resolves plugins from: `local` (default) registers the freshly compiled `dist/claude/` tree as a local-scoped marketplace, so uncommitted builds install without publishing; `remote` pulls from the published marketplace. Codex is local-only and ignores the flag.

`--dry-run` prints the plan without touching the filesystem.

## Authoring a skill

Skills are auto-discovered by walking `/plugins//skills//SKILL.md`. The `name` field in frontmatter must match the skill's folder name.

```md

---
name: my-skill
description: What the skill does — single line.
companions:
- file: details.md
summary: Deeper notes.
---

# My Skill

For type safety conventions, see {{skill:dev-tools:typescript}}.
For TDD discipline, see {{skill:superpowers:test-driven-development}}.
For details, see {{ref:details.md}}.

{{companions}}
```

Compiles to (once per declared vendor):

```md

---
name: my-skill
description: What the skill does — single line.
companions:
- file: details.md
summary: Deeper notes.
---

# My Skill

For type safety conventions, see `dev-tools:typescript`.
For TDD discipline, see `superpowers:test-driven-development`.
For details, see `details.md`.

## Companion files (read on demand)

- `details.md` — Deeper notes.
```

### Composing with includes

Use `{{include:./.fragments/foo.md}}` to inline another Markdown file verbatim into the body. Includes expand recursively (an included file may itself contain `{{include:...}}`), and any other placeholders inside the inlined content are resolved against the **host skill**, not the include source.

Constraints:

- Path must be relative and stay inside the skill directory.
- Target must end in `.md`.
- Cycles are detected and fail the compile.
- Included files are not copied into `dist/` and are not flagged as undeclared companions.

### Fragments vs companions

Two kinds of secondary file appear next to skills, plugins, and configs. They have different lifecycles, so harness-kit gives them different conventions.

| Kind | Lifecycle | Convention | Ships to `dist/`? |
| --- | --- | --- | --- |
| **Fragment** | Compile-time only — inlined via `{{include:...}}` | Leading-dot path (e.g. `.fragments/foo.md`) | No |
| **Companion** | Runtime — read by the artifact (skill, hook, command, agent) at execution | No leading dot (e.g. `details.md`, `companions/foo.md`) | Yes |

**The rule is one sentence: leading dot = source-only.** Any source file or directory whose basename starts with `.` is stripped from dist (the vendor manifest dirs like `.claude-plugin/` are still emitted because each vendor writes them separately). Everything else ships as-is.

Example: a skill that inlines a shared snippet at compile time and ships a runtime companion alongside it.

```text
src/plugins/foo/skills/bar/
SKILL.md # uses {{include:./.fragments/snippet.md}} and references details.md
.fragments/snippet.md # stripped from dist
details.md # ships; consumed at runtime
```

After `harness compile`:

```text
dist//plugins/foo/skills/bar/
SKILL.md # snippet content inlined
details.md # ships unchanged
```

### Authoring with TypeScript (alternative)

If you prefer typed metadata, use `SKILL.ts` + sibling `body.md` instead of a single `SKILL.md`:

```ts
// SKILL.ts
import { defineSkill } from "@jean.gnc/harness-kit";

export default defineSkill({
name: "my-skill",
description: "What the skill does — single line.",
companions: [{ file: "details.md", summary: "Deeper notes." }],
});
```

```md

# My Skill

For type safety conventions, see {{skill:dev-tools:typescript}}.
```

A skill folder must contain exactly one of `SKILL.md` or `SKILL.ts`. Both forms run through the same placeholder pipeline and produce identical `dist/` output.

### Placeholder reference

Three reference kinds — `skill`, `command`, `agent` — each resolve against the **local marketplace ∪ installed plugins** and render the scoped `:` handle. The author never picks a prefix based on where a target lives; a single kind covers both local and cross-plugin references.

Resolution is two-tier: `harness compile` resolves strictly against local artifacts and **warns** (without failing) on a reference found in neither the local marketplace nor an installed plugin — so the compile stays green on a machine without those plugins installed. `harness check --mode=all` is the hard gate: the same unresolved reference fails the check. A malformed value (not `:` shape) is always a hard error.

| Placeholder | Renders to | Validation |
| --- | --- | --- |
| `{{skill::}}` | `` `:` `` | Resolves against local marketplace + installed plugins; warns on compile, fails `check --mode=all` |
| `{{command::}}` | `` `/:` `` | Resolves against local marketplace + installed plugins; warns on compile, fails `check --mode=all` |
| `{{agent::}}` | `` `:` `` | Resolves against local marketplace + installed plugins; warns on compile, fails `check --mode=all` |
| `{{ref:}}` | `` `` `` | Must be a file under the skill directory |
| `{{include:}}` | Inlined content of the target file | Must be a `.md` file inside the skill, no cycles |
| `{{companions}}` | Companion files section | Required iff companions are declared |

## CLI

```sh
harness init # scaffold harness.yaml + src//configs/ + src/plugins/
harness compile # compile src/ → dist/ per declared vendors
harness lint # lint compiled markdown under dist/
harness check # validate plugin references against local + installed sources
harness install # link configs + register plugins per declared vendor (--mode=local|remote)
harness uninstall # remove installed plugins per declared vendor
```

→ Full flag reference, bundled lint rules, and `package.json` integration: [docs/cli.md](./docs/cli.md).

## Programmatic API

Everything the CLI does is also a typed module API. See [docs/api.md](./docs/api.md).

## Requirements

- Node ≥ 24
- A package manager (pnpm, npm, yarn — pnpm is what this repo uses)
- The `claude` and/or `codex` CLIs on `$PATH` — only needed to run `harness install` / `uninstall`

## Contributing

See [CONTRIBUTING.md](./CONTRIBUTING.md).