@eldrforge/kodrdriv 1.2.124 → 1.2.126

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.
@@ -10,15 +10,202 @@ import { ValidationError, ExternalDependencyError, CommandError } from '../error
10
10
  import { getDryRunLogger } from '../logging.js';
11
11
  import { run, validateString, safeJsonParse, validatePackageJson } from '@eldrforge/git-tools';
12
12
  import { sanitizeDirection } from '../util/validation.js';
13
+ import { filterContent } from '../util/stopContext.js';
13
14
  import { getOutputPath, getTimestampedResponseFilename, getTimestampedRequestFilename, getTimestampedCommitFilename } from '../util/general.js';
14
15
  import { createStorage, stringifyJSON, checkForFileDependencies, logFileDependencyWarning, logFileDependencySuggestions } from '@eldrforge/shared';
15
16
  import { getRecentClosedIssuesForCommit } from '@eldrforge/github-tools';
16
- import { createCommitPrompt, createCompletionWithRetry, requireTTY, getUserChoice, STANDARD_CHOICES, getLLMFeedbackInEditor, editContentInEditor } from '@eldrforge/ai-service';
17
+ import { runAgenticCommit, createCommitPrompt, createCompletionWithRetry, requireTTY, getUserChoice, STANDARD_CHOICES, getLLMFeedbackInEditor, editContentInEditor } from '@eldrforge/ai-service';
17
18
  import { improveContentWithLLM } from '../util/interactive.js';
18
19
  import { toAIConfig } from '../util/aiAdapter.js';
19
20
  import { createStorageAdapter } from '../util/storageAdapter.js';
20
21
  import { createLoggerAdapter } from '../util/loggerAdapter.js';
21
22
 
