@eldrforge/kodrdriv 1.2.133 → 1.2.135

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 (96) hide show
  1. package/.cursor/rules/no-local-dependencies.md +6 -0
  2. package/AI-GUIDE.md +4 -1
  3. package/LICENSE +1 -1
  4. package/dist/application.js +34 -42
  5. package/dist/application.js.map +1 -1
  6. package/dist/arguments.js +3 -3
  7. package/dist/arguments.js.map +1 -1
  8. package/dist/constants.js +5 -7
  9. package/dist/constants.js.map +1 -1
  10. package/dist/logging.js +4 -32
  11. package/dist/logging.js.map +1 -1
  12. package/dist/types.js +283 -0
  13. package/dist/types.js.map +1 -0
  14. package/guide/ai-system.md +3 -0
  15. package/guide/architecture.md +3 -0
  16. package/guide/commands.md +3 -0
  17. package/guide/configuration.md +3 -0
  18. package/guide/debugging.md +4 -1
  19. package/guide/development.md +3 -0
  20. package/guide/index.md +4 -1
  21. package/guide/integration.md +3 -0
  22. package/guide/monorepo.md +3 -0
  23. package/guide/quickstart.md +3 -0
  24. package/guide/testing.md +3 -0
  25. package/guide/tree-operations.md +3 -0
  26. package/guide/usage.md +3 -0
  27. package/package.json +15 -10
  28. package/DUPLICATION-CLEANUP.md +0 -104
  29. package/agentic-reflection-commit-2025-12-27T22-56-06-143Z.md +0 -50
  30. package/agentic-reflection-commit-2025-12-27T23-01-57-294Z.md +0 -50
  31. package/agentic-reflection-commit-2025-12-27T23-11-57-811Z.md +0 -50
  32. package/agentic-reflection-commit-2025-12-27T23-12-50-645Z.md +0 -50
  33. package/agentic-reflection-commit-2025-12-27T23-13-59-347Z.md +0 -52
  34. package/agentic-reflection-commit-2025-12-27T23-14-36-001Z.md +0 -50
  35. package/agentic-reflection-commit-2025-12-27T23-18-59-832Z.md +0 -50
  36. package/agentic-reflection-commit-2025-12-27T23-25-20-667Z.md +0 -62
  37. package/dist/commands/audio-commit.js +0 -152
  38. package/dist/commands/audio-commit.js.map +0 -1
  39. package/dist/commands/audio-review.js +0 -274
  40. package/dist/commands/audio-review.js.map +0 -1
  41. package/dist/commands/clean.js +0 -49
  42. package/dist/commands/clean.js.map +0 -1
  43. package/dist/commands/commit.js +0 -680
  44. package/dist/commands/commit.js.map +0 -1
  45. package/dist/commands/development.js +0 -467
  46. package/dist/commands/development.js.map +0 -1
  47. package/dist/commands/link.js +0 -646
  48. package/dist/commands/link.js.map +0 -1
  49. package/dist/commands/precommit.js +0 -99
  50. package/dist/commands/precommit.js.map +0 -1
  51. package/dist/commands/publish.js +0 -1432
  52. package/dist/commands/publish.js.map +0 -1
  53. package/dist/commands/release.js +0 -376
  54. package/dist/commands/release.js.map +0 -1
  55. package/dist/commands/review.js +0 -733
  56. package/dist/commands/review.js.map +0 -1
  57. package/dist/commands/select-audio.js +0 -46
  58. package/dist/commands/select-audio.js.map +0 -1
  59. package/dist/commands/tree.js +0 -2363
  60. package/dist/commands/tree.js.map +0 -1
  61. package/dist/commands/unlink.js +0 -537
  62. package/dist/commands/unlink.js.map +0 -1
  63. package/dist/commands/updates.js +0 -211
  64. package/dist/commands/updates.js.map +0 -1
  65. package/dist/commands/versions.js +0 -221
  66. package/dist/commands/versions.js.map +0 -1
  67. package/dist/content/diff.js +0 -346
  68. package/dist/content/diff.js.map +0 -1
  69. package/dist/content/files.js +0 -190
  70. package/dist/content/files.js.map +0 -1
  71. package/dist/content/log.js +0 -72
  72. package/dist/content/log.js.map +0 -1
  73. package/dist/util/aiAdapter.js +0 -28
  74. package/dist/util/aiAdapter.js.map +0 -1
  75. package/dist/util/fileLock.js +0 -241
  76. package/dist/util/fileLock.js.map +0 -1
  77. package/dist/util/general.js +0 -379
  78. package/dist/util/general.js.map +0 -1
  79. package/dist/util/gitMutex.js +0 -161
  80. package/dist/util/gitMutex.js.map +0 -1
  81. package/dist/util/interactive.js +0 -32
  82. package/dist/util/interactive.js.map +0 -1
  83. package/dist/util/loggerAdapter.js +0 -41
  84. package/dist/util/loggerAdapter.js.map +0 -1
  85. package/dist/util/performance.js +0 -134
  86. package/dist/util/performance.js.map +0 -1
  87. package/dist/util/precommitOptimizations.js +0 -310
  88. package/dist/util/precommitOptimizations.js.map +0 -1
  89. package/dist/util/stopContext.js +0 -146
  90. package/dist/util/stopContext.js.map +0 -1
  91. package/dist/util/storageAdapter.js +0 -31
  92. package/dist/util/storageAdapter.js.map +0 -1
  93. package/dist/util/validation.js +0 -45
  94. package/dist/util/validation.js.map +0 -1
  95. package/dist/utils/branchState.js +0 -700
  96. package/dist/utils/branchState.js.map +0 -1
