matimo-examples 0.1.0-alpha.7
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 +36 -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 +128 -0
- package/edit/edit-factory.ts +120 -0
- package/edit/edit-langchain.ts +272 -0
- package/execute/execute-decorator.ts +49 -0
- package/execute/execute-factory.ts +46 -0
- package/execute/execute-langchain.ts +163 -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/package.json +58 -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 +250 -0
- package/read/read-decorator.ts +107 -0
- package/read/read-factory.ts +104 -0
- package/read/read-langchain.ts +253 -0
- package/search/search-decorator.ts +154 -0
- package/search/search-factory.ts +129 -0
- package/search/search-langchain.ts +215 -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/web/web-decorator.ts +52 -0
- package/web/web-factory.ts +70 -0
- package/web/web-langchain.ts +163 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* READ 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 read files
|
|
12
|
+
* 3. Generates appropriate file paths and line ranges 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 read:langchain
|
|
28
|
+
*
|
|
29
|
+
* # Or from examples/tools directory:
|
|
30
|
+
* npm run read:langchain
|
|
31
|
+
*
|
|
32
|
+
* ============================================================================
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import 'dotenv/config';
|
|
36
|
+
import fs from 'fs';
|
|
37
|
+
import path from 'path';
|
|
38
|
+
import { fileURLToPath } from 'url';
|
|
39
|
+
import readline from 'readline';
|
|
40
|
+
import { createAgent } from 'langchain';
|
|
41
|
+
import { ChatOpenAI } from '@langchain/openai';
|
|
42
|
+
import {
|
|
43
|
+
MatimoInstance,
|
|
44
|
+
convertToolsToLangChain,
|
|
45
|
+
type ToolDefinition,
|
|
46
|
+
getPathApprovalManager,
|
|
47
|
+
} from '@matimo/core';
|
|
48
|
+
|
|
49
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
50
|
+
|
|
51
|
+
// Create readline interface for interactive approval prompts
|
|
52
|
+
const rl = readline.createInterface({
|
|
53
|
+
input: process.stdin,
|
|
54
|
+
output: process.stdout,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
let isReadlineClosed = false;
|
|
58
|
+
|
|
59
|
+
// Track when readline closes (e.g., piped input ends)
|
|
60
|
+
rl.on('close', () => {
|
|
61
|
+
isReadlineClosed = true;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Prompt user for approval decision
|
|
66
|
+
*/
|
|
67
|
+
async function promptForApproval(
|
|
68
|
+
filePath: string,
|
|
69
|
+
mode: 'read' | 'write' | 'search'
|
|
70
|
+
): Promise<boolean> {
|
|
71
|
+
return new Promise((resolve) => {
|
|
72
|
+
// If readline is closed (e.g., non-TTY/piped input), auto-approve
|
|
73
|
+
if (isReadlineClosed) {
|
|
74
|
+
console.info(
|
|
75
|
+
`[${mode.toUpperCase()}] Access to ${filePath} auto-approved (non-interactive mode)`
|
|
76
|
+
);
|
|
77
|
+
resolve(true);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
rl.question(`[${mode.toUpperCase()}] Approve access to ${filePath}? (y/n): `, (answer) => {
|
|
81
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Run AI Agent with Read tool
|
|
88
|
+
* The agent receives natural language requests and decides which files to read
|
|
89
|
+
*/
|
|
90
|
+
async function runReadAIAgent() {
|
|
91
|
+
console.info('\n╔════════════════════════════════════════════════════════╗');
|
|
92
|
+
console.info('║ Read Tool AI Agent - LangChain + OpenAI ║');
|
|
93
|
+
console.info('║ True autonomous agent with LLM reasoning ║');
|
|
94
|
+
console.info('╚════════════════════════════════════════════════════════╝\n');
|
|
95
|
+
|
|
96
|
+
// Check required environment variables
|
|
97
|
+
const openaiKey = process.env.OPENAI_API_KEY;
|
|
98
|
+
if (!openaiKey) {
|
|
99
|
+
console.error('❌ Error: OPENAI_API_KEY not set in .env');
|
|
100
|
+
console.info(' Set it: export OPENAI_API_KEY="sk-..."');
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.info('🤖 Using OpenAI (GPT-4o-mini) as the AI agent\n');
|
|
105
|
+
|
|
106
|
+
// Create a sample file for the agent to read
|
|
107
|
+
const sampleFile = path.join(__dirname, 'sample-code.ts');
|
|
108
|
+
fs.writeFileSync(
|
|
109
|
+
sampleFile,
|
|
110
|
+
`// Sample TypeScript file
|
|
111
|
+
interface User {
|
|
112
|
+
id: string;
|
|
113
|
+
name: string;
|
|
114
|
+
email: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function getUserInfo(userId: string): User {
|
|
118
|
+
// Fetch user from database
|
|
119
|
+
return {
|
|
120
|
+
id: userId,
|
|
121
|
+
name: 'John Doe',
|
|
122
|
+
email: 'john@example.com',
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function validateEmail(email: string): boolean {
|
|
127
|
+
const pattern = /^[^@]+@[^@]+\\.[^@]+$/;
|
|
128
|
+
return pattern.test(email);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export { getUserInfo, validateEmail };
|
|
132
|
+
`
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
// Initialize Matimo with auto-discovery
|
|
137
|
+
console.info('🚀 Initializing Matimo...');
|
|
138
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
139
|
+
|
|
140
|
+
// Set up approval callback for interactive approval
|
|
141
|
+
const approvalManager = getPathApprovalManager();
|
|
142
|
+
approvalManager.setApprovalCallback(promptForApproval);
|
|
143
|
+
|
|
144
|
+
// Get read tool
|
|
145
|
+
console.info('💬 Loading read tool...');
|
|
146
|
+
const matimoTools = matimo.listTools();
|
|
147
|
+
const readTools = matimoTools.filter((t) => t.name === 'read') as ToolDefinition[];
|
|
148
|
+
console.info(`✅ Loaded ${readTools.length} read tool(s)\n`);
|
|
149
|
+
|
|
150
|
+
if (readTools.length === 0) {
|
|
151
|
+
console.error('❌ Read tool not found');
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Convert to LangChain tools using the built-in converter
|
|
156
|
+
const langchainTools = await convertToolsToLangChain(readTools, matimo);
|
|
157
|
+
|
|
158
|
+
// Initialize OpenAI LLM
|
|
159
|
+
console.info('🤖 Initializing OpenAI (GPT-4o-mini) LLM...');
|
|
160
|
+
const model = new ChatOpenAI({
|
|
161
|
+
modelName: 'gpt-4o-mini',
|
|
162
|
+
temperature: 0.7,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Create agent
|
|
166
|
+
console.info('🔧 Creating agent...\n');
|
|
167
|
+
const agent = await createAgent({
|
|
168
|
+
model,
|
|
169
|
+
tools: langchainTools as any,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Define agent tasks (natural language requests)
|
|
173
|
+
const userRequests = [
|
|
174
|
+
{
|
|
175
|
+
title: 'Example 1: Read function definition',
|
|
176
|
+
request: `Read the getUserInfo function from file ${sampleFile}`,
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
title: 'Example 2: Read specific lines',
|
|
180
|
+
request: `Read lines 1-10 from file ${sampleFile} and tell me what interfaces are defined`,
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
title: 'Example 3: Read entire file',
|
|
184
|
+
request: `Show me the complete content of file ${sampleFile}`,
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
console.info('🧪 Running AI Agent Tasks');
|
|
189
|
+
console.info('═'.repeat(60) + '\n');
|
|
190
|
+
|
|
191
|
+
// Run each task through the agent
|
|
192
|
+
for (const task of userRequests) {
|
|
193
|
+
console.info(`${task.title}`);
|
|
194
|
+
console.info('─'.repeat(60));
|
|
195
|
+
console.info(`👤 User: "${task.request}"\n`);
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const response = await agent.invoke({
|
|
199
|
+
messages: [
|
|
200
|
+
{
|
|
201
|
+
role: 'user',
|
|
202
|
+
content: task.request,
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Get the last message from the agent
|
|
208
|
+
const lastMessage = response.messages[response.messages.length - 1];
|
|
209
|
+
if (lastMessage) {
|
|
210
|
+
const content =
|
|
211
|
+
typeof lastMessage.content === 'string'
|
|
212
|
+
? lastMessage.content
|
|
213
|
+
: String(lastMessage.content);
|
|
214
|
+
|
|
215
|
+
if (content && content.trim()) {
|
|
216
|
+
console.info(`🤖 Agent: ${content}\n`);
|
|
217
|
+
} else {
|
|
218
|
+
console.info('🤖 Agent: (File read successfully)\n');
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
} catch (error) {
|
|
222
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
223
|
+
console.info(`⚠️ Agent error: ${errorMsg}\n`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
console.info('═'.repeat(60));
|
|
228
|
+
console.info('\n✨ AI Agent Examples Complete!\n');
|
|
229
|
+
console.info('Key Features:');
|
|
230
|
+
console.info(' ✅ Real LLM (OpenAI) decides which tools to use');
|
|
231
|
+
console.info(' ✅ Natural language requests, not API calls');
|
|
232
|
+
console.info(' ✅ LLM determines file paths and line ranges');
|
|
233
|
+
console.info(' ✅ Agentic reasoning and decision-making\n');
|
|
234
|
+
} catch (error) {
|
|
235
|
+
console.error('❌ Error:', error instanceof Error ? error.message : String(error));
|
|
236
|
+
if (error instanceof Error && error.stack) {
|
|
237
|
+
console.error('Stack:', error.stack);
|
|
238
|
+
}
|
|
239
|
+
process.exit(1);
|
|
240
|
+
} finally {
|
|
241
|
+
// Clean up
|
|
242
|
+
if (fs.existsSync(sampleFile)) {
|
|
243
|
+
fs.unlinkSync(sampleFile);
|
|
244
|
+
}
|
|
245
|
+
if (!isReadlineClosed) {
|
|
246
|
+
rl.close();
|
|
247
|
+
isReadlineClosed = true;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Run the AI agent
|
|
253
|
+
runReadAIAgent().catch(console.error);
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MatimoInstance,
|
|
3
|
+
setGlobalMatimoInstance,
|
|
4
|
+
tool,
|
|
5
|
+
getPathApprovalManager,
|
|
6
|
+
} from '@matimo/core';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import readline from 'readline';
|
|
10
|
+
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
|
|
13
|
+
// Create readline interface for interactive approval prompts
|
|
14
|
+
const rl = readline.createInterface({
|
|
15
|
+
input: process.stdin,
|
|
16
|
+
output: process.stdout,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
let isReadlineClosed = false;
|
|
20
|
+
|
|
21
|
+
// Track when readline closes (e.g., piped input ends)
|
|
22
|
+
rl.on('close', () => {
|
|
23
|
+
isReadlineClosed = true;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Prompt user for approval decision
|
|
28
|
+
*/
|
|
29
|
+
async function promptForApproval(
|
|
30
|
+
filePath: string,
|
|
31
|
+
mode: 'read' | 'write' | 'search'
|
|
32
|
+
): Promise<boolean> {
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
// If readline is closed (e.g., non-TTY/piped input), auto-approve
|
|
35
|
+
if (isReadlineClosed) {
|
|
36
|
+
console.info(
|
|
37
|
+
`[${mode.toUpperCase()}] Access to ${filePath} auto-approved (non-interactive mode)`
|
|
38
|
+
);
|
|
39
|
+
resolve(true);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
rl.question(`[${mode.toUpperCase()}] Approve access to ${filePath}? (y/n): `, (answer) => {
|
|
43
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Example: Search tool using @tool decorator pattern
|
|
50
|
+
* Demonstrates class-based file search with automatic decoration
|
|
51
|
+
*/
|
|
52
|
+
class FileSearcher {
|
|
53
|
+
@tool('search')
|
|
54
|
+
async findPattern(
|
|
55
|
+
query: string,
|
|
56
|
+
directory: string,
|
|
57
|
+
filePattern: string,
|
|
58
|
+
maxResults?: number
|
|
59
|
+
): Promise<unknown> {
|
|
60
|
+
// Decorator automatically intercepts and executes via Matimo with:
|
|
61
|
+
// { query, directory, filePattern, maxResults }
|
|
62
|
+
// Positional args map to parameters in tool definition order
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@tool('search')
|
|
67
|
+
async searchInDirectory(
|
|
68
|
+
query: string,
|
|
69
|
+
directory: string,
|
|
70
|
+
filePattern?: string
|
|
71
|
+
): Promise<unknown> {
|
|
72
|
+
// Decorator automatically intercepts and executes via Matimo with:
|
|
73
|
+
// { query, directory, filePattern }
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@tool('search')
|
|
78
|
+
async regexSearch(
|
|
79
|
+
query: string,
|
|
80
|
+
directory: string,
|
|
81
|
+
filePattern: string,
|
|
82
|
+
isRegex: boolean = true,
|
|
83
|
+
maxResults?: number
|
|
84
|
+
): Promise<unknown> {
|
|
85
|
+
// Decorator automatically intercepts and executes via Matimo with:
|
|
86
|
+
// { query, directory, filePattern, isRegex, maxResults }
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function decoratorExample() {
|
|
92
|
+
// Set up decorator support with autoDiscover
|
|
93
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
94
|
+
setGlobalMatimoInstance(matimo);
|
|
95
|
+
|
|
96
|
+
// Set up approval callback for interactive approval
|
|
97
|
+
const approvalManager = getPathApprovalManager();
|
|
98
|
+
approvalManager.setApprovalCallback(promptForApproval);
|
|
99
|
+
|
|
100
|
+
console.info('=== Search Tool - Decorator Pattern (Interactive Approval) ===\n');
|
|
101
|
+
|
|
102
|
+
// Get the workspace root (parent of examples directory)
|
|
103
|
+
// File is in examples/tools/search/, so go up 3 levels
|
|
104
|
+
const workspaceRoot = path.resolve(__dirname, '../../..');
|
|
105
|
+
|
|
106
|
+
const searcher = new FileSearcher();
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
// Example 1: Find pattern through decorated method
|
|
110
|
+
console.info('1. Finding "export" in TypeScript files\n');
|
|
111
|
+
const result1 = await searcher.findPattern(
|
|
112
|
+
'export',
|
|
113
|
+
path.join(workspaceRoot, 'packages/core/src'),
|
|
114
|
+
'*.ts',
|
|
115
|
+
5
|
|
116
|
+
);
|
|
117
|
+
console.info('Total matches:', (result1 as any).totalMatches);
|
|
118
|
+
console.info('Matches found:', (result1 as any).matches?.length);
|
|
119
|
+
console.info('---\n');
|
|
120
|
+
|
|
121
|
+
// Example 2: Search in specific directory
|
|
122
|
+
console.info('2. Searching in examples directory\n');
|
|
123
|
+
const result2 = await searcher.searchInDirectory(
|
|
124
|
+
'async',
|
|
125
|
+
path.join(workspaceRoot, 'examples/tools'),
|
|
126
|
+
'*.ts'
|
|
127
|
+
);
|
|
128
|
+
console.info('Total matches:', (result2 as any).totalMatches);
|
|
129
|
+
console.info('Matches found:', (result2 as any).matches?.length);
|
|
130
|
+
console.info('---\n');
|
|
131
|
+
|
|
132
|
+
// Example 3: Regex search
|
|
133
|
+
console.info('3. Using regex to find patterns\n');
|
|
134
|
+
const result3 = await searcher.regexSearch(
|
|
135
|
+
'console\\.info',
|
|
136
|
+
path.join(workspaceRoot, 'packages/core/src'),
|
|
137
|
+
'*.ts',
|
|
138
|
+
true,
|
|
139
|
+
5
|
|
140
|
+
);
|
|
141
|
+
console.info('Total matches:', (result3 as any).totalMatches);
|
|
142
|
+
console.info('Matches found:', (result3 as any).matches?.length);
|
|
143
|
+
console.info('---\n');
|
|
144
|
+
} catch (error: any) {
|
|
145
|
+
console.error('Error:', error.message);
|
|
146
|
+
} finally {
|
|
147
|
+
if (!isReadlineClosed) {
|
|
148
|
+
rl.close();
|
|
149
|
+
isReadlineClosed = true;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
decoratorExample();
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { MatimoInstance } from '@matimo/core';
|
|
2
|
+
import { getPathApprovalManager } from '@matimo/core';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import readline from 'readline';
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
// Create readline interface once for reuse across multiple prompts
|
|
10
|
+
const rl = readline.createInterface({
|
|
11
|
+
input: process.stdin,
|
|
12
|
+
output: process.stdout,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
let isReadlineClosed = false;
|
|
16
|
+
|
|
17
|
+
// Track when readline closes (e.g., piped input ends)
|
|
18
|
+
rl.on('close', () => {
|
|
19
|
+
isReadlineClosed = true;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Prompt user for approval decision
|
|
24
|
+
*/
|
|
25
|
+
async function promptForApproval(
|
|
26
|
+
filePath: string,
|
|
27
|
+
mode: 'read' | 'write' | 'search'
|
|
28
|
+
): Promise<boolean> {
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
// If readline is closed (e.g., non-TTY/piped input), auto-approve
|
|
31
|
+
if (isReadlineClosed) {
|
|
32
|
+
console.info(
|
|
33
|
+
`[${mode.toUpperCase()}] Access to ${filePath} auto-approved (non-interactive mode)`
|
|
34
|
+
);
|
|
35
|
+
resolve(true);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
rl.question(`[${mode.toUpperCase()}] Approve access to ${filePath}? (y/n): `, (answer) => {
|
|
39
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Example: Search tool using factory pattern
|
|
46
|
+
* Demonstrates searching files for patterns and content with interactive approval
|
|
47
|
+
*/
|
|
48
|
+
async function searchExample() {
|
|
49
|
+
// Initialize Matimo with autoDiscover to find all tools (core + providers)
|
|
50
|
+
const matimo = await MatimoInstance.init({ autoDiscover: true });
|
|
51
|
+
|
|
52
|
+
// Set up approval callback for examples with INTERACTIVE PROMPTS
|
|
53
|
+
const approvalManager = getPathApprovalManager();
|
|
54
|
+
approvalManager.setApprovalCallback(promptForApproval);
|
|
55
|
+
|
|
56
|
+
console.info('=== Search Tool - Factory Pattern (Interactive Approval) ===\n');
|
|
57
|
+
|
|
58
|
+
// Get the workspace root (parent of examples directory)
|
|
59
|
+
// File is in examples/tools/search/, so go up 3 levels
|
|
60
|
+
const workspaceRoot = path.resolve(__dirname, '../../..');
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// Example 1: Search for pattern in TypeScript files
|
|
64
|
+
console.info('1. Searching for "import" in examples\n');
|
|
65
|
+
const result1 = await matimo.execute('search', {
|
|
66
|
+
query: 'import',
|
|
67
|
+
directory: path.join(workspaceRoot, 'examples/tools'),
|
|
68
|
+
filePattern: '*.ts',
|
|
69
|
+
maxResults: 5,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if ((result1 as any).success) {
|
|
73
|
+
console.info('Total matches:', (result1 as any).totalMatches);
|
|
74
|
+
console.info('Matches found:', (result1 as any).matches?.length);
|
|
75
|
+
if ((result1 as any).matches) {
|
|
76
|
+
(result1 as any).matches.slice(0, 3).forEach((match: any) => {
|
|
77
|
+
console.info(` - ${match.filePath}:${match.lineNumber}: ${match.lineContent}`);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
console.info('Search denied:', (result1 as any).error);
|
|
82
|
+
}
|
|
83
|
+
console.info('---\n');
|
|
84
|
+
|
|
85
|
+
// Example 2: Search for function definitions
|
|
86
|
+
console.info('2. Searching for function definitions\n');
|
|
87
|
+
const result2 = await matimo.execute('search', {
|
|
88
|
+
query: 'export function',
|
|
89
|
+
directory: path.join(workspaceRoot, 'packages/core/src'),
|
|
90
|
+
filePattern: '*.ts',
|
|
91
|
+
maxResults: 10,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if ((result2 as any).success) {
|
|
95
|
+
console.info('Total matches:', (result2 as any).totalMatches);
|
|
96
|
+
console.info('Matches found:', (result2 as any).matches?.length);
|
|
97
|
+
} else {
|
|
98
|
+
console.info('Search denied:', (result2 as any).error);
|
|
99
|
+
}
|
|
100
|
+
console.info('---\n');
|
|
101
|
+
|
|
102
|
+
// Example 3: Search in specific directory with regex
|
|
103
|
+
console.info('3. Searching for "console.info" patterns\n');
|
|
104
|
+
const result3 = await matimo.execute('search', {
|
|
105
|
+
query: 'console\\.info',
|
|
106
|
+
directory: path.join(workspaceRoot, 'packages/core/src'),
|
|
107
|
+
filePattern: '*.ts',
|
|
108
|
+
isRegex: true,
|
|
109
|
+
maxResults: 5,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if ((result3 as any).success) {
|
|
113
|
+
console.info('Total matches:', (result3 as any).totalMatches);
|
|
114
|
+
console.info('Matches found:', (result3 as any).matches?.length);
|
|
115
|
+
} else {
|
|
116
|
+
console.info('Search denied:', (result3 as any).error);
|
|
117
|
+
}
|
|
118
|
+
console.info('---\n');
|
|
119
|
+
} catch (error: any) {
|
|
120
|
+
console.error('Error searching files:', error.message);
|
|
121
|
+
} finally {
|
|
122
|
+
if (!isReadlineClosed) {
|
|
123
|
+
rl.close();
|
|
124
|
+
isReadlineClosed = true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
searchExample();
|