transitions-refine 0.1.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.
Files changed (32) hide show
  1. package/.agents/skills/refine-live/SKILL.md +205 -0
  2. package/.agents/skills/transitions-dev/01-card-resize.md +53 -0
  3. package/.agents/skills/transitions-dev/02-number-pop-in.md +119 -0
  4. package/.agents/skills/transitions-dev/03-notification-badge.md +110 -0
  5. package/.agents/skills/transitions-dev/04-text-states-swap.md +97 -0
  6. package/.agents/skills/transitions-dev/05-menu-dropdown.md +105 -0
  7. package/.agents/skills/transitions-dev/06-modal.md +94 -0
  8. package/.agents/skills/transitions-dev/07-panel-reveal.md +81 -0
  9. package/.agents/skills/transitions-dev/08-page-side-by-side.md +100 -0
  10. package/.agents/skills/transitions-dev/09-icon-swap.md +78 -0
  11. package/.agents/skills/transitions-dev/10-success-check.md +169 -0
  12. package/.agents/skills/transitions-dev/11-avatar-group-hover.md +200 -0
  13. package/.agents/skills/transitions-dev/12-error-state-shake.md +202 -0
  14. package/.agents/skills/transitions-dev/13-input-clear-dissolve.md +276 -0
  15. package/.agents/skills/transitions-dev/14-skeleton-reveal.md +149 -0
  16. package/.agents/skills/transitions-dev/15-shimmer-text.md +95 -0
  17. package/.agents/skills/transitions-dev/16-tabs-sliding.md +146 -0
  18. package/.agents/skills/transitions-dev/17-tooltip.md +103 -0
  19. package/.agents/skills/transitions-dev/18-texts-reveal.md +110 -0
  20. package/.agents/skills/transitions-dev/19-card-tilt.md +170 -0
  21. package/.agents/skills/transitions-dev/20-plus-menu-morph.md +167 -0
  22. package/.agents/skills/transitions-dev/21-accordion.md +124 -0
  23. package/.agents/skills/transitions-dev/SKILL.md +225 -0
  24. package/.agents/skills/transitions-dev/_root.css +204 -0
  25. package/README.md +89 -0
  26. package/bin/cli.mjs +264 -0
  27. package/demo.html +2531 -0
  28. package/package.json +37 -0
  29. package/server/inject.mjs +116 -0
  30. package/server/motion-tokens.mjs +106 -0
  31. package/server/refine-agent.mjs +86 -0
  32. package/server/relay.mjs +421 -0
