dsh-clone 0.0.0-stage → 0.2.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/ACCEPTABLE-USE.md +28 -0
- package/CHANGELOG.md +96 -0
- package/LICENSE +21 -0
- package/README.md +260 -2
- package/client.js +690 -0
- package/cordis.patch.yml +9 -0
- package/docs/assets/icon-180.png +0 -0
- package/icon.svg +29 -0
- package/index.js +626 -0
- package/index.json +75 -0
- package/lib/engine.js +999 -0
- package/lib/parse.js +58 -0
- package/lib/png.js +175 -0
- package/lib/preview.js +141 -0
- package/lib/prompts.js +118 -0
- package/lib/rank.js +72 -0
- package/lib/render.js +148 -0
- package/lib/score-lane.js +38 -0
- package/lib/score.js +282 -0
- package/lib/util.js +85 -0
- package/locale/en.json +6 -0
- package/locale/ru.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +73 -4
- package/screenshots.json +6 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Acceptable use
|
|
2
|
+
|
|
3
|
+
`dsh-clone` rebuilds an interface from a picture. That is a normal design and engineering workflow, and it is also easy to misuse. This page is short on purpose.
|
|
4
|
+
|
|
5
|
+
## Fine
|
|
6
|
+
|
|
7
|
+
- Rebuilding **your own** designs, screenshots, mockups, or an interface you are migrating from.
|
|
8
|
+
- Prototyping: "make it look like this" is a legitimate way to describe intent.
|
|
9
|
+
- Internal tools, throwaway experiments, teaching, and design exploration.
|
|
10
|
+
- Recreating a **layout pattern** (a pricing table, a dashboard grid) — layout is not owned by anyone.
|
|
11
|
+
- Cloning a screen you have written permission to clone, or one published under a permissive licence.
|
|
12
|
+
|
|
13
|
+
## Not fine
|
|
14
|
+
|
|
15
|
+
- Cloning a third-party product's interface to **ship as your own product**, or to pass it off as the original.
|
|
16
|
+
- Reproducing a competitor's brand: logo, wordmark, illustrations, mascots, or photography you do not hold rights to.
|
|
17
|
+
- Cloning a screen in order to bypass a paywall, a login, or any access control.
|
|
18
|
+
- Building phishing look-alikes of banking, wallet, exchange, or identity-provider screens. This is the one case where the author will publicly recommend against the plugin's use, and the one case where a bug report will be closed without discussion.
|
|
19
|
+
|
|
20
|
+
## Practical guidance
|
|
21
|
+
|
|
22
|
+
The builders are instructed to approximate photos and illustrations with CSS and SVG, so the output is a **structural** copy rather than a pixel copy. Keep it that way: if your result would be indistinguishable from someone else's shipped product, that is the signal to stop and take the layout idea rather than the identity.
|
|
23
|
+
|
|
24
|
+
When in doubt, ask: *if the original's team saw this, would they call it inspiration or impersonation?*
|
|
25
|
+
|
|
26
|
+
## Reporting
|
|
27
|
+
|
|
28
|
+
If you believe dsh-clone is being used in a way this page forbids, open an issue with the repository and a description. Do not include credentials, private screenshots, or personal data.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0 — 2026-11-09
|
|
4
|
+
|
|
5
|
+
Everything in this release is about the plugin being right when it is left alone.
|
|
6
|
+
|
|
7
|
+
**Scoring is now a real measurement**
|
|
8
|
+
|
|
9
|
+
- colour comparison forgives a small uniform shift, so a 4px offset is not a
|
|
10
|
+
different design while a misplaced panel still costs real points;
|
|
11
|
+
- a separate **structure** term compares where the detail sits (softened edge
|
|
12
|
+
energy, normalised by the reference), so a flat block that happens to average
|
|
13
|
+
to the right colour no longer scores as a faithful rebuild;
|
|
14
|
+
- published runs re-measured on the same artifacts: dashboard 91.9%, landing
|
|
15
|
+
91.9%, billing card 97.8%, sign-in 79%. Two unrelated screens now score 24-58%
|
|
16
|
+
instead of ~89%, which is the whole point of having a number.
|
|
17
|
+
|
|
18
|
+
**The engine survives being interrupted**
|
|
19
|
+
|
|
20
|
+
- each clone writes `state.json` next to its work; `status` and `pick` restore
|
|
21
|
+
clones from disk after a plugin reload or a restart;
|
|
22
|
+
- a lane that was building when the plugin went away keeps whatever it had
|
|
23
|
+
already written, and is reported as interrupted rather than finished;
|
|
24
|
+
- unloading the plugin now interrupts its builder agents instead of leaving them
|
|
25
|
+
spending tokens for a result nobody can reach.
|
|
26
|
+
|
|
27
|
+
**New pass: iterate instead of restarting**
|
|
28
|
+
|
|
29
|
+
- `/clone refine [lane] [change]` copies the adopted page into a fresh
|
|
30
|
+
single-lane clone and asks for the smallest edit that satisfies one change;
|
|
31
|
+
the reference and viewport are unchanged, so the result reports a score delta.
|
|
32
|
+
|
|
33
|
+
**Budget**
|
|
34
|
+
|
|
35
|
+
- `laneTokenBudget` stops a lane that burns past its ceiling and keeps whatever it
|
|
36
|
+
delivered. Off by default: an agent re-sends its context every step, so a naive
|
|
37
|
+
ceiling stops honest builds mid-analysis — set it deliberately as a backstop.
|
|
38
|
+
|
|
39
|
+
**A nudge instead of a wasted run**
|
|
40
|
+
|
|
41
|
+
- a lane past 12 tool calls with no `index.html` on disk gets one steering
|
|
42
|
+
message telling it to stop measuring and write the page. Observed failure that
|
|
43
|
+
motivated it: a builder spent every call sampling pixels and ended with an
|
|
44
|
+
empty directory. The prompts now also forbid image-measurement scripts
|
|
45
|
+
outright.
|
|
46
|
+
|
|
47
|
+
**Robustness**
|
|
48
|
+
|
|
49
|
+
- a rendered screenshot must decode before it is scored; a truncated capture is
|
|
50
|
+
retried instead of producing a garbage number;
|
|
51
|
+
- the renderer finds Chrome/Chromium/Edge/Brave on Windows as well as macOS and
|
|
52
|
+
Linux;
|
|
53
|
+
- `status` accepts a `clone_id`, so an older clone stays inspectable.
|
|
54
|
+
|
|
55
|
+
## 0.1.0 — 2026-11-09
|
|
56
|
+
|
|
57
|
+
## 0.1.0 — 2026-11-09
|
|
58
|
+
|
|
59
|
+
First release.
|
|
60
|
+
|
|
61
|
+
**Core**
|
|
62
|
+
|
|
63
|
+
- `/clone [lanes] [note]` starts 1-4 builder agents on the last image in the
|
|
64
|
+
conversation (or an image path in the note), plus `status`, `pick <lane>`,
|
|
65
|
+
`cancel` and `discard`; the same operations are exposed as the `clone` tool.
|
|
66
|
+
- Lane isolation: a real `git worktree` on its own branch inside a repository,
|
|
67
|
+
a plain directory otherwise. `node_modules`, `.venv` and `vendor` are shared by
|
|
68
|
+
symlink, and `.dsh-clone/` is added to `.git/info/exclude`.
|
|
69
|
+
- Clone panel in the conversation view ring: the reference beside every lane's
|
|
70
|
+
live page, with strategy, timing, tool calls, file count and token spend.
|
|
71
|
+
- Adoption copies only deliverable files (no `.git`, no symlinked dependencies,
|
|
72
|
+
no dotted scratch directories) into `clone-<id>-lane-<n>/`.
|
|
73
|
+
- Lane pages are served from the page's own origin under a `sandbox` CSP and
|
|
74
|
+
mounted in a `sandbox="allow-scripts"` iframe.
|
|
75
|
+
|
|
76
|
+
**Scoring**
|
|
77
|
+
|
|
78
|
+
- Delivered lanes are screenshotted by an installed Chromium-family browser and
|
|
79
|
+
compared with the reference on a coarse colour grid; the highest score
|
|
80
|
+
suggests the winner. No browser means no score, and nothing else changes.
|
|
81
|
+
- Viewport defaults to the reference image's own pixel size.
|
|
82
|
+
- Per-lane wall-clock limit: a builder that keeps polishing is stopped and its
|
|
83
|
+
finished page is delivered and scored.
|
|
84
|
+
|
|
85
|
+
**Distribution**
|
|
86
|
+
|
|
87
|
+
- Format-1 plugin market index (`index.json`), validated with the official
|
|
88
|
+
`parseMarketIndex`, so the plugin installs from a Discover tab without npm.
|
|
89
|
+
- Storefront icon (`docs/assets/icon-180.png`), `screenshots.json`, GitHub
|
|
90
|
+
release tarball, and a ready-to-submit awesome-dsh-plugin entry.
|
|
91
|
+
|
|
92
|
+
**Tests**
|
|
93
|
+
|
|
94
|
+
- 30 unit tests (grammar, ranking, scoring, PNG decoding, engine lifecycle), a
|
|
95
|
+
real `git worktree` isolation test, and a render-and-score integration test
|
|
96
|
+
that drives the plugin's own scoring path.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Egor Fedorov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,261 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://egorfedorov.github.io/dsh-clone/"><img src="docs/assets/hero.png" alt="dsh-clone — screenshot in, working UI out" width="100%"></a>
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-4d6bff?style=flat-square"></a>
|
|
7
|
+
<img alt="DeepSeek Harness plugin" src="https://img.shields.io/badge/DeepSeek%20Harness-plugin-4d6bff?style=flat-square">
|
|
8
|
+
<img alt="Node 20+" src="https://img.shields.io/badge/node-%E2%89%A520-16202f?style=flat-square">
|
|
9
|
+
<a href="https://egorfedorov.github.io/dsh-clone/demo/"><img alt="Live demo" src="https://img.shields.io/badge/live-demo-22c55e?style=flat-square"></a>
|
|
10
|
+
<a href="https://github.com/egorfedorov/dsh-clone/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/egorfedorov/dsh-clone?style=flat-square&color=4d6bff"></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<h1 align="center">Screenshot in. Working UI out.</h1>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
Paste a screenshot of any interface. Run <code>/clone 3</code>. Three agents rebuild it in parallel,<br>
|
|
17
|
+
each in its own isolated directory. A live panel shows the reference beside every attempt,<br>
|
|
18
|
+
rendered and scored. You adopt the page that looks right — and it is already a working page.
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
<p align="center">
|
|
22
|
+
<a href="https://egorfedorov.github.io/dsh-clone/demo/"><b>→ Open a real clone in the browser</b></a>
|
|
23
|
+
<br>
|
|
24
|
+
<sub>built by this plugin from <a href="docs/assets/reference.png">this screenshot</a> · 92.7% picture match</sub>
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
<p align="center">
|
|
28
|
+
<img src="docs/assets/demo.gif" alt="The flow: the reference arrives, three lanes build in parallel, one is adopted and scored" width="100%">
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## The 30-second version
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
you: [paste a screenshot] /clone 3
|
|
37
|
+
me: ▸ three agents, three isolated directories, same reference
|
|
38
|
+
▸ live panel: reference | lane 1 | lane 2 | lane 3 (rendered + scored)
|
|
39
|
+
you: [Adopt this one]
|
|
40
|
+
me: ▸ ./clone-a91c-lane-2/index.html ← open it, it runs
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
No build step, no dependencies, no server. The adopted page is plain HTML, CSS and vanilla JS.
|
|
44
|
+
|
|
45
|
+
## Why this exists
|
|
46
|
+
|
|
47
|
+
A screenshot is the most precise specification a person can give. Every other input loses information: a paragraph drops the spacing rhythm, the type scale, the alignment of every block; a component list drops the picture.
|
|
48
|
+
|
|
49
|
+
The usual flow is still: describe it, get one attempt, describe the differences, get another attempt. One attempt is a lottery — you cannot tell whether the model misunderstood you or simply chose a different solution, because you only ever see one.
|
|
50
|
+
|
|
51
|
+
`dsh-clone` changes the cost of being wrong. Three or four builders see the same reference and work the same problem from different angles, every delivered page is rendered and compared with the reference, and you keep the best.
|
|
52
|
+
|
|
53
|
+
| Lane strategy | What that builder is told to do |
|
|
54
|
+
|---|---|
|
|
55
|
+
| **Measure first** | write a layout spec — regions, grid, spacing scale, sampled hex palette, type scale — then build strictly to it |
|
|
56
|
+
| **Structure first** | the whole page as semantic HTML first, then style region by region from the outside in |
|
|
57
|
+
| **Visual first** | reproduce the picture first — surfaces, type, spacing, silhouette — then add only the behaviour the image implies |
|
|
58
|
+
| **Component first** | find the 5-8 reused parts, define each once, compose the page from them |
|
|
59
|
+
|
|
60
|
+
## Demo
|
|
61
|
+
|
|
62
|
+
Real runs, not mocks: one command each, no retries, no hand edits. Every page is a static file — open it and it runs.
|
|
63
|
+
|
|
64
|
+
| Reference | The clone, rendered by the plugin | Result |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| <img src="docs/assets/nebula-dashboard.png" alt="reference observability dashboard" width="330"> | <img src="docs/assets/nebula-clone.png" alt="the dashboard clone, rendered" width="330"> | **91.9% match** · 1280×800 · two lanes raced, the better one adopted · [open it](https://egorfedorov.github.io/dsh-clone/demo/nebula/) |
|
|
67
|
+
| <img src="docs/assets/aperture-landing.png" alt="reference landing page" width="330"> | <img src="docs/assets/aperture-clone.png" alt="the landing clone, rendered" width="330"> | **91.9% match** · 1280×800 · one lane · [open it](https://egorfedorov.github.io/dsh-clone/demo/aperture/) |
|
|
68
|
+
| <img src="docs/assets/card-reference.png" alt="reference billing card" width="330"> | <img src="docs/assets/card-clone.png" alt="the billing card clone, rendered" width="330"> | **97.8% match** · 520×360 · one file · [open it](https://egorfedorov.github.io/dsh-clone/demo/card.html) |
|
|
69
|
+
| <img src="docs/assets/reference.png" alt="reference sign-in screen" width="330"> | <img src="demo/preview.png" alt="the sign-in clone, rendered" width="330"> | **79% match** · 900×620 · [open it](https://egorfedorov.github.io/dsh-clone/demo/) |
|
|
70
|
+
|
|
71
|
+
The dashboard and the landing page are the interesting ones: a sidebar, KPI cards with sparklines, a layered area chart, a donut, a data table; glass panels over a gradient mesh, a feature grid and a footer. Both were rebuilt from a single screenshot.
|
|
72
|
+
|
|
73
|
+
The score is the plugin's own, and it has three parts: both images are averaged into a coarse colour grid, the colour comparison forgives a small uniform shift, and a separate **structure** term compares where the detail sits. The mix measures *how much of the picture agrees*, not whether the page is correct — two unrelated dark interfaces land around 58%, a faithful rebuild above 90%. See [Honest limits](#honest-limits).
|
|
74
|
+
|
|
75
|
+
## Install
|
|
76
|
+
|
|
77
|
+
**In the Harness Web UI** — Settings → Plugins → **Add plugin**, then install by package name:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
dsh-clone
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Or add this repository's market index under **Plugins → Custom plugin indexes**, and install `dsh-clone` from the Discover tab in one click:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
https://raw.githubusercontent.com/egorfedorov/dsh-clone/main/index.json
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**From a checkout** (development):
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
git clone https://github.com/egorfedorov/dsh-clone
|
|
93
|
+
# Settings → Plugins → Add plugin → /absolute/path/to/dsh-clone
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Updating** — the market reports `update-available` when this repository's index (or npm) lists a newer compatible version. The market format has no in-place upgrade, so an update is a reinstall: your work lives in the workspace (`.dsh-clone/`, `clone-*/`), so reinstalling loses nothing. See [docs/PUBLISHING.md](docs/PUBLISHING.md) for the whole distribution story.
|
|
97
|
+
|
|
98
|
+
Requires a DeepSeek Harness profile with `subagents` (the `dsh-base` bundle ships the `spawn` backend) and Node 20+. Scoring additionally uses a Chromium-family browser if one is installed; without it everything else still works.
|
|
99
|
+
|
|
100
|
+
## Usage
|
|
101
|
+
|
|
102
|
+
Paste a screenshot into the conversation, then:
|
|
103
|
+
|
|
104
|
+
| Command | What it does |
|
|
105
|
+
|---|---|
|
|
106
|
+
| `/clone` | 3 lanes rebuild the last image in this conversation |
|
|
107
|
+
| `/clone 4` | four lanes — maximal spread of approaches |
|
|
108
|
+
| `/clone 2 darker, mobile-first` | two lanes plus a note every builder must honour |
|
|
109
|
+
| `/clone 3 ./mock.png` | use an explicit image path instead of the conversation image |
|
|
110
|
+
| `/clone status` | lane states, scores, and the suggested winner |
|
|
111
|
+
| `/clone pick 2` | adopt lane 2 into the workspace |
|
|
112
|
+
| `/clone refine 2 move the CTA up` | rebuild the adopted page with one change, scored against the same reference |
|
|
113
|
+
| `/clone cancel` | stop the builders |
|
|
114
|
+
| `/clone discard` | delete the scratch directories and lane branches |
|
|
115
|
+
|
|
116
|
+
The agent has the same operations as a tool, so you can also just say *"rebuild this screenshot, three tries"*:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
clone({ action: "start", lanes: 3, note: "keep the sidebar" })
|
|
120
|
+
clone({ action: "status" })
|
|
121
|
+
clone({ action: "pick", lane: 2 })
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### The panel
|
|
125
|
+
|
|
126
|
+
A **Clone** tab appears in the conversation view ring while a clone exists:
|
|
127
|
+
|
|
128
|
+
- the reference screenshot on the left, at the size the lanes are building for;
|
|
129
|
+
- one card per lane: a live iframe of the actual page, its strategy, elapsed time, tool calls, file count, token spend, and its **picture-match score** once rendered;
|
|
130
|
+
- the best-scoring lane is marked as suggested, and **Adopt this one** works on any lane that produced an `index.html`.
|
|
131
|
+
|
|
132
|
+
A small chip under the composer shows `clone · 2/3` while builders run, so you can leave the tab.
|
|
133
|
+
|
|
134
|
+
## How it works
|
|
135
|
+
|
|
136
|
+
**Isolation.** Each lane gets its own directory. Inside a git repository that is a real `git worktree` on its own branch (`dsh-clone/<id>-lane-<n>`), so nothing leaks between attempts and nothing touches your working tree. Outside a repository it is a plain directory under `.dsh-clone/<id>/lane-<n>/`. Existing `node_modules`, `.venv` and `vendor` are symlinked into each lane instead of reinstalled.
|
|
137
|
+
|
|
138
|
+
**The builders.** Each lane is a subagent with one job: read the reference image, then ship `index.html` plus any local CSS/JS/assets. The delivery contract is identical for every lane and non-negotiable:
|
|
139
|
+
|
|
140
|
+
> no build step · no network at runtime · no CDN · no placeholder copy · fill the viewport · approximate every image with CSS/SVG · leave no scratch files
|
|
141
|
+
|
|
142
|
+
The strategies differ; the contract does not. Every lane also carries an explicit effort budget (≈20-25 tool calls) and a wall-clock limit: a builder that keeps polishing is stopped, and whatever it already wrote is delivered and scored instead of stalling the clone.
|
|
143
|
+
|
|
144
|
+
**The score.** When a lane delivers, the plugin finds an installed Chromium-family browser (Chrome, Chromium, Edge, Brave — `DSH_CLONE_BROWSER` overrides), screenshots the page at the reference's own pixel size, and compares it with the reference: both images are averaged into a 48×32 colour grid and the mean absolute difference becomes a percentage. Highest score suggests the winner; you still choose. No browser, no score — the panel then falls back to completeness and finish order.
|
|
145
|
+
|
|
146
|
+
**The preview.** Lane pages are served from this page's own origin through `/api/dsh-clone/preview/<clone>/<lane>/` — no extra port, no proxy, the same authenticated connection the UI already uses. Because the HTML is model-written, it is served with a `sandbox` CSP (no `allow-same-origin`) and mounted in a `sandbox="allow-scripts"` iframe: a clone can run its own code, but it cannot reach your session, credentials, or the Harness API.
|
|
147
|
+
|
|
148
|
+
**Adoption.** `pick` copies the chosen lane into the workspace as `clone-<id>-lane-<n>/`, without `.git`, without the symlinked dependency directories, and without the builder's scratch files. The other lanes stay where they are until you `/clone discard`.
|
|
149
|
+
|
|
150
|
+
**Iteration.** `refine` takes an adopted lane, copies its page into a fresh single-lane clone and asks the builder for the smallest edit that satisfies one change — the reference and the viewport stay the same, so the new score is directly comparable and the clone reports the delta.
|
|
151
|
+
|
|
152
|
+
**Durability.** Each clone writes a small `state.json` next to its work. A plugin reload or a restart does not throw the run away: `status` and `pick` rebuild it from disk, and a lane that was mid-build keeps whatever it had already written, labelled as interrupted rather than pretending it finished.
|
|
153
|
+
|
|
154
|
+
**Budgets.** Every builder gets an effort budget in the prompt and a wall-clock limit (`laneTimeoutMs`); whichever is hit first settles the lane with whatever it delivered. `laneTokenBudget` adds a hard token ceiling and is off by default, because an agent re-sends its context on every step — a small ceiling stops honest builds mid-analysis, so it is a deliberate opt-in backstop rather than a default.
|
|
155
|
+
|
|
156
|
+
**The nudge.** Some models answer "measure first" by sampling the screenshot's pixels for their entire budget and then ending with nothing written. If a lane is past `nudgeAtTools` (12) tool calls with no `index.html` on disk, the plugin sends it one steering message: stop measuring, write the page now. One message, once per lane.
|
|
157
|
+
|
|
158
|
+
## Configuration
|
|
159
|
+
|
|
160
|
+
All optional; the defaults are the intended experience.
|
|
161
|
+
|
|
162
|
+
| Key | Default | Meaning |
|
|
163
|
+
|---|---|---|
|
|
164
|
+
| `lanes` | `3` | lane count when the command does not name one |
|
|
165
|
+
| `maxLanes` | `4` | hard cap on lanes per clone |
|
|
166
|
+
| `viewport` | `auto` | build and render size; `auto` uses the reference image's own pixel size |
|
|
167
|
+
| `laneTimeoutMs` | `900000` | wall-clock limit per lane (15 min); the lane is stopped and its output kept |
|
|
168
|
+
| `laneTokenBudget` | `0` (off) | token ceiling per lane. Off by default because an agent re-sends its context every step; 2M+ is a sensible backstop |
|
|
169
|
+
| `nudgeAtTools` | `12` | tool calls after which a lane with no `index.html` is told to stop analysing and write the page; `0` disables it |
|
|
170
|
+
| `provider` | `spawn` | `ctx.subagents` backend used for builders |
|
|
171
|
+
| `pollMs` | `900` | live counter refresh interval |
|
|
172
|
+
| `commandName` | `clone` | rename the slash command |
|
|
173
|
+
| `command` / `tool` | `true` | disable either entry point |
|
|
174
|
+
|
|
175
|
+
```yaml
|
|
176
|
+
- id: dsh-clone
|
|
177
|
+
name: dsh-clone
|
|
178
|
+
config:
|
|
179
|
+
lanes: 4
|
|
180
|
+
laneTimeoutMs: 600000
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Safety
|
|
184
|
+
|
|
185
|
+
- Clones are **static pages**: no package installs, no network at runtime, no server.
|
|
186
|
+
- Lane pages are sandboxed into an opaque origin, so model-written HTML cannot reach your session, credentials, or the Harness API.
|
|
187
|
+
- Every write happens inside the workspace: `.dsh-clone/` for scratch, `clone-*/` for adopted results, plus lane branches in your repository (removed by `/clone discard`).
|
|
188
|
+
- `git worktree` is used without touching your checkout: no commit, no checkout, no reset in your working tree. `test/git-lane.mjs` asserts exactly that.
|
|
189
|
+
- The scoring renderer is a one-shot headless screenshot of a local file. It loads no remote page.
|
|
190
|
+
|
|
191
|
+
## Honest limits
|
|
192
|
+
|
|
193
|
+
- **Cost is N×.** Three builders cost roughly three builds. `lanes: 2` is a reasonable everyday setting; `lanes: 1` makes it a plain "screenshot → page" button.
|
|
194
|
+
- **The score measures the picture, not the truth.** 92% agreement can still hide wrong copy, a missing form action, or broken behaviour. It ranks attempts; it does not certify one.
|
|
195
|
+
- **The score is coarse by design.** A 48×32 grid with shift tolerance is forgiving about a few pixels and strict about a panel on the wrong side; the structure term catches "right colour, wrong picture". Two lanes a few points apart are still a coin flip — open both before adopting.
|
|
196
|
+
- **Vision is required.** The builder must actually see the reference. A text-only model produces a guess; pair dsh-clone with a vision-capable route or a vision bridge plugin.
|
|
197
|
+
- **Images are approximated.** Without image generation, photos and illustrations become CSS/SVG stand-ins with the right silhouette and palette, not the original pixels.
|
|
198
|
+
- **Static output.** No Next.js project, no design system — by design, so the result is always runnable in one click.
|
|
199
|
+
- **Scoring needs an installed browser.** Chrome, Chromium, Edge or Brave on macOS, Windows or Linux; `DSH_CLONE_BROWSER` overrides the probe. No browser means no percentage, and nothing else changes.
|
|
200
|
+
- **A careful builder is a slow builder.** Hence the effort budget and the wall-clock limit; tune those, not your patience.
|
|
201
|
+
|
|
202
|
+
## Roadmap
|
|
203
|
+
|
|
204
|
+
1. **Region scoring** — "clone just this card": compare only the rectangle that matters instead of the whole page.
|
|
205
|
+
2. **Adopt as a diff** — when the workspace is a component library, adopt as an edit to an existing file instead of a new folder.
|
|
206
|
+
3. **Multi-reference** — several screenshots of the same flow, one page per screen.
|
|
207
|
+
4. **Panel screenshots in the storefront** — the four declared images are plugin output, not the panel itself.
|
|
208
|
+
|
|
209
|
+
## Repository layout
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
index.js Host half: /clone command, clone tool, web routes, poller
|
|
213
|
+
client.js Client half: the Clone panel and the composer chip
|
|
214
|
+
lib/engine.js Clone state machine (pure, dependency-injected)
|
|
215
|
+
lib/prompts.js The delivery contract, the effort budget, four strategies
|
|
216
|
+
lib/render.js Headless screenshot through an installed Chromium browser
|
|
217
|
+
lib/score-lane.js Render-and-score composition shared by the plugin and its test
|
|
218
|
+
lib/score.js Coarse-grid picture comparison
|
|
219
|
+
lib/png.js Dependency-free PNG decoder
|
|
220
|
+
lib/parse.js /clone grammar
|
|
221
|
+
lib/rank.js Adoptability, score-first ordering, suggestion
|
|
222
|
+
lib/preview.js Static lane serving, media types, sandbox CSP, JSON routes
|
|
223
|
+
lib/util.js Small pure helpers
|
|
224
|
+
test/smoke.mjs 30 tests, no Harness required
|
|
225
|
+
test/git-lane.mjs worktree isolation against a real repository
|
|
226
|
+
test/render-score.mjs real render + score through the plugin's own path
|
|
227
|
+
index.json Format-1 plugin market index (Custom plugin indexes)
|
|
228
|
+
screenshots.json Storefront screenshots (read by dsh-market)
|
|
229
|
+
tools/make-hero.py Regenerates docs/assets/hero.png
|
|
230
|
+
tools/make-references.py Regenerates the showcase reference screenshots
|
|
231
|
+
tools/make-icon.py Regenerates the storefront icon
|
|
232
|
+
tools/make-demo.py Regenerates docs/assets/demo.gif
|
|
233
|
+
docs/PUBLISHING.md npm, the awesome list, indexes, the official program
|
|
234
|
+
docs/storefront/ Ready-to-submit entry for awesome-dsh-plugin
|
|
235
|
+
docs/index.html GitHub Pages landing page
|
|
236
|
+
docs/demo/ Four real clones, served live
|
|
237
|
+
docs/ARCHITECTURE.md
|
|
238
|
+
ACCEPTABLE-USE.md
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Development
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
npm run check # syntax-check every shipped file
|
|
245
|
+
npm test # 30 assertions: grammar, ranking, scoring, engine lifecycle
|
|
246
|
+
npm run test:git # the git-worktree isolation model against a real repository
|
|
247
|
+
npm run test:score # a real render + score through the plugin's own code path
|
|
248
|
+
npm run test:all # all three
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
The engine takes every side effect as a dependency, so the tests drive the real state machine against a temporary directory with fake subagents — no Harness, no network, no model. The pixel tests decode the demo reference and a committed real lane render, so a regression in either the decoder or the scoring curve fails the suite. `test/git-lane.mjs` builds a throwaway repository to prove that lanes are real worktrees, that your own dirty working tree and branch are untouched, that dependency directories are shared by symlink, and that discard removes worktrees and branches while the adopted copy survives. `test/render-score.mjs` goes through the plugin's own scoring path with two fake builders — one faithful, one deliberately desaturated — and asserts that both come back scored, that the faithful one wins, and that the snapshot carries the score and the render URL. It skips itself when no browser is installed.
|
|
252
|
+
|
|
253
|
+
## По-русски
|
|
254
|
+
|
|
255
|
+
Плагин для DeepSeek Harness: скидываешь скриншот интерфейса, пишешь `/clone 3` — три агента параллельно собирают его в изолированных папках (в репозитории — через git worktree). В панели «Clone» видно эталон и все попытки рядом; каждая готовая страница рендерится в headless-браузере и получает процент совпадения картинки. Нажимаешь «Adopt this one» — выбранный вариант копируется в рабочую папку как готовая страница. Остальные удаляются командой `/clone discard`.
|
|
256
|
+
|
|
257
|
+
Честно: стоимость — примерно N попыток; оценка совпадения меряет картинку, а не правильность (текст и поведение всё равно проверяй глазами); нужна модель, которая видит изображение.
|
|
258
|
+
|
|
259
|
+
## License
|
|
260
|
+
|
|
261
|
+
MIT — see [LICENSE](LICENSE). Use it on interfaces you have the right to rebuild; see [ACCEPTABLE-USE.md](ACCEPTABLE-USE.md).
|