ai-unit-test-generator 2.0.4 โ†’ 2.0.6

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/CHANGELOG.md CHANGED
@@ -5,6 +5,66 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.0.6] - 2025-01-11
9
+
10
+ ### ๐Ÿ› Hotfix
11
+ - **Fixed**: `ai-test generate` command - corrected `client.mjs` argument parsing
12
+ - Now supports `--prompt`, `--prompt-file`, and `--promptFile` for compatibility
13
+ - Resolves `Prompt file not found: prompt.txt` error in batch generation workflow
14
+
15
+ ---
16
+
17
+ ## [2.0.5] - 2025-01-11
18
+
19
+ ### โœจ Feature: Simplified AI Suggestion Review
20
+
21
+ **Problem**: Previous review UX was too complex, requiring category-by-category selection.
22
+
23
+ **Solution**: Completely redesigned `lib/ai/reviewer.mjs` with:
24
+ - โœ… **One-click Accept All** (`a`) - instantly accept all AI suggestions
25
+ - โœ… **One-click Reject All** (`r`) - instantly reject all suggestions
26
+ - โœ… **Partial Accept** (input numbers like `1,3,5`) - granular control
27
+ - โœ… **Compact Display** - all suggestions shown at once with global indexing
28
+ - โœ… **Clear Summary** - final acceptance count before applying changes
29
+
30
+ **User Experience**:
31
+ ```bash
32
+ ai-test analyze
33
+
34
+ # AI ๅˆ†ๆžๅฎŒๆˆๅŽ็ซ‹ๅณๅฑ•็คบๆ‰€ๆœ‰ๅปบ่ฎฎ๏ผš
35
+ ๐Ÿค– AI Analysis Results: 12 suggestions
36
+
37
+ ๐Ÿ”ด Business Critical Paths (5):
38
+ [1] services/payment/** | BC=10 | Conf: 95%
39
+ โ†’ Handles Stripe payment processing
40
+ [2] ...
41
+
42
+ โš ๏ธ High Risk Modules (4):
43
+ [6] utils/date/** | ER=8 | Conf: 88%
44
+ โ†’ Complex timezone calculations
45
+ [7] ...
46
+
47
+ โœ… Testability Adjustments (3):
48
+ [10] utils/** | Adj=+1 | Conf: 92%
49
+ โ†’ Pure functions, easy to test
50
+
51
+ โ“ Choose action:
52
+ [a] Accept all 12 suggestions
53
+ [r] Reject all
54
+ Or input numbers (comma-separated, e.g. 1,3,5-8)
55
+
56
+ > a # ๆˆ– r๏ผŒๆˆ– 1,3,5
57
+ ```
58
+
59
+ **Changes**:
60
+ - Removed multi-stage category review loop
61
+ - Added global indexing across all categories
62
+ - Simplified user input parsing (a/r/numbers only)
63
+ - Removed per-category score adjustment (can be done manually after if needed)
64
+ - Single confirmation step at the end
65
+
66
+ ---
67
+
8
68
  ## [2.0.4] - 2025-01-11
9
69
 
10
70
  ### ๐Ÿ› Hotfix
