ai-unit-test-generator 1.4.5 → 2.0.1

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/bin/cli.js CHANGED
@@ -1,239 +1,137 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ /**
4
+ * AI-Unit-Test-Generator CLI
5
+ *
6
+ * Commands:
7
+ * init - Initialize configuration file
8
+ * analyze - AI-powered codebase analysis
9
+ * scan - Scan code and generate priority scoring
10
+ * generate - Generate unit tests
11
+ */
12
+
3
13
  import { program } from 'commander'
4
14
  import { fileURLToPath } from 'url'
5
- import { dirname, join, resolve } from 'path'
6
- import { existsSync, copyFileSync, mkdirSync, readFileSync } from 'fs'
7
- import { spawn } from 'child_process'
15
+ import { dirname, join } from 'path'
16
+ import { readFileSync } from 'fs'
8
17
 
9
18
  const __filename = fileURLToPath(import.meta.url)
10
19
  const __dirname = dirname(__filename)
11
- const PKG_ROOT = resolve(__dirname, '..')
20
+ const PKG_ROOT = join(__dirname, '..')
12
21
 
13
- // 读取 package.json 版本
22
+ // 读取版本号
14
23
  const pkgJson = JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf-8'))
15
24
 
16
25
  program
17
26
  .name('ai-test')
18
27
  .description('AI-powered unit test generator with smart priority scoring')
19
28
  .version(pkgJson.version)
29
+ .addHelpText('after', `
30
+ Quick Start:
31
+ 1. $ ai-test init # Create config file
32
+ 2. $ ai-test analyze # Let AI analyze your codebase (optional)
33
+ 3. $ ai-test scan # Scan & score functions
34
+ 4. $ ai-test generate # Generate tests
35
+
36
+ Examples:
37
+ $ ai-test init
38
+ $ ai-test analyze
39
+ $ ai-test scan --skip-git
40
+ $ ai-test generate -n 20 --all
41
+
42
+ Documentation: https://github.com/YuhengZhou/ai-unit-test-generator
43
+ `)
20
44
 
21
45
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
22
- // 命令 1: scan - 扫描代码并生成优先级报告
46
+ // 命令 1: init - 初始化配置
47
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
48
+ program
49
+ .command('init')
50
+ .description('Initialize ai-test configuration file')
51
+ .option('-c, --config <path>', 'Config file path', 'ai-test.config.jsonc')
52
+ .action(async (options) => {
53
+ const { init } = await import('../lib/workflows/init.mjs')
54
+ await init(options)
55
+ })
56
+
57
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
58
+ // 命令 2: analyze - AI 配置分析
59
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
60
+ program
61
+ .command('analyze')
62
+ .description('AI-powered codebase analysis for scoring optimization')
63
+ .option('-c, --config <path>', 'Config file path')
64
+ .option('-o, --output <dir>', 'Output directory', 'reports')
65
+ .addHelpText('after', `
66
+ How it works:
67
+ 1. Samples representative code from your codebase
68
+ 2. Calls Cursor Agent to analyze business logic
69
+ 3. Generates scoring suggestions (business critical paths, high risk modules)
70
+ 4. Interactive review - you choose which suggestions to apply
71
+ 5. Updates ai-test.config.jsonc with approved suggestions
72
+
73
+ Note: Requires cursor-agent CLI to be installed
74
+ `)
75
+ .action(async (options) => {
76
+ const { analyze } = await import('../lib/workflows/analyze.mjs')
77
+ await analyze(options)
78
+ })
79
+
80
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
81
+ // 命令 3: scan - 扫描代码并打分
23
82
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
24
83
  program
25
84
  .command('scan')
26
85
  .description('Scan code and generate priority scoring report')
27
- .option('-c, --config <path>', 'Config file path', 'ai-test.config.jsonc')
86
+ .option('-c, --config <path>', 'Config file path')
28
87
  .option('-o, --output <dir>', 'Output directory', 'reports')
29
88
  .option('--skip-git', 'Skip Git signals generation')
