@eldrforge/ai-service 0.1.12 → 0.1.14

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/README.md CHANGED
@@ -1,15 +1,28 @@
1
1
  # @eldrforge/ai-service
2
2
 
3
- AI-powered content generation for automation tools with agentic capabilities.
3
+ AI-powered content generation library with agentic capabilities for commit messages, release notes, and code reviews.
4
4
 
5
5
  ## Overview
6
6
 
7
- This package provides OpenAI integration with structured prompts and agentic workflows for generating:
8
- - **Commit messages** (traditional and agentic with tool-calling)
9
- - **Release notes** (traditional and agentic with tool-calling)
10
- - **Code review analyses**
7
+ `@eldrforge/ai-service` is a TypeScript library that provides intelligent content generation powered by OpenAI's GPT models. It was extracted from the [kodrdriv](https://github.com/calenvarek/kodrdriv) automation tool to enable standalone use in any Node.js application.
11
8
 
12
- Extracted from the kodrdriv project for reusability and broader ecosystem use.
9
+ ### Key Features
10
+
11
+ - **🤖 Agentic AI Mode**: Advanced tool-calling capabilities that allow the AI to investigate codebases, analyze history, and understand context deeply before generating content
12
+ - **📝 Traditional Mode**: Direct prompt-based generation for faster, simpler use cases
13
+ - **🔧 Extensible Tool System**: 13+ specialized tools for codebase investigation and analysis
14
+ - **💬 Interactive Features**: Built-in user prompts, editor integration, and feedback loops
15
+ - **🎯 Structured Prompts**: Leverages [@riotprompt/riotprompt](https://www.npmjs.com/package/@riotprompt/riotprompt) for consistent, high-quality prompt engineering
16
+ - **📊 Detailed Metrics**: Track tool usage, iterations, and AI reasoning effectiveness
17
+ - **🔌 Flexible Integration**: Adapter-based design for easy integration with any storage or logging system
18
+
19
+ ### Use Cases
20
+
21
+ - Generate commit messages from staged changes
22
+ - Create comprehensive release notes from version diffs
23
+ - Perform code review analysis
24
+ - Automate documentation generation
25
+ - Integrate AI-powered code understanding into your tools
13
26
 
14
27
  ## Installation
15
28
 
@@ -17,337 +30,1210 @@ Extracted from the kodrdriv project for reusability and broader ecosystem use.
17
30
  npm install @eldrforge/ai-service
18
31
  ```
19
32
 
20
- ## Dependencies
33
+ ### Required Dependencies
34
+
35
+ ```bash
36
+ npm install openai @riotprompt/riotprompt @eldrforge/git-tools
37
+ ```
21
38
 
22
- - `openai` - OpenAI API client
23
- - `@riotprompt/riotprompt` - Structured prompt builder
24
- - `@eldrforge/git-tools` - Utility functions
39
+ ### Optional Dependencies
25
40
 
26
- ## Features
41
+ ```bash
42
+ npm install winston # For logging support
43
+ ```
27
44
 
28
- ### Traditional Content Generation
29
- - Structured prompt generation for commits, releases, and reviews
30
- - OpenAI API integration with retry logic
31
- - Interactive user feedback and editing
32
- - Audio transcription support
45
+ ## Quick Start
33
46
 
34
- ### Agentic Mode (NEW)
35
- - **Tool-calling capabilities** for AI-powered investigation
36
- - **13 specialized tools** for release analysis
37
- - **8 tools** for commit analysis
38
- - **Self-reflection** reports with tool effectiveness metrics
39
- - **Iterative refinement** with configurable limits
47
+ ### 1. Setup OpenAI API Key
40
48
 
41
- ## Usage
49
+ ```typescript
50
+ import { createCompletion } from '@eldrforge/ai-service';
42
51
 
43
- ### Release Notes Generation
52
+ // Set your OpenAI API key
53
+ process.env.OPENAI_API_KEY = 'sk-...';
54
+ ```
44
55
 
45
- #### Traditional Mode
56
+ ### 2. Generate a Commit Message (Traditional Mode)
46
57
 
47
58
  ```typescript
48
- import { createReleasePrompt, createCompletionWithRetry } from '@eldrforge/ai-service';
59
+ import { createCommitPrompt, createCompletionWithRetry } from '@eldrforge/ai-service';
60
+ import { execSync } from 'child_process';
61
+
62
+ // Get staged diff
63
+ const diffContent = execSync('git diff --staged', { encoding: 'utf8' });
49
64
 
50
65
  // Create prompt
51
- const { prompt, maxTokens, isLargeRelease } = await createReleasePrompt(
66
+ const { prompt } = await createCommitPrompt(
52
67
  { overridePaths: [], overrides: true },
53
- {
54
- logContent: 'git log output',
55
- diffContent: 'git diff output',
56
- releaseFocus: 'Performance improvements',
57
- milestoneIssues: 'Issue #1: Bug fix',
58
- },
59
- {
60
- context: 'Major release',
61
- directories: ['src'],
62
- }
68
+ { diffContent },
69
+ { context: 'Feature development', directories: ['src'] }
63
70
  );
64
71
 
65
- // Generate release notes
66
- const result = await createCompletionWithRetry(
67
- prompt.messages,
68
- {
69
- model: 'gpt-4o',
70
- maxTokens,
71
- responseFormat: { type: 'json_object' },
72
- }
73
- );
72
+ // Generate commit message
73
+ const response = await createCompletionWithRetry(prompt.messages, {
74
+ model: 'gpt-4o-mini',
75
+ });
76
+
77
+ console.log('Suggested commit message:', response.choices[0].message.content);
74
78
  ```
75
79
 
76
- #### Agentic Mode (NEW)
80
+ ### 3. Generate Release Notes (Agentic Mode)
77
81
 
78
82
  ```typescript
79
83
  import { runAgenticRelease } from '@eldrforge/ai-service';
84
+ import { execSync } from 'child_process';
80
85
 
86
+ // Get git log and diff between versions
87
+ const logContent = execSync('git log v1.0.0..HEAD --oneline', { encoding: 'utf8' });
88
+ const diffContent = execSync('git diff v1.0.0..HEAD --stat', { encoding: 'utf8' });
89
+
90
+ // Run agentic release notes generation
81
91
  const result = await runAgenticRelease({
82
92
  fromRef: 'v1.0.0',
83
93
  toRef: 'HEAD',
84
- logContent: 'git log output',
85
- diffContent: 'git diff output',
86
- milestoneIssues: 'Issue #1: Bug fix',
87
- releaseFocus: 'Performance improvements',
88
- userContext: 'Major release',
94
+ logContent,
95
+ diffContent,
96
+ releaseFocus: 'Performance improvements and bug fixes',
89
97
  model: 'gpt-4o',
90
- maxIterations: 30, // Default for releases
91
- storage: storageAdapter,
92
- logger: loggerAdapter,
98
+ maxIterations: 30,
93
99
  });
94
100
 
95
- // result.releaseNotes = { title: string, body: string }
96
- // result.iterations = number of iterations used
97
- // result.toolCallsExecuted = number of tool calls made
98
- // result.toolMetrics = detailed metrics for each tool call
101
+ console.log('Title:', result.releaseNotes.title);
102
+ console.log('Body:', result.releaseNotes.body);
103
+ console.log('Iterations:', result.iterations);
104
+ console.log('Tools used:', result.toolCallsExecuted);
99
105
  ```
100
106
 
101
- ### Commit Message Generation
107
+ ## Complete Examples
102
108
 
103
- #### Traditional Mode
109
+ ### Example 1: Standalone Commit Message Generator
104
110
 
105
- ```typescript
106
- import { createCommitPrompt, createCompletionWithRetry } from '@eldrforge/ai-service';
111
+ Create a complete CLI tool for generating commit messages:
107
112
 
108
- const { prompt } = await createCommitPrompt(
109
- { overridePaths: [], overrides: true },
110
- {
111
- diffContent: 'git diff --staged',
112
- logContext: 'Recent commits',
113
- userDirection: 'Focus on API changes',
113
+ ```typescript
114
+ #!/usr/bin/env node
115
+ import { runAgenticCommit } from '@eldrforge/ai-service';
116
+ import { execSync } from 'child_process';
117
+ import * as fs from 'fs/promises';
118
+ import * as path from 'path';
119
+
120
+ // Simple storage adapter that writes to ./output
121
+ const storageAdapter = {
122
+ async writeOutput(fileName: string, content: string): Promise<void> {
123
+ const dir = path.join(process.cwd(), 'output');
124
+ await fs.mkdir(dir, { recursive: true });
125
+ await fs.writeFile(path.join(dir, fileName), content, 'utf8');
114
126
  },
115
- {
116
- context: 'Refactoring',
117
- directories: ['src/api'],
127
+ async readTemp(fileName: string): Promise<string> {
128
+ return fs.readFile(path.join('/tmp', fileName), 'utf8');
129
+ },
130
+ async writeTemp(fileName: string, content: string): Promise<void> {
131
+ await fs.writeFile(path.join('/tmp', fileName), content, 'utf8');
132
+ },
133
+ async readFile(fileName: string): Promise<string> {
134
+ return fs.readFile(fileName, 'utf8');
135
+ },
136
+ };
137
+
138
+ // Simple console logger
139
+ const logger = {
140
+ info: (msg: string) => console.log(`[INFO] ${msg}`),
141
+ error: (msg: string) => console.error(`[ERROR] ${msg}`),
142
+ warn: (msg: string) => console.warn(`[WARN] ${msg}`),
143
+ debug: (msg: string) => console.debug(`[DEBUG] ${msg}`),
144
+ };
145
+
146
+ async function main() {
147
+ try {
148
+ // Get staged changes
149
+ const status = execSync('git status --porcelain', { encoding: 'utf8' });
150
+ const changedFiles = status
151
+ .split('\n')
152
+ .filter(line => line.startsWith('M ') || line.startsWith('A '))
153
+ .map(line => line.substring(3));
154
+
155
+ if (changedFiles.length === 0) {
156
+ console.log('No staged changes found. Stage some changes first with: git add <files>');
157
+ process.exit(1);
158
+ }
159
+
160
+ // Get the diff
161
+ const diffContent = execSync('git diff --staged', { encoding: 'utf8' });
162
+
163
+ // Get recent commits for context
164
+ const logContext = execSync('git log --oneline -5', { encoding: 'utf8' });
165
+
166
+ console.log('🤖 Generating commit message...\n');
167
+
168
+ // Run agentic commit generation
169
+ const result = await runAgenticCommit({
170
+ changedFiles,
171
+ diffContent,
172
+ logContext,
173
+ model: 'gpt-4o-mini',
174
+ maxIterations: 10,
175
+ storage: storageAdapter,
176
+ logger,
177
+ });
178
+
179
+ console.log('\n✨ Suggested Commit Message:\n');
180
+ console.log(result.commitMessage);
181
+ console.log('\n---');
182
+ console.log(`📊 Used ${result.toolCallsExecuted} tool calls in ${result.iterations} iterations`);
183
+
184
+ // Show suggested splits if any
185
+ if (result.suggestedSplits.length > 0) {
186
+ console.log('\n💡 Suggested Commit Splits:\n');
187
+ result.suggestedSplits.forEach((split, idx) => {
188
+ console.log(`\nSplit ${idx + 1}:`);
189
+ console.log(`Files: ${split.files.join(', ')}`);
190
+ console.log(`Rationale: ${split.rationale}`);
191
+ console.log(`Message: ${split.message}`);
192
+ });
193
+ }
194
+
195
+ // Optionally save the message to a file
196
+ await storageAdapter.writeOutput('commit-message.txt', result.commitMessage);
197
+ console.log('\n💾 Commit message saved to: output/commit-message.txt');
198
+
199
+ } catch (error) {
200
+ console.error('Error:', error instanceof Error ? error.message : String(error));
201
+ process.exit(1);
118
202
  }
119
- );
203
+ }
120
204
 
121
- const commitMessage = await createCompletionWithRetry(
122
- prompt.messages,
123
- { model: 'gpt-4o' }
124
- );
205
+ main();
206
+ ```
207
+
208
+ Save as `generate-commit.ts` and run with:
209
+
210
+ ```bash
211
+ npx tsx generate-commit.ts
125
212
  ```
126
213
 
127
- #### Agentic Mode
214
+ ### Example 2: Release Notes Generator for GitHub Releases
215
+
216
+ Automatically generate release notes and create GitHub releases:
128
217
 
129
218
  ```typescript
130
- import { runAgenticCommit } from '@eldrforge/ai-service';
219
+ import { runAgenticRelease } from '@eldrforge/ai-service';
220
+ import { execSync } from 'child_process';
221
+ import { Octokit } from '@octokit/rest';
222
+ import * as fs from 'fs/promises';
223
+
224
+ interface ReleaseConfig {
225
+ owner: string;
226
+ repo: string;
227
+ fromTag: string;
228
+ toTag?: string;
229
+ githubToken: string;
230
+ }
131
231
 
132
- const result = await runAgenticCommit({
133
- changedFiles: ['src/api/users.ts', 'src/api/auth.ts'],
134
- diffContent: 'git diff --staged',
135
- userDirection: 'Focus on API changes',
136
- logContext: 'Recent commits',
137
- model: 'gpt-4o',
138
- maxIterations: 10, // Default for commits
139
- storage: storageAdapter,
140
- logger: loggerAdapter,
232
+ async function createGitHubRelease(config: ReleaseConfig) {
233
+ const { owner, repo, fromTag, toTag = 'HEAD', githubToken } = config;
234
+
235
+ // Simple storage adapter
236
+ const storage = {
237
+ writeOutput: async (fileName: string, content: string) => {
238
+ await fs.mkdir('output', { recursive: true });
239
+ await fs.writeFile(`output/${fileName}`, content);
240
+ },
241
+ readTemp: async (fileName: string) => fs.readFile(`/tmp/${fileName}`, 'utf8'),
242
+ writeTemp: async (fileName: string, content: string) =>
243
+ fs.writeFile(`/tmp/${fileName}`, content),
244
+ readFile: async (fileName: string) => fs.readFile(fileName, 'utf8'),
245
+ };
246
+
247
+ const logger = {
248
+ info: console.log,
249
+ error: console.error,
250
+ warn: console.warn,
251
+ debug: console.debug,
252
+ };
253
+
254
+ try {
255
+ // Get git information
256
+ const logContent = execSync(
257
+ `git log ${fromTag}..${toTag} --pretty=format:"%h %s (%an)" --abbrev-commit`,
258
+ { encoding: 'utf8' }
259
+ );
260
+
261
+ const diffContent = execSync(
262
+ `git diff ${fromTag}..${toTag} --stat`,
263
+ { encoding: 'utf8' }
264
+ );
265
+
266
+ console.log(`🔍 Analyzing changes from ${fromTag} to ${toTag}...\n`);
267
+
268
+ // Generate release notes using agentic mode
269
+ const result = await runAgenticRelease({
270
+ fromRef: fromTag,
271
+ toRef: toTag,
272
+ logContent,
273
+ diffContent,
274
+ model: 'gpt-4o',
275
+ maxIterations: 30,
276
+ storage,
277
+ logger,
278
+ });
279
+
280
+ console.log('✅ Release notes generated!\n');
281
+ console.log(`Title: ${result.releaseNotes.title}`);
282
+ console.log(`\nBody:\n${result.releaseNotes.body}`);
283
+ console.log(`\n📊 Metrics: ${result.toolCallsExecuted} tool calls, ${result.iterations} iterations`);
284
+
285
+ // Save to file
286
+ await storage.writeOutput('release-notes.md', result.releaseNotes.body);
287
+ await storage.writeOutput('release-title.txt', result.releaseNotes.title);
288
+
289
+ // Create GitHub release
290
+ const octokit = new Octokit({ auth: githubToken });
291
+
292
+ const release = await octokit.repos.createRelease({
293
+ owner,
294
+ repo,
295
+ tag_name: toTag === 'HEAD' ? 'v1.0.0' : toTag, // Adjust as needed
296
+ name: result.releaseNotes.title,
297
+ body: result.releaseNotes.body,
298
+ draft: true, // Create as draft for review
299
+ });
300
+
301
+ console.log(`\n🎉 Draft release created: ${release.data.html_url}`);
302
+
303
+ return result;
304
+ } catch (error) {
305
+ console.error('❌ Error:', error);
306
+ throw error;
307
+ }
308
+ }
309
+
310
+ // Usage
311
+ createGitHubRelease({
312
+ owner: 'yourorg',
313
+ repo: 'yourrepo',
314
+ fromTag: 'v0.1.0',
315
+ toTag: 'v0.2.0',
316
+ githubToken: process.env.GITHUB_TOKEN || '',
141
317
  });
318
+ ```
319
+
320
+ ### Example 3: Custom Tool Integration
321
+
322
+ Create custom tools for domain-specific analysis:
323
+
324
+ ```typescript
325
+ import {
326
+ createToolRegistry,
327
+ type Tool,
328
+ type ToolContext,
329
+ runAgentic
330
+ } from '@eldrforge/ai-service';
331
+
332
+ // Create a custom tool
333
+ const checkTestCoverage: Tool = {
334
+ name: 'check_test_coverage',
335
+ description: 'Check test coverage for specific files or entire project',
336
+ parameters: {
337
+ type: 'object',
338
+ properties: {
339
+ filePath: {
340
+ type: 'string',
341
+ description: 'Optional file path to check coverage for. If omitted, checks entire project.',
342
+ },
343
+ },
344
+ },
345
+ execute: async (params: { filePath?: string }, context?: ToolContext) => {
346
+ const { execSync } = await import('child_process');
347
+
348
+ try {
349
+ // Run coverage check
350
+ const cmd = params.filePath
351
+ ? `npm test -- --coverage --testPathPattern="${params.filePath}"`
352
+ : 'npm test -- --coverage';
353
+
354
+ const output = execSync(cmd, {
355
+ encoding: 'utf8',
356
+ cwd: context?.workingDirectory || process.cwd(),
357
+ });
358
+
359
+ // Parse coverage output
360
+ const coverageMatch = output.match(/All files.*?(\d+\.?\d*)\s*\|/);
361
+ const coverage = coverageMatch ? parseFloat(coverageMatch[1]) : 0;
362
+
363
+ return {
364
+ success: true,
365
+ coverage,
366
+ message: `Coverage: ${coverage}%`,
367
+ details: output,
368
+ };
369
+ } catch (error) {
370
+ return {
371
+ success: false,
372
+ error: error instanceof Error ? error.message : String(error),
373
+ };
374
+ }
375
+ },
376
+ };
377
+
378
+ // Use custom tool in agentic workflow
379
+ async function analyzeWithCustomTools() {
380
+ const registry = createToolRegistry({
381
+ workingDirectory: process.cwd(),
382
+ });
383
+
384
+ // Register custom tool
385
+ registry.register(checkTestCoverage);
386
+
387
+ // You can also register built-in tools
388
+ const { createCommitTools } = await import('@eldrforge/ai-service');
389
+ const commitTools = createCommitTools();
390
+ registry.registerAll(commitTools);
391
+
392
+ // Run agentic analysis with custom tools
393
+ const messages = [
394
+ {
395
+ role: 'system' as const,
396
+ content: 'You are a code quality analyst. Use available tools to assess code changes.',
397
+ },
398
+ {
399
+ role: 'user' as const,
400
+ content: 'Analyze the current changes and check if test coverage is adequate.',
401
+ },
402
+ ];
403
+
404
+ const result = await runAgentic({
405
+ messages,
406
+ tools: registry,
407
+ model: 'gpt-4o',
408
+ maxIterations: 15,
409
+ });
410
+
411
+ console.log('Analysis Result:', result.finalMessage);
412
+ console.log('Tools used:', result.toolCallsExecuted);
142
413
 
143
- // result.commitMessage = string
144
- // result.suggestedSplits = array of split suggestions
145
- // result.toolMetrics = detailed metrics
414
+ return result;
415
+ }
146
416
  ```
147
417
 
148
- ## Agentic Mode
418
+ ### Example 4: Interactive Commit Flow
149
419
 
150
- ### Release-Specific Tools (13 total)
420
+ Create an interactive commit workflow with user feedback:
151
421
 
152
- **Investigation Tools** (inherited from commit generation):
422
+ ```typescript
423
+ import {
424
+ runAgenticCommit,
425
+ getUserChoice,
426
+ editContentInEditor,
427
+ STANDARD_CHOICES
428
+ } from '@eldrforge/ai-service';
429
+ import { execSync } from 'child_process';
430
+
431
+ async function interactiveCommit() {
432
+ // Get staged changes
433
+ const diffContent = execSync('git diff --staged', { encoding: 'utf8' });
434
+ const statusOutput = execSync('git status --porcelain', { encoding: 'utf8' });
435
+ const changedFiles = statusOutput
436
+ .split('\n')
437
+ .filter(line => line.trim())
438
+ .map(line => line.substring(3));
439
+
440
+ if (changedFiles.length === 0) {
441
+ console.log('No staged changes. Run: git add <files>');
442
+ return;
443
+ }
444
+
445
+ console.log('📝 Changed files:', changedFiles.join(', '), '\n');
446
+
447
+ // Generate initial commit message
448
+ const result = await runAgenticCommit({
449
+ changedFiles,
450
+ diffContent,
451
+ model: 'gpt-4o-mini',
452
+ });
453
+
454
+ console.log('\n✨ Generated Commit Message:\n');
455
+ console.log(result.commitMessage);
456
+ console.log('\n---\n');
457
+
458
+ // Interactive loop
459
+ let finalMessage = result.commitMessage;
460
+ let shouldContinue = true;
461
+
462
+ while (shouldContinue) {
463
+ const choice = await getUserChoice(
464
+ 'What would you like to do?',
465
+ [
466
+ STANDARD_CHOICES.CONFIRM,
467
+ STANDARD_CHOICES.EDIT,
468
+ STANDARD_CHOICES.SKIP,
469
+ ]
470
+ );
471
+
472
+ switch (choice) {
473
+ case 'c': // Confirm
474
+ // Create the commit
475
+ execSync(`git commit -m "${finalMessage.replace(/"/g, '\\"')}"`, {
476
+ stdio: 'inherit',
477
+ });
478
+ console.log('✅ Commit created successfully!');
479
+ shouldContinue = false;
480
+ break;
481
+
482
+ case 'e': // Edit
483
+ const edited = await editContentInEditor(
484
+ finalMessage,
485
+ ['# Edit your commit message below', '# Lines starting with # will be removed'],
486
+ '.txt'
487
+ );
488
+ finalMessage = edited.content;
489
+ console.log('\n📝 Updated commit message:\n');
490
+ console.log(finalMessage);
491
+ console.log('\n---\n');
492
+ break;
493
+
494
+ case 's': // Skip
495
+ console.log('⏭️ Commit cancelled.');
496
+ shouldContinue = false;
497
+ break;
498
+ }
499
+ }
500
+ }
501
+
502
+ interactiveCommit();
503
+ ```
504
+
505
+ ## Core Concepts
506
+
507
+ ### Agentic vs Traditional Mode
508
+
509
+ **Traditional Mode**:
510
+ - Direct prompt-based generation
511
+ - Fast and straightforward
512
+ - Good for simple use cases
513
+ - Lower token usage
514
+
515
+ **Agentic Mode**:
516
+ - AI uses tools to investigate the codebase
517
+ - More thorough and context-aware
518
+ - Better for complex changes
519
+ - Higher token usage but more accurate results
520
+
521
+ ### Tool System
522
+
523
+ The library provides built-in tools for code analysis:
524
+
525
+ #### Commit Tools (8 tools)
153
526
  1. `get_file_history` - View commit history for files
154
527
  2. `get_file_content` - Read full file contents
155
528
  3. `search_codebase` - Search for patterns
156
529
  4. `get_related_tests` - Find related test files
157
- 5. `get_file_dependencies` - Analyze dependencies
158
- 6. `analyze_diff_section` - Get expanded context
159
- 7. `get_recent_commits` - See recent changes
530
+ 5. `get_file_dependencies` - Analyze import dependencies
531
+ 6. `analyze_diff_section` - Get expanded diff context
532
+ 7. `get_recent_commits` - See recent commit messages
160
533
  8. `group_files_by_concern` - Identify logical groupings
161
534
 
162
- **Release-Specific Tools** (unique to release generation):
535
+ #### Release Tools (13 tools)
536
+ - All commit tools plus:
163
537
  9. `get_tag_history` - View previous release tags
164
538
  10. `compare_previous_release` - Compare with previous versions
165
539
  11. `get_release_stats` - Get comprehensive statistics
166
540
  12. `get_breaking_changes` - Identify breaking changes
167
541
  13. `analyze_commit_patterns` - Find themes and patterns
168
542
 
169
- ### Commit-Specific Tools (8 total)
543
+ ### Adapters
170
544
 
171
- Tools 1-8 from above (investigation tools only).
545
+ The library uses adapters to remain flexible and framework-agnostic:
172
546
 
173
- ### Tool Registry
547
+ #### Storage Adapter
174
548
 
175
549
  ```typescript
