@frontman-ai/astro 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 (36) hide show
  1. package/README.md +53 -0
  2. package/dist/cli.js +2744 -0
  3. package/package.json +66 -0
  4. package/src/FrontmanAstro.res +20 -0
  5. package/src/FrontmanAstro.res.mjs +36 -0
  6. package/src/FrontmanAstro__AstroBindings.res +46 -0
  7. package/src/FrontmanAstro__AstroBindings.res.mjs +2 -0
  8. package/src/FrontmanAstro__Config.res +85 -0
  9. package/src/FrontmanAstro__Config.res.mjs +44 -0
  10. package/src/FrontmanAstro__Integration.res +35 -0
  11. package/src/FrontmanAstro__Integration.res.mjs +36 -0
  12. package/src/FrontmanAstro__Middleware.res +149 -0
  13. package/src/FrontmanAstro__Middleware.res.mjs +141 -0
  14. package/src/FrontmanAstro__Server.res +196 -0
  15. package/src/FrontmanAstro__Server.res.mjs +241 -0
  16. package/src/FrontmanAstro__ToolRegistry.res +21 -0
  17. package/src/FrontmanAstro__ToolRegistry.res.mjs +41 -0
  18. package/src/FrontmanAstro__ToolbarApp.res +50 -0
  19. package/src/FrontmanAstro__ToolbarApp.res.mjs +39 -0
  20. package/src/cli/FrontmanAstro__Cli.res +126 -0
  21. package/src/cli/FrontmanAstro__Cli.res.mjs +180 -0
  22. package/src/cli/FrontmanAstro__Cli__AutoEdit.res +300 -0
  23. package/src/cli/FrontmanAstro__Cli__AutoEdit.res.mjs +266 -0
  24. package/src/cli/FrontmanAstro__Cli__Detect.res +298 -0
  25. package/src/cli/FrontmanAstro__Cli__Detect.res.mjs +345 -0
  26. package/src/cli/FrontmanAstro__Cli__Files.res +244 -0
  27. package/src/cli/FrontmanAstro__Cli__Files.res.mjs +321 -0
  28. package/src/cli/FrontmanAstro__Cli__Install.res +224 -0
  29. package/src/cli/FrontmanAstro__Cli__Install.res.mjs +194 -0
  30. package/src/cli/FrontmanAstro__Cli__Style.res +22 -0
  31. package/src/cli/FrontmanAstro__Cli__Style.res.mjs +61 -0
  32. package/src/cli/FrontmanAstro__Cli__Templates.res +226 -0
  33. package/src/cli/FrontmanAstro__Cli__Templates.res.mjs +237 -0
  34. package/src/cli/cli.mjs +3 -0
  35. package/src/tools/FrontmanAstro__Tool__GetPages.res +164 -0
  36. package/src/tools/FrontmanAstro__Tool__GetPages.res.mjs +180 -0