package/lib/ai/client.mjs CHANGED
@@ -60,7 +60,8 @@ export async function runOnce({ prompt, promptFile, out = 'reports/ai_response.t
60
60
 
61
61
  export async function runCLI(argv = process.argv) {
62
62
  const args = parseArgs(argv)
63
- const promptFile = args['prompt'] || args['prompt-file'] || null
63
+ // ๆ”ฏๆŒ --prompt, --prompt-file, --promptFile ไธ‰็งๅฝขๅผ
64
+ const promptFile = args['prompt'] || args['prompt-file'] || args['promptFile'] || null
64
65
  const out = args['out'] || 'reports/ai_response.txt'
65
66
  const model = args['model'] || null
66
67
  const timeoutSec = args['timeout'] ? Number(args['timeout']) : 600
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * ไบคไบ’ๅผ AI ๅปบ่ฎฎๅฎกๆ ธๅ™จ
3
- * ๆ”ฏๆŒๅˆ†็ฑปๅฎกๆ ธใ€้€ๆก้€‰ๆ‹ฉใ€ๆ‰น้‡่ฐƒๆ•ดๅˆ†ๆ•ฐ
3
+ * ๆ”ฏๆŒ๏ผšไธ€้”ฎๅ…จๆŽฅๅ—ใ€ไธ€้”ฎๅ…จๆ‹’็ปใ€้ƒจๅˆ†ๆŽฅๅ—๏ผˆ่พ“ๆ•ฐๅญ—๏ผ‰
4
4
  */
5
5
 
6
6
  import readline from 'readline'
@@ -47,234 +47,200 @@ function getCategoryName(category) {
47
47
  }
48
48
 
49
49
  /**
50
- * ่Žทๅ–ๅˆ†็ฑปๅ็งฐ๏ผˆๅฐๅ†™๏ผ‰
51
- */
52
- function getCategoryNameLower(category) {
53
- const names = {
54
- businessCriticalPaths: 'business critical paths',
55
- highRiskModules: 'high risk modules',
56
- testabilityAdjustments: 'testability adjustments'
57
- }
58
- return names[category] || category
59
- }
60
-
61
- /**
62
- * ๆ ผๅผๅŒ–ๅ•ไธชๅปบ่ฎฎ
50
+ * ๆ ผๅผๅŒ–ๅ•ไธชๅปบ่ฎฎ๏ผˆ็ดงๅ‡‘ๆ ผๅผ๏ผ‰
63
51
  */
64
52
  function formatSuggestion(item, index, category) {
65
- let output = `\n [${index}] ${item.pattern}\n`
53
+ let scoreInfo = ''
66
54
 
67
55
  if (category === 'businessCriticalPaths') {
68
- output += ` BC: ${item.suggestedBC} | Confidence: ${(item.confidence * 100).toFixed(0)}%\n`
56
+ scoreInfo = `BC=${item.suggestedBC}`
69
57
  } else if (category === 'highRiskModules') {
70
- output += ` ER: ${item.suggestedER} | Confidence: ${(item.confidence * 100).toFixed(0)}%\n`
58
+ scoreInfo = `ER=${item.suggestedER}`
71
59
  } else if (category === 'testabilityAdjustments') {
72
- output += ` Adjustment: ${item.adjustment} | Confidence: ${(item.confidence * 100).toFixed(0)}%\n`
60
+ scoreInfo = `Adj=${item.adjustment}`
73
61
  }
74
62
 
75
- output += ` Reason: ${item.reason}\n`
76
- output += ` Evidence:\n`
77
- item.evidence.forEach(e => {
78
- output += ` - ${e}\n`
79
- })
80
-
81
- return output
82
- }
83
-
84
- /**
85
- * ๆ˜พ็คบๅปบ่ฎฎๅˆ—่กจ
86
- */
87
- function displaySuggestions(category, items) {
88
- const icon = getCategoryIcon(category)
89
- const name = getCategoryName(category)
90
-
91
- console.log(`\nโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n`)
92
- console.log(`${icon} ${name} (${items.length} suggestions):`)
93
-
94
- items.forEach((item, i) => {
95
- console.log(formatSuggestion(item, i + 1, category))
96
- })
97
-
98
- console.log(`\nโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n`)
99
- }
100
-
101
- /**
102
- * ่งฃๆž็”จๆˆท้€‰ๆ‹ฉ
103
- */
104
- function parseSelection(input, maxCount) {
105
- if (!input || input.trim() === '') {
106
- return []
107
- }
63
+ const confidence = `${(item.confidence * 100).toFixed(0)}%`
108
64
 
109
- return input.split(',')
110
- .map(s => parseInt(s.trim()))
111
- .filter(n => !isNaN(n) && n >= 1 && n <= maxCount)
65
+ return ` [${index}] ${item.pattern} | ${scoreInfo} | Conf: ${confidence}\n โ†’ ${item.reason}`
112
66
  }
113
67
 
114
68
  /**
115
- * ๆ‰น้‡่ฐƒๆ•ดๅˆ†ๆ•ฐ
69
+ * ๆ˜พ็คบๆ‰€ๆœ‰ๅปบ่ฎฎ๏ผˆ็ดงๅ‡‘่ง†ๅ›พ๏ผ‰
116
70
  */
117
- async function adjustScores(rl, items, category) {
118
- console.log(`\nAdjust scores (press Enter to keep original):`)
71
+ function displayAllSuggestions(validated) {
72
+ const categories = ['businessCriticalPaths', 'highRiskModules', 'testabilityAdjustments']
73
+ const totalSuggestions = Object.values(validated).reduce((sum, arr) => sum + arr.length, 0)
119
74
 
120
- const scoreField = category === 'businessCriticalPaths' ? 'suggestedBC' : 'suggestedER'
121
- const minScore = category === 'businessCriticalPaths' ? 8 : 7
122
- const maxScore = 10
75
+ console.log(`\nโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”`)
76
+ console.log(`\n๐Ÿค– AI Analysis Results: ${totalSuggestions} suggestions\n`)
123
77
 
124
- const adjusted = []
78
+ let globalIndex = 1
79
+ const indexMapping = [] // [{ globalIndex, category, localIndex }, ...]
125
80
 
126
- for (let i = 0; i < items.length; i++) {
127
- const item = { ...items[i] }
128
- const currentScore = item[scoreField]
81
+ for (const category of categories) {
82
+ const items = validated[category] || []
83
+ if (items.length === 0) continue
129
84
 
130
- const input = await ask(rl, ` [${i + 1}] ${item.pattern} (${scoreField}: ${currentScore}): `)
85
+ const icon = getCategoryIcon(category)
86
+ const name = getCategoryName(category)
131
87
 
132
- if (input && input.trim() !== '') {
133
- const newScore = parseInt(input.trim())
134
- if (!isNaN(newScore) && newScore >= minScore && newScore <= maxScore) {
135
- item[scoreField] = newScore
136
- console.log(` โœ… Updated: ${currentScore} โ†’ ${newScore}`)
137
- } else {
138
- console.log(` โš ๏ธ Invalid score (must be ${minScore}-${maxScore}), keeping original`)
139
- }
140
- }
88
+ console.log(`\n${icon} ${name} (${items.length}):`)
141
89
 
142
- adjusted.push(item)
90
+ items.forEach((item, localIndex) => {
91
+ console.log(formatSuggestion(item, globalIndex, category))
92
+ indexMapping.push({ globalIndex, category, localIndex })
93
+ globalIndex++
94
+ })
143
95
  }
144
96
 
145
- return adjusted
97
+ console.log(`\nโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n`)
98
+
99
+ return { totalSuggestions, indexMapping }
146
100
  }
147
101
 
148
102
  /**
149
- * ๅฎกๆ ธๅ•ไธชๅˆ†็ฑป
103
+ * ่งฃๆž็”จๆˆท่พ“ๅ…ฅ
150
104
  */
151
- async function reviewCategory(rl, category, items) {
152
- if (items.length === 0) {
153
- return []
154
- }
105
+ function parseUserInput(input, totalCount) {
106
+ const trimmed = input.trim().toLowerCase()
155
107
 
156
- // ๆ˜พ็คบๅปบ่ฎฎ
157
- displaySuggestions(category, items)
108
+ // ๅ…จๆŽฅๅ—
109
+ if (trimmed === 'a' || trimmed === 'all') {
110
+ return { type: 'accept_all' }
111
+ }
158
112
 
159
- // ่ฏข้—ฎๆ“ไฝœ
160
- const categoryName = getCategoryNameLower(category)
161
- const action = await ask(rl,
162
- `โ“ Review ${categoryName}:\n` +
163
- ` [a] Accept all (${items.length})\n` +
164
- ` [r] Reject all\n` +
165
- ` [s] Select individually\n` +
166
- ` [n] Skip (keep empty)\n` +
167
- `> `
168
- )
113
+ // ๅ…จๆ‹’็ป
114
+ if (trimmed === 'r' || trimmed === 'reject') {
115
+ return { type: 'reject_all' }
116
+ }
169
117
 
170
- const actionLower = action.trim().toLowerCase()
118
+ // ้ƒจๅˆ†ๆŽฅๅ—๏ผˆๆ•ฐๅญ—ๅˆ—่กจ๏ผ‰
119
+ const numbers = input.split(',')
120
+ .map(s => parseInt(s.trim()))
121
+ .filter(n => !isNaN(n) && n >= 1 && n <= totalCount)
171
122
 
172
- if (actionLower === 'a') {
173
- // ๆŽฅๅ—ๅ…จ้ƒจ
174
- console.log(`โœ… Accepted: ${items.length}/${items.length} ${categoryName}`)
175
-
176
- // ่ฏข้—ฎๆ˜ฏๅฆ่ฐƒๆ•ดๅˆ†ๆ•ฐ
177
- if (category === 'businessCriticalPaths' || category === 'highRiskModules') {
178
- const adjustInput = await ask(rl, `\nAdjust scores? (y/n) `)
179
- if (adjustInput.trim().toLowerCase() === 'y') {
180
- return await adjustScores(rl, items, category)
181
- }
182
- }
183
-
184
- return items
185
- } else if (actionLower === 's') {
186
- // ้€ๆก้€‰ๆ‹ฉ
187
- const selection = await ask(rl, `\nSelect which suggestions to accept (comma-separated, e.g. 1,3):\n> `)
188
- const indices = parseSelection(selection, items.length)
189
-
190
- if (indices.length === 0) {
191
- console.log(`โŒ No suggestions selected`)
192
- return []
193
- }
194
-
195
- const selected = indices.map(i => items[i - 1])
196
- console.log(`โœ… Selected: ${selected.length}/${items.length} ${categoryName}`)
197
-
198
- // ่ฏข้—ฎๆ˜ฏๅฆ่ฐƒๆ•ดๅˆ†ๆ•ฐ
199
- if (category === 'businessCriticalPaths' || category === 'highRiskModules') {
200
- const adjustInput = await ask(rl, `\nAdjust scores? (y/n) `)
201
- if (adjustInput.trim().toLowerCase() === 'y') {
202
- return await adjustScores(rl, selected, category)
203
- }
204
- }
205
-
206
- return selected
207
- } else if (actionLower === 'r') {
208
- // ๆ‹’็ปๅ…จ้ƒจ
209
- console.log(`โŒ Rejected: all ${categoryName}`)
210
- return []
211
- } else {
212
- // ่ทณ่ฟ‡
213
- console.log(`โญ๏ธ Skipped ${categoryName}`)
214
- return []
123
+ if (numbers.length > 0) {
124
+ return { type: 'partial', indices: numbers }
215
125
  }
126
+
127
+ return { type: 'invalid' }
216
128
  }
217
129
 
218
130
  /**
219
- * ๆ˜พ็คบๆ€ป็ป“
131
+ * ๆ˜พ็คบๆœ€็ปˆๆ€ป็ป“
220
132
  */
221
- function displaySummary(result, validated) {
133
+ function displayFinalSummary(result, validated) {
222
134
  const totalSuggested = Object.values(validated).reduce((sum, arr) => sum + arr.length, 0)
223
135
  const totalAccepted = Object.values(result).reduce((sum, arr) => sum + arr.length, 0)
224
136
 
225
- console.log(`\nโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n`)
226
- console.log(`๐Ÿ“Š Summary:`)
227
- console.log(` Business Critical Paths: ${result.businessCriticalPaths.length}/${validated.businessCriticalPaths?.length || 0} accepted`)
228
- console.log(` High Risk Modules: ${result.highRiskModules.length}/${validated.highRiskModules?.length || 0} accepted`)
229
- console.log(` Testability Adjustments: ${result.testabilityAdjustments.length}/${validated.testabilityAdjustments?.length || 0} accepted`)
230
- console.log(` Total: ${totalAccepted}/${totalSuggested} suggestions accepted`)
137
+ console.log(`\nโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”`)
138
+ console.log(`\n๐Ÿ“Š Final Summary:`)
139
+ console.log(` ๐Ÿ”ด Business Critical Paths: ${result.businessCriticalPaths.length}/${validated.businessCriticalPaths?.length || 0}`)
140
+ console.log(` โš ๏ธ High Risk Modules: ${result.highRiskModules.length}/${validated.highRiskModules?.length || 0}`)
141
+ console.log(` โœ… Testability Adjustments: ${result.testabilityAdjustments.length}/${validated.testabilityAdjustments?.length || 0}`)
142
+ console.log(` Total: ${totalAccepted}/${totalSuggested} accepted\n`)
231
143
 
232
- if (totalAccepted === 0) {
233
- console.log(`\nโš ๏ธ No suggestions accepted. Config will not be modified.`)
144
+ if (totalAccepted > 0) {
145
+ console.log(`๐Ÿ’ก These suggestions will be added to ai-test.config.jsonc`)
146
+ console.log(` and will take effect on next: ai-test scan`)
234
147
  }
148
+
149
+ console.log(`\nโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n`)
235
150
  }
236
151
 
237
152
  /**
238
- * ไบคไบ’ๅผๅฎกๆ ธ
153
+ * ไบคไบ’ๅผๅฎกๆ ธ๏ผˆ็ฎ€ๅŒ–็‰ˆ๏ผ‰
239
154
  * @param {Object} validated - ๅทฒ้ชŒ่ฏ็š„ๅปบ่ฎฎ
240
155
  * @returns {Object|null} - ็”จๆˆทๆ‰นๅ‡†็š„ๅปบ่ฎฎ๏ผŒๆˆ– null๏ผˆๅ–ๆถˆ๏ผ‰
241
156
  */
242
157
  export async function interactiveReview(validated) {
243
158
  const rl = createInterface()
244
159
 
245
- const result = {
246
- businessCriticalPaths: [],
247
- highRiskModules: [],
248
- testabilityAdjustments: []
249
- }
250
-
251
160
  try {
252
- // ๅฎกๆ ธๆฏไธชๅˆ†็ฑป
253
- const categories = ['businessCriticalPaths', 'highRiskModules', 'testabilityAdjustments']
161
+ // 1. ๆ˜พ็คบๆ‰€ๆœ‰ๅปบ่ฎฎ
162
+ const { totalSuggestions, indexMapping } = displayAllSuggestions(validated)
254
163
 
255
- for (const category of categories) {
256
- const items = validated[category] || []
257
- if (items.length === 0) continue
258
-
259
- result[category] = await reviewCategory(rl, category, items)
164
+ if (totalSuggestions === 0) {
165
+ console.log('โš ๏ธ No suggestions to review.')
166
+ rl.close()
167
+ return null
260
168
  }
261
169
 
262
- // ๆ˜พ็คบๆ€ป็ป“
263
- displaySummary(result, validated)
264
-
265
- const totalAccepted = Object.values(result).reduce((sum, arr) => sum + arr.length, 0)
170
+ // 2. ่ฏข้—ฎ็”จๆˆทๆ“ไฝœ
171
+ const userInput = await ask(rl,
172
+ `โ“ Choose action:\n` +
173
+ ` [a] Accept all ${totalSuggestions} suggestions\n` +
174
+ ` [r] Reject all\n` +
175
+ ` Or input numbers (comma-separated, e.g. 1,3,5-8)\n` +
176
+ `\n> `
177
+ )
178
+
179
+ const parsed = parseUserInput(userInput, totalSuggestions)
180
+
181
+ // 3. ๅค„็†็”จๆˆท้€‰ๆ‹ฉ
182
+ const result = {
183
+ businessCriticalPaths: [],
184
+ highRiskModules: [],
185
+ testabilityAdjustments: []
186
+ }
266
187
 
267
- if (totalAccepted === 0) {
188
+ if (parsed.type === 'accept_all') {
189
+ // ๅ…จๆŽฅๅ—
190
+ result.businessCriticalPaths = validated.businessCriticalPaths || []
191
+ result.highRiskModules = validated.highRiskModules || []
192
+ result.testabilityAdjustments = validated.testabilityAdjustments || []
193
+
194
+ console.log(`\nโœ… Accepted all ${totalSuggestions} suggestions`)
195
+
196
+ } else if (parsed.type === 'reject_all') {
197
+ // ๅ…จๆ‹’็ป
198
+ console.log(`\nโŒ Rejected all suggestions`)
199
+ rl.close()
200
+ return null
201
+
202
+ } else if (parsed.type === 'partial') {
203
+ // ้ƒจๅˆ†ๆŽฅๅ—
204
+ const selectedIndices = new Set(parsed.indices)
205
+
206
+ indexMapping.forEach(({ globalIndex, category, localIndex }) => {
207
+ if (selectedIndices.has(globalIndex)) {
208
+ const item = validated[category][localIndex]
209
+ result[category].push(item)
210
+ }
211
+ })
212
+
213
+ const totalAccepted = Object.values(result).reduce((sum, arr) => sum + arr.length, 0)
214
+ console.log(`\nโœ… Accepted ${totalAccepted}/${totalSuggestions} suggestions`)
215
+
216
+ if (totalAccepted === 0) {
217
+ console.log(`โš ๏ธ No valid suggestions selected`)
218
+ rl.close()
219
+ return null
220
+ }
221
+
222
+ } else {
223
+ // ๆ— ๆ•ˆ่พ“ๅ…ฅ
224
+ console.log(`\nโŒ Invalid input. No changes made.`)
268
225
  rl.close()
269
226
  return null
270
227
  }
271
228
 
272
- // ๆœ€็ปˆ็กฎ่ฎค
273
- const save = await ask(rl, `\n๐Ÿ’พ Save these changes to ai-test.config.jsonc? (y/n)\n> `)
229
+ // 4. ๆ˜พ็คบๆœ€็ปˆๆ€ป็ป“
230
+ displayFinalSummary(result, validated)
231
+
232
+ // 5. ๆœ€็ปˆ็กฎ่ฎค
233
+ const confirm = await ask(rl, `๐Ÿ’พ Apply these changes? (y/n)\n> `)
274
234
 
275
235
  rl.close()
276
236
 
277
- return save.trim().toLowerCase() === 'y' ? result : null
237
+ if (confirm.trim().toLowerCase() === 'y') {
238
+ return result
239
+ } else {
240
+ console.log(`\nโŒ Changes discarded.`)
241
+ return null
242
+ }
243
+
278
244
  } catch (err) {
279
245
  rl.close()
280
246
  throw err
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-unit-test-generator",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "description": "AI-powered unit test generator with smart priority scoring",
5
5
  "keywords": [
6
6
  "unit-test",