reviewer-harness 1.4.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/bin/cli.js +33 -8
- package/lib/adapters/antigravity.js +15 -0
- package/lib/adapters/copilot.js +15 -0
- package/lib/adapters/freebuff.js +16 -0
- package/lib/adapters/index.js +12 -1
- package/lib/init.js +1 -1
- package/lib/skills.js +14 -0
- package/lib/sync.js +21 -4
- package/package.json +6 -3
- package/skills/safe-editing.md +1 -0
package/README.md
CHANGED
|
@@ -47,20 +47,23 @@ The harness is **not the AI model** — it is what prepares everything the AI re
|
|
|
47
47
|
harness.config.json ← your master spec (ONE file)
|
|
48
48
|
│ validate() ← quality checks + secret scanning
|
|
49
49
|
▼
|
|
50
|
-
lib/adapters/*.js ← adapters: opencode, claude, gemini, cline
|
|
50
|
+
lib/adapters/*.js ← adapters: opencode, claude, gemini, cline,
|
|
51
|
+
freebuff, copilot, antigravity
|
|
51
52
|
│ renderInstructions()
|
|
52
53
|
▼
|
|
53
54
|
.opencode/instructions.md ← OpenCode
|
|
54
55
|
CLAUDE.md ← Claude Code
|
|
55
56
|
GEMINI.md ← Gemini CLI
|
|
56
57
|
.clinerules ← Cline
|
|
58
|
+
.agents/skills/<skill>/SKILL.md ← Freebuff, Antigravity (project skills)
|
|
59
|
+
.github/skills/<skill>/SKILL.md ← GitHub Copilot CLI (project skills)
|
|
57
60
|
```
|
|
58
61
|
|
|
59
62
|
Change the rules in **one** place (`harness.config.json`), and `sync` propagates them to every runtime.
|
|
60
63
|
|
|
61
64
|
## Quickstart
|
|
62
65
|
|
|
63
|
-
You can use reviewer-harness in two ways: **Modular Mode** (zero-install: add individual skills to any project, no setup) or **Full Harness Mode** (one master config synced across all
|
|
66
|
+
You can use reviewer-harness in two ways: **Modular Mode** (zero-install: add individual skills to any project, no setup) or **Full Harness Mode** (one master config synced across all 7 runtimes).
|
|
64
67
|
|
|
65
68
|
### 1. Modular Skills (zero-install via npx)
|
|
66
69
|
|
|
@@ -77,9 +80,9 @@ npx reviewer-harness skills add no-hallucination
|
|
|
77
80
|
|
|
78
81
|
> Prefer a global CLI? Install once with `npm install -g reviewer-harness` and run `reviewer-harness skills add <name>` anywhere.
|
|
79
82
|
|
|
80
|
-
### 2. Full Harness Synchronization (
|
|
83
|
+
### 2. Full Harness Synchronization (7 runtimes)
|
|
81
84
|
|
|
82
|
-
`init` creates your master config, `validate` checks it (including secret scanning), and `sync` generates the native instruction file for every runtime you enable: `.opencode/instructions.md`, `CLAUDE.md`, `GEMINI.md`, and `.clinerules`.
|
|
85
|
+
`init` creates your master config, `validate` checks it (including secret scanning), and `sync` generates the native instruction file for every runtime you enable: `.opencode/instructions.md`, `CLAUDE.md`, `GEMINI.md`, and `.clinerules` — plus Agent Skills bundles (`.agents/skills/<skill>/SKILL.md` for Freebuff and Antigravity, `.github/skills/<skill>/SKILL.md` for GitHub Copilot CLI), so your skills follow you even when you switch CLIs.
|
|
83
86
|
|
|
84
87
|
```bash
|
|
85
88
|
# Using npx (zero-install, recommended)
|
|
@@ -152,7 +155,7 @@ Guardrails that run before any file is written:
|
|
|
152
155
|
|
|
153
156
|
## This repo governs itself
|
|
154
157
|
|
|
155
|
-
reviewer-harness eats its own cooking: the instruction files at this repo's root — `.opencode/instructions.md`, `CLAUDE.md`, `GEMINI.md`, `.clinerules` — plus the agents and commands in `.opencode/` and `.claude/` are **generated by reviewer-harness itself** from its own `harness.config.json`. Open this repo in any AI coding tool and the tool follows the harness's own rules while working on the harness. To refresh them after editing the config, just run `node bin/cli.js sync` here.
|
|
158
|
+
reviewer-harness eats its own cooking: the instruction files at this repo's root — `.opencode/instructions.md`, `CLAUDE.md`, `GEMINI.md`, `.clinerules` — plus the agents and commands in `.opencode/` and `.claude/` and the skill bundles in `.agents/skills/` and `.github/skills/` are **generated by reviewer-harness itself** from its own `harness.config.json`. Open this repo in any AI coding tool and the tool follows the harness's own rules while working on the harness. To refresh them after editing the config, just run `node bin/cli.js sync` here.
|
|
156
159
|
|
|
157
160
|
## Local Memory (graph-style, Obsidian-ready)
|
|
158
161
|
|
|
@@ -238,7 +241,7 @@ Running from a source checkout instead? The same commands work as `node bin/cli.
|
|
|
238
241
|
- `lib/memory.js` — local memory: notes vault, graph builder, index generator
|
|
239
242
|
- `lib/skills.js` — skills system: bundled store, install/enable, section rendering
|
|
240
243
|
- `lib/agents.js` / `lib/commands.js` — one source → OpenCode + Claude formats
|
|
241
|
-
- `lib/adapters/` — registry + one adapter per runtime (opencode, claude, gemini, cline)
|
|
244
|
+
- `lib/adapters/` — registry + one adapter per runtime (opencode, claude, gemini, cline, freebuff, copilot, antigravity)
|
|
242
245
|
- `skills/` — bundled skills · `agents/` — bundled sub-agents · `commands/` — bundled slash commands
|
|
243
246
|
|
|
244
247
|
## How it was built (the lessons)
|
package/bin/cli.js
CHANGED
|
@@ -13,7 +13,7 @@ import { sync, writeSafe } from "../lib/sync.js";
|
|
|
13
13
|
import { initProject } from "../lib/init.js";
|
|
14
14
|
import { initMemory, buildIndex, readNotes, memoryDir } from "../lib/memory.js";
|
|
15
15
|
import { validateConfig } from "../lib/validate.js";
|
|
16
|
-
import { listBundledSkills, addSkill, removeSkill, loadBundledSkills, skillCommandContent } from "../lib/skills.js";
|
|
16
|
+
import { listBundledSkills, addSkill, removeSkill, loadBundledSkills, skillCommandContent, skillBundleContent } from "../lib/skills.js";
|
|
17
17
|
import { listBundledAgents, addAgent } from "../lib/agents.js";
|
|
18
18
|
import { listBundledCommands, addCommand } from "../lib/commands.js";
|
|
19
19
|
|
|
@@ -45,7 +45,7 @@ Usage (run inside your project):
|
|
|
45
45
|
reviewer-harness commands list List bundled slash commands
|
|
46
46
|
reviewer-harness commands add <name> Install a slash command (OpenCode + Claude)
|
|
47
47
|
|
|
48
|
-
reviewer-harness global [--dry-run] Install EVERY bundled skill
|
|
48
|
+
reviewer-harness global [--dry-run] Install EVERY bundled skill for ALL projects (OpenCode + Claude "/" menus; Freebuff, Copilot CLI, Antigravity skill bundles)
|
|
49
49
|
|
|
50
50
|
reviewer-harness memory init Start the local memory (memory/ folder)
|
|
51
51
|
reviewer-harness memory index Rebuild the memory INDEX.md + graph.json
|
|
@@ -62,7 +62,11 @@ function cmdStatus() {
|
|
|
62
62
|
console.log(`Runtimes:`);
|
|
63
63
|
for (const name of config.runtimes) {
|
|
64
64
|
const adapter = adapters[name];
|
|
65
|
-
|
|
65
|
+
const target = !adapter ? "(no adapter!)"
|
|
66
|
+
: typeof adapter.targetFile === "function" ? adapter.targetFile()
|
|
67
|
+
: typeof adapter.skillBundleDir === "function" ? `${adapter.skillBundleDir()}/<skill>/SKILL.md`
|
|
68
|
+
: "(unknown)";
|
|
69
|
+
console.log(` - ${name} → ${target}`);
|
|
66
70
|
}
|
|
67
71
|
console.log(`Skills : ${config.skills?.length ? config.skills.join(", ") : "(none)"}`);
|
|
68
72
|
}
|
|
@@ -82,6 +86,17 @@ function cmdSync() {
|
|
|
82
86
|
}
|
|
83
87
|
} else if (r.skipped) {
|
|
84
88
|
console.log(`- ${r.runtime}: SKIP — ${r.skipped}`);
|
|
89
|
+
} else if (r.skillBundles) {
|
|
90
|
+
const files = r.skillBundles;
|
|
91
|
+
const written = files.filter((f) => f.written).length;
|
|
92
|
+
const pending = files.filter((f) => f.dryRun && f.changed).length;
|
|
93
|
+
const unchanged = files.filter((f) => f.unchanged || (f.dryRun && !f.changed)).length;
|
|
94
|
+
const where = `${r.bundleDir}/<skill>/SKILL.md`;
|
|
95
|
+
if (dryRun) {
|
|
96
|
+
console.log(`- ${r.runtime} → ${where}: ${pending} would be written/updated, ${unchanged} unchanged (Agent Skills bundles)`);
|
|
97
|
+
} else {
|
|
98
|
+
console.log(`- ${r.runtime} → ${where}: ${written} written ✓, ${unchanged} unchanged`);
|
|
99
|
+
}
|
|
85
100
|
} else if (r.dryRun) {
|
|
86
101
|
console.log(`- ${r.runtime} → ${r.file}: ${r.changed ? "WOULD WRITE (changes pending)" : "no changes"}`);
|
|
87
102
|
} else if (r.unchanged) {
|
|
@@ -213,21 +228,31 @@ function cmdGlobal() {
|
|
|
213
228
|
const targets = [
|
|
214
229
|
{ runtime: "OpenCode", dir: join(homedir(), ".config", "opencode", "commands") },
|
|
215
230
|
{ runtime: "Claude", dir: join(homedir(), ".claude", "commands") },
|
|
231
|
+
// Skill-bundle runtimes (Agent Skills open standard: <skill>/SKILL.md)
|
|
232
|
+
{ runtime: "Freebuff", dir: join(homedir(), ".agents", "skills"), bundle: true },
|
|
233
|
+
{ runtime: "Copilot CLI", dir: join(homedir(), ".copilot", "skills"), bundle: true },
|
|
234
|
+
{ runtime: "Antigravity", dir: join(homedir(), ".gemini", "config", "skills"), bundle: true },
|
|
216
235
|
];
|
|
217
236
|
if (dryRun) console.log("(dry-run: nothing was written)\n");
|
|
218
|
-
for (const { runtime, dir } of targets) {
|
|
237
|
+
for (const { runtime, dir, bundle } of targets) {
|
|
219
238
|
let written = 0, unchanged = 0, pending = 0;
|
|
220
239
|
for (const skill of skills) {
|
|
221
|
-
const
|
|
240
|
+
const rel = bundle ? join(skill.name, "SKILL.md") : `${skill.name}.md`;
|
|
241
|
+
const content = bundle
|
|
242
|
+
? skillBundleContent(skill, skills)
|
|
243
|
+
: skillCommandContent(skill, skills);
|
|
244
|
+
const outcome = writeSafe(join(dir, rel), content, dryRun);
|
|
222
245
|
if (outcome.written) written++;
|
|
223
246
|
else if (outcome.dryRun && outcome.changed) pending++;
|
|
224
247
|
else unchanged++;
|
|
225
248
|
}
|
|
226
|
-
|
|
227
|
-
|
|
249
|
+
const where = bundle ? `${dir} (<skill>/SKILL.md)` : dir;
|
|
250
|
+
if (dryRun) console.log(`- ${runtime} (global): ${pending} would be written/updated, ${unchanged} unchanged → ${where}`);
|
|
251
|
+
else console.log(`- ${runtime} (global): ${written} written ✓, ${unchanged} unchanged → ${where}`);
|
|
228
252
|
}
|
|
229
253
|
console.log(
|
|
230
|
-
`\nDone: ${skills.map((s) => "/" + s.name).join(", ")} are now in
|
|
254
|
+
`\nDone: ${skills.map((s) => "/" + s.name).join(", ")} are now available in EVERY project.` +
|
|
255
|
+
`\n(OpenCode + Claude: "/" menu commands. Freebuff, Copilot CLI, Antigravity: Agent Skills bundles.)` +
|
|
231
256
|
`\n(Per-project rules still come from "sync" inside a project.)`
|
|
232
257
|
);
|
|
233
258
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// lib/adapters/antigravity.js — ADAPTER for Google Antigravity.
|
|
2
|
+
//
|
|
3
|
+
// Antigravity follows the Agent Skills open standard (agentskills.io):
|
|
4
|
+
// workspace skills at `<workspace>/.agents/skills/<name>/SKILL.md`
|
|
5
|
+
// (with backward compatibility for `.agent/skills/`), and global
|
|
6
|
+
// skills at `~/.gemini/config/skills/<name>/SKILL.md`. This adapter
|
|
7
|
+
// covers the workspace side (used by `sync`); the global side is
|
|
8
|
+
// covered by the `global` command.
|
|
9
|
+
//
|
|
10
|
+
// Like Freebuff and Copilot CLI, Antigravity is skills-only here:
|
|
11
|
+
// it exports skillBundleDir() instead of targetFile()/renderInstructions().
|
|
12
|
+
|
|
13
|
+
export function skillBundleDir() {
|
|
14
|
+
return ".agents/skills";
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// lib/adapters/copilot.js — ADAPTER for GitHub Copilot CLI.
|
|
2
|
+
//
|
|
3
|
+
// GitHub Copilot CLI reads Agent Skills (agentskills.io) from skill
|
|
4
|
+
// folders: project skills at `<project>/.github/skills/<name>/SKILL.md`
|
|
5
|
+
// (it also recognizes `.claude/skills/` and `.agents/skills/`), and
|
|
6
|
+
// personal skills at `~/.copilot/skills/<name>/SKILL.md` (or
|
|
7
|
+
// `~/.agents/skills/`). This adapter covers the project side (used by
|
|
8
|
+
// `sync`); the personal side is covered by the `global` command.
|
|
9
|
+
//
|
|
10
|
+
// Like Freebuff and Antigravity, Copilot CLI is skills-only here:
|
|
11
|
+
// it exports skillBundleDir() instead of targetFile()/renderInstructions().
|
|
12
|
+
|
|
13
|
+
export function skillBundleDir() {
|
|
14
|
+
return ".github/skills";
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// lib/adapters/freebuff.js — ADAPTER for Freebuff (Codebuff platform).
|
|
2
|
+
//
|
|
3
|
+
// Freebuff follows the Agent Skills open standard (agentskills.io):
|
|
4
|
+
// skills are folders containing a SKILL.md with `name` + `description`
|
|
5
|
+
// frontmatter. Personal skills live at `~/.agents/skills/<name>/SKILL.md`;
|
|
6
|
+
// project skills live at `<project>/.agents/skills/<name>/SKILL.md`.
|
|
7
|
+
// This adapter covers the project side (used by `sync`); the personal
|
|
8
|
+
// side is covered by the `global` command.
|
|
9
|
+
//
|
|
10
|
+
// Freebuff has no single instruction file like CLAUDE.md, so this
|
|
11
|
+
// adapter is skills-only: it exports skillBundleDir() instead of
|
|
12
|
+
// targetFile()/renderInstructions().
|
|
13
|
+
|
|
14
|
+
export function skillBundleDir() {
|
|
15
|
+
return ".agents/skills";
|
|
16
|
+
}
|
package/lib/adapters/index.js
CHANGED
|
@@ -9,7 +9,18 @@ import * as opencode from "./opencode.js";
|
|
|
9
9
|
import * as claude from "./claude.js";
|
|
10
10
|
import * as gemini from "./gemini.js";
|
|
11
11
|
import * as cline from "./cline.js";
|
|
12
|
+
import * as freebuff from "./freebuff.js";
|
|
13
|
+
import * as copilot from "./copilot.js";
|
|
14
|
+
import * as antigravity from "./antigravity.js";
|
|
12
15
|
|
|
13
|
-
export const adapters = {
|
|
16
|
+
export const adapters = {
|
|
17
|
+
opencode,
|
|
18
|
+
claude,
|
|
19
|
+
gemini,
|
|
20
|
+
cline,
|
|
21
|
+
freebuff,
|
|
22
|
+
copilot,
|
|
23
|
+
antigravity,
|
|
24
|
+
};
|
|
14
25
|
|
|
15
26
|
export const supportedRuntimes = Object.keys(adapters);
|
package/lib/init.js
CHANGED
|
@@ -11,7 +11,7 @@ const TEMPLATE = {
|
|
|
11
11
|
version: "1.0.0",
|
|
12
12
|
problem:
|
|
13
13
|
"The problem — an AI with no guardrails: it sometimes breaks existing code it was never asked to touch, and hallucinates — inventing functions, libraries, or output that do not actually exist. These rules are the guardrails against that.",
|
|
14
|
-
runtimes: ["opencode", "claude", "gemini", "cline"],
|
|
14
|
+
runtimes: ["opencode", "claude", "gemini", "cline", "freebuff", "copilot", "antigravity"],
|
|
15
15
|
skills: [],
|
|
16
16
|
rules: [
|
|
17
17
|
"Edit surgically: change only the code the task requires — never rewrite a whole file.",
|
package/lib/skills.js
CHANGED
|
@@ -168,3 +168,17 @@ export function skillCommandContent(skill, allSkills = []) {
|
|
|
168
168
|
content += `\nApply this playbook to the following: $ARGUMENTS\n`;
|
|
169
169
|
return content;
|
|
170
170
|
}
|
|
171
|
+
|
|
172
|
+
// Skill BUNDLE content — the Agent Skills open standard (agentskills.io):
|
|
173
|
+
// one folder per skill, containing a SKILL.md with `name` + `description`
|
|
174
|
+
// frontmatter. Freebuff, GitHub Copilot CLI, and Antigravity all read
|
|
175
|
+
// this format. The body is identical to the slash-command version
|
|
176
|
+
// (companions included) so every runtime enforces the same disciplines;
|
|
177
|
+
// only the frontmatter gains the required `name` field.
|
|
178
|
+
export function skillBundleContent(skill, allSkills = []) {
|
|
179
|
+
const command = skillCommandContent(skill, allSkills);
|
|
180
|
+
return command.replace(
|
|
181
|
+
`---\ndescription: `,
|
|
182
|
+
`---\nname: ${skill.name}\ndescription: `
|
|
183
|
+
);
|
|
184
|
+
}
|
package/lib/sync.js
CHANGED
|
@@ -27,7 +27,7 @@ import { join, dirname } from "node:path";
|
|
|
27
27
|
import { loadConfig } from "./config.js";
|
|
28
28
|
import { adapters, supportedRuntimes } from "./adapters/index.js";
|
|
29
29
|
import { validateConfig } from "./validate.js";
|
|
30
|
-
import { loadSkills, skillCommandContent } from "./skills.js";
|
|
30
|
+
import { loadSkills, skillCommandContent, skillBundleContent } from "./skills.js";
|
|
31
31
|
|
|
32
32
|
// Write one file the safe way. Returns what happened:
|
|
33
33
|
// dry-run → { dryRun: true, changed } · no change → { unchanged: true }
|
|
@@ -86,9 +86,26 @@ export function sync({ targetDir, dryRun = false }) {
|
|
|
86
86
|
continue;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
// Instruction-file runtimes (OpenCode, Claude, Gemini, Cline):
|
|
90
|
+
// one rendered rules file per runtime.
|
|
91
|
+
if (typeof adapter.targetFile === "function") {
|
|
92
|
+
const rel = adapter.targetFile();
|
|
93
|
+
const outcome = writeSafe(join(targetDir, rel), adapter.renderInstructions(config), dryRun);
|
|
94
|
+
results.push({ runtime: name, file: rel, ...outcome });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Skill-bundle runtimes (Freebuff, Copilot CLI, Antigravity):
|
|
98
|
+
// one <skill>/SKILL.md per enabled skill, following the Agent
|
|
99
|
+
// Skills open standard (agentskills.io).
|
|
100
|
+
if (typeof adapter.skillBundleDir === "function") {
|
|
101
|
+
const files = [];
|
|
102
|
+
for (const skill of skills) {
|
|
103
|
+
const rel = `${adapter.skillBundleDir()}/${skill.name}/SKILL.md`;
|
|
104
|
+
const content = skillBundleContent(skill, skills);
|
|
105
|
+
files.push({ file: rel, ...writeSafe(join(targetDir, rel), content, dryRun) });
|
|
106
|
+
}
|
|
107
|
+
results.push({ runtime: name, bundleDir: adapter.skillBundleDir(), skillBundles: files });
|
|
108
|
+
}
|
|
92
109
|
}
|
|
93
110
|
|
|
94
111
|
// SLASH COMMANDS: one per enabled skill, for the two runtimes whose
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"bin": {
|
|
4
4
|
"reviewer-harness": "./bin/cli.js"
|
|
5
5
|
},
|
|
6
|
-
"description": "Your own AI harness
|
|
6
|
+
"description": "Your own AI harness — one master config that syncs guardrail rules, skills, and a local memory to Claude, OpenCode, Gemini, Cline, Freebuff, Copilot CLI, and Antigravity. No API key, no dependencies.",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=18"
|
|
9
9
|
},
|
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
"opencode",
|
|
15
15
|
"claude-code",
|
|
16
16
|
"gemini",
|
|
17
|
-
"cline"
|
|
17
|
+
"cline",
|
|
18
|
+
"freebuff",
|
|
19
|
+
"copilot-cli",
|
|
20
|
+
"antigravity"
|
|
18
21
|
],
|
|
19
22
|
"license": "MIT",
|
|
20
23
|
"name": "reviewer-harness",
|
|
@@ -23,7 +26,7 @@
|
|
|
23
26
|
"url": "https://github.com/xcell321/reviewer-harness.git"
|
|
24
27
|
},
|
|
25
28
|
"type": "module",
|
|
26
|
-
"version": "1.
|
|
29
|
+
"version": "1.5.0",
|
|
27
30
|
"files": [
|
|
28
31
|
"bin",
|
|
29
32
|
"lib",
|
package/skills/safe-editing.md
CHANGED
|
@@ -12,5 +12,6 @@ When to use: every change to existing code.
|
|
|
12
12
|
- Do not reformat, reorder, or rename unrelated lines.
|
|
13
13
|
- Code as a seasoned senior developer with many years of experience shipping real products — websites and mobile apps deployed to production: clear names, small focused functions, no duplication, no clever hacks, edge cases handled, production-ready and maintainable — every change must be easy to understand at a glance.
|
|
14
14
|
- Keep the code well structured: separate concerns into files and modules with clear organization and placement. Never leave spaghetti code behind — if the change touches messy code, refactor that part (and only that part) so the result stays clean.
|
|
15
|
+
- Never hardcode: values that can change — URLs, endpoints, file paths, credentials, API keys, user-specific data, magic numbers — must come from config, environment variables, parameters, or named constants, never buried as literals in code. Secrets must never be hardcoded, ever.
|
|
15
16
|
- If another file truly must change too, say why before doing it.
|
|
16
17
|
- Afterwards, show the diff and describe in one sentence what changed.
|