176
- import { createToolRegistry, createReleaseTools, createCommitTools } from '@eldrforge/ai-service';
550
+ interface StorageAdapter {
551
+ writeOutput(fileName: string, content: string): Promise<void>;
552
+ readTemp(fileName: string): Promise<string>;
553
+ writeTemp(fileName: string, content: string): Promise<void>;
554
+ readFile(fileName: string, encoding?: string): Promise<string>;
555
+ }
556
+ ```
177
557
 
178
- // For release notes
179
- const registry = createToolRegistry({
180
- workingDirectory: process.cwd(),
181
- storage: storageAdapter,
182
- logger: loggerAdapter,
183
- });
184
- const tools = createReleaseTools();
185
- registry.registerAll(tools);
558
+ #### Logger Adapter
186
559
 
187
- // For commits
188
- const commitTools = createCommitTools();
189
- registry.registerAll(commitTools);
560
+ ```typescript
561
+ interface Logger {
562
+ info(message: string, ...meta: unknown[]): void;
563
+ error(message: string, ...meta: unknown[]): void;
564
+ warn(message: string, ...meta: unknown[]): void;
565
+ debug(message: string, ...meta: unknown[]): void;
566
+ }
190
567
  ```
191
568
 
192
- ## Interactive Features
569
+ ## API Reference
570
+
571
+ ### Agentic Functions
572
+
573
+ #### `runAgenticCommit(config: AgenticCommitConfig): Promise<AgenticCommitResult>`
193
574
 
575
+ Generate commit messages with AI tool-calling capabilities.
576
+
577
+ **Config Options:**
194
578
  ```typescript
