create-bunli 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 (96) hide show
  1. package/README.md +302 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +310 -0
  4. package/dist/create-project.d.ts +13 -0
  5. package/dist/create.d.ts +13 -0
  6. package/dist/index.d.ts +4 -0
  7. package/dist/index.js +217 -0
  8. package/dist/template-engine.d.ts +27 -0
  9. package/dist/templates/advanced/README.md +114 -0
  10. package/dist/templates/advanced/package.json +36 -0
  11. package/dist/templates/advanced/src/commands/config.ts +145 -0
  12. package/dist/templates/advanced/src/commands/init.ts +153 -0
  13. package/dist/templates/advanced/src/commands/serve.ts +176 -0
  14. package/dist/templates/advanced/src/commands/validate.ts +116 -0
  15. package/dist/templates/advanced/src/index.ts +44 -0
  16. package/dist/templates/advanced/src/utils/config.ts +83 -0
  17. package/dist/templates/advanced/src/utils/constants.ts +12 -0
  18. package/dist/templates/advanced/src/utils/glob.ts +49 -0
  19. package/dist/templates/advanced/src/utils/validator.ts +131 -0
  20. package/dist/templates/advanced/template.json +37 -0
  21. package/dist/templates/advanced/test/commands.test.ts +34 -0
  22. package/dist/templates/advanced/tsconfig.json +23 -0
  23. package/dist/templates/basic/README.md +41 -0
  24. package/dist/templates/basic/package.json +29 -0
  25. package/dist/templates/basic/src/commands/hello.ts +29 -0
  26. package/dist/templates/basic/src/index.ts +13 -0
  27. package/dist/templates/basic/template.json +31 -0
  28. package/dist/templates/basic/test/hello.test.ts +26 -0
  29. package/dist/templates/basic/tsconfig.json +19 -0
  30. package/dist/templates/monorepo/README.md +74 -0
  31. package/dist/templates/monorepo/package.json +28 -0
  32. package/dist/templates/monorepo/packages/cli/package.json +34 -0
  33. package/dist/templates/monorepo/packages/cli/src/index.ts +22 -0
  34. package/dist/templates/monorepo/packages/cli/tsconfig.json +15 -0
  35. package/dist/templates/monorepo/packages/core/package.json +32 -0
  36. package/dist/templates/monorepo/packages/core/scripts/build.ts +18 -0
  37. package/dist/templates/monorepo/packages/core/src/commands/analyze.ts +84 -0
  38. package/dist/templates/monorepo/packages/core/src/commands/process.ts +64 -0
  39. package/dist/templates/monorepo/packages/core/src/index.ts +3 -0
  40. package/dist/templates/monorepo/packages/core/src/types.ts +21 -0
  41. package/dist/templates/monorepo/packages/core/tsconfig.json +15 -0
  42. package/dist/templates/monorepo/packages/utils/package.json +26 -0
  43. package/dist/templates/monorepo/packages/utils/scripts/build.ts +17 -0
  44. package/dist/templates/monorepo/packages/utils/src/format.ts +27 -0
  45. package/dist/templates/monorepo/packages/utils/src/index.ts +3 -0
  46. package/dist/templates/monorepo/packages/utils/src/json.ts +11 -0
  47. package/dist/templates/monorepo/packages/utils/src/logger.ts +19 -0
  48. package/dist/templates/monorepo/packages/utils/tsconfig.json +12 -0
  49. package/dist/templates/monorepo/template.json +24 -0
  50. package/dist/templates/monorepo/tsconfig.json +14 -0
  51. package/dist/templates/monorepo/turbo.json +28 -0
  52. package/dist/types.d.ts +48 -0
  53. package/package.json +57 -0
  54. package/templates/advanced/README.md +114 -0
  55. package/templates/advanced/package.json +36 -0
  56. package/templates/advanced/src/commands/config.ts +145 -0
  57. package/templates/advanced/src/commands/init.ts +153 -0
  58. package/templates/advanced/src/commands/serve.ts +176 -0
  59. package/templates/advanced/src/commands/validate.ts +116 -0
  60. package/templates/advanced/src/index.ts +44 -0
  61. package/templates/advanced/src/utils/config.ts +83 -0
  62. package/templates/advanced/src/utils/constants.ts +12 -0
  63. package/templates/advanced/src/utils/glob.ts +49 -0
  64. package/templates/advanced/src/utils/validator.ts +131 -0
  65. package/templates/advanced/template.json +37 -0
  66. package/templates/advanced/test/commands.test.ts +34 -0
  67. package/templates/advanced/tsconfig.json +23 -0
  68. package/templates/basic/README.md +41 -0
  69. package/templates/basic/package.json +29 -0
  70. package/templates/basic/src/commands/hello.ts +29 -0
  71. package/templates/basic/src/index.ts +13 -0
  72. package/templates/basic/template.json +31 -0
  73. package/templates/basic/test/hello.test.ts +26 -0
  74. package/templates/basic/tsconfig.json +19 -0
  75. package/templates/monorepo/README.md +74 -0
  76. package/templates/monorepo/package.json +28 -0
  77. package/templates/monorepo/packages/cli/package.json +34 -0
  78. package/templates/monorepo/packages/cli/src/index.ts +22 -0
  79. package/templates/monorepo/packages/cli/tsconfig.json +15 -0
  80. package/templates/monorepo/packages/core/package.json +32 -0
  81. package/templates/monorepo/packages/core/scripts/build.ts +18 -0
  82. package/templates/monorepo/packages/core/src/commands/analyze.ts +84 -0
  83. package/templates/monorepo/packages/core/src/commands/process.ts +64 -0
  84. package/templates/monorepo/packages/core/src/index.ts +3 -0
  85. package/templates/monorepo/packages/core/src/types.ts +21 -0
  86. package/templates/monorepo/packages/core/tsconfig.json +15 -0
  87. package/templates/monorepo/packages/utils/package.json +26 -0
  88. package/templates/monorepo/packages/utils/scripts/build.ts +17 -0
  89. package/templates/monorepo/packages/utils/src/format.ts +27 -0
  90. package/templates/monorepo/packages/utils/src/index.ts +3 -0
  91. package/templates/monorepo/packages/utils/src/json.ts +11 -0
  92. package/templates/monorepo/packages/utils/src/logger.ts +19 -0
  93. package/templates/monorepo/packages/utils/tsconfig.json +12 -0
  94. package/templates/monorepo/template.json +24 -0
  95. package/templates/monorepo/tsconfig.json +14 -0
  96. package/templates/monorepo/turbo.json +28 -0
