ai-cmg 0.1.3 → 0.1.5

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 (3) hide show
  1. package/README.md +90 -11
  2. package/dist/index.js +74 -5
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -81,7 +81,24 @@ export default {
81
81
 
82
82
  try {
83
83
  // 3. Parse request data (diff, hint, prompt)
84
- const { diff, hint, prompt } = await request.json();
84
+ let requestData;
85
+ try {
86
+ requestData = await request.json();
87
+ } catch (parseError) {
88
+ console.error('JSON parse error:', parseError);
89
+ return new Response(
90
+ JSON.stringify({
91
+ error: 'Invalid request format',
92
+ details: 'Failed to parse request body as JSON.'
93
+ }),
94
+ {
95
+ status: 400,
96
+ headers: { 'Content-Type': 'application/json' }
97
+ }
98
+ );
99
+ }
100
+
101
+ const { diff, hint, prompt } = requestData;
85
102
 
86
103
  if (!diff) {
87
104
  return new Response(JSON.stringify({ error: 'Diff data is missing' }), { status: 400 });
@@ -145,20 +162,82 @@ export default {
145
162
  }
146
163
 
147
164
  // 5. Run AI model
148
- const response = await env.AI.run(model, {
149
- messages: [
150
- { role: 'system', content: systemInstruction },
151
- { role: 'user', content: diff }
152
- ]
153
- });
165
+ let response;
166
+ try {
167
+ response = await env.AI.run(model, {
168
+ messages: [
169
+ { role: 'system', content: systemInstruction },
170
+ { role: 'user', content: diff }
171
+ ]
172
+ });
173
+ } catch (aiError) {
174
+ console.error('AI model error:', aiError);
175
+ const errorMessage = aiError?.message || String(aiError);
176
+
177
+ // Check for context length exceeded error
178
+ const isContextLengthError = errorMessage.includes('maximum context length') ||
179
+ errorMessage.includes('3030') ||
180
+ errorMessage.includes('context length');
181
+
182
+ if (isContextLengthError) {
183
+ return new Response(
184
+ JSON.stringify({
185
+ error: 'Context length exceeded',
186
+ details: errorMessage,
187
+ hint: 'The diff is too large. Please reduce the number of staged changes or split into multiple commits. The CLI should automatically truncate large diffs, but you may need to stage fewer files.'
188
+ }),
189
+ {
190
+ status: 400,
191
+ headers: { 'Content-Type': 'application/json' }
192
+ }
193
+ );
194
+ }
195
+
196
+ return new Response(
197
+ JSON.stringify({
198
+ error: 'AI model error',
199
+ details: errorMessage,
200
+ hint: 'Check if the model is available and your Workers AI quota is sufficient.'
201
+ }),
202
+ {
203
+ status: 500,
204
+ headers: { 'Content-Type': 'application/json' }
205
+ }
206
+ );
207
+ }
208
+
209
+ if (!response || !response.response) {
210
+ console.error('Invalid AI response:', response);
211
+ return new Response(
212
+ JSON.stringify({
213
+ error: 'Invalid AI response',
214
+ details: 'The AI model returned an unexpected response format.'
215
+ }),
216
+ {
217
+ status: 500,
218
+ headers: { 'Content-Type': 'application/json' }
219
+ }
220
+ );
221
+ }
154
222
 
155
223
  return Response.json({ message: response.response });
156
224
 
157
225
  } catch (error) {
158
- return new Response(JSON.stringify({ error: error.message }), {
159
- status: 500,
160
- headers: { 'Content-Type': 'application/json' }
161
- });
226
+ console.error('Worker error:', error);
227
+ const errorMessage = error?.message || String(error);
228
+ const errorStack = error?.stack;
229
+
230
+ return new Response(
231
+ JSON.stringify({
232
+ error: 'Server error',
233
+ details: errorMessage,
234
+ ...(errorStack && { stack: errorStack })
235
+ }),
236
+ {
237
+ status: 500,
238
+ headers: { 'Content-Type': 'application/json' }
239
+ }
240
+ );
162
241
  }
163
242
  }
164
243
  };
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ const LOCK_FILES = new Set([
23
23
  const GENERATED_PREFIXES = ['dist/', 'build/', '.next/', 'coverage/', 'node_modules/'];
24
24
  const MAX_BLOCK_LINES = 400;
25
25
  const MAX_BLOCK_CHARS = 20000;
26
- const MAX_INPUT_CHARS = 200000;
26
+ const MAX_INPUT_CHARS = 60000;
27
27
  function getPackageVersion() {
28
28
  try {
29
29
  const currentFile = fileURLToPath(import.meta.url);
@@ -378,10 +378,63 @@ function limitDiffSize(diff) {
378
378
  if (diff.length <= MAX_INPUT_CHARS) {
379
379
  return { diff, truncated: false };
380
380
  }
381
- const trimmed = diff.slice(0, MAX_INPUT_CHARS);
381
+ // Split by file blocks to preserve context - keep file boundaries intact
382
+ const blocks = diff.split(/^diff --git /m);
383
+ const keptBlocks = [];
384
+ const truncatedFiles = [];
385
+ let currentLength = 0;
386
+ let isFirstBlock = true;
387
+ // Preserve existing summary header if present
388
+ const headerMatch = diff.match(/^(# Omitted file contents \(summarized\)\n.+?\n\n)/s);
389
+ if (headerMatch) {
390
+ const header = headerMatch[1];
391
+ keptBlocks.push(header.trim());
392
+ currentLength = header.length;
393
+ isFirstBlock = false;
394
+ }
395
+ for (const block of blocks) {
396
+ if (!block.trim())
397
+ continue;
398
+ const headerLine = block.split('\n')[0] ?? '';
399
+ const match = headerLine.match(/^a\/(.+?) b\/(.+?)$/);
400
+ const fullBlock = isFirstBlock ? block : `diff --git ${block}`;
401
+ const blockLength = fullBlock.length;
402
+ isFirstBlock = false;
403
+ if (match) {
404
+ const aPath = match[1];
405
+ const bPath = match[2];
406
+ const filePath = bPath === '/dev/null' ? aPath : bPath;
407
+ // Check if this block is already summarized (from summarizeDiff)
408
+ const isAlreadySummarized = fullBlock.includes('# content omitted');
409
+ // Check if adding this block would exceed limit
410
+ if (!isAlreadySummarized && currentLength + blockLength > MAX_INPUT_CHARS) {
411
+ // Truncate: replace full diff with summary
412
+ truncatedFiles.push({ path: filePath });
413
+ const summaryBlock = `diff --git a/${aPath} b/${bPath}\n# content truncated due to size limit\n`;
414
+ keptBlocks.push(summaryBlock);
415
+ currentLength += summaryBlock.length;
416
+ }
417
+ else {
418
+ // Include full block (either fits, or already summarized)
419
+ keptBlocks.push(fullBlock);
420
+ currentLength += blockLength;
421
+ }
422
+ }
423
+ else {
424
+ // Non-file block (should be rare), include if space allows
425
+ if (currentLength + blockLength <= MAX_INPUT_CHARS) {
426
+ keptBlocks.push(fullBlock);
427
+ currentLength += blockLength;
428
+ }
429
+ }
430
+ }
431
+ // Add summary of truncated files if any
432
+ const truncatedSummary = truncatedFiles.length > 0
433
+ ? `\n\n# Additional files truncated due to size limit (${truncatedFiles.length} file${truncatedFiles.length > 1 ? 's' : ''})\n${truncatedFiles.map(f => `- ${f.path}`).join('\n')}`
434
+ : '';
382
435
  return {
383
- diff: `${trimmed}\n\n# diff truncated due to size limit\n`,
384
- truncated: true
436
+ diff: `${keptBlocks.join('\n')}${truncatedSummary}`.trim(),
437
+ truncated: truncatedFiles.length > 0
385
438
  };
386
439
  }
387
440
  async function main() {
@@ -486,7 +539,23 @@ async function main() {
486
539
  return;
487
540
  }
488
541
  if (!response.ok) {
489
- throw new Error(`Server Error: ${response.status}`);
542
+ let errorMessage = `Server Error: ${response.status}`;
543
+ try {
544
+ const errorData = await response.json();
545
+ if (errorData.error) {
546
+ errorMessage = errorData.error;
547
+ if (errorData.details) {
548
+ errorMessage += `\nDetails: ${errorData.details}`;
549
+ }
550
+ if (errorData.hint) {
551
+ errorMessage += `\nHint: ${errorData.hint}`;
552
+ }
553
+ }
554
+ }
555
+ catch {
556
+ // If JSON parsing fails, use the default error message
557
+ }
558
+ throw new Error(errorMessage);
490
559
  }
491
560
  const { message } = await response.json();
492
561
  // 5. 결과 출력 및 액션 선택
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-cmg",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "AI Commit Message Generator",
5
5
  "type": "module",
6
6
  "bin": {