@@ -1,680 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Formatter } from '@riotprompt/riotprompt';
3
- import 'dotenv/config';
4
- import shellescape from 'shell-escape';
5
- import { DEFAULT_MAX_DIFF_BYTES, DEFAULT_EXCLUDED_PATTERNS, DEFAULT_OUTPUT_DIRECTORY } from '../constants.js';
6
- import { create, hasCriticalExcludedChanges, getMinimalExcludedPatterns, hasStagedChanges } from '../content/diff.js';
7
- import { create as create$2 } from '../content/log.js';
8
- import { create as create$1 } from '../content/files.js';
9
- import { ValidationError, ExternalDependencyError, CommandError, createStorage, checkForFileDependencies, logFileDependencyWarning, logFileDependencySuggestions } from '@eldrforge/shared';
10
- import { getDryRunLogger } from '../logging.js';
11
- import { run, validateString, safeJsonParse, validatePackageJson } from '@eldrforge/git-tools';
12
- import { sanitizeDirection } from '../util/validation.js';
13
- import { filterContent } from '../util/stopContext.js';
14
- import { getOutputPath, getTimestampedResponseFilename, getTimestampedRequestFilename, getTimestampedCommitFilename } from '../util/general.js';
15
- import { getRecentClosedIssuesForCommit } from '@eldrforge/github-tools';
16
- import { runAgenticCommit, requireTTY, generateReflectionReport, getUserChoice, STANDARD_CHOICES, getLLMFeedbackInEditor, editContentInEditor, createCompletionWithRetry, createCommitPrompt } from '@eldrforge/ai-service';
17
- import { improveContentWithLLM } from '../util/interactive.js';
18
- import { toAIConfig } from '../util/aiAdapter.js';
19
- import { createStorageAdapter } from '../util/storageAdapter.js';
20
- import { createLoggerAdapter } from '../util/loggerAdapter.js';
21
-
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
41
- async function generateSelfReflection(agenticResult, outputDirectory, storage, logger) {
42
- try {
43
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('.')[0];
44
- const reflectionPath = getOutputPath(outputDirectory, `agentic-reflection-commit-${timestamp}.md`);
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');
58
- logger.info('');
59
- logger.info('═'.repeat(80));
60
- logger.info('📊 SELF-REFLECTION REPORT GENERATED');
61
- logger.info('═'.repeat(80));
62
- logger.info('');
63
- logger.info('📁 Location: %s', reflectionPath);
64
- logger.info('');
65
- logger.info('📈 Report Summary:');
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`);
72
- logger.info('');
73
- logger.info('💡 Use this report to:');
74
- logger.info(' • Understand which tools were most effective');
75
- logger.info(' • Identify performance bottlenecks');
76
- logger.info(' • Review the complete agentic conversation');
77
- logger.info(' • Improve tool implementation based on metrics');
78
- logger.info('');
79
- logger.info('═'.repeat(80));
80
- } catch (error) {
81
- logger.warn('Failed to generate self-reflection output: %s', error.message);
82
- logger.debug('Self-reflection error details:', error);
83
- }
84
- }
85
- // Helper function to get current version from package.json
86
- async function getCurrentVersion(storage) {
87
- try {
88
- const packageJsonContents = await storage.readFile('package.json', 'utf-8');
89
- const packageJson = safeJsonParse(packageJsonContents, 'package.json');
90
- const validated = validatePackageJson(packageJson, 'package.json');
91
- return validated.version;
92
- } catch {
93
- // Return undefined if we can't read the version (not a critical failure)
94
- return undefined;
95
- }
96
- }
97
- // Helper function to edit commit message using editor
98
- async function editCommitMessageInteractively(commitMessage) {
99
- const templateLines = [
100
- '# Edit your commit message below. Lines starting with "#" will be ignored.',
101
- '# Save and close the editor when you are done.'
102
- ];
103
- const result = await editContentInEditor(commitMessage, templateLines, '.txt');
104
- return result.content;
105
- }
106
- // Helper function to improve commit message using LLM
107
- async function improveCommitMessageWithLLM(commitMessage, runConfig, promptConfig, promptContext, outputDirectory, diffContent) {
108
- // Get user feedback on what to improve using the editor
109
- const userFeedback = await getLLMFeedbackInEditor('commit message', commitMessage);
110
- // Create AI config from kodrdriv config
111
- const aiConfig = toAIConfig(runConfig);
112
- const aiStorageAdapter = createStorageAdapter(outputDirectory);
113
- const aiLogger = createLoggerAdapter(false);
114
- const improvementConfig = {
115
- contentType: 'commit message',
116
- createImprovedPrompt: async (promptConfig, currentMessage, promptContext)=>{
117
- var _aiConfig_commands_commit, _aiConfig_commands;
118
- const improvementPromptContent = {
119
- diffContent: diffContent,
120
- userDirection: `Please improve this commit message based on the user's feedback: "${userFeedback}".
121
-
122
- Current commit message: "${currentMessage}"
123
-
124
- Please revise the commit message according to the user's feedback while maintaining accuracy and following conventional commit standards if appropriate.`
125
- };
126
- const prompt = await createCommitPrompt(promptConfig, improvementPromptContent, promptContext);
127
- // Format the prompt into a proper request with messages
128
- 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';
129
- return Formatter.create({
130
- logger: getDryRunLogger(false)
131
- }).formatPrompt(modelToUse, prompt);
132
- },
133
- callLLM: async (request, runConfig, outputDirectory)=>{
134
- var _aiConfig_commands_commit, _aiConfig_commands, _aiConfig_commands_commit1, _aiConfig_commands1;
135
- return await createCompletionWithRetry(request.messages, {
136
- 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,
137
- 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,
138
- debug: runConfig.debug,
139
- debugRequestFile: getOutputPath(outputDirectory, getTimestampedRequestFilename('commit-improve')),
140
- debugResponseFile: getOutputPath(outputDirectory, getTimestampedResponseFilename('commit-improve')),
141
- storage: aiStorageAdapter,
142
- logger: aiLogger
143
- });
144
- }
145
- };
146
- return await improveContentWithLLM(commitMessage, runConfig, promptConfig, promptContext, outputDirectory, improvementConfig);
147
- }
148
- // Interactive feedback loop for commit message
149
- async function handleInteractiveCommitFeedback(commitMessage, runConfig, promptConfig, promptContext, outputDirectory, storage, diffContent, hasActualChanges, cached) {
150
- var _runConfig_commit, _runConfig_commit1;
151
- const logger = getDryRunLogger(false);
152
- let currentMessage = commitMessage;
153
- // Determine what the confirm action will do based on configuration
154
- const senditEnabled = (_runConfig_commit = runConfig.commit) === null || _runConfig_commit === void 0 ? void 0 : _runConfig_commit.sendit;
155
- const willActuallyCommit = senditEnabled && hasActualChanges && cached;
156
- // Create dynamic confirm choice based on configuration
157
- const isAmendMode = (_runConfig_commit1 = runConfig.commit) === null || _runConfig_commit1 === void 0 ? void 0 : _runConfig_commit1.amend;
158
- const confirmChoice = willActuallyCommit ? {
159
- key: 'c',
160
- label: isAmendMode ? 'Amend last commit with this message (sendit enabled)' : 'Commit changes with this message (sendit enabled)'
161
- } : {
162
- key: 'c',
163
- label: 'Accept message (you will need to commit manually)'
164
- };
165
- while(true){
166
- // Display the current commit message
167
- logger.info('\n📝 Generated Commit Message:');
168
- logger.info('─'.repeat(50));
169
- logger.info(currentMessage);
170
- logger.info('─'.repeat(50));
171
- // Show configuration status
172
- if (senditEnabled) {
173
- if (willActuallyCommit) {
174
- logger.info('\nSENDIT_MODE_ACTIVE: SendIt mode enabled | Action: Commit choice will execute git commit automatically | Staged Changes: Available');
175
- } else {
176
- logger.info('\nSENDIT_MODE_NO_CHANGES: SendIt mode configured but no staged changes | Action: Only message save available | Staged Changes: None');
177
- }
178
- } else {
179
- logger.info('\nSENDIT_MODE_INACTIVE: SendIt mode not active | Action: Accept choice will only save message | Commit: Manual');
180
- }
181
- // Get user choice
182
- const userChoice = await getUserChoice('\nWhat would you like to do with this commit message?', [
183
- confirmChoice,
184
- STANDARD_CHOICES.EDIT,
185
- STANDARD_CHOICES.SKIP,
186
- STANDARD_CHOICES.IMPROVE
187
- ], {
188
- nonTtyErrorSuggestions: [
189
- 'Use --sendit flag to auto-commit without review'
190
- ]
191
- });
192
- switch(userChoice){
193
- case 'c':
194
- return {
195
- action: 'commit',
196
- finalMessage: currentMessage
197
- };
198
- case 'e':
199
- try {
200
- currentMessage = await editCommitMessageInteractively(currentMessage);
201
- } catch (error) {
202
- logger.error(`Failed to edit commit message: ${error.message}`);
203
- // Continue the loop to show options again
204
- }
205
- break;
206
- case 's':
207
- return {
208
- action: 'skip',
209
- finalMessage: currentMessage
210
- };
211
- case 'i':
212
- try {
213
- currentMessage = await improveCommitMessageWithLLM(currentMessage, runConfig, promptConfig, promptContext, outputDirectory, diffContent);
214
- } catch (error) {
215
- logger.error(`Failed to improve commit message: ${error.message}`);
216
- // Continue the loop to show options again
217
- }
218
- break;
219
- }
220
- }
221
- }
222
- // Helper function to check if there are any commits in the repository
223
- const hasCommits = async ()=>{
224
- try {
225
- await run('git rev-parse HEAD');
226
- return true;
227
- } catch {
228
- // No commits found or not a git repository
229
- return false;
230
- }
231
- };
232
- // Helper function to push the commit
233
- const pushCommit = async (pushConfig, logger, isDryRun)=>{
234
- if (!pushConfig) {
235
- return; // No push requested
236
- }
237
- // Determine the remote to push to
238
- let remote = 'origin';
239
- if (typeof pushConfig === 'string') {
240
- remote = pushConfig;
241
- }
242
- const pushCommand = `git push ${remote}`;
243
- if (isDryRun) {
244
- logger.info('Would push to %s with: %s', remote, pushCommand);
245
- } else {
246
- logger.info('🚀 Pushing to %s...', remote);
247
- try {
248
- await run(pushCommand);
249
- logger.info('✅ Push successful!');
250
- } catch (error) {
251
- logger.error('Failed to push to %s: %s', remote, error.message);
252
- throw new ExternalDependencyError(`Failed to push to ${remote}`, 'git', error);
253
- }
254
- }
255
- };
256
- // Simplified cached determination with single check
257
- const determineCachedState = async (config)=>{
258
- var _config_commit, _config_commit1, _config_commit2;
259
- // If amend is used, we use staged changes (since we're amending the last commit)
260
- if ((_config_commit = config.commit) === null || _config_commit === void 0 ? void 0 : _config_commit.amend) {
261
- // For amend mode, check that there's a previous commit to amend
262
- const hasAnyCommits = await hasCommits();
263
- if (!hasAnyCommits) {
264
- throw new ValidationError('Cannot use --amend: no commits found in repository. Create an initial commit first.');
265
- }
266
- return true;
267
- }
268
- // If add is used, we always look at staged changes after add
269
- if ((_config_commit1 = config.commit) === null || _config_commit1 === void 0 ? void 0 : _config_commit1.add) {
270
- return true;
271
- }
272
- // If explicitly set, use that value
273
- if (((_config_commit2 = config.commit) === null || _config_commit2 === void 0 ? void 0 : _config_commit2.cached) !== undefined) {
274
- return config.commit.cached;
275
- }
276
- // Otherwise, check if there are staged changes
277
- return await hasStagedChanges();
278
- };
279
- // Single validation of sendit + cached state
280
- const validateSenditState = (config, cached, isDryRun, logger)=>{
281
- var _config_commit;
282
- if (((_config_commit = config.commit) === null || _config_commit === void 0 ? void 0 : _config_commit.sendit) && !cached && !isDryRun) {
283
- const message = 'SendIt mode enabled, but no changes to commit.';
284
- logger.warn(message);
285
- return false; // Return false to indicate no changes to commit
286
- }
287
- return true; // Return true to indicate we can proceed
288
- };
289
- // Better file save handling with fallbacks
290
- const saveCommitMessage = async (outputDirectory, summary, storage, logger)=>{
291
- const timestampedFilename = getTimestampedCommitFilename();
292
- const primaryPath = getOutputPath(outputDirectory, timestampedFilename);
293
- try {
294
- await storage.writeFile(primaryPath, summary, 'utf-8');
295
- logger.debug('Saved timestamped commit message: %s', primaryPath);
296
- return; // Success, no fallback needed
297
- } catch (error) {
298
- logger.warn('Failed to save commit message to primary location (%s): %s', primaryPath, error.message);
299
- logger.debug('Primary save error details:', error);
300
- // First fallback: try output directory root (in case subdirectory has issues)
301
- try {
302
- const outputRootPath = getOutputPath('output', timestampedFilename);
303
- await storage.writeFile(outputRootPath, summary, 'utf-8');
304
- logger.info('COMMIT_MESSAGE_SAVED_FALLBACK: Saved commit message to fallback location | Path: %s | Purpose: Preserve message for later use', outputRootPath);
305
- return;
306
- } catch (outputError) {
307
- logger.warn('Failed to save to output directory fallback: %s', outputError.message);
308
- }
309
- // Last resort fallback: save to current directory (this creates the clutter!)
310
- try {
311
- const fallbackPath = `commit-message-${Date.now()}.txt`;
312
- await storage.writeFile(fallbackPath, summary, 'utf-8');
313
- logger.warn('⚠️ Saved commit message to current directory as last resort: %s', fallbackPath);
314
- logger.warn('⚠️ This file should be moved to the output directory and may clutter your workspace');
315
- } catch (fallbackError) {
316
- logger.error('Failed to save commit message anywhere: %s', fallbackError.message);
317
- logger.error('Commit message will only be available in console output');
318
- // Continue execution - commit message is still returned
319
- }
320
- }
321
- };
322
- const executeInternal = async (runConfig)=>{
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;
324
- const isDryRun = runConfig.dryRun || false;
325
- const logger = getDryRunLogger(isDryRun);
326
- // Track if user explicitly chose to skip in interactive mode
327
- let userSkippedCommit = false;
328
- if ((_runConfig_commit = runConfig.commit) === null || _runConfig_commit === void 0 ? void 0 : _runConfig_commit.add) {
329
- if (isDryRun) {
330
- logger.info('GIT_ADD_DRY_RUN: Would stage all changes | Mode: dry-run | Command: git add -A');
331
- } else {
332
- logger.info('GIT_ADD_STAGING: Adding all changes to index | Command: git add -A | Scope: all files | Purpose: Stage for commit');
333
- await run('git add -A');
334
- logger.info('GIT_ADD_SUCCESS: Successfully staged all changes | Command: git add -A | Status: completed');
335
- }
336
- }
337
- // Determine cached state with single, clear logic
338
- const cached = await determineCachedState(runConfig);
339
- // Validate sendit state early - now returns boolean instead of throwing
340
- validateSenditState(runConfig, cached, isDryRun, logger);
341
- let diffContent = '';
342
- var _runConfig_commit_maxDiffBytes;
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;
344
- var _runConfig_excludedPatterns;
345
- const options = {
346
- cached,
347
- excludedPatterns: (_runConfig_excludedPatterns = runConfig.excludedPatterns) !== null && _runConfig_excludedPatterns !== void 0 ? _runConfig_excludedPatterns : DEFAULT_EXCLUDED_PATTERNS,
348
- maxDiffBytes
349
- };
350
- const diff = await create(options);
351
- diffContent = await diff.get();
352
- // Check if there are actually any changes in the diff
353
- let hasActualChanges = diffContent.trim().length > 0;
354
- // If no changes found with current patterns, check for critical excluded files
355
- if (!hasActualChanges) {
356
- const criticalChanges = await hasCriticalExcludedChanges();
357
- if (criticalChanges.hasChanges) {
358
- var _runConfig_commit15;
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(', '));
360
- if (((_runConfig_commit15 = runConfig.commit) === null || _runConfig_commit15 === void 0 ? void 0 : _runConfig_commit15.sendit) && !isDryRun) {
361
- // In sendit mode, automatically include critical files
362
- logger.info('SENDIT_INCLUDING_CRITICAL: SendIt mode including critical files in diff | Purpose: Ensure all important changes are captured');
363
- var _runConfig_excludedPatterns1;
364
- const minimalPatterns = getMinimalExcludedPatterns((_runConfig_excludedPatterns1 = runConfig.excludedPatterns) !== null && _runConfig_excludedPatterns1 !== void 0 ? _runConfig_excludedPatterns1 : DEFAULT_EXCLUDED_PATTERNS);
365
- const updatedOptions = {
366
- ...options,
367
- excludedPatterns: minimalPatterns
368
- };
369
- const updatedDiff = await create(updatedOptions);
370
- diffContent = await updatedDiff.get();
371
- if (diffContent.trim().length > 0) {
372
- logger.info('CRITICAL_FILES_INCLUDED: Successfully added critical files to diff | Status: ready for commit message generation');
373
- // Update hasActualChanges since we now have content after including critical files
374
- hasActualChanges = true;
375
- } else {
376
- logger.warn('No changes detected even after including critical files.');
377
- return 'No changes to commit.';
378
- }
379
- } else {
380
- // In non-sendit mode, suggest including the files
381
- logger.warn('Consider including these files by using:');
382
- var _runConfig_excludedPatterns2;
383
- logger.warn(' kodrdriv commit --excluded-paths %s', ((_runConfig_excludedPatterns2 = runConfig.excludedPatterns) !== null && _runConfig_excludedPatterns2 !== void 0 ? _runConfig_excludedPatterns2 : DEFAULT_EXCLUDED_PATTERNS).filter((p)=>!criticalChanges.files.some((f)=>p.includes(f.split('/').pop() || ''))).map((p)=>`"${p}"`).join(' '));
384
- logger.warn('Or run with --sendit to automatically include critical files.');
385
- if (!isDryRun) {
386
- return 'No changes to commit. Use suggestions above to include critical files.';
387
- } else {
388
- logger.info('Generating commit message template for future use...');
389
- }
390
- }
391
- } else {
392
- var _runConfig_commit16;
393
- // No changes at all - try fallback to file content for new repositories
394
- logger.info('NO_CHANGES_DETECTED: No changes found in working directory | Status: clean | Action: Nothing to commit');
395
- if (((_runConfig_commit16 = runConfig.commit) === null || _runConfig_commit16 === void 0 ? void 0 : _runConfig_commit16.sendit) && !isDryRun) {
396
- logger.warn('No changes detected to commit. Skipping commit operation.');
397
- return 'No changes to commit.';
398
- } else {
399
- logger.info('NO_DIFF_FALLBACK: No diff content available | Action: Attempting to generate commit message from file content | Strategy: fallback');
400
- var _runConfig_excludedPatterns3;
401
- // Create file content collector as fallback
402
- const fileOptions = {
403
- excludedPatterns: (_runConfig_excludedPatterns3 = runConfig.excludedPatterns) !== null && _runConfig_excludedPatterns3 !== void 0 ? _runConfig_excludedPatterns3 : DEFAULT_EXCLUDED_PATTERNS,
404
- maxTotalBytes: maxDiffBytes * 5,
405
- workingDirectory: process.cwd()
406
- };
407
- const files = await create$1(fileOptions);
408
- const fileContent = await files.get();
409
- if (fileContent && fileContent.trim().length > 0) {
410
- logger.info('FILE_CONTENT_USING: Using file content for commit message generation | Content Length: %d characters | Source: file content', fileContent.length);
411
- diffContent = fileContent;
412
- hasActualChanges = true; // We have content to work with
413
- } else {
414
- var _runConfig_commit17;
415
- if ((_runConfig_commit17 = runConfig.commit) === null || _runConfig_commit17 === void 0 ? void 0 : _runConfig_commit17.sendit) {
416
- logger.info('COMMIT_SKIPPED: Skipping commit operation | Reason: No changes detected | Action: None');
417
- return 'No changes to commit.';
418
- } else {
419
- logger.info('COMMIT_TEMPLATE_GENERATING: Creating commit message template for future use | Reason: No changes | Purpose: Provide template');
420
- }
421
- }
422
- }
423
- }
424
- }
425
- const logOptions = {
426
- limit: (_runConfig_commit2 = runConfig.commit) === null || _runConfig_commit2 === void 0 ? void 0 : _runConfig_commit2.messageLimit
427
- };
428
- const log = await create$2(logOptions);
429
- const logContext = await log.get();
430
- // Always ensure output directory exists for request/response files and GitHub issues lookup
431
- const outputDirectory = runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY;
432
- const storage = createStorage();
433
- await storage.ensureDirectory(outputDirectory);
434
- // Get GitHub issues context for large commits [[memory:5887795]]
435
- let githubIssuesContext = '';
436
- try {
437
- const currentVersion = await getCurrentVersion(storage);
438
- if (currentVersion) {
439
- logger.debug(`Found current version: ${currentVersion}, fetching related GitHub issues...`);
440
- githubIssuesContext = await getRecentClosedIssuesForCommit(currentVersion, 10);
441
- if (githubIssuesContext) {
442
- logger.debug(`Fetched GitHub issues context (${githubIssuesContext.length} characters)`);
443
- } else {
444
- logger.debug('No relevant GitHub issues found for commit context');
445
- }
446
- } else {
447
- logger.debug('Could not determine current version, fetching recent issues without milestone filtering...');
448
- githubIssuesContext = await getRecentClosedIssuesForCommit(undefined, 10);
449
- if (githubIssuesContext) {
450
- logger.debug(`Fetched general GitHub issues context (${githubIssuesContext.length} characters)`);
451
- }
452
- }
453
- } catch (error) {
454
- logger.debug(`Failed to fetch GitHub issues for commit context: ${error.message}`);
455
- // Continue without GitHub context - this shouldn't block commit generation
456
- }
457
- const promptConfig = {
458
- overridePaths: runConfig.discoveredConfigDirs || [],
459
- overrides: runConfig.overrides || false
460
- };
461
- const userDirection = sanitizeDirection((_runConfig_commit3 = runConfig.commit) === null || _runConfig_commit3 === void 0 ? void 0 : _runConfig_commit3.direction);
462
- if (userDirection) {
463
- logger.debug('Using user direction: %s', userDirection);
464
- }
465
- // Create adapters for ai-service
466
- const aiConfig = toAIConfig(runConfig);
467
- const aiStorageAdapter = createStorageAdapter(outputDirectory);
468
- const aiLogger = createLoggerAdapter(isDryRun);
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
477
- const promptContext = {
478
- logContext,
479
- context: combinedContext || undefined,
480
- directories: runConfig.contextDirectories
481
- };
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);
522
- }
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);
527
- }
528
- const rawSummary = agenticResult.commitMessage;
529
- // Apply stop-context filtering to commit message
530
- const filterResult = filterContent(rawSummary, runConfig.stopContext);
531
- const summary = filterResult.filtered;
532
- // Save timestamped copy of commit message with better error handling
533
- await saveCommitMessage(outputDirectory, summary, storage, logger);
534
- // 🛡️ Universal Safety Check: Run before ANY commit operation
535
- // This protects both direct commits (--sendit) and automated commits (publish, etc.)
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) {
538
- logger.debug('Checking for file: dependencies before commit operation...');
539
- try {
540
- const fileDependencyIssues = await checkForFileDependencies(storage, process.cwd());
541
- if (fileDependencyIssues.length > 0) {
542
- var _runConfig_commit18;
543
- logger.error('🚫 COMMIT BLOCKED: Found file: dependencies that should not be committed!');
544
- logger.error('');
545
- logFileDependencyWarning(fileDependencyIssues, 'commit');
546
- logFileDependencySuggestions(true);
547
- logger.error('Generated commit message was:');
548
- logger.error('%s', summary);
549
- logger.error('');
550
- if ((_runConfig_commit18 = runConfig.commit) === null || _runConfig_commit18 === void 0 ? void 0 : _runConfig_commit18.sendit) {
551
- logger.error('To bypass this check, use: kodrdriv commit --skip-file-check --sendit');
552
- } else {
553
- logger.error('To bypass this check, add skipFileCheck: true to your commit configuration');
554
- }
555
- throw new ValidationError('Found file: dependencies that should not be committed. Use --skip-file-check to bypass.');
556
- }
557
- logger.debug('✅ No file: dependencies found, proceeding with commit');
558
- } catch (error) {
559
- logger.warn('Warning: Could not check for file: dependencies: %s', error.message);
560
- logger.warn('Proceeding with commit...');
561
- }
562
- } else if (((_runConfig_commit12 = runConfig.commit) === null || _runConfig_commit12 === void 0 ? void 0 : _runConfig_commit12.skipFileCheck) && willCreateCommit) {
563
- logger.warn('⚠️ Skipping file: dependency check as requested');
564
- }
565
- // Handle interactive mode
566
- if (((_runConfig_commit13 = runConfig.commit) === null || _runConfig_commit13 === void 0 ? void 0 : _runConfig_commit13.interactive) && !isDryRun) {
567
- var _runConfig_commit19;
568
- requireTTY('Interactive mode requires a terminal. Use --sendit or --dry-run instead.');
569
- const interactiveResult = await handleInteractiveCommitFeedback(summary, runConfig, promptConfig, promptContext, outputDirectory, storage, diffContent, hasActualChanges, cached);
570
- if (interactiveResult.action === 'skip') {
571
- logger.info('COMMIT_ABORTED: User aborted commit operation | Reason: User choice | Action: No commit performed');
572
- logger.info('COMMIT_NO_ACTION: No commit will be performed | Status: aborted | Next: User can retry or modify changes');
573
- userSkippedCommit = true;
574
- return interactiveResult.finalMessage;
575
- }
576
- // User chose to commit - check if sendit is enabled to determine what action to take
577
- const senditEnabled = (_runConfig_commit19 = runConfig.commit) === null || _runConfig_commit19 === void 0 ? void 0 : _runConfig_commit19.sendit;
578
- const willActuallyCommit = senditEnabled && hasActualChanges && cached;
579
- if (willActuallyCommit) {
580
- var _runConfig_commit20;
581
- const commitAction = ((_runConfig_commit20 = runConfig.commit) === null || _runConfig_commit20 === void 0 ? void 0 : _runConfig_commit20.amend) ? 'amending last commit' : 'committing';
582
- 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);
583
- try {
584
- var _runConfig_commit21, _runConfig_commit22;
585
- const validatedSummary = validateString(interactiveResult.finalMessage, 'commit summary');
586
- const escapedSummary = shellescape([
587
- validatedSummary
588
- ]);
589
- 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}`;
590
- await run(commitCommand);
591
- logger.info('COMMIT_SUCCESS: Commit operation completed successfully | Status: committed | Action: Changes saved to repository');
592
- // Push if requested
593
- await pushCommit((_runConfig_commit22 = runConfig.commit) === null || _runConfig_commit22 === void 0 ? void 0 : _runConfig_commit22.push, logger, isDryRun);
594
- } catch (error) {
595
- logger.error('Failed to commit:', error);
596
- throw new ExternalDependencyError('Failed to create commit', 'git', error);
597
- }
598
- } else if (senditEnabled && (!hasActualChanges || !cached)) {
599
- logger.info('📝 SendIt enabled but no staged changes available. Final message saved: \n\n%s\n\n', interactiveResult.finalMessage);
600
- if (!hasActualChanges) {
601
- logger.info('💡 No changes detected to commit');
602
- } else if (!cached) {
603
- logger.info('💡 No staged changes found. Use "git add" to stage changes or configure add: true in commit settings');
604
- }
605
- } else {
606
- logger.info('📝 Message accepted (SendIt not enabled). Use this commit message manually: \n\n%s\n\n', interactiveResult.finalMessage);
607
- logger.info('💡 To automatically commit, add sendit: true to your commit configuration');
608
- }
609
- return interactiveResult.finalMessage;
610
- }
611
- // Safety check: Never commit if user explicitly skipped in interactive mode
612
- if (userSkippedCommit) {
613
- logger.debug('Skipping sendit logic because user chose to skip in interactive mode');
614
- return summary;
615
- }
616
- if ((_runConfig_commit14 = runConfig.commit) === null || _runConfig_commit14 === void 0 ? void 0 : _runConfig_commit14.sendit) {
617
- if (isDryRun) {
618
- var _runConfig_commit23, _runConfig_commit24;
619
- logger.info('Would commit with message: \n\n%s\n\n', summary);
620
- 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>';
621
- logger.info('Would execute: %s', commitAction);
622
- // Show push command in dry run if requested
623
- if ((_runConfig_commit24 = runConfig.commit) === null || _runConfig_commit24 === void 0 ? void 0 : _runConfig_commit24.push) {
624
- const remote = typeof runConfig.commit.push === 'string' ? runConfig.commit.push : 'origin';
625
- logger.info('Would push to %s with: git push %s', remote, remote);
626
- }
627
- } else if (hasActualChanges && cached) {
628
- var _runConfig_commit25;
629
- const commitAction = ((_runConfig_commit25 = runConfig.commit) === null || _runConfig_commit25 === void 0 ? void 0 : _runConfig_commit25.amend) ? 'amending commit' : 'committing';
630
- logger.info('SendIt mode enabled. %s with message: \n\n%s\n\n', commitAction.charAt(0).toUpperCase() + commitAction.slice(1), summary);
631
- try {
632
- var _runConfig_commit26, _runConfig_commit27;
633
- const validatedSummary = validateString(summary, 'commit summary');
634
- const escapedSummary = shellescape([
635
- validatedSummary
636
- ]);
637
- 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}`;
638
- await run(commitCommand);
639
- logger.info('Commit successful!');
640
- // Push if requested
641
- await pushCommit((_runConfig_commit27 = runConfig.commit) === null || _runConfig_commit27 === void 0 ? void 0 : _runConfig_commit27.push, logger, isDryRun);
642
- } catch (error) {
643
- logger.error('Failed to commit:', error);
644
- throw new ExternalDependencyError('Failed to create commit', 'git', error);
645
- }
646
- } else {
647
- logger.info('SendIt mode enabled, but no changes to commit. Generated message: \n\n%s\n\n', summary);
648
- }
649
- } else if (isDryRun) {
650
- logger.info('Generated commit message: \n\n%s\n\n', summary);
651
- } else {
652
- // Default behavior when neither --interactive nor --sendit is specified
653
- logger.info('Generated commit message: \n\n%s\n\n', summary);
654
- }
655
- return summary;
656
- };
657
- const execute = async (runConfig)=>{
658
- try {
659
- return await executeInternal(runConfig);
660
- } catch (error) {
661
- // Import getLogger for error handling
662
- const { getLogger } = await import('../logging.js');
663
- const standardLogger = getLogger();
664
- if (error instanceof ValidationError || error instanceof ExternalDependencyError || error instanceof CommandError) {
665
- standardLogger.error(`commit failed: ${error.message}`);
666
- if (error.cause && typeof error.cause === 'object' && 'message' in error.cause) {
667
- standardLogger.debug(`Caused by: ${error.cause.message}`);
668
- } else if (error.cause) {
669
- standardLogger.debug(`Caused by: ${error.cause}`);
670
- }
671
- throw error;
672
- }
673
- // Unexpected errors
674
- standardLogger.error(`commit encountered unexpected error: ${error.message}`);
675
- throw error;
676
- }
677
- };
678
-
679
- export { execute };
680
- //# sourceMappingURL=commit.js.map