@eldrforge/kodrdriv 1.2.132 → 1.2.134

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 (50) hide show
  1. package/AI-GUIDE.md +837 -0
  2. package/LICENSE +1 -1
  3. package/README.md +35 -6
  4. package/dist/application.js +2 -0
  5. package/dist/application.js.map +1 -1
  6. package/dist/arguments.js +54 -21
  7. package/dist/arguments.js.map +1 -1
  8. package/dist/commands/audio-commit.js +2 -1
  9. package/dist/commands/audio-commit.js.map +1 -1
  10. package/dist/commands/audio-review.js +4 -2
  11. package/dist/commands/audio-review.js.map +1 -1
  12. package/dist/commands/commit.js +91 -133
  13. package/dist/commands/commit.js.map +1 -1
  14. package/dist/commands/publish.js +3 -6
  15. package/dist/commands/publish.js.map +1 -1
  16. package/dist/commands/release.js +62 -139
  17. package/dist/commands/release.js.map +1 -1
  18. package/dist/commands/review.js +1 -1
  19. package/dist/commands/review.js.map +1 -1
  20. package/dist/commands/tree.js +29 -33
  21. package/dist/commands/tree.js.map +1 -1
  22. package/dist/constants.js +3 -1
  23. package/dist/constants.js.map +1 -1
  24. package/dist/types.js +282 -0
  25. package/dist/types.js.map +1 -0
  26. package/dist/util/storageAdapter.js +9 -2
  27. package/dist/util/storageAdapter.js.map +1 -1
  28. package/guide/ai-system.md +522 -0
  29. package/guide/architecture.md +349 -0
  30. package/guide/commands.md +383 -0
  31. package/guide/configuration.md +516 -0
  32. package/guide/debugging.md +587 -0
  33. package/guide/development.md +632 -0
  34. package/guide/index.md +215 -0
  35. package/guide/integration.md +510 -0
  36. package/guide/monorepo.md +533 -0
  37. package/guide/quickstart.md +226 -0
  38. package/guide/testing.md +463 -0
  39. package/guide/tree-operations.md +621 -0
  40. package/guide/usage.md +578 -0
  41. package/package.json +10 -10
  42. package/DUPLICATION-CLEANUP.md +0 -104
  43. package/agentic-reflection-commit-2025-12-27T22-56-06-143Z.md +0 -50
  44. package/agentic-reflection-commit-2025-12-27T23-01-57-294Z.md +0 -50
  45. package/agentic-reflection-commit-2025-12-27T23-11-57-811Z.md +0 -50
  46. package/agentic-reflection-commit-2025-12-27T23-12-50-645Z.md +0 -50
  47. package/agentic-reflection-commit-2025-12-27T23-13-59-347Z.md +0 -52
  48. package/agentic-reflection-commit-2025-12-27T23-14-36-001Z.md +0 -50
  49. package/agentic-reflection-commit-2025-12-27T23-18-59-832Z.md +0 -50
  50. package/agentic-reflection-commit-2025-12-27T23-25-20-667Z.md +0 -62
@@ -4,8 +4,8 @@ import 'dotenv/config';
4
4
  import { DEFAULT_TO_COMMIT_ALIAS, DEFAULT_MAX_DIFF_BYTES, DEFAULT_EXCLUDED_PATTERNS, DEFAULT_OUTPUT_DIRECTORY } from '../constants.js';
5
5
  import { getCurrentBranch, getDefaultFromRef, safeJsonParse } from '@eldrforge/git-tools';
6
6
  import { create } from '../content/log.js';
7
- import { create as create$1, truncateDiffByFiles } from '../content/diff.js';
8
- import { runAgenticRelease, requireTTY, createReleasePrompt, createCompletionWithRetry, generateReflectionReport, getUserChoice, STANDARD_CHOICES, getLLMFeedbackInEditor, editContentInEditor } from '@eldrforge/ai-service';
7
+ import { create as create$1 } from '../content/diff.js';
8
+ import { runAgenticRelease, requireTTY, generateReflectionReport, getUserChoice, STANDARD_CHOICES, getLLMFeedbackInEditor, editContentInEditor, createCompletionWithRetry, createReleasePrompt } from '@eldrforge/ai-service';
9
9
  import { improveContentWithLLM } from '../util/interactive.js';