89
+ .addHelpText('after', `
90
+ Generates:
91
+ - reports/targets.json (AST + complexity data)
92
+ - reports/git_signals.json (Git history signals)
93
+ - reports/ut_scores.md (Human-readable report)
94
+ - reports/ut_scores.csv (Machine-readable scores)
95
+
96
+ Scoring modes:
97
+ - Layered: Different weights for each layer (foundation, business, state, UI)
98
+ - Legacy: Unified weights across all code
99
+
100
+ AI enhancement:
101
+ - If you ran 'ai-test analyze', scores will automatically use AI suggestions
102
+ - AI-identified business critical paths get higher priority
103
+ `)
30
104
  .action(async (options) => {
31
- let { config, output, skipGit } = options
32
-
33
- // 自动探测现有配置 & 初始化(不存在则创建)
34
- const detectOrder = [config, 'ai-test.config.jsonc', 'ai-test.config.json']
35
- const detected = detectOrder.find(p => existsSync(p))
36
- if (detected) config = detected
37
- if (!existsSync(config)) {
38
- console.log('⚙️ Config not found, creating default config...')
39
- const templatePath = join(PKG_ROOT, 'templates', 'default.config.jsonc')
40
- copyFileSync(templatePath, config)
41
- console.log(`✅ Config created: ${config}\n`)
42
- }
43
-
44
- // 创建输出目录
45
- if (!existsSync(output)) {
46
- mkdirSync(output, { recursive: true })
47
- }
48
-
49
- // 可选:在扫描前自动运行覆盖率(由配置控制)
50
- try {
51
- const cfgText = existsSync(config) ? readFileSync(config, 'utf-8') : '{}'
52
- const cfg = JSON.parse(cfgText)
53
- const covCfg = cfg?.coverage || { runBeforeScan: false }
54
- if (covCfg.runBeforeScan) {
55
- console.log('🧪 Running coverage before scan...')
56
- await new Promise((resolve, reject) => {
57
- const cmd = covCfg.command || 'npx jest --coverage --silent'
58
- const child = spawn(cmd, { stdio: 'inherit', shell: true, cwd: process.cwd() })
59
- child.on('close', code => code === 0 ? resolve(0) : reject(new Error(`coverage exited ${code}`)))
60
- child.on('error', reject)
61
- })
62
- }
63
- } catch (err) {
64
- console.warn('⚠️ Coverage step failed or Jest not installed. Skipping coverage and continuing scan.')
65
- console.warn(' - npm i -D jest@29 ts-jest@29 @types/jest@29 jest-environment-jsdom@29 --legacy-peer-deps')
66
- }
67
-
68
- console.log('🚀 Starting code scan...\n')
69
-
70
- try {
71
- // Step 1: 生成目标列表
72
- console.log('📋 Step 1: Scanning targets...')
73
- await runScript('core/scanner.mjs', [
74
- '--config', config,
75
- '--out', join(output, 'targets.json')
76
- ])
77
-
78
- // Step 2: 生成 Git 信号 (可选)
79
- if (!skipGit) {
80
- console.log('\n📊 Step 2: Analyzing Git history...')
81
- await runScript('core/git-analyzer.mjs', [
82
- '--targets', join(output, 'targets.json'),
83
- '--out', join(output, 'git_signals.json')
84
- ])
85
- }
86
-
87
- // Step 3: 运行评分(保留现有状态)
88
- console.log('\n🎯 Step 3: Scoring targets...')
89
- const scoreArgs = [
90
- '--targets', join(output, 'targets.json'),
91
- '--config', config,
92
- '--out-md', join(output, 'ut_scores.md'),
93
- '--out-csv', join(output, 'ut_scores.csv')
94
- ]
95
- if (!skipGit && existsSync(join(output, 'git_signals.json'))) {
96
- scoreArgs.push('--git', join(output, 'git_signals.json'))
97
- }
98
- await runScript('core/scorer.mjs', scoreArgs)
99
-
100
- // 统计 TODO/DONE
101
- const reportPath = join(output, 'ut_scores.md')
102
- if (existsSync(reportPath)) {
103
- const content = readFileSync(reportPath, 'utf-8')
104
- const todoCount = (content.match(/\| TODO \|/g) || []).length
105
- const doneCount = (content.match(/\| DONE \|/g) || []).length
106
-
107
- console.log('\n✅ Scan completed!')
108
- console.log(`\n📊 Status:`)
109
- console.log(` TODO: ${todoCount}`)
110
- console.log(` DONE: ${doneCount}`)
111
- console.log(` Total: ${todoCount + doneCount}`)
112
- console.log(`\n📄 Report: ${reportPath}`)
113
- console.log(`\n💡 Next: ai-test generate`)
114
- }
115
- } catch (err) {
116
- console.error('❌ Scan failed:', err.message)
117
- process.exit(1)
118
- }
105
+ const { scan } = await import('../lib/workflows/scan.mjs')
106
+ await scan(options)
119
107
  })
