@libria/scaffold 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 (38) hide show
  1. package/.clean-publish.hash +1 -0
  2. package/LICENSE +21 -0
  3. package/README.md +275 -0
  4. package/dist/cli/cli.d.mts +1 -0
  5. package/dist/cli/cli.mjs +4 -0
  6. package/dist/cli/cli.mjs.map +1 -0
  7. package/dist/cli/index.cjs +3 -0
  8. package/dist/cli/index.cjs.map +1 -0
  9. package/dist/cli/index.d.cts +57 -0
  10. package/dist/cli/index.d.mts +57 -0
  11. package/dist/cli/index.mjs +3 -0
  12. package/dist/cli/index.mjs.map +1 -0
  13. package/dist/templates/angular/index.cjs +20 -0
  14. package/dist/templates/angular/index.cjs.map +1 -0
  15. package/dist/templates/angular/index.d.cts +31 -0
  16. package/dist/templates/angular/index.d.mts +31 -0
  17. package/dist/templates/angular/index.mjs +20 -0
  18. package/dist/templates/angular/index.mjs.map +1 -0
  19. package/dist/templates/angular/plugin.json +5 -0
  20. package/dist/templates/ts-lib/files/.prettierrc +8 -0
  21. package/dist/templates/ts-lib/files/LICENSE +21 -0
  22. package/dist/templates/ts-lib/files/README.md +58 -0
  23. package/dist/templates/ts-lib/files/eslint.config.mjs +106 -0
  24. package/dist/templates/ts-lib/files/package.json +61 -0
  25. package/dist/templates/ts-lib/files/src/index.ts +31 -0
  26. package/dist/templates/ts-lib/files/tests/my.test.ts +44 -0
  27. package/dist/templates/ts-lib/files/tsconfig.json +22 -0
  28. package/dist/templates/ts-lib/files/tsconfig.test.json +8 -0
  29. package/dist/templates/ts-lib/files/tsdown.config.ts +10 -0
  30. package/dist/templates/ts-lib/files/vitest.config.ts +12 -0
  31. package/dist/templates/ts-lib/index.cjs +19 -0
  32. package/dist/templates/ts-lib/index.cjs.map +1 -0
  33. package/dist/templates/ts-lib/index.d.cts +29 -0
  34. package/dist/templates/ts-lib/index.d.mts +29 -0
  35. package/dist/templates/ts-lib/index.mjs +19 -0
  36. package/dist/templates/ts-lib/index.mjs.map +1 -0
  37. package/dist/templates/ts-lib/plugin.json +5 -0
  38. package/package.json +44 -0