@@ -0,0 +1,194 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Nodepath from "node:path";
4
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
+ import * as ChildProcess$FrontmanBindings from "@frontman/bindings/src/ChildProcess.res.mjs";
6
+ import * as FrontmanAstro__Cli__Files$FrontmanAiAstro from "./FrontmanAstro__Cli__Files.res.mjs";
7
+ import * as FrontmanAstro__Cli__Style$FrontmanAiAstro from "./FrontmanAstro__Cli__Style.res.mjs";
8
+ import * as FrontmanAstro__Cli__Detect$FrontmanAiAstro from "./FrontmanAstro__Cli__Detect.res.mjs";
9
+ import * as FrontmanAstro__Cli__AutoEdit$FrontmanAiAstro from "./FrontmanAstro__Cli__AutoEdit.res.mjs";
10
+ import * as FrontmanAstro__Cli__Templates$FrontmanAiAstro from "./FrontmanAstro__Cli__Templates.res.mjs";
11
+
12
+ async function installDependencies(projectDir, packageManager, dryRun) {
13
+ let pm = FrontmanAstro__Cli__Detect$FrontmanAiAstro.getPackageManagerCommand(packageManager);
14
+ let args = FrontmanAstro__Cli__Detect$FrontmanAiAstro.getInstallArgs(packageManager, undefined);
15
+ let packages = [
16
+ "@frontman-ai/astro",
17
+ "@astrojs/node"
18
+ ];
19
+ let packages$1;
20
+ packages$1 = packageManager === "Deno" ? packages.map(p => "npm:" + p) : packages;
21
+ let cmd = pm + ` ` + args.join(" ") + ` ` + packages$1.join(" ");
22
+ if (dryRun) {
23
+ console.log(` ` + FrontmanAstro__Cli__Style$FrontmanAiAstro.dim(`Would run: ` + cmd));
24
+ return {
25
+ TAG: "Ok",
26
+ _0: undefined
27
+ };
28
+ }
29
+ console.log(` ` + FrontmanAstro__Cli__Style$FrontmanAiAstro.purple("Installing dependencies with " + pm + "..."));
30
+ let err = await ChildProcess$FrontmanBindings.execWithOptions(cmd, {
31
+ cwd: projectDir
32
+ });
33
+ if (err.TAG === "Ok") {
34
+ console.log(` ` + FrontmanAstro__Cli__Style$FrontmanAiAstro.check + ` Dependencies installed`);
35
+ return {
36
+ TAG: "Ok",
37
+ _0: undefined
38
+ };
39
+ }
40
+ let err$1 = err._0;
41
+ let stderr = err$1.stderr === "" ? "Unknown error" : err$1.stderr;
42
+ return {
43
+ TAG: "Error",
44
+ _0: `Failed to install dependencies: ` + stderr
45
+ };
46
+ }
47
+
48
+ function processFileResult(result, manualSteps) {
49
+ if (result.TAG === "Ok") {
50
+ let fileResult = result._0;
51
+ console.log(FrontmanAstro__Cli__Files$FrontmanAiAstro.formatResult(fileResult));
52
+ if (fileResult.TAG === "ManualEditRequired") {
53
+ manualSteps.push(fileResult.details);
54
+ }
55
+ return {
56
+ TAG: "Ok",
57
+ _0: undefined
58
+ };
59
+ }
60
+ let msg = result._0;
61
+ console.error(` ` + FrontmanAstro__Cli__Style$FrontmanAiAstro.warn + ` ` + FrontmanAstro__Cli__Style$FrontmanAiAstro.bold("Error:") + ` ` + msg);
62
+ return {
63
+ TAG: "Error",
64
+ _0: msg
65
+ };
66
+ }
67
+
68
+ function collectPendingAutoEdits(projectDir, host, info) {
69
+ let pending = [];
70
+ let p = FrontmanAstro__Cli__Files$FrontmanAiAstro.getPendingAutoEdit(info.config, Nodepath.join(projectDir, info.configFileName), info.configFileName, "Config", FrontmanAstro__Cli__Templates$FrontmanAiAstro.ManualInstructions.config(info.configFileName, host));
71
+ if (p !== undefined) {
72
+ pending.push(p);
73
+ }
74
+ let p$1 = FrontmanAstro__Cli__Files$FrontmanAiAstro.getPendingAutoEdit(info.middleware, Nodepath.join(projectDir, info.middlewareFileName), info.middlewareFileName, "Middleware", FrontmanAstro__Cli__Templates$FrontmanAiAstro.ManualInstructions.middleware(info.middlewareFileName, host));
75
+ if (p$1 !== undefined) {
76
+ pending.push(p$1);
77
+ }
78
+ return pending;
79
+ }
80
+
81
+ async function run(options) {
82
+ let projectDir = Stdlib_Option.getOr(options.prefix, process.cwd());
83
+ let host = options.server;
84
+ console.log(FrontmanAstro__Cli__Templates$FrontmanAiAstro.banner());
85
+ console.log(` ` + FrontmanAstro__Cli__Style$FrontmanAiAstro.bullet + ` ` + FrontmanAstro__Cli__Style$FrontmanAiAstro.bold("Server:") + ` ` + host);
86
+ if (options.dryRun) {
87
+ console.log("");
88
+ console.log(FrontmanAstro__Cli__Templates$FrontmanAiAstro.SuccessMessages.dryRunHeader);
89
+ }
90
+ let msg = await FrontmanAstro__Cli__Detect$FrontmanAiAstro.detect(projectDir);
91
+ if (msg.TAG === "Ok") {
92
+ let info = msg._0;
93
+ let version = Stdlib_Option.getOrThrow(info.astroVersion, undefined).raw;
94
+ console.log(` ` + FrontmanAstro__Cli__Style$FrontmanAiAstro.bullet + ` ` + FrontmanAstro__Cli__Style$FrontmanAiAstro.bold("Detected:") + ` Astro ` + version);
95
+ console.log("");
96
+ if (!options.skipDeps) {
97
+ let msg$1 = await installDependencies(projectDir, info.packageManager, options.dryRun);
98
+ if (msg$1.TAG !== "Ok") {
99
+ console.error(` ` + FrontmanAstro__Cli__Style$FrontmanAiAstro.warn + ` ` + msg$1._0);
100
+ }
101
+ console.log("");
102
+ }
103
+ let pendingEdits = collectPendingAutoEdits(projectDir, host, info);
104
+ let match = pendingEdits.length !== 0;
105
+ let match$1 = options.dryRun;
106
+ let shouldAutoEdit;
107
+ if (match && !match$1) {
108
+ let fileNames = pendingEdits.map(p => p.fileName);
109
+ shouldAutoEdit = await FrontmanAstro__Cli__AutoEdit$FrontmanAiAstro.promptUserForAutoEdit(fileNames);
110
+ } else {
111
+ shouldAutoEdit = false;
112
+ }
113
+ let manualSteps = [];
114
+ let configResult = await FrontmanAstro__Cli__Files$FrontmanAiAstro.handleConfig(projectDir, host, info.configFileName, info.config, options.dryRun, shouldAutoEdit);
115
+ let msg$2 = processFileResult(configResult, manualSteps);
116
+ if (msg$2.TAG !== "Ok") {
117
+ return {
118
+ TAG: "Failure",
119
+ _0: msg$2._0
120
+ };
121
+ }
122
+ let middlewareResult = await FrontmanAstro__Cli__Files$FrontmanAiAstro.handleMiddleware(projectDir, host, info.middlewareFileName, info.middleware, options.dryRun, shouldAutoEdit);
123
+ let msg$3 = processFileResult(middlewareResult, manualSteps);
124
+ if (msg$3.TAG !== "Ok") {
125
+ return {
126
+ TAG: "Failure",
127
+ _0: msg$3._0
128
+ };
129
+ }
130
+ if (manualSteps.length !== 0) {
131
+ console.log("");
132
+ console.log(` ` + FrontmanAstro__Cli__Style$FrontmanAiAstro.divider);
133
+ console.log("");
134
+ console.log(` ` + FrontmanAstro__Cli__Style$FrontmanAiAstro.yellowBold("Manual steps required:"));
135
+ console.log("");
136
+ manualSteps.forEach(step => {
137
+ console.log(step);
138
+ });
139
+ console.log("");
140
+ return {
141
+ TAG: "PartialSuccess",
142
+ manualStepsRequired: manualSteps
143
+ };
144
+ }
145
+ if (!options.dryRun) {
146
+ let devCommand = FrontmanAstro__Cli__Detect$FrontmanAiAstro.getDevCommand(info.packageManager);
147
+ console.log("");
148
+ console.log(` ` + FrontmanAstro__Cli__Style$FrontmanAiAstro.divider);
149
+ console.log(FrontmanAstro__Cli__Templates$FrontmanAiAstro.SuccessMessages.installComplete(devCommand));
150
+ }
151
+ return "Success";
152
+ }
153
+ let msg$4 = msg._0;
154
+ console.error(` ` + FrontmanAstro__Cli__Style$FrontmanAiAstro.warn + ` ` + FrontmanAstro__Cli__Style$FrontmanAiAstro.bold("Error:") + ` ` + msg$4);
155
+ return {
156
+ TAG: "Failure",
157
+ _0: msg$4
158
+ };
159
+ }
160
+
161
+ let Bindings;
162
+
163
+ let ChildProcess;
164
+
165
+ let Path;
166
+
167
+ let Process;
168
+
169
+ let AutoEdit;
170
+
171
+ let Detect;
172
+
173
+ let Files;
174
+
175
+ let Templates;
176
+
177
+ let Style;
178
+
179
+ export {
180
+ Bindings,
181
+ ChildProcess,
182
+ Path,
183
+ Process,
184
+ AutoEdit,
185
+ Detect,
186
+ Files,
187
+ Templates,
188
+ Style,
189
+ installDependencies,
190
+ processFileResult,
191
+ collectPendingAutoEdits,
192
+ run,
193
+ }
194
+ /* node:path Not a pure module */
@@ -0,0 +1,22 @@
1
+ // ANSI terminal styling for CLI output
2
+ // Brand purple: #985DF7 (primary), #8051CD (secondary)
3
+
4
+ let esc = "\x1b"
5
+
6
+ // Color functions — wrap text with ANSI codes
7
+ let purple = text => `${esc}[38;2;152;93;247m${text}${esc}[0m`
8
+ let purpleBold = text => `${esc}[1;38;2;152;93;247m${text}${esc}[0m`
9
+ let purpleDim = text => `${esc}[38;2;128;81;205m${text}${esc}[0m`
10
+ let green = text => `${esc}[32m${text}${esc}[0m`
11
+ let yellow = text => `${esc}[33m${text}${esc}[0m`
12
+ let yellowBold = text => `${esc}[1;33m${text}${esc}[0m`
13
+ let bold = text => `${esc}[1m${text}${esc}[0m`
14
+ let dim = text => `${esc}[2m${text}${esc}[0m`
15
+
16
+ // Symbols
17
+ let check = green("✔")
18
+ let warn = yellow("⚠")
19
+ let bullet = purple("▸")
20
+
21
+ // Decorative
22
+ let divider = purple("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
@@ -0,0 +1,61 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+
4
+ let esc = "\x1b";
5
+
6
+ function purple(text) {
7
+ return esc + `[38;2;152;93;247m` + text + esc + `[0m`;
8
+ }
9
+
10
+ function purpleBold(text) {
11
+ return esc + `[1;38;2;152;93;247m` + text + esc + `[0m`;
12
+ }
13
+
14
+ function purpleDim(text) {
15
+ return esc + `[38;2;128;81;205m` + text + esc + `[0m`;
16
+ }
17
+
18
+ function green(text) {
19
+ return esc + `[32m` + text + esc + `[0m`;
20
+ }
21
+
22
+ function yellow(text) {
23
+ return esc + `[33m` + text + esc + `[0m`;
24
+ }
25
+
26
+ function yellowBold(text) {
27
+ return esc + `[1;33m` + text + esc + `[0m`;
28
+ }
29
+
30
+ function bold(text) {
31
+ return esc + `[1m` + text + esc + `[0m`;
32
+ }
33
+
34
+ function dim(text) {
35
+ return esc + `[2m` + text + esc + `[0m`;
36
+ }
37
+
38
+ let check = green("✔");
39
+
40
+ let warn = yellow("⚠");
41
+
42
+ let bullet = purple("▸");
43
+
44
+ let divider = purple("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
45
+
46
+ export {
47
+ esc,
48
+ purple,
49
+ purpleBold,
50
+ purpleDim,
51
+ green,
52
+ yellow,
53
+ yellowBold,
54
+ bold,
55
+ dim,
56
+ check,
57
+ warn,
58
+ bullet,
59
+ divider,
60
+ }
61
+ /* check Not a pure module */
@@ -0,0 +1,226 @@
1
+ // Templates for generated files
2
+ module Style = FrontmanAstro__Cli__Style
3
+
4
+ // ASCII art banner for the installer
5
+ let banner = () => {
6
+ let l1 = Style.purpleBold(" ___ _ ")
7
+ let l2 = Style.purpleBold(" | __| _ ___ _ _ | |_ _ __ __ _ _ _ ")
8
+ // Use double-quoted string for the backtick line
9
+ let l3 = Style.purpleBold(" | _| '_/ _ \\ ' \\| _| ' \\/ _` | ' \\ ")
10
+ let l4 = Style.purpleBold(" |_||_| \\___/_||_|\\__|_|_|_\\__,_|_||_|")
11
+ let tagline = Style.purpleDim(" AI that sees your DOM and edits your frontend")
12
+
13
+ `
14
+ ${l1}
15
+ ${l2}
16
+ ${l3}
17
+ ${l4}
18
+
19
+ ${tagline}
20
+ `
21
+ }
22
+
23
+ // src/middleware.ts template
24
+ let middlewareTemplate = (host: string) =>
25
+ `import { createMiddleware, makeConfig } from '@frontman-ai/astro';
26
+ import { defineMiddleware } from 'astro:middleware';
27
+
28
+ const config = makeConfig({ host: '${host}' });
29
+ const frontman = createMiddleware(config);
30
+
31
+ export const onRequest = defineMiddleware(async (context, next) => {
32
+ return frontman(context, next);
33
+ });
34
+ `
35
+
36
+ // astro.config.mjs template (for clean projects with no existing config)
37
+ let configTemplate = (host: string) => {
38
+ // host is unused in the config template (it's used via makeConfig in middleware),
39
+ // but we accept it to keep the API consistent and for future use
40
+ ignore(host)
41
+ `import { defineConfig } from 'astro/config';
42
+ import node from '@astrojs/node';
43
+ import { frontmanIntegration } from '@frontman-ai/astro/integration';
44
+
45
+ const isProd = process.env.NODE_ENV === 'production';
46
+
47
+ export default defineConfig({
48
+ // SSR needed in dev for Frontman middleware routes
49
+ ...(isProd ? {} : { output: 'server', adapter: node({ mode: 'standalone' }) }),
50
+ integrations: [frontmanIntegration()],
51
+ });
52
+ `
53
+ }
54
+
55
+ // Manual setup instructions (shown in summary when auto-edit is skipped)
56
+ module ManualInstructions = {
57
+ let config = (fileName: string, _host: string) => {
58
+ let h = Style.yellowBold
59
+ let s = Style.purple
60
+ let d = Style.dim
61
+ let b = Style.bold
62
+ let bar = Style.yellow("|")
63
+
64
+ ` ${bar}
65
+ ${bar} ${h(fileName)} needs manual modification.
66
+ ${bar}
67
+ ${bar} ${s("1.")} Add imports at the top of the file:
68
+ ${bar}
69
+ ${bar} ${d("import node from '@astrojs/node';")}
70
+ ${bar} ${d("import { frontmanIntegration } from '@frontman-ai/astro/integration';")}
71
+ ${bar}
72
+ ${bar} ${s("2.")} Add SSR config for dev mode ${d("(needed for middleware routes)")}:
73
+ ${bar}
74
+ ${bar} ${d("const isProd = process.env.NODE_ENV === 'production';")}
75
+ ${bar}
76
+ ${bar} ${d("export default defineConfig({")}
77
+ ${bar} ${d(" ...(isProd ? {} : { output: 'server', adapter: node({ mode: 'standalone' }) }),")}
78
+ ${bar} ${d(" // ... your existing config")}
79
+ ${bar} ${d("});")}
80
+ ${bar}
81
+ ${bar} ${s("3.")} Add ${b("frontmanIntegration()")} to your integrations array:
82
+ ${bar}
83
+ ${bar} ${d("integrations: [frontmanIntegration(), ...yourExistingIntegrations]")}
84
+ ${bar}
85
+ ${bar} ${b("Docs:")} ${d("https://frontman.sh/docs/astro")}
86
+ ${bar}`
87
+ }
88
+
89
+ let middleware = (fileName: string, host: string) => {
90
+ let h = Style.yellowBold
91
+ let s = Style.purple
92
+ let d = Style.dim
93
+ let b = Style.bold
94
+ let bar = Style.yellow("|")
95
+
96
+ ` ${bar}
97
+ ${bar} ${h(fileName)} needs manual modification.
98
+ ${bar}
99
+ ${bar} ${s("1.")} Add imports at the top of the file:
100
+ ${bar}
101
+ ${bar} ${d("import { createMiddleware, makeConfig } from '@frontman-ai/astro';")}
102
+ ${bar} ${d("import { defineMiddleware, sequence } from 'astro:middleware';")}
103
+ ${bar}
104
+ ${bar} ${s("2.")} Create the Frontman middleware instance ${d("(after imports)")}:
105
+ ${bar}
106
+ ${bar} ${d(`const frontmanConfig = makeConfig({ host: '${host}' });`)}
107
+ ${bar} ${d("const frontman = createMiddleware(frontmanConfig);")}
108
+ ${bar}
109
+ ${bar} ${s("3.")} Combine with your existing middleware using ${b("sequence()")}:
110
+ ${bar}
111
+ ${bar} ${d("const frontmanMiddleware = defineMiddleware(async (context, next) => {")}
112
+ ${bar} ${d(" return frontman(context, next);")}
113
+ ${bar} ${d("});")}
114
+ ${bar}
115
+ ${bar} ${d("export const onRequest = sequence(frontmanMiddleware, yourExistingMiddleware);")}
116
+ ${bar}
117
+ ${bar} ${b("Docs:")} ${d("https://frontman.sh/docs/astro")}
118
+ ${bar}`
119
+ }
120
+ }
121
+
122
+ // Keep plain-text versions for the LLM system prompt (no ANSI codes)
123
+ module ErrorMessages = {
124
+ let configManualSetup = (fileName: string, _host: string) =>
125
+ `
126
+ ${fileName} already exists and requires manual modification.
127
+
128
+ Add the following to your ${fileName}:
129
+
130
+ 1. Add imports at the top of the file:
131
+
132
+ import node from '@astrojs/node';
133
+ import { frontmanIntegration } from '@frontman-ai/astro/integration';
134
+
135
+ 2. Add SSR config for dev mode (needed for middleware routes):
136
+
137
+ const isProd = process.env.NODE_ENV === 'production';
138
+
139
+ export default defineConfig({
140
+ ...(isProd ? {} : { output: 'server', adapter: node({ mode: 'standalone' }) }),
141
+ // ... your existing config
142
+ });
143
+
144
+ 3. Add frontmanIntegration() to your integrations array:
145
+
146
+ integrations: [frontmanIntegration(), ...yourExistingIntegrations],
147
+
148
+ For full documentation, see: https://frontman.sh/docs/astro
149
+ `
150
+
151
+ let middlewareManualSetup = (fileName: string, host: string) =>
152
+ `
153
+ ${fileName} already exists and requires manual modification.
154
+
155
+ Add the following to your ${fileName}:
156
+
157
+ 1. Add imports at the top of the file:
158
+
159
+ import { createMiddleware, makeConfig } from '@frontman-ai/astro';
160
+ import { defineMiddleware, sequence } from 'astro:middleware';
161
+
162
+ 2. Create the Frontman middleware instance (after imports):
163
+
164
+ const frontmanConfig = makeConfig({ host: '${host}' });
165
+ const frontman = createMiddleware(frontmanConfig);
166
+
167
+ 3. Combine with your existing middleware using sequence():
168
+
169
+ const frontmanMiddleware = defineMiddleware(async (context, next) => {
170
+ return frontman(context, next);
171
+ });
172
+
173
+ export const onRequest = sequence(frontmanMiddleware, yourExistingMiddleware);
174
+
175
+ For full documentation, see: https://frontman.sh/docs/astro
176
+ `
177
+ }
178
+
179
+ // Success messages
180
+ module SuccessMessages = {
181
+ let fileCreated = (fileName: string) =>
182
+ ` ${Style.check} Created ${Style.bold(fileName)}`
183
+
184
+ let fileSkipped = (fileName: string) =>
185
+ ` ${Style.purple("–")} Skipped ${Style.bold(fileName)} ${Style.dim("(already configured)")}`
186
+
187
+ let hostUpdated = (fileName: string, oldHost: string, newHost: string) =>
188
+ ` ${Style.check} Updated ${Style.bold(fileName)} ${Style.dim(`(host: '${oldHost}' -> '${newHost}')`)}`
189
+
190
+ let fileAutoEdited = (fileName: string) =>
191
+ ` ${Style.check} Auto-edited ${Style.bold(fileName)} ${Style.dim("(Frontman integrated via AI)")}`
192
+
193
+ let autoEditFailed = (fileName: string, error: string) =>
194
+ ` ${Style.warn} Auto-edit failed for ${Style.bold(fileName)}: ${Style.dim(error)}`
195
+
196
+ let manualEditRequired = (fileName: string) =>
197
+ ` ${Style.warn} ${Style.bold(fileName)} requires manual setup ${Style.dim("(see details below)")}`
198
+
199
+ let installComplete = (~devCommand: string) => {
200
+ let p = Style.purple
201
+ let pb = Style.purpleBold
202
+ let d = Style.dim
203
+ let pd = Style.purpleDim
204
+
205
+ `
206
+ ${pb("Frontman setup complete!")}
207
+
208
+ ${pb("Next steps:")}
209
+ ${p("1.")} Start your dev server ${d(devCommand)}
210
+ ${p("2.")} Open your browser to ${d("http://localhost:4321/frontman")}
211
+
212
+ ${p("┌───────────────────────────────────────────────┐")}
213
+ ${p("│")} ${p("│")}
214
+ ${p("│")} Questions? Comments? Need support? ${p("│")}
215
+ ${p("│")} ${p("│")}
216
+ ${p("│")} Join us on Discord: ${p("│")}
217
+ ${p("│")} ${pd("https://discord.gg/J77jBzMM")} ${p("│")}
218
+ ${p("│")} ${p("│")}
219
+ ${p("└───────────────────────────────────────────────┘")}
220
+ `
221
+ }
222
+
223
+ let dryRunHeader =
224
+ ` ${Style.warn} ${Style.yellowBold("DRY RUN MODE")} ${Style.dim("— No files will be created")}
225
+ `
226
+ }