@devosurf/vynt 0.1.2

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 ADDED
@@ -0,0 +1,287 @@
1
+ # vynt
2
+
3
+ vynt is a local utility for fast UI iteration with parallel agent variants in a single active workspace.
4
+
5
+ ## Why
6
+
7
+ - Run multiple implementation paths in parallel.
8
+ - Keep one active workspace and one dev server.
9
+ - Compare variants quickly and choose a winner.
10
+ - Keep rollback deterministic.
11
+
12
+ ## Current Scope
13
+
14
+ This scaffold provides:
15
+
16
+ - Variant metadata storage at `.vynt/state.json`
17
+ - Objective + variant hierarchy
18
+ - Profile composition with one variant per objective
19
+ - Conflict detection for composed profile selections
20
+ - Workspace apply engine that restores the pinned base and applies selected patch artifacts
21
+ - Docs for MVP architecture and phase plan
22
+
23
+ This scaffold does not yet capture screenshots automatically.
24
+
25
+ ## Recommended Core Workflow (Now)
26
+
27
+ Use `vynt` as a standalone CLI while you review UI manually in your browser.
28
+
29
+ 1. Start your web app once and keep it running.
30
+ 2. Register variants with `add` under each objective.
31
+ 3. Switch quickly with `apply` (single variant) or `profile apply` (cross-objective composition).
32
+ 4. Refresh the browser and review the result manually.
33
+ 5. Finalize objective winners after visual review.
34
+
35
+ This keeps the core loop simple: deterministic patch switching plus manual UI verification.
36
+
37
+ ## Browser Toolbar Devtool
38
+
39
+ You can run a local bridge and control variant switching directly in the browser.
40
+
41
+ 1. Use `vynt/vite` plugin in your Vite config (recommended) so bridge starts automatically.
42
+ 2. Mount the React provider (recommended) or inject `toolbar.js` for non-React pages.
43
+
44
+ ```html
45
+ <script src="http://127.0.0.1:4173/toolbar.js" data-vynt-bridge="http://127.0.0.1:4173"></script>
46
+ ```
47
+
48
+ Toolbar capabilities in this MVP:
49
+
50
+ - Objective + variant selection
51
+ - Previous/next variant stepping with index counter (`x/y`)
52
+ - Single-variant apply
53
+ - Profile apply
54
+ - Rollback to latest snapshot
55
+
56
+ React/Next helper component (one-line mount, no script fetch required):
57
+
58
+ ```tsx
59
+ // app/layout.tsx or root provider
60
+ import { VyntToolbarProvider } from "vynt/web/react/index.js"
61
+
62
+ export function DevTools() {
63
+ return <VyntToolbarProvider />
64
+ }
65
+ ```
66
+
67
+ The React provider renders the toolbar inline and talks directly to the bridge API (`/status`, `/apply`, `/rollback`, `/events`). It does not load `toolbar.js`.
68
+ With `vynt/vite` plugin, provider default bridge URL is same-origin `"/__vynt"`.
69
+
70
+ Vite auto-bridge setup (no separate `vynt bridge serve` command):
71
+
72
+ ```ts
73
+ import { defineConfig } from "vite"
74
+ import react from "@vitejs/plugin-react"
75
+ import { vyntVitePlugin } from "vynt/vite"
76
+
77
+ export default defineConfig({
78
+ plugins: [react(), vyntVitePlugin()],
79
+ })
80
+ ```
81
+
82
+ Optional plugin config:
83
+
84
+ ```ts
85
+ vyntVitePlugin({
86
+ bridgeHost: "127.0.0.1",
87
+ bridgePort: 4173,
88
+ prefix: "/__vynt",
89
+ })
90
+ ```
91
+
92
+ Manual script injection is still available when you are not using React:
93
+
94
+ ```html
95
+ <script src="http://127.0.0.1:4173/toolbar.js" data-vynt-bridge="http://127.0.0.1:4173"></script>
96
+ ```
97
+
98
+ Custom bridge URL:
99
+
100
+ ```tsx
101
+ <VyntToolbarProvider bridgeUrl="http://127.0.0.1:4173" />
102
+ ```
103
+
104
+ Use custom `bridgeUrl` when you do not use the Vite plugin proxy.
105
+
106
+ ## Test in a Real Project
107
+
108
+ Yes, you can test this today with the same link workflow.
109
+
110
+ 1. In this repo: `bun link`
111
+ 2. In your target web project: `bun link vynt`
112
+ 3. In target project root: `vynt init "$(git rev-parse HEAD)"` (if not already initialized)
113
+ 4. Add `vyntVitePlugin()` to your Vite config
114
+ 5. Mount the provider in your app:
115
+
116
+ ```tsx
117
+ import { VyntToolbarProvider } from "vynt/web/react/index.js"
118
+
119
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
120
+ return (
121
+ <html>
122
+ <body>
123
+ <VyntToolbarProvider />
124
+ {children}
125
+ </body>
126
+ </html>
127
+ )
128
+ }
129
+ ```
130
+
131
+ 6. Open the app in browser; toolbar appears bottom-right.
132
+ 7. Use objective/variant selectors or previous/next stepping to switch quickly.
133
+
134
+ ## tmux-First Example
135
+
136
+ The project can be operated in tmux without requiring any plugin.
137
+
138
+ ```bash
139
+ # start a tmux layout (server + live vynt status + operator pane)
140
+ scripts/vynt-tmux.sh start vynt-lab "npm run dev:web"
141
+
142
+ # quick objective/variant apply
143
+ scripts/vynt-tmux.sh apply hero <variant-id>
144
+
145
+ # quick profile apply
146
+ scripts/vynt-tmux.sh profile-apply <profile-id>
147
+
148
+ # interactive selector (uses fzf if installed)
149
+ scripts/vynt-tmux.sh selector
150
+ scripts/vynt-tmux.sh selector profile
151
+ ```
152
+
153
+ ## OpenCode Integration: Standalone First, Plugin Later
154
+
155
+ - Current recommendation: run `vynt` as a standalone CLI from terminal/tmux.
156
+ - Future optional path: an OpenCode plugin hook that auto-registers variants from session lifecycle events.
157
+ - Rationale: keep core switching reliable and tool-agnostic first; add automation hooks when the workflow stabilizes.
158
+
159
+ ## Future Optional Automation
160
+
161
+ - `capture`: auto-screenshot routes per variant/profile using Playwright.
162
+ - `compare`: side-by-side or grouped screenshot review.
163
+ - Auth-protected pages can be supported by persisted Playwright auth state (cookies/local storage), so screenshots remain automated.
164
+
165
+ ## Quick Start
166
+
167
+ 1. `npm install`
168
+ 2. `npm run dev -- init <base-ref>`
169
+ 3. `npm run dev -- objective create "landing hero redesign" --id x`
170
+ 4. `npm run dev -- add x "bold hero" ./patches/x-v2.patch --files=src/app.tsx,src/hero.tsx`
171
+ 5. `npm run dev -- profile create "design-a"`
172
+ 6. `npm run dev -- profile set design-a x <variant-id>`
173
+ 7. `npm run dev -- profile apply design-a`
174
+ 8. `npm run dev -- status`
175
+ 9. `npm run dev -- list`
176
+
177
+ ## Use in Real Projects (bun link)
178
+
179
+ 1. In this repo: `bun link`
180
+ 2. Ensure Bun bin path is on your shell PATH (usually `~/.bun/bin`).
181
+ 3. In any target project: `bun link vynt`
182
+ 4. Run from the target project: `vynt --help`
183
+
184
+ Then use the same commands directly (without `npm run dev --`), for example:
185
+
186
+ - `vynt init "$(git rev-parse HEAD)"`
187
+ - `vynt objective create "hero exploration"`
188
+ - `vynt status`
189
+
190
+ ## Command Overview
191
+
192
+ All commands are invoked through `vynt`.
193
+
194
+ - `vynt init <base-ref>`
195
+ - `vynt objective create <name> [--id <id>]`
196
+ - `vynt objective list [--json]`
197
+ - `vynt objective finalize <objective-id> <variant-id>`
198
+ - `vynt add <objective-id> <name> <patch-file> [--files=a,b,c] [--session=<id>] [--notes=<text>]`
199
+ - `vynt activate <objective-id> <variant-id>`
200
+ - `vynt apply [<variant-id>] [--objective=<objective-id> --variant=<variant-id>] [--review]`
201
+ - `vynt rollback [<snapshot-id>]`
202
+ - `vynt bridge serve [--host <host>] [--port <port>]`
203
+ - `vynt status [--json]`
204
+ - `vynt opencode register <objective-id> <session-id> <name> <patch-file> [--files=a,b,c] [--notes=<text>]`
205
+ - `vynt opencode register-auto <session-id> <name> <patch-file> [--objective=<objective-id>] [--files=a,b,c] [--notes=<text>]`
206
+ - `vynt opencode capture <session-id> <name> [--objective=<objective-id>] [--notes=<text>] [--reset]`
207
+ - `vynt profile create <name> [--id <id>]`
208
+ - `vynt profile list [--json]`
209
+ - `vynt profile set <profile-id> <objective-id> <variant-id>`
210
+ - `vynt profile clear <profile-id> <objective-id>`
211
+ - `vynt profile apply <profile-id>`
212
+ - `vynt list [--json]`
213
+
214
+ ## Helper Scripts
215
+
216
+ - `scripts/vynt-tmux.sh`: tmux-first helper for start/apply/profile-apply/selector flows, including profile selector mode.
217
+ - `scripts/vynt-opencode-hook.sh`: standalone OpenCode hook scaffold for registering session output with direct args or environment variables.
218
+ - `scripts/vynt-variants-parallel.sh`: deterministic backend helper for `/variants` orchestration (`setup`, `wait`, `finalize`, `cleanup`) with patch-based `register-auto`; defaults to `sandbox` backend and supports `worktree` fallback.
219
+
220
+ ## Variant Generation Command
221
+
222
+ Use OpenCode custom command `/variants <objective> [count]` (configured in `~/.config/opencode/opencode.jsonc`) to orchestrate multi-variant generation with subagent exploration and `vynt` registration.
223
+
224
+ Variant contract for generated UI patches:
225
+
226
+ - If a variant changes UI markup (`.tsx`, `.jsx`, `.vue`, `.svelte`, `.astro`, `.html`) for an objective, include `data-vynt-objective="<objective-id>"` on the objective wrapper container.
227
+ - `vynt opencode register-auto` now enforces this for UI patches and rejects registration when the marker is missing.
228
+ - Subagents should preserve existing `data-vynt-*` attributes and never remove objective markers.
229
+
230
+ `vynt opencode capture` exists for explicit in-session registration of the current diff, including optional `--reset` back to base for the next variant iteration.
231
+
232
+ ## OpenCode Hook Scaffold (Standalone)
233
+
234
+ Use the helper script when you want a lightweight plugin-compatible registration path without hard-coupling to any specific OpenCode runtime internals.
235
+
236
+ ```bash
237
+ # direct mode
238
+ scripts/vynt-opencode-hook.sh register hero ses_123 "hero bold" ./patches/hero.patch --files src/hero.tsx
239
+
240
+ # env mode (for hooks)
241
+ scripts/vynt-opencode-hook.sh env-template
242
+ scripts/vynt-opencode-hook.sh register-env
243
+ ```
244
+
245
+ ## OpenCode Auto-Register Plugin Setup
246
+
247
+ Auto-registration is optional and runs through an OpenCode plugin event hook.
248
+
249
+ 1. Ensure global plugin file exists at `~/.config/opencode/plugin/vynt-autoregister.ts`.
250
+ 2. Restart OpenCode so the plugin is loaded.
251
+
252
+ No config is required for objective routing.
253
+
254
+ Optional override file:
255
+
256
+ ```json
257
+ {
258
+ "objectiveId": "hero",
259
+ "namePrefix": "agent",
260
+ "enabled": true
261
+ }
262
+ ```
263
+
264
+ Save this as `.vynt/opencode-autoregister.json` in your project root.
265
+
266
+ Notes:
267
+ - Auto-register now performs objective routing automatically.
268
+ - If one objective matches changed files strongly, it is reused.
269
+ - If routing is ambiguous, a new objective is auto-created from file/name hints.
270
+ - Optional: set `objectiveId` only when you want to force all auto-registered variants into one objective.
271
+ - You can override objective and title prefix through env vars:
272
+ - `VYNT_AUTO_OBJECTIVE_ID`
273
+ - `VYNT_AUTO_NAME_PREFIX`
274
+ - On each `session.idle`, the plugin writes a patch to `.vynt/patches/` and registers it through `vynt opencode register-auto`.
275
+
276
+ ## Layout
277
+
278
+ - `src/cli.ts`: command entrypoint
279
+ - `src/bridge.ts`: local HTTP/SSE bridge for browser devtool controls
280
+ - `src/state.ts`: state load/save logic
281
+ - `src/types.ts`: variant model types
282
+ - `web/vynt-toolbar.js`: injected browser toolbar client
283
+ - `web/react/VyntToolbarProvider.js`: React helper for script injection
284
+ - `web/react/index.js`: tiny React barrel export for provider import
285
+ - `docs/mvp-spec.md`: product spec
286
+ - `docs/architecture.md`: technical architecture
287
+ - `docs/implementation-plan.md`: phased execution plan
package/bin/vynt ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SOURCE_PATH="${BASH_SOURCE[0]}"
5
+
6
+ while [ -L "$SOURCE_PATH" ]; do
7
+ SOURCE_DIR="$(cd -P "$(dirname "$SOURCE_PATH")" >/dev/null 2>&1 && pwd)"
8
+ LINK_TARGET="$(readlink "$SOURCE_PATH")"
9
+ if [[ "$LINK_TARGET" = /* ]]; then
10
+ SOURCE_PATH="$LINK_TARGET"
11
+ else
12
+ SOURCE_PATH="$SOURCE_DIR/$LINK_TARGET"
13
+ fi
14
+ done
15
+
16
+ SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE_PATH")" >/dev/null 2>&1 && pwd)"
17
+ exec bun "$SCRIPT_DIR/../src/cli.ts" "$@"
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@devosurf/vynt",
3
+ "version": "0.1.2",
4
+ "type": "module",
5
+ "files": [
6
+ "bin",
7
+ "src",
8
+ "vite",
9
+ "web"
10
+ ],
11
+ "bin": {
12
+ "vynt": "./bin/vynt"
13
+ },
14
+ "exports": {
15
+ ".": "./src/cli.ts",
16
+ "./web/react": {
17
+ "types": "./web/react/index.d.ts",
18
+ "default": "./web/react/index.js"
19
+ },
20
+ "./web/react/index.js": {
21
+ "types": "./web/react/index.d.ts",
22
+ "default": "./web/react/index.js"
23
+ },
24
+ "./vite": {
25
+ "types": "./vite/index.d.ts",
26
+ "default": "./vite/index.js"
27
+ },
28
+ "./vite/index.js": {
29
+ "types": "./vite/index.d.ts",
30
+ "default": "./vite/index.js"
31
+ }
32
+ },
33
+ "scripts": {
34
+ "dev": "tsx src/cli.ts",
35
+ "test": "tsx --test tests/**/*.test.ts",
36
+ "typecheck": "tsc --noEmit"
37
+ },
38
+ "dependencies": {
39
+ "commander": "^14.0.3"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^22.13.10",
43
+ "tsx": "^4.19.2",
44
+ "typescript": "^5.8.2"
45
+ }
46
+ }