pqm-cli 1.0.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 (43) hide show
  1. package/README.md +254 -0
  2. package/bin/pqm.js +6 -0
  3. package/package.json +31 -0
  4. package/src/ai/analyzer/collector.js +191 -0
  5. package/src/ai/analyzer/dependency.js +269 -0
  6. package/src/ai/analyzer/index.js +234 -0
  7. package/src/ai/analyzer/quality.js +241 -0
  8. package/src/ai/analyzer/security.js +302 -0
  9. package/src/ai/index.js +16 -0
  10. package/src/ai/providers/bailian.js +121 -0
  11. package/src/ai/providers/base.js +177 -0
  12. package/src/ai/providers/deepseek.js +100 -0
  13. package/src/ai/providers/index.js +100 -0
  14. package/src/ai/providers/openai.js +100 -0
  15. package/src/builders/base.js +35 -0
  16. package/src/builders/rollup.js +47 -0
  17. package/src/builders/vite.js +47 -0
  18. package/src/cli.js +41 -0
  19. package/src/commands/ai.js +317 -0
  20. package/src/commands/build.js +24 -0
  21. package/src/commands/commit.js +68 -0
  22. package/src/commands/config.js +113 -0
  23. package/src/commands/doctor.js +146 -0
  24. package/src/commands/init.js +61 -0
  25. package/src/commands/login.js +37 -0
  26. package/src/commands/publish.js +250 -0
  27. package/src/commands/release.js +107 -0
  28. package/src/commands/scan.js +239 -0
  29. package/src/commands/status.js +129 -0
  30. package/src/commands/watch.js +170 -0
  31. package/src/commands/webhook.js +240 -0
  32. package/src/config/detector.js +82 -0
  33. package/src/config/global.js +136 -0
  34. package/src/config/loader.js +49 -0
  35. package/src/core/builder.js +88 -0
  36. package/src/index.js +5 -0
  37. package/src/logs/build.js +47 -0
  38. package/src/logs/manager.js +60 -0
  39. package/src/report/formatter.js +282 -0
  40. package/src/utils/http.js +130 -0
  41. package/src/utils/logger.js +24 -0
  42. package/src/utils/prompt.js +132 -0
  43. package/src/utils/spinner.js +134 -0