120
108
 
121
109
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
122
- // 命令 2: generate - 生成测试
110
+ // 命令 4: generate - 生成测试
123
111
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
124
112
  program
125
113
  .command('generate')
126
- .description('Generate tests (default: 10 TODO functions)')
114
+ .description('Generate unit tests for untested functions')
127
115
  .option('-n, --count <number>', 'Number of functions to generate', parseInt, 10)
128
116
  .option('-p, --priority <level>', 'Priority filter (P0, P1, P2, P3)', 'P0')
129
117
  .option('--all', 'Generate all remaining TODO functions')
130
118
  .option('--report <path>', 'Report file path', 'reports/ut_scores.md')
119
+ .addHelpText('after', `
120
+ Examples:
121
+ $ ai-test generate # Generate 10 P0 functions
122
+ $ ai-test generate -n 20 # Generate 20 P0 functions
123
+ $ ai-test generate -p P1 -n 15 # Generate 15 P1 functions
124
+ $ ai-test generate --all # Generate all P0 TODO functions
125
+
126
+ Features:
127
+ - Automatic test generation using Cursor Agent
128
+ - Jest integration with coverage tracking
129
+ - Failure retry with hints
130
+ - Auto-marking DONE on success
131
+ `)
131
132
  .action(async (options) => {
132
- const { count, priority, all, report } = options
133
-
134
- // 检查报告是否存在
135
- if (!existsSync(report)) {
136
- console.error(`❌ Report not found: ${report}`)
137
- console.log(` Run: ai-test scan`)
138
- process.exit(1)
139
- }
140
-
141
- if (all) {
142
- // 持续生成直到所有 TODO 完成
143
- console.log(`🚀 Generating all ${priority} TODO functions...\n`)
144
-
145
- let batchNum = 1
146
- let totalGenerated = 0
147
- let totalPassed = 0
148
-
149
- while (true) {
150
- // 检查还有多少 TODO
151
- const content = readFileSync(report, 'utf-8')
152
- const lines = content.split('\n')
153
- const todoLines = lines.filter(line =>
154
- line.includes('| TODO |') && line.includes(`| ${priority} |`)
155
- )
156
-
157
- if (todoLines.length === 0) {
158
- console.log(`\n✅ All ${priority} functions completed!`)
159
- console.log(` Total generated: ${totalGenerated}`)
160
- console.log(` Total passed: ${totalPassed}`)
161
- break
162
- }
163
-
164
- console.log(`\n━━━━ Batch ${batchNum} (${todoLines.length} TODO remaining) ━━━━`)
165
-
166
- try {
167
- const result = await generateBatch(priority, Math.min(count, todoLines.length), 0, report)
168
- totalGenerated += result.generated
169
- totalPassed += result.passed
170
- batchNum++
171
- } catch (err) {
172
- console.error(`❌ Batch ${batchNum} failed:`, err.message)
173
- break
174
- }
175
- }
176
- } else {
177
- // 生成指定数量
178
- console.log(`🚀 Generating ${count} ${priority} functions...\n`)
179
- try {
180
- await generateBatch(priority, count, 0, report)
181
- } catch (err) {
182
- console.error('❌ Generation failed:', err.message)
183
- process.exit(1)
184
- }
185
- }
186
- })
187
-
188
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
189
- // 辅助函数
190
- // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
191
-
192
- /**
193
- * 生成单批测试
194
- */
195
- async function generateBatch(priority, count, skip, report) {
196
- const batchScript = join(PKG_ROOT, 'lib/workflows/batch.mjs')
197
-
198
- return new Promise((resolve, reject) => {
199
- const child = spawn('node', [batchScript, priority, String(count), String(skip), report], {
200
- stdio: 'inherit',
201
- cwd: process.cwd()
202
- })
203
-
204
- child.on('close', (code) => {
205
- if (code === 0) {
206
- resolve({ generated: count, passed: count }) // TODO: 从 batch 输出获取实际数字
207
- } else {
208
- reject(new Error(`Batch generation failed with code ${code}`))
209
- }
210
- })
211
-
212
- child.on('error', reject)
213
- })
214
- }
215
-
216
- /**
217
- * 运行脚本
218
- */
219
- function runScript(scriptPath, args) {
220
- return new Promise((resolve, reject) => {
221
- const fullPath = join(PKG_ROOT, 'lib', scriptPath)
222
- const child = spawn('node', [fullPath, ...args], {
223
- stdio: 'inherit',
224
- cwd: process.cwd()
225
- })
226
-
227
- child.on('close', (code) => {
228
- if (code === 0) {
229
- resolve()
230
- } else {
231
- reject(new Error(`Script ${scriptPath} exited with code ${code}`))
232
- }
233
- })
234
-
235
- child.on('error', reject)
133
+ const { generate } = await import('../lib/workflows/generate.mjs')
134
+ await generate(options)
236
135
  })
237
- }
238
136
 
