matimo-examples 1.0.0

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.
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ============================================================================
4
+ * SLACK TOOLS - FACTORY PATTERN EXAMPLE
5
+ * ============================================================================
6
+ *
7
+ * PATTERN: SDK Factory Pattern
8
+ * ─────────────────────────────────────────────────────────────────────────
9
+ * Direct tool execution via MatimoInstance - the simplest way to use tools.
10
+ *
11
+ * Use this pattern when:
12
+ * ✅ Building simple scripts or CLI tools
13
+ * ✅ Direct API calls without abstraction
14
+ * ✅ Quick prototyping
15
+ * ✅ One-off tool execution
16
+ *
17
+ * SETUP:
18
+ * ─────────────────────────────────────────────────────────────────────────
19
+ * 1. Create .env file in project root:
20
+ * SLACK_BOT_TOKEN=xoxb-xxxxxxxxxxxx
21
+ *
22
+ * 2. Get a Slack bot token:
23
+ * - Go to: https://api.slack.com/apps
24
+ * - Create a new app or select existing
25
+ * - OAuth & Permissions → Install app to workspace
26
+ * - Copy "Bot User OAuth Token"
27
+ * - Required scopes: chat:write, channels:read, conversations:history
28
+ *
29
+ * USAGE:
30
+ * ─────────────────────────────────────────────────────────────────────────
31
+ * export SLACK_BOT_TOKEN=xoxb-xxxx
32
+ * npm run slack:factory -- --channel:C123456
33
+ *
34
+ * AVAILABLE TOOLS:
35
+ * ─────────────────────────────────────────────────────────────────────────
36
+ * 1. slack-send-message
37
+ * Parameters: channel (required), text (required), [blocks]
38
+ * Returns: Message timestamp and channel ID
39
+ * Example: Send a message to #general channel
40
+ *
41
+ * 2. slack-list-channels
42
+ * Parameters: [types], [limit], [cursor]
43
+ * Returns: List of channels, DMs, and groups
44
+ * Types: public_channel, private_channel, mpim, im
45
+ *
46
+ * 3. slack_create_channel
47
+ * Parameters: name (required), [is_private]
48
+ * Returns: Channel object with ID and name
49
+ * Example: Create a new public or private channel
50
+ *
51
+ * 4. slack_join_channel
52
+ * Parameters: channel (required)
53
+ * Returns: { ok: true/false }
54
+ * Example: Bot joins a public channel
55
+ *
56
+ * 5. slack_set_channel_topic
57
+ * Parameters: channel (required), topic (required)
58
+ * Returns: { ok: true/false, topic }
59
+ * Example: Set channel description/topic
60
+ *
61
+ * 6. slack_get_channel_history
62
+ * Parameters: channel (required), [limit], [oldest], [latest], [cursor]
63
+ * Returns: Messages array with pagination
64
+ * Example: Get recent messages from channel
65
+ *
66
+ * And more...
67
+ *
68
+ * ============================================================================
69
+ */
70
+
71
+ import 'dotenv/config';
72
+ import path from 'path';
73
+ import { fileURLToPath } from 'url';
74
+ import { MatimoInstance } from 'matimo';
75
+
76
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
77
+
78
+ /**
79
+ * Run factory pattern examples
80
+ */
81
+ async function runFactoryPatternExamples() {
82
+ // Parse CLI arguments
83
+ const args = process.argv.slice(2);
84
+ let channelId = process.env.SLACK_CHANNEL_ID || 'C0000000000';
85
+
86
+ for (const arg of args) {
87
+ if (arg.startsWith('--channel:')) {
88
+ channelId = arg.split(':')[1];
89
+ } else if (arg.startsWith('--channel=')) {
90
+ channelId = arg.split('=')[1];
91
+ }
92
+ }
93
+
94
+ console.info('\n╔════════════════════════════════════════════════════════╗');
95
+ console.info('║ Slack Tools - Factory Pattern ║');
96
+ console.info('║ (Direct execution - simplest approach) ║');
97
+ console.info('╚════════════════════════════════════════════════════════╝\n');
98
+
99
+ const botToken = process.env.SLACK_BOT_TOKEN;
100
+ if (!botToken) {
101
+ console.error('❌ Error: SLACK_BOT_TOKEN not set in .env');
102
+ console.info(' Set it: export SLACK_BOT_TOKEN="xoxb-xxxx"');
103
+ console.info(' Get one from: https://api.slack.com/apps');
104
+ process.exit(1);
105
+ }
106
+
107
+ console.info(`🤖 Bot Token: ${botToken.slice(0, 10)}...`);
108
+ console.info(`📍 Target Channel: ${channelId}\n`);
109
+
110
+ // Initialize Matimo with auto-discovery to find all @matimo/* packages
111
+ console.info('🚀 Initializing Matimo...');
112
+ const matimo = await MatimoInstance.init({ autoDiscover: true });
113
+
114
+ const allTools = matimo.listTools();
115
+ console.info(`✅ Loaded ${allTools.length} tools\n`);
116
+
117
+ // Get Slack tools
118
+ const slackTools = allTools.filter((t) => t.name.startsWith('slack'));
119
+ console.info(`🔧 Found ${slackTools.length} Slack tools\n`);
120
+
121
+ // List available channels and use first one if default doesn't exist
122
+ console.info('📋 Finding an available channel...');
123
+ const listResult = await matimo.execute('slack-list-channels', {
124
+ limit: 10,
125
+ types: 'public_channel,private_channel',
126
+ });
127
+ const listData = (listResult as any).data || listResult;
128
+ let activeChannel = channelId;
129
+
130
+ if (listData.ok === true && listData.channels && listData.channels.length > 0) {
131
+ // Use first available channel if default is not found
132
+ const defaultChannelExists = listData.channels.some((ch: any) => ch.id === channelId);
133
+ if (!defaultChannelExists) {
134
+ activeChannel = listData.channels[0].id;
135
+ console.info(
136
+ ` Using first available channel: #${listData.channels[0].name} (${activeChannel})`
137
+ );
138
+ } else {
139
+ console.info(
140
+ ` Using specified channel: #${listData.channels.find((ch: any) => ch.id === channelId)?.name} (${channelId})`
141
+ );
142
+ }
143
+ } else {
144
+ console.info(` ⚠️ Could not list channels, using default: ${channelId}`);
145
+ }
146
+ console.info();
147
+
148
+ console.info('════════════════════════════════════════════════════════════\n');
149
+ console.info('Running Examples:');
150
+ console.info('════════════════════════════════════════════════════════════\n');
151
+
152
+ try {
153
+ // Example 1: Send a message
154
+ console.info('1️⃣ Sending message to channel...');
155
+ const sendResult = await matimo.execute('slack-send-message', {
156
+ channel: activeChannel,
157
+ text: `🤖 Factory Pattern test message at ${new Date().toISOString()}`,
158
+ });
159
+ // Slack API returns {ok: true/false, ...} or wrapped in data
160
+ const sendData = (sendResult as any).data || sendResult;
161
+ if (sendData.ok === true) {
162
+ console.info(' ✅ Message sent successfully');
163
+ console.info(` Channel: ${sendData.channel}`);
164
+ console.info(` Timestamp: ${sendData.ts}\n`);
165
+ } else {
166
+ console.info(` ❌ Failed: ${sendData.error || 'Unknown error'}`);
167
+ console.info(` Response: ${JSON.stringify(sendData)}\n`);
168
+ }
169
+
170
+ // Example 2: List channels
171
+ console.info('2️⃣ Listing channels...');
172
+ const listResult = await matimo.execute('slack-list-channels', {
173
+ limit: 5,
174
+ types: 'public_channel,private_channel',
175
+ });
176
+ const listData = (listResult as any).data || listResult;
177
+ if (listData.ok === true && listData.channels) {
178
+ const channels = listData.channels || [];
179
+ console.info(` ✅ Found ${channels.length} channels`);
180
+ channels.slice(0, 3).forEach((ch: any) => {
181
+ console.info(` • #${ch.name} (${ch.id})`);
182
+ });
183
+ console.info();
184
+ } else {
185
+ console.info(` ❌ Failed: ${listData.error || 'Unknown error'}`);
186
+ console.info(` Response: ${JSON.stringify(listData)}\n`);
187
+ }
188
+
189
+ // Example 3: Set channel topic
190
+ console.info('3️⃣ Setting channel topic...');
191
+ const topicResult = await matimo.execute('slack_set_channel_topic', {
192
+ channel: activeChannel,
193
+ topic: '🎯 Matimo Testing Channel - Factory Pattern Example',
194
+ });
195
+ const topicData = (topicResult as any).data || topicResult;
196
+ if (topicData.ok === true) {
197
+ console.info(' ✅ Topic set successfully\n');
198
+ } else {
199
+ console.info(` ❌ Failed: ${topicData.error || 'Unknown error'}`);
200
+ console.info(` Response: ${JSON.stringify(topicData)}\n`);
201
+ }
202
+
203
+ // Example 4: Get channel history
204
+ console.info('4️⃣ Retrieving channel history...');
205
+ const historyResult = await matimo.execute('slack_get_channel_history', {
206
+ channel: activeChannel,
207
+ limit: 5,
208
+ });
209
+ const historyData = (historyResult as any).data || historyResult;
210
+ if (historyData.ok === true && historyData.messages) {
211
+ console.info(` ✅ Retrieved ${historyData.messages.length} recent messages\n`);
212
+ } else {
213
+ console.info(` ❌ Failed: ${historyData.error || 'Unknown error'}`);
214
+ console.info(` Response: ${JSON.stringify(historyData)}\n`);
215
+ }
216
+ } catch (error) {
217
+ console.error('❌ Error:', error);
218
+ process.exit(1);
219
+ }
220
+
221
+ console.info('════════════════════════════════════════════════════════════');
222
+ console.info('✨ Factory Pattern Example Complete!');
223
+ console.info('════════════════════════════════════════════════════════════\n');
224
+ }
225
+
226
+ runFactoryPatternExamples().catch(console.error);
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ============================================================================
4
+ * SLACK TOOLS - 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 which Slack tools to use
12
+ * 3. Generates appropriate parameters based on context
13
+ * 4. Executes tools autonomously
14
+ * 5. Processes results and responds naturally
15
+ *
16
+ * Use this pattern when:
17
+ * ✅ Building true autonomous AI agents
18
+ * ✅ LLM should decide which tools to use
19
+ * ✅ Complex workflows with LLM reasoning
20
+ * ✅ Multi-step agentic processes
21
+ * ✅ User gives high-level instructions (not low-level API calls)
22
+ *
23
+ * SETUP:
24
+ * ─────────────────────────────────────────────────────────────────────────
25
+ * 1. Create .env file:
26
+ * SLACK_BOT_TOKEN=xoxb-xxxxxxxxxxxxx
27
+ * OPENAI_API_KEY=sk-xxxxxxxxxxxxx
28
+ *
29
+ * 2. Install dependencies:
30
+ * npm install
31
+ *
32
+ * USAGE:
33
+ * ─────────────────────────────────────────────────────────────────────────
34
+ * export SLACK_BOT_TOKEN=xoxb-xxxx
35
+ * export OPENAI_API_KEY=sk-xxxx
36
+ * npm run slack:langchain
37
+ *
38
+ * WHAT IT DOES:
39
+ * ─────────────────────────────────────────────────────────────────────────
40
+ * This example shows an AI agent that can:
41
+ * 1. List Slack channels
42
+ * 2. Send messages to channels
43
+ * 3. Search message history
44
+ * 4. Retrieve channel information
45
+ * 5. Respond naturally in conversation style
46
+ *
47
+ * Example conversation:
48
+ * User: "Send a test message to #general"
49
+ * AI Agent: "I'll send a test message to the general channel..."
50
+ * [AI Agent calls slack-send-message tool]
51
+ * AI Agent: "Done! Message sent successfully."
52
+ *
53
+ * ============================================================================
54
+ */
55
+
56
+ import 'dotenv/config';
57
+ import path from 'path';
58
+ import { fileURLToPath } from 'url';
59
+ import { createAgent } from 'langchain';
60
+ import { ChatOpenAI } from '@langchain/openai';
61
+ import { MatimoInstance, convertToolsToLangChain } from 'matimo';
62
+
63
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
64
+
65
+ /**
66
+ * Run AI Agent with Slack tools
67
+ * The agent receives natural language requests and decides which Slack tools to use
68
+ */
69
+ async function runSlackAIAgent() {
70
+ // Parse CLI arguments
71
+ const args = process.argv.slice(2);
72
+ let channelId = process.env.TEST_CHANNEL || 'C0000000000';
73
+
74
+ for (const arg of args) {
75
+ if (arg.startsWith('--channel:')) {
76
+ channelId = arg.split(':')[1];
77
+ } else if (arg.startsWith('--channel=')) {
78
+ channelId = arg.split('=')[1];
79
+ }
80
+ }
81
+
82
+ console.info('\n╔════════════════════════════════════════════════════════╗');
83
+ console.info('║ Slack AI Agent - LangChain + OpenAI ║');
84
+ console.info('║ True autonomous agent with LLM reasoning ║');
85
+ console.info('╚════════════════════════════════════════════════════════╝\n');
86
+
87
+ // Check required environment variables
88
+ const botToken = process.env.SLACK_BOT_TOKEN;
89
+ if (!botToken) {
90
+ console.error('❌ Error: SLACK_BOT_TOKEN not set in .env');
91
+ console.info(' Set it: export SLACK_BOT_TOKEN="xoxb-..."');
92
+ console.info(' Get one from: https://api.slack.com/apps');
93
+ process.exit(1);
94
+ }
95
+
96
+ const openaiKey = process.env.OPENAI_API_KEY;
97
+ if (!openaiKey) {
98
+ console.error('❌ Error: OPENAI_API_KEY not set in .env');
99
+ console.info(' Set it: export OPENAI_API_KEY="sk-..."');
100
+ process.exit(1);
101
+ }
102
+
103
+ console.info(`📍 Target Channel: ${channelId}`);
104
+ console.info(`🤖 Using OpenAI (GPT-4o-mini) as the AI agent\n`);
105
+
106
+ try {
107
+ // Initialize Matimo with auto-discovery
108
+ console.info('🚀 Initializing Matimo...');
109
+ const matimo = await MatimoInstance.init({ autoDiscover: true });
110
+
111
+ // Get Slack tools and convert to LangChain format
112
+ console.info('💬 Loading Slack tools...');
113
+ const matimoTools = matimo.listTools();
114
+ const slackTools = matimoTools.filter((t) => t.name.startsWith('slack'));
115
+ console.info(`✅ Loaded ${slackTools.length} Slack tools\n`);
116
+
117
+ // Find an available channel before creating agent
118
+ console.info('📋 Finding an available channel...');
119
+ const listChannelsResult = await matimo.execute('slack-list-channels', {
120
+ types: 'public_channel,private_channel',
121
+ limit: 10,
122
+ });
123
+
124
+ const listData = (listChannelsResult as any).data || listChannelsResult;
125
+ let activeChannel = channelId;
126
+
127
+ if (listData.ok === true && listData.channels && listData.channels.length > 0) {
128
+ const defaultChannelExists = listData.channels.some((ch: any) => ch.id === channelId);
129
+ if (!defaultChannelExists) {
130
+ activeChannel = listData.channels[0].id;
131
+ console.info(
132
+ ` Using first available channel: #${listData.channels[0].name} (${activeChannel})\n`
133
+ );
134
+ } else {
135
+ console.info(
136
+ ` Using specified channel: #${listData.channels.find((ch: any) => ch.id === channelId)?.name} (${channelId})\n`
137
+ );
138
+ }
139
+ } else {
140
+ console.info(` ⚠️ Could not list channels, using default: ${channelId}\n`);
141
+ }
142
+
143
+ // Convert to LangChain tools (select key ones for agent)
144
+ const keySlackTools = slackTools.filter((t) =>
145
+ [
146
+ 'slack-send-message',
147
+ 'slack-list-channels',
148
+ 'slack_get_channel_history',
149
+ 'slack_search_messages',
150
+ 'slack_get_user_info',
151
+ ].includes(t.name)
152
+ );
153
+
154
+ // ✅ Convert Matimo tools to LangChain format using the new integration
155
+ const langchainTools = await convertToolsToLangChain(keySlackTools, matimo, {
156
+ SLACK_BOT_TOKEN: botToken,
157
+ });
158
+
159
+ // Initialize OpenAI LLM
160
+ console.info('🤖 Initializing OpenAI (GPT-4o-mini) LLM...');
161
+ const model = new ChatOpenAI({
162
+ modelName: 'gpt-4o-mini',
163
+ temperature: 0.7,
164
+ });
165
+
166
+ // Create agent
167
+ console.info('🔧 Creating agent...\n');
168
+ const agent = await createAgent({
169
+ model,
170
+ tools: langchainTools,
171
+ });
172
+
173
+ // Define agent tasks (natural language requests)
174
+ const userRequests = [
175
+ {
176
+ title: 'Example 1: List channels',
177
+ request: 'What Slack channels are available in this workspace?',
178
+ },
179
+ {
180
+ title: 'Example 2: Send a message',
181
+ request: `Send a test message to channel ${activeChannel} saying "Hello from AI Agent! This message was sent autonomously."`,
182
+ },
183
+ {
184
+ title: 'Example 3: Get channel history',
185
+ request: `What are the recent messages in channel ${activeChannel}?`,
186
+ },
187
+ ];
188
+
189
+ console.info('🧪 Running AI Agent Tasks');
190
+ console.info('═'.repeat(60));
191
+
192
+ // Run each task through the agent
193
+ for (const task of userRequests) {
194
+ console.info(`\n${task.title}`);
195
+ console.info('─'.repeat(60));
196
+ console.info(`👤 User: "${task.request}"\n`);
197
+
198
+ try {
199
+ const response = await agent.invoke({
200
+ messages: [
201
+ {
202
+ role: 'user',
203
+ content: task.request,
204
+ },
205
+ ],
206
+ });
207
+
208
+ // Get the last message from the agent
209
+ const lastMessage = response.messages[response.messages.length - 1];
210
+ if (lastMessage) {
211
+ if (typeof lastMessage.content === 'string') {
212
+ console.info(`🤖 Agent: ${lastMessage.content}\n`);
213
+ } else {
214
+ console.info(`🤖 Agent:`, lastMessage.content, '\n');
215
+ }
216
+ }
217
+ } catch (error) {
218
+ const errorMsg = error instanceof Error ? error.message : String(error);
219
+ console.info(`⚠️ Agent error: ${errorMsg}\n`);
220
+ }
221
+ }
222
+
223
+ console.info('═'.repeat(60));
224
+ console.info('✨ AI Agent Examples Complete!\n');
225
+ console.info('Key Features:');
226
+ console.info(' ✅ Real LLM (OpenAI) decides which tools to use');
227
+ console.info(' ✅ Natural language requests, not API calls');
228
+ console.info(' ✅ LLM generates tool parameters based on context');
229
+ console.info(' ✅ Agentic reasoning and decision-making\n');
230
+ } catch (error) {
231
+ console.error('❌ Error:', error instanceof Error ? error.message : String(error));
232
+ if (error instanceof Error && error.stack) {
233
+ console.error('Stack:', error.stack);
234
+ }
235
+ process.exit(1);
236
+ }
237
+ }
238
+
239
+ // Run the AI agent
240
+ runSlackAIAgent().catch(console.error);
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ES2020",
5
+ "lib": ["ES2020"],
6
+ "moduleResolution": "bundler",
7
+ "resolveJsonModule": true,
8
+ "esModuleInterop": true,
9
+ "allowSyntheticDefaultImports": true,
10
+ "strict": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "declaration": true,
14
+ "declarationMap": true,
15
+ "sourceMap": true,
16
+ "outDir": "./dist"
17
+ },
18
+ "include": ["agents/**/*.ts", "gmail/**/*.ts", "slack/**/*.ts"],
19
+ "exclude": ["node_modules", "dist"]
20
+ }