23
+ // Helper function to generate self-reflection output
24
+ async function generateSelfReflection(agenticResult, outputDirectory, storage, logger) {
25
+ try {
26
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('.')[0];
27
+ const reflectionPath = getOutputPath(outputDirectory, `agentic-reflection-commit-${timestamp}.md`);
28
+ // Calculate tool effectiveness metrics
29
+ const toolMetrics = agenticResult.toolMetrics || [];
30
+ const toolStats = new Map();
31
+ for (const metric of toolMetrics){
32
+ if (!toolStats.has(metric.name)) {
33
+ toolStats.set(metric.name, {
34
+ total: 0,
35
+ success: 0,
36
+ failures: 0,
37
+ totalDuration: 0
38
+ });
39
+ }
40
+ const stats = toolStats.get(metric.name);
41
+ stats.total++;
42
+ stats.totalDuration += metric.duration;
43
+ if (metric.success) {
44
+ stats.success++;
45
+ } else {
46
+ stats.failures++;
47
+ }
48
+ }
49
+ // Build reflection document
50
+ const sections = [];
51
+ sections.push('# Agentic Commit - Self-Reflection Report');
52
+ sections.push('');
53
+ sections.push(`Generated: ${new Date().toISOString()}`);
54
+ sections.push('');
55
+ sections.push('## Execution Summary');
56
+ sections.push('');
57
+ sections.push(`- **Iterations**: ${agenticResult.iterations}`);
58
+ sections.push(`- **Tool Calls**: ${agenticResult.toolCallsExecuted}`);
59
+ sections.push(`- **Unique Tools Used**: ${toolStats.size}`);
60
+ sections.push('');
61
+ sections.push('## Tool Effectiveness Analysis');
62
+ sections.push('');
63
+ if (toolStats.size === 0) {
64
+ sections.push('*No tools were executed during this run.*');
65
+ } else {
66
+ sections.push('| Tool | Calls | Success | Failures | Success Rate | Avg Duration |');
67
+ sections.push('|------|-------|---------|----------|--------------|--------------|');
68
+ for (const [toolName, stats] of Array.from(toolStats.entries()).sort((a, b)=>b[1].total - a[1].total)){
69
+ const successRate = (stats.success / stats.total * 100).toFixed(1);
70
+ const avgDuration = (stats.totalDuration / stats.total).toFixed(0);
71
+ sections.push(`| ${toolName} | ${stats.total} | ${stats.success} | ${stats.failures} | ${successRate}% | ${avgDuration}ms |`);
72
+ }
73
+ sections.push('');
74
+ sections.push('### Tool Performance Insights');
75
+ sections.push('');
76
+ // Identify problematic tools
77
+ const failedTools = Array.from(toolStats.entries()).filter(([_, stats])=>stats.failures > 0);
78
+ if (failedTools.length > 0) {
79
+ sections.push('**Tools with Failures:**');
80
+ for (const [toolName, stats] of failedTools){
81
+ const failureRate = (stats.failures / stats.total * 100).toFixed(1);
82
+ sections.push(`- ${toolName}: ${stats.failures}/${stats.total} failures (${failureRate}%)`);
83
+ }
84
+ sections.push('');
85
+ }
86
+ // Identify slow tools
87
+ const slowTools = Array.from(toolStats.entries()).filter(([_, stats])=>stats.totalDuration / stats.total > 1000).sort((a, b)=>b[1].totalDuration / b[1].total - a[1].totalDuration / a[1].total);
88
+ if (slowTools.length > 0) {
89
+ sections.push('**Slow Tools (>1s average):**');
90
+ for (const [toolName, stats] of slowTools){
91
+ const avgDuration = (stats.totalDuration / stats.total / 1000).toFixed(2);
92
+ sections.push(`- ${toolName}: ${avgDuration}s average`);
93
+ }
94
+ sections.push('');
95
+ }
96
+ // Identify most useful tools
97
+ sections.push('**Most Frequently Used:**');
98
+ const topTools = Array.from(toolStats.entries()).slice(0, 3);
99
+ for (const [toolName, stats] of topTools){
100
+ sections.push(`- ${toolName}: ${stats.total} calls`);
101
+ }
102
+ sections.push('');
103
+ }
104
+ sections.push('## Detailed Execution Timeline');
105
+ sections.push('');
106
+ if (toolMetrics.length === 0) {
107
+ sections.push('*No tool execution timeline available.*');
108
+ } else {
109
+ sections.push('| Time | Iteration | Tool | Result | Duration |');
110
+ sections.push('|------|-----------|------|--------|----------|');
111
+ for (const metric of toolMetrics){
112
+ const time = new Date(metric.timestamp).toLocaleTimeString();
113
+ const result = metric.success ? '✅ Success' : `❌ ${metric.error || 'Failed'}`;
114
+ sections.push(`| ${time} | ${metric.iteration} | ${metric.name} | ${result} | ${metric.duration}ms |`);
115
+ }
116
+ sections.push('');
117
+ }
118
+ sections.push('## Conversation History');
119
+ sections.push('');
120
+ sections.push('<details>');
121
+ sections.push('<summary>Click to expand full agentic interaction</summary>');
122
+ sections.push('');
123
+ sections.push('```json');
124
+ sections.push(JSON.stringify(agenticResult.conversationHistory, null, 2));
125
+ sections.push('```');
126
+ sections.push('');
127
+ sections.push('</details>');
128
+ sections.push('');
129
+ sections.push('## Generated Commit Message');
130
+ sections.push('');
131
+ sections.push('```');
132
+ sections.push(agenticResult.commitMessage);
133
+ sections.push('```');
134
+ sections.push('');
135
+ if (agenticResult.suggestedSplits && agenticResult.suggestedSplits.length > 1) {
136
+ sections.push('## Suggested Commit Splits');
137
+ sections.push('');
138
+ for(let i = 0; i < agenticResult.suggestedSplits.length; i++){
139
+ const split = agenticResult.suggestedSplits[i];
140
+ sections.push(`### Split ${i + 1}`);
141
+ sections.push('');
142
+ sections.push(`**Files**: ${split.files.join(', ')}`);
143
+ sections.push('');
144
+ sections.push(`**Rationale**: ${split.rationale}`);
145
+ sections.push('');
146
+ sections.push(`**Message**:`);
147
+ sections.push('```');
148
+ sections.push(split.message);
149
+ sections.push('```');
150
+ sections.push('');
151
+ }
152
+ }
153
+ sections.push('## Recommendations for Improvement');
154
+ sections.push('');
155
+ // Generate recommendations based on metrics
156
+ const recommendations = [];
157
+ // Recalculate failed tools for recommendations
158
+ const toolsWithFailures = Array.from(toolStats.entries()).filter(([_, stats])=>stats.failures > 0);
159
+ if (toolsWithFailures.length > 0) {
160
+ recommendations.push('- **Tool Failures**: Investigate and fix tools that are failing. This may indicate issues with error handling or tool implementation.');
161
+ }
162
+ // Recalculate slow tools for recommendations
163
+ const slowToolsForRecs = Array.from(toolStats.entries()).filter(([_, stats])=>stats.totalDuration / stats.total > 1000);
164
+ if (slowToolsForRecs.length > 0) {
165
+ recommendations.push('- **Performance**: Consider optimizing slow tools or caching results to improve execution speed.');
166
+ }
167
+ if (agenticResult.iterations >= (agenticResult.maxIterations || 10)) {
168
+ recommendations.push('- **Max Iterations Reached**: The agent reached maximum iterations. Consider increasing the limit or improving tool efficiency to allow the agent to complete naturally.');
169
+ }
170
+ const underutilizedTools = Array.from(toolStats.entries()).filter(([_, stats])=>stats.total === 1);
171
+ if (underutilizedTools.length > 3) {
172
+ recommendations.push('- **Underutilized Tools**: Many tools were called only once. Consider whether all tools are necessary or if the agent needs better guidance on when to use them.');
173
+ }
174
+ if (recommendations.length === 0) {
175
+ sections.push('*No specific recommendations at this time. Execution appears optimal.*');
176
+ } else {
177
+ for (const rec of recommendations){
178
+ sections.push(rec);
179
+ }
180
+ }
181
+ sections.push('');
182
+ // Write the reflection file
183
+ const reflectionContent = sections.join('\n');
184
+ await storage.writeFile(reflectionPath, reflectionContent, 'utf-8');
185
+ logger.info('');
186
+ logger.info('═'.repeat(80));
187
+ logger.info('📊 SELF-REFLECTION REPORT GENERATED');
188
+ logger.info('═'.repeat(80));
189
+ logger.info('');
190
+ logger.info('📁 Location: %s', reflectionPath);
191
+ logger.info('');
192
+ logger.info('📈 Report Summary:');
193
+ logger.info(' • %d iterations completed', agenticResult.iterations);
194
+ logger.info(' • %d tool calls executed', agenticResult.toolCallsExecuted);
195
+ logger.info(' • %d unique tools used', toolStats.size);
196
+ logger.info('');
197
+ logger.info('💡 Use this report to:');
198
+ logger.info(' • Understand which tools were most effective');
199
+ logger.info(' • Identify performance bottlenecks');
200
+ logger.info(' • Review the complete agentic conversation');
201
+ logger.info(' • Improve tool implementation based on metrics');
202
+ logger.info('');
203
+ logger.info('═'.repeat(80));
204
+ } catch (error) {
205
+ logger.warn('Failed to generate self-reflection output: %s', error.message);
206
+ logger.debug('Self-reflection error details:', error);
207
+ }
208
+ }
22
209
  // Helper function to get current version from package.json