195
- import {
196
- getUserChoice,
197
- editContentInEditor,
198
- getLLMFeedbackInEditor,
199
- requireTTY,
200
- STANDARD_CHOICES,
201
- } from '@eldrforge/ai-service';
579
+ interface AgenticCommitConfig {
580
+ changedFiles: string[]; // List of changed file paths
581
+ diffContent: string; // Git diff output
582
+ userDirection?: string; // Optional user guidance
583
+ logContext?: string; // Recent commit history
584
+ model?: string; // OpenAI model (default: 'gpt-4o')
585
+ maxIterations?: number; // Max tool-calling iterations (default: 10)
586
+ debug?: boolean; // Enable debug output
587
+ debugRequestFile?: string; // File to save debug requests
588
+ debugResponseFile?: string; // File to save debug responses
589
+ storage?: StorageAdapter; // Storage adapter for file operations
590
+ logger?: Logger; // Logger adapter
591
+ openaiReasoning?: 'low' | 'medium' | 'high'; // Reasoning effort level
592
+ }
593
+ ```
594
+
595
+ **Returns:**
596
+ ```typescript
597
+ interface AgenticCommitResult {
598
+ commitMessage: string; // Generated commit message
599
+ iterations: number; // Number of iterations used
600
+ toolCallsExecuted: number; // Number of tool calls made
601
+ suggestedSplits: Array<{ // Optional split suggestions
602
+ files: string[];
603
+ message: string;
604
+ rationale: string;
605
+ }>;
606
+ conversationHistory: ChatCompletionMessageParam[]; // Full conversation
607
+ toolMetrics: ToolExecutionMetric[]; // Tool usage metrics
608
+ }
609
+ ```
610
+
611
+ #### `runAgenticRelease(config: AgenticReleaseConfig): Promise<AgenticReleaseResult>`
612
+
613
+ Generate release notes with AI tool-calling capabilities.
614
+
615
+ **Config Options:**
616
+ ```typescript
617
+ interface AgenticReleaseConfig {
618
+ fromRef: string; // Starting git ref (e.g., 'v1.0.0')
619
+ toRef: string; // Ending git ref (e.g., 'HEAD')
620
+ logContent: string; // Git log output
621
+ diffContent: string; // Git diff output
622
+ milestoneIssues?: string; // GitHub milestone issues
623
+ releaseFocus?: string; // Focus area for release
624
+ userContext?: string; // Additional context
625
+ model?: string; // OpenAI model (default: 'gpt-4o')
626
+ maxIterations?: number; // Max iterations (default: 30)
627
+ debug?: boolean; // Enable debug output
628
+ debugRequestFile?: string; // Debug request file
629
+ debugResponseFile?: string; // Debug response file
630
+ storage?: StorageAdapter; // Storage adapter
631
+ logger?: Logger; // Logger adapter
632
+ openaiReasoning?: 'low' | 'medium' | 'high'; // Reasoning level
633
+ }
634
+ ```
635
+
636
+ **Returns:**
637
+ ```typescript
638
+ interface AgenticReleaseResult {
639
+ releaseNotes: {
640
+ title: string; // Release title
641
+ body: string; // Release notes markdown
642
+ };
643
+ iterations: number; // Iterations used
644
+ toolCallsExecuted: number; // Tool calls made
645
+ conversationHistory: ChatCompletionMessageParam[]; // Full conversation
646
+ toolMetrics: ToolExecutionMetric[]; // Tool usage metrics
647
+ }
648
+ ```
649
+
650
+ #### `runAgentic(config: AgenticConfig): Promise<AgenticResult>`
651
+
652
+ Low-level agentic execution for custom workflows.
653
+
654
+ ### Traditional Prompt Functions
655
+
656
+ #### `createCommitPrompt(pathConfig, promptConfig, commandConfig)`
657
+
658
+ Create a structured prompt for commit message generation.
659
+
660
+ ```typescript
661
+ const { prompt, maxTokens } = await createCommitPrompt(
662
+ { overridePaths: [], overrides: true },
663
+ {
664
+ diffContent: string,
665
+ logContext?: string,
666
+ userDirection?: string,
667
+ transcriptionText?: string,
668
+ },
669
+ {
670
+ context?: string,
671
+ directories?: string[],
672
+ }
673
+ );
674
+ ```
675
+
676
+ #### `createReleasePrompt(pathConfig, promptConfig, commandConfig)`
677
+
678
+ Create a structured prompt for release notes generation.
679
+
680
+ ```typescript
681
+ const { prompt, maxTokens, isLargeRelease } = await createReleasePrompt(
682
+ { overridePaths: [], overrides: true },
683
+ {
684
+ logContent: string,
685
+ diffContent: string,
686
+ releaseFocus?: string,
687
+ milestoneIssues?: string,
688
+ transcriptionText?: string,
689
+ },
690
+ {
691
+ context?: string,
692
+ directories?: string[],
693
+ }
694
+ );
695
+ ```
696
+
697
+ #### `createReviewPrompt(pathConfig, promptConfig, commandConfig)`
698
+
699
+ Create a structured prompt for code review analysis.
700
+
701
+ ### OpenAI Integration
702
+
703
+ #### `createCompletion(messages, options)`
202
704
 
203
- // Get user choice
705
+ Create a chat completion with OpenAI.
706
+
707
+ ```typescript
708
+ const response = await createCompletion(
709
+ messages: ChatCompletionMessageParam[],
710
+ {
711
+ model?: string,
712
+ maxTokens?: number,
713
+ temperature?: number,
714
+ responseFormat?: { type: 'json_object' | 'text' },
715
+ }
716
+ );
717
+ ```
718
+
719
+ #### `createCompletionWithRetry(messages, options, maxRetries?)`
720
+
721
+ Create a chat completion with automatic retry logic.
722
+
723
+ ```typescript
724
+ const response = await createCompletionWithRetry(
725
+ messages,
726
+ options,
727
+ maxRetries: number = 3
728
+ );
729
+ ```
730
+
731
+ #### `transcribeAudio(audioPath, options?)`
732
+
733
+ Transcribe audio files using OpenAI Whisper.
734
+
735
+ ```typescript
736
+ const transcription = await transcribeAudio(
737
+ audioPath: string,
738
+ {
739
+ model?: string,
740
+ language?: string,
741
+ prompt?: string,
742
+ }
743
+ );
744
+ ```
745
+
746
+ ### Interactive Functions
747
+
748
+ #### `getUserChoice(prompt, choices, options?)`
749
+
750
+ Get a single-key choice from the user.
751
+
752
+ ```typescript
204
753
  const choice = await getUserChoice(
205
754
  'What would you like to do?',
206
- [STANDARD_CHOICES.CONFIRM, STANDARD_CHOICES.EDIT],
755
+ [
756
+ { key: 'c', label: 'Confirm' },
757
+ { key: 'e', label: 'Edit' },
758
+ { key: 's', label: 'Skip' },
759
+ ],
207
760
  { nonTtyErrorSuggestions: ['Use --dry-run'] }
208
761
  );
