roughbook 0.0.0-stage → 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 (144) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +166 -2
  3. package/dist/agents/claude.js +214 -0
  4. package/dist/agents/claude.js.map +1 -0
  5. package/dist/agents/codex.js +179 -0
  6. package/dist/agents/codex.js.map +1 -0
  7. package/dist/agents/command.js +47 -0
  8. package/dist/agents/command.js.map +1 -0
  9. package/dist/agents/index.js +21 -0
  10. package/dist/agents/index.js.map +1 -0
  11. package/dist/agents/process.js +76 -0
  12. package/dist/agents/process.js.map +1 -0
  13. package/dist/agents/session.js +26 -0
  14. package/dist/agents/session.js.map +1 -0
  15. package/dist/agents/types.js +2 -0
  16. package/dist/agents/types.js.map +1 -0
  17. package/dist/api/client.js +138 -0
  18. package/dist/api/client.js.map +1 -0
  19. package/dist/api/errors.js +40 -0
  20. package/dist/api/errors.js.map +1 -0
  21. package/dist/api/outbox.js +105 -0
  22. package/dist/api/outbox.js.map +1 -0
  23. package/dist/api/types.js +4 -0
  24. package/dist/api/types.js.map +1 -0
  25. package/dist/cli.js +10 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/commands/agents.js +39 -0
  28. package/dist/commands/agents.js.map +1 -0
  29. package/dist/commands/context.js +27 -0
  30. package/dist/commands/context.js.map +1 -0
  31. package/dist/commands/done.js +125 -0
  32. package/dist/commands/done.js.map +1 -0
  33. package/dist/commands/index.js +101 -0
  34. package/dist/commands/index.js.map +1 -0
  35. package/dist/commands/init.js +203 -0
  36. package/dist/commands/init.js.map +1 -0
  37. package/dist/commands/login.js +93 -0
  38. package/dist/commands/login.js.map +1 -0
  39. package/dist/commands/logout.js +34 -0
  40. package/dist/commands/logout.js.map +1 -0
  41. package/dist/commands/promote.js +105 -0
  42. package/dist/commands/promote.js.map +1 -0
  43. package/dist/commands/pull.js +45 -0
  44. package/dist/commands/pull.js.map +1 -0
  45. package/dist/commands/revert.js +39 -0
  46. package/dist/commands/revert.js.map +1 -0
  47. package/dist/commands/review.js +73 -0
  48. package/dist/commands/review.js.map +1 -0
  49. package/dist/commands/run.js +147 -0
  50. package/dist/commands/run.js.map +1 -0
  51. package/dist/commands/shared.js +81 -0
  52. package/dist/commands/shared.js.map +1 -0
  53. package/dist/commands/start.js +119 -0
  54. package/dist/commands/start.js.map +1 -0
  55. package/dist/commands/status.js +62 -0
  56. package/dist/commands/status.js.map +1 -0
  57. package/dist/config/credentials.js +17 -0
  58. package/dist/config/credentials.js.map +1 -0
  59. package/dist/config/server.js +29 -0
  60. package/dist/config/server.js.map +1 -0
  61. package/dist/config/settings.js +21 -0
  62. package/dist/config/settings.js.map +1 -0
  63. package/dist/config/userDir.js +18 -0
  64. package/dist/config/userDir.js.map +1 -0
  65. package/dist/fs/json.js +44 -0
  66. package/dist/fs/json.js.map +1 -0
  67. package/dist/fs/lock.js +38 -0
  68. package/dist/fs/lock.js.map +1 -0
  69. package/dist/io.js +26 -0
  70. package/dist/io.js.map +1 -0
  71. package/dist/launch/open.js +48 -0
  72. package/dist/launch/open.js.map +1 -0
  73. package/dist/launch/platform.js +38 -0
  74. package/dist/launch/platform.js.map +1 -0
  75. package/dist/launch/pty.js +61 -0
  76. package/dist/launch/pty.js.map +1 -0
  77. package/dist/launch/terminal.js +194 -0
  78. package/dist/launch/terminal.js.map +1 -0
  79. package/dist/launch/which.js +59 -0
  80. package/dist/launch/which.js.map +1 -0
  81. package/dist/repo/diff.js +26 -0
  82. package/dist/repo/diff.js.map +1 -0
  83. package/dist/repo/framework.js +41 -0
  84. package/dist/repo/framework.js.map +1 -0
  85. package/dist/repo/link.js +28 -0
  86. package/dist/repo/link.js.map +1 -0
  87. package/dist/repo/prep.js +447 -0
  88. package/dist/repo/prep.js.map +1 -0
  89. package/dist/repo/preview.js +144 -0
  90. package/dist/repo/preview.js.map +1 -0
  91. package/dist/repo/root.js +48 -0
  92. package/dist/repo/root.js.map +1 -0
  93. package/dist/repo/scratch.js +111 -0
  94. package/dist/repo/scratch.js.map +1 -0
  95. package/dist/runs/checks.js +160 -0
  96. package/dist/runs/checks.js.map +1 -0
  97. package/dist/runs/lifecycle.js +142 -0
  98. package/dist/runs/lifecycle.js.map +1 -0
  99. package/dist/runs/promote.js +185 -0
  100. package/dist/runs/promote.js.map +1 -0
  101. package/dist/runs/pull.js +182 -0
  102. package/dist/runs/pull.js.map +1 -0
  103. package/dist/runs/reviews.js +34 -0
  104. package/dist/runs/reviews.js.map +1 -0
  105. package/dist/runs/slots.js +96 -0
  106. package/dist/runs/slots.js.map +1 -0
  107. package/dist/runs/snapshot.js +265 -0
  108. package/dist/runs/snapshot.js.map +1 -0
  109. package/dist/runs/store.js +38 -0
  110. package/dist/runs/store.js.map +1 -0
  111. package/dist/runs/types.js +3 -0
  112. package/dist/runs/types.js.map +1 -0
  113. package/dist/runs/watch.js +42 -0
  114. package/dist/runs/watch.js.map +1 -0
  115. package/dist/screenshot/browser.js +163 -0
  116. package/dist/screenshot/browser.js.map +1 -0
  117. package/dist/screenshot/iteration.js +37 -0
  118. package/dist/screenshot/iteration.js.map +1 -0
  119. package/dist/server/app.js +321 -0
  120. package/dist/server/app.js.map +1 -0
  121. package/dist/server/security.js +44 -0
  122. package/dist/server/security.js.map +1 -0
  123. package/dist/server/studio.js +618 -0
  124. package/dist/server/studio.js.map +1 -0
  125. package/dist/server/terminals.js +142 -0
  126. package/dist/server/terminals.js.map +1 -0
  127. package/dist/ui/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  128. package/dist/ui/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  129. package/dist/ui/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  130. package/dist/ui/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  131. package/dist/ui/assets/index-D-TSXfvz.css +1 -0
  132. package/dist/ui/assets/index-HGV8FKwI.js +27 -0
  133. package/dist/ui/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
  134. package/dist/ui/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
  135. package/dist/ui/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
  136. package/dist/ui/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
  137. package/dist/ui/assets/space-grotesk-latin-400-normal-BnQMeOim.woff +0 -0
  138. package/dist/ui/assets/space-grotesk-latin-400-normal-CJ-V5oYT.woff2 +0 -0
  139. package/dist/ui/assets/space-grotesk-latin-500-normal-CNSSEhBt.woff +0 -0
  140. package/dist/ui/assets/space-grotesk-latin-500-normal-lFbtlQH6.woff2 +0 -0
  141. package/dist/ui/index.html +14 -0
  142. package/dist/version.js +4 -0
  143. package/dist/version.js.map +1 -0
  144. package/package.json +68 -4