239
137
  program.parse()
@@ -0,0 +1,120 @@
1
+ /**
2
+ * AI 分析 Prompt 构建器
3
+ */
4
+
5
+ /**
6
+ * 构建分析 Prompt
7
+ */
8
+ export function buildAnalysisPrompt(samples, stats, projectCtx) {
9
+ return `You are analyzing a ${projectCtx.framework} codebase to identify business-critical paths and high-risk modules.
10
+
11
+ ## 📊 Project Overview
12
+
13
+ - **Framework**: ${projectCtx.framework}
14
+ - **Total Files**: ${stats.totalFiles}
15
+ - **Total Lines**: ${stats.totalLines}
16
+ - **Key Dependencies**: ${projectCtx.criticalDeps.join(', ') || 'None detected'}
17
+
18
+ ## 📂 Code Samples (${samples.length} files)
19
+
20
+ ${samples.map((s, i) => `
21
+ ### Sample ${i + 1}: ${s.path}
22
+ **Layer**: ${s.layer}
23
+ **Reason**: ${s.reason}
24
+
25
+ \`\`\`typescript
26
+ ${s.preview}
27
+ \`\`\`
28
+ `).join('\n')}
29
+
30
+ ## 🎯 Your Task
31
+
32
+ **YOU HAVE ACCESS TO THE FULL CODEBASE** via Cursor's indexing. The samples above are just for quick reference.
33
+
34
+ Please analyze the ENTIRE codebase (not just the samples) and suggest:
35
+
36
+ 1. **businessCriticalPaths**: Which paths handle core business logic?
37
+ - Look for: payment, booking, pricing, checkout, order processing
38
+ - Use your codebase index to find all relevant files
39
+
40
+ 2. **highRiskModules**: Which modules have high error risk?
41
+ - Look for: date/time handling, external APIs, money calculations, parsing
42
+ - Check for complex logic, many try-catch blocks
43
+
44
+ 3. **testabilityAdjustments**: Which paths are easier/harder to test?
45
+ - Look for: pure functions (easier), heavy dependencies (harder)
46
+ - Consider side effects, I/O operations
47
+
48
+ ## 💡 Use Your Codebase Knowledge
49
+
50
+ - You can search the codebase using @codebase
51
+ - You know the full dependency graph
52
+ - You understand the business logic from code context
53
+
54
+ ## OUTPUT SCHEMA
55
+
56
+ \`\`\`json
57
+ {
58
+ "suggestions": {
59
+ "businessCriticalPaths": [
60
+ {
61
+ "pattern": "services/payment/**",
62
+ "confidence": 0.95,
63
+ "reason": "Handles Stripe payment processing",
64
+ "suggestedBC": 10,
65
+ "evidence": [
66
+ "Contains processPayment function with Stripe API calls",
67
+ "Referenced by checkout flow in multiple places",
68
+ "Handles money transactions"
69
+ ]
70
+ }
71
+ ],
72
+ "highRiskModules": [
73
+ {
74
+ "pattern": "utils/date/**",
75
+ "confidence": 0.88,
76
+ "reason": "Complex timezone and date calculations",
77
+ "suggestedER": 8,
78
+ "evidence": [
79
+ "Multiple timezone conversion functions",
80
+ "Handles daylight saving time"
81
+ ]
82
+ }
83
+ ],
84
+ "testabilityAdjustments": [
85
+ {
86
+ "pattern": "utils/**",
87
+ "confidence": 0.92,
88
+ "reason": "Pure utility functions with no side effects",
89
+ "adjustment": "+1",
90
+ "evidence": [
91
+ "All exports are pure functions",
92
+ "No external dependencies observed"
93
+ ]
94
+ }
95
+ ]
96
+ }
97
+ }
98
+ \`\`\`
99
+
100
+ ## CRITICAL RULES
101
+
102
+ 1. **Output ONLY JSON** - No explanations, no markdown wrapper
103
+ 2. **Match Schema Exactly** - Any deviation will be rejected
104
+ 3. **Stay Within Bounds** - All scores must be within specified ranges
105
+ 4. **Require Evidence** - Each suggestion needs 2-3 concrete evidence points
106
+ 5. **No Assumptions** - Only suggest what you can directly observe
107
+
108
+ ## CONSTRAINTS
109
+
110
+ - confidence ≥ 0.70 (businessCriticalPaths ≥ 0.85)
111
+ - 2-3 evidence items per suggestion
112
+ - Pattern must match actual paths in codebase
113
+ - Max 10 suggestions per category
114
+ - suggestedBC: 8 | 9 | 10
115
+ - suggestedER: 7 | 8 | 9 | 10
116
+ - adjustment: "-2" | "-1" | "+1" | "+2"
117
+
118
+ Output ONLY the JSON, no explanation.`
119
+ }
120
+
@@ -0,0 +1,99 @@
1
+ /**
2
+ * 安全的配置写入器
3
+ */
4
+
5
+ import { readConfig, writeConfig } from '../utils/config-manager.mjs'
6
+
7
+ const LOCKED_PATHS = [
8
+ 'scoringMode',
9
+ 'layers',
10
+ 'weights',
11
+ 'thresholds',
12
+ 'ccMapping',
13
+ 'dependencyCountMapping',
14
+ 'coverageScoring',
15
+ 'fallbacks',
16
+ 'aiEnhancement.enabled'
17
+ ]
18
+
19
+ const WRITABLE_PATHS = [
20
+ 'aiEnhancement.analyzed',
21
+ 'aiEnhancement.analyzedAt',
22
+ 'aiEnhancement.suggestions'
23
+ ]
24
+
25
+ /**
26
+ * 应用 AI 建议到配置文件
27
+ */
28
+ export async function applyAISuggestions(configPath, suggestions) {
29
+ // 1. 读取现有配置
30
+ const config = readConfig(configPath)
31
+
32
+ // 2. 验证权限
33
+ validateWritePermissions(suggestions)
34
+
35
+ // 3. 深拷贝配置
36
+ const newConfig = JSON.parse(JSON.stringify(config))
37
+
38
+ // 4. 初始化 aiEnhancement(如果不存在)
39
+ if (!newConfig.aiEnhancement) {
40
+ newConfig.aiEnhancement = {
41
+ enabled: true,
42
+ analyzed: false,
43
+ analyzedAt: null,
44
+ suggestions: {
45
+ businessCriticalPaths: [],
46
+ highRiskModules: [],
47
+ testabilityAdjustments: []
48
+ }
49
+ }
50
+ }
51
+
52
+ // 5. 只写入允许的字段
53
+ newConfig.aiEnhancement.analyzed = true
54
+ newConfig.aiEnhancement.analyzedAt = new Date().toISOString()
55
+ newConfig.aiEnhancement.suggestions = suggestions
56
+
57
+ // 6. 验证核心配置未被修改
58
+ validateCoreConfigIntact(config, newConfig)
59
+
60
+ // 7. 写入文件
61
+ writeConfig(configPath, newConfig)
62
+
63
+ return newConfig
64
+ }
65
+
66
+ /**
67
+ * 验证写入权限
68
+ */
69
+ function validateWritePermissions(suggestions) {
70
+ const allowedKeys = ['businessCriticalPaths', 'highRiskModules', 'testabilityAdjustments']
71
+
72
+ for (const key of Object.keys(suggestions)) {
73
+ if (!allowedKeys.includes(key)) {
74
+ throw new Error(`AI attempted to write forbidden field: ${key}`)
75
+ }
76
+ }
77
+ }
78
+
79
+ /**
80
+ * 验证核心配置完整性
81
+ */
82
+ function validateCoreConfigIntact(oldConfig, newConfig) {
83
+ for (const path of LOCKED_PATHS) {
84
+ const oldValue = getNestedValue(oldConfig, path)
85
+ const newValue = getNestedValue(newConfig, path)
86
+
87
+ if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) {
88
+ throw new Error(`Core config was modified (locked path: ${path})`)
89
+ }
90
+ }
91
+ }
92
+
93
+ /**
94
+ * 获取嵌套对象的值
95
+ */
96
+ function getNestedValue(obj, path) {
97
+ return path.split('.').reduce((curr, key) => curr?.[key], obj)
98
+ }
99
+
@@ -0,0 +1,52 @@
1
+ /**
2
+ * 项目上下文构建工具
3
+ */
4
+
5
+ import { existsSync, readFileSync } from 'node:fs'
6
+
7
+ /**
8
+ * 构建项目上下文信息
9
+ */
10
+ export async function buildProjectContext() {
11
+ const context = {
12
+ framework: 'Unknown',
13
+ criticalDeps: [],
14
+ devDeps: []
15
+ }
16
+
17
+ // 读取 package.json
18
+ if (existsSync('package.json')) {
19
+ try {
20
+ const pkg = JSON.parse(readFileSync('package.json', 'utf-8'))
21
+
22
+ context.name = pkg.name
23
+ context.framework = detectFramework(pkg.dependencies || {})
24
+
25
+ // 识别关键依赖
26
+ const deps = Object.keys(pkg.dependencies || {})
27
+ const criticalKeywords = ['stripe', 'payment', 'auth', 'prisma', 'db', 'axios', 'fetch', 'jotai', 'zustand', 'redux']
28
+
29
+ context.criticalDeps = deps.filter(dep =>
30
+ criticalKeywords.some(kw => dep.toLowerCase().includes(kw))
31
+ )
32
+
33
+ context.devDeps = Object.keys(pkg.devDependencies || {})
34
+ } catch (err) {
35
+ console.warn('Warning: Could not read package.json')
36
+ }
37
+ }
38
+
39
+ return context
40
+ }
41
+
42
+ /**
43
+ * 检测项目框架
44
+ */
45
+ function detectFramework(deps) {
46
+ if (deps['next']) return 'Next.js'
47
+ if (deps['react']) return 'React'
48
+ if (deps['vue']) return 'Vue'
49
+ if (deps['@angular/core']) return 'Angular'
50
+ return 'Node.js'
51
+ }
52
+
package/lib/ai/index.mjs CHANGED
@@ -3,9 +3,20 @@
3
3
  *
4
4
  * 提供 Prompt 构建、AI 调用和测试提取功能
5
5
  * 支持多种 LLM(目前实现:cursor-agent)
6
+ *
7
+ * AI 分析功能:代码库分析、配置优化建议
6
8
  */
7
9
 
10
+ // 测试生成相关
8
11
  export { buildBatchPrompt, runCLI as buildPrompt } from './prompt-builder.mjs'
9
12
  export { main as callAI } from './client.mjs'
10
13
  export { extractTests } from './extractor.mjs'
11
14
 
15
+ // AI 分析相关
16
+ export * from './sampler.mjs'
17
+ export * from './context-builder.mjs'
18
+ export * from './analyzer-prompt.mjs'
19
+ export * from './validator.mjs'
20
+ export * from './reviewer.mjs'
21
+ export * from './config-writer.mjs'
22
+