762
+ ```
763
+
764
+ #### `getUserTextInput(prompt, options?)`
765
+
766
+ Get multi-line text input from the user.
767
+
768
+ ```typescript
769
+ const userInput = await getUserTextInput(
770
+ 'Provide additional context:',
771
+ { logger }
772
+ );
773
+ ```
209
774
 
210
- // Edit in editor
775
+ #### `editContentInEditor(content, templateLines?, extension?, editor?, logger?)`
776
+
777
+ Open content in user's editor for editing.
778
+
779
+ ```typescript
211
780
  const result = await editContentInEditor(
212
781
  'Initial content',
213
- ['# Instructions', '# Edit below'],
214
- '.md'
782
+ ['# Edit your content below'],
783
+ '.md',
784
+ process.env.EDITOR,
785
+ logger
215
786
  );
216
787
 
217
- // Get LLM feedback
788
+ console.log(result.content);
789
+ console.log(result.wasEdited);
790
+ ```
791
+
792
+ #### `getLLMFeedbackInEditor(contentType, currentContent, editor?, logger?)`
793
+
794
+ Get structured feedback from user via editor for LLM improvement loop.
795
+
796
+ ```typescript
218
797
  const feedback = await getLLMFeedbackInEditor(
219
- 'release notes',
220
- 'Current content'
798
+ 'commit message',
799
+ currentCommitMessage,
800
+ undefined,
801
+ logger
221
802
  );