@@ -0,0 +1,447 @@
1
+ // Prepares a repo for the studio: a rough/ workspace that TypeScript and ESLint ignore, so an
2
+ // iteration can never break the app's build; the development-only preview route; and git told
3
+ // what to keep. Planning only reads; applying writes exactly what the plan showed, so `init` can
4
+ // show every change first. Ported from roughbook src/lib/delivery/init.ts (v2-local), which kept
5
+ // rough/ out of git; now committing it is the default, since git is its only record.
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import { renderRepoLink } from './link.js';
9
+ import { PREVIEW_WRAPPER, REGISTRY_FILE, detectAppDir, isOurPreviewFile, listIterations, previewRouteDir, previewRouteFiles, renderPreviewRegistry } from './preview.js';
10
+ import { LINK_FILE, ROUGH_DIR, STATE_DIR, isHomeOrAbove } from './root.js';
11
+ const GITIGNORE_HEADER = '# Roughbook';
12
+ const OLD_GITIGNORE_HEADER = '# Roughbook (local only)';
13
+ const OURS = 'Written by `roughbook init`';
14
+ function readme(committed) {
15
+ return `# rough/
16
+
17
+ Roughbook's design workspace for this repo: design iterations, the prompts that
18
+ made them, and the link to this repo's Roughbook project (\`roughbook.json\`).
19
+
20
+ TypeScript and ESLint ignore this folder, so an iteration can never fail the
21
+ app's build, and nothing in the app imports from it. \`roughbook promote\` turns
22
+ a chosen iteration into a real component.
23
+
24
+ ${committed
25
+ ? `This folder is committed: git is the only record of these iterations, since
26
+ Roughbook's server keeps only their review decisions. Run state
27
+ (\`${STATE_DIR}/\`) and screenshots (\`.screenshots/\`) are not committed.`
28
+ : `This folder is not committed (see .gitignore), so nothing backs these
29
+ iterations up. Run \`roughbook init\` again to commit it.`}
30
+
31
+ ${OURS}; it rewrites this file.
32
+ `;
33
+ }
34
+ /** The iteration rules every agent reads when it works in rough/, whichever agent it is. */
35
+ export const ROUGH_AGENTS = `# Working in rough/
36
+
37
+ This folder is Roughbook's design workspace. Each iteration is a folder
38
+ \`<page>/<component>/iteration-NN/\` whose \`index.tsx\` default-exports the design.
39
+ Each run's instructions are in \`<page>/<component>/prompts/\`.
40
+
41
+ Rules for any agent working here:
42
+
43
+ - Write only inside the iteration folder your instructions name.
44
+ - Never edit an existing iteration. A new idea is a new iteration, and a change
45
+ to one is a fork or refinement that Roughbook creates for you.
46
+ - An iteration never imports from another iteration. It may import the app's
47
+ own components, styles and utilities.
48
+ - Nothing outside rough/ imports from rough/.
49
+ - Do not install dependencies, change the app's configuration, or commit.
50
+
51
+ ${OURS}; it rewrites this file.
52
+ `;
53
+ /** Claude Code reads CLAUDE.md, so it points at the same rules. */
54
+ export const ROUGH_CLAUDE = '@AGENTS.md\n';
55
+ /** Why a folder cannot be prepared, or nothing. */
56
+ export function checkRepo(repo, home) {
57
+ if (!path.isAbsolute(repo))
58
+ return ['The repo path must be absolute.'];
59
+ if (isHomeOrAbove(repo, home)) {
60
+ return ['That is your home folder, or contains it. Run roughbook init inside the app’s repo.'];
61
+ }
62
+ let stat;
63
+ try {
64
+ stat = fs.statSync(repo);
65
+ }
66
+ catch {
67
+ return ['That folder does not exist.'];
68
+ }
69
+ if (!stat.isDirectory())
70
+ return ['That path is a file, not a folder.'];
71
+ if (!fs.existsSync(path.join(repo, 'package.json'))) {
72
+ return ['This folder has no package.json, so it is not an app the studio can work in.'];
73
+ }
74
+ return [];
75
+ }
76
+ function read(repo, file) {
77
+ try {
78
+ return fs.readFileSync(path.join(repo, file), 'utf8');
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ /** JSON with comments and trailing commas, as tsconfig and .eslintrc allow, made parseable. */
85
+ export function stripJsonc(text) {
86
+ let out = '';
87
+ let inString = false;
88
+ for (let i = 0; i < text.length; i += 1) {
89
+ const char = text[i];
90
+ const next = text[i + 1];
91
+ if (inString) {
92
+ out += char;
93
+ if (char === '\\') {
94
+ out += next ?? '';
95
+ i += 1;
96
+ }
97
+ else if (char === '"') {
98
+ inString = false;
99
+ }
100
+ continue;
101
+ }
102
+ if (char === '"') {
103
+ inString = true;
104
+ out += char;
105
+ }
106
+ else if (char === '/' && next === '/') {
107
+ while (i < text.length && text[i] !== '\n')
108
+ i += 1;
109
+ out += '\n';
110
+ }
111
+ else if (char === '/' && next === '*') {
112
+ i += 2;
113
+ while (i < text.length && !(text[i] === '*' && text[i + 1] === '/'))
114
+ i += 1;
115
+ i += 1;
116
+ }
117
+ else {
118
+ out += char;
119
+ }
120
+ }
121
+ return out.replace(/,(\s*[}\]])/g, '$1');
122
+ }
123
+ function parseJsonc(text) {
124
+ try {
125
+ const value = JSON.parse(stripJsonc(text));
126
+ return value && typeof value === 'object' && !Array.isArray(value)
127
+ ? value
128
+ : null;
129
+ }
130
+ catch {
131
+ return null;
132
+ }
133
+ }
134
+ /** A path pattern written any of the usual ways: "rough", "./rough", "rough/", "/rough/**". */
135
+ function normalizePattern(entry) {
136
+ return entry
137
+ .trim()
138
+ .replace(/^\.?\//, '')
139
+ .replace(/\/(\*\*(\/\*)?)?$/, '')
140
+ .replace(/\/$/, '');
141
+ }
142
+ function covers(entry, target) {
143
+ return normalizePattern(entry) === normalizePattern(target);
144
+ }
145
+ /** A list written one item per line gets the new item on its own line. */
146
+ function afterComma(space) {
147
+ return space.includes('\n') ? space : ' ';
148
+ }
149
+ /**
150
+ * Brings .gitignore to `wanted` (entries written as "/path/" or "/file") and takes out any line
151
+ * in `unwanted`: a repo prepared before rough/ was committed ignores the whole folder.
152
+ */
153
+ export function planGitignore(current, wanted, unwanted = []) {
154
+ const lines = (current ?? '').split(/\r?\n/);
155
+ const kept = lines
156
+ .filter(line => !unwanted.some(entry => covers(line, entry)))
157
+ // v2's header said "local only", which is no longer true once rough/ is committed.
158
+ .map(line => (line.trim() === OLD_GITIGNORE_HEADER ? GITIGNORE_HEADER : line));
159
+ const removed = lines.length - kept.length;
160
+ // Ignoring the whole of rough/ already covers what is inside it.
161
+ const ignoresRough = kept.some(line => covers(line, ROUGH_DIR));
162
+ const missing = wanted.filter(entry => !kept.some(line => covers(line, entry)) &&
163
+ !(ignoresRough && normalizePattern(entry).startsWith(`${ROUGH_DIR}/`)));
164
+ const body = kept.join('\n').replace(/\n*$/, '');
165
+ let after = body ? `${body}\n` : '';
166
+ if (missing.length) {
167
+ const hasHeader = kept.some(line => line.trim() === GITIGNORE_HEADER);
168
+ after = hasHeader
169
+ ? `${body}\n${missing.join('\n')}\n`
170
+ : `${body}${body ? '\n\n' : ''}${GITIGNORE_HEADER}\n${missing.join('\n')}\n`;
171
+ }
172
+ const renamed = lines.some(line => line.trim() === OLD_GITIGNORE_HEADER);
173
+ if (!missing.length && !removed && !renamed)
174
+ return null;
175
+ const parts = [
176
+ removed ? `stop ignoring ${ROUGH_DIR}/, so it is committed` : '',
177
+ missing.length ? `ignore ${missing.join(', ')}` : ''
178
+ ].filter(Boolean);
179
+ return {
180
+ file: '.gitignore',
181
+ action: current === null ? 'create' : 'edit',
182
+ summary: parts.length
183
+ ? `${parts.join('; ').replace(/^./, char => char.toUpperCase())}.`
184
+ : 'Rename the Roughbook section.',
185
+ after
186
+ };
187
+ }
188
+ /**
189
+ * Adds folders to tsconfig's exclude with a small text edit, keeping the file's own formatting
190
+ * and comments. A manual step when the edit cannot be made safely. An exclude that did not exist
191
+ * gets node_modules too, since setting exclude replaces TypeScript's default of excluding it.
192
+ */
193
+ export function planTsconfig(current, folders) {
194
+ if (current === null)
195
+ return null;
196
+ const list = (items) => items.map(item => JSON.stringify(item)).join(', ');
197
+ const parsed = parseJsonc(current);
198
+ if (!parsed) {
199
+ return {
200
+ manual: `tsconfig.json could not be read. Add ${list(folders)} to its "exclude" list by hand.`
201
+ };
202
+ }
203
+ const exclude = Array.isArray(parsed.exclude) ? parsed.exclude : null;
204
+ const missing = folders.filter(folder => !exclude?.some(item => typeof item === 'string' && covers(item, folder)));
205
+ if (!missing.length)
206
+ return null;
207
+ const items = missing.map(folder => JSON.stringify(folder));
208
+ let after;
209
+ if (exclude) {
210
+ after = current.replace(/("exclude"\s*:\s*\[)(\s*)/, (_match, open, space) => exclude.length
211
+ ? `${open}${space}${items.map(item => `${item},${afterComma(space)}`).join('')}`
212
+ : `${open}${space}${items.join(', ')}`);
213
+ }
214
+ else {
215
+ const end = current.lastIndexOf('}');
216
+ if (end === -1) {
217
+ return {
218
+ manual: `Add "exclude": ["node_modules", ${list(missing)}] to tsconfig.json by hand.`
219
+ };
220
+ }
221
+ const head = current.slice(0, end).replace(/\s*$/, '');
222
+ const needsComma = !head.endsWith('{') && !head.endsWith(',');
223
+ after = `${head}${needsComma ? ',' : ''}\n "exclude": ["node_modules", ${items.join(', ')}]\n${current.slice(end)}`;
224
+ }
225
+ const check = parseJsonc(after);
226
+ const ok = Array.isArray(check?.exclude) &&
227
+ missing.every(folder => check.exclude.includes(folder));
228
+ if (!ok)
229
+ return { manual: `Add ${list(missing)} to the "exclude" list in tsconfig.json by hand.` };
230
+ return {
231
+ file: 'tsconfig.json',
232
+ action: 'edit',
233
+ summary: `Exclude ${missing.map(folder => `${folder}/`).join(' and ')} from type checking, so an iteration can never fail the build.`,
234
+ after
235
+ };
236
+ }
237
+ const TAILWIND_IMPORT = /^\s*@import\s+["']tailwindcss["'][^;\n]*;?[^\n]*$/m;
238
+ const TAILWIND_CONFIGS = [
239
+ 'tailwind.config.js',
240
+ 'tailwind.config.ts',
241
+ 'tailwind.config.mjs',
242
+ 'tailwind.config.cjs'
243
+ ];
244
+ function cssFiles(dir) {
245
+ if (!fs.existsSync(dir))
246
+ return [];
247
+ return fs.readdirSync(dir, { withFileTypes: true }).flatMap(entry => {
248
+ const full = path.join(dir, entry.name);
249
+ if (entry.isDirectory()) {
250
+ return entry.name === 'node_modules' || entry.name.startsWith('.') ? [] : cssFiles(full);
251
+ }
252
+ return entry.name.endsWith('.css') ? [full] : [];
253
+ });
254
+ }
255
+ /**
256
+ * Tailwind v4 finds classes by scanning the repo and skips gitignored folders, so classes used
257
+ * only in an ignored rough/ would never be generated. One @source line in the stylesheet that
258
+ * imports Tailwind fixes that, committed or not. Tailwind v3 is configured in JavaScript, so that
259
+ * is left to the designer.
260
+ */
261
+ export function planTailwindSource(repo, appDir) {
262
+ const v3 = TAILWIND_CONFIGS.find(name => fs.existsSync(path.join(repo, name)));
263
+ for (const file of [
264
+ ...cssFiles(path.join(repo, appDir)),
265
+ ...cssFiles(path.join(repo, 'src', 'styles')),
266
+ ...cssFiles(path.join(repo, 'styles'))
267
+ ]) {
268
+ const text = fs.readFileSync(file, 'utf8');
269
+ const match = TAILWIND_IMPORT.exec(text);
270
+ if (!match)
271
+ continue;
272
+ const toRough = path
273
+ .relative(path.dirname(file), path.join(repo, ROUGH_DIR))
274
+ .split(path.sep)
275
+ .join('/');
276
+ const escaped = toRough.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
277
+ if (new RegExp(`@source\\s+["']${escaped}`).test(text))
278
+ return null;
279
+ const end = match.index + match[0].length;
280
+ return {
281
+ file: path.relative(repo, file).split(path.sep).join('/'),
282
+ action: 'edit',
283
+ summary: `Let Tailwind generate the classes used in ${ROUGH_DIR}/.`,
284
+ after: `${text.slice(0, end)}\n@source "${toRough}";${text.slice(end)}`
285
+ };
286
+ }
287
+ if (v3) {
288
+ return {
289
+ manual: `Add "./${ROUGH_DIR}/**/*.{ts,tsx}" to "content" in ${v3}, so previews are styled.`
290
+ };
291
+ }
292
+ return null;
293
+ }
294
+ const FLAT_CONFIGS = [
295
+ 'eslint.config.mjs',
296
+ 'eslint.config.js',
297
+ 'eslint.config.ts',
298
+ 'eslint.config.cjs'
299
+ ];
300
+ export function planEslint(files) {
301
+ const flat = FLAT_CONFIGS.find(name => files[name] != null);
302
+ if (flat) {
303
+ const text = files[flat];
304
+ if (/["'`]\.?\/?rough\//.test(text))
305
+ return null;
306
+ for (const anchor of [/(globalIgnores\(\s*\[)(\s*)/, /(ignores\s*:\s*\[)(\s*)/]) {
307
+ if (anchor.test(text)) {
308
+ return {
309
+ file: flat,
310
+ action: 'edit',
311
+ summary: `Ignore ${ROUGH_DIR}/ in ESLint.`,
312
+ after: text.replace(anchor, (_match, open, space) => `${open}${space}"${ROUGH_DIR}/**",${afterComma(space)}`)
313
+ };
314
+ }
315
+ }
316
+ return { manual: `Add "${ROUGH_DIR}/**" to the ignores in ${flat} by hand.` };
317
+ }
318
+ const ignoreFile = files['.eslintignore'];
319
+ if (ignoreFile != null) {
320
+ if (/^\/?rough\/?(\*\*)?$/m.test(ignoreFile))
321
+ return null;
322
+ return {
323
+ file: '.eslintignore',
324
+ action: 'edit',
325
+ summary: `Ignore ${ROUGH_DIR}/ in ESLint.`,
326
+ after: `${ignoreFile}${ignoreFile.endsWith('\n') || !ignoreFile ? '' : '\n'}${ROUGH_DIR}/\n`
327
+ };
328
+ }
329
+ if (files['.eslintrc.json'] != null) {
330
+ return { manual: `Add "${ROUGH_DIR}/**" to "ignorePatterns" in .eslintrc.json by hand.` };
331
+ }
332
+ return null;
333
+ }
334
+ /** A file the studio owns: written when missing, rewritten when ours and different. */
335
+ function ownFile(repo, file, content, summary, isOurs) {
336
+ const current = read(repo, file);
337
+ if (current === content)
338
+ return null;
339
+ if (current === null)
340
+ return { file, action: 'create', summary, after: content };
341
+ if (!isOurs(current))
342
+ return { manual: `${file} exists and was not written by Roughbook, so it was left alone.` };
343
+ return { file, action: 'edit', summary, after: content };
344
+ }
345
+ export function planPrep(repo, options) {
346
+ const problems = checkRepo(repo);
347
+ if (problems.length)
348
+ return { repo, problems, changes: [], manual: [], before: {} };
349
+ const root = path.resolve(repo);
350
+ const committed = options.link.roughCommitted;
351
+ const changes = [];
352
+ const manual = [];
353
+ const add = (result) => {
354
+ if (!result)
355
+ return;
356
+ if ('manual' in result)
357
+ manual.push(result.manual);
358
+ else
359
+ changes.push(result);
360
+ };
361
+ const appDir = detectAppDir(root);
362
+ const link = { ...options.link, appDir };
363
+ add(ownFile(root, `${ROUGH_DIR}/README.md`, readme(committed), `Explain the ${ROUGH_DIR}/ workspace.`,
364
+ // v2's README said the folder was never committed.
365
+ text => text.includes(OURS) || text.startsWith("# rough/\n\nRoughbook's local design workspace")));
366
+ add(ownFile(root, `${ROUGH_DIR}/AGENTS.md`, ROUGH_AGENTS, 'Give every agent the iteration rules.', text => text.includes(OURS)));
367
+ add(ownFile(root, `${ROUGH_DIR}/CLAUDE.md`, ROUGH_CLAUDE, 'Point Claude Code at the same rules.', text => text.trim() === ROUGH_CLAUDE.trim()));
368
+ const linkFile = `${ROUGH_DIR}/${LINK_FILE}`;
369
+ const linkText = renderRepoLink(link);
370
+ const currentLink = read(root, linkFile);
371
+ if (currentLink !== linkText) {
372
+ changes.push({
373
+ file: linkFile,
374
+ action: currentLink === null ? 'create' : 'edit',
375
+ summary: `Link this repo to the project ${link.project}.`,
376
+ after: linkText
377
+ });
378
+ }
379
+ const ignoredFromBuild = [ROUGH_DIR];
380
+ const gitWanted = committed
381
+ ? [
382
+ `/${ROUGH_DIR}/${STATE_DIR}/`,
383
+ `/${ROUGH_DIR}/.screenshots/`,
384
+ `/${ROUGH_DIR}/${REGISTRY_FILE}`
385
+ ]
386
+ : [`/${ROUGH_DIR}/`];
387
+ if (!appDir) {
388
+ manual.push('No App Router folder (app/ or src/app/) was found, so previews cannot render through this app. They need the Next.js App Router.');
389
+ }
390
+ else {
391
+ const routeDir = previewRouteDir(appDir);
392
+ ignoredFromBuild.push(routeDir);
393
+ gitWanted.push(`/${routeDir}/`);
394
+ for (const route of previewRouteFiles(appDir)) {
395
+ const current = read(root, route.file);
396
+ if (current === route.content)
397
+ continue;
398
+ if (current !== null && !isOurPreviewFile(current)) {
399
+ manual.push(`${route.file} exists and was not written by Roughbook, so it was left alone.`);
400
+ continue;
401
+ }
402
+ changes.push({
403
+ file: route.file,
404
+ action: current === null ? 'create' : 'edit',
405
+ summary: current === null
406
+ ? 'Add the development-only preview route.'
407
+ : 'Update the preview route.',
408
+ after: route.content
409
+ });
410
+ }
411
+ if (read(root, `${ROUGH_DIR}/preview.tsx`) === null) {
412
+ changes.push({
413
+ file: `${ROUGH_DIR}/preview.tsx`,
414
+ action: 'create',
415
+ summary: "Add the wrapper for the app's providers.",
416
+ after: PREVIEW_WRAPPER
417
+ });
418
+ }
419
+ add(planTailwindSource(root, appDir));
420
+ if (read(root, `${ROUGH_DIR}/${REGISTRY_FILE}`) === null) {
421
+ changes.push({
422
+ file: `${ROUGH_DIR}/${REGISTRY_FILE}`,
423
+ action: 'create',
424
+ summary: 'Add the registry of iterations the route can render.',
425
+ after: renderPreviewRegistry(listIterations(root))
426
+ });
427
+ }
428
+ }
429
+ add(planGitignore(read(root, '.gitignore'), gitWanted, committed ? [ROUGH_DIR] : []));
430
+ add(planTsconfig(read(root, 'tsconfig.json'), ignoredFromBuild));
431
+ add(planEslint(Object.fromEntries([...FLAT_CONFIGS, '.eslintignore', '.eslintrc.json'].map(name => [name, read(root, name)]))));
432
+ const before = Object.fromEntries(changes
433
+ .filter(change => change.action === 'edit')
434
+ .map(change => [change.file, read(root, change.file) ?? '']));
435
+ return { repo: root, problems, changes, manual, before };
436
+ }
437
+ /** Writes a plan. Each file is written whole, as the plan showed it, with `\n` line endings. */
438
+ export function applyPrep(plan) {
439
+ if (plan.problems.length)
440
+ throw new Error(plan.problems[0]);
441
+ for (const change of plan.changes) {
442
+ const file = path.join(plan.repo, ...change.file.split('/'));
443
+ fs.mkdirSync(path.dirname(file), { recursive: true });
444
+ fs.writeFileSync(file, change.after);
445
+ }
446
+ }
447
+ //# sourceMappingURL=prep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prep.js","sourceRoot":"","sources":["../../src/repo/prep.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,144 @@
1
+ // Iterations live in the repo's rough/ and render through the repo's own dev server, so they can
2
+ // use its components, fonts and tokens. Three pieces make that work:
3
+ //
4
+ // <app>/rough-preview/[...slug]/page.tsx the route (development only, gitignored)
5
+ // rough/preview-registry.ts one loader per iteration, generated (gitignored)
6
+ // rough/preview.tsx wraps iterations in the app's providers
7
+ //
8
+ // The route loads the registry only when NODE_ENV is "development", inside a branch the bundler
9
+ // removes from production builds, so a broken iteration can never fail the app's build. The
10
+ // folder is "rough-preview", not "_rough": Next.js treats folders starting with "_" as private.
11
+ // Ported from roughbook src/lib/delivery/preview.ts (v2-local).
12
+ import fs from 'node:fs';
13
+ import path from 'node:path';
14
+ import { ROUGH_DIR } from './root.js';
15
+ export const PREVIEW_ROUTE = 'rough-preview';
16
+ export const REGISTRY_FILE = 'preview-registry.ts';
17
+ const MARKER = 'Roughbook preview route';
18
+ export const ITERATION = /^iteration-\d{2,}$/;
19
+ export const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
20
+ /** Where the App Router lives in this repo, or null when it has none. */
21
+ export function detectAppDir(repo) {
22
+ for (const candidate of ['src/app', 'app']) {
23
+ if (fs.existsSync(path.join(repo, candidate)))
24
+ return candidate;
25
+ }
26
+ return null;
27
+ }
28
+ export function previewRouteDir(appDir) {
29
+ return `${appDir}/${PREVIEW_ROUTE}`;
30
+ }
31
+ /** An iteration's folder, relative to the repo, with forward slashes. */
32
+ export function iterationDir(page, component, iteration) {
33
+ return `${ROUGH_DIR}/${page}/${component}/${iteration}`;
34
+ }
35
+ export function previewUrl(devUrl, page, component, iteration) {
36
+ return `${devUrl.replace(/\/$/, '')}/${PREVIEW_ROUTE}/${page}/${component}/${iteration}`;
37
+ }
38
+ /** The route's two files. Imports climb from <app>/rough-preview/[...slug]/ to the repo root. */
39
+ export function previewRouteFiles(appDir) {
40
+ const up = '../'.repeat(appDir.split('/').length + 2);
41
+ const dir = `${previewRouteDir(appDir)}/[...slug]`;
42
+ return [
43
+ {
44
+ file: `${dir}/page.tsx`,
45
+ content: `// ${MARKER}. Development only, not committed (see .gitignore).
46
+ // Renders one iteration from ${ROUGH_DIR}/ with this app's own layout, styles and components.
47
+ import { notFound } from "next/navigation";
48
+ import { Ready } from "./ready";
49
+
50
+ export default async function RoughPreview({ params }: { params: Promise<{ slug: string[] }> }) {
51
+ // The bundler drops this branch from production builds, so nothing in ${ROUGH_DIR}/ is ever built for production.
52
+ const registry = process.env.NODE_ENV === "development" ? await import("${up}${ROUGH_DIR}/preview-registry") : null;
53
+ if (!registry) notFound();
54
+ const key = (await params).slug.join("/");
55
+ const load = registry.loaders[key];
56
+ if (!load) notFound();
57
+ const { default: Iteration } = await load();
58
+ const { Preview } = registry;
59
+ return (
60
+ <>
61
+ <Preview>
62
+ <Iteration />
63
+ </Preview>
64
+ <Ready id={key} />
65
+ </>
66
+ );
67
+ }
68
+ `
69
+ },
70
+ {
71
+ file: `${dir}/ready.tsx`,
72
+ content: `"use client";
73
+ // ${MARKER}. Tells Roughbook the iteration rendered, so it does not fall back to a screenshot.
74
+ import { useEffect } from "react";
75
+
76
+ export function Ready({ id }: { id: string }) {
77
+ useEffect(() => {
78
+ window.parent.postMessage({ roughbook: "ready", id }, "*");
79
+ }, [id]);
80
+ return null;
81
+ }
82
+ `
83
+ }
84
+ ];
85
+ }
86
+ export const PREVIEW_WRAPPER = `// Wraps every iteration previewed through this app. Add the providers your
87
+ // components need (theme, i18n, query client), as your root layout does.
88
+ import type { ReactNode } from "react";
89
+
90
+ export default function Preview({ children }: { children: ReactNode }) {
91
+ return <>{children}</>;
92
+ }
93
+ `;
94
+ function subdirs(dir) {
95
+ if (!fs.existsSync(dir))
96
+ return [];
97
+ return fs
98
+ .readdirSync(dir, { withFileTypes: true })
99
+ .filter(entry => entry.isDirectory())
100
+ .map(entry => entry.name)
101
+ .sort();
102
+ }
103
+ /** Every iteration under rough/<page>/<component>/iteration-NN/ with an index.tsx, as keys. */
104
+ export function listIterations(repo) {
105
+ const root = path.join(repo, ROUGH_DIR);
106
+ const keys = [];
107
+ for (const page of subdirs(root).filter(name => SLUG.test(name))) {
108
+ for (const component of subdirs(path.join(root, page)).filter(name => SLUG.test(name))) {
109
+ for (const iteration of subdirs(path.join(root, page, component)).filter(name => ITERATION.test(name))) {
110
+ if (fs.existsSync(path.join(root, page, component, iteration, 'index.tsx'))) {
111
+ keys.push(`${page}/${component}/${iteration}`);
112
+ }
113
+ }
114
+ }
115
+ }
116
+ return keys;
117
+ }
118
+ export function renderPreviewRegistry(keys) {
119
+ const lines = keys.map(key => ` ${JSON.stringify(key)}: () => import(${JSON.stringify(`./${key}/index`)}),`);
120
+ return `// Generated by Roughbook from the iterations in this folder. Do not edit.
121
+ import type { ComponentType } from "react";
122
+
123
+ export { default as Preview } from "./preview";
124
+
125
+ export const loaders: Record<string, () => Promise<{ default: ComponentType }>> = {
126
+ ${lines.map(line => `${line}\n`).join('')}};
127
+ `;
128
+ }
129
+ /** Rewrites rough/preview-registry.ts when the iterations changed. Returns whether it wrote. */
130
+ export function writePreviewRegistry(repo) {
131
+ const file = path.join(repo, ROUGH_DIR, REGISTRY_FILE);
132
+ const next = renderPreviewRegistry(listIterations(repo));
133
+ const current = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
134
+ if (current === next)
135
+ return false;
136
+ fs.mkdirSync(path.dirname(file), { recursive: true });
137
+ fs.writeFileSync(file, next);
138
+ return true;
139
+ }
140
+ /** Whether a file is one Roughbook wrote for the preview route, so it may be updated. */
141
+ export function isOurPreviewFile(text) {
142
+ return Boolean(text?.includes(MARKER));
143
+ }
144
+ //# sourceMappingURL=preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.js","sourceRoot":"","sources":["../../src/repo/preview.ts"],"names":[],"mappings":"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"}