23
210
  async function getCurrentVersion(storage) {
24
211
  try {
@@ -257,7 +444,7 @@ const saveCommitMessage = async (outputDirectory, summary, storage, logger)=>{
257
444
  }
258
445
  };
259
446
  const executeInternal = async (runConfig)=>{
260
- var _runConfig_commit, _runConfig_commit1, _runConfig_commit2, _runConfig_commit3, _runConfig_commit4, _aiConfig_commands_commit, _aiConfig_commands, _aiConfig_commands_commit1, _aiConfig_commands1, _runConfig_commit5, _runConfig_commit6, _runConfig_commit7, _runConfig_commit8, _runConfig_commit9;
447
+ var _runConfig_commit, _runConfig_commit1, _runConfig_commit2, _runConfig_commit3, _runConfig_commit4, _runConfig_commit5, _runConfig_commit6, _runConfig_commit7, _runConfig_commit8, _runConfig_commit9, _runConfig_commit10;
261
448
  const isDryRun = runConfig.dryRun || false;
262
449
  const logger = getDryRunLogger(isDryRun);
263
450
  // Track if user explicitly chose to skip in interactive mode
@@ -293,9 +480,9 @@ const executeInternal = async (runConfig)=>{
293
480
  if (!hasActualChanges) {
294
481
  const criticalChanges = await hasCriticalExcludedChanges();
295
482
  if (criticalChanges.hasChanges) {
296
- var _runConfig_commit10;
483
+ var _runConfig_commit11;
297
484
  logger.info('CRITICAL_FILES_DETECTED: No changes with exclusion patterns, but critical files modified | Files: %s | Action: May need to include critical files', criticalChanges.files.join(', '));
298
- if (((_runConfig_commit10 = runConfig.commit) === null || _runConfig_commit10 === void 0 ? void 0 : _runConfig_commit10.sendit) && !isDryRun) {
485
+ if (((_runConfig_commit11 = runConfig.commit) === null || _runConfig_commit11 === void 0 ? void 0 : _runConfig_commit11.sendit) && !isDryRun) {
299
486
  // In sendit mode, automatically include critical files
300
487
  logger.info('SENDIT_INCLUDING_CRITICAL: SendIt mode including critical files in diff | Purpose: Ensure all important changes are captured');
301
488
  var _runConfig_excludedPatterns1;
@@ -327,10 +514,10 @@ const executeInternal = async (runConfig)=>{
327
514
  }
328
515
  }
329
516
  } else {
330
- var _runConfig_commit11;
517
+ var _runConfig_commit12;
331
518
  // No changes at all - try fallback to file content for new repositories
332
519
  logger.info('NO_CHANGES_DETECTED: No changes found in working directory | Status: clean | Action: Nothing to commit');
333
- if (((_runConfig_commit11 = runConfig.commit) === null || _runConfig_commit11 === void 0 ? void 0 : _runConfig_commit11.sendit) && !isDryRun) {
520
+ if (((_runConfig_commit12 = runConfig.commit) === null || _runConfig_commit12 === void 0 ? void 0 : _runConfig_commit12.sendit) && !isDryRun) {
334
521
  logger.warn('No changes detected to commit. Skipping commit operation.');
335
522
  return 'No changes to commit.';
336
523
  } else {
@@ -350,8 +537,8 @@ const executeInternal = async (runConfig)=>{
350
537
  isUsingFileContent = true;
351
538
  hasActualChanges = true; // We have content to work with
352
539
  } else {
353
- var _runConfig_commit12;
354
- if ((_runConfig_commit12 = runConfig.commit) === null || _runConfig_commit12 === void 0 ? void 0 : _runConfig_commit12.sendit) {
540
+ var _runConfig_commit13;
541
+ if ((_runConfig_commit13 = runConfig.commit) === null || _runConfig_commit13 === void 0 ? void 0 : _runConfig_commit13.sendit) {
355
542
  logger.info('COMMIT_SKIPPED: Skipping commit operation | Reason: No changes detected | Action: None');
356
543
  return 'No changes to commit.';
357
544
  } else {
@@ -405,6 +592,7 @@ const executeInternal = async (runConfig)=>{
405
592
  const aiConfig = toAIConfig(runConfig);
406
593
  const aiStorageAdapter = createStorageAdapter();
407
594
  const aiLogger = createLoggerAdapter(isDryRun);
595
+ // Define promptContext for use in both agentic and traditional modes
408
596
  const promptContent = {
409
597
  diffContent,
410
598
  userDirection,
@@ -416,65 +604,122 @@ const executeInternal = async (runConfig)=>{
416
604
  context: (_runConfig_commit4 = runConfig.commit) === null || _runConfig_commit4 === void 0 ? void 0 : _runConfig_commit4.context,
417
605
  directories: runConfig.contextDirectories
418
606
  };
419
- const prompt = await createCommitPrompt(promptConfig, promptContent, promptContext);
420
- // Get the appropriate model for the commit command
421
- const modelToUse = ((_aiConfig_commands = aiConfig.commands) === null || _aiConfig_commands === void 0 ? void 0 : (_aiConfig_commands_commit = _aiConfig_commands.commit) === null || _aiConfig_commands_commit === void 0 ? void 0 : _aiConfig_commands_commit.model) || aiConfig.model || 'gpt-4o-mini';
422
- // Use consistent model for debug (fix hardcoded model)
423
- if (runConfig.debug) {
424
- const formattedPrompt = Formatter.create({
607
+ let rawSummary;
608
+ // Check if agentic mode is enabled
609
+ if ((_runConfig_commit5 = runConfig.commit) === null || _runConfig_commit5 === void 0 ? void 0 : _runConfig_commit5.agentic) {
610
+ var _runConfig_commit14, _aiConfig_commands_commit, _aiConfig_commands, _runConfig_commit15, _aiConfig_commands_commit1, _aiConfig_commands1, _runConfig_commit16, _runConfig_commit17;
611
+ logger.info('🤖 Using agentic mode for commit message generation');
612
+ // Announce self-reflection if enabled
613
+ if ((_runConfig_commit14 = runConfig.commit) === null || _runConfig_commit14 === void 0 ? void 0 : _runConfig_commit14.selfReflection) {
614
+ logger.info('📊 Self-reflection enabled - detailed analysis will be generated');
615
+ }
616
+ // Get list of changed files
617
+ const changedFilesResult = await run(`git diff --name-only ${cached ? '--cached' : ''}`);
618
+ const changedFilesOutput = typeof changedFilesResult === 'string' ? changedFilesResult : changedFilesResult.stdout;
619
+ const changedFiles = changedFilesOutput.split('\n').filter((f)=>f.trim().length > 0);
620
+ logger.debug('Changed files for agentic analysis: %d files', changedFiles.length);
621
+ // Run agentic commit generation
622
+ const agenticResult = await runAgenticCommit({
623
+ changedFiles,
624
+ diffContent,
625
+ userDirection,
626
+ logContext,
627
+ model: ((_aiConfig_commands = aiConfig.commands) === null || _aiConfig_commands === void 0 ? void 0 : (_aiConfig_commands_commit = _aiConfig_commands.commit) === null || _aiConfig_commands_commit === void 0 ? void 0 : _aiConfig_commands_commit.model) || aiConfig.model,
628
+ maxIterations: ((_runConfig_commit15 = runConfig.commit) === null || _runConfig_commit15 === void 0 ? void 0 : _runConfig_commit15.maxAgenticIterations) || 10,
629
+ debug: runConfig.debug,
630
+ debugRequestFile: getOutputPath(outputDirectory, getTimestampedRequestFilename('commit-agentic')),
631
+ debugResponseFile: getOutputPath(outputDirectory, getTimestampedResponseFilename('commit-agentic')),
632
+ storage: aiStorageAdapter,
633
+ logger: aiLogger,
634
+ openaiReasoning: ((_aiConfig_commands1 = aiConfig.commands) === null || _aiConfig_commands1 === void 0 ? void 0 : (_aiConfig_commands_commit1 = _aiConfig_commands1.commit) === null || _aiConfig_commands_commit1 === void 0 ? void 0 : _aiConfig_commands_commit1.reasoning) || aiConfig.reasoning
635
+ });
636
+ logger.info('🔍 Agentic analysis complete: %d iterations, %d tool calls', agenticResult.iterations, agenticResult.toolCallsExecuted);
637
+ // Generate self-reflection output if enabled
638
+ if ((_runConfig_commit16 = runConfig.commit) === null || _runConfig_commit16 === void 0 ? void 0 : _runConfig_commit16.selfReflection) {
639
+ await generateSelfReflection(agenticResult, outputDirectory, storage, logger);
640
+ }
641
+ // Check for suggested splits
642
+ if (agenticResult.suggestedSplits.length > 1 && ((_runConfig_commit17 = runConfig.commit) === null || _runConfig_commit17 === void 0 ? void 0 : _runConfig_commit17.allowCommitSplitting)) {
643
+ logger.info('\n📋 Agent suggests splitting this into %d commits:', agenticResult.suggestedSplits.length);
644
+ for(let i = 0; i < agenticResult.suggestedSplits.length; i++){
645
+ const split = agenticResult.suggestedSplits[i];
646
+ logger.info('\nCommit %d (%d files):', i + 1, split.files.length);
647
+ logger.info(' Files: %s', split.files.join(', '));
648
+ logger.info(' Rationale: %s', split.rationale);
649
+ logger.info(' Message: %s', split.message);
650
+ }
651
+ logger.info('\n⚠️ Commit splitting is not yet automated. Please stage and commit files separately.');
652
+ logger.info('Using combined message for now...\n');
653
+ } else if (agenticResult.suggestedSplits.length > 1) {
654
+ logger.debug('Agent suggested %d splits but commit splitting is not enabled', agenticResult.suggestedSplits.length);
655
+ }
656
+ rawSummary = agenticResult.commitMessage;
657
+ } else {
658
+ var _aiConfig_commands_commit2, _aiConfig_commands2, _aiConfig_commands_commit3, _aiConfig_commands3;
659
+ // Traditional single-shot approach
660
+ const prompt = await createCommitPrompt(promptConfig, promptContent, promptContext);
661
+ // Get the appropriate model for the commit command
662
+ const modelToUse = ((_aiConfig_commands2 = aiConfig.commands) === null || _aiConfig_commands2 === void 0 ? void 0 : (_aiConfig_commands_commit2 = _aiConfig_commands2.commit) === null || _aiConfig_commands_commit2 === void 0 ? void 0 : _aiConfig_commands_commit2.model) || aiConfig.model || 'gpt-4o-mini';
663
+ // Use consistent model for debug (fix hardcoded model)
664
+ if (runConfig.debug) {
665
+ const formattedPrompt = Formatter.create({
666
+ logger
667
+ }).formatPrompt(modelToUse, prompt);
668
+ logger.silly('Formatted Prompt: %s', stringifyJSON(formattedPrompt));
669
+ }
670
+ const request = Formatter.create({
425
671
  logger
426
672
  }).formatPrompt(modelToUse, prompt);
427
- logger.silly('Formatted Prompt: %s', stringifyJSON(formattedPrompt));
428
- }
429
- const request = Formatter.create({
430
- logger
431
- }).formatPrompt(modelToUse, prompt);
432
- // Create retry callback that reduces diff size on token limit errors
433
- const createRetryCallback = (originalDiffContent)=>async (attempt)=>{
434
- var _runConfig_commit;
435
- logger.info('COMMIT_RETRY: Retrying with reduced diff size | Attempt: %d | Strategy: Truncate diff | Reason: Previous attempt failed', attempt);
436
- // Progressively reduce the diff size on retries
437
- const reductionFactor = Math.pow(0.5, attempt - 1); // 50% reduction per retry
438
- const reducedMaxDiffBytes = Math.max(512, Math.floor(maxDiffBytes * reductionFactor));
439
- logger.debug('Reducing maxDiffBytes from %d to %d for retry', maxDiffBytes, reducedMaxDiffBytes);
440
- // Re-truncate the diff with smaller limits
441
- const reducedDiffContent = originalDiffContent.length > reducedMaxDiffBytes ? truncateDiffByFiles(originalDiffContent, reducedMaxDiffBytes) : originalDiffContent;
442
- // Rebuild the prompt with the reduced diff
443
- const reducedPromptContent = {
444
- diffContent: reducedDiffContent,
445
- userDirection
446
- };
447
- const reducedPromptContext = {
448
- logContext,
449
- context: (_runConfig_commit = runConfig.commit) === null || _runConfig_commit === void 0 ? void 0 : _runConfig_commit.context,
450
- directories: runConfig.contextDirectories
673
+ // Create retry callback that reduces diff size on token limit errors
674
+ const createRetryCallback = (originalDiffContent)=>async (attempt)=>{
675
+ var _runConfig_commit;
676
+ logger.info('COMMIT_RETRY: Retrying with reduced diff size | Attempt: %d | Strategy: Truncate diff | Reason: Previous attempt failed', attempt);
677
+ // Progressively reduce the diff size on retries
678
+ const reductionFactor = Math.pow(0.5, attempt - 1); // 50% reduction per retry
679
+ const reducedMaxDiffBytes = Math.max(512, Math.floor(maxDiffBytes * reductionFactor));
680
+ logger.debug('Reducing maxDiffBytes from %d to %d for retry', maxDiffBytes, reducedMaxDiffBytes);
681
+ // Re-truncate the diff with smaller limits
682
+ const reducedDiffContent = originalDiffContent.length > reducedMaxDiffBytes ? truncateDiffByFiles(originalDiffContent, reducedMaxDiffBytes) : originalDiffContent;
683
+ // Rebuild the prompt with the reduced diff
684
+ const reducedPromptContent = {
685
+ diffContent: reducedDiffContent,
686
+ userDirection
687
+ };
688
+ const reducedPromptContext = {
689
+ logContext,
690
+ context: (_runConfig_commit = runConfig.commit) === null || _runConfig_commit === void 0 ? void 0 : _runConfig_commit.context,
691
+ directories: runConfig.contextDirectories
692
+ };
693
+ const retryPrompt = await createCommitPrompt(promptConfig, reducedPromptContent, reducedPromptContext);
694
+ const retryRequest = Formatter.create({
695
+ logger
696
+ }).formatPrompt(modelToUse, retryPrompt);
697
+ return retryRequest.messages;
451
698
  };
452
- const retryPrompt = await createCommitPrompt(promptConfig, reducedPromptContent, reducedPromptContext);
453
- const retryRequest = Formatter.create({
454
- logger
455
- }).formatPrompt(modelToUse, retryPrompt);
456
- return retryRequest.messages;
457
- };
458
- const summary = await createCompletionWithRetry(request.messages, {
459
- model: modelToUse,
460
- openaiReasoning: ((_aiConfig_commands1 = aiConfig.commands) === null || _aiConfig_commands1 === void 0 ? void 0 : (_aiConfig_commands_commit1 = _aiConfig_commands1.commit) === null || _aiConfig_commands_commit1 === void 0 ? void 0 : _aiConfig_commands_commit1.reasoning) || aiConfig.reasoning,
461
- debug: runConfig.debug,
462
- debugRequestFile: getOutputPath(runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY, getTimestampedRequestFilename('commit')),
463
- debugResponseFile: getOutputPath(runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY, getTimestampedResponseFilename('commit')),
464
- storage: aiStorageAdapter,
465
- logger: aiLogger
466
- }, createRetryCallback(diffContent));
699
+ rawSummary = await createCompletionWithRetry(request.messages, {
700
+ model: modelToUse,
701
+ openaiReasoning: ((_aiConfig_commands3 = aiConfig.commands) === null || _aiConfig_commands3 === void 0 ? void 0 : (_aiConfig_commands_commit3 = _aiConfig_commands3.commit) === null || _aiConfig_commands_commit3 === void 0 ? void 0 : _aiConfig_commands_commit3.reasoning) || aiConfig.reasoning,
702
+ debug: runConfig.debug,
703
+ debugRequestFile: getOutputPath(runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY, getTimestampedRequestFilename('commit')),
704
+ debugResponseFile: getOutputPath(runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY, getTimestampedResponseFilename('commit')),
705
+ storage: aiStorageAdapter,
706
+ logger: aiLogger
707
+ }, createRetryCallback(diffContent));
708
+ }
709
+ // Apply stop-context filtering to commit message
710
+ const filterResult = filterContent(rawSummary, runConfig.stopContext);
711
+ const summary = filterResult.filtered;
467
712
  // Save timestamped copy of commit message with better error handling
468
713
  await saveCommitMessage(outputDirectory, summary, storage, logger);
469
714
  // 🛡️ Universal Safety Check: Run before ANY commit operation
470
715
  // This protects both direct commits (--sendit) and automated commits (publish, etc.)
471
- const willCreateCommit = ((_runConfig_commit5 = runConfig.commit) === null || _runConfig_commit5 === void 0 ? void 0 : _runConfig_commit5.sendit) && hasActualChanges && cached;
472
- if (willCreateCommit && !((_runConfig_commit6 = runConfig.commit) === null || _runConfig_commit6 === void 0 ? void 0 : _runConfig_commit6.skipFileCheck) && !isDryRun) {
716
+ const willCreateCommit = ((_runConfig_commit6 = runConfig.commit) === null || _runConfig_commit6 === void 0 ? void 0 : _runConfig_commit6.sendit) && hasActualChanges && cached;
717
+ if (willCreateCommit && !((_runConfig_commit7 = runConfig.commit) === null || _runConfig_commit7 === void 0 ? void 0 : _runConfig_commit7.skipFileCheck) && !isDryRun) {
473
718
  logger.debug('Checking for file: dependencies before commit operation...');
474
719
  try {
475
720
  const fileDependencyIssues = await checkForFileDependencies(storage, process.cwd());
476
721
  if (fileDependencyIssues.length > 0) {
477
- var _runConfig_commit13;
722
+ var _runConfig_commit18;
478
723
  logger.error('🚫 COMMIT BLOCKED: Found file: dependencies that should not be committed!');
479
724
  logger.error('');
480
725
  logFileDependencyWarning(fileDependencyIssues, 'commit');
@@ -482,7 +727,7 @@ const executeInternal = async (runConfig)=>{
482
727
  logger.error('Generated commit message was:');
483
728
  logger.error('%s', summary);
484
729
  logger.error('');
485
- if ((_runConfig_commit13 = runConfig.commit) === null || _runConfig_commit13 === void 0 ? void 0 : _runConfig_commit13.sendit) {
730
+ if ((_runConfig_commit18 = runConfig.commit) === null || _runConfig_commit18 === void 0 ? void 0 : _runConfig_commit18.sendit) {
486
731
  logger.error('To bypass this check, use: kodrdriv commit --skip-file-check --sendit');
487
732
  } else {
488
733
  logger.error('To bypass this check, add skipFileCheck: true to your commit configuration');
@@ -494,12 +739,12 @@ const executeInternal = async (runConfig)=>{
494
739
  logger.warn('Warning: Could not check for file: dependencies: %s', error.message);
495
740
  logger.warn('Proceeding with commit...');
496
741
  }
497
- } else if (((_runConfig_commit7 = runConfig.commit) === null || _runConfig_commit7 === void 0 ? void 0 : _runConfig_commit7.skipFileCheck) && willCreateCommit) {
742
+ } else if (((_runConfig_commit8 = runConfig.commit) === null || _runConfig_commit8 === void 0 ? void 0 : _runConfig_commit8.skipFileCheck) && willCreateCommit) {
498
743
  logger.warn('⚠️ Skipping file: dependency check as requested');
499
744
  }
500
745
  // Handle interactive mode
501
- if (((_runConfig_commit8 = runConfig.commit) === null || _runConfig_commit8 === void 0 ? void 0 : _runConfig_commit8.interactive) && !isDryRun) {
502
- var _runConfig_commit14;
746
+ if (((_runConfig_commit9 = runConfig.commit) === null || _runConfig_commit9 === void 0 ? void 0 : _runConfig_commit9.interactive) && !isDryRun) {
747
+ var _runConfig_commit19;
503
748
  requireTTY('Interactive mode requires a terminal. Use --sendit or --dry-run instead.');
504
749
  const interactiveResult = await handleInteractiveCommitFeedback(summary, runConfig, promptConfig, promptContext, outputDirectory, storage, diffContent, hasActualChanges, cached);
505
750
  if (interactiveResult.action === 'skip') {
@@ -509,23 +754,23 @@ const executeInternal = async (runConfig)=>{
509
754
  return interactiveResult.finalMessage;
510
755
  }
511
756
  // User chose to commit - check if sendit is enabled to determine what action to take
512
- const senditEnabled = (_runConfig_commit14 = runConfig.commit) === null || _runConfig_commit14 === void 0 ? void 0 : _runConfig_commit14.sendit;
757
+ const senditEnabled = (_runConfig_commit19 = runConfig.commit) === null || _runConfig_commit19 === void 0 ? void 0 : _runConfig_commit19.sendit;
513
758
  const willActuallyCommit = senditEnabled && hasActualChanges && cached;
514
759
  if (willActuallyCommit) {
515
- var _runConfig_commit15;
516
- const commitAction = ((_runConfig_commit15 = runConfig.commit) === null || _runConfig_commit15 === void 0 ? void 0 : _runConfig_commit15.amend) ? 'amending last commit' : 'committing';
760
+ var _runConfig_commit20;
761
+ const commitAction = ((_runConfig_commit20 = runConfig.commit) === null || _runConfig_commit20 === void 0 ? void 0 : _runConfig_commit20.amend) ? 'amending last commit' : 'committing';
517
762
  logger.info('SENDIT_EXECUTING: SendIt enabled, executing commit action | Action: %s | Message Length: %d | Final Message: \n\n%s\n\n', commitAction.charAt(0).toUpperCase() + commitAction.slice(1), interactiveResult.finalMessage.length, interactiveResult.finalMessage);
518
763
  try {
519
- var _runConfig_commit16, _runConfig_commit17;
764
+ var _runConfig_commit21, _runConfig_commit22;
520
765
  const validatedSummary = validateString(interactiveResult.finalMessage, 'commit summary');
521
766
  const escapedSummary = shellescape([
522
767
  validatedSummary
523
768
  ]);
524
- const commitCommand = ((_runConfig_commit16 = runConfig.commit) === null || _runConfig_commit16 === void 0 ? void 0 : _runConfig_commit16.amend) ? `git commit --amend -m ${escapedSummary}` : `git commit -m ${escapedSummary}`;
769
+ const commitCommand = ((_runConfig_commit21 = runConfig.commit) === null || _runConfig_commit21 === void 0 ? void 0 : _runConfig_commit21.amend) ? `git commit --amend -m ${escapedSummary}` : `git commit -m ${escapedSummary}`;
525
770
  await run(commitCommand);
526
771
  logger.info('COMMIT_SUCCESS: Commit operation completed successfully | Status: committed | Action: Changes saved to repository');
527
772
  // Push if requested
528
- await pushCommit((_runConfig_commit17 = runConfig.commit) === null || _runConfig_commit17 === void 0 ? void 0 : _runConfig_commit17.push, logger, isDryRun);
773
+ await pushCommit((_runConfig_commit22 = runConfig.commit) === null || _runConfig_commit22 === void 0 ? void 0 : _runConfig_commit22.push, logger, isDryRun);
529
774
  } catch (error) {
530
775
  logger.error('Failed to commit:', error);
531
776
  throw new ExternalDependencyError('Failed to create commit', 'git', error);
@@ -548,32 +793,32 @@ const executeInternal = async (runConfig)=>{
548
793
  logger.debug('Skipping sendit logic because user chose to skip in interactive mode');
549
794
  return summary;
550
795
  }
551
- if ((_runConfig_commit9 = runConfig.commit) === null || _runConfig_commit9 === void 0 ? void 0 : _runConfig_commit9.sendit) {
796
+ if ((_runConfig_commit10 = runConfig.commit) === null || _runConfig_commit10 === void 0 ? void 0 : _runConfig_commit10.sendit) {
552
797
  if (isDryRun) {
553
- var _runConfig_commit18, _runConfig_commit19;
798
+ var _runConfig_commit23, _runConfig_commit24;
554
799
  logger.info('Would commit with message: \n\n%s\n\n', summary);
555
- const commitAction = ((_runConfig_commit18 = runConfig.commit) === null || _runConfig_commit18 === void 0 ? void 0 : _runConfig_commit18.amend) ? 'git commit --amend -m <generated-message>' : 'git commit -m <generated-message>';
800
+ const commitAction = ((_runConfig_commit23 = runConfig.commit) === null || _runConfig_commit23 === void 0 ? void 0 : _runConfig_commit23.amend) ? 'git commit --amend -m <generated-message>' : 'git commit -m <generated-message>';
556
801
  logger.info('Would execute: %s', commitAction);
557
802
  // Show push command in dry run if requested
558
- if ((_runConfig_commit19 = runConfig.commit) === null || _runConfig_commit19 === void 0 ? void 0 : _runConfig_commit19.push) {
803
+ if ((_runConfig_commit24 = runConfig.commit) === null || _runConfig_commit24 === void 0 ? void 0 : _runConfig_commit24.push) {
559
804
  const remote = typeof runConfig.commit.push === 'string' ? runConfig.commit.push : 'origin';
560
805
  logger.info('Would push to %s with: git push %s', remote, remote);
561
806
  }
562
807
  } else if (hasActualChanges && cached) {
563
- var _runConfig_commit20;
564
- const commitAction = ((_runConfig_commit20 = runConfig.commit) === null || _runConfig_commit20 === void 0 ? void 0 : _runConfig_commit20.amend) ? 'amending commit' : 'committing';
808
+ var _runConfig_commit25;
809
+ const commitAction = ((_runConfig_commit25 = runConfig.commit) === null || _runConfig_commit25 === void 0 ? void 0 : _runConfig_commit25.amend) ? 'amending commit' : 'committing';
565
810
  logger.info('SendIt mode enabled. %s with message: \n\n%s\n\n', commitAction.charAt(0).toUpperCase() + commitAction.slice(1), summary);
566
811
  try {
567
- var _runConfig_commit21, _runConfig_commit22;
812
+ var _runConfig_commit26, _runConfig_commit27;
568
813
  const validatedSummary = validateString(summary, 'commit summary');
569
814
  const escapedSummary = shellescape([
570
815
  validatedSummary
571
816
  ]);
572
- const commitCommand = ((_runConfig_commit21 = runConfig.commit) === null || _runConfig_commit21 === void 0 ? void 0 : _runConfig_commit21.amend) ? `git commit --amend -m ${escapedSummary}` : `git commit -m ${escapedSummary}`;
817
+ const commitCommand = ((_runConfig_commit26 = runConfig.commit) === null || _runConfig_commit26 === void 0 ? void 0 : _runConfig_commit26.amend) ? `git commit --amend -m ${escapedSummary}` : `git commit -m ${escapedSummary}`;
573
818
  await run(commitCommand);
574
819
  logger.info('Commit successful!');
575
820
  // Push if requested
576
- await pushCommit((_runConfig_commit22 = runConfig.commit) === null || _runConfig_commit22 === void 0 ? void 0 : _runConfig_commit22.push, logger, isDryRun);
821
+ await pushCommit((_runConfig_commit27 = runConfig.commit) === null || _runConfig_commit27 === void 0 ? void 0 : _runConfig_commit27.push, logger, isDryRun);
577
822
  } catch (error) {
578
823
  logger.error('Failed to commit:', error);
579
824
  throw new ExternalDependencyError('Failed to create commit', 'git', error);