@@ -0,0 +1,129 @@
1
+ import { execSync } from 'child_process';
2
+ import { existsSync, readFileSync } from 'fs';
3
+ import { join } from 'path';
4
+ import chalk from 'chalk';
5
+
6
+ export default function (program) {
7
+ program
8
+ .command('status')
9
+ .description('Show project status and recent changes')
10
+ .action(() => {
11
+ console.log(chalk.bold(chalk.cyan('\n📊 项目状态\n')));
12
+
13
+ // 读取 package.json
14
+ const pkgPath = join(process.cwd(), 'package.json');
15
+ if (existsSync(pkgPath)) {
16
+ try {
17
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
18
+ console.log(chalk.cyan('📦 包信息'));
19
+ console.log(chalk.gray('─'.repeat(40)));
20
+ console.log(`名称: ${chalk.bold(pkg.name || 'N/A')}`);
21
+ console.log(`版本: ${chalk.green('v' + (pkg.version || '0.0.0'))}`);
22
+ if (pkg.description) {
23
+ console.log(`描述: ${pkg.description}`);
24
+ }
25
+ console.log(chalk.gray('─'.repeat(40)));
26
+ } catch {
27
+ console.log(chalk.red('无法读取 package.json'));
28
+ }
29
+ }
30
+
31
+ // Git 状态
32
+ try {
33
+ const branch = execSync('git branch --show-current 2>/dev/null', {
34
+ encoding: 'utf8'
35
+ }).trim();
36
+
37
+ if (branch) {
38
+ const remote = execSync('git remote get-url origin 2>/dev/null', {
39
+ encoding: 'utf8'
40
+ }).trim();
41
+
42
+ const status = execSync('git status --porcelain 2>/dev/null', {
43
+ encoding: 'utf8'
44
+ }).trim();
45
+
46
+ console.log(chalk.cyan('\n🌿 Git 状态'));
47
+ console.log(chalk.gray('─'.repeat(40)));
48
+ console.log(`分支: ${chalk.green(branch)}`);
49
+ if (remote) {
50
+ console.log(`远程: ${remote}`);
51
+ }
52
+
53
+ if (status) {
54
+ const lines = status.split('\n').filter(l => l.trim());
55
+ console.log(`变更: ${chalk.yellow(lines.length + ' 个文件')}`);
56
+ lines.slice(0, 5).forEach(line => {
57
+ console.log(` ${line}`);
58
+ });
59
+ if (lines.length > 5) {
60
+ console.log(chalk.gray(` ... 还有 ${lines.length - 5} 个文件`));
61
+ }
62
+ } else {
63
+ console.log(`变更: ${chalk.green('工作区干净')}`);
64
+ }
65
+ console.log(chalk.gray('─'.repeat(40)));
66
+
67
+ // 最近的提交
68
+ try {
69
+ const recentCommits = execSync('git log --oneline -5 2>/dev/null', {
70
+ encoding: 'utf8'
71
+ }).trim();
72
+
73
+ if (recentCommits) {
74
+ console.log(chalk.cyan('\n📝 最近提交'));
75
+ console.log(chalk.gray('─'.repeat(40)));
76
+ recentCommits.split('\n').forEach(line => {
77
+ console.log(` ${line}`);
78
+ });
79
+ console.log(chalk.gray('─'.repeat(40)));
80
+ }
81
+ } catch {
82
+ // 无提交历史
83
+ }
84
+
85
+ // 最近的标签
86
+ try {
87
+ const lastTag = execSync('git describe --tags --abbrev=0 2>/dev/null', {
88
+ encoding: 'utf8'
89
+ }).trim();
90
+
91
+ if (lastTag) {
92
+ console.log(chalk.cyan('\n🏷 最新标签'));
93
+ console.log(chalk.gray('─'.repeat(40)));
94
+ console.log(` ${chalk.green(lastTag)}`);
95
+ console.log(chalk.gray('─'.repeat(40)));
96
+ }
97
+ } catch {
98
+ // 没有标签
99
+ }
100
+ }
101
+ } catch {
102
+ // 不是 Git 仓库,忽略
103
+ }
104
+
105
+ // NPM 认证状态
106
+ try {
107
+ const user = execSync('npm whoami 2>/dev/null', {
108
+ encoding: 'utf8'
109
+ }).trim();
110
+
111
+ const registry = execSync('npm config get registry 2>/dev/null', {
112
+ encoding: 'utf8'
113
+ }).trim();
114
+
115
+ console.log(chalk.cyan('\n🔐 NPM 状态'));
116
+ console.log(chalk.gray('─'.repeat(40)));
117
+ console.log(`用户: ${user && user !== 'undefined' ? chalk.green(user) : chalk.red('未登录')}`);
118
+ console.log(`Registry: ${registry || 'https://registry.npmjs.org/'}`);
119
+ console.log(chalk.gray('─'.repeat(40)));
120
+ } catch {
121
+ console.log(chalk.cyan('\n🔐 NPM 状态'));
122
+ console.log(chalk.gray('─'.repeat(40)));
123
+ console.log(`用户: ${chalk.red('未登录')}`);
124
+ console.log(chalk.gray('─'.repeat(40)));
125
+ }
126
+
127
+ console.log('');
128
+ });
129
+ }
@@ -0,0 +1,170 @@
1
+ import chokidar from 'chokidar';
2
+ import http from 'http';
3
+ import chalk from 'chalk';
4
+ import { logger } from '../utils/logger.js';
5
+ import { loadConfig } from './config.js';
6
+ import { buildProject } from '../core/builder.js';
7
+
8
+ // Debounce timer for build triggers
9
+ let buildTimeout = null;
10
+
11
+ export default function (program) {
12
+ program
13
+ .command('watch')
14
+ .description('Start file watcher and auto build')
15
+ .option('-p, --path <path>', 'Watch path', './src')
16
+ .option('-t, --tool <tool>', 'Build tool (vite/rollup)')
17
+ .option('-m, --mode <mode>', 'Build mode (incremental/full)', 'incremental')
18
+ .option('--webhook', 'Enable webhook receiver')
19
+ .option('--port <port>', 'Webhook port', '3200')
20
+ .option('--poll', 'Use polling', false)
21
+ .action(async (options) => {
22
+ logger.info('Starting pqm watch...');
23
+ console.log('');
24
+
25
+ // Load config
26
+ const config = loadConfig();
27
+ const watchPath = options.path || config.root || './src';
28
+ const buildTool = options.tool || config.buildTool || 'auto';
29
+ const buildMode = options.mode || config.buildMode || 'incremental';
30
+
31
+ logger.info(`Watch path: ${watchPath}`);
32
+ logger.info(`Build tool: ${buildTool}`);
33
+ logger.info(`Build mode: ${buildMode}`);
34
+ logger.info(`Platform: ${process.platform}`);
35
+
36
+ // Use polling on Windows by default
37
+ const usePolling = options.poll || process.platform === 'win32';
38
+ logger.info(`Polling: ${usePolling ? 'enabled' : 'disabled'}`);
39
+ console.log('');
40
+
41
+ // Excludes
42
+ const excludes = config.exclude || ['node_modules', 'dist', '.git'];
43
+
44
+ // Create watcher
45
+ const watcher = chokidar.watch(watchPath, {
46
+ ignored: [
47
+ ...excludes,
48
+ '**/node_modules/**',
49
+ '**/dist/**',
50
+ '**/.git/**',
51
+ '**/*.log',
52
+ '**/.pqm/**'
53
+ ],
54
+ persistent: true,
55
+ ignoreInitial: true,
56
+ usePolling: usePolling,
57
+ interval: 100,
58
+ binaryInterval: 300,
59
+ awaitWriteFinish: {
60
+ stabilityThreshold: 200,
61
+ pollInterval: 50
62
+ }
63
+ });
64
+
65
+ // Helper function to get relative path
66
+ const getRelativePath = (filePath) => {
67
+ return filePath.replace(process.cwd(), '.').replace(/\\/g, '/');
68
+ };
69
+
70
+ // Event handlers
71
+ const handleChange = (event, filePath) => {
72
+ const relPath = getRelativePath(filePath);
73
+ const timestamp = new Date().toISOString().slice(11, 19);
74
+
75
+ const eventIcon = {
76
+ add: chalk.green('+'),
77
+ change: chalk.yellow('~'),
78
+ unlink: chalk.red('-')
79
+ }[event] || '?';
80
+
81
+ console.log(`[${timestamp}] ${eventIcon} ${relPath}`);
82
+
83
+ // Trigger build with debounce
84
+ if (event !== 'unlink') {
85
+ clearTimeout(buildTimeout);
86
+ buildTimeout = setTimeout(() => {
87
+ triggerBuild(buildTool, buildMode);
88
+ }, 300);
89
+ }
90
+ };
91
+
92
+ // Register event handlers
93
+ watcher.on('add', (path) => handleChange('add', path));
94
+ watcher.on('change', (path) => handleChange('change', path));
95
+ watcher.on('unlink', (path) => handleChange('unlink', path));
96
+
97
+ watcher.on('ready', () => {
98
+ logger.success('Watcher ready');
99
+ logger.info(`Watching: ${watchPath}`);
100
+ console.log('');
101
+ logger.info('Press Ctrl+C to exit');
102
+
103
+ // Build on start if configured
104
+ if (config.buildOnStart) {
105
+ logger.build(buildMode);
106
+ triggerBuild(buildTool, buildMode);
107
+ }
108
+ });
109
+
110
+ watcher.on('error', (err) => {
111
+ logger.error(`Watcher error: ${err.message}`);
112
+ });
113
+
114
+ // Webhook server
115
+ if (options.webhook || config.webhook?.enabled) {
116
+ const port = parseInt(options.port) || config.webhook?.port || 3200;
117
+ startWebhookServer(port);
118
+ }
119
+
120
+ // Cleanup on exit
121
+ const cleanup = () => {
122
+ console.log('');
123
+ logger.info('Shutting down...');
124
+ watcher.close().then(() => {
125
+ logger.success('Watcher closed');
126
+ process.exit(0);
127
+ });
128
+ };
129
+
130
+ process.on('SIGINT', cleanup);
131
+ process.on('SIGTERM', cleanup);
132
+ });
133
+ }
134
+
135
+ function triggerBuild(tool, mode) {
136
+ buildProject({ tool, mode })
137
+ .then(() => {
138
+ logger.success('Build completed');
139
+ })
140
+ .catch(err => {
141
+ logger.error(`Build failed: ${err.message}`);
142
+ });
143
+ }
144
+
145
+ function startWebhookServer(port) {
146
+ const server = http.createServer((req, res) => {
147
+ if (req.method === 'POST' && req.url === '/trigger') {
148
+ let body = '';
149
+ req.on('data', chunk => body += chunk);
150
+ req.on('end', () => {
151
+ try {
152
+ const data = JSON.parse(body);
153
+ logger.info(`Webhook received: ${data.action || 'trigger'}`);
154
+ res.writeHead(200, { 'Content-Type': 'application/json' });
155
+ res.end(JSON.stringify({ status: 'ok' }));
156
+ } catch {
157
+ res.writeHead(400);
158
+ res.end('Invalid JSON');
159
+ }
160
+ });
161
+ } else {
162
+ res.writeHead(404);
163
+ res.end('Not found');
164
+ }
165
+ });
166
+
167
+ server.listen(port, () => {
168
+ logger.info(`Webhook server on port ${port}`);
169
+ });
170
+ }
@@ -0,0 +1,240 @@
1
+ import http from 'http';
2
+ import { execSync } from 'child_process';
3
+ import { createHmac } from 'crypto';
4
+ import { existsSync, mkdirSync, writeFileSync, rmSync } from 'fs';
5
+ import { tmpdir } from 'os';
6
+ import { join } from 'path';
7
+ import { logger } from '../utils/logger.js';
8
+ import chalk from 'chalk';
9
+
10
+ export default function (program) {
11
+ const webhookCmd = program
12
+ .command('webhook')
13
+ .description('Webhook server for auto npm publish');
14
+
15
+ // 启动 webhook 服务器
16
+ webhookCmd
17
+ .command('start')
18
+ .description('Start webhook server for auto publish')
19
+ .option('-p, --port <port>', 'Server port', '3000')
20
+ .option('-s, --secret <secret>', 'Webhook secret for signature verification')
21
+ .action((options) => {
22
+ const PORT = parseInt(options.port) || 3000;
23
+ const SECRET = options.secret || '';
24
+ const NPM_TOKEN = process.env.NPM_TOKEN;
25
+
26
+ console.log(chalk.bold(chalk.cyan('\n🌐 NPM 发布 Webhook 服务器\n')));
27
+ console.log(chalk.gray('─'.repeat(50)));
28
+
29
+ // 检查 NPM_TOKEN
30
+ if (!NPM_TOKEN) {
31
+ logger.warn('未设置 NPM_TOKEN 环境变量');
32
+ console.log(chalk.yellow('建议: export NPM_TOKEN=your_token'));
33
+ }
34
+
35
+ logger.info(`端口: ${PORT}`);
36
+ logger.info(`签名验证: ${SECRET ? '已启用' : '未启用'}`);
37
+ console.log(chalk.gray('─'.repeat(50)));
38
+
39
+ // 验证签名
40
+ const verifySignature = (body, signature) => {
41
+ if (!SECRET) return true;
42
+ const hmac = createHmac('sha256', SECRET);
43
+ hmac.update(body);
44
+ const digest = `sha256=${hmac.digest('hex')}`;
45
+ return digest === signature;
46
+ };
47
+
48
+ // 处理发布
49
+ const handlePublish = async (tag, repoUrl) => {
50
+ const workDir = join(tmpdir(), `npm-publish-${Date.now()}`);
51
+ mkdirSync(workDir, { recursive: true });
52
+
53
+ try {
54
+ console.log(chalk.cyan('\n🚀 开始发布流程...'));
55
+ logger.info(`标签: ${tag}`);
56
+ logger.info(`工作目录: ${workDir}`);
57
+
58
+ // 1. 克隆代码
59
+ console.log(chalk.yellow('\n1️⃣ 克隆代码...'));
60
+ execSync(`git clone --depth 1 --branch ${tag} ${repoUrl} .`, {
61
+ cwd: workDir,
62
+ stdio: 'inherit',
63
+ env: { ...process.env, NPM_TOKEN }
64
+ });
65
+
66
+ // 2. 安装依赖
67
+ console.log(chalk.yellow('\n2️⃣ 安装依赖...'));
68
+ execSync('npm ci', {
69
+ cwd: workDir,
70
+ stdio: 'inherit',
71
+ env: { ...process.env, NPM_TOKEN }
72
+ });
73
+
74
+ // 3. 运行测试
75
+ console.log(chalk.yellow('\n3️⃣ 运行测试...'));
76
+ try {
77
+ execSync('npm test', {
78
+ cwd: workDir,
79
+ stdio: 'inherit',
80
+ env: { ...process.env, NPM_TOKEN }
81
+ });
82
+ } catch {
83
+ logger.warn('测试失败或未配置');
84
+ }
85
+
86
+ // 4. 构建
87
+ console.log(chalk.yellow('\n4️⃣ 构建...'));
88
+ try {
89
+ execSync('npm run build', {
90
+ cwd: workDir,
91
+ stdio: 'inherit',
92
+ env: { ...process.env, NPM_TOKEN }
93
+ });
94
+ } catch {
95
+ logger.warn('构建失败或未配置');
96
+ }
97
+
98
+ // 5. 发布
99
+ console.log(chalk.yellow('\n5️⃣ 发布到 npm...'));
100
+ const npmrcContent = `//registry.npmjs.org/:_authToken=${NPM_TOKEN}\nregistry=https://registry.npmjs.org/\n`;
101
+ writeFileSync(join(workDir, '.npmrc'), npmrcContent);
102
+
103
+ execSync('npm publish --access public', {
104
+ cwd: workDir,
105
+ stdio: 'inherit',
106
+ env: { ...process.env, NPM_TOKEN }
107
+ });
108
+
109
+ console.log(chalk.green('\n✅ 发布成功!'));
110
+ return { success: true };
111
+
112
+ } catch (error) {
113
+ console.log(chalk.red(`\n❌ 发布失败: ${error.message}`));
114
+ return { success: false, error: error.message };
115
+ } finally {
116
+ console.log(chalk.yellow('\n🧹 清理工作目录...'));
117
+ rmSync(workDir, { recursive: true, force: true });
118
+ }
119
+ };
120
+
121
+ // 创建服务器
122
+ const server = http.createServer(async (req, res) => {
123
+ if (req.method !== 'POST' || req.url !== '/webhook/npm-publish') {
124
+ res.statusCode = 404;
125
+ res.end('Not Found');
126
+ return;
127
+ }
128
+
129
+ let body = '';
130
+ req.on('data', chunk => body += chunk);
131
+ req.on('end', async () => {
132
+ try {
133
+ // 验证签名
134
+ const signature = req.headers['x-hub-signature-256'] || '';
135
+ if (!verifySignature(body, signature)) {
136
+ res.statusCode = 401;
137
+ res.end(JSON.stringify({ error: 'Invalid signature' }));
138
+ return;
139
+ }
140
+
141
+ // 解析数据
142
+ const data = JSON.parse(body);
143
+ const ref = data.ref || '';
144
+
145
+ // 只处理标签推送
146
+ if (!ref.startsWith('refs/tags/')) {
147
+ res.statusCode = 200;
148
+ res.end(JSON.stringify({ message: 'Not a tag push' }));
149
+ return;
150
+ }
151
+
152
+ const tag = ref.replace('refs/tags/', '');
153
+ const repoUrl = data.repository?.git_http_url ||
154
+ data.repository?.url ||
155
+ data.repository?.clone_url;
156
+
157
+ if (!repoUrl) {
158
+ res.statusCode = 400;
159
+ res.end(JSON.stringify({ error: 'Missing repository URL' }));
160
+ return;
161
+ }
162
+
163
+ console.log(chalk.cyan(`\n📥 收到标签推送: ${tag}`));
164
+ logger.info(`仓库: ${repoUrl}`);
165
+
166
+ // 立即响应
167
+ res.statusCode = 202;
168
+ res.end(JSON.stringify({ status: 'publishing', tag, repo: repoUrl }));
169
+
170
+ // 异步处理
171
+ await handlePublish(tag, repoUrl);
172
+
173
+ } catch (error) {
174
+ logger.error(`Webhook error: ${error.message}`);
175
+ res.statusCode = 500;
176
+ res.end(JSON.stringify({ error: error.message }));
177
+ }
178
+ });
179
+ });
180
+
181
+ server.listen(PORT, () => {
182
+ console.log(chalk.green('\n✅ Webhook 服务器已启动'));
183
+ console.log(chalk.cyan(` http://localhost:${PORT}/webhook/npm-publish`));
184
+ console.log(chalk.gray('\n按 Ctrl+C 停止'));
185
+ });
186
+ });
187
+
188
+ // 测试 webhook
189
+ webhookCmd
190
+ .command('test')
191
+ .description('Test webhook endpoint')
192
+ .option('-p, --port <port>', 'Server port', '3000')
193
+ .option('-t, --tag <tag>', 'Test tag', 'v1.0.0')
194
+ .option('-r, --repo <repo>', 'Test repo URL')
195
+ .action((options) => {
196
+ const PORT = options.port || '3000';
197
+ const tag = options.tag || 'v1.0.0';
198
+ const repo = options.repo || 'https://github.com/test/repo.git';
199
+
200
+ const testData = JSON.stringify({
201
+ ref: `refs/tags/${tag}`,
202
+ repository: {
203
+ git_http_url: repo,
204
+ url: repo,
205
+ clone_url: repo
206
+ }
207
+ });
208
+
209
+ console.log(chalk.cyan('\n🧪 测试 Webhook\n'));
210
+ console.log(`POST http://localhost:${PORT}/webhook/npm-publish`);
211
+ console.log('Body:', testData);
212
+
213
+ const req = http.request({
214
+ hostname: 'localhost',
215
+ port: PORT,
216
+ path: '/webhook/npm-publish',
217
+ method: 'POST',
218
+ headers: {
219
+ 'Content-Type': 'application/json',
220
+ 'Content-Length': Buffer.byteLength(testData)
221
+ }
222
+ }, (res) => {
223
+ let data = '';
224
+ res.on('data', chunk => data += chunk);
225
+ res.on('end', () => {
226
+ console.log(chalk.green(`\n响应: ${res.statusCode}`));
227
+ console.log(data);
228
+ });
229
+ });
230
+
231
+ req.on('error', (err) => {
232
+ console.log(chalk.red(`\n连接失败: ${err.message}`));
233
+ console.log(chalk.yellow('请确保 webhook 服务器正在运行:'));
234
+ console.log(' pqm webhook start');
235
+ });
236
+
237
+ req.write(testData);
238
+ req.end();
239
+ });
240
+ }
@@ -0,0 +1,82 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ export function detectProjectConfig(cwd = process.cwd()) {
5
+ const result = {
6
+ type: 'unknown',
7
+ buildTool: null,
8
+ srcDir: null,
9
+ distDir: null,
10
+ hasPackageJson: false
11
+ };
12
+
13
+ // Check for package.json
14
+ const pkgPath = path.join(cwd, 'package.json');
15
+ if (fs.existsSync(pkgPath)) {
16
+ result.hasPackageJson = true;
17
+ try {
18
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
19
+
20
+ // Get source and dist directories from package.json
21
+ if (pkg.main) result.distDir = pkg.main.startsWith('dist') ? 'dist' : null;
22
+ if (pkg.source) result.srcDir = path.dirname(pkg.source);
23
+ if (pkg.exports) {
24
+ // Check for dist in exports
25
+ }
26
+
27
+ // Detect build tool from dependencies
28
+ if (pkg.devDependencies?.vite || pkg.dependencies?.vite) {
29
+ result.buildTool = 'vite';
30
+ } else if (pkg.devDependencies?.rollup || pkg.dependencies?.rollup) {
31
+ result.buildTool = 'rollup';
32
+ }
33
+ } catch {
34
+ // Ignore parse errors
35
+ }
36
+ }
37
+
38
+ // Check for config files
39
+ if (fs.existsSync(path.join(cwd, 'vite.config.js')) ||
40
+ fs.existsSync(path.join(cwd, 'vite.config.ts'))) {
41
+ result.buildTool = 'vite';
42
+ result.type = 'vite';
43
+ } else if (fs.existsSync(path.join(cwd, 'rollup.config.js')) ||
44
+ fs.existsSync(path.join(cwd, 'rollup.config.mjs'))) {
45
+ result.buildTool = 'rollup';
46
+ result.type = 'rollup';
47
+ }
48
+
49
+ // Detect source directory
50
+ if (!result.srcDir) {
51
+ const possibleDirs = ['src', 'source', 'lib', 'packages'];
52
+ for (const dir of possibleDirs) {
53
+ if (fs.existsSync(path.join(cwd, dir))) {
54
+ result.srcDir = dir;
55
+ break;
56
+ }
57
+ }
58
+ }
59
+
60
+ // Detect TypeScript
61
+ if (fs.existsSync(path.join(cwd, 'tsconfig.json'))) {
62
+ result.type = result.type === 'unknown' ? 'typescript' : result.type;
63
+ }
64
+
65
+ return result;
66
+ }
67
+
68
+ export function autoDetectConfig() {
69
+ const detected = detectProjectConfig();
70
+
71
+ return {
72
+ root: detected.srcDir || './src',
73
+ exclude: ['node_modules', 'dist', '.git', '**/*.test.js', '**/*.spec.js'],
74
+ buildTool: detected.buildTool || 'auto',
75
+ buildMode: 'incremental',
76
+ buildOnStart: true,
77
+ webhook: {
78
+ enabled: false,
79
+ port: 3200
80
+ }
81
+ };
82
+ }