803
+ ```
222
804
 
223
- // Require TTY
805
+ #### `requireTTY(errorMessage?, logger?)`
806
+
807
+ Ensure the process is running in a TTY (terminal).
808
+
809
+ ```typescript
224
810
  requireTTY('This feature requires a terminal');
225
811
  ```
226
812
 
227
- ## API Reference
813
+ ### Tool System
814
+
815
+ #### `createToolRegistry(context)`
228
816
 
229
- ### Types
817
+ Create a tool registry for managing tools.
230
818
 
231
819
  ```typescript
232
- // Release Types
233
- interface AgenticReleaseConfig {
234
- fromRef: string;
235
- toRef: string;
236
- logContent: string;
237
- diffContent: string;
238
- milestoneIssues?: string;
239
- releaseFocus?: string;
240
- userContext?: string;
241
- model?: string;
242
- maxIterations?: number; // Default: 30
243
- debug?: boolean;
244
- storage?: StorageAdapter;
245
- logger?: Logger;
246
- openaiReasoning?: 'low' | 'medium' | 'high';
247
- }
820
+ const registry = createToolRegistry({
821
+ workingDirectory: process.cwd(),
822
+ storage: storageAdapter,
823
+ logger: loggerAdapter,
824
+ });
825
+ ```
248
826
 
249
- interface AgenticReleaseResult {
250
- releaseNotes: { title: string; body: string };
251
- iterations: number;
252
- toolCallsExecuted: number;
253
- conversationHistory: ChatCompletionMessageParam[];
254
- toolMetrics: ToolExecutionMetric[];
255
- }
827
+ #### `createCommitTools()`
256
828
 
257
- // Commit Types
258
- interface AgenticCommitConfig {
259
- changedFiles: string[];
260
- diffContent: string;
261
- userDirection?: string;
262
- logContext?: string;
263
- model?: string;
264
- maxIterations?: number; // Default: 10
265
- debug?: boolean;
266
- storage?: StorageAdapter;
267
- logger?: Logger;
268
- openaiReasoning?: 'low' | 'medium' | 'high';
269
- }
829
+ Get all commit-specific tools.
270
830
 
271
- interface AgenticCommitResult {
272
- commitMessage: string;
273
- iterations: number;
274
- toolCallsExecuted: number;
275
- suggestedSplits: Array<{
276
- files: string[];
277
- message: string;
278
- rationale: string;
279
- }>;
280
- conversationHistory: ChatCompletionMessageParam[];
281
- toolMetrics: ToolExecutionMetric[];
831
+ ```typescript
832
+ const tools = createCommitTools();
833
+ registry.registerAll(tools);
834
+ ```
835
+
836
+ #### `createReleaseTools()`
837
+
838
+ Get all release-specific tools (includes commit tools).
839
+
840
+ ```typescript
841
+ const tools = createReleaseTools();
842
+ registry.registerAll(tools);
843
+ ```
844
+
845
+ ## Configuration
846
+
847
+ ### Environment Variables
848
+
849
+ ```bash
850
+ # Required
851
+ OPENAI_API_KEY=sk-... # Your OpenAI API key
852
+
853
+ # Optional
854
+ EDITOR=code --wait # Editor for interactive editing
855
+ OPENAI_BASE_URL=https://... # Custom OpenAI API endpoint
856
+ ```
857
+
858
+ ### Model Selection
859
+
860
+ Choose the appropriate model based on your needs:
861
+
862
+ ```typescript
863
+ // Fast and economical
864
+ model: 'gpt-4o-mini'
865
+
866
+ // Balanced performance
867
+ model: 'gpt-4o'
868
+
869
+ // Maximum capability (reasoning models)
870
+ model: 'o1-preview'
871
+ model: 'o1-mini'
872
+ ```
873
+
874
+ ### Reasoning Levels
875
+
876
+ For reasoning models, control the effort level:
877
+
878
+ ```typescript
879
+ openaiReasoning: 'low' // Faster, less thorough
880
+ openaiReasoning: 'medium' // Balanced
881
+ openaiReasoning: 'high' // Most thorough, slower
882
+ ```
883
+
884
+ ## Advanced Usage
885
+
886
+ ### Custom Storage Backend
887
+
888
+ Implement your own storage backend:
889
+
890
+ ```typescript
891
+ import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
892
+
893
+ const s3Storage = {
894
+ async writeOutput(fileName: string, content: string): Promise<void> {
895
+ const s3 = new S3Client({ region: 'us-east-1' });
896
+ await s3.send(new PutObjectCommand({
897
+ Bucket: 'my-bucket',
898
+ Key: `output/${fileName}`,
899
+ Body: content,
900
+ }));
901
+ },
902
+
903
+ async readTemp(fileName: string): Promise<string> {
904
+ // Implementation
905
+ },
906
+
907
+ async writeTemp(fileName: string, content: string): Promise<void> {
908
+ // Implementation
909
+ },
910
+
911
+ async readFile(fileName: string): Promise<string> {
912
+ // Implementation
913
+ },
914
+ };
915
+
916
+ // Use with agentic functions
917
+ await runAgenticCommit({
918
+ changedFiles,
919
+ diffContent,
920
+ storage: s3Storage,
921
+ });
922
+ ```
923
+
924
+ ### Custom Logger Integration
925
+
926
+ Use Winston, Pino, or any other logger:
927
+
928
+ ```typescript
929
+ import winston from 'winston';
930
+
931
+ const winstonLogger = winston.createLogger({
932
+ level: 'info',
933
+ format: winston.format.json(),
934
+ transports: [
935
+ new winston.transports.File({ filename: 'error.log', level: 'error' }),
936
+ new winston.transports.File({ filename: 'combined.log' }),
937
+ ],
938
+ });
939
+
940
+ await runAgenticRelease({
941
+ fromRef: 'v1.0.0',
942
+ toRef: 'HEAD',
943
+ logContent,
944
+ diffContent,
945
+ logger: winstonLogger,
946
+ });
947
+ ```
948
+
949
+ ### Monitoring Tool Usage
950
+
951
+ Track which tools are being used and how effective they are:
952
+
953
+ ```typescript
954
+ const result = await runAgenticCommit({
955
+ changedFiles,
956
+ diffContent,
957
+ model: 'gpt-4o',
958
+ });
959
+
960
+ // Analyze tool metrics
961
+ result.toolMetrics.forEach(metric => {
962
+ console.log(`Tool: ${metric.name}`);
963
+ console.log(` Success: ${metric.success}`);
964
+ console.log(` Duration: ${metric.duration}ms`);
965
+ console.log(` Iteration: ${metric.iteration}`);
966
+ if (metric.error) {
967
+ console.log(` Error: ${metric.error}`);
968
+ }
969
+ });
970
+
971
+ // Identify most used tools
972
+ const toolUsage = result.toolMetrics.reduce((acc, metric) => {
973
+ acc[metric.name] = (acc[metric.name] || 0) + 1;
974
+ return acc;
975
+ }, {} as Record<string, number>);
976
+
977
+ console.log('Tool usage:', toolUsage);
978
+ ```
979
+
980
+ ### Debugging
981
+
982
+ Enable debug mode to see all AI interactions:
983
+
984
+ ```typescript
985
+ const result = await runAgenticCommit({
986
+ changedFiles,
987
+ diffContent,
988
+ debug: true,
989
+ debugRequestFile: 'debug/request.json',
990
+ debugResponseFile: 'debug/response.json',
991
+ storage: storageAdapter,
992
+ });
993
+
994
+ // Check the debug files to see:
995
+ // - All messages sent to OpenAI
996
+ // - Tool calls and responses
997
+ // - Complete conversation history
998
+ ```
999
+
1000
+ ## Best Practices
1001
+
1002
+ ### 1. Choose the Right Mode
1003
+
1004
+ - Use **Traditional Mode** for:
1005
+ - Simple, obvious changes
1006
+ - Quick iterations during development
1007
+ - Cost-sensitive operations
1008
+ - Well-understood codebases
1009
+
1010
+ - Use **Agentic Mode** for:
1011
+ - Complex multi-file changes
1012
+ - Release notes requiring deep analysis
1013
+ - Unfamiliar codebases
1014
+ - High-quality, thorough documentation
1015
+
1016
+ ### 2. Provide Context
1017
+
1018
+ The more context you provide, the better the results:
1019
+
1020
+ ```typescript
1021
+ await runAgenticCommit({
1022
+ changedFiles,
1023
+ diffContent,
1024
+ userDirection: 'This refactors the authentication system to use OAuth2',
1025
+ logContext: recentCommits, // Provide recent commit history
1026
+ });
1027
+ ```
1028
+
1029
+ ### 3. Configure Iteration Limits
1030
+
1031
+ Balance thoroughness with cost:
1032
+
1033
+ ```typescript
1034
+ // For commits: 5-15 iterations is usually sufficient
1035
+ maxIterations: 10
1036
+
1037
+ // For releases: 20-40 iterations for comprehensive analysis
1038
+ maxIterations: 30
1039
+ ```
1040
+
1041
+ ### 4. Handle Errors Gracefully
1042
+
1043
+ ```typescript
1044
+ try {
1045
+ const result = await runAgenticCommit({
1046
+ changedFiles,
1047
+ diffContent,
1048
+ });
1049
+
1050
+ // Use the result
1051
+ console.log(result.commitMessage);
1052
+ } catch (error) {
1053
+ if (error.message.includes('API key')) {
1054
+ console.error('OpenAI API key not configured');
1055
+ } else if (error.message.includes('rate limit')) {
1056
+ console.error('Rate limit exceeded, try again later');
1057
+ } else {
1058
+ console.error('Unexpected error:', error);
1059
+ }
282
1060
  }
