@litodocs/cli 0.5.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lito docs
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,260 @@
1
+ # Lito
2
+
3
+ Beautiful documentation sites from Markdown. Fast, simple, and open-source.
4
+
5
+ ## Features
6
+
7
+ ✨ **Simple Setup** - Point to your docs folder and go
8
+ 🚀 **Astro-Powered** - Leverages Astro's speed and SEO optimization
9
+ 📝 **Markdown & MDX** - Full support for both formats with frontmatter
10
+ 🎨 **Customizable Templates** - Use GitHub-hosted or local templates
11
+ 🔥 **Hot Reload** - Dev server with live file watching
12
+ ⚡ **Fast Builds** - Static site generation for optimal performance
13
+ 🎯 **SEO Optimized** - Meta tags, semantic HTML, and proper structure
14
+
15
+ ## Installation
16
+
17
+ ### Global Installation
18
+
19
+ ```bash
20
+ npm install -g @litodocs/cli
21
+ # or
22
+ pnpm add -g @litodocs/cli
23
+ ```
24
+
25
+ ### Local Development
26
+
27
+ ```bash
28
+ cd lito
29
+ pnpm install
30
+ chmod +x bin/cli.js
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ### Build Command
36
+
37
+ Generate a static documentation site:
38
+
39
+ ```bash
40
+ lito build --input ./my-docs --output ./dist
41
+ ```
42
+
43
+ **Options:**
44
+
45
+ - `-i, --input <path>` (required) - Path to your docs folder
46
+ - `-o, --output <path>` - Output directory (default: `./dist`)
47
+ - `-t, --template <name>` - Template to use (see [Templates](#templates))
48
+ - `-b, --base-url <url>` - Base URL for the site (default: `/`)
49
+ - `--provider <name>` - optimize for hosting provider (vercel, netlify, cloudflare, static)
50
+ - `--rendering <mode>` - Rendering mode (static, server, hybrid)
51
+ - `--search` - Enable search functionality
52
+ - `--refresh` - Force re-download template from GitHub
53
+
54
+ ### Dev Command
55
+
56
+ Start a development server with hot reload:
57
+
58
+ ```bash
59
+ lito dev --input ./my-docs
60
+ ```
61
+
62
+ **Options:**
63
+
64
+ - `-i, --input <path>` (required) - Path to your docs folder
65
+ - `-t, --template <name>` - Template to use
66
+ - `-b, --base-url <url>` - Base URL for the site
67
+ - `-p, --port <number>` - Port for dev server (default: `4321`)
68
+ - `--search` - Enable search functionality
69
+ - `--refresh` - Force re-download template
70
+
71
+ ### Eject Command
72
+
73
+ Export the full Astro project source code to customize it further:
74
+
75
+ ```bash
76
+ lito eject --input ./my-docs --output ./my-project
77
+ ```
78
+
79
+ ## Deployment
80
+
81
+ Lito includes built-in optimizations for major hosting providers. Use the `--provider` flag during build:
82
+
83
+ ### Vercel
84
+ ```bash
85
+ lito build -i ./docs --provider vercel
86
+ ```
87
+ Generates `vercel.json` and optimizes for Vercel's edge network.
88
+
89
+ ### Netlify
90
+ ```bash
91
+ lito build -i ./docs --provider netlify
92
+ ```
93
+ Generates `netlify.toml` with security headers.
94
+
95
+ ### Cloudflare Pages
96
+ ```bash
97
+ lito build -i ./docs --provider cloudflare --rendering server
98
+ ```
99
+ Configures the project for Cloudflare's edge runtime with SSR support.
100
+
101
+ ## Analytics
102
+
103
+ Lito supports Google Analytics 4 out of the box with zero performance penalty (powered by Partytown).
104
+
105
+ Add this to your `docs-config.json`:
106
+
107
+ ```json
108
+ {
109
+ "integrations": {
110
+ "analytics": {
111
+ "provider": "google-analytics",
112
+ "measurementId": "G-XXXXXXXXXX"
113
+ }
114
+ }
115
+ }
116
+ ```
117
+
118
+ ## Templates
119
+
120
+ Lito supports flexible template sources:
121
+
122
+ ### Default Template
123
+
124
+ ```bash
125
+ lito dev -i ./docs
126
+ ```
127
+
128
+ ### GitHub Templates
129
+
130
+ Use templates hosted on GitHub:
131
+
132
+ ```bash
133
+ # From a GitHub repo
134
+ lito dev -i ./docs --template github:owner/repo
135
+
136
+ # Specific branch or tag
137
+ lito dev -i ./docs --template github:owner/repo#v1.0.0
138
+
139
+ # Template in a subdirectory
140
+ lito dev -i ./docs --template github:owner/repo/templates/modern
141
+ ```
142
+
143
+ ### Local Templates
144
+
145
+ Use a local template folder:
146
+
147
+ ```bash
148
+ lito dev -i ./docs --template ./my-custom-template
149
+ ```
150
+
151
+ ### Template Management
152
+
153
+ ```bash
154
+ # List available templates
155
+ lito template list
156
+
157
+ # Clear template cache
158
+ lito template cache --clear
159
+ ```
160
+
161
+ ### Update Templates
162
+
163
+ Templates are cached for 24 hours. Force update with:
164
+
165
+ ```bash
166
+ lito dev -i ./docs --refresh
167
+ ```
168
+
169
+ ## Documentation Structure
170
+
171
+ Your docs folder should contain Markdown (`.md`) or MDX (`.mdx`) files:
172
+
173
+ ```
174
+ my-docs/
175
+ ├── index.md
176
+ ├── getting-started.md
177
+ ├── api/
178
+ │ ├── reference.md
179
+ │ └── examples.md
180
+ └── guides/
181
+ └── advanced.md
182
+ ```
183
+
184
+ ### Frontmatter
185
+
186
+ Add frontmatter to your markdown files for better metadata:
187
+
188
+ ```markdown
189
+ ---
190
+ title: Getting Started
191
+ description: Learn how to get started quickly
192
+ ---
193
+
194
+ # Getting Started
195
+
196
+ Your content here...
197
+ ```
198
+
199
+ ## Architecture
200
+
201
+ The CLI tool:
202
+
203
+ 1. **Resolves Template** - Fetches from GitHub or uses local template
204
+ 2. **Scaffolds** - Creates a temporary Astro project from the template
205
+ 3. **Syncs** - Copies your docs into `src/pages/` for automatic routing
206
+ 4. **Configures** - Generates dynamic `astro.config.mjs` with your options
207
+ 5. **Builds/Serves** - Spawns native Astro CLI commands
208
+ 6. **Watches** (dev mode) - Uses `chokidar` to monitor file changes
209
+
210
+ ## Development
211
+
212
+ ### Project Structure
213
+
214
+ ```
215
+ lito/
216
+ ├── bin/
217
+ │ └── cli.js # CLI entry point
218
+ ├── src/
219
+ │ ├── cli.js # Commander setup
220
+ │ ├── commands/
221
+ │ │ ├── build.js # Build command
222
+ │ │ ├── dev.js # Dev command with watcher
223
+ │ │ ├── eject.js # Eject command
224
+ │ │ └── template.js # Template management
225
+ │ ├── core/
226
+ │ │ ├── scaffold.js # Project scaffolding
227
+ │ │ ├── sync.js # File syncing
228
+ │ │ ├── config.js # Config generation
229
+ │ │ ├── astro.js # Astro CLI spawning
230
+ │ │ ├── template-fetcher.js # GitHub template fetching
231
+ │ │ └── template-registry.js # Template name registry
232
+ │ └── template/ # Bundled fallback template
233
+ └── package.json
234
+ ```
235
+
236
+ ### Running Tests
237
+
238
+ ```bash
239
+ # Create sample docs
240
+ mkdir sample-docs
241
+ echo "# Hello\n\nWelcome!" > sample-docs/index.md
242
+
243
+ # Test build
244
+ node bin/cli.js build -i sample-docs -o test-output
245
+
246
+ # Test dev server
247
+ node bin/cli.js dev -i sample-docs
248
+ ```
249
+
250
+ ## Contributing
251
+
252
+ Contributions are welcome! Please feel free to submit a Pull Request.
253
+
254
+ ## License
255
+
256
+ MIT
257
+
258
+ ---
259
+
260
+ **Built with ❤️ using Astro and Node.js**
package/bin/cli.js ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+
3
+ import('../src/cli.js')
4
+ .then(({ cli }) => cli())
5
+ .catch((error) => {
6
+ console.error('Fatal error:', error);
7
+ process.exit(1);
8
+ });
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@litodocs/cli",
3
+ "version": "0.5.0",
4
+ "description": "Beautiful documentation sites from Markdown. Fast, simple, and open-source.",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "lito": "./bin/cli.js"
9
+ },
10
+ "scripts": {
11
+ "test": "echo \"Error: no test specified\" && exit 1"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/Lito-docs/cli.git"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/Lito-docs/cli/issues"
19
+ },
20
+ "homepage": "https://github.com/Lito-docs/cli#readme",
21
+ "keywords": [
22
+ "documentation",
23
+ "docs",
24
+ "markdown",
25
+ "mdx",
26
+ "astro",
27
+ "static-site-generator",
28
+ "documentation-generator",
29
+ "lito",
30
+ "open-source"
31
+ ],
32
+ "author": "Lito-docs",
33
+ "license": "MIT",
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "engines": {
38
+ "node": ">=18.0.0"
39
+ },
40
+ "dependencies": {
41
+ "@astrojs/mdx": "^3.1.0",
42
+ "@clack/prompts": "^0.11.0",
43
+ "astro": "^4.16.0",
44
+ "chokidar": "^4.0.0",
45
+ "commander": "^12.1.0",
46
+ "execa": "^9.0.0",
47
+ "fs-extra": "^11.2.0",
48
+ "picocolors": "^1.1.1"
49
+ }
50
+ }
package/src/cli.js ADDED
@@ -0,0 +1,116 @@
1
+ import { Command } from "commander";
2
+ import pc from "picocolors";
3
+ import { buildCommand } from "./commands/build.js";
4
+ import { devCommand } from "./commands/dev.js";
5
+ import { ejectCommand } from "./commands/eject.js";
6
+ import {
7
+ templateListCommand,
8
+ templateCacheCommand,
9
+ } from "./commands/template.js";
10
+
11
+ export async function cli() {
12
+ const program = new Command();
13
+
14
+ program
15
+ .name("lito")
16
+ .description(
17
+ "Beautiful documentation sites from Markdown. Fast, simple, and open-source."
18
+ )
19
+ .version("0.5.0");
20
+
21
+ program
22
+ .command("build")
23
+ .description("Build the documentation site")
24
+ .requiredOption("-i, --input <path>", "Path to the docs folder")
25
+ .option(
26
+ "-o, --output <path>",
27
+ "Output directory for the built site",
28
+ "./dist"
29
+ )
30
+ .option(
31
+ "-t, --template <name>",
32
+ "Template to use (default, github:owner/repo, or local path)",
33
+ "default"
34
+ )
35
+ .option("-b, --base-url <url>", "Base URL for the site", "/")
36
+ .option("--name <name>", "Project name")
37
+ .option("--description <description>", "Project description")
38
+ .option("--primary-color <color>", "Primary theme color (hex)")
39
+ .option("--accent-color <color>", "Accent theme color (hex)")
40
+ .option("--favicon <path>", "Favicon path")
41
+ .option("--logo <path>", "Logo path")
42
+ .option("--provider <name>", "Hosting provider optimization (vercel, netlify, cloudflare, static)", "static")
43
+ .option("--rendering <type>", "Rendering mode (static, server, hybrid)", "static")
44
+ .option("--search", "Enable search functionality", false)
45
+ .option("--refresh", "Force re-download template (bypass cache)", false)
46
+ .action(buildCommand);
47
+
48
+ program
49
+ .command("dev")
50
+ .description("Start development server with watch mode")
51
+ .requiredOption("-i, --input <path>", "Path to the docs folder")
52
+ .option(
53
+ "-t, --template <name>",
54
+ "Template to use (default, github:owner/repo, or local path)",
55
+ "default"
56
+ )
57
+ .option("-b, --base-url <url>", "Base URL for the site", "/")
58
+ .option("--name <name>", "Project name")
59
+ .option("--description <description>", "Project description")
60
+ .option("--primary-color <color>", "Primary theme color (hex)")
61
+ .option("--accent-color <color>", "Accent theme color (hex)")
62
+ .option("--favicon <path>", "Favicon path")
63
+ .option("--logo <path>", "Logo path")
64
+ .option("--search", "Enable search functionality", false)
65
+ .option("-p, --port <number>", "Port for dev server", "4321")
66
+ .option("--refresh", "Force re-download template (bypass cache)", false)
67
+ .action(devCommand);
68
+
69
+ program
70
+ .command("eject")
71
+ .description("Export the full Astro project source code")
72
+ .requiredOption("-i, --input <path>", "Path to the docs folder")
73
+ .option(
74
+ "-o, --output <path>",
75
+ "Output directory for the project",
76
+ "./astro-docs-project"
77
+ )
78
+ .option(
79
+ "-t, --template <name>",
80
+ "Template to use (default, github:owner/repo, or local path)",
81
+ "default"
82
+ )
83
+ .option("-b, --base-url <url>", "Base URL for the site", "/")
84
+ .option("--name <name>", "Project name")
85
+ .option("--description <description>", "Project description")
86
+ .option("--primary-color <color>", "Primary theme color (hex)")
87
+ .option("--accent-color <color>", "Accent theme color (hex)")
88
+ .option("--favicon <path>", "Favicon path")
89
+ .option("--logo <path>", "Logo path")
90
+ .option("--search", "Enable search functionality", false)
91
+ .option("--refresh", "Force re-download template (bypass cache)", false)
92
+ .action(ejectCommand);
93
+
94
+ // Template management commands
95
+ const templateCmd = program
96
+ .command("template")
97
+ .description("Manage documentation templates");
98
+
99
+ templateCmd
100
+ .command("list")
101
+ .description("List available templates")
102
+ .action(templateListCommand);
103
+
104
+ templateCmd
105
+ .command("cache")
106
+ .description("Manage template cache")
107
+ .option("--clear", "Clear all cached templates")
108
+ .action(templateCacheCommand);
109
+
110
+ try {
111
+ await program.parseAsync(process.argv);
112
+ } catch (error) {
113
+ console.error(pc.red("Error:"), error.message);
114
+ process.exit(1);
115
+ }
116
+ }
@@ -0,0 +1,105 @@
1
+ import { existsSync } from 'fs';
2
+ import { resolve } from 'path';
3
+ import { intro, outro, spinner, log, isCancel, cancel } from '@clack/prompts';
4
+ import pc from 'picocolors';
5
+ import { scaffoldProject, cleanupProject } from '../core/scaffold.js';
6
+ import { syncDocs } from '../core/sync.js';
7
+ import { generateConfig } from '../core/config.js';
8
+ import { runAstroBuild } from '../core/astro.js';
9
+ import { syncDocsConfig } from '../core/config-sync.js';
10
+ import { copyOutput } from '../core/output.js';
11
+ import { getTemplatePath } from '../core/template-fetcher.js';
12
+
13
+ export async function buildCommand(options) {
14
+ try {
15
+ // Validate input path
16
+ const inputPath = resolve(options.input);
17
+ if (!existsSync(inputPath)) {
18
+ log.error(`Input path does not exist: ${pc.cyan(inputPath)}`);
19
+ process.exit(1);
20
+ }
21
+
22
+ console.clear();
23
+ intro(pc.inverse(pc.cyan(' Lito - Build ')));
24
+
25
+ const s = spinner();
26
+
27
+ // Step 0: Resolve template
28
+ s.start('Resolving template...');
29
+ const templatePath = await getTemplatePath(options.template, options.refresh);
30
+ s.stop(templatePath ? `Using template: ${pc.cyan(templatePath)}` : 'Using bundled template');
31
+
32
+ // Step 1: Scaffold temporary Astro project
33
+ s.start('Setting up Astro project...');
34
+ const projectDir = await scaffoldProject(templatePath);
35
+ s.stop('Astro project scaffolded');
36
+
37
+ // Step 2: Prepare project (Install dependencies, Sync docs, Generate navigation)
38
+ const { installDependencies, runBinary } = await import('../core/package-manager.js');
39
+ s.start('Preparing project (installing dependencies, syncing files)...');
40
+
41
+ const userConfigPath = resolve(options.input, 'docs-config.json');
42
+
43
+ await Promise.all([
44
+ installDependencies(projectDir, { silent: true }),
45
+ syncDocs(inputPath, projectDir),
46
+ syncDocsConfig(projectDir, inputPath, userConfigPath)
47
+ ]);
48
+
49
+ s.stop('Project prepared (dependencies installed, docs synced, navigation generated)');
50
+
51
+ // Step 4: Generate config
52
+ s.start('Generating Astro configuration...');
53
+ await generateConfig(projectDir, options);
54
+ s.stop('Configuration generated');
55
+
56
+ // Step 4.5: Configure for provider
57
+ if (options.provider && options.provider !== 'static') {
58
+ s.start(`Configuring for ${options.provider} (${options.rendering})...`);
59
+ const { configureProvider } = await import('../core/providers.js');
60
+ await configureProvider(projectDir, options.provider, options.rendering);
61
+ s.stop(`Configured for ${options.provider}`);
62
+ }
63
+
64
+ // Step 5: Build with Astro
65
+ s.start('Building site with Astro...');
66
+ await runAstroBuild(projectDir);
67
+ s.stop('Site built successfully');
68
+
69
+ // Step 5.5: Generate Pagefind search index
70
+ s.start('Generating search index...');
71
+ await runBinary(projectDir, 'pagefind', ['--site', 'dist']);
72
+ s.stop('Search index generated');
73
+
74
+ // Step 6: Copy output
75
+ const outputPath = resolve(options.output);
76
+ s.start(`Copying output to ${pc.cyan(outputPath)}...`);
77
+ await copyOutput(projectDir, outputPath);
78
+ s.stop('Output copied');
79
+
80
+ // Cleanup temp directory
81
+ s.start('Cleaning up...');
82
+ await cleanupProject();
83
+ s.stop('Cleanup complete');
84
+
85
+ outro(pc.green('Build completed successfully!'));
86
+ } catch (error) {
87
+ if (isCancel(error)) {
88
+ cancel('Operation cancelled.');
89
+ process.exit(0);
90
+ }
91
+
92
+ // Attempt to cleanup even on error
93
+ try {
94
+ await cleanupProject();
95
+ } catch (e) {
96
+ // failed to cleanup
97
+ }
98
+
99
+ log.error(pc.red(error.message));
100
+ if (error.stack) {
101
+ log.error(pc.gray(error.stack));
102
+ }
103
+ process.exit(1);
104
+ }
105
+ }
@@ -0,0 +1,133 @@
1
+ import { existsSync } from 'fs';
2
+ import { resolve } from 'path';
3
+ import { intro, outro, spinner, log, note, isCancel, cancel } from '@clack/prompts';
4
+ import pc from 'picocolors';
5
+ import chokidar from 'chokidar';
6
+ import { scaffoldProject, cleanupProject } from '../core/scaffold.js';
7
+ import { syncDocs } from '../core/sync.js';
8
+ import { generateConfig } from '../core/config.js';
9
+ import { runAstroDev } from '../core/astro.js';
10
+ import { syncDocsConfig } from '../core/config-sync.js';
11
+ import { getTemplatePath } from '../core/template-fetcher.js';
12
+
13
+ export async function devCommand(options) {
14
+ try {
15
+ // Validate input path
16
+ const inputPath = resolve(options.input);
17
+ if (!existsSync(inputPath)) {
18
+ log.error(`Input path does not exist: ${pc.cyan(inputPath)}`);
19
+ process.exit(1);
20
+ }
21
+
22
+ console.clear();
23
+ intro(pc.inverse(pc.cyan(' Lito - Dev Server ')));
24
+
25
+ const s = spinner();
26
+
27
+ // Step 0: Resolve template
28
+ s.start('Resolving template...');
29
+ const templatePath = await getTemplatePath(options.template, options.refresh);
30
+ s.stop(templatePath ? `Using template: ${pc.cyan(templatePath)}` : 'Using bundled template');
31
+
32
+ // Step 1: Scaffold temporary Astro project
33
+ s.start('Setting up Astro project...');
34
+ const projectDir = await scaffoldProject(templatePath);
35
+ s.stop('Astro project scaffolded');
36
+
37
+ // Register cleanup handlers
38
+ const cleanup = async () => {
39
+ s.start('Cleaning up...');
40
+ await cleanupProject();
41
+ s.stop('Cleanup complete');
42
+ process.exit(0);
43
+ };
44
+ process.on('SIGINT', cleanup);
45
+ process.on('SIGTERM', cleanup);
46
+
47
+ // Step 2: Prepare project (Install dependencies, Sync docs, Generate navigation)
48
+ const { installDependencies } = await import('../core/package-manager.js');
49
+ s.start('Preparing project (installing dependencies, syncing files)...');
50
+
51
+ const userConfigPath = resolve(options.input, 'docs-config.json');
52
+
53
+ await Promise.all([
54
+ installDependencies(projectDir, { silent: true }),
55
+ syncDocs(inputPath, projectDir),
56
+ syncDocsConfig(projectDir, inputPath, userConfigPath)
57
+ ]);
58
+
59
+ s.stop('Project prepared (dependencies installed, docs synced, navigation generated)');
60
+
61
+ // Step 4: Generate config
62
+ s.start('Generating Astro configuration...');
63
+ await generateConfig(projectDir, options);
64
+ s.stop('Configuration generated');
65
+
66
+ // Step 4: Setup file watcher with debouncing
67
+ log.info(pc.cyan('Watching for file changes...'));
68
+
69
+ let syncTimeout = null;
70
+ let isSyncing = false;
71
+
72
+ const debouncedSync = async () => {
73
+ if (isSyncing) return;
74
+
75
+ if (syncTimeout) {
76
+ clearTimeout(syncTimeout);
77
+ }
78
+
79
+ syncTimeout = setTimeout(async () => {
80
+ if (isSyncing) return;
81
+ isSyncing = true;
82
+
83
+ try {
84
+ await Promise.all([
85
+ syncDocs(inputPath, projectDir),
86
+ syncDocsConfig(projectDir, inputPath, userConfigPath)
87
+ ]);
88
+ log.success('Documentation and config re-synced');
89
+ } catch (error) {
90
+ log.error('Sync failed: ' + error.message);
91
+ } finally {
92
+ isSyncing = false;
93
+ }
94
+ }, 300); // 300ms debounce
95
+ };
96
+
97
+ const watcher = chokidar.watch(inputPath, {
98
+ ignored: /(^|[\/\\])\../,
99
+ persistent: true,
100
+ ignoreInitial: true, // Don't trigger for existing files
101
+ });
102
+
103
+ watcher.on('change', async (path) => {
104
+ log.info(`File changed: ${pc.dim(path)}`);
105
+ debouncedSync();
106
+ });
107
+
108
+ watcher.on('add', async (path) => {
109
+ log.success(`File added: ${pc.dim(path)}`);
110
+ debouncedSync();
111
+ });
112
+
113
+ watcher.on('unlink', async (path) => {
114
+ log.warning(`File removed: ${pc.dim(path)}`);
115
+ debouncedSync();
116
+ });
117
+
118
+ // Step 5: Start Astro dev server
119
+ note(`Starting Astro dev server at http://localhost:${options.port}`, 'Dev Server');
120
+ await runAstroDev(projectDir, options.port);
121
+
122
+ } catch (error) {
123
+ if (isCancel(error)) {
124
+ cancel('Operation cancelled.');
125
+ process.exit(0);
126
+ }
127
+ log.error(pc.red('Dev server failed: ' + error.message));
128
+ if (error.stack) {
129
+ log.error(pc.gray(error.stack));
130
+ }
131
+ process.exit(1);
132
+ }
133
+ }