feat(ccw): migrate backend to TypeScript

- Convert 40 JS files to TypeScript (CLI, tools, core, MCP server)
- Add Zod for runtime parameter validation
- Add type definitions in src/types/
- Keep src/templates/ as JavaScript (dashboard frontend)
- Update bin entries to use dist/
- Add tsconfig.json with strict mode
- Add backward-compatible exports for tests
- All 39 tests passing

Breaking changes: None (backward compatible)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
catlog22
2025-12-13 10:43:15 +08:00
parent d4e59770d0
commit 25ac862f46
93 changed files with 5531 additions and 9302 deletions

76
ccw/src/commands/serve.ts Normal file
View File

@@ -0,0 +1,76 @@
import { startServer } from '../core/server.js';
import { launchBrowser } from '../utils/browser-launcher.js';
import { resolvePath, validatePath } from '../utils/path-resolver.js';
import chalk from 'chalk';
import type { Server } from 'http';
interface ServeOptions {
port?: number;
path?: string;
browser?: boolean;
}
/**
* Serve command handler - starts dashboard server with live path switching
* @param {Object} options - Command options
*/
export async function serveCommand(options: ServeOptions): Promise<void> {
const port = options.port || 3456;
// Validate project path
let initialPath = process.cwd();
if (options.path) {
const pathValidation = validatePath(options.path, { mustExist: true });
if (!pathValidation.valid || !pathValidation.path) {
console.error(chalk.red(`\n Error: ${pathValidation.error}\n`));
process.exit(1);
}
initialPath = pathValidation.path;
}
console.log(chalk.blue.bold('\n CCW Dashboard Server\n'));
console.log(chalk.gray(` Initial project: ${initialPath}`));
console.log(chalk.gray(` Port: ${port}\n`));
try {
// Start server
console.log(chalk.cyan(' Starting server...'));
const server = await startServer({ port, initialPath });
const url = `http://localhost:${port}`;
console.log(chalk.green(` Server running at ${url}`));
// Open browser
if (options.browser !== false) {
console.log(chalk.cyan(' Opening in browser...'));
try {
await launchBrowser(url);
console.log(chalk.green.bold('\n Dashboard opened in browser!'));
} catch (err) {
const error = err as Error;
console.log(chalk.yellow(`\n Could not open browser: ${error.message}`));
console.log(chalk.gray(` Open manually: ${url}`));
}
}
console.log(chalk.gray('\n Press Ctrl+C to stop the server\n'));
// Handle graceful shutdown
process.on('SIGINT', () => {
console.log(chalk.yellow('\n Shutting down server...'));
server.close(() => {
console.log(chalk.green(' Server stopped.\n'));
process.exit(0);
});
});
} catch (error) {
const err = error as Error & { code?: string };
console.error(chalk.red(`\n Error: ${err.message}\n`));
if (err.code === 'EADDRINUSE') {
console.error(chalk.yellow(` Port ${port} is already in use.`));
console.error(chalk.gray(` Try a different port: ccw serve --port ${port + 1}\n`));
}
process.exit(1);
}
}