1061
+ ```
1062
+
1063
+ ### 5. Implement Rate Limiting
1064
+
1065
+ If you're processing many requests:
1066
+
1067
+ ```typescript
1068
+ import pLimit from 'p-limit';
1069
+
1070
+ const limit = pLimit(3); // Max 3 concurrent requests
1071
+
1072
+ const commits = await Promise.all(
1073
+ changedFileSets.map(files =>
1074
+ limit(() => runAgenticCommit({ changedFiles: files, diffContent }))
1075
+ )
1076
+ );
1077
+ ```
1078
+
1079
+ ## Troubleshooting
1080
+
1081
+ ### "OpenAI API key not found"
1082
+
1083
+ Ensure your API key is set:
1084
+
1085
+ ```bash
1086
+ export OPENAI_API_KEY=sk-...
1087
+ ```
1088
+
1089
+ Or set it programmatically:
1090
+
1091
+ ```typescript
1092
+ process.env.OPENAI_API_KEY = 'sk-...';
1093
+ ```
283
1094
 
284
- // Tool Metrics
285
- interface ToolExecutionMetric {
286
- name: string;
287
- success: boolean;
288
- duration: number;
289
- error?: string;
290
- iteration: number;
291
- timestamp: string;
1095
+ ### "Interactive mode requires a terminal"
1096
+
1097
+ This error occurs when trying to use interactive features in non-TTY environments (e.g., CI/CD):
1098
+
1099
+ ```typescript
1100
+ // Check before using interactive features
1101
+ if (process.stdin.isTTY) {
1102
+ const choice = await getUserChoice(prompt, choices);
1103
+ } else {
1104
+ // Use default behavior
1105
+ console.log('Non-interactive mode, using defaults');
292
1106
  }
