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.
Files changed (57) hide show
  1. package/.env.example +49 -0
  2. package/LICENSE +21 -0
  3. package/README.md +525 -0
  4. package/agents/decorator-pattern-agent.ts +368 -0
  5. package/agents/factory-pattern-agent.ts +253 -0
  6. package/agents/langchain-agent.ts +146 -0
  7. package/edit/edit-decorator.ts +178 -0
  8. package/edit/edit-factory.ts +138 -0
  9. package/edit/edit-langchain.ts +292 -0
  10. package/execute/execute-decorator.ts +49 -0
  11. package/execute/execute-factory.ts +46 -0
  12. package/execute/execute-langchain.ts +232 -0
  13. package/github/github-decorator.ts +326 -0
  14. package/github/github-factory.ts +355 -0
  15. package/github/github-langchain.ts +206 -0
  16. package/github/github-with-approval.ts +228 -0
  17. package/gmail/README.md +345 -0
  18. package/gmail/gmail-decorator.ts +216 -0
  19. package/gmail/gmail-factory.ts +231 -0
  20. package/gmail/gmail-langchain.ts +201 -0
  21. package/hubspot/README.md +316 -0
  22. package/hubspot/hubspot-decorator.ts +180 -0
  23. package/hubspot/hubspot-factory.ts +188 -0
  24. package/hubspot/hubspot-langchain.ts +222 -0
  25. package/logger-example.ts +40 -0
  26. package/mailchimp/README.md +321 -0
  27. package/mailchimp/mailchimp-decorator.ts +277 -0
  28. package/mailchimp/mailchimp-factory.ts +187 -0
  29. package/mailchimp/mailchimp-langchain.ts +155 -0
  30. package/notion/README.md +293 -0
  31. package/notion/notion-decorator.ts +275 -0
  32. package/notion/notion-factory.ts +256 -0
  33. package/notion/notion-langchain.ts +237 -0
  34. package/package.json +79 -0
  35. package/postgres/README.md +188 -0
  36. package/postgres/postgres-decorator.ts +198 -0
  37. package/postgres/postgres-factory.ts +180 -0
  38. package/postgres/postgres-langchain.ts +213 -0
  39. package/postgres/postgres-with-approval.ts +344 -0
  40. package/read/read-decorator.ts +154 -0
  41. package/read/read-factory.ts +121 -0
  42. package/read/read-langchain.ts +273 -0
  43. package/search/search-decorator.ts +206 -0
  44. package/search/search-factory.ts +146 -0
  45. package/search/search-langchain.ts +255 -0
  46. package/slack/README.md +339 -0
  47. package/slack/slack-decorator.ts +245 -0
  48. package/slack/slack-factory.ts +226 -0
  49. package/slack/slack-langchain.ts +242 -0
  50. package/tsconfig.json +20 -0
  51. package/twilio/README.md +309 -0
  52. package/twilio/twilio-decorator.ts +288 -0
  53. package/twilio/twilio-factory.ts +238 -0
  54. package/twilio/twilio-langchain.ts +218 -0
  55. package/web/web-decorator.ts +52 -0
  56. package/web/web-factory.ts +70 -0
  57. package/web/web-langchain.ts +163 -0
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Matimo + LangChain Agent - Proper ReAct Agent Pattern
4
+ *
5
+ * This demonstrates a complete agent loop:
6
+ * 1. LLM decides which tool to use based on goal
7
+ * 2. Tool is executed via Matimo
8
+ * 3. Result is fed back to LLM
9
+ * 4. Process repeats until agent reaches conclusion
10
+ *
11
+ * Key advantages:
12
+ * - Shows real agent reasoning loop
13
+ * - Single source of truth (Matimo YAML definitions)
14
+ * - How to integrate Matimo with any LangChain setup
15
+ * - Demonstrates tool selection and execution
16
+ *
17
+ * Run: npm run agent:langchain
18
+ */
19
+
20
+ import 'dotenv/config';
21
+ import path from 'path';
22
+ import { fileURLToPath } from 'url';
23
+ import { ChatOpenAI } from '@langchain/openai';
24
+ import { BaseMessage, HumanMessage, ToolMessage } from '@langchain/core/messages';
25
+ import { MatimoInstance, convertToolsToLangChain, ToolDefinition } from 'matimo';
26
+
27
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
28
+
29
+ /**
30
+ * Run LangChain ReAct Agent with Matimo Tools
31
+ */
32
+ async function runLangChainAgent() {
33
+ console.info('\n╔════════════════════════════════════════════════════════╗');
34
+ console.info('║ Matimo + LangChain Agent (ReAct Pattern) ║');
35
+ console.info('║ Demonstrates real agent reasoning loop ║');
36
+ console.info('╚════════════════════════════════════════════════════════╝\n');
37
+
38
+ try {
39
+ // Initialize Matimo
40
+ console.info('🚀 Initializing Matimo...');
41
+ const matimo = await MatimoInstance.init({ autoDiscover: true });
42
+
43
+ const matimoTools = matimo.listTools();
44
+ console.info(`📦 Loaded ${matimoTools.length} tools:\n`);
45
+ matimoTools.forEach((t) => {
46
+ console.info(` • ${t.name}`);
47
+ console.info(` ${t.description}\n`);
48
+ });
49
+
50
+ // ✅ Convert Matimo tools to LangChain tools
51
+ console.info('🔧 Converting Matimo tools to LangChain format...\n');
52
+ const langchainTools = await convertToolsToLangChain(matimoTools as ToolDefinition[], matimo);
53
+
54
+ console.info(`✅ Successfully converted ${langchainTools.length} tools!\n`);
55
+
56
+ // 🤖 Create GPT-4o-mini LLM with tool binding
57
+ console.info('🧠 Creating GPT-4o-mini LLM with tool binding...\n');
58
+ const llm = new ChatOpenAI({
59
+ model: 'gpt-4o-mini',
60
+ temperature: 0,
61
+ });
62
+
63
+ const llmWithTools = llm.bindTools(langchainTools as any);
64
+
65
+ // 🎯 Agent Loop - ReAct Pattern
66
+ console.info('🧪 Starting Agent Loop (ReAct Pattern)\n');
67
+ console.info('═'.repeat(60));
68
+
69
+ const userQuery = 'What is 42 plus 58?';
70
+ console.info(`\n❓ User Query: "${userQuery}"\n`);
71
+
72
+ const messages: BaseMessage[] = [new HumanMessage(userQuery)];
73
+
74
+ let iterationCount = 0;
75
+ const maxIterations = 10;
76
+ let continueLoop = true;
77
+
78
+ while (continueLoop && iterationCount < maxIterations) {
79
+ iterationCount++;
80
+ console.info(`\n[Iteration ${iterationCount}]`);
81
+ console.info('─'.repeat(60));
82
+
83
+ // Step 1: Call LLM with tools
84
+ console.info('🤔 LLM Thinking...');
85
+ const response = await llmWithTools.invoke(messages);
86
+ console.info(`LLM Response Content: ${response.content || '(no text content)'}`);
87
+
88
+ // Step 2: Check if LLM wants to use tools
89
+ if (response.tool_calls && response.tool_calls.length > 0) {
90
+ // Add assistant message to conversation
91
+ messages.push(response);
92
+
93
+ // Step 3: Execute each tool call
94
+ for (const toolCall of response.tool_calls) {
95
+ console.info(`\n🔧 Executing Tool: ${toolCall.name}`);
96
+ console.info(` Input: ${JSON.stringify(toolCall.args)}`);
97
+
98
+ try {
99
+ // Execute via Matimo
100
+ const result = await matimo.execute(toolCall.name, toolCall.args);
101
+ console.info(` ✅ Result: ${JSON.stringify(result)}`);
102
+
103
+ // Add tool result to conversation
104
+ messages.push(
105
+ new ToolMessage({
106
+ tool_call_id: toolCall.id || '',
107
+ content: JSON.stringify(result),
108
+ name: toolCall.name,
109
+ })
110
+ );
111
+ } catch (toolError) {
112
+ const msg = toolError instanceof Error ? toolError.message : String(toolError);
113
+ console.info(` ❌ Error: ${msg}`);
114
+
115
+ // Add error to conversation
116
+ messages.push(
117
+ new ToolMessage({
118
+ tool_call_id: toolCall.id || '',
119
+ content: `Error: ${msg}`,
120
+ name: toolCall.name,
121
+ })
122
+ );
123
+ }
124
+ }
125
+ } else {
126
+ // Step 4: No more tools - agent reached conclusion
127
+ console.info('\n✅ Agent Reached Conclusion');
128
+ console.info(`\n📝 Final Response:\n${response.content || '(no response)'}`);
129
+ continueLoop = false;
130
+ }
131
+ }
132
+
133
+ if (iterationCount >= maxIterations) {
134
+ console.info('\n⚠️ Max iterations reached');
135
+ }
136
+
137
+ console.info('\n' + '═'.repeat(60));
138
+ console.info(`\n✨ Agent Loop Complete (${iterationCount} iterations)\n`);
139
+ } catch (error) {
140
+ console.error('❌ Agent failed:', error instanceof Error ? error.message : String(error));
141
+ process.exit(1);
142
+ }
143
+ }
144
+
145
+ // Run the agent
146
+ runLangChainAgent().catch(console.error);
@@ -0,0 +1,178 @@
1
+ import {
2
+ MatimoInstance,
3
+ setGlobalMatimoInstance,
4
+ tool,
5
+ getGlobalApprovalHandler,
6
+ type ApprovalRequest,
7
+ } from '@matimo/core';
8
+ import fs from 'fs';
9
+ import path from 'path';
10
+ import { fileURLToPath } from 'url';
11
+ import * as readline from 'readline';
12
+
13
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
+
15
+ /**
16
+ * Create an interactive approval callback
17
+ */
18
+ function createApprovalCallback() {
19
+ return async (request: ApprovalRequest): Promise<boolean> => {
20
+ const isInteractive = process.stdin.isTTY;
21
+
22
+ console.info('\n' + '='.repeat(70));
23
+ console.info('🔒 APPROVAL REQUIRED FOR FILE OPERATION');
24
+ console.info('='.repeat(70));
25
+ console.info(`\n📋 Tool: ${request.toolName}`);
26
+ console.info(`📝 Description: ${request.description || '(no description provided)'}`);
27
+ console.info(`\n📄 File Operation:`);
28
+ console.info(` Path: ${request.params.filePath}`);
29
+ console.info(` Operation: ${request.params.operation}`);
30
+ if (request.params.content) {
31
+ console.info(` Content: ${String(request.params.content).substring(0, 50)}...`);
32
+ }
33
+
34
+ if (!isInteractive) {
35
+ console.info('\n❌ REJECTED - Non-interactive environment (no terminal)');
36
+ console.info('\n💡 To enable auto-approval in CI/scripts:');
37
+ console.info(' export MATIMO_AUTO_APPROVE=true');
38
+ console.info('\n💡 Or approve specific patterns:');
39
+ console.info(' export MATIMO_APPROVED_PATTERNS="edit"');
40
+ console.info('\n' + '='.repeat(70) + '\n');
41
+ return false;
42
+ }
43
+
44
+ // Interactive mode: prompt user
45
+ const rl = readline.createInterface({
46
+ input: process.stdin,
47
+ output: process.stdout,
48
+ });
49
+
50
+ return new Promise((resolve) => {
51
+ console.info('\n❓ User Action Required');
52
+ const question = ' Type "yes" to approve or "no" to reject: ';
53
+
54
+ rl.question(question, (answer) => {
55
+ const approved = answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y';
56
+
57
+ if (approved) {
58
+ console.info(' ✅ Operation APPROVED by user');
59
+ } else {
60
+ console.info(' ❌ Operation REJECTED by user');
61
+ }
62
+ console.info('='.repeat(70) + '\n');
63
+
64
+ rl.close();
65
+ resolve(approved);
66
+ });
67
+ });
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Example: Edit tool using @tool decorator pattern
73
+ * Demonstrates class-based file editing with automatic decoration
74
+ */
75
+ class FileEditor {
76
+ @tool('edit')
77
+ async replaceContent(
78
+ filePath: string,
79
+ operation: string,
80
+ content: string,
81
+ startLine: number,
82
+ endLine: number
83
+ ): Promise<unknown> {
84
+ // Decorator automatically intercepts and executes via Matimo
85
+ return undefined;
86
+ }
87
+
88
+ @tool('edit')
89
+ async insertContent(
90
+ filePath: string,
91
+ operation: string,
92
+ content: string,
93
+ startLine: number,
94
+ endLine: number
95
+ ): Promise<unknown> {
96
+ // Decorator automatically intercepts and executes via Matimo
97
+ return undefined;
98
+ }
99
+ }
100
+
101
+ async function decoratorExample() {
102
+ // Set up decorator support with autoDiscover
103
+ const matimo = await MatimoInstance.init({ autoDiscover: true });
104
+ setGlobalMatimoInstance(matimo);
105
+
106
+ // Configure centralized approval handler
107
+ const approvalHandler = getGlobalApprovalHandler();
108
+ approvalHandler.setApprovalCallback(createApprovalCallback());
109
+
110
+ console.info('\n' + '='.repeat(70));
111
+ console.info('🚀 Edit Tool - Decorator Pattern Example');
112
+ console.info('='.repeat(70));
113
+
114
+ // Show current approval mode
115
+ const autoApproveEnabled = process.env.MATIMO_AUTO_APPROVE === 'true';
116
+ const approvedPatterns = process.env.MATIMO_APPROVED_PATTERNS;
117
+
118
+ console.info('\n🔐 APPROVAL CONFIGURATION:');
119
+ if (autoApproveEnabled) {
120
+ console.info(' ✅ MATIMO_AUTO_APPROVE=true');
121
+ console.info(' → All destructive operations will be AUTO-APPROVED');
122
+ } else if (approvedPatterns) {
123
+ console.info(` ✅ MATIMO_APPROVED_PATTERNS="${approvedPatterns}"`);
124
+ console.info(' → Matching operations will be auto-approved');
125
+ } else {
126
+ console.info(' ⚠️ INTERACTIVE MODE ENABLED');
127
+ console.info(' → You will be prompted to approve file operations');
128
+ }
129
+
130
+ const editor = new FileEditor();
131
+
132
+ // Create a temp file for demonstration
133
+ const tempFile = path.join(__dirname, 'temp-demo-decorator.txt');
134
+ fs.writeFileSync(tempFile, 'Original line\nAnother line\nThird line\n');
135
+
136
+ try {
137
+ // Example 1: Replace through decorated method
138
+ console.info('\n1️⃣ REPLACING CONTENT');
139
+ console.info('-'.repeat(70));
140
+ console.info('Replacing line 1 with: "Updated line"');
141
+ console.info('(This is a write operation - may require approval)\n');
142
+ const result1 = await editor.replaceContent(tempFile, 'replace', 'Updated line', 1, 1);
143
+ if (result1) {
144
+ console.info('✅ Edit Result:', (result1 as any).success);
145
+ console.info('📊 Lines Affected:', (result1 as any).linesAffected);
146
+ console.info('📝 File content:');
147
+ console.info(fs.readFileSync(tempFile, 'utf-8'));
148
+ }
149
+ console.info('---\n');
150
+
151
+ // Example 2: Insert through decorated method
152
+ console.info('2️⃣ INSERTING CONTENT');
153
+ console.info('-'.repeat(70));
154
+ console.info('Inserting "Inserted line" at line 1');
155
+ console.info('(This is a write operation - may require approval)\n');
156
+ const result2 = await editor.insertContent(tempFile, 'insert', 'Inserted line', 1, 0);
157
+ if (result2) {
158
+ console.info('✅ Insert Result:', (result2 as any).success);
159
+ console.info('📊 Lines Affected:', (result2 as any).linesAffected);
160
+ console.info('📝 File content:');
161
+ console.info(fs.readFileSync(tempFile, 'utf-8'));
162
+ }
163
+ console.info('---\n');
164
+
165
+ console.info('✅ Decorator example completed successfully');
166
+ } catch (error: any) {
167
+ console.error('❌ Error:', error.message);
168
+ } finally {
169
+ // Clean up temp file
170
+ if (fs.existsSync(tempFile)) {
171
+ fs.unlinkSync(tempFile);
172
+ console.info('\n🧹 Cleaned up temporary file');
173
+ }
174
+ console.info('');
175
+ }
176
+ }
177
+
178
+ decoratorExample();
@@ -0,0 +1,138 @@
1
+ import { MatimoInstance, getGlobalApprovalHandler, type ApprovalRequest } from '@matimo/core';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import * as readline from 'readline';
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+
9
+ /**
10
+ * Create an interactive approval callback for file operations
11
+ */
12
+ function createApprovalCallback() {
13
+ return async (request: ApprovalRequest): Promise<boolean> => {
14
+ const isInteractive = process.stdin.isTTY;
15
+
16
+ console.info('\n' + '='.repeat(70));
17
+ console.info('🔒 APPROVAL REQUIRED FOR FILE OPERATION');
18
+ console.info('='.repeat(70));
19
+ console.info(`\n📋 Tool: ${request.toolName}`);
20
+ console.info(`📝 Description: ${request.description || '(no description provided)'}`);
21
+ console.info(`\n📄 File Operation:`);
22
+ console.info(` Path: ${request.params.filePath}`);
23
+ if (request.params.startLine) {
24
+ console.info(` Start Line: ${request.params.startLine}`);
25
+ }
26
+ if (request.params.endLine) {
27
+ console.info(` End Line: ${request.params.endLine}`);
28
+ }
29
+
30
+ if (!isInteractive) {
31
+ console.info('\n❌ REJECTED - Non-interactive environment (no terminal)');
32
+ console.info('\n💡 To enable auto-approval in CI/scripts:');
33
+ console.info(' export MATIMO_AUTO_APPROVE=true');
34
+ console.info('\n💡 Or approve specific patterns:');
35
+ console.info(' export MATIMO_APPROVED_PATTERNS="edit"');
36
+ console.info('\n' + '='.repeat(70) + '\n');
37
+ return false;
38
+ }
39
+
40
+ // Interactive mode: prompt user
41
+ const rl = readline.createInterface({
42
+ input: process.stdin,
43
+ output: process.stdout,
44
+ });
45
+
46
+ return new Promise((resolve) => {
47
+ console.info('\n❓ User Action Required');
48
+ const question = ' Type "yes" to approve or "no" to reject: ';
49
+
50
+ rl.question(question, (answer) => {
51
+ const approved = answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y';
52
+
53
+ if (approved) {
54
+ console.info(' ✅ Operation APPROVED by user');
55
+ } else {
56
+ console.info(' ❌ Operation REJECTED by user');
57
+ }
58
+ console.info('='.repeat(70) + '\n');
59
+
60
+ rl.close();
61
+ resolve(approved);
62
+ });
63
+ });
64
+ };
65
+ }
66
+
67
+ /**
68
+ * Example: Edit tool using factory pattern
69
+ * Demonstrates editing and modifying file contents with interactive approval
70
+ */
71
+ async function editExample() {
72
+ // Initialize Matimo with autoDiscover to find all tools (core + providers)
73
+ const matimo = await MatimoInstance.init({ autoDiscover: true });
74
+
75
+ // Configure centralized approval handler
76
+ const approvalHandler = getGlobalApprovalHandler();
77
+ approvalHandler.setApprovalCallback(createApprovalCallback());
78
+
79
+ console.info('=== Edit Tool - Factory Pattern (Interactive Approval) ===\n');
80
+
81
+ // Create a temp file for demonstration
82
+ const tempFile = path.join(__dirname, 'temp-demo.txt');
83
+ fs.writeFileSync(tempFile, 'Line 1\nLine 2\nLine 3\n');
84
+
85
+ try {
86
+ // Example 1: Replace text in file (replace line 2)
87
+ console.info('1. Replacing content in file\n');
88
+ console.info('Original content:');
89
+ console.info(fs.readFileSync(tempFile, 'utf-8'));
90
+ console.info('---\n');
91
+
92
+ const result = await matimo.execute('edit', {
93
+ filePath: tempFile,
94
+ operation: 'replace',
95
+ content: 'Line 2 (Modified)',
96
+ startLine: 2,
97
+ endLine: 2,
98
+ });
99
+
100
+ if ((result as any).success) {
101
+ console.info('Edit Result:', (result as any).success);
102
+ console.info('Lines Affected:', (result as any).linesAffected);
103
+ console.info('\nModified content:');
104
+ console.info(fs.readFileSync(tempFile, 'utf-8'));
105
+ } else {
106
+ console.info('Edit denied:', (result as any).error);
107
+ }
108
+ console.info('---\n');
109
+
110
+ // Example 2: Insert new content
111
+ console.info('2. Inserting new line\n');
112
+ const insertResult = await matimo.execute('edit', {
113
+ filePath: tempFile,
114
+ operation: 'insert',
115
+ content: 'New inserted line',
116
+ startLine: 2,
117
+ });
118
+
119
+ if ((insertResult as any).success) {
120
+ console.info('Insert Result:', (insertResult as any).success);
121
+ console.info('Lines Affected:', (insertResult as any).linesAffected);
122
+ console.info('\nFinal content:');
123
+ console.info(fs.readFileSync(tempFile, 'utf-8'));
124
+ } else {
125
+ console.info('Insert denied:', (insertResult as any).error);
126
+ }
127
+ console.info('---\n');
128
+ } catch (error: any) {
129
+ console.error('Error editing file:', error.message);
130
+ } finally {
131
+ // Clean up temp file
132
+ if (fs.existsSync(tempFile)) {
133
+ fs.unlinkSync(tempFile);
134
+ }
135
+ }
136
+ }
137
+
138
+ editExample();