@@ -0,0 +1 @@
1
+ 57b3e0c037fdd49e8bafe4dc03e2d06c264ff70b28a71b8ae4ce5843c3a32937
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CinemaCove
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,275 @@
1
+ # @libria/scaffold
2
+
3
+ Forge your next project with lightning-fast scaffolding. A pluggable CLI that transforms blank directories into production-ready codebases in seconds.
4
+
5
+ ![Version](https://img.shields.io/npm/v/@libria/scaffold)
6
+ ![License](https://img.shields.io/npm/l/@libria/scaffold)
7
+
8
+ ## ✨ Features
9
+
10
+ - **Interactive CLI**: Guided project creation with sensible defaults
11
+ - **Plugin System**: Extensible architecture for custom templates
12
+ - **Dry Run Mode**: Preview what will be generated before committing
13
+ - **Force Overwrite**: Safely regenerate existing projects
14
+ - **Built-in Templates**: TypeScript libraries and more included
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install -g @libria/scaffold
20
+ ```
21
+
22
+ Or use with npx:
23
+
24
+ ```bash
25
+ npx lb-scaffold create
26
+ ```
27
+
28
+ ## CLI Usage
29
+
30
+ ### Quick Start
31
+
32
+ Create a new project interactively:
33
+
34
+ ```bash
35
+ lb-scaffold create
36
+ ```
37
+
38
+ You'll be prompted for:
39
+ - Template choice (e.g., `ts-lib`)
40
+ - Project name
41
+ - Additional template-specific options
42
+ - Whether to initialize git and install dependencies
43
+
44
+ ### Non-Interactive Mode
45
+
46
+ Pass all options up-front for CI/CD or scripting:
47
+
48
+ ```bash
49
+ lb-scaffold create -t ts-lib -n my-awesome-lib --dry-run
50
+ ```
51
+
52
+ ### Options
53
+
54
+ | Option | Description | Default |
55
+ |--------|-------------|---------|
56
+ | `-t, --template <name>` | Template to use | (prompted) |
57
+ | `-n, --name <project-name>` | Name of the new project folder | (prompted, required) |
58
+ | `--dry-run` | Show what would be generated without writing files | `false` |
59
+ | `--force` | Overwrite existing project folder if it exists | `false` |
60
+ | `-i, --interactive` | Run in interactive mode | `true` |
61
+
62
+ ### Examples
63
+
64
+ Create a TypeScript library:
65
+
66
+ ```bash
67
+ lb-scaffold create -t ts-lib -n my-utils
68
+ ```
69
+
70
+ Preview generation without files:
71
+
72
+ ```bash
73
+ lb-scaffold create -t ts-lib -n my-utils --dry-run
74
+ ```
75
+
76
+ Force overwrite an existing project:
77
+
78
+ ```bash
79
+ lb-scaffold create -t ts-lib -n my-utils --force
80
+ ```
81
+
82
+ ## Included Templates
83
+
84
+ ### ts-lib
85
+
86
+ A modern TypeScript library template with:
87
+
88
+ - ESLint + Prettier configuration
89
+ - Vitest for testing
90
+ - tsdown for fast builds
91
+ - TypeScript path aliases
92
+ - Package.json with proper exports
93
+ - Comprehensive README and LICENSE
94
+
95
+ ## Creating Custom Template Plugins
96
+
97
+ The scaffold CLI uses a plugin system powered by `@libria/plugin-loader`. Each template is a self-contained plugin.
98
+
99
+ ### Plugin Structure
100
+
101
+ ```
102
+ templates/
103
+ └── my-template/
104
+ ├── plugin.json # Plugin metadata
105
+ ├── index.ts # Plugin entry point
106
+ ├── types.ts # TypeScript types
107
+ ├── my-template.ts # Implementation
108
+ └── files/ # Template files
109
+ ├── package.json
110
+ ├── tsconfig.json
111
+ └── src/
112
+ ```
113
+
114
+ ### Step 1: Create plugin.json
115
+
116
+ ```json
117
+ {
118
+ "name": "my-template",
119
+ "version": "1.0.0",
120
+ "type": "scaffold-template",
121
+ "main": "./index.ts",
122
+ "description": "My custom template"
123
+ }
124
+ ```
125
+
126
+ ### Step 2: Define Types (types.ts)
127
+
128
+ ```typescript
129
+ import { ScaffoldTemplatePluginOptions } from "@libria/scaffold";
130
+
131
+ export type MyTemplateOptions = ScaffoldTemplatePluginOptions & {
132
+ packageName: string;
133
+ description: string;
134
+ framework: 'react' | 'vue' | 'svelte';
135
+ };
136
+ ```
137
+
138
+ ### Step 3: Implement the Plugin (my-template.ts)
139
+
140
+ ```typescript
141
+ import path from 'path';
142
+ import { fileURLToPath } from 'url';
143
+ import fs from 'fs-extra';
144
+ import { input, confirm } from '@inquirer/prompts';
145
+ import { definePlugin } from '@libria/plugin-loader';
146
+ import { SCAFFOLD_TEMPLATE_PLUGIN_TYPE, ScaffoldTemplatePlugin } from '@libria/scaffold';
147
+ import { MyTemplateOptions } from './types';
148
+
149
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
150
+ const FILES_DIR = path.resolve(__dirname, 'files');
151
+
152
+ export default definePlugin<ScaffoldTemplatePlugin>(
153
+ SCAFFOLD_TEMPLATE_PLUGIN_TYPE,
154
+ 'my-template',
155
+ {
156
+ argument: 'my-template',
157
+ async execute(options: ScaffoldTemplatePluginOptions): Promise<void> {
158
+ // Collect user input
159
+ const userOptions = await getInitialOptions(options);
160
+ // Generate the project
161
+ await generateProject(userOptions);
162
+ // Post-processing
163
+ await postProcess(userOptions);
164
+ }
165
+ }
166
+ );
167
+
168
+ async function getInitialOptions(
169
+ options: ScaffoldTemplatePluginOptions
170
+ ): Promise<MyTemplateOptions> {
171
+ const packageName = await input({
172
+ message: 'Package name:',
173
+ default: options.name,
174
+ });
175
+
176
+ const description = await input({
177
+ message: 'Description:',
178
+ });
179
+
180
+ const framework = await input({
181
+ message: 'Framework (react/vue/svelte):',
182
+ default: 'react',
183
+ });
184
+
185
+ return { packageName, description, framework, ...options };
186
+ }
187
+
188
+ async function generateProject(options: MyTemplateOptions): Promise<void> {
189
+ const { name, dryRun, force } = options;
190
+ const targetDir = path.resolve(process.cwd(), name);
191
+
192
+ // Handle existing directory
193
+ if (await fs.pathExists(targetDir)) {
194
+ if (!force) {
195
+ console.error(`Directory '${name}' already exists. Use --force to overwrite.`);
196
+ process.exit(1);
197
+ }
198
+ if (!dryRun) {
199
+ await fs.remove(targetDir);
200
+ }
201
+ }
202
+
203
+ // Create and copy files
204
+ if (!dryRun) {
205
+ await fs.ensureDir(targetDir);
206
+ await fs.copy(FILES_DIR, targetDir);
207
+ // Replace placeholders
208
+ await replacePlaceholders(targetDir, options);
209
+ }
210
+
211
+ console.log(`Project '${name}' created successfully!`);
212
+ }
213
+
214
+ async function replacePlaceholders(
215
+ targetDir: string,
216
+ options: MyTemplateOptions
217
+ ): Promise<void> {
218
+ // Replace {PACKAGE_NAME}, {DESCRIPTION}, etc. in files
219
+ }
220
+
221
+ async function postProcess(options: MyTemplateOptions): Promise<void> {
222
+ // Git init, npm install, etc.
223
+ }
224
+ ```
225
+
226
+ ### Step 4: Export the Plugin (index.ts)
227
+
228
+ ```typescript
229
+ export { default } from './my-template';
230
+ export * from './types';
231
+ ```
232
+
233
+ ### Step 5: Create Template Files (files/)
234
+
235
+ Use placeholders that will be replaced:
236
+
237
+ `package.json`:
238
+ ```json
239
+ {
240
+ "name": "{PACKAGE_NAME}",
241
+ "description": "{DESCRIPTION}",
242
+ "version": "1.0.0"
243
+ }
244
+ ```
245
+
246
+ ### Step 6: Build the Templates
247
+
248
+ ```bash
249
+ npm run build:templates
250
+ ```
251
+
252
+ This compiles your template plugins to the `dist/templates` directory.
253
+
254
+ ## Development
255
+
256
+ ```bash
257
+ # Install dependencies
258
+ npm install
259
+
260
+ # Build the project
261
+ npm run build
262
+
263
+ # Build templates
264
+ npm run build:templates
265
+
266
+ # Run tests
267
+ npm test
268
+
269
+ # Lint and format
270
+ npm run clean
271
+ ```
272
+
273
+ ## License
274
+
275
+ MIT - see LICENSE file for details
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import e from"path";import{fileURLToPath as t}from"url";import{loadAllPlugins as n}from"@libria/plugin-loader";import{InteractiveCommand as r,InteractiveOption as i,Option as a}from"interactive-commander";import o from"fs/promises";const s=`.lbscaffold`;async function c(t=process.cwd()){let n=t;for(;;){let t=e.join(n,s);try{return await o.access(t),t}catch{let t=e.dirname(n);if(t===n)return null;n=t}}}function l(){return e.join(process.cwd(),s)}async function u(e){let t=e??await c();if(!t)return{};try{let e=await o.readFile(t,`utf-8`);return JSON.parse(e)}catch(e){if(e.code===`ENOENT`)return{};throw Error(`Failed to load config from ${t}: ${e.message}`)}}async function d(e,t){let n=t??l();await o.writeFile(n,JSON.stringify(e,null,2)+`
3
+ `,`utf-8`)}async function f(e){let t=e??l();try{throw await o.access(t),Error(`Config file already exists at ${t}`)}catch(e){if(e.code!==`ENOENT`)throw e}return await d({plugins:[`./plugins/**`]},t),t}async function p(e,t){let n=await u(t);if(n.plugins||=[],n.plugins.includes(e))throw Error(`Plugin pattern '${e}' already exists in config`);n.plugins.push(e),await d(n,t??await c()??l())}async function m(e,t){let n=t??await c();if(!n)throw Error(`No config file found`);let r=await u(n);if(!r.plugins||!r.plugins.includes(e))throw Error(`Plugin pattern '${e}' not found in config`);r.plugins=r.plugins.filter(t=>t!==e),await d(r,n)}async function h(e){return(await u(e)).plugins??[]}async function g(t){let n=t??await c();if(!n)return[];let r=await u(n),i=e.dirname(n);return(r.plugins??[]).map(t=>e.isAbsolute(t)?t.replace(/\\/g,`/`):e.resolve(i,t).replace(/\\/g,`/`))}const _=`scaffold-template`,v=e.dirname(t(import.meta.url)),y=await n(`${e.resolve(v,`../templates`).replace(/\\/g,`/`)}/**`,_),b=await g(),x=[];for(let e of b)try{let t=await n(e,_);x.push(...t)}catch(t){console.warn(`Warning: Failed to load plugins from '${e}': ${t.message}`)}const S=new Map;for(let e of y)S.set(e.name,e);for(let e of x)S.set(e.name,e);const C=Array.from(S.values()).sort((e,t)=>e.name.localeCompare(t.name)),w=C.map(e=>e.name),T=new r;T.name(`lb-scaffold`).description(`Scaffold new projects from templates`),T.command(`create`).description(`Create a new project from a template`).addOption(new i(`-t, --template <name>`,`Template to use`).choices(w)).addOption(new i(`-n, --name <project-name>`,`Name of the new project folder`).makeOptionMandatory(!0)).addOption(new a(`--dry-run`,`Show what would be generated without writing files`).default(!1)).addOption(new i(`--force`,`Overwrite existing project folder if it exists`).default(!1)).action(async e=>{let t=C.find(t=>t.api.argument===e.template);t||(console.error(`Template '${e.template}' not found.`),process.exit(1));try{await t.api.execute(e)}catch(e){console.error(`Error creating project:`,e),process.exit(1)}});const E=T.command(`config`).description(`Manage lb-scaffold configuration`);E.command(`init`).description(`Initialize a new .lbscaffold config file in the current directory`).action(async()=>{try{let e=await f();console.log(`Created config file: ${e}`)}catch(e){console.error(e.message),process.exit(1)}}),E.command(`add <glob>`).description(`Add a plugin glob pattern to the config`).action(async e=>{try{await p(e),console.log(`Added plugin pattern: ${e}`)}catch(e){console.error(e.message),process.exit(1)}}),E.command(`remove <glob>`).description(`Remove a plugin glob pattern from the config`).action(async e=>{try{await m(e),console.log(`Removed plugin pattern: ${e}`)}catch(e){console.error(e.message),process.exit(1)}}),E.command(`list`).description(`List all plugin glob patterns in the config`).action(async()=>{let e=await c();if(!e){console.log(`No .lbscaffold config file found.`),console.log(`Run "lb-scaffold config init" to create one.`);return}console.log(`Config file: ${e}\n`);let t=await h();if(t.length===0)console.log(`No plugin patterns configured.`);else{console.log(`Plugin patterns:`);for(let e of t)console.log(` - ${e}`)}}),E.command(`show`).description(`Show the full config file contents`).action(async()=>{let e=await c();if(!e){console.log(`No .lbscaffold config file found.`),console.log(`Run "lb-scaffold config init" to create one.`);return}console.log(`Config file: ${e}\n`);let t=await u(e);console.log(JSON.stringify(t,null,2))}),T.addOption(new a(`-i, --interactive`,`Run in interactive mode`).default(!0)),await T.interactive().parseAsync();export{};
4
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../../src/config.ts","../../src/core/scaffold-template-plugin.ts","../../src/cli.ts"],"sourcesContent":["import fs from 'fs/promises';\nimport path from 'path';\n\nexport interface LbScaffoldConfig {\n plugins?: string[];\n}\n\nconst CONFIG_FILENAME = '.lbscaffold';\n\n/**\n * Find the config file by searching up the directory tree\n */\nexport async function findConfigPath(startDir: string = process.cwd()): Promise<string | null> {\n let currentDir = startDir;\n\n while (true) {\n const configPath = path.join(currentDir, CONFIG_FILENAME);\n try {\n await fs.access(configPath);\n return configPath;\n } catch {\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) {\n // Reached root\n return null;\n }\n currentDir = parentDir;\n }\n }\n}\n\n/**\n * Get the default config path in the current directory\n */\nexport function getDefaultConfigPath(): string {\n return path.join(process.cwd(), CONFIG_FILENAME);\n}\n\n/**\n * Load the config file\n */\nexport async function loadConfig(configPath?: string): Promise<LbScaffoldConfig> {\n const resolvedPath = configPath ?? (await findConfigPath());\n\n if (!resolvedPath) {\n return {};\n }\n\n try {\n const content = await fs.readFile(resolvedPath, 'utf-8');\n return JSON.parse(content) as LbScaffoldConfig;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return {};\n }\n throw new Error(`Failed to load config from ${resolvedPath}: ${(error as Error).message}`);\n }\n}\n\n/**\n * Save the config file\n */\nexport async function saveConfig(config: LbScaffoldConfig, configPath?: string): Promise<void> {\n const resolvedPath = configPath ?? getDefaultConfigPath();\n await fs.writeFile(resolvedPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\n/**\n * Initialize a new config file\n */\nexport async function initConfig(configPath?: string): Promise<string> {\n const resolvedPath = configPath ?? getDefaultConfigPath();\n\n try {\n await fs.access(resolvedPath);\n throw new Error(`Config file already exists at ${resolvedPath}`);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n\n const defaultConfig: LbScaffoldConfig = {\n plugins: ['./plugins/**'],\n };\n\n await saveConfig(defaultConfig, resolvedPath);\n return resolvedPath;\n}\n\n/**\n * Add a plugin glob pattern to the config\n */\nexport async function addPluginGlob(glob: string, configPath?: string): Promise<void> {\n const config = await loadConfig(configPath);\n\n if (!config.plugins) {\n config.plugins = [];\n }\n\n if (config.plugins.includes(glob)) {\n throw new Error(`Plugin pattern '${glob}' already exists in config`);\n }\n\n config.plugins.push(glob);\n await saveConfig(config, configPath ?? (await findConfigPath()) ?? getDefaultConfigPath());\n}\n\n/**\n * Remove a plugin glob pattern from the config\n */\nexport async function removePluginGlob(glob: string, configPath?: string): Promise<void> {\n const resolvedPath = configPath ?? (await findConfigPath());\n\n if (!resolvedPath) {\n throw new Error('No config file found');\n }\n\n const config = await loadConfig(resolvedPath);\n\n if (!config.plugins || !config.plugins.includes(glob)) {\n throw new Error(`Plugin pattern '${glob}' not found in config`);\n }\n\n config.plugins = config.plugins.filter(p => p !== glob);\n await saveConfig(config, resolvedPath);\n}\n\n/**\n * List all plugin glob patterns in the config\n */\nexport async function listPluginGlobs(configPath?: string): Promise<string[]> {\n const config = await loadConfig(configPath);\n return config.plugins ?? [];\n}\n\n/**\n * Get absolute plugin paths from config globs\n */\nexport async function getPluginPaths(configPath?: string): Promise<string[]> {\n const resolvedConfigPath = configPath ?? (await findConfigPath());\n\n if (!resolvedConfigPath) {\n return [];\n }\n\n const config = await loadConfig(resolvedConfigPath);\n const configDir = path.dirname(resolvedConfigPath);\n\n return (config.plugins ?? []).map(glob => {\n // If glob is absolute, use as-is; otherwise resolve relative to config file\n if (path.isAbsolute(glob)) {\n return glob.replace(/\\\\/g, '/');\n }\n return path.resolve(configDir, glob).replace(/\\\\/g, '/');\n });\n}\n","import { ScaffoldTemplatePluginOptions } from './types';\n\nexport const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = 'scaffold-template';\n\nexport interface ScaffoldTemplatePlugin {\n argument: string;\n\n execute(options: ScaffoldTemplatePluginOptions): Promise<void>;\n}\n","#!/usr/bin/env node\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nimport { LibriaPlugin, loadAllPlugins } from '@libria/plugin-loader';\nimport { InteractiveCommand, InteractiveOption, Option } from 'interactive-commander';\n\nimport {\n initConfig,\n addPluginGlob,\n removePluginGlob,\n listPluginGlobs,\n getPluginPaths,\n findConfigPath,\n loadConfig,\n} from './config';\nimport {\n SCAFFOLD_TEMPLATE_PLUGIN_TYPE,\n ScaffoldTemplatePlugin,\n ScaffoldTemplatePluginOptions,\n} from './core';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nconst PLUGINS_FOLDER = path.resolve(__dirname, '../templates').replace(/\\\\/g, '/');\n\n// Load built-in plugins\nconst builtInPlugins = await loadAllPlugins<ScaffoldTemplatePlugin>(\n `${PLUGINS_FOLDER}/**`,\n SCAFFOLD_TEMPLATE_PLUGIN_TYPE\n);\n\n// Load user plugins from config\nconst userPluginPaths = await getPluginPaths();\nconst userPlugins: LibriaPlugin<ScaffoldTemplatePlugin>[] = [];\n\nfor (const pluginPath of userPluginPaths) {\n try {\n const loaded = await loadAllPlugins<ScaffoldTemplatePlugin>(\n pluginPath,\n SCAFFOLD_TEMPLATE_PLUGIN_TYPE\n );\n userPlugins.push(...loaded);\n } catch (error) {\n console.warn(\n `Warning: Failed to load plugins from '${pluginPath}': ${(error as Error).message}`\n );\n }\n}\n\n// Merge and deduplicate plugins (user plugins override built-in with same name)\nconst pluginMap = new Map<string, LibriaPlugin<ScaffoldTemplatePlugin>>();\nfor (const plugin of builtInPlugins) {\n pluginMap.set(plugin.name, plugin);\n}\nfor (const plugin of userPlugins) {\n pluginMap.set(plugin.name, plugin);\n}\nconst plugins = Array.from(pluginMap.values()).sort((a, b) => a.name.localeCompare(b.name));\n\nconst templateChoices = plugins.map(plugin => plugin.name);\n\nconst program = new InteractiveCommand();\n\nprogram.name('lb-scaffold').description('Scaffold new projects from templates');\n\n// Create subcommand to enable interactive hooks\nprogram\n .command('create')\n .description('Create a new project from a template')\n .addOption(\n new InteractiveOption('-t, --template <name>', 'Template to use').choices(templateChoices)\n )\n .addOption(\n new InteractiveOption(\n '-n, --name <project-name>',\n 'Name of the new project folder'\n ).makeOptionMandatory(true)\n )\n .addOption(\n new Option('--dry-run', 'Show what would be generated without writing files').default(false)\n )\n .addOption(\n new InteractiveOption('--force', 'Overwrite existing project folder if it exists').default(\n false\n )\n )\n .action(async (options: ScaffoldTemplatePluginOptions & { template: string }) => {\n const plugin = plugins.find(plugin => plugin.api.argument === options.template);\n if (!plugin) {\n console.error(`Template '${options.template}' not found.`);\n process.exit(1);\n }\n try {\n await plugin.api.execute(options);\n } catch (error) {\n console.error('Error creating project:', error);\n process.exit(1);\n }\n });\n\n// Config command\nconst configCommand = program.command('config').description('Manage lb-scaffold configuration');\n\nconfigCommand\n .command('init')\n .description('Initialize a new .lbscaffold config file in the current directory')\n .action(async () => {\n try {\n const configPath = await initConfig();\n console.log(`Created config file: ${configPath}`);\n } catch (error) {\n console.error((error as Error).message);\n process.exit(1);\n }\n });\n\nconfigCommand\n .command('add <glob>')\n .description('Add a plugin glob pattern to the config')\n .action(async (glob: string) => {\n try {\n await addPluginGlob(glob);\n console.log(`Added plugin pattern: ${glob}`);\n } catch (error) {\n console.error((error as Error).message);\n process.exit(1);\n }\n });\n\nconfigCommand\n .command('remove <glob>')\n .description('Remove a plugin glob pattern from the config')\n .action(async (glob: string) => {\n try {\n await removePluginGlob(glob);\n console.log(`Removed plugin pattern: ${glob}`);\n } catch (error) {\n console.error((error as Error).message);\n process.exit(1);\n }\n });\n\nconfigCommand\n .command('list')\n .description('List all plugin glob patterns in the config')\n .action(async () => {\n const configPath = await findConfigPath();\n if (!configPath) {\n console.log('No .lbscaffold config file found.');\n console.log('Run \"lb-scaffold config init\" to create one.');\n return;\n }\n\n console.log(`Config file: ${configPath}\\n`);\n\n const globs = await listPluginGlobs();\n if (globs.length === 0) {\n console.log('No plugin patterns configured.');\n } else {\n console.log('Plugin patterns:');\n for (const glob of globs) {\n console.log(` - ${glob}`);\n }\n }\n });\n\nconfigCommand\n .command('show')\n .description('Show the full config file contents')\n .action(async () => {\n const configPath = await findConfigPath();\n if (!configPath) {\n console.log('No .lbscaffold config file found.');\n console.log('Run \"lb-scaffold config init\" to create one.');\n return;\n }\n\n console.log(`Config file: ${configPath}\\n`);\n const config = await loadConfig(configPath);\n console.log(JSON.stringify(config, null, 2));\n });\n\n// Enable interactive mode by default\nprogram.addOption(new Option('-i, --interactive', 'Run in interactive mode').default(true));\n\nawait program.interactive().parseAsync();\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`fs/promises`);c=s(c);let l=require(`path`);l=s(l);const u=`.lbscaffold`;async function d(e=process.cwd()){let t=e;for(;;){let e=l.default.join(t,u);try{return await c.default.access(e),e}catch{let e=l.default.dirname(t);if(e===t)return null;t=e}}}function f(){return l.default.join(process.cwd(),u)}async function p(e){let t=e??await d();if(!t)return{};try{let e=await c.default.readFile(t,`utf-8`);return JSON.parse(e)}catch(e){if(e.code===`ENOENT`)return{};throw Error(`Failed to load config from ${t}: ${e.message}`)}}async function m(e,t){let n=t??f();await c.default.writeFile(n,JSON.stringify(e,null,2)+`
2
+ `,`utf-8`)}async function h(e){let t=e??f();try{throw await c.default.access(t),Error(`Config file already exists at ${t}`)}catch(e){if(e.code!==`ENOENT`)throw e}return await m({plugins:[`./plugins/**`]},t),t}async function g(e,t){let n=await p(t);if(n.plugins||=[],n.plugins.includes(e))throw Error(`Plugin pattern '${e}' already exists in config`);n.plugins.push(e),await m(n,t??await d()??f())}async function _(e,t){let n=t??await d();if(!n)throw Error(`No config file found`);let r=await p(n);if(!r.plugins||!r.plugins.includes(e))throw Error(`Plugin pattern '${e}' not found in config`);r.plugins=r.plugins.filter(t=>t!==e),await m(r,n)}async function v(e){return(await p(e)).plugins??[]}async function y(e){let t=e??await d();if(!t)return[];let n=await p(t),r=l.default.dirname(t);return(n.plugins??[]).map(e=>l.default.isAbsolute(e)?e.replace(/\\/g,`/`):l.default.resolve(r,e).replace(/\\/g,`/`))}exports.SCAFFOLD_TEMPLATE_PLUGIN_TYPE=`scaffold-template`,exports.addPluginGlob=g,exports.findConfigPath=d,exports.getDefaultConfigPath=f,exports.getPluginPaths=y,exports.initConfig=h,exports.listPluginGlobs=v,exports.loadConfig=p,exports.removePluginGlob=_,exports.saveConfig=m;
3
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["fs"],"sources":["../../src/core/scaffold-template-plugin.ts","../../src/config.ts"],"sourcesContent":["import { ScaffoldTemplatePluginOptions } from './types';\n\nexport const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = 'scaffold-template';\n\nexport interface ScaffoldTemplatePlugin {\n argument: string;\n\n execute(options: ScaffoldTemplatePluginOptions): Promise<void>;\n}\n","import fs from 'fs/promises';\nimport path from 'path';\n\nexport interface LbScaffoldConfig {\n plugins?: string[];\n}\n\nconst CONFIG_FILENAME = '.lbscaffold';\n\n/**\n * Find the config file by searching up the directory tree\n */\nexport async function findConfigPath(startDir: string = process.cwd()): Promise<string | null> {\n let currentDir = startDir;\n\n while (true) {\n const configPath = path.join(currentDir, CONFIG_FILENAME);\n try {\n await fs.access(configPath);\n return configPath;\n } catch {\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) {\n // Reached root\n return null;\n }\n currentDir = parentDir;\n }\n }\n}\n\n/**\n * Get the default config path in the current directory\n */\nexport function getDefaultConfigPath(): string {\n return path.join(process.cwd(), CONFIG_FILENAME);\n}\n\n/**\n * Load the config file\n */\nexport async function loadConfig(configPath?: string): Promise<LbScaffoldConfig> {\n const resolvedPath = configPath ?? (await findConfigPath());\n\n if (!resolvedPath) {\n return {};\n }\n\n try {\n const content = await fs.readFile(resolvedPath, 'utf-8');\n return JSON.parse(content) as LbScaffoldConfig;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return {};\n }\n throw new Error(`Failed to load config from ${resolvedPath}: ${(error as Error).message}`);\n }\n}\n\n/**\n * Save the config file\n */\nexport async function saveConfig(config: LbScaffoldConfig, configPath?: string): Promise<void> {\n const resolvedPath = configPath ?? getDefaultConfigPath();\n await fs.writeFile(resolvedPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\n/**\n * Initialize a new config file\n */\nexport async function initConfig(configPath?: string): Promise<string> {\n const resolvedPath = configPath ?? getDefaultConfigPath();\n\n try {\n await fs.access(resolvedPath);\n throw new Error(`Config file already exists at ${resolvedPath}`);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n\n const defaultConfig: LbScaffoldConfig = {\n plugins: ['./plugins/**'],\n };\n\n await saveConfig(defaultConfig, resolvedPath);\n return resolvedPath;\n}\n\n/**\n * Add a plugin glob pattern to the config\n */\nexport async function addPluginGlob(glob: string, configPath?: string): Promise<void> {\n const config = await loadConfig(configPath);\n\n if (!config.plugins) {\n config.plugins = [];\n }\n\n if (config.plugins.includes(glob)) {\n throw new Error(`Plugin pattern '${glob}' already exists in config`);\n }\n\n config.plugins.push(glob);\n await saveConfig(config, configPath ?? (await findConfigPath()) ?? getDefaultConfigPath());\n}\n\n/**\n * Remove a plugin glob pattern from the config\n */\nexport async function removePluginGlob(glob: string, configPath?: string): Promise<void> {\n const resolvedPath = configPath ?? (await findConfigPath());\n\n if (!resolvedPath) {\n throw new Error('No config file found');\n }\n\n const config = await loadConfig(resolvedPath);\n\n if (!config.plugins || !config.plugins.includes(glob)) {\n throw new Error(`Plugin pattern '${glob}' not found in config`);\n }\n\n config.plugins = config.plugins.filter(p => p !== glob);\n await saveConfig(config, resolvedPath);\n}\n\n/**\n * List all plugin glob patterns in the config\n */\nexport async function listPluginGlobs(configPath?: string): Promise<string[]> {\n const config = await loadConfig(configPath);\n return config.plugins ?? [];\n}\n\n/**\n * Get absolute plugin paths from config globs\n */\nexport async function getPluginPaths(configPath?: string): Promise<string[]> {\n const resolvedConfigPath = configPath ?? (await findConfigPath());\n\n if (!resolvedConfigPath) {\n return [];\n }\n\n const config = await loadConfig(resolvedConfigPath);\n const configDir = path.dirname(resolvedConfigPath);\n\n return (config.plugins ?? []).map(glob => {\n // If glob is absolute, use as-is; otherwise resolve relative to config file\n if (path.isAbsolute(glob)) {\n return glob.replace(/\\\\/g, '/');\n }\n return path.resolve(configDir, glob).replace(/\\\\/g, '/');\n });\n}\n"],"mappings":"+hBAEA,MCKM,EAAkB,cAKxB,eAAsB,EAAe,EAAmB,QAAQ,KAAK,CAA0B,CAC3F,IAAI,EAAa,EAEjB,OAAa,CACT,IAAM,EAAa,EAAA,QAAK,KAAK,EAAY,EAAgB,CACzD,GAAI,CAEA,OADA,MAAMA,EAAAA,QAAG,OAAO,EAAW,CACpB,OACH,CACJ,IAAM,EAAY,EAAA,QAAK,QAAQ,EAAW,CAC1C,GAAI,IAAc,EAEd,OAAO,KAEX,EAAa,IAQzB,SAAgB,GAA+B,CAC3C,OAAO,EAAA,QAAK,KAAK,QAAQ,KAAK,CAAE,EAAgB,CAMpD,eAAsB,EAAW,EAAgD,CAC7E,IAAM,EAAe,GAAe,MAAM,GAAgB,CAE1D,GAAI,CAAC,EACD,MAAO,EAAE,CAGb,GAAI,CACA,IAAM,EAAU,MAAMA,EAAAA,QAAG,SAAS,EAAc,QAAQ,CACxD,OAAO,KAAK,MAAM,EAAQ,OACrB,EAAO,CACZ,GAAK,EAAgC,OAAS,SAC1C,MAAO,EAAE,CAEb,MAAU,MAAM,8BAA8B,EAAa,IAAK,EAAgB,UAAU,EAOlG,eAAsB,EAAW,EAA0B,EAAoC,CAC3F,IAAM,EAAe,GAAc,GAAsB,CACzD,MAAMA,EAAAA,QAAG,UAAU,EAAc,KAAK,UAAU,EAAQ,KAAM,EAAE,CAAG;EAAM,QAAQ,CAMrF,eAAsB,EAAW,EAAsC,CACnE,IAAM,EAAe,GAAc,GAAsB,CAEzD,GAAI,CAEA,MADA,MAAMA,EAAAA,QAAG,OAAO,EAAa,CACnB,MAAM,iCAAiC,IAAe,OAC3D,EAAO,CACZ,GAAK,EAAgC,OAAS,SAC1C,MAAM,EASd,OADA,MAAM,EAJkC,CACpC,QAAS,CAAC,eAAe,CAC5B,CAE+B,EAAa,CACtC,EAMX,eAAsB,EAAc,EAAc,EAAoC,CAClF,IAAM,EAAS,MAAM,EAAW,EAAW,CAM3C,GAJA,AACI,EAAO,UAAU,EAAE,CAGnB,EAAO,QAAQ,SAAS,EAAK,CAC7B,MAAU,MAAM,mBAAmB,EAAK,4BAA4B,CAGxE,EAAO,QAAQ,KAAK,EAAK,CACzB,MAAM,EAAW,EAAQ,GAAe,MAAM,GAAgB,EAAK,GAAsB,CAAC,CAM9F,eAAsB,EAAiB,EAAc,EAAoC,CACrF,IAAM,EAAe,GAAe,MAAM,GAAgB,CAE1D,GAAI,CAAC,EACD,MAAU,MAAM,uBAAuB,CAG3C,IAAM,EAAS,MAAM,EAAW,EAAa,CAE7C,GAAI,CAAC,EAAO,SAAW,CAAC,EAAO,QAAQ,SAAS,EAAK,CACjD,MAAU,MAAM,mBAAmB,EAAK,uBAAuB,CAGnE,EAAO,QAAU,EAAO,QAAQ,OAAO,GAAK,IAAM,EAAK,CACvD,MAAM,EAAW,EAAQ,EAAa,CAM1C,eAAsB,EAAgB,EAAwC,CAE1E,OADe,MAAM,EAAW,EAAW,EAC7B,SAAW,EAAE,CAM/B,eAAsB,EAAe,EAAwC,CACzE,IAAM,EAAqB,GAAe,MAAM,GAAgB,CAEhE,GAAI,CAAC,EACD,MAAO,EAAE,CAGb,IAAM,EAAS,MAAM,EAAW,EAAmB,CAC7C,EAAY,EAAA,QAAK,QAAQ,EAAmB,CAElD,OAAQ,EAAO,SAAW,EAAE,EAAE,IAAI,GAE1B,EAAA,QAAK,WAAW,EAAK,CACd,EAAK,QAAQ,MAAO,IAAI,CAE5B,EAAA,QAAK,QAAQ,EAAW,EAAK,CAAC,QAAQ,MAAO,IAAI,CAC1D"}
@@ -0,0 +1,57 @@
1
+ //#region src/core/types.d.ts
2
+ type ScaffoldTemplatePluginOptions = {
3
+ name: string;
4
+ dryRun?: boolean;
5
+ force?: boolean;
6
+ };
7
+ //#endregion
8
+ //#region src/core/scaffold-template-plugin.d.ts
9
+ declare const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = "scaffold-template";
10
+ interface ScaffoldTemplatePlugin {
11
+ argument: string;
12
+ execute(options: ScaffoldTemplatePluginOptions): Promise<void>;
13
+ }
14
+ //#endregion
15
+ //#region src/config.d.ts
16
+ interface LbScaffoldConfig {
17
+ plugins?: string[];
18
+ }
19
+ /**
20
+ * Find the config file by searching up the directory tree
21
+ */
22
+ declare function findConfigPath(startDir?: string): Promise<string | null>;
23
+ /**
24
+ * Get the default config path in the current directory
25
+ */
26
+ declare function getDefaultConfigPath(): string;
27
+ /**
28
+ * Load the config file
29
+ */
30
+ declare function loadConfig(configPath?: string): Promise<LbScaffoldConfig>;
31
+ /**
32
+ * Save the config file
33
+ */
34
+ declare function saveConfig(config: LbScaffoldConfig, configPath?: string): Promise<void>;
35
+ /**
36
+ * Initialize a new config file
37
+ */
38
+ declare function initConfig(configPath?: string): Promise<string>;
39
+ /**
40
+ * Add a plugin glob pattern to the config
41
+ */
42
+ declare function addPluginGlob(glob: string, configPath?: string): Promise<void>;
43
+ /**
44
+ * Remove a plugin glob pattern from the config
45
+ */
46
+ declare function removePluginGlob(glob: string, configPath?: string): Promise<void>;
47
+ /**
48
+ * List all plugin glob patterns in the config
49
+ */
50
+ declare function listPluginGlobs(configPath?: string): Promise<string[]>;
51
+ /**
52
+ * Get absolute plugin paths from config globs
53
+ */
54
+ declare function getPluginPaths(configPath?: string): Promise<string[]>;
55
+ //#endregion
56
+ export { LbScaffoldConfig, SCAFFOLD_TEMPLATE_PLUGIN_TYPE, ScaffoldTemplatePlugin, ScaffoldTemplatePluginOptions, addPluginGlob, findConfigPath, getDefaultConfigPath, getPluginPaths, initConfig, listPluginGlobs, loadConfig, removePluginGlob, saveConfig };
57
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1,57 @@
1
+ //#region src/core/types.d.ts
2
+ type ScaffoldTemplatePluginOptions = {
3
+ name: string;
4
+ dryRun?: boolean;
5
+ force?: boolean;
6
+ };
7
+ //#endregion
8
+ //#region src/core/scaffold-template-plugin.d.ts
9
+ declare const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = "scaffold-template";
10
+ interface ScaffoldTemplatePlugin {
11
+ argument: string;
12
+ execute(options: ScaffoldTemplatePluginOptions): Promise<void>;
13
+ }
14
+ //#endregion
15
+ //#region src/config.d.ts
16
+ interface LbScaffoldConfig {
17
+ plugins?: string[];
18
+ }
19
+ /**
20
+ * Find the config file by searching up the directory tree
21
+ */
22
+ declare function findConfigPath(startDir?: string): Promise<string | null>;
23
+ /**
24
+ * Get the default config path in the current directory
25
+ */
26
+ declare function getDefaultConfigPath(): string;
27
+ /**
28
+ * Load the config file
29
+ */
30
+ declare function loadConfig(configPath?: string): Promise<LbScaffoldConfig>;
31
+ /**
32
+ * Save the config file
33
+ */
34
+ declare function saveConfig(config: LbScaffoldConfig, configPath?: string): Promise<void>;
35
+ /**
36
+ * Initialize a new config file
37
+ */
38
+ declare function initConfig(configPath?: string): Promise<string>;
39
+ /**
40
+ * Add a plugin glob pattern to the config
41
+ */
42
+ declare function addPluginGlob(glob: string, configPath?: string): Promise<void>;
43
+ /**
44
+ * Remove a plugin glob pattern from the config
45
+ */
46
+ declare function removePluginGlob(glob: string, configPath?: string): Promise<void>;
47
+ /**
48
+ * List all plugin glob patterns in the config
49
+ */
50
+ declare function listPluginGlobs(configPath?: string): Promise<string[]>;
51
+ /**
52
+ * Get absolute plugin paths from config globs
53
+ */
54
+ declare function getPluginPaths(configPath?: string): Promise<string[]>;
55
+ //#endregion
56
+ export { LbScaffoldConfig, SCAFFOLD_TEMPLATE_PLUGIN_TYPE, ScaffoldTemplatePlugin, ScaffoldTemplatePluginOptions, addPluginGlob, findConfigPath, getDefaultConfigPath, getPluginPaths, initConfig, listPluginGlobs, loadConfig, removePluginGlob, saveConfig };
57
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1,3 @@
1
+ import e from"fs/promises";import t from"path";const n=`scaffold-template`,r=`.lbscaffold`;async function i(n=process.cwd()){let i=n;for(;;){let n=t.join(i,r);try{return await e.access(n),n}catch{let e=t.dirname(i);if(e===i)return null;i=e}}}function a(){return t.join(process.cwd(),r)}async function o(t){let n=t??await i();if(!n)return{};try{let t=await e.readFile(n,`utf-8`);return JSON.parse(t)}catch(e){if(e.code===`ENOENT`)return{};throw Error(`Failed to load config from ${n}: ${e.message}`)}}async function s(t,n){let r=n??a();await e.writeFile(r,JSON.stringify(t,null,2)+`
2
+ `,`utf-8`)}async function c(t){let n=t??a();try{throw await e.access(n),Error(`Config file already exists at ${n}`)}catch(e){if(e.code!==`ENOENT`)throw e}return await s({plugins:[`./plugins/**`]},n),n}async function l(e,t){let n=await o(t);if(n.plugins||=[],n.plugins.includes(e))throw Error(`Plugin pattern '${e}' already exists in config`);n.plugins.push(e),await s(n,t??await i()??a())}async function u(e,t){let n=t??await i();if(!n)throw Error(`No config file found`);let r=await o(n);if(!r.plugins||!r.plugins.includes(e))throw Error(`Plugin pattern '${e}' not found in config`);r.plugins=r.plugins.filter(t=>t!==e),await s(r,n)}async function d(e){return(await o(e)).plugins??[]}async function f(e){let n=e??await i();if(!n)return[];let r=await o(n),a=t.dirname(n);return(r.plugins??[]).map(e=>t.isAbsolute(e)?e.replace(/\\/g,`/`):t.resolve(a,e).replace(/\\/g,`/`))}export{n as SCAFFOLD_TEMPLATE_PLUGIN_TYPE,l as addPluginGlob,i as findConfigPath,a as getDefaultConfigPath,f as getPluginPaths,c as initConfig,d as listPluginGlobs,o as loadConfig,u as removePluginGlob,s as saveConfig};
3
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/core/scaffold-template-plugin.ts","../../src/config.ts"],"sourcesContent":["import { ScaffoldTemplatePluginOptions } from './types';\n\nexport const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = 'scaffold-template';\n\nexport interface ScaffoldTemplatePlugin {\n argument: string;\n\n execute(options: ScaffoldTemplatePluginOptions): Promise<void>;\n}\n","import fs from 'fs/promises';\nimport path from 'path';\n\nexport interface LbScaffoldConfig {\n plugins?: string[];\n}\n\nconst CONFIG_FILENAME = '.lbscaffold';\n\n/**\n * Find the config file by searching up the directory tree\n */\nexport async function findConfigPath(startDir: string = process.cwd()): Promise<string | null> {\n let currentDir = startDir;\n\n while (true) {\n const configPath = path.join(currentDir, CONFIG_FILENAME);\n try {\n await fs.access(configPath);\n return configPath;\n } catch {\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) {\n // Reached root\n return null;\n }\n currentDir = parentDir;\n }\n }\n}\n\n/**\n * Get the default config path in the current directory\n */\nexport function getDefaultConfigPath(): string {\n return path.join(process.cwd(), CONFIG_FILENAME);\n}\n\n/**\n * Load the config file\n */\nexport async function loadConfig(configPath?: string): Promise<LbScaffoldConfig> {\n const resolvedPath = configPath ?? (await findConfigPath());\n\n if (!resolvedPath) {\n return {};\n }\n\n try {\n const content = await fs.readFile(resolvedPath, 'utf-8');\n return JSON.parse(content) as LbScaffoldConfig;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return {};\n }\n throw new Error(`Failed to load config from ${resolvedPath}: ${(error as Error).message}`);\n }\n}\n\n/**\n * Save the config file\n */\nexport async function saveConfig(config: LbScaffoldConfig, configPath?: string): Promise<void> {\n const resolvedPath = configPath ?? getDefaultConfigPath();\n await fs.writeFile(resolvedPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\n/**\n * Initialize a new config file\n */\nexport async function initConfig(configPath?: string): Promise<string> {\n const resolvedPath = configPath ?? getDefaultConfigPath();\n\n try {\n await fs.access(resolvedPath);\n throw new Error(`Config file already exists at ${resolvedPath}`);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n\n const defaultConfig: LbScaffoldConfig = {\n plugins: ['./plugins/**'],\n };\n\n await saveConfig(defaultConfig, resolvedPath);\n return resolvedPath;\n}\n\n/**\n * Add a plugin glob pattern to the config\n */\nexport async function addPluginGlob(glob: string, configPath?: string): Promise<void> {\n const config = await loadConfig(configPath);\n\n if (!config.plugins) {\n config.plugins = [];\n }\n\n if (config.plugins.includes(glob)) {\n throw new Error(`Plugin pattern '${glob}' already exists in config`);\n }\n\n config.plugins.push(glob);\n await saveConfig(config, configPath ?? (await findConfigPath()) ?? getDefaultConfigPath());\n}\n\n/**\n * Remove a plugin glob pattern from the config\n */\nexport async function removePluginGlob(glob: string, configPath?: string): Promise<void> {\n const resolvedPath = configPath ?? (await findConfigPath());\n\n if (!resolvedPath) {\n throw new Error('No config file found');\n }\n\n const config = await loadConfig(resolvedPath);\n\n if (!config.plugins || !config.plugins.includes(glob)) {\n throw new Error(`Plugin pattern '${glob}' not found in config`);\n }\n\n config.plugins = config.plugins.filter(p => p !== glob);\n await saveConfig(config, resolvedPath);\n}\n\n/**\n * List all plugin glob patterns in the config\n */\nexport async function listPluginGlobs(configPath?: string): Promise<string[]> {\n const config = await loadConfig(configPath);\n return config.plugins ?? [];\n}\n\n/**\n * Get absolute plugin paths from config globs\n */\nexport async function getPluginPaths(configPath?: string): Promise<string[]> {\n const resolvedConfigPath = configPath ?? (await findConfigPath());\n\n if (!resolvedConfigPath) {\n return [];\n }\n\n const config = await loadConfig(resolvedConfigPath);\n const configDir = path.dirname(resolvedConfigPath);\n\n return (config.plugins ?? []).map(glob => {\n // If glob is absolute, use as-is; otherwise resolve relative to config file\n if (path.isAbsolute(glob)) {\n return glob.replace(/\\\\/g, '/');\n }\n return path.resolve(configDir, glob).replace(/\\\\/g, '/');\n });\n}\n"],"mappings":"+CAEA,MAAa,EAAgC,oBCKvC,EAAkB,cAKxB,eAAsB,EAAe,EAAmB,QAAQ,KAAK,CAA0B,CAC3F,IAAI,EAAa,EAEjB,OAAa,CACT,IAAM,EAAa,EAAK,KAAK,EAAY,EAAgB,CACzD,GAAI,CAEA,OADA,MAAM,EAAG,OAAO,EAAW,CACpB,OACH,CACJ,IAAM,EAAY,EAAK,QAAQ,EAAW,CAC1C,GAAI,IAAc,EAEd,OAAO,KAEX,EAAa,IAQzB,SAAgB,GAA+B,CAC3C,OAAO,EAAK,KAAK,QAAQ,KAAK,CAAE,EAAgB,CAMpD,eAAsB,EAAW,EAAgD,CAC7E,IAAM,EAAe,GAAe,MAAM,GAAgB,CAE1D,GAAI,CAAC,EACD,MAAO,EAAE,CAGb,GAAI,CACA,IAAM,EAAU,MAAM,EAAG,SAAS,EAAc,QAAQ,CACxD,OAAO,KAAK,MAAM,EAAQ,OACrB,EAAO,CACZ,GAAK,EAAgC,OAAS,SAC1C,MAAO,EAAE,CAEb,MAAU,MAAM,8BAA8B,EAAa,IAAK,EAAgB,UAAU,EAOlG,eAAsB,EAAW,EAA0B,EAAoC,CAC3F,IAAM,EAAe,GAAc,GAAsB,CACzD,MAAM,EAAG,UAAU,EAAc,KAAK,UAAU,EAAQ,KAAM,EAAE,CAAG;EAAM,QAAQ,CAMrF,eAAsB,EAAW,EAAsC,CACnE,IAAM,EAAe,GAAc,GAAsB,CAEzD,GAAI,CAEA,MADA,MAAM,EAAG,OAAO,EAAa,CACnB,MAAM,iCAAiC,IAAe,OAC3D,EAAO,CACZ,GAAK,EAAgC,OAAS,SAC1C,MAAM,EASd,OADA,MAAM,EAJkC,CACpC,QAAS,CAAC,eAAe,CAC5B,CAE+B,EAAa,CACtC,EAMX,eAAsB,EAAc,EAAc,EAAoC,CAClF,IAAM,EAAS,MAAM,EAAW,EAAW,CAM3C,GAJA,AACI,EAAO,UAAU,EAAE,CAGnB,EAAO,QAAQ,SAAS,EAAK,CAC7B,MAAU,MAAM,mBAAmB,EAAK,4BAA4B,CAGxE,EAAO,QAAQ,KAAK,EAAK,CACzB,MAAM,EAAW,EAAQ,GAAe,MAAM,GAAgB,EAAK,GAAsB,CAAC,CAM9F,eAAsB,EAAiB,EAAc,EAAoC,CACrF,IAAM,EAAe,GAAe,MAAM,GAAgB,CAE1D,GAAI,CAAC,EACD,MAAU,MAAM,uBAAuB,CAG3C,IAAM,EAAS,MAAM,EAAW,EAAa,CAE7C,GAAI,CAAC,EAAO,SAAW,CAAC,EAAO,QAAQ,SAAS,EAAK,CACjD,MAAU,MAAM,mBAAmB,EAAK,uBAAuB,CAGnE,EAAO,QAAU,EAAO,QAAQ,OAAO,GAAK,IAAM,EAAK,CACvD,MAAM,EAAW,EAAQ,EAAa,CAM1C,eAAsB,EAAgB,EAAwC,CAE1E,OADe,MAAM,EAAW,EAAW,EAC7B,SAAW,EAAE,CAM/B,eAAsB,EAAe,EAAwC,CACzE,IAAM,EAAqB,GAAe,MAAM,GAAgB,CAEhE,GAAI,CAAC,EACD,MAAO,EAAE,CAGb,IAAM,EAAS,MAAM,EAAW,EAAmB,CAC7C,EAAY,EAAK,QAAQ,EAAmB,CAElD,OAAQ,EAAO,SAAW,EAAE,EAAE,IAAI,GAE1B,EAAK,WAAW,EAAK,CACd,EAAK,QAAQ,MAAO,IAAI,CAE5B,EAAK,QAAQ,EAAW,EAAK,CAAC,QAAQ,MAAO,IAAI,CAC1D"}