293
1107
  ```
294
1108
 
295
- ### Exports
1109
+ ### "Rate limit exceeded"
1110
+
1111
+ OpenAI has rate limits. Implement retry logic:
296
1112
 
297
1113
  ```typescript
298
- // Prompt generation
299
- export { createCommitPrompt } from './prompts/commit';
300
- export { createReleasePrompt } from './prompts/release';
301
- export { createReviewPrompt } from './prompts/review';
1114
+ import { createCompletionWithRetry } from '@eldrforge/ai-service';
1115
+
1116
+ // This already includes retry logic
1117
+ const response = await createCompletionWithRetry(
1118
+ messages,
1119
+ options,
1120
+ 5 // Max 5 retries
1121
+ );
1122
+ ```
302
1123
 
303
- // Agentic execution
304
- export { runAgenticCommit } from './agentic/commit';
305
- export { runAgenticRelease } from './agentic/release';
306
- export { runAgentic } from './agentic/executor';
1124
+ ### "Tool execution failed"
307
1125
 
308
- // Tools
309
- export { createToolRegistry } from './tools/registry';
310
- export { createCommitTools } from './tools/commit-tools';
311
- export { createReleaseTools } from './tools/release-tools';
1126
+ Tool failures are logged in the metrics. Check them:
312
1127
 
313
- // OpenAI integration
314
- export {
315
- createCompletion,
316
- createCompletionWithRetry,
317
- transcribeAudio,
318
- } from './ai';
1128
+ ```typescript
1129
+ const result = await runAgenticCommit({ ... });
319
1130
 
