@marwes-ui/cli 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.
package/dist/index.js ADDED
@@ -0,0 +1,843 @@
1
+ import { parseArgs } from 'util';
2
+ import { resolve, join } from 'path';
3
+ import { spawn } from 'child_process';
4
+ import { readFile, access, readdir, writeFile } from 'fs/promises';
5
+
6
+ // src/index.ts
7
+ var defaultCommandRunner = (command, options) => {
8
+ return new Promise((resolve2, reject) => {
9
+ const child = spawn(command.command, command.args, {
10
+ cwd: options.cwd,
11
+ stdio: "inherit",
12
+ shell: process.platform === "win32"
13
+ });
14
+ child.on("error", reject);
15
+ child.on("exit", (code) => resolve2(code ?? 1));
16
+ });
17
+ };
18
+ async function exists(path) {
19
+ try {
20
+ await access(path);
21
+ return true;
22
+ } catch {
23
+ return false;
24
+ }
25
+ }
26
+ async function detectPackageManager(cwd) {
27
+ if (await exists(join(cwd, "pnpm-lock.yaml"))) {
28
+ return "pnpm";
29
+ }
30
+ if (await exists(join(cwd, "bun.lockb")) || await exists(join(cwd, "bun.lock"))) {
31
+ return "bun";
32
+ }
33
+ if (await exists(join(cwd, "yarn.lock"))) {
34
+ return "yarn";
35
+ }
36
+ if (await exists(join(cwd, "package-lock.json"))) {
37
+ return "npm";
38
+ }
39
+ return "pnpm";
40
+ }
41
+ function installCommand(packageManager, packages) {
42
+ if (packageManager === "npm") {
43
+ return { command: "npm", args: ["install", ...packages] };
44
+ }
45
+ return { command: packageManager, args: ["add", ...packages] };
46
+ }
47
+ function runScriptCommand(packageManager, scriptName) {
48
+ if (packageManager === "npm" || packageManager === "pnpm" || packageManager === "bun") {
49
+ return { command: packageManager, args: ["run", scriptName] };
50
+ }
51
+ return { command: "yarn", args: [scriptName] };
52
+ }
53
+ function createViteCommand(packageManager, projectName, viteTemplate) {
54
+ if (packageManager === "pnpm") {
55
+ return {
56
+ command: "pnpm",
57
+ args: ["create", "vite@latest", projectName, "--", "--template", viteTemplate]
58
+ };
59
+ }
60
+ if (packageManager === "npm") {
61
+ return {
62
+ command: "npm",
63
+ args: ["create", "vite@latest", projectName, "--", "--template", viteTemplate]
64
+ };
65
+ }
66
+ if (packageManager === "bun") {
67
+ return {
68
+ command: "bun",
69
+ args: ["create", "vite", projectName, "--template", viteTemplate]
70
+ };
71
+ }
72
+ return {
73
+ command: "yarn",
74
+ args: ["create", "vite", projectName, "--template", viteTemplate]
75
+ };
76
+ }
77
+ function formatShellCommand(command) {
78
+ return [command.command, ...command.args].join(" ");
79
+ }
80
+
81
+ // src/recipes.ts
82
+ var adapters = ["react", "vue", "svelte"];
83
+ var packageManagers = ["pnpm", "npm", "yarn", "bun"];
84
+ var marwesTemplates = ["react-ts", "vue-ts", "svelte-ts"];
85
+ var adapterRecipes = {
86
+ react: {
87
+ adapter: "react",
88
+ displayName: "React",
89
+ packageName: "@marwes-ui/react",
90
+ installPackages: ["@marwes-ui/react", "react", "react-dom"],
91
+ providerImport: "MarwesProvider",
92
+ viteTemplate: "react-ts",
93
+ rootFiles: ["src/main.tsx", "src/main.jsx"]
94
+ },
95
+ vue: {
96
+ adapter: "vue",
97
+ displayName: "Vue",
98
+ packageName: "@marwes-ui/vue",
99
+ installPackages: ["@marwes-ui/vue", "vue"],
100
+ providerImport: "MarwesProvider",
101
+ viteTemplate: "vue-ts",
102
+ rootFiles: ["src/App.vue", "src/main.ts"]
103
+ },
104
+ svelte: {
105
+ adapter: "svelte",
106
+ displayName: "Svelte",
107
+ packageName: "@marwes-ui/svelte",
108
+ installPackages: ["@marwes-ui/svelte"],
109
+ providerImport: "MarwesProvider",
110
+ viteTemplate: "svelte-ts",
111
+ rootFiles: ["src/App.svelte"]
112
+ }
113
+ };
114
+ function isAdapter(value) {
115
+ return adapters.includes(value);
116
+ }
117
+ function isPackageManager(value) {
118
+ return packageManagers.includes(value);
119
+ }
120
+ function isMarwesTemplate(value) {
121
+ return marwesTemplates.includes(value);
122
+ }
123
+ function getAdapterRecipe(adapter) {
124
+ return adapterRecipes[adapter];
125
+ }
126
+ function adapterFromTemplate(template) {
127
+ if (template === "react-ts") {
128
+ return "react";
129
+ }
130
+ if (template === "vue-ts") {
131
+ return "vue";
132
+ }
133
+ return "svelte";
134
+ }
135
+ function packageRunnerCommand(packageManager, packageName = "@marwes-ui/cli") {
136
+ if (packageManager === "pnpm") {
137
+ return `pnpm dlx ${packageName}`;
138
+ }
139
+ if (packageManager === "npm") {
140
+ return `npx ${packageName}`;
141
+ }
142
+ if (packageManager === "yarn") {
143
+ return `yarn dlx ${packageName}`;
144
+ }
145
+ return `bunx ${packageName}`;
146
+ }
147
+ function createAgenticInitCommand(adapter, packageManager) {
148
+ return `${packageRunnerCommand(packageManager)} init --adapter ${adapter} --agentic`;
149
+ }
150
+ function createAiPrompt(adapter, packageManager) {
151
+ const recipe = getAdapterRecipe(adapter);
152
+ const installCommand2 = createAgenticInitCommand(adapter, packageManager);
153
+ return [
154
+ `Install and wire Marwes UI for a ${recipe.displayName} app using ${packageManager}.`,
155
+ `Run: ${installCommand2}.`,
156
+ "Follow the CLI output exactly.",
157
+ `If automatic patching is skipped, wrap the app root with ${recipe.providerImport} from ${recipe.packageName}.`,
158
+ `Import Marwes components, enums, hooks/helpers, and theme tokens only from ${recipe.packageName}.`,
159
+ "Do not install @marwes-ui/core or @marwes-ui/presets directly.",
160
+ `Do not add a separate Marwes stylesheet import; the ${recipe.displayName} adapter loads the default styles automatically.`,
161
+ "Add a small smoke render, for example a Marwes Button, after the installer completes."
162
+ ].join("\n");
163
+ }
164
+ function supportedAdaptersLabel() {
165
+ return adapters.join("|");
166
+ }
167
+ function supportedPackageManagersLabel() {
168
+ return packageManagers.join("|");
169
+ }
170
+ function supportedTemplatesLabel() {
171
+ return marwesTemplates.join("|");
172
+ }
173
+
174
+ // src/doctor.ts
175
+ var sourceExtensions = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".vue", ".svelte", ".css"]);
176
+ function dependencyVersion(packageJson, packageName) {
177
+ return packageJson.dependencies?.[packageName] ?? packageJson.devDependencies?.[packageName] ?? packageJson.peerDependencies?.[packageName] ?? packageJson.optionalDependencies?.[packageName];
178
+ }
179
+ async function readPackageJson(cwd) {
180
+ const source = await readFile(join(cwd, "package.json"), "utf8");
181
+ return JSON.parse(source);
182
+ }
183
+ async function collectSourceFiles(directory) {
184
+ const entries = await readdir(directory, { withFileTypes: true }).catch(() => []);
185
+ const files = [];
186
+ for (const entry of entries) {
187
+ const path = join(directory, entry.name);
188
+ if (entry.isDirectory()) {
189
+ if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".git") {
190
+ continue;
191
+ }
192
+ files.push(...await collectSourceFiles(path));
193
+ continue;
194
+ }
195
+ const extension = entry.name.slice(entry.name.lastIndexOf("."));
196
+ if (sourceExtensions.has(extension)) {
197
+ files.push(path);
198
+ }
199
+ }
200
+ return files;
201
+ }
202
+ function detectInstalledAdapter(packageJson) {
203
+ return adapters.find(
204
+ (adapter) => dependencyVersion(packageJson, getAdapterRecipe(adapter).packageName)
205
+ );
206
+ }
207
+ async function sourceContains(cwd, needle) {
208
+ const sourceFiles = await collectSourceFiles(join(cwd, "src"));
209
+ for (const file of sourceFiles) {
210
+ const source = await readFile(file, "utf8");
211
+ if (source.includes(needle)) {
212
+ return true;
213
+ }
214
+ }
215
+ return false;
216
+ }
217
+ function formatItem(item) {
218
+ const prefix = item.level === "pass" ? "[pass]" : item.level === "warn" ? "[warn]" : "[fail]";
219
+ return item.fix ? `${prefix} ${item.message}
220
+ fix: ${item.fix}` : `${prefix} ${item.message}`;
221
+ }
222
+ async function runDoctor(options = {}) {
223
+ const cwd = options.cwd ?? process.cwd();
224
+ const write = options.write ?? ((message) => console.log(message));
225
+ const runner = options.runner ?? defaultCommandRunner;
226
+ const packageJson = await readPackageJson(cwd);
227
+ const adapter = options.adapter ?? detectInstalledAdapter(packageJson);
228
+ const items = [];
229
+ if (!adapter) {
230
+ items.push({
231
+ level: "fail",
232
+ message: "No Marwes adapter dependency found.",
233
+ fix: "Run marwes init --adapter react, marwes init --adapter vue, or marwes init --adapter svelte."
234
+ });
235
+ } else {
236
+ const recipe = getAdapterRecipe(adapter);
237
+ for (const packageName of recipe.installPackages) {
238
+ if (dependencyVersion(packageJson, packageName)) {
239
+ items.push({ level: "pass", message: `${packageName} is installed.` });
240
+ } else {
241
+ items.push({
242
+ level: "fail",
243
+ message: `${packageName} is missing.`,
244
+ fix: `${options.packageManager ?? await detectPackageManager(cwd)} add ${recipe.installPackages.join(" ")}`
245
+ });
246
+ }
247
+ }
248
+ const hasProvider = await sourceContains(cwd, "MarwesProvider");
249
+ items.push(
250
+ hasProvider ? { level: "pass", message: "MarwesProvider is referenced in app source." } : {
251
+ level: "warn",
252
+ message: "MarwesProvider was not found in app source.",
253
+ fix: `Wrap the app root with MarwesProvider from ${recipe.packageName}.`
254
+ }
255
+ );
256
+ }
257
+ for (const internalPackage of ["@marwes-ui/core", "@marwes-ui/presets"]) {
258
+ if (dependencyVersion(packageJson, internalPackage)) {
259
+ items.push({
260
+ level: "warn",
261
+ message: `${internalPackage} is installed directly.`,
262
+ fix: "App projects should import from the public adapter package instead."
263
+ });
264
+ }
265
+ }
266
+ if (await sourceContains(cwd, "@marwes-ui/presets/firstEdition/styles.css")) {
267
+ items.push({
268
+ level: "warn",
269
+ message: "Manual Marwes preset stylesheet import found.",
270
+ fix: "Remove the stylesheet import; adapter packages load the default styles automatically."
271
+ });
272
+ }
273
+ if (options.runBuild) {
274
+ const packageManager = options.packageManager ?? await detectPackageManager(cwd);
275
+ const scriptName = packageJson.scripts?.typecheck ? "typecheck" : packageJson.scripts?.build ? "build" : void 0;
276
+ if (!scriptName) {
277
+ items.push({ level: "warn", message: "No typecheck or build script found to run." });
278
+ } else {
279
+ const command = runScriptCommand(packageManager, scriptName);
280
+ write(`Running ${formatShellCommand(command)}`);
281
+ const exitCode2 = await runner(command, { cwd });
282
+ items.push(
283
+ exitCode2 === 0 ? { level: "pass", message: `${scriptName} completed successfully.` } : { level: "fail", message: `${scriptName} failed.` }
284
+ );
285
+ }
286
+ }
287
+ for (const item of items) {
288
+ write(formatItem(item));
289
+ }
290
+ const exitCode = items.some((item) => item.level === "fail") ? 1 : 0;
291
+ return { ...adapter ? { adapter } : {}, items, exitCode };
292
+ }
293
+ function adapterFromString(value) {
294
+ return isAdapter(value) ? value : void 0;
295
+ }
296
+ async function fileExists(path) {
297
+ try {
298
+ await access(path);
299
+ return true;
300
+ } catch {
301
+ return false;
302
+ }
303
+ }
304
+ function insertImport(source, importLine) {
305
+ if (source.includes(importLine)) {
306
+ return source;
307
+ }
308
+ const lines = source.split("\n");
309
+ let lastImportIndex = -1;
310
+ for (let index = 0; index < lines.length; index += 1) {
311
+ if (lines[index]?.startsWith("import ")) {
312
+ lastImportIndex = index;
313
+ }
314
+ }
315
+ if (lastImportIndex === -1) {
316
+ return `${importLine}
317
+ ${source}`;
318
+ }
319
+ lines.splice(lastImportIndex + 1, 0, importLine);
320
+ return lines.join("\n");
321
+ }
322
+ function indentBlock(source, indentation) {
323
+ return source.split("\n").map((line) => line.trim().length === 0 ? "" : `${indentation}${line}`).join("\n");
324
+ }
325
+ async function writeIfNeeded(path, source, dryRun) {
326
+ const current = await readFile(path, "utf8");
327
+ if (current === source) {
328
+ return false;
329
+ }
330
+ if (!dryRun) {
331
+ await writeFile(path, source);
332
+ }
333
+ return true;
334
+ }
335
+ async function patchReact(cwd, dryRun) {
336
+ const candidates = ["src/main.tsx", "src/main.jsx"];
337
+ let relativeFile;
338
+ for (const candidate of candidates) {
339
+ if (await fileExists(join(cwd, candidate))) {
340
+ relativeFile = candidate;
341
+ break;
342
+ }
343
+ }
344
+ if (!relativeFile) {
345
+ return {
346
+ adapter: "react",
347
+ changed: false,
348
+ dryRun,
349
+ message: "No React Vite entrypoint found. Expected src/main.tsx or src/main.jsx."
350
+ };
351
+ }
352
+ const file = join(cwd, relativeFile);
353
+ const source = await readFile(file, "utf8");
354
+ if (source.includes("MarwesProvider")) {
355
+ return {
356
+ adapter: "react",
357
+ changed: false,
358
+ dryRun,
359
+ file: relativeFile,
360
+ message: "React entrypoint already references MarwesProvider."
361
+ };
362
+ }
363
+ if (!source.includes("<App />")) {
364
+ return {
365
+ adapter: "react",
366
+ changed: false,
367
+ dryRun,
368
+ file: relativeFile,
369
+ message: "React entrypoint did not contain the standard <App /> starter render."
370
+ };
371
+ }
372
+ const withImport = insertImport(source, 'import { MarwesProvider } from "@marwes-ui/react"');
373
+ const next = withImport.replace(
374
+ "<App />",
375
+ ["<MarwesProvider>", " <App />", " </MarwesProvider>"].join("\n")
376
+ );
377
+ const changed = await writeIfNeeded(file, next, dryRun);
378
+ return {
379
+ adapter: "react",
380
+ changed,
381
+ dryRun,
382
+ file: relativeFile,
383
+ message: changed ? "Wrapped the React root render with MarwesProvider." : "React entrypoint already matched the expected MarwesProvider setup."
384
+ };
385
+ }
386
+ function ensureVueScript(source) {
387
+ if (source.includes('MarwesProvider } from "@marwes-ui/vue"')) {
388
+ return source;
389
+ }
390
+ const importLine = 'import { MarwesProvider } from "@marwes-ui/vue"';
391
+ const scriptSetupMatch = source.match(/<script\s+setup[^>]*>/);
392
+ if (!scriptSetupMatch?.index && scriptSetupMatch?.index !== 0) {
393
+ return `<script setup lang="ts">
394
+ ${importLine}
395
+ </script>
396
+
397
+ ${source}`;
398
+ }
399
+ const insertAt = scriptSetupMatch.index + scriptSetupMatch[0].length;
400
+ return `${source.slice(0, insertAt)}
401
+ ${importLine}${source.slice(insertAt)}`;
402
+ }
403
+ async function patchVue(cwd, dryRun) {
404
+ const relativeFile = "src/App.vue";
405
+ const file = join(cwd, relativeFile);
406
+ if (!await fileExists(file)) {
407
+ return {
408
+ adapter: "vue",
409
+ changed: false,
410
+ dryRun,
411
+ message: "No Vue starter component found. Expected src/App.vue."
412
+ };
413
+ }
414
+ const source = await readFile(file, "utf8");
415
+ if (source.includes("<MarwesProvider")) {
416
+ return {
417
+ adapter: "vue",
418
+ changed: false,
419
+ dryRun,
420
+ file: relativeFile,
421
+ message: "Vue starter component already contains MarwesProvider."
422
+ };
423
+ }
424
+ const templateMatch = source.match(/<template>([\s\S]*?)<\/template>/);
425
+ if (!templateMatch) {
426
+ return {
427
+ adapter: "vue",
428
+ changed: false,
429
+ dryRun,
430
+ file: relativeFile,
431
+ message: "Vue starter component did not contain a standard <template> block."
432
+ };
433
+ }
434
+ const innerTemplate = templateMatch[1]?.trim();
435
+ if (!innerTemplate) {
436
+ return {
437
+ adapter: "vue",
438
+ changed: false,
439
+ dryRun,
440
+ file: relativeFile,
441
+ message: "Vue starter template was empty."
442
+ };
443
+ }
444
+ const wrappedTemplate = [
445
+ "<template>",
446
+ " <MarwesProvider>",
447
+ indentBlock(innerTemplate, " "),
448
+ " </MarwesProvider>",
449
+ "</template>"
450
+ ].join("\n");
451
+ const withProviderTemplate = source.replace(templateMatch[0], wrappedTemplate);
452
+ const next = ensureVueScript(withProviderTemplate);
453
+ const changed = await writeIfNeeded(file, next, dryRun);
454
+ return {
455
+ adapter: "vue",
456
+ changed,
457
+ dryRun,
458
+ file: relativeFile,
459
+ message: changed ? "Wrapped the Vue starter template with MarwesProvider." : "Vue starter component already matched the expected MarwesProvider setup."
460
+ };
461
+ }
462
+ function blockMatches(source, tagName) {
463
+ return source.match(new RegExp(`<${tagName}[^>]*>[\\s\\S]*?<\\/${tagName}>`, "g")) ?? [];
464
+ }
465
+ function removeBlocks(source, blocks) {
466
+ return blocks.reduce((next, block) => next.replace(block, ""), source);
467
+ }
468
+ function ensureSvelteScript(scriptBlocks) {
469
+ const importLine = ' import { MarwesProvider } from "@marwes-ui/svelte"';
470
+ const script = scriptBlocks[0];
471
+ if (!script) {
472
+ return `<script lang="ts">
473
+ ${importLine}
474
+ </script>`;
475
+ }
476
+ if (script.includes("MarwesProvider")) {
477
+ return script;
478
+ }
479
+ return script.replace(/<script([^>]*)>/i, `<script$1>
480
+ ${importLine}`);
481
+ }
482
+ async function patchSvelte(cwd, dryRun) {
483
+ const relativeFile = "src/App.svelte";
484
+ const file = join(cwd, relativeFile);
485
+ if (!await fileExists(file)) {
486
+ return {
487
+ adapter: "svelte",
488
+ changed: false,
489
+ dryRun,
490
+ message: "No Svelte starter component found. Expected src/App.svelte."
491
+ };
492
+ }
493
+ const source = await readFile(file, "utf8");
494
+ if (source.includes("<MarwesProvider")) {
495
+ return {
496
+ adapter: "svelte",
497
+ changed: false,
498
+ dryRun,
499
+ file: relativeFile,
500
+ message: "Svelte starter component already contains MarwesProvider."
501
+ };
502
+ }
503
+ const scriptBlocks = blockMatches(source, "script");
504
+ const styleBlocks = blockMatches(source, "style");
505
+ const body = removeBlocks(source, [...scriptBlocks, ...styleBlocks]).trim();
506
+ if (!body) {
507
+ return {
508
+ adapter: "svelte",
509
+ changed: false,
510
+ dryRun,
511
+ file: relativeFile,
512
+ message: "Svelte starter component had no markup to wrap."
513
+ };
514
+ }
515
+ const script = ensureSvelteScript(scriptBlocks);
516
+ const styles = styleBlocks.join("\n\n");
517
+ const next = [
518
+ script,
519
+ "",
520
+ "<MarwesProvider>",
521
+ indentBlock(body, " "),
522
+ "</MarwesProvider>",
523
+ styles ? `
524
+ ${styles}` : ""
525
+ ].filter(Boolean).join("\n");
526
+ const changed = await writeIfNeeded(file, `${next.trimEnd()}
527
+ `, dryRun);
528
+ return {
529
+ adapter: "svelte",
530
+ changed,
531
+ dryRun,
532
+ file: relativeFile,
533
+ message: changed ? "Wrapped the Svelte starter markup with MarwesProvider." : "Svelte starter component already matched the expected MarwesProvider setup."
534
+ };
535
+ }
536
+ async function patchProject(cwd, adapter, dryRun) {
537
+ if (adapter === "react") {
538
+ return patchReact(cwd, dryRun);
539
+ }
540
+ if (adapter === "vue") {
541
+ return patchVue(cwd, dryRun);
542
+ }
543
+ return patchSvelte(cwd, dryRun);
544
+ }
545
+
546
+ // src/init.ts
547
+ function providerGuidance(adapter) {
548
+ const recipe = getAdapterRecipe(adapter);
549
+ return `Manual follow-up: wrap the app root with ${recipe.providerImport} from ${recipe.packageName}.`;
550
+ }
551
+ function shouldPrintProviderGuidance(patch) {
552
+ if (!patch || patch.changed) {
553
+ return false;
554
+ }
555
+ return !patch.message.toLowerCase().includes("already");
556
+ }
557
+ function writeAgenticBoundaryRules(adapter, write) {
558
+ const recipe = getAdapterRecipe(adapter);
559
+ write("Agentic rules:");
560
+ write(`- Import Marwes APIs only from ${recipe.packageName}.`);
561
+ write("- Do not install @marwes-ui/core or @marwes-ui/presets directly.");
562
+ write("- Do not add a separate Marwes stylesheet import.");
563
+ }
564
+ async function runInit(options) {
565
+ const cwd = options.cwd ?? process.cwd();
566
+ const write = options.write ?? ((message) => console.log(message));
567
+ const runner = options.runner ?? defaultCommandRunner;
568
+ const recipe = getAdapterRecipe(options.adapter);
569
+ const packageManager = options.packageManager ?? await detectPackageManager(cwd);
570
+ const command = installCommand(packageManager, recipe.installPackages);
571
+ const commandLabel = formatShellCommand(command);
572
+ const dryRun = options.dryRun === true;
573
+ const agentic = options.agentic === true;
574
+ const noInstall = options.noInstall === true;
575
+ const noPatch = options.noPatch === true;
576
+ write(`Marwes init: ${recipe.displayName}`);
577
+ write(`Package manager: ${packageManager}`);
578
+ if (agentic) {
579
+ write("Mode: agentic");
580
+ }
581
+ let exitCode = 0;
582
+ if (noInstall) {
583
+ write(`Install skipped. Run manually: ${commandLabel}`);
584
+ } else if (dryRun) {
585
+ write(`[dry-run] ${commandLabel}`);
586
+ } else {
587
+ exitCode = await runner(command, { cwd });
588
+ if (exitCode !== 0) {
589
+ return {
590
+ adapter: options.adapter,
591
+ packageManager,
592
+ installCommand: commandLabel,
593
+ installSkipped: false,
594
+ patchSkipped: noPatch,
595
+ exitCode
596
+ };
597
+ }
598
+ }
599
+ let patch;
600
+ if (noPatch) {
601
+ write("Patch skipped.");
602
+ } else {
603
+ patch = await patchProject(cwd, options.adapter, dryRun);
604
+ const patchLabel = patch.file ? `${patch.file}: ${patch.message}` : patch.message;
605
+ write(dryRun && patch.changed ? `[dry-run] ${patchLabel}` : patchLabel);
606
+ }
607
+ let doctor;
608
+ if (agentic) {
609
+ if (shouldPrintProviderGuidance(patch) || noPatch) {
610
+ write(providerGuidance(options.adapter));
611
+ }
612
+ writeAgenticBoundaryRules(options.adapter, write);
613
+ if (dryRun) {
614
+ write(`[dry-run] marwes doctor --adapter ${options.adapter}`);
615
+ } else {
616
+ write("Marwes doctor:");
617
+ doctor = await runDoctor({
618
+ adapter: options.adapter,
619
+ cwd,
620
+ packageManager,
621
+ runner,
622
+ write
623
+ });
624
+ exitCode = doctor.exitCode;
625
+ }
626
+ }
627
+ write("Marwes init complete.");
628
+ return {
629
+ adapter: options.adapter,
630
+ packageManager,
631
+ installCommand: commandLabel,
632
+ installSkipped: noInstall || dryRun,
633
+ patchSkipped: noPatch,
634
+ ...patch ? { patch } : {},
635
+ ...doctor ? { doctor } : {},
636
+ exitCode
637
+ };
638
+ }
639
+
640
+ // src/create.ts
641
+ async function runCreate(options) {
642
+ const cwd = options.cwd ?? process.cwd();
643
+ const write = options.write ?? ((message) => console.log(message));
644
+ const runner = options.runner ?? defaultCommandRunner;
645
+ const packageManager = options.packageManager ?? await detectPackageManager(cwd);
646
+ const command = createViteCommand(packageManager, options.projectName, options.template);
647
+ const adapter = adapterFromTemplate(options.template);
648
+ const targetCwd = resolve(cwd, options.projectName);
649
+ write(`Create Marwes app: ${options.projectName}`);
650
+ write(`Template: ${options.template}`);
651
+ if (options.dryRun) {
652
+ write(`[dry-run] ${formatShellCommand(command)}`);
653
+ write(`[dry-run] marwes init --adapter ${adapter}`);
654
+ return {
655
+ projectName: options.projectName,
656
+ template: options.template,
657
+ exitCode: 0
658
+ };
659
+ }
660
+ const createExitCode = await runner(command, { cwd });
661
+ if (createExitCode !== 0) {
662
+ return {
663
+ projectName: options.projectName,
664
+ template: options.template,
665
+ exitCode: createExitCode
666
+ };
667
+ }
668
+ const initResult = await runInit({
669
+ adapter,
670
+ cwd: targetCwd,
671
+ packageManager,
672
+ noInstall: options.noInstall === true,
673
+ runner,
674
+ write
675
+ });
676
+ return {
677
+ projectName: options.projectName,
678
+ template: options.template,
679
+ exitCode: initResult.exitCode
680
+ };
681
+ }
682
+
683
+ // src/index.ts
684
+ function writeHelp(write) {
685
+ write(`Marwes CLI
686
+
687
+ Commands:
688
+ marwes init --adapter <${supportedAdaptersLabel()}> [--agentic]
689
+ marwes doctor [--adapter <${supportedAdaptersLabel()}]
690
+ marwes ai-prompt --adapter <${supportedAdaptersLabel()}>
691
+ marwes create <name> --template <${supportedTemplatesLabel()}>
692
+
693
+ Options:
694
+ --pm <${supportedPackageManagersLabel()}>
695
+ --agentic
696
+ --dry-run
697
+ --no-install
698
+ --no-patch
699
+ `);
700
+ }
701
+ function parsePackageManager(value) {
702
+ return typeof value === "string" && isPackageManager(value) ? value : void 0;
703
+ }
704
+ async function runMarwesCli(argv, options = {}) {
705
+ const write = options.write ?? ((message) => console.log(message));
706
+ const [command, ...args] = argv;
707
+ if (!command || command === "help" || command === "--help" || command === "-h") {
708
+ writeHelp(write);
709
+ return 0;
710
+ }
711
+ if (command === "init") {
712
+ const parsed = parseArgs({
713
+ args,
714
+ options: {
715
+ adapter: { type: "string" },
716
+ pm: { type: "string" },
717
+ agentic: { type: "boolean" },
718
+ "dry-run": { type: "boolean" },
719
+ yes: { type: "boolean" },
720
+ "no-install": { type: "boolean" },
721
+ "no-patch": { type: "boolean" }
722
+ },
723
+ allowPositionals: true
724
+ });
725
+ const adapter = adapterFromString(String(parsed.values.adapter ?? ""));
726
+ if (!adapter) {
727
+ write(`Missing or invalid --adapter. Expected one of: ${supportedAdaptersLabel()}`);
728
+ return 1;
729
+ }
730
+ const packageManager = parsePackageManager(parsed.values.pm);
731
+ const initOptions = {
732
+ adapter,
733
+ agentic: parsed.values.agentic === true,
734
+ dryRun: parsed.values["dry-run"] === true,
735
+ yes: parsed.values.yes === true,
736
+ noInstall: parsed.values["no-install"] === true,
737
+ noPatch: parsed.values["no-patch"] === true,
738
+ write
739
+ };
740
+ if (options.cwd) {
741
+ initOptions.cwd = options.cwd;
742
+ }
743
+ if (packageManager) {
744
+ initOptions.packageManager = packageManager;
745
+ }
746
+ const result = await runInit(initOptions);
747
+ return result.exitCode;
748
+ }
749
+ if (command === "doctor") {
750
+ const parsed = parseArgs({
751
+ args,
752
+ options: {
753
+ adapter: { type: "string" },
754
+ pm: { type: "string" },
755
+ "run-build": { type: "boolean" }
756
+ },
757
+ allowPositionals: true
758
+ });
759
+ const adapter = adapterFromString(String(parsed.values.adapter ?? ""));
760
+ const packageManager = parsePackageManager(parsed.values.pm);
761
+ const doctorOptions = {
762
+ runBuild: parsed.values["run-build"] === true,
763
+ write
764
+ };
765
+ if (adapter) {
766
+ doctorOptions.adapter = adapter;
767
+ }
768
+ if (options.cwd) {
769
+ doctorOptions.cwd = options.cwd;
770
+ }
771
+ if (packageManager) {
772
+ doctorOptions.packageManager = packageManager;
773
+ }
774
+ const result = await runDoctor(doctorOptions);
775
+ return result.exitCode;
776
+ }
777
+ if (command === "ai-prompt") {
778
+ const parsed = parseArgs({
779
+ args,
780
+ options: {
781
+ adapter: { type: "string" },
782
+ pm: { type: "string" }
783
+ },
784
+ allowPositionals: true
785
+ });
786
+ const adapter = adapterFromString(String(parsed.values.adapter ?? ""));
787
+ if (!adapter) {
788
+ write(`Missing or invalid --adapter. Expected one of: ${supportedAdaptersLabel()}`);
789
+ return 1;
790
+ }
791
+ write(createAiPrompt(adapter, parsePackageManager(parsed.values.pm) ?? "pnpm"));
792
+ return 0;
793
+ }
794
+ if (command === "create") {
795
+ return runCreateMarwesCli(args, options);
796
+ }
797
+ write(`Unknown command: ${command}`);
798
+ writeHelp(write);
799
+ return 1;
800
+ }
801
+ async function runCreateMarwesCli(argv, options = {}) {
802
+ const write = options.write ?? ((message) => console.log(message));
803
+ const parsed = parseArgs({
804
+ args: argv,
805
+ options: {
806
+ template: { type: "string" },
807
+ pm: { type: "string" },
808
+ "dry-run": { type: "boolean" },
809
+ "no-install": { type: "boolean" }
810
+ },
811
+ allowPositionals: true
812
+ });
813
+ const projectName = parsed.positionals[0];
814
+ const template = String(parsed.values.template ?? "react-ts");
815
+ if (!projectName) {
816
+ write("Missing project name.");
817
+ return 1;
818
+ }
819
+ if (!isMarwesTemplate(template)) {
820
+ write(`Invalid --template. Expected one of: ${supportedTemplatesLabel()}`);
821
+ return 1;
822
+ }
823
+ const packageManager = parsePackageManager(parsed.values.pm);
824
+ const createOptions = {
825
+ projectName,
826
+ template,
827
+ dryRun: parsed.values["dry-run"] === true,
828
+ noInstall: parsed.values["no-install"] === true,
829
+ write
830
+ };
831
+ if (options.cwd) {
832
+ createOptions.cwd = options.cwd;
833
+ }
834
+ if (packageManager) {
835
+ createOptions.packageManager = packageManager;
836
+ }
837
+ const result = await runCreate(createOptions);
838
+ return result.exitCode;
839
+ }
840
+
841
+ export { adapters, createAgenticInitCommand, createAiPrompt, getAdapterRecipe, marwesTemplates, packageManagers, packageRunnerCommand, runCreate, runCreateMarwesCli, runDoctor, runInit, runMarwesCli };
842
+ //# sourceMappingURL=index.js.map
843
+ //# sourceMappingURL=index.js.map