@eldrforge/ai-service 0.1.13 ā 0.1.15
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 +1112 -226
- package/dist/index.js +1325 -108
- package/dist/index.js.map +1 -1
- package/dist/src/agentic/commit.d.ts +6 -0
- package/dist/src/agentic/commit.d.ts.map +1 -1
- package/dist/src/agentic/executor.d.ts +7 -1
- package/dist/src/agentic/executor.d.ts.map +1 -1
- package/dist/src/agentic/publish.d.ts +31 -0
- package/dist/src/agentic/publish.d.ts.map +1 -0
- package/dist/src/agentic/release.d.ts +6 -0
- package/dist/src/agentic/release.d.ts.map +1 -1
- package/dist/src/ai.d.ts.map +1 -1
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/observability/conversation-logger.d.ts +53 -0
- package/dist/src/observability/conversation-logger.d.ts.map +1 -0
- package/dist/src/observability/index.d.ts +15 -0
- package/dist/src/observability/index.d.ts.map +1 -0
- package/dist/src/observability/metrics.d.ts +53 -0
- package/dist/src/observability/metrics.d.ts.map +1 -0
- package/dist/src/observability/reflection.d.ts +36 -0
- package/dist/src/observability/reflection.d.ts.map +1 -0
- package/dist/src/prompts/commit.d.ts.map +1 -1
- package/dist/src/prompts/index.d.ts +1 -0
- package/dist/src/prompts/index.d.ts.map +1 -1
- package/dist/src/prompts/release.d.ts.map +1 -1
- package/dist/src/prompts/review.d.ts.map +1 -1
- package/dist/src/prompts/templates.d.ts +22 -0
- package/dist/src/prompts/templates.d.ts.map +1 -0
- package/dist/src/tools/publish-tools.d.ts +6 -0
- package/dist/src/tools/publish-tools.d.ts.map +1 -0
- package/dist/src/tools/types.d.ts +17 -0
- package/dist/src/tools/types.d.ts.map +1 -1
- package/examples/01-simple-commit.ts +80 -0
- package/examples/02-release-notes.ts +124 -0
- package/examples/03-interactive-commit.ts +150 -0
- package/examples/04-custom-storage.ts +162 -0
- package/examples/05-custom-tools.ts +243 -0
- package/examples/README.md +320 -0
- package/package.json +6 -5
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Example 3: Interactive Commit Workflow
|
|
4
|
+
*
|
|
5
|
+
* This example demonstrates an interactive workflow where the user can:
|
|
6
|
+
* - Generate an initial commit message
|
|
7
|
+
* - Review and choose to confirm, edit, or regenerate
|
|
8
|
+
* - Optionally add user feedback for improvement
|
|
9
|
+
* - Automatically create the commit
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* 1. Stage changes: git add .
|
|
13
|
+
* 2. Run: npx tsx examples/03-interactive-commit.ts
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/* eslint-disable no-console */
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
runAgenticCommit,
|
|
20
|
+
getUserChoice,
|
|
21
|
+
editContentInEditor,
|
|
22
|
+
STANDARD_CHOICES,
|
|
23
|
+
} from '@eldrforge/ai-service';
|
|
24
|
+
import { execSync } from 'child_process';
|
|
25
|
+
|
|
26
|
+
async function main() {
|
|
27
|
+
try {
|
|
28
|
+
// Check for staged changes
|
|
29
|
+
const diffContent = execSync('git diff --staged', { encoding: 'utf8' });
|
|
30
|
+
|
|
31
|
+
if (!diffContent.trim()) {
|
|
32
|
+
console.log('ā No staged changes found.');
|
|
33
|
+
console.log(' Stage some changes first with: git add <files>');
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Get changed files
|
|
38
|
+
const statusOutput = execSync('git status --porcelain', { encoding: 'utf8' });
|
|
39
|
+
const changedFiles = statusOutput
|
|
40
|
+
.split('\n')
|
|
41
|
+
.filter(line => line.trim())
|
|
42
|
+
.map(line => line.substring(3));
|
|
43
|
+
|
|
44
|
+
console.log('š Changed files:', changedFiles.length);
|
|
45
|
+
changedFiles.forEach(file => console.log(` - ${file}`));
|
|
46
|
+
console.log('');
|
|
47
|
+
|
|
48
|
+
// Generate initial commit message
|
|
49
|
+
console.log('š¤ Generating commit message...\n');
|
|
50
|
+
|
|
51
|
+
const result = await runAgenticCommit({
|
|
52
|
+
changedFiles,
|
|
53
|
+
diffContent,
|
|
54
|
+
model: 'gpt-4o-mini',
|
|
55
|
+
maxIterations: 10,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
let currentMessage = result.commitMessage;
|
|
59
|
+
let shouldContinue = true;
|
|
60
|
+
|
|
61
|
+
while (shouldContinue) {
|
|
62
|
+
// Display current message
|
|
63
|
+
console.log('\n⨠Commit Message:\n');
|
|
64
|
+
console.log('ā'.repeat(60));
|
|
65
|
+
console.log(currentMessage);
|
|
66
|
+
console.log('ā'.repeat(60));
|
|
67
|
+
console.log('');
|
|
68
|
+
|
|
69
|
+
// Get user choice
|
|
70
|
+
const choice = await getUserChoice(
|
|
71
|
+
'What would you like to do?',
|
|
72
|
+
[
|
|
73
|
+
STANDARD_CHOICES.CONFIRM,
|
|
74
|
+
STANDARD_CHOICES.EDIT,
|
|
75
|
+
STANDARD_CHOICES.SKIP,
|
|
76
|
+
],
|
|
77
|
+
{
|
|
78
|
+
nonTtyErrorSuggestions: [
|
|
79
|
+
'Run in a terminal environment',
|
|
80
|
+
'Use examples/01-simple-commit.ts for non-interactive mode',
|
|
81
|
+
],
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
switch (choice) {
|
|
86
|
+
case 'c': { // Confirm
|
|
87
|
+
console.log('\nš Creating commit...');
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
execSync('git', ['commit', '-m', currentMessage], {
|
|
91
|
+
stdio: 'inherit',
|
|
92
|
+
});
|
|
93
|
+
console.log('ā
Commit created successfully!');
|
|
94
|
+
} catch (commitError) {
|
|
95
|
+
console.error('ā Failed to create commit:', commitError);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
shouldContinue = false;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
case 'e': { // Edit
|
|
103
|
+
console.log('\nš Opening editor...');
|
|
104
|
+
|
|
105
|
+
const edited = await editContentInEditor(
|
|
106
|
+
currentMessage,
|
|
107
|
+
[
|
|
108
|
+
'# Edit your commit message below',
|
|
109
|
+
'# Lines starting with # will be removed',
|
|
110
|
+
'# Save and close the editor when done',
|
|
111
|
+
],
|
|
112
|
+
'.txt'
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
if (edited.wasEdited) {
|
|
116
|
+
currentMessage = edited.content;
|
|
117
|
+
console.log('\nā
Commit message updated!');
|
|
118
|
+
} else {
|
|
119
|
+
console.log('\nā ļø No changes made');
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
case 's': // Skip
|
|
125
|
+
console.log('\nāļø Commit cancelled. Your changes are still staged.');
|
|
126
|
+
shouldContinue = false;
|
|
127
|
+
break;
|
|
128
|
+
|
|
129
|
+
default:
|
|
130
|
+
console.log('\nā Invalid choice');
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.error('ā Error:', error instanceof Error ? error.message : String(error));
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Check if running in TTY
|
|
142
|
+
if (!process.stdin.isTTY) {
|
|
143
|
+
console.error('ā This example requires a terminal (TTY).');
|
|
144
|
+
console.error(' Run it directly in your terminal, not piped or in CI.');
|
|
145
|
+
console.error(' For non-interactive mode, use: examples/01-simple-commit.ts');
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
main();
|
|
150
|
+
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Example 4: Custom Storage Adapter
|
|
4
|
+
*
|
|
5
|
+
* This example demonstrates how to implement a custom storage adapter
|
|
6
|
+
* to save AI-generated content to a specific location or cloud storage.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* npx tsx examples/04-custom-storage.ts
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* eslint-disable no-console */
|
|
13
|
+
|
|
14
|
+
import { runAgenticCommit, type StorageAdapter } from '@eldrforge/ai-service';
|
|
15
|
+
import { execSync } from 'child_process';
|
|
16
|
+
import * as fs from 'fs/promises';
|
|
17
|
+
import * as path from 'path';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Custom storage adapter that saves files to a structured output directory
|
|
21
|
+
*/
|
|
22
|
+
class CustomStorageAdapter implements StorageAdapter {
|
|
23
|
+
private outputDir: string;
|
|
24
|
+
private tempDir: string;
|
|
25
|
+
|
|
26
|
+
constructor(outputDir: string, tempDir: string = '/tmp') {
|
|
27
|
+
this.outputDir = outputDir;
|
|
28
|
+
this.tempDir = tempDir;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async writeOutput(fileName: string, content: string): Promise<void> {
|
|
32
|
+
// Ensure output directory exists
|
|
33
|
+
await fs.mkdir(this.outputDir, { recursive: true });
|
|
34
|
+
|
|
35
|
+
const filePath = path.join(this.outputDir, fileName);
|
|
36
|
+
await fs.writeFile(filePath, content, 'utf8');
|
|
37
|
+
|
|
38
|
+
console.log(` š Saved to: ${filePath}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async readTemp(fileName: string): Promise<string> {
|
|
42
|
+
const filePath = path.join(this.tempDir, fileName);
|
|
43
|
+
return fs.readFile(filePath, 'utf8');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async writeTemp(fileName: string, content: string): Promise<void> {
|
|
47
|
+
await fs.mkdir(this.tempDir, { recursive: true });
|
|
48
|
+
const filePath = path.join(this.tempDir, fileName);
|
|
49
|
+
await fs.writeFile(filePath, content, 'utf8');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async readFile(fileName: string): Promise<string> {
|
|
53
|
+
return fs.readFile(fileName, 'utf8');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Alternative: Cloud storage adapter (example structure)
|
|
59
|
+
* In a real implementation, you would integrate with AWS S3, Google Cloud Storage, etc.
|
|
60
|
+
*/
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
62
|
+
class CloudStorageAdapter implements StorageAdapter {
|
|
63
|
+
async writeOutput(fileName: string, _content: string): Promise<void> {
|
|
64
|
+
// Example: Upload to S3
|
|
65
|
+
// const s3 = new S3Client({ region: 'us-east-1' });
|
|
66
|
+
// await s3.send(new PutObjectCommand({
|
|
67
|
+
// Bucket: 'my-bucket',
|
|
68
|
+
// Key: `ai-output/${fileName}`,
|
|
69
|
+
// Body: content,
|
|
70
|
+
// }));
|
|
71
|
+
|
|
72
|
+
console.log(` āļø Would upload to cloud: ${fileName}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async readTemp(fileName: string): Promise<string> {
|
|
76
|
+
// Local temp files
|
|
77
|
+
return fs.readFile(path.join('/tmp', fileName), 'utf8');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async writeTemp(fileName: string, content: string): Promise<void> {
|
|
81
|
+
await fs.writeFile(path.join('/tmp', fileName), content, 'utf8');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async readFile(fileName: string): Promise<string> {
|
|
85
|
+
return fs.readFile(fileName, 'utf8');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function main() {
|
|
90
|
+
try {
|
|
91
|
+
console.log('š Checking for staged changes...\n');
|
|
92
|
+
|
|
93
|
+
const diffContent = execSync('git diff --staged', { encoding: 'utf8' });
|
|
94
|
+
|
|
95
|
+
if (!diffContent.trim()) {
|
|
96
|
+
console.log('ā No staged changes found.');
|
|
97
|
+
console.log(' Stage some changes first with: git add <files>');
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const statusOutput = execSync('git status --porcelain', { encoding: 'utf8' });
|
|
102
|
+
const changedFiles = statusOutput
|
|
103
|
+
.split('\n')
|
|
104
|
+
.filter(line => line.trim())
|
|
105
|
+
.map(line => line.substring(3));
|
|
106
|
+
|
|
107
|
+
console.log('š Files changed:', changedFiles.length);
|
|
108
|
+
console.log('');
|
|
109
|
+
|
|
110
|
+
// Create custom storage adapter
|
|
111
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
112
|
+
const outputDir = path.join(process.cwd(), 'output', 'ai-service', timestamp);
|
|
113
|
+
const storage = new CustomStorageAdapter(outputDir);
|
|
114
|
+
|
|
115
|
+
console.log('š¤ Generating commit message with custom storage...\n');
|
|
116
|
+
|
|
117
|
+
const result = await runAgenticCommit({
|
|
118
|
+
changedFiles,
|
|
119
|
+
diffContent,
|
|
120
|
+
model: 'gpt-4o-mini',
|
|
121
|
+
maxIterations: 10,
|
|
122
|
+
storage,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
console.log('\n⨠Generated Commit Message:\n');
|
|
126
|
+
console.log('ā'.repeat(60));
|
|
127
|
+
console.log(result.commitMessage);
|
|
128
|
+
console.log('ā'.repeat(60));
|
|
129
|
+
console.log('');
|
|
130
|
+
|
|
131
|
+
// Save additional metadata
|
|
132
|
+
console.log('š¾ Saving files...\n');
|
|
133
|
+
|
|
134
|
+
await storage.writeOutput('commit-message.txt', result.commitMessage);
|
|
135
|
+
|
|
136
|
+
await storage.writeOutput(
|
|
137
|
+
'metadata.json',
|
|
138
|
+
JSON.stringify({
|
|
139
|
+
timestamp: new Date().toISOString(),
|
|
140
|
+
iterations: result.iterations,
|
|
141
|
+
toolCallsExecuted: result.toolCallsExecuted,
|
|
142
|
+
model: 'gpt-4o-mini',
|
|
143
|
+
changedFiles,
|
|
144
|
+
}, null, 2)
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Save tool metrics
|
|
148
|
+
await storage.writeOutput(
|
|
149
|
+
'tool-metrics.json',
|
|
150
|
+
JSON.stringify(result.toolMetrics, null, 2)
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
console.log('\nā
Complete! All files saved to:', outputDir);
|
|
154
|
+
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error('ā Error:', error instanceof Error ? error.message : String(error));
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
main();
|
|
162
|
+
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Example 5: Custom Tool Integration
|
|
4
|
+
*
|
|
5
|
+
* This example demonstrates how to create and register custom tools
|
|
6
|
+
* that extend the AI's capabilities for your specific use case.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* npx tsx examples/05-custom-tools.ts
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* eslint-disable no-console */
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
createToolRegistry,
|
|
16
|
+
runAgentic,
|
|
17
|
+
type Tool,
|
|
18
|
+
type ToolContext,
|
|
19
|
+
} from '@eldrforge/ai-service';
|
|
20
|
+
import { execSync } from 'child_process';
|
|
21
|
+
import * as fs from 'fs/promises';
|
|
22
|
+
import * as path from 'path';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Custom tool: Check test coverage
|
|
26
|
+
*/
|
|
27
|
+
const checkTestCoverage: Tool = {
|
|
28
|
+
name: 'check_test_coverage',
|
|
29
|
+
description: 'Check test coverage for the project or specific files',
|
|
30
|
+
parameters: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
properties: {
|
|
33
|
+
filePath: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
description: 'Optional specific file to check coverage for',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
execute: async (params: { filePath?: string }, context?: ToolContext) => {
|
|
40
|
+
try {
|
|
41
|
+
const cmd = params.filePath
|
|
42
|
+
? `npm test -- --coverage --testPathPattern="${params.filePath}"`
|
|
43
|
+
: 'npm test -- --coverage --silent 2>&1';
|
|
44
|
+
|
|
45
|
+
const output = execSync(cmd, {
|
|
46
|
+
encoding: 'utf8',
|
|
47
|
+
cwd: context?.workingDirectory || process.cwd(),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Parse coverage from output
|
|
51
|
+
const coverageMatch = output.match(/All files.*?(\d+\.?\d*)/);
|
|
52
|
+
const coverage = coverageMatch ? parseFloat(coverageMatch[1]) : 0;
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
success: true,
|
|
56
|
+
coverage: `${coverage}%`,
|
|
57
|
+
message: `Test coverage: ${coverage}%`,
|
|
58
|
+
};
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return {
|
|
61
|
+
success: false,
|
|
62
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Custom tool: Check for linter errors
|
|
70
|
+
*/
|
|
71
|
+
const checkLinterErrors: Tool = {
|
|
72
|
+
name: 'check_linter_errors',
|
|
73
|
+
description: 'Check for linter/ESLint errors in the codebase',
|
|
74
|
+
parameters: {
|
|
75
|
+
type: 'object',
|
|
76
|
+
properties: {
|
|
77
|
+
filePath: {
|
|
78
|
+
type: 'string',
|
|
79
|
+
description: 'Optional specific file to lint',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
execute: async (params: { filePath?: string }, context?: ToolContext) => {
|
|
84
|
+
try {
|
|
85
|
+
const target = params.filePath || '.';
|
|
86
|
+
const cmd = `npx eslint ${target} --format json`;
|
|
87
|
+
|
|
88
|
+
const output = execSync(cmd, {
|
|
89
|
+
encoding: 'utf8',
|
|
90
|
+
cwd: context?.workingDirectory || process.cwd(),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const results = JSON.parse(output);
|
|
94
|
+
const errorCount = results.reduce((sum: number, file: any) => sum + file.errorCount, 0);
|
|
95
|
+
const warningCount = results.reduce((sum: number, file: any) => sum + file.warningCount, 0);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
success: errorCount === 0,
|
|
99
|
+
errors: errorCount,
|
|
100
|
+
warnings: warningCount,
|
|
101
|
+
message: `Found ${errorCount} errors and ${warningCount} warnings`,
|
|
102
|
+
};
|
|
103
|
+
} catch (error) {
|
|
104
|
+
return {
|
|
105
|
+
success: false,
|
|
106
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Custom tool: Analyze package dependencies
|
|
114
|
+
*/
|
|
115
|
+
const analyzePackageDeps: Tool = {
|
|
116
|
+
name: 'analyze_package_dependencies',
|
|
117
|
+
description: 'Analyze package.json dependencies and check for outdated packages',
|
|
118
|
+
parameters: {
|
|
119
|
+
type: 'object',
|
|
120
|
+
properties: {},
|
|
121
|
+
},
|
|
122
|
+
execute: async (_params: any, context?: ToolContext) => {
|
|
123
|
+
try {
|
|
124
|
+
const packageJsonPath = path.join(
|
|
125
|
+
context?.workingDirectory || process.cwd(),
|
|
126
|
+
'package.json'
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const packageJson = JSON.parse(
|
|
130
|
+
await fs.readFile(packageJsonPath, 'utf8')
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const deps = packageJson.dependencies || {};
|
|
134
|
+
const devDeps = packageJson.devDependencies || {};
|
|
135
|
+
const totalDeps = Object.keys(deps).length + Object.keys(devDeps).length;
|
|
136
|
+
|
|
137
|
+
// Check for outdated packages
|
|
138
|
+
let outdatedOutput = '';
|
|
139
|
+
try {
|
|
140
|
+
outdatedOutput = execSync('npm outdated --json', {
|
|
141
|
+
encoding: 'utf8',
|
|
142
|
+
cwd: context?.workingDirectory || process.cwd(),
|
|
143
|
+
});
|
|
144
|
+
} catch {
|
|
145
|
+
// npm outdated exits with code 1 if there are outdated packages
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const outdated = outdatedOutput ? JSON.parse(outdatedOutput) : {};
|
|
149
|
+
const outdatedCount = Object.keys(outdated).length;
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
success: true,
|
|
153
|
+
totalDependencies: totalDeps,
|
|
154
|
+
outdatedPackages: outdatedCount,
|
|
155
|
+
dependencies: Object.keys(deps),
|
|
156
|
+
devDependencies: Object.keys(devDeps),
|
|
157
|
+
outdatedDetails: outdated,
|
|
158
|
+
};
|
|
159
|
+
} catch (error) {
|
|
160
|
+
return {
|
|
161
|
+
success: false,
|
|
162
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
async function main() {
|
|
169
|
+
try {
|
|
170
|
+
console.log('š§ Creating tool registry with custom tools...\n');
|
|
171
|
+
|
|
172
|
+
// Create tool registry
|
|
173
|
+
const registry = createToolRegistry({
|
|
174
|
+
workingDirectory: process.cwd(),
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Register custom tools
|
|
178
|
+
registry.register(checkTestCoverage);
|
|
179
|
+
registry.register(checkLinterErrors);
|
|
180
|
+
registry.register(analyzePackageDeps);
|
|
181
|
+
|
|
182
|
+
console.log('ā
Registered custom tools:');
|
|
183
|
+
console.log(' - check_test_coverage');
|
|
184
|
+
console.log(' - check_linter_errors');
|
|
185
|
+
console.log(' - analyze_package_dependencies');
|
|
186
|
+
console.log('');
|
|
187
|
+
|
|
188
|
+
// Create a conversation that uses these tools
|
|
189
|
+
const messages = [
|
|
190
|
+
{
|
|
191
|
+
role: 'system' as const,
|
|
192
|
+
content: `You are a code quality analyst. Use the available tools to assess the project's health.
|
|
193
|
+
|
|
194
|
+
Your task:
|
|
195
|
+
1. Check test coverage
|
|
196
|
+
2. Check for linter errors
|
|
197
|
+
3. Analyze package dependencies
|
|
198
|
+
|
|
199
|
+
Then provide a summary report of the project's code quality status.`,
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
role: 'user' as const,
|
|
203
|
+
content: 'Please analyze the current project and provide a code quality report.',
|
|
204
|
+
},
|
|
205
|
+
];
|
|
206
|
+
|
|
207
|
+
console.log('š¤ Running AI analysis with custom tools...\n');
|
|
208
|
+
|
|
209
|
+
const result = await runAgentic({
|
|
210
|
+
messages,
|
|
211
|
+
tools: registry,
|
|
212
|
+
model: 'gpt-4o-mini',
|
|
213
|
+
maxIterations: 10,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
console.log('⨠Analysis Complete!\n');
|
|
217
|
+
console.log('ā'.repeat(70));
|
|
218
|
+
console.log(result.finalMessage);
|
|
219
|
+
console.log('ā'.repeat(70));
|
|
220
|
+
console.log('');
|
|
221
|
+
console.log(`š Tool calls made: ${result.toolCallsExecuted}`);
|
|
222
|
+
console.log(`š Iterations: ${result.iterations}`);
|
|
223
|
+
console.log('');
|
|
224
|
+
|
|
225
|
+
// Show which tools were used
|
|
226
|
+
const toolUsage: Record<string, number> = {};
|
|
227
|
+
result.toolMetrics.forEach(metric => {
|
|
228
|
+
toolUsage[metric.name] = (toolUsage[metric.name] || 0) + 1;
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
console.log('š§ Tools used:');
|
|
232
|
+
Object.entries(toolUsage).forEach(([tool, count]) => {
|
|
233
|
+
console.log(` - ${tool}: ${count}x`);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.error('ā Error:', error instanceof Error ? error.message : String(error));
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
main();
|
|
243
|
+
|