@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/release.js
CHANGED
|
@@ -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
|
|
8
|
-
import { runAgenticRelease, requireTTY,
|
|
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;
|
|
@@ -88,166 +106,23 @@ Please revise the release notes according to the user's feedback while maintaini
|
|
|
88
106
|
};
|
|
89
107
|
return await improveContentWithLLM(releaseSummary, runConfig, promptConfig, promptContext, outputDirectory, improvementConfig);
|
|
90
108
|
}
|
|
91
|
-
// Helper function to generate self-reflection output for release notes
|
|
109
|
+
// Helper function to generate self-reflection output for release notes using observability module
|
|
92
110
|
async function generateSelfReflection(agenticResult, outputDirectory, storage, logger) {
|
|
93
111
|
try {
|
|
94
112
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('.')[0];
|
|
95
113
|
const reflectionPath = getOutputPath(outputDirectory, `agentic-reflection-release-${timestamp}.md`);
|
|
96
|
-
//
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const stats = toolStats.get(metric.name);
|
|
109
|
-
stats.total++;
|
|
110
|
-
stats.totalDuration += metric.duration;
|
|
111
|
-
if (metric.success) {
|
|
112
|
-
stats.success++;
|
|
113
|
-
} else {
|
|
114
|
-
stats.failures++;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
// Build reflection document
|
|
118
|
-
const sections = [];
|
|
119
|
-
sections.push('# Agentic Release Notes - Self-Reflection Report');
|
|
120
|
-
sections.push('');
|
|
121
|
-
sections.push(`Generated: ${new Date().toISOString()}`);
|
|
122
|
-
sections.push('');
|
|
123
|
-
sections.push('## Execution Summary');
|
|
124
|
-
sections.push('');
|
|
125
|
-
sections.push(`- **Iterations**: ${agenticResult.iterations}`);
|
|
126
|
-
sections.push(`- **Tool Calls**: ${agenticResult.toolCallsExecuted}`);
|
|
127
|
-
sections.push(`- **Unique Tools Used**: ${toolStats.size}`);
|
|
128
|
-
sections.push('');
|
|
129
|
-
sections.push('## Tool Effectiveness Analysis');
|
|
130
|
-
sections.push('');
|
|
131
|
-
if (toolStats.size === 0) {
|
|
132
|
-
sections.push('*No tools were called during execution.*');
|
|
133
|
-
sections.push('');
|
|
134
|
-
} else {
|
|
135
|
-
sections.push('| Tool | Calls | Success Rate | Avg Duration | Total Time |');
|
|
136
|
-
sections.push('|------|-------|--------------|--------------|------------|');
|
|
137
|
-
const sortedTools = Array.from(toolStats.entries()).sort((a, b)=>b[1].total - a[1].total);
|
|
138
|
-
for (const [toolName, stats] of sortedTools){
|
|
139
|
-
const successRate = (stats.success / stats.total * 100).toFixed(1);
|
|
140
|
-
const avgDuration = (stats.totalDuration / stats.total).toFixed(0);
|
|
141
|
-
const totalTime = stats.totalDuration.toFixed(0);
|
|
142
|
-
sections.push(`| ${toolName} | ${stats.total} | ${successRate}% | ${avgDuration}ms | ${totalTime}ms |`);
|
|
143
|
-
}
|
|
144
|
-
sections.push('');
|
|
145
|
-
}
|
|
146
|
-
// Tool usage insights
|
|
147
|
-
sections.push('## Tool Usage Insights');
|
|
148
|
-
sections.push('');
|
|
149
|
-
if (toolStats.size > 0) {
|
|
150
|
-
const mostUsedTool = Array.from(toolStats.entries()).sort((a, b)=>b[1].total - a[1].total)[0];
|
|
151
|
-
sections.push(`- **Most Used Tool**: \`${mostUsedTool[0]}\` (${mostUsedTool[1].total} calls)`);
|
|
152
|
-
const slowestTool = Array.from(toolStats.entries()).sort((a, b)=>b[1].totalDuration / b[1].total - a[1].totalDuration / a[1].total)[0];
|
|
153
|
-
const slowestAvg = (slowestTool[1].totalDuration / slowestTool[1].total).toFixed(0);
|
|
154
|
-
sections.push(`- **Slowest Tool**: \`${slowestTool[0]}\` (${slowestAvg}ms average)`);
|
|
155
|
-
const failedTools = Array.from(toolStats.entries()).filter(([_, stats])=>stats.failures > 0);
|
|
156
|
-
if (failedTools.length > 0) {
|
|
157
|
-
sections.push(`- **Tools with Failures**: ${failedTools.length} tool(s) had at least one failure`);
|
|
158
|
-
for (const [toolName, stats] of failedTools){
|
|
159
|
-
sections.push(` - \`${toolName}\`: ${stats.failures}/${stats.total} calls failed`);
|
|
160
|
-
}
|
|
161
|
-
} else {
|
|
162
|
-
sections.push('- **Reliability**: All tool calls succeeded ✓');
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
sections.push('');
|
|
166
|
-
// Execution patterns
|
|
167
|
-
sections.push('## Execution Patterns');
|
|
168
|
-
sections.push('');
|
|
169
|
-
const iterationsPerToolCall = agenticResult.toolCallsExecuted > 0 ? (agenticResult.iterations / agenticResult.toolCallsExecuted).toFixed(2) : 'N/A';
|
|
170
|
-
sections.push(`- **Iterations per Tool Call**: ${iterationsPerToolCall}`);
|
|
171
|
-
const totalExecutionTime = Array.from(toolStats.values()).reduce((sum, stats)=>sum + stats.totalDuration, 0);
|
|
172
|
-
sections.push(`- **Total Tool Execution Time**: ${totalExecutionTime.toFixed(0)}ms`);
|
|
173
|
-
if (agenticResult.toolCallsExecuted > 0) {
|
|
174
|
-
const avgTimePerCall = (totalExecutionTime / agenticResult.toolCallsExecuted).toFixed(0);
|
|
175
|
-
sections.push(`- **Average Time per Tool Call**: ${avgTimePerCall}ms`);
|
|
176
|
-
}
|
|
177
|
-
sections.push('');
|
|
178
|
-
// Recommendations
|
|
179
|
-
sections.push('## Recommendations');
|
|
180
|
-
sections.push('');
|
|
181
|
-
const recommendations = [];
|
|
182
|
-
const failedTools = Array.from(toolStats.entries()).filter(([_, stats])=>stats.failures > 0);
|
|
183
|
-
if (failedTools.length > 0) {
|
|
184
|
-
recommendations.push('- **Tool Reliability**: Some tools failed during execution. Review error messages and consider improving error handling or tool implementation.');
|
|
185
|
-
}
|
|
186
|
-
const slowTools = Array.from(toolStats.entries()).filter(([_, stats])=>stats.totalDuration / stats.total > 1000);
|
|
187
|
-
if (slowTools.length > 0) {
|
|
188
|
-
recommendations.push('- **Performance**: Consider optimizing slow tools or caching results to improve execution speed.');
|
|
189
|
-
}
|
|
190
|
-
if (agenticResult.iterations >= (agenticResult.maxIterations || 30)) {
|
|
191
|
-
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.');
|
|
192
|
-
}
|
|
193
|
-
const underutilizedTools = Array.from(toolStats.entries()).filter(([_, stats])=>stats.total === 1);
|
|
194
|
-
if (underutilizedTools.length > 3) {
|
|
195
|
-
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.');
|
|
196
|
-
}
|
|
197
|
-
if (agenticResult.toolCallsExecuted === 0) {
|
|
198
|
-
recommendations.push('- **No Tools Used**: The agent completed without calling any tools. This might indicate the initial prompt provided sufficient information, or the agent may benefit from more explicit guidance to use tools.');
|
|
199
|
-
}
|
|
200
|
-
if (recommendations.length === 0) {
|
|
201
|
-
sections.push('*No specific recommendations at this time. Execution appears optimal.*');
|
|
202
|
-
} else {
|
|
203
|
-
for (const rec of recommendations){
|
|
204
|
-
sections.push(rec);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
sections.push('');
|
|
208
|
-
// Add detailed execution timeline
|
|
209
|
-
sections.push('## Detailed Execution Timeline');
|
|
210
|
-
sections.push('');
|
|
211
|
-
if (toolMetrics.length === 0) {
|
|
212
|
-
sections.push('*No tool execution timeline available.*');
|
|
213
|
-
} else {
|
|
214
|
-
sections.push('| Time | Iteration | Tool | Result | Duration |');
|
|
215
|
-
sections.push('|------|-----------|------|--------|----------|');
|
|
216
|
-
for (const metric of toolMetrics){
|
|
217
|
-
const time = new Date(metric.timestamp).toLocaleTimeString();
|
|
218
|
-
const result = metric.success ? '✅ Success' : `❌ ${metric.error || 'Failed'}`;
|
|
219
|
-
sections.push(`| ${time} | ${metric.iteration} | ${metric.name} | ${result} | ${metric.duration}ms |`);
|
|
220
|
-
}
|
|
221
|
-
sections.push('');
|
|
222
|
-
}
|
|
223
|
-
// Add conversation history
|
|
224
|
-
sections.push('## Conversation History');
|
|
225
|
-
sections.push('');
|
|
226
|
-
sections.push('<details>');
|
|
227
|
-
sections.push('<summary>Click to expand full agentic interaction</summary>');
|
|
228
|
-
sections.push('');
|
|
229
|
-
sections.push('```json');
|
|
230
|
-
sections.push(JSON.stringify(agenticResult.conversationHistory, null, 2));
|
|
231
|
-
sections.push('```');
|
|
232
|
-
sections.push('');
|
|
233
|
-
sections.push('</details>');
|
|
234
|
-
sections.push('');
|
|
235
|
-
// Add generated release notes
|
|
236
|
-
sections.push('## Generated Release Notes');
|
|
237
|
-
sections.push('');
|
|
238
|
-
sections.push('### Title');
|
|
239
|
-
sections.push('```');
|
|
240
|
-
sections.push(agenticResult.releaseNotes.title);
|
|
241
|
-
sections.push('```');
|
|
242
|
-
sections.push('');
|
|
243
|
-
sections.push('### Body');
|
|
244
|
-
sections.push('```markdown');
|
|
245
|
-
sections.push(agenticResult.releaseNotes.body);
|
|
246
|
-
sections.push('```');
|
|
247
|
-
sections.push('');
|
|
248
|
-
// Write the reflection file
|
|
249
|
-
const reflectionContent = sections.join('\n');
|
|
250
|
-
await storage.writeFile(reflectionPath, reflectionContent, 'utf-8');
|
|
114
|
+
// Use new observability reflection generator
|
|
115
|
+
const report = await generateReflectionReport({
|
|
116
|
+
iterations: agenticResult.iterations || 0,
|
|
117
|
+
toolCallsExecuted: agenticResult.toolCallsExecuted || 0,
|
|
118
|
+
maxIterations: agenticResult.maxIterations || 30,
|
|
119
|
+
toolMetrics: agenticResult.toolMetrics || [],
|
|
120
|
+
conversationHistory: agenticResult.conversationHistory || [],
|
|
121
|
+
releaseNotes: agenticResult.releaseNotes,
|
|
122
|
+
logger
|
|
123
|
+
});
|
|
124
|
+
// Save the report to output directory
|
|
125
|
+
await storage.writeFile(reflectionPath, report, 'utf8');
|
|
251
126
|
logger.info('');
|
|
252
127
|
logger.info('═'.repeat(80));
|
|
253
128
|
logger.info('📊 SELF-REFLECTION REPORT GENERATED');
|
|
@@ -256,9 +131,12 @@ async function generateSelfReflection(agenticResult, outputDirectory, storage, l
|
|
|
256
131
|
logger.info('📁 Location: %s', reflectionPath);
|
|
257
132
|
logger.info('');
|
|
258
133
|
logger.info('📈 Report Summary:');
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
134
|
+
const iterations = agenticResult.iterations || 0;
|
|
135
|
+
const toolCalls = agenticResult.toolCallsExecuted || 0;
|
|
136
|
+
const uniqueTools = new Set((agenticResult.toolMetrics || []).map((m)=>m.name)).size;
|
|
137
|
+
logger.info(` • ${iterations} iterations completed`);
|
|
138
|
+
logger.info(` • ${toolCalls} tool calls executed`);
|
|
139
|
+
logger.info(` • ${uniqueTools} unique tools used`);
|
|
262
140
|
logger.info('');
|
|
263
141
|
logger.info('💡 Use this report to:');
|
|
264
142
|
logger.info(' • Understand which tools were most effective');
|
|
@@ -326,7 +204,7 @@ async function handleInteractiveReleaseFeedback(releaseSummary, runConfig, promp
|
|
|
326
204
|
}
|
|
327
205
|
}
|
|
328
206
|
const execute = async (runConfig)=>{
|
|
329
|
-
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,
|
|
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;
|
|
330
208
|
const isDryRun = runConfig.dryRun || false;
|
|
331
209
|
const logger = getDryRunLogger(isDryRun);
|
|
332
210
|
// Get current branch to help determine best tag comparison
|
|
@@ -413,152 +291,61 @@ const execute = async (runConfig)=>{
|
|
|
413
291
|
} else {
|
|
414
292
|
logger.debug('Milestone integration disabled via --no-milestones');
|
|
415
293
|
}
|
|
416
|
-
// Create adapters for ai-service
|
|
417
|
-
const aiConfig = toAIConfig(runConfig);
|
|
418
|
-
const aiStorageAdapter = createStorageAdapter();
|
|
419
|
-
const aiLogger = createLoggerAdapter(isDryRun);
|
|
420
294
|
// Always ensure output directory exists for request/response files
|
|
421
295
|
const outputDirectory = runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY;
|
|
422
296
|
const storage = createStorage();
|
|
423
297
|
await storage.ensureDirectory(outputDirectory);
|
|
424
|
-
//
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
debug: runConfig.debug,
|
|
440
|
-
debugRequestFile: getOutputPath(outputDirectory, getTimestampedRequestFilename('release-agentic')),
|
|
441
|
-
debugResponseFile: getOutputPath(outputDirectory, getTimestampedResponseFilename('release-agentic')),
|
|
442
|
-
storage: aiStorageAdapter,
|
|
443
|
-
logger: aiLogger,
|
|
444
|
-
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
|
|
445
|
-
});
|
|
446
|
-
logger.info('🔍 Agentic analysis complete: %d iterations, %d tool calls', agenticResult.iterations, agenticResult.toolCallsExecuted);
|
|
447
|
-
// Generate self-reflection output if enabled
|
|
448
|
-
if ((_runConfig_release14 = runConfig.release) === null || _runConfig_release14 === void 0 ? void 0 : _runConfig_release14.selfReflection) {
|
|
449
|
-
await generateSelfReflection(agenticResult, outputDirectory, storage, logger);
|
|
450
|
-
}
|
|
451
|
-
// Apply stop-context filtering to release notes
|
|
452
|
-
const titleFilterResult = filterContent(agenticResult.releaseNotes.title, runConfig.stopContext);
|
|
453
|
-
const bodyFilterResult = filterContent(agenticResult.releaseNotes.body, runConfig.stopContext);
|
|
454
|
-
let releaseSummary = {
|
|
455
|
-
title: titleFilterResult.filtered,
|
|
456
|
-
body: bodyFilterResult.filtered
|
|
457
|
-
};
|
|
458
|
-
// Handle interactive mode
|
|
459
|
-
if (((_runConfig_release15 = runConfig.release) === null || _runConfig_release15 === void 0 ? void 0 : _runConfig_release15.interactive) && !isDryRun) {
|
|
460
|
-
var _runConfig_release16;
|
|
461
|
-
requireTTY('Interactive mode requires a terminal. Use --dry-run instead.');
|
|
462
|
-
const interactivePromptContext = {
|
|
463
|
-
context: (_runConfig_release16 = runConfig.release) === null || _runConfig_release16 === void 0 ? void 0 : _runConfig_release16.context,
|
|
464
|
-
directories: runConfig.contextDirectories
|
|
465
|
-
};
|
|
466
|
-
const interactiveResult = await handleInteractiveReleaseFeedback(releaseSummary, runConfig, promptConfig, interactivePromptContext, outputDirectory, storage, logContent, diffContent);
|
|
467
|
-
if (interactiveResult.action === 'skip') {
|
|
468
|
-
logger.info('RELEASE_ABORTED: Release notes generation aborted by user | Reason: User choice | Status: cancelled');
|
|
469
|
-
} else {
|
|
470
|
-
logger.info('RELEASE_FINALIZED: Release notes finalized and accepted | Status: ready | Next: Create release or save');
|
|
471
|
-
}
|
|
472
|
-
releaseSummary = interactiveResult.finalSummary;
|
|
473
|
-
}
|
|
474
|
-
// Save timestamped copy of release notes to output directory
|
|
475
|
-
try {
|
|
476
|
-
const timestampedFilename = getTimestampedReleaseNotesFilename();
|
|
477
|
-
const outputPath = getOutputPath(outputDirectory, timestampedFilename);
|
|
478
|
-
// Format the release notes as markdown
|
|
479
|
-
const releaseNotesContent = `# ${releaseSummary.title}\n\n${releaseSummary.body}`;
|
|
480
|
-
await storage.writeFile(outputPath, releaseNotesContent, 'utf-8');
|
|
481
|
-
logger.debug('Saved timestamped release notes: %s', outputPath);
|
|
482
|
-
} catch (error) {
|
|
483
|
-
logger.warn('RELEASE_SAVE_FAILED: Failed to save timestamped release notes | Error: %s | Impact: Notes not persisted to file', error.message);
|
|
484
|
-
}
|
|
485
|
-
if (isDryRun) {
|
|
486
|
-
logger.info('RELEASE_SUMMARY_COMPLETE: Generated release summary successfully | Status: completed');
|
|
487
|
-
logger.info('RELEASE_SUMMARY_TITLE: %s', releaseSummary.title);
|
|
488
|
-
logger.info('RELEASE_SUMMARY_BODY: %s', releaseSummary.body);
|
|
489
|
-
}
|
|
490
|
-
return releaseSummary;
|
|
491
|
-
}
|
|
492
|
-
// Non-agentic mode: use traditional prompt-based approach
|
|
493
|
-
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,
|
|
494
313
|
logContent,
|
|
495
314
|
diffContent,
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
directories: runConfig.contextDirectories
|
|
502
|
-
};
|
|
503
|
-
const promptResult = await createReleasePrompt(promptConfig, promptContent, promptContext);
|
|
504
|
-
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';
|
|
505
|
-
const request = Formatter.create({
|
|
506
|
-
logger
|
|
507
|
-
}).formatPrompt(modelToUse, promptResult.prompt);
|
|
508
|
-
logger.debug('Release analysis: isLargeRelease=%s, maxTokens=%d', promptResult.isLargeRelease, promptResult.maxTokens);
|
|
509
|
-
// Create retry callback that reduces diff size on token limit errors
|
|
510
|
-
const createRetryCallback = (originalDiffContent, originalLogContent)=>async (attempt)=>{
|
|
511
|
-
var _runConfig_release, _runConfig_release1;
|
|
512
|
-
logger.info('RELEASE_RETRY: Retrying with reduced diff size | Attempt: %d | Strategy: Truncate diff | Reason: Previous attempt failed', attempt);
|
|
513
|
-
// Progressively reduce the diff size on retries
|
|
514
|
-
const reductionFactor = Math.pow(0.5, attempt - 1); // 50% reduction per retry
|
|
515
|
-
const reducedMaxDiffBytes = Math.max(512, Math.floor(maxDiffBytes * reductionFactor));
|
|
516
|
-
logger.debug('Reducing maxDiffBytes from %d to %d for retry', maxDiffBytes, reducedMaxDiffBytes);
|
|
517
|
-
// Re-truncate the diff with smaller limits
|
|
518
|
-
const reducedDiffContent = originalDiffContent.length > reducedMaxDiffBytes ? truncateDiffByFiles(originalDiffContent, reducedMaxDiffBytes) : originalDiffContent;
|
|
519
|
-
// Rebuild the prompt with the reduced diff
|
|
520
|
-
const reducedPromptContent = {
|
|
521
|
-
logContent: originalLogContent,
|
|
522
|
-
diffContent: reducedDiffContent,
|
|
523
|
-
releaseFocus: (_runConfig_release = runConfig.release) === null || _runConfig_release === void 0 ? void 0 : _runConfig_release.focus,
|
|
524
|
-
milestoneIssues: milestoneIssuesContent
|
|
525
|
-
};
|
|
526
|
-
const reducedPromptContext = {
|
|
527
|
-
context: (_runConfig_release1 = runConfig.release) === null || _runConfig_release1 === void 0 ? void 0 : _runConfig_release1.context,
|
|
528
|
-
directories: runConfig.contextDirectories
|
|
529
|
-
};
|
|
530
|
-
const retryPromptResult = await createReleasePrompt(promptConfig, reducedPromptContent, reducedPromptContext);
|
|
531
|
-
const retryRequest = Formatter.create({
|
|
532
|
-
logger
|
|
533
|
-
}).formatPrompt(modelToUse, retryPromptResult.prompt);
|
|
534
|
-
return retryRequest.messages;
|
|
535
|
-
};
|
|
536
|
-
const summary = await createCompletionWithRetry(request.messages, {
|
|
537
|
-
model: modelToUse,
|
|
538
|
-
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,
|
|
539
|
-
maxTokens: promptResult.maxTokens,
|
|
540
|
-
responseFormat: {
|
|
541
|
-
type: 'json_object'
|
|
542
|
-
},
|
|
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,
|
|
543
320
|
debug: runConfig.debug,
|
|
544
321
|
debugRequestFile: getOutputPath(outputDirectory, getTimestampedRequestFilename('release')),
|
|
545
322
|
debugResponseFile: getOutputPath(outputDirectory, getTimestampedResponseFilename('release')),
|
|
546
323
|
storage: aiStorageAdapter,
|
|
547
|
-
logger: aiLogger
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
const
|
|
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
|
+
}
|
|
551
334
|
// Apply stop-context filtering to release notes
|
|
552
|
-
const titleFilterResult = filterContent(
|
|
553
|
-
const bodyFilterResult = filterContent(
|
|
335
|
+
const titleFilterResult = filterContent(agenticResult.releaseNotes.title, runConfig.stopContext);
|
|
336
|
+
const bodyFilterResult = filterContent(agenticResult.releaseNotes.body, runConfig.stopContext);
|
|
554
337
|
let releaseSummary = {
|
|
555
338
|
title: titleFilterResult.filtered,
|
|
556
339
|
body: bodyFilterResult.filtered
|
|
557
340
|
};
|
|
558
341
|
// Handle interactive mode
|
|
559
|
-
if (((
|
|
342
|
+
if (((_runConfig_release12 = runConfig.release) === null || _runConfig_release12 === void 0 ? void 0 : _runConfig_release12.interactive) && !isDryRun) {
|
|
560
343
|
requireTTY('Interactive mode requires a terminal. Use --dry-run instead.');
|
|
561
|
-
const
|
|
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);
|
|
562
349
|
if (interactiveResult.action === 'skip') {
|
|
563
350
|
logger.info('RELEASE_ABORTED: Release notes generation aborted by user | Reason: User choice | Status: cancelled');
|
|
564
351
|
} else {
|