matimo-examples 0.1.0-alpha.11
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/.env.example +49 -0
- package/LICENSE +21 -0
- package/README.md +525 -0
- package/agents/decorator-pattern-agent.ts +368 -0
- package/agents/factory-pattern-agent.ts +253 -0
- package/agents/langchain-agent.ts +146 -0
- package/edit/edit-decorator.ts +178 -0
- package/edit/edit-factory.ts +138 -0
- package/edit/edit-langchain.ts +292 -0
- package/execute/execute-decorator.ts +49 -0
- package/execute/execute-factory.ts +46 -0
- package/execute/execute-langchain.ts +232 -0
- package/github/github-decorator.ts +326 -0
- package/github/github-factory.ts +355 -0
- package/github/github-langchain.ts +206 -0
- package/github/github-with-approval.ts +228 -0
- package/gmail/README.md +345 -0
- package/gmail/gmail-decorator.ts +216 -0
- package/gmail/gmail-factory.ts +231 -0
- package/gmail/gmail-langchain.ts +201 -0
- package/hubspot/README.md +316 -0
- package/hubspot/hubspot-decorator.ts +180 -0
- package/hubspot/hubspot-factory.ts +188 -0
- package/hubspot/hubspot-langchain.ts +222 -0
- package/logger-example.ts +40 -0
- package/mailchimp/README.md +321 -0
- package/mailchimp/mailchimp-decorator.ts +277 -0
- package/mailchimp/mailchimp-factory.ts +187 -0
- package/mailchimp/mailchimp-langchain.ts +155 -0
- package/notion/README.md +293 -0
- package/notion/notion-decorator.ts +275 -0
- package/notion/notion-factory.ts +256 -0
- package/notion/notion-langchain.ts +237 -0
- package/package.json +79 -0
- package/postgres/README.md +188 -0
- package/postgres/postgres-decorator.ts +198 -0
- package/postgres/postgres-factory.ts +180 -0
- package/postgres/postgres-langchain.ts +213 -0
- package/postgres/postgres-with-approval.ts +344 -0
- package/read/read-decorator.ts +154 -0
- package/read/read-factory.ts +121 -0
- package/read/read-langchain.ts +273 -0
- package/search/search-decorator.ts +206 -0
- package/search/search-factory.ts +146 -0
- package/search/search-langchain.ts +255 -0
- package/slack/README.md +339 -0
- package/slack/slack-decorator.ts +245 -0
- package/slack/slack-factory.ts +226 -0
- package/slack/slack-langchain.ts +242 -0
- package/tsconfig.json +20 -0
- package/twilio/README.md +309 -0
- package/twilio/twilio-decorator.ts +288 -0
- package/twilio/twilio-factory.ts +238 -0
- package/twilio/twilio-langchain.ts +218 -0
- package/web/web-decorator.ts +52 -0
- package/web/web-factory.ts +70 -0
- package/web/web-langchain.ts +163 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MatimoInstance,
|
|
3
|
+
setGlobalMatimoInstance,
|
|
4
|
+
tool,
|
|
5
|
+
getGlobalApprovalHandler,
|
|
6
|
+
type ApprovalRequest,
|
|
7
|
+
} from '@matimo/core';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import * as readline from 'readline';
|
|
11
|
+
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Create an interactive approval callback
|
|
16
|
+
*/
|
|
17
|
+
function createApprovalCallback() {
|
|
18
|
+
return async (request: ApprovalRequest): Promise<boolean> => {
|
|
19
|
+
const isInteractive = process.stdin.isTTY;
|
|
20
|
+
|
|
21
|
+
console.info('\n' + '='.repeat(70));
|
|
22
|
+
console.info('๐ APPROVAL REQUIRED FOR FILE OPERATION');
|
|
23
|
+
console.info('='.repeat(70));
|
|
24
|
+
console.info(`\n๐ Tool: ${request.toolName}`);
|
|
25
|
+
console.info(`๐ Description: ${request.description || '(no description provided)'}`);
|
|
26
|
+
console.info(`\n๐ Search Operation:`);
|
|
27
|
+
console.info(` Query: ${request.params.query}`);
|
|
28
|
+
console.info(` Directory: ${request.params.directory}`);
|
|
29
|
+
if (request.params.filePattern) {
|
|
30
|
+
console.info(` File Pattern: ${request.params.filePattern}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!isInteractive) {
|
|
34
|
+
console.info('\nโ REJECTED - Non-interactive environment (no terminal)');
|
|
35
|
+
console.info('\n๐ก To enable auto-approval in CI/scripts:');
|
|
36
|
+
console.info(' export MATIMO_AUTO_APPROVE=true');
|
|
37
|
+
console.info('\n๐ก Or approve specific patterns:');
|
|
38
|
+
console.info(' export MATIMO_APPROVED_PATTERNS="search"');
|
|
39
|
+
console.info('\n' + '='.repeat(70) + '\n');
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Interactive mode: prompt user
|
|
44
|
+
const rl = readline.createInterface({
|
|
45
|
+
input: process.stdin,
|
|
46
|
+
output: process.stdout,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
console.info('\nโ User Action Required');
|
|
51
|
+
const question = ' Type "yes" to approve or "no" to reject: ';
|
|
52
|
+
|
|
53
|
+
rl.question(question, (answer) => {
|
|
54
|
+
const approved = answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y';
|
|
55
|
+
|
|
56
|
+
if (approved) {
|
|
57
|
+
console.info(' โ
Operation APPROVED by user');
|
|
58
|
+
} else {
|
|
59
|
+
console.info(' โ Operation REJECTED by user');
|
|
60
|
+
}
|
|
61
|
+
console.info('='.repeat(70) + '\n');
|
|
62
|
+
|
|
63
|
+
rl.close();
|
|
64
|
+
resolve(approved);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Example: Search tool using @tool decorator pattern
|
|
72
|
+
* Demonstrates class-based file search with automatic decoration
|
|
73
|
+
*/
|
|
74
|
+
class FileSearcher {
|
|
75
|
+
@tool('search')
|
|
76
|
+
async findPattern(
|
|
77
|
+
query: string,
|
|
78
|
+
directory: string,
|
|
79
|
+
filePattern: string,
|
|
80
|
+
maxResults?: number
|
|
81
|
+
): Promise<unknown> {
|
|
82
|
+
// Decorator automatically intercepts and executes via Matimo with:
|
|
83
|
+
// { query, directory, filePattern, maxResults }
|
|
84
|
+
// Positional args map to parameters in tool definition order
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@tool('search')
|
|
89
|
+
async searchInDirectory(
|
|
90
|
+
query: string,
|
|
91
|
+
directory: string,
|
|
92
|
+
filePattern?: string
|
|
93
|
+
): Promise<unknown> {
|
|
94
|
+
// Decorator automatically intercepts and executes via Matimo with:
|
|
95
|
+
// { query, directory, filePattern }
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@tool('search')
|
|
100
|
+
async regexSearch(
|
|
101
|
+
query: string,
|
|
102
|
+
directory: string,
|
|
103
|
+
filePattern: string,
|
|
104
|
+
isRegex: boolean = true,
|
|
105
|
+
maxResults?: number
|
|
106
|
+
): Promise<unknown> {
|
|
107
|
+
// Decorator automatically intercepts and executes via Matimo with:
|
|
108
|
+
// { query, directory, filePattern, isRegex, maxResults }
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function decoratorExample() {
|
|
114
|
+
// Set up decorator support with autoDiscover
|
|
115
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
116
|
+
setGlobalMatimoInstance(matimo);
|
|
117
|
+
|
|
118
|
+
// Configure centralized approval handler
|
|
119
|
+
const approvalHandler = getGlobalApprovalHandler();
|
|
120
|
+
approvalHandler.setApprovalCallback(createApprovalCallback());
|
|
121
|
+
|
|
122
|
+
console.info('\n' + '='.repeat(70));
|
|
123
|
+
console.info('๐ Search Tool - Decorator Pattern Example');
|
|
124
|
+
console.info('='.repeat(70));
|
|
125
|
+
|
|
126
|
+
// Show current approval mode
|
|
127
|
+
const autoApproveEnabled = process.env.MATIMO_AUTO_APPROVE === 'true';
|
|
128
|
+
const approvedPatterns = process.env.MATIMO_APPROVED_PATTERNS;
|
|
129
|
+
|
|
130
|
+
console.info('\n๐ APPROVAL CONFIGURATION:');
|
|
131
|
+
if (autoApproveEnabled) {
|
|
132
|
+
console.info(' โ
MATIMO_AUTO_APPROVE=true');
|
|
133
|
+
console.info(' โ All search operations will be AUTO-APPROVED');
|
|
134
|
+
} else if (approvedPatterns) {
|
|
135
|
+
console.info(` โ
MATIMO_APPROVED_PATTERNS="${approvedPatterns}"`);
|
|
136
|
+
console.info(' โ Matching operations will be auto-approved');
|
|
137
|
+
} else {
|
|
138
|
+
console.info(' โ ๏ธ INTERACTIVE MODE ENABLED');
|
|
139
|
+
console.info(' โ You will be prompted to approve search operations');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Get the workspace root (parent of examples directory)
|
|
143
|
+
// File is in examples/tools/search/, so go up 3 levels
|
|
144
|
+
const workspaceRoot = path.resolve(__dirname, '../../..');
|
|
145
|
+
|
|
146
|
+
const searcher = new FileSearcher();
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
// Example 1: Find pattern through decorated method
|
|
150
|
+
console.info('\n1๏ธโฃ SEARCHING FOR A PATTERN');
|
|
151
|
+
console.info('-'.repeat(70));
|
|
152
|
+
console.info('Finding "export" in TypeScript files (max 5 results)\n');
|
|
153
|
+
const result1 = await searcher.findPattern(
|
|
154
|
+
'export',
|
|
155
|
+
path.join(workspaceRoot, 'packages/core/src'),
|
|
156
|
+
'*.ts',
|
|
157
|
+
5
|
|
158
|
+
);
|
|
159
|
+
if (result1) {
|
|
160
|
+
console.info('โ
Total matches:', (result1 as any).totalMatches);
|
|
161
|
+
console.info('๐ Matches found:', (result1 as any).matches?.length);
|
|
162
|
+
if ((result1 as any).matches?.length > 0) {
|
|
163
|
+
console.info('๐ First match:', (result1 as any).matches[0]);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
console.info('---\n');
|
|
167
|
+
|
|
168
|
+
// Example 2: Search in specific directory
|
|
169
|
+
console.info('2๏ธโฃ SEARCHING IN EXAMPLES DIRECTORY');
|
|
170
|
+
console.info('-'.repeat(70));
|
|
171
|
+
console.info('Searching for "async" in TypeScript files\n');
|
|
172
|
+
const result2 = await searcher.searchInDirectory(
|
|
173
|
+
'async',
|
|
174
|
+
path.join(workspaceRoot, 'examples/tools'),
|
|
175
|
+
'*.ts'
|
|
176
|
+
);
|
|
177
|
+
if (result2) {
|
|
178
|
+
console.info('โ
Total matches:', (result2 as any).totalMatches);
|
|
179
|
+
console.info('๐ Matches found:', (result2 as any).matches?.length);
|
|
180
|
+
}
|
|
181
|
+
console.info('---\n');
|
|
182
|
+
|
|
183
|
+
// Example 3: Regex search
|
|
184
|
+
console.info('3๏ธโฃ USING REGEX PATTERN');
|
|
185
|
+
console.info('-'.repeat(70));
|
|
186
|
+
console.info('Finding "console.info" calls (max 5 results)\n');
|
|
187
|
+
const result3 = await searcher.regexSearch(
|
|
188
|
+
'console\\.info',
|
|
189
|
+
path.join(workspaceRoot, 'packages/core/src'),
|
|
190
|
+
'*.ts',
|
|
191
|
+
true,
|
|
192
|
+
5
|
|
193
|
+
);
|
|
194
|
+
if (result3) {
|
|
195
|
+
console.info('โ
Total matches:', (result3 as any).totalMatches);
|
|
196
|
+
console.info('๐ Matches found:', (result3 as any).matches?.length);
|
|
197
|
+
}
|
|
198
|
+
console.info('---\n');
|
|
199
|
+
|
|
200
|
+
console.info('โ
Decorator example completed successfully');
|
|
201
|
+
} catch (error: any) {
|
|
202
|
+
console.error('โ Error:', error.message);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
decoratorExample();
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { MatimoInstance, getGlobalApprovalHandler, type ApprovalRequest } from '@matimo/core';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import * as readline from 'readline';
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Create an interactive approval callback for file operations
|
|
10
|
+
*/
|
|
11
|
+
function createApprovalCallback() {
|
|
12
|
+
return async (request: ApprovalRequest): Promise<boolean> => {
|
|
13
|
+
const isInteractive = process.stdin.isTTY;
|
|
14
|
+
|
|
15
|
+
console.info('\n' + '='.repeat(70));
|
|
16
|
+
console.info('๐ APPROVAL REQUIRED FOR FILE OPERATION');
|
|
17
|
+
console.info('='.repeat(70));
|
|
18
|
+
console.info(`\n๐ Tool: ${request.toolName}`);
|
|
19
|
+
console.info(`๐ Description: ${request.description || '(no description provided)'}`);
|
|
20
|
+
console.info(`\n๐ File Operation:`);
|
|
21
|
+
console.info(` Path: ${request.params.filePath}`);
|
|
22
|
+
if (request.params.startLine) {
|
|
23
|
+
console.info(` Start Line: ${request.params.startLine}`);
|
|
24
|
+
}
|
|
25
|
+
if (request.params.endLine) {
|
|
26
|
+
console.info(` End Line: ${request.params.endLine}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!isInteractive) {
|
|
30
|
+
console.info('\nโ REJECTED - Non-interactive environment (no terminal)');
|
|
31
|
+
console.info('\n๐ก To enable auto-approval in CI/scripts:');
|
|
32
|
+
console.info(' export MATIMO_AUTO_APPROVE=true');
|
|
33
|
+
console.info('\n๐ก Or approve specific patterns:');
|
|
34
|
+
console.info(' export MATIMO_APPROVED_PATTERNS="search"');
|
|
35
|
+
console.info('\n' + '='.repeat(70) + '\n');
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Interactive mode: prompt user
|
|
40
|
+
const rl = readline.createInterface({
|
|
41
|
+
input: process.stdin,
|
|
42
|
+
output: process.stdout,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
console.info('\nโ User Action Required');
|
|
47
|
+
const question = ' Type "yes" to approve or "no" to reject: ';
|
|
48
|
+
|
|
49
|
+
rl.question(question, (answer) => {
|
|
50
|
+
const approved = answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y';
|
|
51
|
+
|
|
52
|
+
if (approved) {
|
|
53
|
+
console.info(' โ
Operation APPROVED by user');
|
|
54
|
+
} else {
|
|
55
|
+
console.info(' โ Operation REJECTED by user');
|
|
56
|
+
}
|
|
57
|
+
console.info('='.repeat(70) + '\n');
|
|
58
|
+
|
|
59
|
+
rl.close();
|
|
60
|
+
resolve(approved);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Example: Search tool using factory pattern
|
|
68
|
+
* Demonstrates searching files for patterns and content with interactive approval
|
|
69
|
+
*/
|
|
70
|
+
async function searchExample() {
|
|
71
|
+
// Initialize Matimo with autoDiscover to find all tools (core + providers)
|
|
72
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
73
|
+
|
|
74
|
+
// Configure centralized approval handler
|
|
75
|
+
const approvalHandler = getGlobalApprovalHandler();
|
|
76
|
+
approvalHandler.setApprovalCallback(createApprovalCallback());
|
|
77
|
+
|
|
78
|
+
console.info('=== Search Tool - Factory Pattern (Interactive Approval) ===\n');
|
|
79
|
+
|
|
80
|
+
// Get the workspace root (parent of examples directory)
|
|
81
|
+
// File is in examples/tools/search/, so go up 3 levels
|
|
82
|
+
const workspaceRoot = path.resolve(__dirname, '../../..');
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
// Example 1: Search for pattern in TypeScript files
|
|
86
|
+
console.info('1. Searching for "import" in examples\n');
|
|
87
|
+
const result1 = await matimo.execute('search', {
|
|
88
|
+
query: 'import',
|
|
89
|
+
directory: path.join(workspaceRoot, 'examples/tools'),
|
|
90
|
+
filePattern: '*.ts',
|
|
91
|
+
maxResults: 5,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if ((result1 as any).success) {
|
|
95
|
+
console.info('Total matches:', (result1 as any).totalMatches);
|
|
96
|
+
console.info('Matches found:', (result1 as any).matches?.length);
|
|
97
|
+
if ((result1 as any).matches) {
|
|
98
|
+
(result1 as any).matches.slice(0, 3).forEach((match: any) => {
|
|
99
|
+
console.info(` - ${match.filePath}:${match.lineNumber}: ${match.lineContent}`);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
console.info('Search denied:', (result1 as any).error);
|
|
104
|
+
}
|
|
105
|
+
console.info('---\n');
|
|
106
|
+
|
|
107
|
+
// Example 2: Search for function definitions
|
|
108
|
+
console.info('2. Searching for function definitions\n');
|
|
109
|
+
const result2 = await matimo.execute('search', {
|
|
110
|
+
query: 'export function',
|
|
111
|
+
directory: path.join(workspaceRoot, 'packages/core/src'),
|
|
112
|
+
filePattern: '*.ts',
|
|
113
|
+
maxResults: 10,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if ((result2 as any).success) {
|
|
117
|
+
console.info('Total matches:', (result2 as any).totalMatches);
|
|
118
|
+
console.info('Matches found:', (result2 as any).matches?.length);
|
|
119
|
+
} else {
|
|
120
|
+
console.info('Search denied:', (result2 as any).error);
|
|
121
|
+
}
|
|
122
|
+
console.info('---\n');
|
|
123
|
+
|
|
124
|
+
// Example 3: Search in specific directory with regex
|
|
125
|
+
console.info('3. Searching for "console.info" patterns\n');
|
|
126
|
+
const result3 = await matimo.execute('search', {
|
|
127
|
+
query: 'console\\.info',
|
|
128
|
+
directory: path.join(workspaceRoot, 'packages/core/src'),
|
|
129
|
+
filePattern: '*.ts',
|
|
130
|
+
isRegex: true,
|
|
131
|
+
maxResults: 5,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if ((result3 as any).success) {
|
|
135
|
+
console.info('Total matches:', (result3 as any).totalMatches);
|
|
136
|
+
console.info('Matches found:', (result3 as any).matches?.length);
|
|
137
|
+
} else {
|
|
138
|
+
console.info('Search denied:', (result3 as any).error);
|
|
139
|
+
}
|
|
140
|
+
console.info('---\n');
|
|
141
|
+
} catch (error: any) {
|
|
142
|
+
console.error('Error searching files:', error.message);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
searchExample();
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* SEARCH TOOL - LANGCHAIN AI AGENT EXAMPLE
|
|
5
|
+
* ============================================================================
|
|
6
|
+
*
|
|
7
|
+
* PATTERN: True AI Agent with OpenAI + LangChain
|
|
8
|
+
* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
9
|
+
* This is a REAL AI agent that:
|
|
10
|
+
* 1. Takes natural language user requests
|
|
11
|
+
* 2. Uses OpenAI LLM (GPT-4o-mini) to decide when to search files
|
|
12
|
+
* 3. Generates appropriate search queries and patterns based on context
|
|
13
|
+
* 4. Executes tools autonomously
|
|
14
|
+
* 5. Processes results and responds naturally
|
|
15
|
+
*
|
|
16
|
+
* SETUP:
|
|
17
|
+
* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
18
|
+
* 1. Create .env file in examples/tools/:
|
|
19
|
+
* OPENAI_API_KEY=sk-xxxxxxxxxxxxx
|
|
20
|
+
*
|
|
21
|
+
* 2. Install dependencies:
|
|
22
|
+
* cd examples/tools && npm install
|
|
23
|
+
*
|
|
24
|
+
* USAGE:
|
|
25
|
+
* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
26
|
+
* # From root directory:
|
|
27
|
+
* pnpm search:langchain
|
|
28
|
+
*
|
|
29
|
+
* # Or from examples/tools directory:
|
|
30
|
+
* npm run search:langchain
|
|
31
|
+
*
|
|
32
|
+
* ============================================================================
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import 'dotenv/config';
|
|
36
|
+
import * as readline from 'readline';
|
|
37
|
+
import { createAgent } from 'langchain';
|
|
38
|
+
import { ChatOpenAI } from '@langchain/openai';
|
|
39
|
+
import {
|
|
40
|
+
MatimoInstance,
|
|
41
|
+
convertToolsToLangChain,
|
|
42
|
+
type ToolDefinition,
|
|
43
|
+
getGlobalApprovalHandler,
|
|
44
|
+
type ApprovalRequest,
|
|
45
|
+
} from '@matimo/core';
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Create an interactive approval callback for file operations
|
|
49
|
+
*/
|
|
50
|
+
function createApprovalCallback() {
|
|
51
|
+
return async (request: ApprovalRequest): Promise<boolean> => {
|
|
52
|
+
const isInteractive = process.stdin.isTTY;
|
|
53
|
+
|
|
54
|
+
console.info('\n' + '='.repeat(70));
|
|
55
|
+
console.info('๐ APPROVAL REQUIRED FOR FILE OPERATION');
|
|
56
|
+
console.info('='.repeat(70));
|
|
57
|
+
console.info(`\n๐ Tool: ${request.toolName}`);
|
|
58
|
+
console.info(`๐ Description: ${request.description || '(no description provided)'}`);
|
|
59
|
+
console.info(`\n๐ File Operation:`);
|
|
60
|
+
console.info(` Path: ${request.params.filePath}`);
|
|
61
|
+
if (request.params.startLine) {
|
|
62
|
+
console.info(` Start Line: ${request.params.startLine}`);
|
|
63
|
+
}
|
|
64
|
+
if (request.params.endLine) {
|
|
65
|
+
console.info(` End Line: ${request.params.endLine}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!isInteractive) {
|
|
69
|
+
console.info('\nโ REJECTED - Non-interactive environment (no terminal)');
|
|
70
|
+
console.info('\n๐ก To enable auto-approval in CI/scripts:');
|
|
71
|
+
console.info(' export MATIMO_AUTO_APPROVE=true');
|
|
72
|
+
console.info('\n๐ก Or approve specific patterns:');
|
|
73
|
+
console.info(' export MATIMO_APPROVED_PATTERNS="search"');
|
|
74
|
+
console.info('\n' + '='.repeat(70) + '\n');
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Interactive mode: prompt user
|
|
79
|
+
const rl = readline.createInterface({
|
|
80
|
+
input: process.stdin,
|
|
81
|
+
output: process.stdout,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return new Promise((resolve) => {
|
|
85
|
+
console.info('\nโ User Action Required');
|
|
86
|
+
const question = ' Type "yes" to approve or "no" to reject: ';
|
|
87
|
+
|
|
88
|
+
rl.question(question, (answer) => {
|
|
89
|
+
const approved = answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y';
|
|
90
|
+
|
|
91
|
+
if (approved) {
|
|
92
|
+
console.info(' โ
Operation APPROVED by user');
|
|
93
|
+
} else {
|
|
94
|
+
console.info(' โ Operation REJECTED by user');
|
|
95
|
+
}
|
|
96
|
+
console.info('='.repeat(70) + '\n');
|
|
97
|
+
|
|
98
|
+
rl.close();
|
|
99
|
+
resolve(approved);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Run AI Agent with Search tool
|
|
107
|
+
* The agent receives natural language requests and decides what to search for
|
|
108
|
+
*/
|
|
109
|
+
async function runSearchAIAgent() {
|
|
110
|
+
console.info('\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
|
|
111
|
+
console.info('โ Search Tool AI Agent - LangChain + OpenAI โ');
|
|
112
|
+
console.info('โ True autonomous agent with LLM reasoning โ');
|
|
113
|
+
console.info('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n');
|
|
114
|
+
|
|
115
|
+
// Check required environment variables
|
|
116
|
+
const openaiKey = process.env.OPENAI_API_KEY;
|
|
117
|
+
if (!openaiKey) {
|
|
118
|
+
console.error('โ Error: OPENAI_API_KEY not set in .env');
|
|
119
|
+
console.info(' Set it: export OPENAI_API_KEY="sk-..."');
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
console.info('๐ค Using OpenAI (GPT-4o-mini) as the AI agent\n');
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
// Initialize Matimo with auto-discovery
|
|
127
|
+
console.info('๐ Initializing Matimo...');
|
|
128
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
129
|
+
|
|
130
|
+
// Configure centralized approval handler
|
|
131
|
+
const approvalHandler = getGlobalApprovalHandler();
|
|
132
|
+
approvalHandler.setApprovalCallback(createApprovalCallback());
|
|
133
|
+
|
|
134
|
+
// Get search tool
|
|
135
|
+
console.info('๐ฌ Loading search tool...');
|
|
136
|
+
const matimoTools = matimo.listTools();
|
|
137
|
+
const searchTools = matimoTools.filter((t) => t.name === 'search') as ToolDefinition[];
|
|
138
|
+
console.info(`โ
Loaded ${searchTools.length} search tool(s)\n`);
|
|
139
|
+
|
|
140
|
+
if (searchTools.length === 0) {
|
|
141
|
+
console.error('โ Search tool not found');
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Convert to LangChain tools using the built-in converter
|
|
146
|
+
const langchainTools = await convertToolsToLangChain(searchTools, matimo);
|
|
147
|
+
|
|
148
|
+
// Initialize OpenAI LLM
|
|
149
|
+
console.info('๐ค Initializing OpenAI (GPT-4o-mini) LLM...');
|
|
150
|
+
const model = new ChatOpenAI({
|
|
151
|
+
modelName: 'gpt-4o-mini',
|
|
152
|
+
temperature: 0.7,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Create agent
|
|
156
|
+
console.info('๐ง Creating agent...\n');
|
|
157
|
+
const agent = await createAgent({
|
|
158
|
+
model,
|
|
159
|
+
tools: langchainTools as any,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Define agent tasks (natural language requests)
|
|
163
|
+
const userRequests = [
|
|
164
|
+
{
|
|
165
|
+
title: 'Example 1: Search for TypeScript files',
|
|
166
|
+
request:
|
|
167
|
+
'Search for all TypeScript files by looking for files matching the pattern "*.ts" in the packages directory',
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
title: 'Example 2: Search for specific imports',
|
|
171
|
+
request:
|
|
172
|
+
'Search for all files containing "import MatimoInstance" to find where MatimoInstance is being imported',
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
title: 'Example 3: Search for function definitions',
|
|
176
|
+
request:
|
|
177
|
+
'Search for files containing "export default async function" to find all exported async functions in the codebase',
|
|
178
|
+
},
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
console.info('๐งช Running AI Agent Tasks');
|
|
182
|
+
console.info('โ'.repeat(60) + '\n');
|
|
183
|
+
|
|
184
|
+
// Run each task through the agent
|
|
185
|
+
for (const task of userRequests) {
|
|
186
|
+
console.info(`${task.title}`);
|
|
187
|
+
console.info('โ'.repeat(60));
|
|
188
|
+
console.info(`๐ค User: "${task.request}"\n`);
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
// Add system context to help the LLM understand how to use the search tool
|
|
192
|
+
const systemPrompt = `You have access to a search tool that can find files in the filesystem.
|
|
193
|
+
The search tool takes a 'query' parameter which can be:
|
|
194
|
+
- A file pattern (glob) like '*.ts', '*.json', 'src/**/*.test.ts'
|
|
195
|
+
- Text to search within files like 'import MatimoInstance', 'export default function'
|
|
196
|
+
|
|
197
|
+
When the user asks you to search, extract the search query from their request and pass it to the search tool.
|
|
198
|
+
For example:
|
|
199
|
+
- If user says "find TypeScript files", use query "*.ts"
|
|
200
|
+
- If user says "find imports of X", use query "import X"
|
|
201
|
+
- If user says "find where Y is exported", use query "export Y"
|
|
202
|
+
|
|
203
|
+
Always use the search tool with the appropriate query parameter.`;
|
|
204
|
+
|
|
205
|
+
const response = await agent.invoke({
|
|
206
|
+
messages: [
|
|
207
|
+
{
|
|
208
|
+
role: 'system',
|
|
209
|
+
content: systemPrompt,
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
role: 'user',
|
|
213
|
+
content: task.request,
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Get the last message from the agent
|
|
219
|
+
const lastMessage = response.messages[response.messages.length - 1];
|
|
220
|
+
if (lastMessage) {
|
|
221
|
+
const content =
|
|
222
|
+
typeof lastMessage.content === 'string'
|
|
223
|
+
? lastMessage.content
|
|
224
|
+
: String(lastMessage.content);
|
|
225
|
+
|
|
226
|
+
if (content && content.trim()) {
|
|
227
|
+
console.info(`๐ค Agent: ${content}\n`);
|
|
228
|
+
} else {
|
|
229
|
+
console.info('๐ค Agent: (Search completed successfully)\n');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
} catch (error) {
|
|
233
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
234
|
+
console.info(`โ ๏ธ Agent error: ${errorMsg}\n`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
console.info('โ'.repeat(60));
|
|
239
|
+
console.info('\nโจ AI Agent Examples Complete!\n');
|
|
240
|
+
console.info('Key Features:');
|
|
241
|
+
console.info(' โ
Real LLM (OpenAI) decides which tools to use');
|
|
242
|
+
console.info(' โ
Natural language requests, not API calls');
|
|
243
|
+
console.info(' โ
LLM generates search patterns based on context');
|
|
244
|
+
console.info(' โ
Agentic reasoning and decision-making\n');
|
|
245
|
+
} catch (error) {
|
|
246
|
+
console.error('โ Error:', error instanceof Error ? error.message : String(error));
|
|
247
|
+
if (error instanceof Error && error.stack) {
|
|
248
|
+
console.error('Stack:', error.stack);
|
|
249
|
+
}
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Run the AI agent
|
|
255
|
+
runSearchAIAgent().catch(console.error);
|