package/README.md ADDED
@@ -0,0 +1,302 @@
1
+ # create-bunli
2
+
3
+ Scaffold new Bunli CLI projects with ease.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # Using bunx (recommended)
9
+ bunx create-bunli my-cli
10
+
11
+ # Using npm
12
+ npm create bunli@latest my-cli
13
+
14
+ # Using yarn
15
+ yarn create bunli my-cli
16
+
17
+ # Using pnpm
18
+ pnpm create bunli my-cli
19
+ ```
20
+
21
+ ## Features
22
+
23
+ - 🚀 **Fast scaffolding** - Get started in seconds
24
+ - 📦 **Multiple templates** - Choose from basic, advanced, or monorepo setups
25
+ - 🔧 **TypeScript ready** - Full TypeScript support out of the box
26
+ - 🧪 **Testing included** - Comes with @bunli/test for CLI testing
27
+ - 🎨 **Best practices** - Follows Bunli conventions and patterns
28
+ - 🌐 **Flexible sources** - Use bundled templates or any GitHub repository
29
+
30
+ ## Usage
31
+
32
+ ### Basic Usage
33
+
34
+ Create a new project with the default template:
35
+
36
+ ```bash
37
+ bunx create-bunli my-cli
38
+ ```
39
+
40
+ ### Using Templates
41
+
42
+ Choose from bundled templates:
43
+
44
+ ```bash
45
+ # Basic single-command CLI
46
+ bunx create-bunli my-cli --template basic
47
+
48
+ # Advanced multi-command CLI with subcommands
49
+ bunx create-bunli my-cli --template advanced
50
+
51
+ # Monorepo setup with Turborepo
52
+ bunx create-bunli my-cli --template monorepo
53
+ ```
54
+
55
+ ### Using External Templates
56
+
57
+ Use any GitHub repository as a template:
58
+
59
+ ```bash
60
+ # GitHub repository
61
+ bunx create-bunli my-cli --template username/repo
62
+
63
+ # With full GitHub URL
64
+ bunx create-bunli my-cli --template github:username/repo
65
+
66
+ # Specific branch or tag
67
+ bunx create-bunli my-cli --template username/repo#branch
68
+ ```
69
+
70
+ ### Options
71
+
72
+ ```bash
73
+ bunx create-bunli [name] [options]
74
+
75
+ Options:
76
+ -t, --template <template> Project template (default: "basic")
77
+ -d, --dir <dir> Directory to create project in
78
+ -p, --package-manager <pm> Package manager (bun, npm, yarn, pnpm) (default: "bun")
79
+ -g, --git Initialize git repository (default: true)
80
+ -i, --install Install dependencies (default: true)
81
+ --offline Use cached templates when available
82
+ -h, --help Display help
83
+ -v, --version Display version
84
+ ```
85
+
86
+ ### Examples
87
+
88
+ ```bash
89
+ # Create in current directory
90
+ bunx create-bunli .
91
+
92
+ # Create with specific package manager
93
+ bunx create-bunli my-cli --package-manager pnpm
94
+
95
+ # Create without installing dependencies
96
+ bunx create-bunli my-cli --no-install
97
+
98
+ # Create in custom directory
99
+ bunx create-bunli my-cli --dir ~/projects/my-cli
100
+
101
+ # Use external template
102
+ bunx create-bunli my-cli --template pvtnbr/bunli-starter
103
+ ```
104
+
105
+ ## Templates
106
+
107
+ ### Basic Template
108
+
109
+ Perfect for simple CLI tools with a single command.
110
+
111
+ **Features:**
112
+ - Single command setup
113
+ - TypeScript configuration
114
+ - Test setup with @bunli/test
115
+ - Build script using bunli
116
+
117
+ **Structure:**
118
+ ```
119
+ my-cli/
120
+ ├── src/
121
+ │ ├── index.ts # CLI entry point
122
+ │ └── commands/
123
+ │ └── hello.ts # Example command
124
+ ├── test/
125
+ │ └── hello.test.ts # Example test
126
+ ├── package.json
127
+ ├── tsconfig.json
128
+ └── README.md
129
+ ```
130
+
131
+ ### Advanced Template
132
+
133
+ For complex CLIs with multiple commands and features.
134
+
135
+ **Features:**
136
+ - Multiple commands with subcommands
137
+ - Configuration management
138
+ - File validation system
139
+ - Built-in development server
140
+ - Advanced command examples
141
+
142
+ **Commands included:**
143
+ - `init` - Initialize configuration
144
+ - `validate` - Validate files with rules
145
+ - `serve` - Start development server
146
+ - `config` - Manage configuration
147
+
148
+ **Structure:**
149
+ ```
150
+ my-cli/
151
+ ├── src/
152
+ │ ├── index.ts # CLI entry point
153
+ │ ├── commands/ # Command implementations
154
+ │ │ ├── init.ts
155
+ │ │ ├── validate.ts
156
+ │ │ ├── serve.ts
157
+ │ │ └── config.ts
158
+ │ └── utils/ # Utility functions
159
+ │ ├── config.ts
160
+ │ ├── validator.ts
161
+ │ └── glob.ts
162
+ ├── test/
163
+ ├── package.json
164
+ ├── tsconfig.json
165
+ └── README.md
166
+ ```
167
+
168
+ ### Monorepo Template
169
+
170
+ For large projects with multiple packages.
171
+
172
+ **Features:**
173
+ - Turborepo configuration
174
+ - Multiple packages setup
175
+ - Shared dependencies
176
+ - Changeset support
177
+ - Parallel builds
178
+
179
+ **Structure:**
180
+ ```
181
+ my-cli/
182
+ ├── packages/
183
+ │ ├── cli/ # Main CLI package
184
+ │ ├── core/ # Core functionality
185
+ │ └── utils/ # Shared utilities
186
+ ├── turbo.json # Turborepo config
187
+ ├── package.json # Root package.json
188
+ ├── tsconfig.json # Root TypeScript config
189
+ └── README.md
190
+ ```
191
+
192
+ ## Creating Custom Templates
193
+
194
+ Templates can include a `template.json` manifest:
195
+
196
+ ```json
197
+ {
198
+ "name": "my-template",
199
+ "description": "My custom template",
200
+ "variables": [
201
+ {
202
+ "name": "projectName",
203
+ "message": "Project name",
204
+ "type": "string",
205
+ "default": "my-project"
206
+ },
207
+ {
208
+ "name": "license",
209
+ "message": "License",
210
+ "type": "select",
211
+ "choices": ["MIT", "Apache-2.0", "GPL-3.0"]
212
+ }
213
+ ]
214
+ }
215
+ ```
216
+
217
+ ### Template Variables
218
+
219
+ Use these variables in your template files:
220
+
221
+ - `{{projectName}}` - The project name
222
+ - `{{description}}` - Project description
223
+ - `{{author}}` - Author name
224
+ - `{{license}}` - License type
225
+ - `{{year}}` - Current year
226
+ - `{{packageManager}}` - Selected package manager
227
+
228
+ Variables can be used in file contents and filenames:
229
+ - `__projectName__.config.js` → `my-app.config.js`
230
+
231
+ ## Programmatic Usage
232
+
233
+ ```typescript
234
+ import { createProject } from 'create-bunli'
235
+
236
+ await createProject({
237
+ name: 'my-cli',
238
+ template: 'advanced',
239
+ packageManager: 'bun',
240
+ install: true,
241
+ git: true
242
+ })
243
+ ```
244
+
245
+ ## Development
246
+
247
+ ```bash
248
+ # Clone the repository
249
+ git clone https://github.com/AryaLabsHQ/bunli.git
250
+ cd bunli/packages/create-bunli
251
+
252
+ # Install dependencies
253
+ bun install
254
+
255
+ # Run in development
256
+ bun dev
257
+
258
+ # Run tests
259
+ bun test
260
+
261
+ # Build
262
+ bun run build
263
+ ```
264
+
265
+ ## Troubleshooting
266
+
267
+ ### Template not found
268
+
269
+ If you get a "template not found" error, ensure:
270
+ - The template name is correct
271
+ - For GitHub templates, the repository exists and is public
272
+ - You have internet connection (for external templates)
273
+
274
+ ### Installation fails
275
+
276
+ If dependency installation fails:
277
+ - Check your internet connection
278
+ - Ensure the package manager is installed
279
+ - Try running with `--no-install` and install manually
280
+
281
+ ### Permission errors
282
+
283
+ If you get permission errors:
284
+ - Ensure you have write access to the target directory
285
+ - Try running in a different directory
286
+ - Check disk space availability
287
+
288
+ ## Contributing
289
+
290
+ Contributions are welcome! Please read our [Contributing Guide](../../CONTRIBUTING.md) for details.
291
+
292
+ ### Adding a New Template
293
+
294
+ 1. Create a new directory in `templates/`
295
+ 2. Add all template files
296
+ 3. Create a `template.json` manifest
297
+ 4. Test the template thoroughly
298
+ 5. Submit a pull request
299
+
300
+ ## License
301
+
302
+ MIT © [Arya Labs, Inc.](../../LICENSE)
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,310 @@
1
+ #!/usr/bin/env bun
2
+ #!/usr/bin/env bun
3
+ // @bun
4
+
5
+ // src/cli.ts
6
+ import { createCLI, defineCommand, option } from "@bunli/core";
7
+ import { z } from "zod";
8
+
9
+ // src/template-engine.ts
10
+ import { downloadTemplate } from "giget";
11
+ import { readdir } from "fs/promises";
12
+ import { join } from "path";
13
+ async function processTemplate(options) {
14
+ const { source, dir, offline, variables = {} } = options;
15
+ let templateDir;
16
+ if (source.startsWith("/") || source.startsWith("./") || source.startsWith("../")) {
17
+ const sourceDir = source.startsWith("/") ? source : join(process.cwd(), source);
18
+ await Bun.spawn(["cp", "-r", sourceDir + "/.", dir], {
19
+ stdout: "inherit",
20
+ stderr: "inherit"
21
+ }).exited;
22
+ templateDir = dir;
23
+ } else {
24
+ const result = await downloadTemplate(source, {
25
+ dir,
26
+ offline,
27
+ preferOffline: true,
28
+ force: true
29
+ });
30
+ templateDir = result.dir;
31
+ }
32
+ const manifest = await loadTemplateManifest(templateDir);
33
+ if (manifest?.files || Object.keys(variables).length > 0) {
34
+ await processTemplateFiles(templateDir, variables, manifest);
35
+ }
36
+ if (manifest?.hooks?.postInstall) {
37
+ await runPostInstallHooks(templateDir, manifest.hooks.postInstall);
38
+ }
39
+ return { dir: templateDir, manifest };
40
+ }
41
+ async function loadTemplateManifest(dir) {
42
+ const possiblePaths = [
43
+ join(dir, "template.json"),
44
+ join(dir, ".template.json"),
45
+ join(dir, "template.yaml"),
46
+ join(dir, ".template.yaml")
47
+ ];
48
+ for (const path of possiblePaths) {
49
+ const file = Bun.file(path);
50
+ if (await file.exists()) {
51
+ const content = await file.text();
52
+ if (path.endsWith(".json")) {
53
+ const manifest = JSON.parse(content);
54
+ try {
55
+ await Bun.spawn(["rm", "-f", path], {
56
+ stdout: "ignore",
57
+ stderr: "ignore"
58
+ }).exited;
59
+ } catch {}
60
+ return manifest;
61
+ }
62
+ }
63
+ }
64
+ return null;
65
+ }
66
+ async function processTemplateFiles(dir, variables, manifest) {
67
+ const files = await getFilesToProcess(dir, manifest);
68
+ for (const file of files) {
69
+ const filePath = join(dir, file);
70
+ const content = await Bun.file(filePath).text();
71
+ let processedContent = content;
72
+ for (const [key, value] of Object.entries(variables)) {
73
+ processedContent = processedContent.replaceAll(`{{${key}}}`, value).replaceAll(`<%= ${key} %>`, value).replaceAll(`$${key}`, value).replaceAll(`__${key}__`, value);
74
+ }
75
+ let newFilePath = filePath;
76
+ for (const [key, value] of Object.entries(variables)) {
77
+ newFilePath = newFilePath.replaceAll(`__${key}__`, value);
78
+ }
79
+ await Bun.write(newFilePath, processedContent);
80
+ if (newFilePath !== filePath) {
81
+ await Bun.spawn(["rm", filePath]).exited;
82
+ }
83
+ }
84
+ }
85
+ async function getFilesToProcess(dir, manifest) {
86
+ if (manifest?.files?.include) {
87
+ return manifest.files.include;
88
+ }
89
+ const files = [];
90
+ async function walk(currentDir, prefix = "") {
91
+ const entries = await readdir(currentDir, { withFileTypes: true });
92
+ for (const entry of entries) {
93
+ const path = join(prefix, entry.name);
94
+ if (entry.isDirectory()) {
95
+ if (!["node_modules", ".git", ".next", "dist", "build"].includes(entry.name)) {
96
+ await walk(join(currentDir, entry.name), path);
97
+ }
98
+ } else {
99
+ if (!path.match(/^(template\.json|\.template\.json|\.DS_Store|Thumbs\.db)$/)) {
100
+ files.push(path);
101
+ }
102
+ }
103
+ }
104
+ }
105
+ await walk(dir);
106
+ return files;
107
+ }
108
+ async function runPostInstallHooks(dir, hooks) {
109
+ for (const hook of hooks) {
110
+ const proc = Bun.spawn(hook.split(" "), {
111
+ cwd: dir,
112
+ stdout: "inherit",
113
+ stderr: "inherit"
114
+ });
115
+ await proc.exited;
116
+ }
117
+ }
118
+ function resolveTemplateSource(template) {
119
+ const specialTemplates = {
120
+ basic: "github:bunli/templates/basic",
121
+ advanced: "github:bunli/templates/advanced",
122
+ monorepo: "github:bunli/templates/monorepo"
123
+ };
124
+ if (specialTemplates[template]) {
125
+ return specialTemplates[template];
126
+ }
127
+ if (template.startsWith("npm:")) {
128
+ return template.replace("npm:", "npm:/");
129
+ }
130
+ if (template.includes("/") && !template.includes(":")) {
131
+ return `github:${template}`;
132
+ }
133
+ return template;
134
+ }
135
+ function getBundledTemplatePath(name) {
136
+ return join(import.meta.dir, "..", "templates", name);
137
+ }
138
+ async function isLocalTemplate(template) {
139
+ if (template.startsWith("file:") || template.startsWith("./") || template.startsWith("../")) {
140
+ return true;
141
+ }
142
+ const bundledPath = getBundledTemplatePath(template);
143
+ return await Bun.file(join(bundledPath, "package.json")).exists();
144
+ }
145
+
146
+ // src/create-project.ts
147
+ async function createProject(options) {
148
+ const { name, dir, template, git, install, packageManager, prompt, spinner, colors, shell, offline } = options;
149
+ try {
150
+ await shell`test -d ${dir}`.quiet();
151
+ const overwrite = await prompt.confirm(`Directory ${dir} already exists. Overwrite?`, { default: false });
152
+ if (!overwrite) {
153
+ console.log(colors.red("Cancelled"));
154
+ process.exit(1);
155
+ }
156
+ await shell`rm -rf ${dir}`;
157
+ } catch {}
158
+ const spin = spinner("Creating project structure...");
159
+ spin.start();
160
+ await shell`mkdir -p ${dir}`;
161
+ try {
162
+ let templateSource = template;
163
+ if (await isLocalTemplate(template)) {
164
+ templateSource = getBundledTemplatePath(template);
165
+ } else {
166
+ templateSource = resolveTemplateSource(template);
167
+ }
168
+ const { manifest } = await processTemplate({
169
+ source: templateSource,
170
+ dir,
171
+ offline,
172
+ variables: {
173
+ projectName: name,
174
+ description: `A CLI built with Bunli`,
175
+ author: "",
176
+ packageManager: packageManager || "bun",
177
+ year: new Date().getFullYear().toString()
178
+ }
179
+ });
180
+ spin.succeed("Project structure created");
181
+ if (git) {
182
+ const gitSpin = spinner("Initializing git repository...");
183
+ gitSpin.start();
184
+ try {
185
+ await shell`cd ${dir} && git init`.quiet();
186
+ await shell`cd ${dir} && git add .`.quiet();
187
+ await shell`cd ${dir} && git commit -m "Initial commit"`.quiet();
188
+ gitSpin.succeed("Git repository initialized");
189
+ } catch (error) {
190
+ gitSpin.fail("Failed to initialize git repository");
191
+ console.error(colors.dim(` ${error}`));
192
+ }
193
+ }
194
+ if (install) {
195
+ const installSpin = spinner(`Installing dependencies with ${packageManager}...`);
196
+ installSpin.start();
197
+ try {
198
+ const installCmd = packageManager === "bun" ? "bun install" : packageManager === "pnpm" ? "pnpm install" : packageManager === "yarn" ? "yarn install" : "npm install";
199
+ await shell`cd ${dir} && ${installCmd}`;
200
+ installSpin.succeed("Dependencies installed");
201
+ } catch (error) {
202
+ installSpin.fail("Failed to install dependencies");
203
+ console.error(colors.dim(` You can install them manually by running: ${packageManager} install`));
204
+ }
205
+ }
206
+ } catch (error) {
207
+ spin.fail("Failed to create project");
208
+ console.error(colors.red(`Error: ${error}`));
209
+ try {
210
+ await shell`rm -rf ${dir}`.quiet();
211
+ } catch {}
212
+ process.exit(1);
213
+ }
214
+ }
215
+
216
+ // src/create.ts
217
+ import path from "path";
218
+ async function create(context) {
219
+ const { flags, positional, prompt, spinner, colors, shell } = context;
220
+ let projectName = positional[0] || flags.name;
221
+ if (!projectName) {
222
+ projectName = await prompt("Project name:", {
223
+ validate: (value) => {
224
+ if (!value)
225
+ return "Project name is required";
226
+ if (!/^[a-z0-9-]+$/.test(value)) {
227
+ return "Project name must only contain lowercase letters, numbers, and hyphens";
228
+ }
229
+ return true;
230
+ }
231
+ });
232
+ } else if (!/^[a-z0-9-]+$/.test(projectName)) {
233
+ console.error(colors.red("Project name must only contain lowercase letters, numbers, and hyphens"));
234
+ process.exit(1);
235
+ }
236
+ const projectDir = flags.dir || path.join(process.cwd(), projectName);
237
+ console.log();
238
+ console.log(colors.bold("Creating Bunli project:"));
239
+ console.log(colors.dim(" Name: ") + colors.cyan(projectName));
240
+ console.log(colors.dim(" Template: ") + colors.cyan(flags.template));
241
+ console.log(colors.dim(" Location: ") + colors.cyan(projectDir));
242
+ console.log(colors.dim(" Git: ") + colors.cyan(flags.git ? "Yes" : "No"));
243
+ console.log(colors.dim(" Install: ") + colors.cyan(flags.install ? "Yes" : "No"));
244
+ console.log(colors.dim(" Package: ") + colors.cyan(flags["package-manager"]));
245
+ console.log();
246
+ const confirmed = await prompt.confirm("Continue?", { default: true });
247
+ if (!confirmed) {
248
+ console.log(colors.red("Cancelled"));
249
+ process.exit(1);
250
+ }
251
+ console.log();
252
+ await createProject({
253
+ name: projectName,
254
+ dir: projectDir,
255
+ template: flags.template,
256
+ git: flags.git,
257
+ install: flags.install,
258
+ packageManager: flags["package-manager"],
259
+ offline: flags.offline,
260
+ prompt,
261
+ spinner,
262
+ colors,
263
+ shell
264
+ });
265
+ console.log();
266
+ console.log(colors.green("\u2728 Project created successfully!"));
267
+ console.log();
268
+ console.log("Next steps:");
269
+ console.log(colors.gray(` cd ${path.relative(process.cwd(), projectDir)}`));
270
+ if (!flags.install) {
271
+ console.log(colors.gray(` ${flags["package-manager"]} install`));
272
+ }
273
+ if (flags.template === "monorepo") {
274
+ console.log(colors.gray(` ${flags["package-manager"]} run dev`));
275
+ } else {
276
+ console.log(colors.gray(` ${flags["package-manager"] === "bun" ? "bun" : flags["package-manager"] + " run"} dev`));
277
+ }
278
+ console.log();
279
+ }
280
+
281
+ // src/cli.ts
282
+ async function run() {
283
+ const args = process.argv.slice(2);
284
+ if (args.length === 0 || args[0]?.startsWith("-")) {
285
+ process.argv.splice(2, 0, "create");
286
+ } else if (args[0] && !args[0].startsWith("-") && args[0] !== "create") {
287
+ process.argv.splice(2, 0, "create");
288
+ }
289
+ const cli = createCLI({
290
+ name: "create-bunli",
291
+ version: "0.1.0",
292
+ description: "Scaffold new Bunli CLI projects"
293
+ });
294
+ cli.command(defineCommand({
295
+ name: "create",
296
+ description: "Create a new Bunli CLI project",
297
+ options: {
298
+ name: option(z.string().optional(), { description: "Project name" }),
299
+ template: option(z.string().default("basic"), { short: "t", description: "Project template (basic, advanced, monorepo, or github:user/repo)" }),
300
+ dir: option(z.string().optional(), { short: "d", description: "Directory to create project in" }),
301
+ git: option(z.boolean().default(true), { short: "g", description: "Initialize git repository" }),
302
+ install: option(z.boolean().default(true), { short: "i", description: "Install dependencies" }),
303
+ "package-manager": option(z.enum(["bun", "pnpm", "yarn", "npm"]).default("bun"), { short: "p", description: "Package manager to use" }),
304
+ offline: option(z.boolean().default(false), { description: "Use cached templates when available" })
305
+ },
306
+ handler: create
307
+ }));
308
+ await cli.run();
309
+ }
310
+ await run();
@@ -0,0 +1,13 @@
1
+ import type { BunliUtils } from '@bunli/utils';
2
+ import type { CreateOptions } from './types.js';
3
+ interface CreateProjectOptions extends CreateOptions {
4
+ name: string;
5
+ dir: string;
6
+ template: string;
7
+ prompt: BunliUtils['prompt'];
8
+ spinner: BunliUtils['spinner'];
9
+ colors: BunliUtils['colors'];
10
+ shell: typeof Bun.$;
11
+ }
12
+ export declare function createProject(options: CreateProjectOptions): Promise<void>;
13
+ export {};
@@ -0,0 +1,13 @@
1
+ import type { HandlerArgs } from '@bunli/core';
2
+ import type { PackageManager } from './types.js';
3
+ interface CreateOptions {
4
+ name?: string;
5
+ template: string;
6
+ dir?: string;
7
+ git: boolean;
8
+ install: boolean;
9
+ 'package-manager': PackageManager;
10
+ offline?: boolean;
11
+ }
12
+ export declare function create(context: HandlerArgs<CreateOptions>): Promise<void>;
13
+ export {};
@@ -0,0 +1,4 @@
1
+ export { createProject } from './create-project.js';
2
+ export { processTemplate, resolveTemplateSource, isLocalTemplate } from './template-engine.js';
3
+ export type { CreateOptions, ProjectConfig, TemplateManifest, TemplateVariable } from './types.js';
4
+ export declare const version = "0.1.0";