10
10
  import { toAIConfig } from '../util/aiAdapter.js';
11
11
  import { createStorageAdapter } from '../util/storageAdapter.js';
@@ -17,6 +17,24 @@ import { validateReleaseSummary } from '../util/validation.js';
17
17
  import * as GitHub from '@eldrforge/github-tools';
18
18
  import { filterContent } from '../util/stopContext.js';
19
19
 
20
+ // Helper function to read context files
21
+ async function readContextFiles(contextFiles, logger) {
22
+ if (!contextFiles || contextFiles.length === 0) {
23
+ return '';
24
+ }
25
+ const storage = createStorage();
26
+ const contextParts = [];
27
+ for (const filePath of contextFiles){
28
+ try {
29
+ const content = await storage.readFile(filePath, 'utf8');
30
+ contextParts.push(`## Context from ${filePath}\n\n${content}\n`);
31
+ logger.debug(`Read context from file: ${filePath}`);
32
+ } catch (error) {
33
+ logger.warn(`Failed to read context file ${filePath}: ${error.message}`);
34
+ }
35
+ }
36
+ return contextParts.join('\n---\n\n');
37
+ }
20
38
  // Helper function to edit release notes using editor
21
39
  async function editReleaseNotesInteractively(releaseSummary) {
22
40
  const templateLines = [
@@ -65,7 +83,7 @@ Please revise the release notes according to the user's feedback while maintaini
65
83
  callLLM: async (request, runConfig, outputDirectory)=>{
66
84
  var _aiConfig_commands_release, _aiConfig_commands, _aiConfig_commands_release1, _aiConfig_commands1;
67
85
  const aiConfig = toAIConfig(runConfig);
68
- const aiStorageAdapter = createStorageAdapter();
86
+ const aiStorageAdapter = createStorageAdapter(outputDirectory);
69
87
  const aiLogger = createLoggerAdapter(false);
70
88
  const modelToUse = ((_aiConfig_commands = aiConfig.commands) === null || _aiConfig_commands === void 0 ? void 0 : (_aiConfig_commands_release = _aiConfig_commands.release) === null || _aiConfig_commands_release === void 0 ? void 0 : _aiConfig_commands_release.model) || aiConfig.model || 'gpt-4o-mini';
71
89
  const openaiReasoning = ((_aiConfig_commands1 = aiConfig.commands) === null || _aiConfig_commands1 === void 0 ? void 0 : (_aiConfig_commands_release1 = _aiConfig_commands1.release) === null || _aiConfig_commands_release1 === void 0 ? void 0 : _aiConfig_commands_release1.reasoning) || aiConfig.reasoning;
@@ -103,10 +121,8 @@ async function generateSelfReflection(agenticResult, outputDirectory, storage, l
103
121
  releaseNotes: agenticResult.releaseNotes,
104
122
  logger
105
123
  });
106
- // Save the report
107
- const storageAdapter = createStorageAdapter();
108
- const filename = `agentic-reflection-release-${timestamp}.md`;
109
- await storageAdapter.writeOutput(filename, report);
124
+ // Save the report to output directory
125
+ await storage.writeFile(reflectionPath, report, 'utf8');
110
126
  logger.info('');
111
127
  logger.info('═'.repeat(80));
112
128
  logger.info('📊 SELF-REFLECTION REPORT GENERATED');
@@ -188,7 +204,7 @@ async function handleInteractiveReleaseFeedback(releaseSummary, runConfig, promp
188
204
  }
189
205
  }
190
206
  const execute = async (runConfig)=>{
191
- var _runConfig_release, _runConfig_release1, _runConfig_release2, _runConfig_release3, _runConfig_release4, _runConfig_release5, _runConfig_release6, _runConfig_release7, _runConfig_release8, _runConfig_release9, _aiConfig_commands_release, _aiConfig_commands, _aiConfig_commands_release1, _aiConfig_commands1, _runConfig_release10;
207
+ var _runConfig_release, _runConfig_release1, _runConfig_release2, _runConfig_release3, _runConfig_release4, _runConfig_release5, _runConfig_release6, _runConfig_release7, _runConfig_release8, _runConfig_release9, _aiConfig_commands_release, _aiConfig_commands, _runConfig_release10, _aiConfig_commands_release1, _aiConfig_commands1, _runConfig_release11, _runConfig_release12;
192
208
  const isDryRun = runConfig.dryRun || false;
193
209
  const logger = getDryRunLogger(isDryRun);
194
210
  // Get current branch to help determine best tag comparison
@@ -275,154 +291,61 @@ const execute = async (runConfig)=>{
275
291
  } else {
276
292
  logger.debug('Milestone integration disabled via --no-milestones');
277
293
  }
278
- // Create adapters for ai-service
279
- const aiConfig = toAIConfig(runConfig);
280
- const aiStorageAdapter = createStorageAdapter();
281
- const aiLogger = createLoggerAdapter(isDryRun);
282
294
  // Always ensure output directory exists for request/response files
283
295
  const outputDirectory = runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY;
284
296
  const storage = createStorage();
285
297
  await storage.ensureDirectory(outputDirectory);
286
- // Check if agentic mode is enabled
287
- if ((_runConfig_release7 = runConfig.release) === null || _runConfig_release7 === void 0 ? void 0 : _runConfig_release7.agentic) {
288
- var _runConfig_release11, _runConfig_release12, _aiConfig_commands_release2, _aiConfig_commands2, _runConfig_release13, _aiConfig_commands_release3, _aiConfig_commands3, _runConfig_release14, _runConfig_release15;
289
- logger.info('🤖 Using agentic mode for release notes generation');
290
- // Run agentic release notes generation
291
- const agenticResult = await runAgenticRelease({
292
- fromRef,
293
- toRef,
294
- logContent,
295
- diffContent,
296
- milestoneIssues: milestoneIssuesContent,
297
- releaseFocus: (_runConfig_release11 = runConfig.release) === null || _runConfig_release11 === void 0 ? void 0 : _runConfig_release11.focus,
298
- userContext: (_runConfig_release12 = runConfig.release) === null || _runConfig_release12 === void 0 ? void 0 : _runConfig_release12.context,
299
- model: ((_aiConfig_commands2 = aiConfig.commands) === null || _aiConfig_commands2 === void 0 ? void 0 : (_aiConfig_commands_release2 = _aiConfig_commands2.release) === null || _aiConfig_commands_release2 === void 0 ? void 0 : _aiConfig_commands_release2.model) || aiConfig.model || 'gpt-4o',
300
- maxIterations: ((_runConfig_release13 = runConfig.release) === null || _runConfig_release13 === void 0 ? void 0 : _runConfig_release13.maxAgenticIterations) || 30,
301
- debug: runConfig.debug,
302
- debugRequestFile: getOutputPath(outputDirectory, getTimestampedRequestFilename('release-agentic')),
303
- debugResponseFile: getOutputPath(outputDirectory, getTimestampedResponseFilename('release-agentic')),
304
- storage: aiStorageAdapter,
305
- logger: aiLogger,
306
- openaiReasoning: ((_aiConfig_commands3 = aiConfig.commands) === null || _aiConfig_commands3 === void 0 ? void 0 : (_aiConfig_commands_release3 = _aiConfig_commands3.release) === null || _aiConfig_commands_release3 === void 0 ? void 0 : _aiConfig_commands_release3.reasoning) || aiConfig.reasoning
307
- });
308
- const iterations = agenticResult.iterations || 0;
309
- const toolCalls = agenticResult.toolCallsExecuted || 0;
310
- logger.info(`🔍 Agentic analysis complete: ${iterations} iterations, ${toolCalls} tool calls`);
311
- // Generate self-reflection output if enabled
312
- if ((_runConfig_release14 = runConfig.release) === null || _runConfig_release14 === void 0 ? void 0 : _runConfig_release14.selfReflection) {
313
- await generateSelfReflection(agenticResult, outputDirectory, storage, logger);
314
- }
315
- // Apply stop-context filtering to release notes
316
- const titleFilterResult = filterContent(agenticResult.releaseNotes.title, runConfig.stopContext);
317
- const bodyFilterResult = filterContent(agenticResult.releaseNotes.body, runConfig.stopContext);
318
- let releaseSummary = {
319
- title: titleFilterResult.filtered,
320
- body: bodyFilterResult.filtered
321
- };
322
- // Handle interactive mode
323
- if (((_runConfig_release15 = runConfig.release) === null || _runConfig_release15 === void 0 ? void 0 : _runConfig_release15.interactive) && !isDryRun) {
324
- var _runConfig_release16;
325
- requireTTY('Interactive mode requires a terminal. Use --dry-run instead.');
326
- const interactivePromptContext = {
327
- context: (_runConfig_release16 = runConfig.release) === null || _runConfig_release16 === void 0 ? void 0 : _runConfig_release16.context,
328
- directories: runConfig.contextDirectories
329
- };
330
- const interactiveResult = await handleInteractiveReleaseFeedback(releaseSummary, runConfig, promptConfig, interactivePromptContext, outputDirectory, storage, logContent, diffContent);
331
- if (interactiveResult.action === 'skip') {
332
- logger.info('RELEASE_ABORTED: Release notes generation aborted by user | Reason: User choice | Status: cancelled');
333
- } else {
334
- logger.info('RELEASE_FINALIZED: Release notes finalized and accepted | Status: ready | Next: Create release or save');
335
- }
336
- releaseSummary = interactiveResult.finalSummary;
337
- }
338
- // Save timestamped copy of release notes to output directory
339
- try {
340
- const timestampedFilename = getTimestampedReleaseNotesFilename();
341
- const outputPath = getOutputPath(outputDirectory, timestampedFilename);
342
- // Format the release notes as markdown
343
- const releaseNotesContent = `# ${releaseSummary.title}\n\n${releaseSummary.body}`;
344
- await storage.writeFile(outputPath, releaseNotesContent, 'utf-8');
345
- logger.debug('Saved timestamped release notes: %s', outputPath);
346
- } catch (error) {
347
- logger.warn('RELEASE_SAVE_FAILED: Failed to save timestamped release notes | Error: %s | Impact: Notes not persisted to file', error.message);
348
- }
349
- if (isDryRun) {
350
- logger.info('RELEASE_SUMMARY_COMPLETE: Generated release summary successfully | Status: completed');
351
- logger.info('RELEASE_SUMMARY_TITLE: %s', releaseSummary.title);
352
- logger.info('RELEASE_SUMMARY_BODY: %s', releaseSummary.body);
353
- }
354
- return releaseSummary;
355
- }
356
- // Non-agentic mode: use traditional prompt-based approach
357
- const promptContent = {
298
+ // Create adapters for ai-service
299
+ const aiConfig = toAIConfig(runConfig);
300
+ const aiStorageAdapter = createStorageAdapter(outputDirectory);
301
+ const aiLogger = createLoggerAdapter(isDryRun);
302
+ // Read context from files if provided
303
+ const contextFromFiles = await readContextFiles((_runConfig_release7 = runConfig.release) === null || _runConfig_release7 === void 0 ? void 0 : _runConfig_release7.contextFiles, logger);
304
+ // Combine file context with existing context
305
+ const combinedContext = [
306
+ (_runConfig_release8 = runConfig.release) === null || _runConfig_release8 === void 0 ? void 0 : _runConfig_release8.context,
307
+ contextFromFiles
308
+ ].filter(Boolean).join('\n\n---\n\n');
309
+ // Run agentic release notes generation
310
+ const agenticResult = await runAgenticRelease({
311
+ fromRef,
312
+ toRef,
358
313
  logContent,
359
314
  diffContent,
360
- releaseFocus: (_runConfig_release8 = runConfig.release) === null || _runConfig_release8 === void 0 ? void 0 : _runConfig_release8.focus,
361
- milestoneIssues: milestoneIssuesContent
362
- };
363
- const promptContext = {
364
- context: (_runConfig_release9 = runConfig.release) === null || _runConfig_release9 === void 0 ? void 0 : _runConfig_release9.context,
365
- directories: runConfig.contextDirectories
366
- };
367
- const promptResult = await createReleasePrompt(promptConfig, promptContent, promptContext);
368
- const modelToUse = ((_aiConfig_commands = aiConfig.commands) === null || _aiConfig_commands === void 0 ? void 0 : (_aiConfig_commands_release = _aiConfig_commands.release) === null || _aiConfig_commands_release === void 0 ? void 0 : _aiConfig_commands_release.model) || aiConfig.model || 'gpt-4o-mini';
369
- const request = Formatter.create({
370
- logger
371
- }).formatPrompt(modelToUse, promptResult.prompt);
372
- logger.debug('Release analysis: isLargeRelease=%s, maxTokens=%d', promptResult.isLargeRelease, promptResult.maxTokens);
373
- // Create retry callback that reduces diff size on token limit errors
374
- const createRetryCallback = (originalDiffContent, originalLogContent)=>async (attempt)=>{
375
- var _runConfig_release, _runConfig_release1;
376
- logger.info('RELEASE_RETRY: Retrying with reduced diff size | Attempt: %d | Strategy: Truncate diff | Reason: Previous attempt failed', attempt);
377
- // Progressively reduce the diff size on retries
378
- const reductionFactor = Math.pow(0.5, attempt - 1); // 50% reduction per retry
379
- const reducedMaxDiffBytes = Math.max(512, Math.floor(maxDiffBytes * reductionFactor));
380
- logger.debug('Reducing maxDiffBytes from %d to %d for retry', maxDiffBytes, reducedMaxDiffBytes);
381
- // Re-truncate the diff with smaller limits
382
- const reducedDiffContent = originalDiffContent.length > reducedMaxDiffBytes ? truncateDiffByFiles(originalDiffContent, reducedMaxDiffBytes) : originalDiffContent;
383
- // Rebuild the prompt with the reduced diff
384
- const reducedPromptContent = {
385
- logContent: originalLogContent,
386
- diffContent: reducedDiffContent,
387
- releaseFocus: (_runConfig_release = runConfig.release) === null || _runConfig_release === void 0 ? void 0 : _runConfig_release.focus,
388
- milestoneIssues: milestoneIssuesContent
389
- };
390
- const reducedPromptContext = {
391
- context: (_runConfig_release1 = runConfig.release) === null || _runConfig_release1 === void 0 ? void 0 : _runConfig_release1.context,
392
- directories: runConfig.contextDirectories
393
- };
394
- const retryPromptResult = await createReleasePrompt(promptConfig, reducedPromptContent, reducedPromptContext);
395
- const retryRequest = Formatter.create({
396
- logger
397
- }).formatPrompt(modelToUse, retryPromptResult.prompt);
398
- return retryRequest.messages;
399
- };
400
- const summary = await createCompletionWithRetry(request.messages, {
401
- model: modelToUse,
402
- openaiReasoning: ((_aiConfig_commands1 = aiConfig.commands) === null || _aiConfig_commands1 === void 0 ? void 0 : (_aiConfig_commands_release1 = _aiConfig_commands1.release) === null || _aiConfig_commands_release1 === void 0 ? void 0 : _aiConfig_commands_release1.reasoning) || aiConfig.reasoning,
403
- maxTokens: promptResult.maxTokens,
404
- responseFormat: {
405
- type: 'json_object'
406
- },
315
+ milestoneIssues: milestoneIssuesContent,
316
+ releaseFocus: (_runConfig_release9 = runConfig.release) === null || _runConfig_release9 === void 0 ? void 0 : _runConfig_release9.focus,
317
+ userContext: combinedContext || undefined,
318
+ model: ((_aiConfig_commands = aiConfig.commands) === null || _aiConfig_commands === void 0 ? void 0 : (_aiConfig_commands_release = _aiConfig_commands.release) === null || _aiConfig_commands_release === void 0 ? void 0 : _aiConfig_commands_release.model) || aiConfig.model || 'gpt-4o',
319
+ maxIterations: ((_runConfig_release10 = runConfig.release) === null || _runConfig_release10 === void 0 ? void 0 : _runConfig_release10.maxAgenticIterations) || 30,
407
320
  debug: runConfig.debug,
408
321
  debugRequestFile: getOutputPath(outputDirectory, getTimestampedRequestFilename('release')),
409
322
  debugResponseFile: getOutputPath(outputDirectory, getTimestampedResponseFilename('release')),
410
323
  storage: aiStorageAdapter,
411
- logger: aiLogger
412
- }, createRetryCallback(diffContent, logContent));
413
- // Validate and safely cast the response
414
- const rawReleaseSummary = validateReleaseSummary(summary);
324
+ logger: aiLogger,
325
+ openaiReasoning: ((_aiConfig_commands1 = aiConfig.commands) === null || _aiConfig_commands1 === void 0 ? void 0 : (_aiConfig_commands_release1 = _aiConfig_commands1.release) === null || _aiConfig_commands_release1 === void 0 ? void 0 : _aiConfig_commands_release1.reasoning) || aiConfig.reasoning
326
+ });
327
+ const iterations = agenticResult.iterations || 0;
328
+ const toolCalls = agenticResult.toolCallsExecuted || 0;
329
+ logger.info(`🔍 Analysis complete: ${iterations} iterations, ${toolCalls} tool calls`);
330
+ // Generate self-reflection output if enabled
331
+ if ((_runConfig_release11 = runConfig.release) === null || _runConfig_release11 === void 0 ? void 0 : _runConfig_release11.selfReflection) {
332
+ await generateSelfReflection(agenticResult, outputDirectory, storage, logger);
333
+ }
415
334
  // Apply stop-context filtering to release notes
416
- const titleFilterResult = filterContent(rawReleaseSummary.title, runConfig.stopContext);
417
- const bodyFilterResult = filterContent(rawReleaseSummary.body, runConfig.stopContext);
335
+ const titleFilterResult = filterContent(agenticResult.releaseNotes.title, runConfig.stopContext);
336
+ const bodyFilterResult = filterContent(agenticResult.releaseNotes.body, runConfig.stopContext);
418
337
  let releaseSummary = {
419
338
  title: titleFilterResult.filtered,
420
339
  body: bodyFilterResult.filtered
421
340
  };
422
341
  // Handle interactive mode
423
- if (((_runConfig_release10 = runConfig.release) === null || _runConfig_release10 === void 0 ? void 0 : _runConfig_release10.interactive) && !isDryRun) {
342
+ if (((_runConfig_release12 = runConfig.release) === null || _runConfig_release12 === void 0 ? void 0 : _runConfig_release12.interactive) && !isDryRun) {
424
343
  requireTTY('Interactive mode requires a terminal. Use --dry-run instead.');
425
- const interactiveResult = await handleInteractiveReleaseFeedback(releaseSummary, runConfig, promptConfig, promptContext, outputDirectory, storage, logContent, diffContent);
344
+ const interactivePromptContext = {
345
+ context: combinedContext || undefined,
346
+ directories: runConfig.contextDirectories
347
+ };
348
+ const interactiveResult = await handleInteractiveReleaseFeedback(releaseSummary, runConfig, promptConfig, interactivePromptContext, outputDirectory, storage, logContent, diffContent);
426
349
  if (interactiveResult.action === 'skip') {
427
350
  logger.info('RELEASE_ABORTED: Release notes generation aborted by user | Reason: User choice | Status: cancelled');
428
351
  } else {