320
- // Interactive features
321
- export {
322
- getUserChoice,
323
- getUserText,
324
- editContentInEditor,
325
- getLLMFeedbackInEditor,
326
- requireTTY,
327
- STANDARD_CHOICES,
328
- } from './interactive';
1131
+ const failedTools = result.toolMetrics.filter(m => !m.success);
1132
+ failedTools.forEach(tool => {
1133
+ console.error(`Tool ${tool.name} failed: ${tool.error}`);
1134
+ });
1135
+ ```
329
1136
 
330
- // Types
331
- export type { StorageAdapter, Logger } from './types';
332
- export type { Tool, ToolContext } from './tools/types';
1137
+ ### "Model not found"
1138
+
1139
+ Ensure you're using a valid OpenAI model:
1140
+
1141
+ ```typescript
1142
+ // Valid models as of 2024
1143
+ const validModels = [
1144
+ 'gpt-4o',
1145
+ 'gpt-4o-mini',
1146
+ 'o1-preview',
1147
+ 'o1-mini',
1148
+ 'gpt-4-turbo',
1149
+ 'gpt-3.5-turbo',
1150
+ ];
333
1151
  ```
334
1152
 
335
- ## Development
1153
+ ## TypeScript Support
1154
+
1155
+ The library is written in TypeScript and exports all types:
1156
+
1157
+ ```typescript
1158
+ import type {
1159
+ AgenticCommitConfig,
1160
+ AgenticCommitResult,
1161
+ AgenticReleaseConfig,
1162
+ AgenticReleaseResult,
1163
+ StorageAdapter,
1164
+ Logger,
1165
+ Tool,
1166
+ ToolContext,
1167
+ ToolExecutionMetric,
1168
+ Choice,
1169
+ InteractiveOptions,
1170
+ EditorOptions,
1171
+ AIConfig,
1172
+ } from '@eldrforge/ai-service';
1173
+ ```
1174
+
1175
+ ## Performance Considerations
1176
+
1177
+ ### Token Usage
1178
+
1179
+ Agentic mode uses more tokens due to tool-calling:
1180
+
1181
+ - **Commit generation**: ~5,000-20,000 tokens
1182
+ - **Release generation**: ~20,000-100,000 tokens
1183
+
1184
+ Monitor usage with the `toolMetrics` data.
1185
+
1186
+ ### Execution Time
1187
+
1188
+ - **Traditional mode**: 2-10 seconds
1189
+ - **Agentic mode (commits)**: 10-60 seconds
1190
+ - **Agentic mode (releases)**: 30-180 seconds
1191
+
1192
+ ### Cost Optimization
1193
+
1194
+ 1. Use `gpt-4o-mini` for development and testing
1195
+ 2. Limit `maxIterations` for cost control
1196
+ 3. Cache results when possible
1197
+ 4. Use traditional mode for simple cases
1198
+
1199
+ ## Contributing
1200
+
1201
+ Contributions are welcome! This library was extracted from [kodrdriv](https://github.com/calenvarek/kodrdriv).
1202
+
1203
+ ### Development Setup
336
1204
 
337
1205
  ```bash
338
- # Install dependencies
1206
+ git clone https://github.com/calenvarek/ai-service.git
1207
+ cd ai-service
339
1208
  npm install
340
-
341
- # Build
342
1209
  npm run build
1210
+ npm test
1211
+ ```
343
1212
 
344
- # Test
345
- npm run test
1213
+ ### Running Tests
346
1214
 
347
- # Lint
348
- npm run lint
1215
+ ```bash
1216
+ npm test # Run all tests
1217
+ npm test -- --watch # Watch mode
1218
+ npm test -- --coverage # With coverage
349
1219
  ```
350
1220
 
351
1221
  ## License
352
1222
 
353
1223
  Apache-2.0
1224
+
1225
+ ## Related Projects
1226
+
1227
+ - **[kodrdriv](https://github.com/calenvarek/kodrdriv)** - Full automation toolkit that uses this library
1228
+ - **[@eldrforge/git-tools](https://www.npmjs.com/package/@eldrforge/git-tools)** - Git utility functions
1229
+ - **[@riotprompt/riotprompt](https://www.npmjs.com/package/@riotprompt/riotprompt)** - Structured prompt builder
1230
+
1231
+ ## Support
1232
+
1233
+ - 📖 [Full Documentation](https://github.com/calenvarek/ai-service)
1234
+ - 🐛 [Issue Tracker](https://github.com/calenvarek/ai-service/issues)
1235
+ - 💬 [Discussions](https://github.com/calenvarek/ai-service/discussions)
1236
+
1237
+ ## Changelog
1238
+
1239
+ See [RELEASE_NOTES.md](./RELEASE_NOTES.md) for version history and changes.