reviewer-harness 1.1.0 → 1.3.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 +32 -11
- package/bin/cli.js +60 -19
- package/lib/skills.js +62 -11
- package/lib/sync.js +49 -32
- package/package.json +1 -1
- package/skills/clone.md +16 -0
package/README.md
CHANGED
|
@@ -176,7 +176,11 @@ Reusable playbooks, enabled per project:
|
|
|
176
176
|
- `skills add <name>` — install into the project and enable it in the config
|
|
177
177
|
- `skills remove <name>` — disable it
|
|
178
178
|
|
|
179
|
-
Bundled: `safe-editing`, `no-hallucination`, `debug-first`, `explain-bisaya`. Enabled skills render below the rules in every instruction file.
|
|
179
|
+
Bundled: `safe-editing`, `no-hallucination`, `debug-first`, `explain-bisaya`, `clone`. Enabled skills render below the rules in every instruction file — and sync also generates a **slash command** for each one (`.opencode/commands/<name>.md` and `.claude/commands/<name>.md`), so every enabled skill appears in the `/` menu of OpenCode and Claude, ready to invoke on a specific task: `/safe-editing`, `/no-hallucination`, and so on.
|
|
180
|
+
|
|
181
|
+
**Every project, instantly:** want the skills in the `/` menu of *every* project, with no per-project setup? Run `reviewer-harness global` once — it installs all bundled skills as slash commands into the global OpenCode (`~/.config/opencode/commands`) and Claude (`~/.claude/commands`) folders. From then on, they are in the `/` menu of any project you open. (Project rules still come from `sync` inside a project.)
|
|
182
|
+
|
|
183
|
+
**A built-in pairing:** the `/safe-editing` command always carries the `no-hallucination` playbook inside it as a companion section — one call, both disciplines, so the AI that edits can never invent the code it edits with.
|
|
180
184
|
|
|
181
185
|
## Sub-agents and Slash Commands
|
|
182
186
|
|
|
@@ -190,20 +194,37 @@ One master definition in the harness, generated into each runtime's format:
|
|
|
190
194
|
|
|
191
195
|
## Command reference
|
|
192
196
|
|
|
193
|
-
Requires Node.js 18+ (no dependencies).
|
|
197
|
+
Requires Node.js 18+ (no dependencies). Run these inside your project — every command works on the current directory unless you add `--target <dir>`.
|
|
198
|
+
|
|
199
|
+
**Start & everyday**
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
reviewer-harness init # one-time: create the master config
|
|
203
|
+
reviewer-harness validate # check the config (quality + secret scan)
|
|
204
|
+
reviewer-harness sync --dry-run # preview the changes, writes nothing
|
|
205
|
+
reviewer-harness sync # apply the rules to every runtime
|
|
206
|
+
reviewer-harness status # show the config + target files
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Skills, agents, commands**
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
reviewer-harness skills list
|
|
213
|
+
reviewer-harness skills add safe-editing
|
|
214
|
+
reviewer-harness skills remove safe-editing
|
|
215
|
+
reviewer-harness agents add reviewer
|
|
216
|
+
reviewer-harness commands add review
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Local memory**
|
|
194
220
|
|
|
195
221
|
```bash
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
node bin/cli.js sync --target ./my-project
|
|
200
|
-
node bin/cli.js memory init --target ./my-project
|
|
201
|
-
node bin/cli.js skills add safe-editing --target ./my-project
|
|
202
|
-
node bin/cli.js agents add reviewer --target ./my-project
|
|
203
|
-
node bin/cli.js commands add review --target ./my-project
|
|
222
|
+
reviewer-harness memory init # start the memory/ vault
|
|
223
|
+
reviewer-harness memory list # list notes + their links
|
|
224
|
+
reviewer-harness memory index # rebuild INDEX.md + graph.json
|
|
204
225
|
```
|
|
205
226
|
|
|
206
|
-
|
|
227
|
+
Running from a source checkout instead? The same commands work as `node bin/cli.js <command>`.
|
|
207
228
|
|
|
208
229
|
## File map
|
|
209
230
|
|
package/bin/cli.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// reviewer-harness — CLI: sync engine, guardrail validation, local memory,
|
|
3
3
|
// skills, sub-agents, and slash commands.
|
|
4
|
-
// Commands: status | sync | init | validate | memory | skills | agents | commands
|
|
4
|
+
// Commands: status | sync | init | validate | memory | skills | agents | commands | global
|
|
5
5
|
|
|
6
6
|
import { parseArgs } from "node:util";
|
|
7
7
|
import { existsSync } from "node:fs";
|
|
8
8
|
import { join } from "node:path";
|
|
9
|
+
import { homedir } from "node:os";
|
|
9
10
|
import { loadConfig } from "../lib/config.js";
|
|
10
11
|
import { adapters, supportedRuntimes } from "../lib/adapters/index.js";
|
|
11
|
-
import { sync } from "../lib/sync.js";
|
|
12
|
+
import { sync, writeSafe } from "../lib/sync.js";
|
|
12
13
|
import { initProject } from "../lib/init.js";
|
|
13
14
|
import { initMemory, buildIndex, readNotes, memoryDir } from "../lib/memory.js";
|
|
14
15
|
import { validateConfig } from "../lib/validate.js";
|
|
15
|
-
import { listBundledSkills, addSkill, removeSkill } from "../lib/skills.js";
|
|
16
|
+
import { listBundledSkills, addSkill, removeSkill, loadBundledSkills, skillCommandContent } from "../lib/skills.js";
|
|
16
17
|
import { listBundledAgents, addAgent } from "../lib/agents.js";
|
|
17
18
|
import { listBundledCommands, addCommand } from "../lib/commands.js";
|
|
18
19
|
|
|
@@ -30,21 +31,27 @@ const dryRun = values["dry-run"];
|
|
|
30
31
|
function help() {
|
|
31
32
|
console.log(`reviewer-harness — your own AI harness
|
|
32
33
|
|
|
33
|
-
Usage:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
34
|
+
Usage (run inside your project):
|
|
35
|
+
reviewer-harness init Create harness.config.json (one-time setup)
|
|
36
|
+
reviewer-harness validate Check the config: quality + secret scanning
|
|
37
|
+
reviewer-harness sync [--dry-run] Sync the rules to every runtime (--dry-run = preview only)
|
|
38
|
+
reviewer-harness status Show the config + target files
|
|
39
|
+
|
|
40
|
+
reviewer-harness skills list List bundled skills (+ which are enabled)
|
|
41
|
+
reviewer-harness skills add <name> Install + enable a skill in the project
|
|
42
|
+
reviewer-harness skills remove <name> Disable + remove a skill
|
|
43
|
+
reviewer-harness agents list List bundled sub-agents
|
|
44
|
+
reviewer-harness agents add <name> Install a sub-agent (OpenCode + Claude)
|
|
45
|
+
reviewer-harness commands list List bundled slash commands
|
|
46
|
+
reviewer-harness commands add <name> Install a slash command (OpenCode + Claude)
|
|
47
|
+
|
|
48
|
+
reviewer-harness global [--dry-run] Install EVERY bundled skill as a slash command for ALL projects (global OpenCode + Claude "/" menus)
|
|
49
|
+
|
|
50
|
+
reviewer-harness memory init Start the local memory (memory/ folder)
|
|
51
|
+
reviewer-harness memory index Rebuild the memory INDEX.md + graph.json
|
|
52
|
+
reviewer-harness memory list List memory notes + their links
|
|
53
|
+
|
|
54
|
+
Every command accepts --target <dir> (default: the current directory).
|
|
48
55
|
`);
|
|
49
56
|
}
|
|
50
57
|
|
|
@@ -64,7 +71,16 @@ function cmdSync() {
|
|
|
64
71
|
const results = sync({ targetDir, dryRun });
|
|
65
72
|
if (dryRun) console.log("(dry-run: nothing was written)\n");
|
|
66
73
|
for (const r of results) {
|
|
67
|
-
if (r.
|
|
74
|
+
if (r.skillCommands) {
|
|
75
|
+
const written = r.skillCommands.filter((f) => f.written).length;
|
|
76
|
+
const pending = r.skillCommands.filter((f) => f.dryRun && f.changed).length;
|
|
77
|
+
const unchanged = r.skillCommands.filter((f) => f.unchanged || (f.dryRun && !f.changed)).length;
|
|
78
|
+
if (dryRun) {
|
|
79
|
+
console.log(`- skill commands: ${pending} would be written/updated, ${unchanged} unchanged (they appear in the "/" menu of OpenCode and Claude)`);
|
|
80
|
+
} else {
|
|
81
|
+
console.log(`- skill commands: ${written} written ✓, ${unchanged} unchanged (one per skill, in the "/" menu of OpenCode and Claude)`);
|
|
82
|
+
}
|
|
83
|
+
} else if (r.skipped) {
|
|
68
84
|
console.log(`- ${r.runtime}: SKIP — ${r.skipped}`);
|
|
69
85
|
} else if (r.dryRun) {
|
|
70
86
|
console.log(`- ${r.runtime} → ${r.file}: ${r.changed ? "WOULD WRITE (changes pending)" : "no changes"}`);
|
|
@@ -192,6 +208,30 @@ function cmdCommands() {
|
|
|
192
208
|
}
|
|
193
209
|
}
|
|
194
210
|
|
|
211
|
+
function cmdGlobal() {
|
|
212
|
+
const skills = loadBundledSkills();
|
|
213
|
+
const targets = [
|
|
214
|
+
{ runtime: "OpenCode", dir: join(homedir(), ".config", "opencode", "commands") },
|
|
215
|
+
{ runtime: "Claude", dir: join(homedir(), ".claude", "commands") },
|
|
216
|
+
];
|
|
217
|
+
if (dryRun) console.log("(dry-run: nothing was written)\n");
|
|
218
|
+
for (const { runtime, dir } of targets) {
|
|
219
|
+
let written = 0, unchanged = 0, pending = 0;
|
|
220
|
+
for (const skill of skills) {
|
|
221
|
+
const outcome = writeSafe(join(dir, `${skill.name}.md`), skillCommandContent(skill, skills), dryRun);
|
|
222
|
+
if (outcome.written) written++;
|
|
223
|
+
else if (outcome.dryRun && outcome.changed) pending++;
|
|
224
|
+
else unchanged++;
|
|
225
|
+
}
|
|
226
|
+
if (dryRun) console.log(`- ${runtime} (global): ${pending} would be written/updated, ${unchanged} unchanged → ${dir}`);
|
|
227
|
+
else console.log(`- ${runtime} (global): ${written} written ✓, ${unchanged} unchanged → ${dir}`);
|
|
228
|
+
}
|
|
229
|
+
console.log(
|
|
230
|
+
`\nDone: ${skills.map((s) => "/" + s.name).join(", ")} are now in the "/" menu of EVERY project.` +
|
|
231
|
+
`\n(Per-project rules still come from "sync" inside a project.)`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
195
235
|
async function main() {
|
|
196
236
|
if (!command || command === "status") cmdStatus();
|
|
197
237
|
else if (command === "sync") cmdSync();
|
|
@@ -201,6 +241,7 @@ async function main() {
|
|
|
201
241
|
else if (command === "skills") cmdSkills();
|
|
202
242
|
else if (command === "agents") cmdAgents();
|
|
203
243
|
else if (command === "commands") cmdCommands();
|
|
244
|
+
else if (command === "global") cmdGlobal();
|
|
204
245
|
else help();
|
|
205
246
|
}
|
|
206
247
|
|
package/lib/skills.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// lib/skills.js — LESSON 4: SKILLS SYSTEM.
|
|
2
2
|
//
|
|
3
|
-
// Rules are short laws. SKILLS are deeper playbooks for specific work
|
|
4
|
-
//
|
|
5
|
-
// per project.
|
|
3
|
+
// Rules are short laws. SKILLS are deeper playbooks for specific work:
|
|
4
|
+
// reusable instruction bundles you enable per project.
|
|
6
5
|
//
|
|
7
6
|
// skills/<name>.md — bundled in the harness (the "store")
|
|
8
7
|
// <project>/skills/ — the installed copy in your project
|
|
@@ -27,6 +26,26 @@ import { parseFrontmatter } from "./frontmatter.js";
|
|
|
27
26
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
28
27
|
const BUNDLED_DIR = join(ROOT, "skills");
|
|
29
28
|
|
|
29
|
+
// The bundled skills in full (name + description + body) — used by
|
|
30
|
+
// the "global" command, which installs them as slash commands for
|
|
31
|
+
// every project on the machine.
|
|
32
|
+
export function loadBundledSkills() {
|
|
33
|
+
if (!existsSync(BUNDLED_DIR)) return [];
|
|
34
|
+
return readdirSync(BUNDLED_DIR)
|
|
35
|
+
.filter((f) => f.endsWith(".md"))
|
|
36
|
+
.sort()
|
|
37
|
+
.map((file) => {
|
|
38
|
+
const { data, body } = parseFrontmatter(
|
|
39
|
+
readFileSync(join(BUNDLED_DIR, file), "utf8")
|
|
40
|
+
);
|
|
41
|
+
return {
|
|
42
|
+
name: file.replace(/\.md$/, ""),
|
|
43
|
+
description: data.description ?? "",
|
|
44
|
+
body: body.trim(),
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
30
49
|
export function listBundledSkills() {
|
|
31
50
|
if (!existsSync(BUNDLED_DIR)) return [];
|
|
32
51
|
return readdirSync(BUNDLED_DIR)
|
|
@@ -101,19 +120,51 @@ export function removeSkill(name, targetDir) {
|
|
|
101
120
|
return { wasEnabled, removedFile };
|
|
102
121
|
}
|
|
103
122
|
|
|
104
|
-
// Read the enabled skills
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
export function
|
|
108
|
-
const
|
|
123
|
+
// Read the enabled skills in full (name + description + body) so sync
|
|
124
|
+
// can render them into the instruction files AND generate a slash
|
|
125
|
+
// command for each one.
|
|
126
|
+
export function loadSkills(targetDir, config) {
|
|
127
|
+
const skills = [];
|
|
109
128
|
for (const name of config.skills ?? []) {
|
|
110
129
|
const file = join(targetDir, "skills", `${name}.md`);
|
|
111
130
|
if (!existsSync(file))
|
|
112
131
|
throw new Error(
|
|
113
132
|
`Skill "${name}" is enabled in the config but has no file in skills/ — run "skills add ${name}".`
|
|
114
133
|
);
|
|
115
|
-
const { body } = parseFrontmatter(readFileSync(file, "utf8"));
|
|
116
|
-
|
|
134
|
+
const { data, body } = parseFrontmatter(readFileSync(file, "utf8"));
|
|
135
|
+
skills.push({ name, description: data.description ?? "", body: body.trim() });
|
|
136
|
+
}
|
|
137
|
+
return skills;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Enabled but no file? loadSkills throws a clear error, never a
|
|
141
|
+
// silent skip: a strict harness never guesses.
|
|
142
|
+
export function loadSkillSections(targetDir, config) {
|
|
143
|
+
return loadSkills(targetDir, config).map((s) => s.body);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// The slash-command file generated for a skill, so the skill shows up
|
|
147
|
+
// in the runtime's "/" menu (OpenCode and Claude list the files in
|
|
148
|
+
// their commands folders). Invoking /<skill> hands the playbook to
|
|
149
|
+
// the AI together with whatever the user types after it.
|
|
150
|
+
// Companion pairings: when one of these skills is rendered as a slash
|
|
151
|
+
// command, the companion's playbook is embedded in the same file —
|
|
152
|
+
// so a single call always carries both disciplines. safe-editing
|
|
153
|
+
// pairs with no-hallucination: the AI that edits must never invent
|
|
154
|
+
// the code it edits with.
|
|
155
|
+
const COMPANIONS = { "safe-editing": ["no-hallucination"] };
|
|
156
|
+
|
|
157
|
+
export function skillCommandContent(skill, allSkills = []) {
|
|
158
|
+
let content =
|
|
159
|
+
`---\ndescription: ${skill.description}\n---\n\n` + `${skill.body}\n`;
|
|
160
|
+
for (const companionName of COMPANIONS[skill.name] ?? []) {
|
|
161
|
+
const companion = allSkills.find((s) => s.name === companionName);
|
|
162
|
+
if (companion) {
|
|
163
|
+
content +=
|
|
164
|
+
`\n---\n\n## Companion playbook: ${companion.name} ` +
|
|
165
|
+
`(always paired with ${skill.name})\n\n${companion.body}\n`;
|
|
166
|
+
}
|
|
117
167
|
}
|
|
118
|
-
|
|
168
|
+
content += `\nApply this playbook to the following: $ARGUMENTS\n`;
|
|
169
|
+
return content;
|
|
119
170
|
}
|
package/lib/sync.js
CHANGED
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
// 3. BACKUP — before overwriting an existing file, make a .bak copy
|
|
9
9
|
// 4. SKIP — if nothing changed, touch nothing (idempotent)
|
|
10
10
|
//
|
|
11
|
+
// Sync also generates one SLASH COMMAND per enabled skill (into the
|
|
12
|
+
// OpenCode and Claude commands folders), so every skill appears in
|
|
13
|
+
// the runtime's "/" menu and can be invoked on demand.
|
|
14
|
+
//
|
|
11
15
|
// This is the harness's first guardrail: the harness itself cannot
|
|
12
16
|
// destroy your files — the same protection it gives you against an
|
|
13
17
|
// AI breaking your code.
|
|
@@ -23,7 +27,33 @@ import { join, dirname } from "node:path";
|
|
|
23
27
|
import { loadConfig } from "./config.js";
|
|
24
28
|
import { adapters, supportedRuntimes } from "./adapters/index.js";
|
|
25
29
|
import { validateConfig } from "./validate.js";
|
|
26
|
-
import {
|
|
30
|
+
import { loadSkills, skillCommandContent } from "./skills.js";
|
|
31
|
+
|
|
32
|
+
// Write one file the safe way. Returns what happened:
|
|
33
|
+
// dry-run → { dryRun: true, changed } · no change → { unchanged: true }
|
|
34
|
+
// changed → { written: true, backup } (with a .bak copy when the
|
|
35
|
+
// file already existed). Every real write is verified by reading
|
|
36
|
+
// the file back.
|
|
37
|
+
export function writeSafe(abs, content, dryRun) {
|
|
38
|
+
if (dryRun) {
|
|
39
|
+
const existing = existsSync(abs) ? readFileSync(abs, "utf8") : null;
|
|
40
|
+
return { dryRun: true, changed: existing !== content };
|
|
41
|
+
}
|
|
42
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
43
|
+
if (existsSync(abs)) {
|
|
44
|
+
const old = readFileSync(abs, "utf8");
|
|
45
|
+
if (old === content) return { unchanged: true };
|
|
46
|
+
copyFileSync(abs, abs + ".bak");
|
|
47
|
+
writeFileSync(abs, content);
|
|
48
|
+
if (readFileSync(abs, "utf8") !== content)
|
|
49
|
+
throw new Error(`Failed to write ${abs}`);
|
|
50
|
+
return { written: true, backup: true };
|
|
51
|
+
}
|
|
52
|
+
writeFileSync(abs, content);
|
|
53
|
+
if (readFileSync(abs, "utf8") !== content)
|
|
54
|
+
throw new Error(`Failed to write ${abs}`);
|
|
55
|
+
return { written: true, backup: false };
|
|
56
|
+
}
|
|
27
57
|
|
|
28
58
|
export function sync({ targetDir, dryRun = false }) {
|
|
29
59
|
const config = loadConfig(targetDir);
|
|
@@ -39,8 +69,11 @@ export function sync({ targetDir, dryRun = false }) {
|
|
|
39
69
|
);
|
|
40
70
|
}
|
|
41
71
|
|
|
42
|
-
// LESSON 4: attach the enabled skills — the adapters render them
|
|
43
|
-
|
|
72
|
+
// LESSON 4: attach the enabled skills — the adapters render them
|
|
73
|
+
// into the instruction files, and each one also becomes a slash
|
|
74
|
+
// command below.
|
|
75
|
+
const skills = loadSkills(targetDir, config);
|
|
76
|
+
config.skillSections = skills.map((s) => s.body);
|
|
44
77
|
|
|
45
78
|
const results = [];
|
|
46
79
|
|
|
@@ -54,38 +87,22 @@ export function sync({ targetDir, dryRun = false }) {
|
|
|
54
87
|
}
|
|
55
88
|
|
|
56
89
|
const rel = adapter.targetFile();
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// DRY-RUN: read only, never write. "Look before you agree."
|
|
61
|
-
if (dryRun) {
|
|
62
|
-
const existing = existsSync(abs) ? readFileSync(abs, "utf8") : null;
|
|
63
|
-
results.push({ runtime: name, file: rel, dryRun: true, changed: existing !== content });
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
mkdirSync(dirname(abs), { recursive: true });
|
|
90
|
+
const outcome = writeSafe(join(targetDir, rel), adapter.renderInstructions(config), dryRun);
|
|
91
|
+
results.push({ runtime: name, file: rel, ...outcome });
|
|
92
|
+
}
|
|
68
93
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
94
|
+
// SLASH COMMANDS: one per enabled skill, for the two runtimes whose
|
|
95
|
+
// "/" menu lists command files (OpenCode and Claude).
|
|
96
|
+
if (skills.length) {
|
|
97
|
+
const files = [];
|
|
98
|
+
for (const skill of skills) {
|
|
99
|
+
const content = skillCommandContent(skill, skills);
|
|
100
|
+
for (const dir of [".opencode/commands", ".claude/commands"]) {
|
|
101
|
+
const rel = `${dir}/${skill.name}.md`;
|
|
102
|
+
files.push({ file: rel, ...writeSafe(join(targetDir, rel), content, dryRun) });
|
|
75
103
|
}
|
|
76
|
-
// About to overwrite? BACK UP first — protection against loss.
|
|
77
|
-
copyFileSync(abs, abs + ".bak");
|
|
78
|
-
writeFileSync(abs, content);
|
|
79
|
-
results.push({ runtime: name, file: rel, written: true, backup: true });
|
|
80
|
-
} else {
|
|
81
|
-
writeFileSync(abs, content);
|
|
82
|
-
results.push({ runtime: name, file: rel, written: true, backup: false });
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Verify the write actually landed (read it back).
|
|
86
|
-
if (!dryRun && readFileSync(abs, "utf8") !== content) {
|
|
87
|
-
throw new Error(`Failed to write ${abs}`);
|
|
88
104
|
}
|
|
105
|
+
results.push({ skillCommands: files });
|
|
89
106
|
}
|
|
90
107
|
|
|
91
108
|
return results;
|
package/package.json
CHANGED
package/skills/clone.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clone
|
|
3
|
+
description: Fully clone a website's design — layout, components, animations, borders — while replacing all text and assets with your own.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Skill: clone
|
|
7
|
+
|
|
8
|
+
When to use: the user asks to clone a website or design, and gives a URL or reference to copy.
|
|
9
|
+
|
|
10
|
+
- Clone the DESIGN, replace the CONTENT. That is the whole rule: everything visual may be reproduced, everything owned must be replaced.
|
|
11
|
+
- First, inventory the target: sections, components, layout grid, colors, fonts, spacing, borders, and every animation (type, duration, easing, trigger). Write the inventory down before writing any code.
|
|
12
|
+
- Rebuild from the inventory with original code: reproduce the look and motion, never paste the target's source code.
|
|
13
|
+
- Replace every owned thing: all text (headings, skills, descriptions, projects), all image files, logos, and brand names become the user's own content or placeholders. Image placement and sizes may be copied; image files may not.
|
|
14
|
+
- Front-end only: a clone reproduces the look, never the backend — no login, database, or dynamic data.
|
|
15
|
+
- If the target is the user's own site, their own assets may be reused.
|
|
16
|
+
- Afterwards, validate: compare screenshots side by side, check responsive breakpoints, and audit that no third-party image, text, or brand remains. Report what was cloned and what was replaced.
|