package/bin/cli.mjs ADDED
@@ -0,0 +1,264 @@
1
+ #!/usr/bin/env node
2
+ // Refine — transitions.dev live tool.
3
+ //
4
+ // npx transitions-refine live # inject the panel + start the relay
5
+ // npx transitions-refine live --llm # + install/wire cursor-agent (persistent LLM)
6
+ // npx transitions-refine stop # remove the injected <script> tag
7
+ //
8
+ // `live` sets up the timeline + Refine with no npm install and no source edits
9
+ // of your own:
10
+ // 1. injects one <script type="module" src=".../inject.js"> into your page
11
+ // 2. drops the `refine-live` + `transitions-dev` skills (for token-aware picks)
12
+ // 3. ensures an LLM backend:
13
+ // --llm → installs/wires the Cursor CLI (cursor-agent) so the relay
14
+ // answers LLM jobs itself, persistently (no /refine live loop).
15
+ // else → falls back to /refine live (in-IDE agent) + deterministic.
16
+ // 4. starts the local refine relay (serves the panel at /inject.js).
17
+
18
+ import { spawn, spawnSync } from "node:child_process";
19
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync } from "node:fs";
20
+ import { dirname, join, resolve } from "node:path";
21
+ import { fileURLToPath } from "node:url";
22
+ import { homedir } from "node:os";
23
+
24
+ const PKG_ROOT = fileURLToPath(new URL("..", import.meta.url));
25
+ const CWD = process.cwd();
26
+ const HOME = process.env.HOME || homedir();
27
+
28
+ const MARK_START = "<!-- timeline-inject:start -->";
29
+ const MARK_END = "<!-- timeline-inject:end -->";
30
+ const PAGE_CANDIDATES = [
31
+ "index.html",
32
+ "public/index.html",
33
+ "src/index.html",
34
+ "app/index.html",
35
+ "dist/index.html",
36
+ ];
37
+
38
+ function parseArgs(argv) {
39
+ const args = { _: [] };
40
+ for (let i = 0; i < argv.length; i++) {
41
+ const a = argv[i];
42
+ if (a === "--page" || a === "-p") args.page = argv[++i];
43
+ else if (a === "--port") args.port = argv[++i];
44
+ else if (a.startsWith("--")) args[a.slice(2)] = true;
45
+ else args._.push(a);
46
+ }
47
+ return args;
48
+ }
49
+
50
+ function findPage(explicit) {
51
+ if (explicit) return resolve(CWD, explicit);
52
+ for (const c of PAGE_CANDIDATES) {
53
+ const p = join(CWD, c);
54
+ if (existsSync(p)) return p;
55
+ }
56
+ return null;
57
+ }
58
+
59
+ function injectTag(pagePath, port) {
60
+ const tag = `${MARK_START}\n<script type="module" src="http://localhost:${port}/inject.js"></script>\n${MARK_END}`;
61
+ let html = readFileSync(pagePath, "utf8");
62
+ if (html.includes(MARK_START)) {
63
+ html = stripTag(html); // refresh (e.g. port changed)
64
+ }
65
+ if (html.includes("</body>")) {
66
+ html = html.replace(/<\/body>/i, `${tag}\n</body>`);
67
+ } else {
68
+ html += `\n${tag}\n`;
69
+ }
70
+ writeFileSync(pagePath, html);
71
+ }
72
+
73
+ function stripTag(html) {
74
+ const re = new RegExp(`\\n?${escapeRe(MARK_START)}[\\s\\S]*?${escapeRe(MARK_END)}\\n?`, "g");
75
+ return html.replace(re, "");
76
+ }
77
+
78
+ function escapeRe(s) {
79
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
80
+ }
81
+
82
+ // Copy a whole skill directory from the package into the user's project so the
83
+ // in-IDE agent (/refine live) and any spawned cursor-agent can read it.
84
+ function dropSkill(name) {
85
+ const src = join(PKG_ROOT, ".agents/skills", name);
86
+ const destDir = join(CWD, ".agents/skills", name);
87
+ if (!existsSync(src)) return false;
88
+ if (existsSync(destDir)) return "exists";
89
+ mkdirSync(dirname(destDir), { recursive: true });
90
+ cpSync(src, destDir, { recursive: true });
91
+ return true;
92
+ }
93
+
94
+ // ── agent CLI (for the persistent LLM path) ──────────────────────────────────
95
+ // The relay spawns `REFINE_AGENT_CMD` per job. We point it at cursor-agent so
96
+ // LLM Refine works without a live `/refine live` loop. The binary may not be on
97
+ // the non-interactive PATH, so we probe known install locations and use an
98
+ // absolute path when wiring it up.
99
+ const AGENT_BIN_CANDIDATES = [
100
+ "cursor-agent",
101
+ join(HOME, ".local/bin/cursor-agent"),
102
+ join(HOME, ".cursor/bin/cursor-agent"),
103
+ ];
104
+
105
+ function isRunnable(bin) {
106
+ try {
107
+ return spawnSync(bin, ["--version"], { stdio: "ignore" }).status === 0;
108
+ } catch {
109
+ return false;
110
+ }
111
+ }
112
+
113
+ function findAgentBin() {
114
+ return AGENT_BIN_CANDIDATES.find(isRunnable) || null;
115
+ }
116
+
117
+ function installAgentCli() {
118
+ log("• installing the Cursor CLI (cursor-agent) — one-time…");
119
+ const r =
120
+ process.platform === "win32"
121
+ ? spawnSync(
122
+ "powershell",
123
+ ["-NoProfile", "-Command", "irm 'https://cursor.com/install?win32=true' | iex"],
124
+ { stdio: "inherit" }
125
+ )
126
+ : spawnSync("sh", ["-c", "curl https://cursor.com/install -fsS | bash"], {
127
+ stdio: "inherit",
128
+ });
129
+ if (r.status !== 0) log("! the installer exited non-zero — see its output above.");
130
+ return findAgentBin();
131
+ }
132
+
133
+ // Returns an absolute-ish command string to put in REFINE_AGENT_CMD, or null.
134
+ function ensureAgentCli({ autoInstall }) {
135
+ const bin = findAgentBin();
136
+ if (bin) return bin;
137
+ if (!autoInstall) return null;
138
+ return installAgentCli();
139
+ }
140
+
141
+ function log(msg) {
142
+ process.stdout.write(`${msg}\n`);
143
+ }
144
+
145
+ function cmdLive(args) {
146
+ const port = String(args.port || process.env.REFINE_RELAY_PORT || 7331);
147
+ const page = findPage(args.page);
148
+
149
+ // 1) inject the script tag
150
+ if (page) {
151
+ injectTag(page, port);
152
+ log(`✓ injected timeline into ${page.replace(CWD + "/", "")}`);
153
+ } else {
154
+ log("! no HTML entry found (looked for index.html, public/index.html, …).");
155
+ log(" Add this one line to your page <body> yourself:");
156
+ log(` <script type="module" src="http://localhost:${port}/inject.js"></script>`);
157
+ log(" …or re-run with --page <path-to-your-html>.");
158
+ }
159
+
160
+ // 2) drop the skills the Refine flow relies on (the in-IDE agent and any
161
+ // spawned cursor-agent both read transitions-dev for token-aware picks).
162
+ for (const name of ["refine-live", "transitions-dev"]) {
163
+ const r = dropSkill(name);
164
+ if (r === true) log(`✓ added .agents/skills/${name}`);
165
+ else if (r === "exists") log(`✓ ${name} skill already present`);
166
+ }
167
+
168
+ // 2.5) ensure an agent CLI so the relay can answer LLM jobs itself — this is
169
+ // the persistent path (no `/refine live` loop to keep alive). Installing
170
+ // fetches a system binary, so it only happens with explicit opt-in via
171
+ // `--llm`. If REFINE_AGENT_CMD is already set we respect it as-is.
172
+ const wantLlm = Boolean(args.llm);
173
+ const env = { ...process.env, REFINE_RELAY_PORT: port };
174
+ let agentBin = null;
175
+ if (process.env.REFINE_AGENT_CMD) {
176
+ log(`✓ using REFINE_AGENT_CMD from environment: ${process.env.REFINE_AGENT_CMD}`);
177
+ } else {
178
+ agentBin = ensureAgentCli({ autoInstall: wantLlm });
179
+ if (agentBin) {
180
+ // Absolute path: the relay spawns via `sh -c`, whose PATH may not include
181
+ // the CLI's install dir (e.g. ~/.local/bin). `-p` = headless print mode;
182
+ // `--force` clears the workspace-trust / tool-approval prompts that would
183
+ // otherwise hang a non-interactive spawn.
184
+ const cmd = `${agentBin} -p --force`;
185
+ env.REFINE_AGENT_CMD = cmd;
186
+ log(`✓ LLM path wired: relay will spawn ${cmd}`);
187
+ }
188
+ }
189
+
190
+ // 3) start the relay (foreground; Ctrl-C stops it + reverts the injection)
191
+ const relay = spawn(process.execPath, [join(PKG_ROOT, "server/relay.mjs")], {
192
+ stdio: "inherit",
193
+ env,
194
+ });
195
+
196
+ const llmWired = Boolean(env.REFINE_AGENT_CMD);
197
+ log("");
198
+ log("Next:");
199
+ log(" 1. Open your app — the timeline panel is now on the page.");
200
+ log(" 2. Click Refine. Deterministic suggestions work immediately.");
201
+ if (llmWired) {
202
+ log(" 3. LLM suggestions are ON — the relay runs the agent CLI per click,");
203
+ log(" so you never have to run /refine live.");
204
+ log(" One-time: make sure the CLI is authenticated —");
205
+ log(" run `cursor-agent` once to log in, or set CURSOR_API_KEY.");
206
+ } else if (wantLlm) {
207
+ log(" 3. LLM was requested but cursor-agent isn't available (install failed).");
208
+ log(" Install it manually, then re-run:");
209
+ log(" curl https://cursor.com/install -fsS | bash");
210
+ log(" …or run /refine live in your editor for the in-IDE-agent path.");
211
+ } else {
212
+ log(" 3. For persistent LLM (no /refine live needed), re-run with --llm");
213
+ log(" (installs the Cursor CLI once). Or run /refine live in your editor");
214
+ log(" to use the in-IDE agent.");
215
+ }
216
+ log("");
217
+ log("Press Ctrl-C to stop the relay and remove the injected tag.");
218
+
219
+ const cleanup = () => {
220
+ try {
221
+ if (page && existsSync(page)) {
222
+ const html = stripTag(readFileSync(page, "utf8"));
223
+ writeFileSync(page, html);
224
+ log(`\n✓ removed injected tag from ${page.replace(CWD + "/", "")}`);
225
+ }
226
+ } catch {}
227
+ if (!relay.killed) relay.kill("SIGINT");
228
+ process.exit(0);
229
+ };
230
+ process.on("SIGINT", cleanup);
231
+ process.on("SIGTERM", cleanup);
232
+ relay.on("exit", (code) => process.exit(code ?? 0));
233
+ }
234
+
235
+ function cmdStop(args) {
236
+ const page = findPage(args.page);
237
+ if (!page || !existsSync(page)) {
238
+ log("! no HTML entry found to clean. Pass --page <path> if needed.");
239
+ return;
240
+ }
241
+ const html = readFileSync(page, "utf8");
242
+ if (!html.includes(MARK_START)) {
243
+ log(`nothing to remove in ${page.replace(CWD + "/", "")}`);
244
+ return;
245
+ }
246
+ writeFileSync(page, stripTag(html));
247
+ log(`✓ removed injected tag from ${page.replace(CWD + "/", "")}`);
248
+ }
249
+
250
+ function main() {
251
+ const args = parseArgs(process.argv.slice(2));
252
+ const cmd = args._[0] || "live";
253
+ if (cmd === "live") return cmdLive(args);
254
+ if (cmd === "stop") return cmdStop(args);
255
+ log("Refine — transitions.dev live tool");
256
+ log(" npx transitions-refine live # inject panel + start relay");
257
+ log(" npx transitions-refine live --llm # + install/wire cursor-agent for persistent LLM");
258
+ log(" npx transitions-refine stop # remove the injected tag");
259
+ log("");
260
+ log("Options: --page <html> --port <n> --llm (enable persistent LLM via the Cursor CLI)");
261
+ process.exit(cmd ? 1 : 0);
262
+ }
263
+
264
+ main();