docs: rewrite README with install + commands + canonical layout + conflict resolution

This commit is contained in:
2026-05-23 03:35:13 +01:00
parent 75d72f26ac
commit 6e7f2a98fd
+99 -7
View File
@@ -1,13 +1,105 @@
# harness-sync # harness-sync
Sync skills, agents, MCP, and LLM endpoints across multiple LLM harnesses One canonical source of truth for skills, agents, MCP servers, LLM endpoints, and global instructions across multiple LLM harnesses. Edit once at `~/.config/harness-sync/`; propagate everywhere.
(claude-code, crush, kilo, opencode, goose, cagent, zed).
Canonical config lives in `~/.config/harness-sync/` (git-tracked). Run ## Supported harnesses
`harness-sync apply` to propagate.
See `docs/superpowers/specs/2026-05-23-harness-sync-design.md` for design. | Harness | Config path | What harness-sync writes |
|---|---|---|
| claude-code | `~/.claude/` | `skills/` (symlink), `agents/` (symlink), `CLAUDE.md`, `settings.json` `mcpServers` (merged) |
| crush | `~/.config/crush/` | `skills/` (symlink), `crush.json` `providers`/`default_model`/`mcp` (merged) |
| kilo | `~/.config/kilo/` | `agent/` (symlink), `kilo.json` `model`/`mcp` (merged) |
| opencode | `~/.config/opencode/` | `opencode.jsonc` `provider`/`model`/`mcp` (merged), `AGENTS.md` |
| goose | `~/.config/goose/` | `config.yaml` `GOOSE_PROVIDER`/`GOOSE_MODEL`/`extensions` (merged) |
| cagent | `~/.config/cagent/` | starter `default.yaml` (per-run config) |
| zed | `~/.config/zed/` | `settings.json` `agent`/`context_servers` (merged — other keys preserved) |
## Build Adding a new harness: drop a Go package under `internal/adapters/<name>/` implementing `adapter.Adapter`, register it in `cmd/harness-sync/main.go`.
make build && make install ## Install
```bash
make build && make install # installs to ~/.local/bin/harness-sync
```
## First run
```bash
harness-sync detect # see which harnesses are installed
harness-sync init # pick which to import from; canonical tree created at ~/.config/harness-sync/
harness-sync apply # propagate canonical to all detected harnesses
```
`init` opens an interactive multi-select of detected harnesses. Use `--no-prompt` to import from all, or `--from claude-code,crush` to be explicit.
## Day-to-day
```bash
harness-sync show # list files each adapter would write
harness-sync diff # apply --dry-run shorthand
harness-sync apply # propagate canonical
harness-sync apply crush # propagate to one harness only
harness-sync profile use work # switch active profile and reapply
harness-sync rollback 1 # revert last apply via git revert
harness-sync adapter list # print registered adapters
```
## Canonical layout
```
~/.config/harness-sync/
├── config.yaml # enabled_harnesses, active_profile
├── profiles/
│ ├── home.yaml # gateway URL + token + model allowlist + upstreams
│ └── work.yaml
├── instructions/
│ ├── global.md # rendered to CLAUDE.md / AGENTS.md / etc.
│ └── per-harness/<name>.md # optional override
├── skills/<name>/SKILL.md # markdown + frontmatter; symlinked into harnesses that accept it
├── agents/<name>.md # markdown + frontmatter
├── mcp.yaml # canonical MCP server registry
├── state/<harness>/... # last-rendered snapshots (git-tracked, do not hand-edit)
└── backups/ # pre-apply backups; git-ignored
```
The whole tree is a git repository. Push it to a private remote for cross-machine sync.
## Profiles
A profile bundles the LLM stack. Switching profiles re-renders every harness's LLM config in one command.
```yaml
# profiles/home.yaml
name: home
gateway:
url: https://gateway.lan
token: dummy-local-token
default_model: claude-sonnet-4-6
upstreams:
- name: anthropic
api_key: ${ANTHROPIC_API_KEY}
- name: openai
api_key: ${OPENAI_API_KEY}
models:
- id: claude-sonnet-4-6
alias: sonnet
- id: claude-opus-4-7
alias: opus
```
Secrets use `${VAR}` for env-var substitution at render time. The dummy gateway token may be plaintext (it has no value if leaked); real provider keys must be env-var references.
## Conflict resolution
`apply` performs git-style three-way merge on every rendered file. The base is the previous render snapshot from `state/<harness>/`; "ours" is the new render; "theirs" is the current target file. When the merge is clean, the target is updated. When it conflicts, harness-sync writes the merged-with-markers result next to the target as `<file>.rej`, leaves the original file untouched, and exits non-zero. Resolve the conflict (edit the target, delete `.rej`), then run `apply` again.
The same canonical repo holds the state snapshots. `rollback N` calls `git revert` on the last N commits and reapplies.
## Design and implementation
- Design: [`docs/superpowers/specs/2026-05-23-harness-sync-design.md`](docs/superpowers/specs/2026-05-23-harness-sync-design.md)
- Plan: [`docs/superpowers/plans/2026-05-23-harness-sync.md`](docs/superpowers/plans/2026-05-23-harness-sync.md)
## License
Same as the surrounding repo.