@eldrforge/kodrdriv 1.2.131 ā 1.2.133
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/AI-GUIDE.md +834 -0
- package/README.md +35 -6
- package/agentic-reflection-commit-2025-12-27T22-56-06-143Z.md +50 -0
- package/agentic-reflection-commit-2025-12-27T23-01-57-294Z.md +50 -0
- package/agentic-reflection-commit-2025-12-27T23-11-57-811Z.md +50 -0
- package/agentic-reflection-commit-2025-12-27T23-12-50-645Z.md +50 -0
- package/agentic-reflection-commit-2025-12-27T23-13-59-347Z.md +52 -0
- package/agentic-reflection-commit-2025-12-27T23-14-36-001Z.md +50 -0
- package/agentic-reflection-commit-2025-12-27T23-18-59-832Z.md +50 -0
- package/agentic-reflection-commit-2025-12-27T23-25-20-667Z.md +62 -0
- package/dist/application.js +3 -0
- package/dist/application.js.map +1 -1
- package/dist/arguments.js +54 -21
- package/dist/arguments.js.map +1 -1
- package/dist/commands/audio-commit.js +2 -1
- package/dist/commands/audio-commit.js.map +1 -1
- package/dist/commands/audio-review.js +4 -2
- package/dist/commands/audio-review.js.map +1 -1
- package/dist/commands/commit.js +109 -288
- package/dist/commands/commit.js.map +1 -1
- package/dist/commands/publish.js +48 -6
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/release.js +79 -292
- package/dist/commands/release.js.map +1 -1
- package/dist/commands/review.js +1 -1
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/tree.js +29 -33
- package/dist/commands/tree.js.map +1 -1
- package/dist/constants.js +3 -1
- package/dist/constants.js.map +1 -1
- package/dist/util/loggerAdapter.js +17 -0
- package/dist/util/loggerAdapter.js.map +1 -1
- package/dist/util/storageAdapter.js +9 -2
- package/dist/util/storageAdapter.js.map +1 -1
- package/guide/ai-system.md +519 -0
- package/guide/architecture.md +346 -0
- package/guide/commands.md +380 -0
- package/guide/configuration.md +513 -0
- package/guide/debugging.md +584 -0
- package/guide/development.md +629 -0
- package/guide/index.md +212 -0
- package/guide/integration.md +507 -0
- package/guide/monorepo.md +530 -0
- package/guide/quickstart.md +223 -0
- package/guide/testing.md +460 -0
- package/guide/tree-operations.md +618 -0
- package/guide/usage.md +575 -0
- package/package.json +9 -9
package/dist/commands/commit.js
CHANGED
|
@@ -3,184 +3,58 @@ import { Formatter } from '@riotprompt/riotprompt';
|
|
|
3
3
|
import 'dotenv/config';
|
|
4
4
|
import shellescape from 'shell-escape';
|
|
5
5
|
import { DEFAULT_MAX_DIFF_BYTES, DEFAULT_EXCLUDED_PATTERNS, DEFAULT_OUTPUT_DIRECTORY } from '../constants.js';
|
|
6
|
-
import { create, hasCriticalExcludedChanges, getMinimalExcludedPatterns, hasStagedChanges
|
|
6
|
+
import { create, hasCriticalExcludedChanges, getMinimalExcludedPatterns, hasStagedChanges } from '../content/diff.js';
|
|
7
7
|
import { create as create$2 } from '../content/log.js';
|
|
8
8
|
import { create as create$1 } from '../content/files.js';
|
|
9
|
-
import { ValidationError, ExternalDependencyError, CommandError, createStorage,
|
|
9
|
+
import { ValidationError, ExternalDependencyError, CommandError, createStorage, checkForFileDependencies, logFileDependencyWarning, logFileDependencySuggestions } from '@eldrforge/shared';
|
|
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
13
|
import { filterContent } from '../util/stopContext.js';
|
|
14
14
|
import { getOutputPath, getTimestampedResponseFilename, getTimestampedRequestFilename, getTimestampedCommitFilename } from '../util/general.js';
|
|
15
15
|
import { getRecentClosedIssuesForCommit } from '@eldrforge/github-tools';
|
|
16
|
-
import { runAgenticCommit,
|
|
16
|
+
import { runAgenticCommit, requireTTY, generateReflectionReport, getUserChoice, STANDARD_CHOICES, getLLMFeedbackInEditor, editContentInEditor, createCompletionWithRetry, createCommitPrompt } from '@eldrforge/ai-service';
|
|
17
17
|
import { improveContentWithLLM } from '../util/interactive.js';
|
|
18
18
|
import { toAIConfig } from '../util/aiAdapter.js';
|
|
19
19
|
import { createStorageAdapter } from '../util/storageAdapter.js';
|
|
20
20
|
import { createLoggerAdapter } from '../util/loggerAdapter.js';
|
|
21
21
|
|
|
22
|
-
// Helper function to
|
|
22
|
+
// Helper function to read context files
|
|
23
|
+
async function readContextFiles(contextFiles, logger) {
|
|
24
|
+
if (!contextFiles || contextFiles.length === 0) {
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
const storage = createStorage();
|
|
28
|
+
const contextParts = [];
|
|
29
|
+
for (const filePath of contextFiles){
|
|
30
|
+
try {
|
|
31
|
+
const content = await storage.readFile(filePath, 'utf8');
|
|
32
|
+
contextParts.push(`## Context from ${filePath}\n\n${content}\n`);
|
|
33
|
+
logger.debug(`Read context from file: ${filePath}`);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
logger.warn(`Failed to read context file ${filePath}: ${error.message}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return contextParts.join('\n---\n\n');
|
|
39
|
+
}
|
|
40
|
+
// Helper function to generate self-reflection output using observability module
|
|
23
41
|
async function generateSelfReflection(agenticResult, outputDirectory, storage, logger) {
|
|
24
42
|
try {
|
|
25
43
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('.')[0];
|
|
26
44
|
const reflectionPath = getOutputPath(outputDirectory, `agentic-reflection-commit-${timestamp}.md`);
|
|
27
|
-
//
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
stats.total++;
|
|
41
|
-
stats.totalDuration += metric.duration;
|
|
42
|
-
if (metric.success) {
|
|
43
|
-
stats.success++;
|
|
44
|
-
} else {
|
|
45
|
-
stats.failures++;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
// Build reflection document
|
|
49
|
-
const sections = [];
|
|
50
|
-
sections.push('# Agentic Commit - Self-Reflection Report');
|
|
51
|
-
sections.push('');
|
|
52
|
-
sections.push(`Generated: ${new Date().toISOString()}`);
|
|
53
|
-
sections.push('');
|
|
54
|
-
sections.push('## Execution Summary');
|
|
55
|
-
sections.push('');
|
|
56
|
-
sections.push(`- **Iterations**: ${agenticResult.iterations}`);
|
|
57
|
-
sections.push(`- **Tool Calls**: ${agenticResult.toolCallsExecuted}`);
|
|
58
|
-
sections.push(`- **Unique Tools Used**: ${toolStats.size}`);
|
|
59
|
-
sections.push('');
|
|
60
|
-
sections.push('## Tool Effectiveness Analysis');
|
|
61
|
-
sections.push('');
|
|
62
|
-
if (toolStats.size === 0) {
|
|
63
|
-
sections.push('*No tools were executed during this run.*');
|
|
64
|
-
} else {
|
|
65
|
-
sections.push('| Tool | Calls | Success | Failures | Success Rate | Avg Duration |');
|
|
66
|
-
sections.push('|------|-------|---------|----------|--------------|--------------|');
|
|
67
|
-
for (const [toolName, stats] of Array.from(toolStats.entries()).sort((a, b)=>b[1].total - a[1].total)){
|
|
68
|
-
const successRate = (stats.success / stats.total * 100).toFixed(1);
|
|
69
|
-
const avgDuration = (stats.totalDuration / stats.total).toFixed(0);
|
|
70
|
-
sections.push(`| ${toolName} | ${stats.total} | ${stats.success} | ${stats.failures} | ${successRate}% | ${avgDuration}ms |`);
|
|
71
|
-
}
|
|
72
|
-
sections.push('');
|
|
73
|
-
sections.push('### Tool Performance Insights');
|
|
74
|
-
sections.push('');
|
|
75
|
-
// Identify problematic tools
|
|
76
|
-
const failedTools = Array.from(toolStats.entries()).filter(([_, stats])=>stats.failures > 0);
|
|
77
|
-
if (failedTools.length > 0) {
|
|
78
|
-
sections.push('**Tools with Failures:**');
|
|
79
|
-
for (const [toolName, stats] of failedTools){
|
|
80
|
-
const failureRate = (stats.failures / stats.total * 100).toFixed(1);
|
|
81
|
-
sections.push(`- ${toolName}: ${stats.failures}/${stats.total} failures (${failureRate}%)`);
|
|
82
|
-
}
|
|
83
|
-
sections.push('');
|
|
84
|
-
}
|
|
85
|
-
// Identify slow tools
|
|
86
|
-
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);
|
|
87
|
-
if (slowTools.length > 0) {
|
|
88
|
-
sections.push('**Slow Tools (>1s average):**');
|
|
89
|
-
for (const [toolName, stats] of slowTools){
|
|
90
|
-
const avgDuration = (stats.totalDuration / stats.total / 1000).toFixed(2);
|
|
91
|
-
sections.push(`- ${toolName}: ${avgDuration}s average`);
|
|
92
|
-
}
|
|
93
|
-
sections.push('');
|
|
94
|
-
}
|
|
95
|
-
// Identify most useful tools
|
|
96
|
-
sections.push('**Most Frequently Used:**');
|
|
97
|
-
const topTools = Array.from(toolStats.entries()).slice(0, 3);
|
|
98
|
-
for (const [toolName, stats] of topTools){
|
|
99
|
-
sections.push(`- ${toolName}: ${stats.total} calls`);
|
|
100
|
-
}
|
|
101
|
-
sections.push('');
|
|
102
|
-
}
|
|
103
|
-
sections.push('## Detailed Execution Timeline');
|
|
104
|
-
sections.push('');
|
|
105
|
-
if (toolMetrics.length === 0) {
|
|
106
|
-
sections.push('*No tool execution timeline available.*');
|
|
107
|
-
} else {
|
|
108
|
-
sections.push('| Time | Iteration | Tool | Result | Duration |');
|
|
109
|
-
sections.push('|------|-----------|------|--------|----------|');
|
|
110
|
-
for (const metric of toolMetrics){
|
|
111
|
-
const time = new Date(metric.timestamp).toLocaleTimeString();
|
|
112
|
-
const result = metric.success ? 'ā
Success' : `ā ${metric.error || 'Failed'}`;
|
|
113
|
-
sections.push(`| ${time} | ${metric.iteration} | ${metric.name} | ${result} | ${metric.duration}ms |`);
|
|
114
|
-
}
|
|
115
|
-
sections.push('');
|
|
116
|
-
}
|
|
117
|
-
sections.push('## Conversation History');
|
|
118
|
-
sections.push('');
|
|
119
|
-
sections.push('<details>');
|
|
120
|
-
sections.push('<summary>Click to expand full agentic interaction</summary>');
|
|
121
|
-
sections.push('');
|
|
122
|
-
sections.push('```json');
|
|
123
|
-
sections.push(JSON.stringify(agenticResult.conversationHistory, null, 2));
|
|
124
|
-
sections.push('```');
|
|
125
|
-
sections.push('');
|
|
126
|
-
sections.push('</details>');
|
|
127
|
-
sections.push('');
|
|
128
|
-
sections.push('## Generated Commit Message');
|
|
129
|
-
sections.push('');
|
|
130
|
-
sections.push('```');
|
|
131
|
-
sections.push(agenticResult.commitMessage);
|
|
132
|
-
sections.push('```');
|
|
133
|
-
sections.push('');
|
|
134
|
-
if (agenticResult.suggestedSplits && agenticResult.suggestedSplits.length > 1) {
|
|
135
|
-
sections.push('## Suggested Commit Splits');
|
|
136
|
-
sections.push('');
|
|
137
|
-
for(let i = 0; i < agenticResult.suggestedSplits.length; i++){
|
|
138
|
-
const split = agenticResult.suggestedSplits[i];
|
|
139
|
-
sections.push(`### Split ${i + 1}`);
|
|
140
|
-
sections.push('');
|
|
141
|
-
sections.push(`**Files**: ${split.files.join(', ')}`);
|
|
142
|
-
sections.push('');
|
|
143
|
-
sections.push(`**Rationale**: ${split.rationale}`);
|
|
144
|
-
sections.push('');
|
|
145
|
-
sections.push(`**Message**:`);
|
|
146
|
-
sections.push('```');
|
|
147
|
-
sections.push(split.message);
|
|
148
|
-
sections.push('```');
|
|
149
|
-
sections.push('');
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
sections.push('## Recommendations for Improvement');
|
|
153
|
-
sections.push('');
|
|
154
|
-
// Generate recommendations based on metrics
|
|
155
|
-
const recommendations = [];
|
|
156
|
-
// Recalculate failed tools for recommendations
|
|
157
|
-
const toolsWithFailures = Array.from(toolStats.entries()).filter(([_, stats])=>stats.failures > 0);
|
|
158
|
-
if (toolsWithFailures.length > 0) {
|
|
159
|
-
recommendations.push('- **Tool Failures**: Investigate and fix tools that are failing. This may indicate issues with error handling or tool implementation.');
|
|
160
|
-
}
|
|
161
|
-
// Recalculate slow tools for recommendations
|
|
162
|
-
const slowToolsForRecs = Array.from(toolStats.entries()).filter(([_, stats])=>stats.totalDuration / stats.total > 1000);
|
|
163
|
-
if (slowToolsForRecs.length > 0) {
|
|
164
|
-
recommendations.push('- **Performance**: Consider optimizing slow tools or caching results to improve execution speed.');
|
|
165
|
-
}
|
|
166
|
-
if (agenticResult.iterations >= (agenticResult.maxIterations || 10)) {
|
|
167
|
-
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.');
|
|
168
|
-
}
|
|
169
|
-
const underutilizedTools = Array.from(toolStats.entries()).filter(([_, stats])=>stats.total === 1);
|
|
170
|
-
if (underutilizedTools.length > 3) {
|
|
171
|
-
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.');
|
|
172
|
-
}
|
|
173
|
-
if (recommendations.length === 0) {
|
|
174
|
-
sections.push('*No specific recommendations at this time. Execution appears optimal.*');
|
|
175
|
-
} else {
|
|
176
|
-
for (const rec of recommendations){
|
|
177
|
-
sections.push(rec);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
sections.push('');
|
|
181
|
-
// Write the reflection file
|
|
182
|
-
const reflectionContent = sections.join('\n');
|
|
183
|
-
await storage.writeFile(reflectionPath, reflectionContent, 'utf-8');
|
|
45
|
+
// Use new observability reflection generator
|
|
46
|
+
const report = await generateReflectionReport({
|
|
47
|
+
iterations: agenticResult.iterations || 0,
|
|
48
|
+
toolCallsExecuted: agenticResult.toolCallsExecuted || 0,
|
|
49
|
+
maxIterations: agenticResult.maxIterations || 10,
|
|
50
|
+
toolMetrics: agenticResult.toolMetrics || [],
|
|
51
|
+
conversationHistory: agenticResult.conversationHistory || [],
|
|
52
|
+
commitMessage: agenticResult.commitMessage,
|
|
53
|
+
suggestedSplits: agenticResult.suggestedSplits || [],
|
|
54
|
+
logger
|
|
55
|
+
});
|
|
56
|
+
// Save the report to output directory
|
|
57
|
+
await storage.writeFile(reflectionPath, report, 'utf8');
|
|
184
58
|
logger.info('');
|
|
185
59
|
logger.info('ā'.repeat(80));
|
|
186
60
|
logger.info('š SELF-REFLECTION REPORT GENERATED');
|
|
@@ -189,9 +63,12 @@ async function generateSelfReflection(agenticResult, outputDirectory, storage, l
|
|
|
189
63
|
logger.info('š Location: %s', reflectionPath);
|
|
190
64
|
logger.info('');
|
|
191
65
|
logger.info('š Report Summary:');
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
66
|
+
const iterations = agenticResult.iterations || 0;
|
|
67
|
+
const toolCalls = agenticResult.toolCallsExecuted || 0;
|
|
68
|
+
const uniqueTools = new Set((agenticResult.toolMetrics || []).map((m)=>m.name)).size;
|
|
69
|
+
logger.info(` ⢠${iterations} iterations completed`);
|
|
70
|
+
logger.info(` ⢠${toolCalls} tool calls executed`);
|
|
71
|
+
logger.info(` ⢠${uniqueTools} unique tools used`);
|
|
195
72
|
logger.info('');
|
|
196
73
|
logger.info('š” Use this report to:');
|
|
197
74
|
logger.info(' ⢠Understand which tools were most effective');
|
|
@@ -232,7 +109,7 @@ async function improveCommitMessageWithLLM(commitMessage, runConfig, promptConfi
|
|
|
232
109
|
const userFeedback = await getLLMFeedbackInEditor('commit message', commitMessage);
|
|
233
110
|
// Create AI config from kodrdriv config
|
|
234
111
|
const aiConfig = toAIConfig(runConfig);
|
|
235
|
-
const aiStorageAdapter = createStorageAdapter();
|
|
112
|
+
const aiStorageAdapter = createStorageAdapter(outputDirectory);
|
|
236
113
|
const aiLogger = createLoggerAdapter(false);
|
|
237
114
|
const improvementConfig = {
|
|
238
115
|
contentType: 'commit message',
|
|
@@ -443,7 +320,7 @@ const saveCommitMessage = async (outputDirectory, summary, storage, logger)=>{
|
|
|
443
320
|
}
|
|
444
321
|
};
|
|
445
322
|
const executeInternal = async (runConfig)=>{
|
|
446
|
-
var _runConfig_commit, _runConfig_commit1, _runConfig_commit2, _runConfig_commit3, _runConfig_commit4, _runConfig_commit5, _runConfig_commit6, _runConfig_commit7, _runConfig_commit8, _runConfig_commit9, _runConfig_commit10;
|
|
323
|
+
var _runConfig_commit, _runConfig_commit1, _runConfig_commit2, _runConfig_commit3, _runConfig_commit4, _runConfig_commit5, _runConfig_commit6, _aiConfig_commands_commit, _aiConfig_commands, _runConfig_commit7, _aiConfig_commands_commit1, _aiConfig_commands1, _runConfig_commit8, _runConfig_commit9, _runConfig_commit10, _runConfig_commit11, _runConfig_commit12, _runConfig_commit13, _runConfig_commit14;
|
|
447
324
|
const isDryRun = runConfig.dryRun || false;
|
|
448
325
|
const logger = getDryRunLogger(isDryRun);
|
|
449
326
|
// Track if user explicitly chose to skip in interactive mode
|
|
@@ -462,7 +339,6 @@ const executeInternal = async (runConfig)=>{
|
|
|
462
339
|
// Validate sendit state early - now returns boolean instead of throwing
|
|
463
340
|
validateSenditState(runConfig, cached, isDryRun, logger);
|
|
464
341
|
let diffContent = '';
|
|
465
|
-
let isUsingFileContent = false;
|
|
466
342
|
var _runConfig_commit_maxDiffBytes;
|
|
467
343
|
const maxDiffBytes = (_runConfig_commit_maxDiffBytes = (_runConfig_commit1 = runConfig.commit) === null || _runConfig_commit1 === void 0 ? void 0 : _runConfig_commit1.maxDiffBytes) !== null && _runConfig_commit_maxDiffBytes !== void 0 ? _runConfig_commit_maxDiffBytes : DEFAULT_MAX_DIFF_BYTES;
|
|
468
344
|
var _runConfig_excludedPatterns;
|
|
@@ -479,9 +355,9 @@ const executeInternal = async (runConfig)=>{
|
|
|
479
355
|
if (!hasActualChanges) {
|
|
480
356
|
const criticalChanges = await hasCriticalExcludedChanges();
|
|
481
357
|
if (criticalChanges.hasChanges) {
|
|
482
|
-
var
|
|
358
|
+
var _runConfig_commit15;
|
|
483
359
|
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(', '));
|
|
484
|
-
if (((
|
|
360
|
+
if (((_runConfig_commit15 = runConfig.commit) === null || _runConfig_commit15 === void 0 ? void 0 : _runConfig_commit15.sendit) && !isDryRun) {
|
|
485
361
|
// In sendit mode, automatically include critical files
|
|
486
362
|
logger.info('SENDIT_INCLUDING_CRITICAL: SendIt mode including critical files in diff | Purpose: Ensure all important changes are captured');
|
|
487
363
|
var _runConfig_excludedPatterns1;
|
|
@@ -513,10 +389,10 @@ const executeInternal = async (runConfig)=>{
|
|
|
513
389
|
}
|
|
514
390
|
}
|
|
515
391
|
} else {
|
|
516
|
-
var
|
|
392
|
+
var _runConfig_commit16;
|
|
517
393
|
// No changes at all - try fallback to file content for new repositories
|
|
518
394
|
logger.info('NO_CHANGES_DETECTED: No changes found in working directory | Status: clean | Action: Nothing to commit');
|
|
519
|
-
if (((
|
|
395
|
+
if (((_runConfig_commit16 = runConfig.commit) === null || _runConfig_commit16 === void 0 ? void 0 : _runConfig_commit16.sendit) && !isDryRun) {
|
|
520
396
|
logger.warn('No changes detected to commit. Skipping commit operation.');
|
|
521
397
|
return 'No changes to commit.';
|
|
522
398
|
} else {
|
|
@@ -533,11 +409,10 @@ const executeInternal = async (runConfig)=>{
|
|
|
533
409
|
if (fileContent && fileContent.trim().length > 0) {
|
|
534
410
|
logger.info('FILE_CONTENT_USING: Using file content for commit message generation | Content Length: %d characters | Source: file content', fileContent.length);
|
|
535
411
|
diffContent = fileContent;
|
|
536
|
-
isUsingFileContent = true;
|
|
537
412
|
hasActualChanges = true; // We have content to work with
|
|
538
413
|
} else {
|
|
539
|
-
var
|
|
540
|
-
if ((
|
|
414
|
+
var _runConfig_commit17;
|
|
415
|
+
if ((_runConfig_commit17 = runConfig.commit) === null || _runConfig_commit17 === void 0 ? void 0 : _runConfig_commit17.sendit) {
|
|
541
416
|
logger.info('COMMIT_SKIPPED: Skipping commit operation | Reason: No changes detected | Action: None');
|
|
542
417
|
return 'No changes to commit.';
|
|
543
418
|
} else {
|
|
@@ -589,122 +464,68 @@ const executeInternal = async (runConfig)=>{
|
|
|
589
464
|
}
|
|
590
465
|
// Create adapters for ai-service
|
|
591
466
|
const aiConfig = toAIConfig(runConfig);
|
|
592
|
-
const aiStorageAdapter = createStorageAdapter();
|
|
467
|
+
const aiStorageAdapter = createStorageAdapter(outputDirectory);
|
|
593
468
|
const aiLogger = createLoggerAdapter(isDryRun);
|
|
594
|
-
//
|
|
595
|
-
const
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
469
|
+
// Read context from files if provided
|
|
470
|
+
const contextFromFiles = await readContextFiles((_runConfig_commit4 = runConfig.commit) === null || _runConfig_commit4 === void 0 ? void 0 : _runConfig_commit4.contextFiles, logger);
|
|
471
|
+
// Combine file context with existing context
|
|
472
|
+
const combinedContext = [
|
|
473
|
+
(_runConfig_commit5 = runConfig.commit) === null || _runConfig_commit5 === void 0 ? void 0 : _runConfig_commit5.context,
|
|
474
|
+
contextFromFiles
|
|
475
|
+
].filter(Boolean).join('\n\n---\n\n');
|
|
476
|
+
// Define promptContext for use in interactive improvements
|
|
601
477
|
const promptContext = {
|
|
602
478
|
logContext,
|
|
603
|
-
context:
|
|
479
|
+
context: combinedContext || undefined,
|
|
604
480
|
directories: runConfig.contextDirectories
|
|
605
481
|
};
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
logger.info('\
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
logger.info(' Files: %s', split.files.join(', '));
|
|
647
|
-
logger.info(' Rationale: %s', split.rationale);
|
|
648
|
-
logger.info(' Message: %s', split.message);
|
|
649
|
-
}
|
|
650
|
-
logger.info('\nā ļø Commit splitting is not yet automated. Please stage and commit files separately.');
|
|
651
|
-
logger.info('Using combined message for now...\n');
|
|
652
|
-
} else if (agenticResult.suggestedSplits.length > 1) {
|
|
653
|
-
logger.debug('Agent suggested %d splits but commit splitting is not enabled', agenticResult.suggestedSplits.length);
|
|
654
|
-
}
|
|
655
|
-
rawSummary = agenticResult.commitMessage;
|
|
656
|
-
} else {
|
|
657
|
-
var _aiConfig_commands_commit2, _aiConfig_commands2, _aiConfig_commands_commit3, _aiConfig_commands3;
|
|
658
|
-
// Traditional single-shot approach
|
|
659
|
-
const prompt = await createCommitPrompt(promptConfig, promptContent, promptContext);
|
|
660
|
-
// Get the appropriate model for the commit command
|
|
661
|
-
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';
|
|
662
|
-
// Use consistent model for debug (fix hardcoded model)
|
|
663
|
-
if (runConfig.debug) {
|
|
664
|
-
const formattedPrompt = Formatter.create({
|
|
665
|
-
logger
|
|
666
|
-
}).formatPrompt(modelToUse, prompt);
|
|
667
|
-
logger.silly('Formatted Prompt: %s', stringifyJSON(formattedPrompt));
|
|
482
|
+
// Announce self-reflection if enabled
|
|
483
|
+
if ((_runConfig_commit6 = runConfig.commit) === null || _runConfig_commit6 === void 0 ? void 0 : _runConfig_commit6.selfReflection) {
|
|
484
|
+
logger.info('š Self-reflection enabled - detailed analysis will be generated');
|
|
485
|
+
}
|
|
486
|
+
// Get list of changed files
|
|
487
|
+
const changedFilesResult = await run(`git diff --name-only ${cached ? '--cached' : ''}`);
|
|
488
|
+
const changedFilesOutput = typeof changedFilesResult === 'string' ? changedFilesResult : changedFilesResult.stdout;
|
|
489
|
+
const changedFiles = changedFilesOutput.split('\n').filter((f)=>f.trim().length > 0);
|
|
490
|
+
logger.debug('Changed files for analysis: %d files', changedFiles.length);
|
|
491
|
+
// Run agentic commit generation
|
|
492
|
+
const agenticResult = await runAgenticCommit({
|
|
493
|
+
changedFiles,
|
|
494
|
+
diffContent,
|
|
495
|
+
userDirection,
|
|
496
|
+
logContext,
|
|
497
|
+
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,
|
|
498
|
+
maxIterations: ((_runConfig_commit7 = runConfig.commit) === null || _runConfig_commit7 === void 0 ? void 0 : _runConfig_commit7.maxAgenticIterations) || 10,
|
|
499
|
+
debug: runConfig.debug,
|
|
500
|
+
debugRequestFile: getOutputPath(outputDirectory, getTimestampedRequestFilename('commit')),
|
|
501
|
+
debugResponseFile: getOutputPath(outputDirectory, getTimestampedResponseFilename('commit')),
|
|
502
|
+
storage: aiStorageAdapter,
|
|
503
|
+
logger: aiLogger,
|
|
504
|
+
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
|
|
505
|
+
});
|
|
506
|
+
const iterations = agenticResult.iterations || 0;
|
|
507
|
+
const toolCalls = agenticResult.toolCallsExecuted || 0;
|
|
508
|
+
logger.info(`š Analysis complete: ${iterations} iterations, ${toolCalls} tool calls`);
|
|
509
|
+
// Generate self-reflection output if enabled
|
|
510
|
+
if ((_runConfig_commit8 = runConfig.commit) === null || _runConfig_commit8 === void 0 ? void 0 : _runConfig_commit8.selfReflection) {
|
|
511
|
+
await generateSelfReflection(agenticResult, outputDirectory, storage, logger);
|
|
512
|
+
}
|
|
513
|
+
// Check for suggested splits
|
|
514
|
+
if (agenticResult.suggestedSplits.length > 1 && ((_runConfig_commit9 = runConfig.commit) === null || _runConfig_commit9 === void 0 ? void 0 : _runConfig_commit9.allowCommitSplitting)) {
|
|
515
|
+
logger.info('\nš AI suggests splitting this into %d commits:', agenticResult.suggestedSplits.length);
|
|
516
|
+
for(let i = 0; i < agenticResult.suggestedSplits.length; i++){
|
|
517
|
+
const split = agenticResult.suggestedSplits[i];
|
|
518
|
+
logger.info('\nCommit %d (%d files):', i + 1, split.files.length);
|
|
519
|
+
logger.info(' Files: %s', split.files.join(', '));
|
|
520
|
+
logger.info(' Rationale: %s', split.rationale);
|
|
521
|
+
logger.info(' Message: %s', split.message);
|
|
668
522
|
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
const createRetryCallback = (originalDiffContent)=>async (attempt)=>{
|
|
674
|
-
var _runConfig_commit;
|
|
675
|
-
logger.info('COMMIT_RETRY: Retrying with reduced diff size | Attempt: %d | Strategy: Truncate diff | Reason: Previous attempt failed', attempt);
|
|
676
|
-
// Progressively reduce the diff size on retries
|
|
677
|
-
const reductionFactor = Math.pow(0.5, attempt - 1); // 50% reduction per retry
|
|
678
|
-
const reducedMaxDiffBytes = Math.max(512, Math.floor(maxDiffBytes * reductionFactor));
|
|
679
|
-
logger.debug('Reducing maxDiffBytes from %d to %d for retry', maxDiffBytes, reducedMaxDiffBytes);
|
|
680
|
-
// Re-truncate the diff with smaller limits
|
|
681
|
-
const reducedDiffContent = originalDiffContent.length > reducedMaxDiffBytes ? truncateDiffByFiles(originalDiffContent, reducedMaxDiffBytes) : originalDiffContent;
|
|
682
|
-
// Rebuild the prompt with the reduced diff
|
|
683
|
-
const reducedPromptContent = {
|
|
684
|
-
diffContent: reducedDiffContent,
|
|
685
|
-
userDirection
|
|
686
|
-
};
|
|
687
|
-
const reducedPromptContext = {
|
|
688
|
-
logContext,
|
|
689
|
-
context: (_runConfig_commit = runConfig.commit) === null || _runConfig_commit === void 0 ? void 0 : _runConfig_commit.context,
|
|
690
|
-
directories: runConfig.contextDirectories
|
|
691
|
-
};
|
|
692
|
-
const retryPrompt = await createCommitPrompt(promptConfig, reducedPromptContent, reducedPromptContext);
|
|
693
|
-
const retryRequest = Formatter.create({
|
|
694
|
-
logger
|
|
695
|
-
}).formatPrompt(modelToUse, retryPrompt);
|
|
696
|
-
return retryRequest.messages;
|
|
697
|
-
};
|
|
698
|
-
rawSummary = await createCompletionWithRetry(request.messages, {
|
|
699
|
-
model: modelToUse,
|
|
700
|
-
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,
|
|
701
|
-
debug: runConfig.debug,
|
|
702
|
-
debugRequestFile: getOutputPath(runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY, getTimestampedRequestFilename('commit')),
|
|
703
|
-
debugResponseFile: getOutputPath(runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY, getTimestampedResponseFilename('commit')),
|
|
704
|
-
storage: aiStorageAdapter,
|
|
705
|
-
logger: aiLogger
|
|
706
|
-
}, createRetryCallback(diffContent));
|
|
523
|
+
logger.info('\nā ļø Commit splitting is not yet automated. Please stage and commit files separately.');
|
|
524
|
+
logger.info('Using combined message for now...\n');
|
|
525
|
+
} else if (agenticResult.suggestedSplits.length > 1) {
|
|
526
|
+
logger.debug('AI suggested %d splits but commit splitting is not enabled', agenticResult.suggestedSplits.length);
|
|
707
527
|
}
|
|
528
|
+
const rawSummary = agenticResult.commitMessage;
|
|
708
529
|
// Apply stop-context filtering to commit message
|
|
709
530
|
const filterResult = filterContent(rawSummary, runConfig.stopContext);
|
|
710
531
|
const summary = filterResult.filtered;
|
|
@@ -712,8 +533,8 @@ const executeInternal = async (runConfig)=>{
|
|
|
712
533
|
await saveCommitMessage(outputDirectory, summary, storage, logger);
|
|
713
534
|
// š”ļø Universal Safety Check: Run before ANY commit operation
|
|
714
535
|
// This protects both direct commits (--sendit) and automated commits (publish, etc.)
|
|
715
|
-
const willCreateCommit = ((
|
|
716
|
-
if (willCreateCommit && !((
|
|
536
|
+
const willCreateCommit = ((_runConfig_commit10 = runConfig.commit) === null || _runConfig_commit10 === void 0 ? void 0 : _runConfig_commit10.sendit) && hasActualChanges && cached;
|
|
537
|
+
if (willCreateCommit && !((_runConfig_commit11 = runConfig.commit) === null || _runConfig_commit11 === void 0 ? void 0 : _runConfig_commit11.skipFileCheck) && !isDryRun) {
|
|
717
538
|
logger.debug('Checking for file: dependencies before commit operation...');
|
|
718
539
|
try {
|
|
719
540
|
const fileDependencyIssues = await checkForFileDependencies(storage, process.cwd());
|
|
@@ -738,11 +559,11 @@ const executeInternal = async (runConfig)=>{
|
|
|
738
559
|
logger.warn('Warning: Could not check for file: dependencies: %s', error.message);
|
|
739
560
|
logger.warn('Proceeding with commit...');
|
|
740
561
|
}
|
|
741
|
-
} else if (((
|
|
562
|
+
} else if (((_runConfig_commit12 = runConfig.commit) === null || _runConfig_commit12 === void 0 ? void 0 : _runConfig_commit12.skipFileCheck) && willCreateCommit) {
|
|
742
563
|
logger.warn('ā ļø Skipping file: dependency check as requested');
|
|
743
564
|
}
|
|
744
565
|
// Handle interactive mode
|
|
745
|
-
if (((
|
|
566
|
+
if (((_runConfig_commit13 = runConfig.commit) === null || _runConfig_commit13 === void 0 ? void 0 : _runConfig_commit13.interactive) && !isDryRun) {
|
|
746
567
|
var _runConfig_commit19;
|
|
747
568
|
requireTTY('Interactive mode requires a terminal. Use --sendit or --dry-run instead.');
|
|
748
569
|
const interactiveResult = await handleInteractiveCommitFeedback(summary, runConfig, promptConfig, promptContext, outputDirectory, storage, diffContent, hasActualChanges, cached);
|
|
@@ -792,7 +613,7 @@ const executeInternal = async (runConfig)=>{
|
|
|
792
613
|
logger.debug('Skipping sendit logic because user chose to skip in interactive mode');
|
|
793
614
|
return summary;
|
|
794
615
|
}
|
|
795
|
-
if ((
|
|
616
|
+
if ((_runConfig_commit14 = runConfig.commit) === null || _runConfig_commit14 === void 0 ? void 0 : _runConfig_commit14.sendit) {
|
|
796
617
|
if (isDryRun) {
|
|
797
618
|
var _runConfig_commit23, _runConfig_commit24;
|
|
798
619
|
logger.info('Would commit with message: \n\n%s\n\n', summary);
|