@toothfairyai/cli 1.0.3 ā 1.0.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/README.md +173 -10
- package/bin/toothfairy.js +455 -197
- package/package.json +3 -2
- package/src/api.js +268 -33
- package/src/config.js +5 -1
package/bin/toothfairy.js
CHANGED
|
@@ -1,57 +1,63 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const { Command } = require(
|
|
4
|
-
const chalk = require(
|
|
5
|
-
const ora = require(
|
|
6
|
-
const { table } = require(
|
|
7
|
-
require(
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const ora = require('ora');
|
|
6
|
+
const { table } = require('table');
|
|
7
|
+
require('dotenv').config();
|
|
8
8
|
|
|
9
|
-
const ToothFairyAPI = require(
|
|
9
|
+
const ToothFairyAPI = require('../src/api');
|
|
10
10
|
const {
|
|
11
11
|
loadConfig,
|
|
12
12
|
saveConfig,
|
|
13
13
|
ToothFairyConfig,
|
|
14
14
|
getConfigPath,
|
|
15
15
|
validateConfiguration,
|
|
16
|
-
} = require(
|
|
16
|
+
} = require('../src/config');
|
|
17
17
|
|
|
18
18
|
const program = new Command();
|
|
19
19
|
|
|
20
20
|
program
|
|
21
|
-
.name(
|
|
21
|
+
.name('toothfairy')
|
|
22
22
|
.description(
|
|
23
|
-
|
|
23
|
+
'ToothFairy AI CLI - Interact with ToothFairy AI agents via command line'
|
|
24
24
|
)
|
|
25
|
-
.version(
|
|
25
|
+
.version('1.0.0');
|
|
26
26
|
|
|
27
27
|
program
|
|
28
|
-
.option(
|
|
29
|
-
.option(
|
|
28
|
+
.option('-c, --config <path>', 'path to configuration file')
|
|
29
|
+
.option('-v, --verbose', 'enable verbose logging');
|
|
30
30
|
|
|
31
31
|
// Configure command
|
|
32
32
|
program
|
|
33
|
-
.command(
|
|
34
|
-
.description(
|
|
33
|
+
.command('configure')
|
|
34
|
+
.description('Configure ToothFairy CLI credentials and settings')
|
|
35
35
|
.option(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
'--base-url <url>',
|
|
37
|
+
'ToothFairy API base URL',
|
|
38
|
+
'https://api.toothfairyai.com'
|
|
39
39
|
)
|
|
40
|
-
.option(
|
|
41
|
-
.
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
.option('--ai-url <url>', 'ToothFairy AI URL', 'https://ai.toothfairyai.com')
|
|
41
|
+
.option(
|
|
42
|
+
'--ai-stream-url <url>',
|
|
43
|
+
'ToothFairy AI Streaming URL',
|
|
44
|
+
'https://ais.toothfairyai.com'
|
|
45
|
+
)
|
|
46
|
+
.requiredOption('--api-key <key>', 'API key')
|
|
47
|
+
.requiredOption('--workspace-id <id>', 'Workspace ID')
|
|
48
|
+
.option('--config-path <path>', 'Path to save config file')
|
|
44
49
|
.action(async (options) => {
|
|
45
50
|
try {
|
|
46
51
|
const config = new ToothFairyConfig(
|
|
47
52
|
options.baseUrl,
|
|
48
53
|
options.aiUrl,
|
|
54
|
+
options.aiStreamUrl,
|
|
49
55
|
options.apiKey,
|
|
50
56
|
options.workspaceId
|
|
51
57
|
);
|
|
52
58
|
|
|
53
59
|
saveConfig(config, options.configPath);
|
|
54
|
-
console.log(chalk.green(
|
|
60
|
+
console.log(chalk.green('Configuration saved successfully!'));
|
|
55
61
|
if (!options.configPath) {
|
|
56
62
|
console.log(`Config saved to: ${getConfigPath()}`);
|
|
57
63
|
}
|
|
@@ -63,16 +69,16 @@ program
|
|
|
63
69
|
|
|
64
70
|
// Send command
|
|
65
71
|
program
|
|
66
|
-
.command(
|
|
67
|
-
.description(
|
|
68
|
-
.argument(
|
|
69
|
-
.requiredOption(
|
|
70
|
-
.option(
|
|
71
|
-
.option(
|
|
72
|
-
.option(
|
|
73
|
-
.option(
|
|
74
|
-
.option(
|
|
75
|
-
.option(
|
|
72
|
+
.command('send')
|
|
73
|
+
.description('Send a message to a ToothFairy AI agent')
|
|
74
|
+
.argument('<message>', 'message to send')
|
|
75
|
+
.requiredOption('--agent-id <id>', 'Agent ID to send message to')
|
|
76
|
+
.option('--phone-number <number>', 'Phone number for SMS channel')
|
|
77
|
+
.option('--customer-id <id>', 'Customer ID')
|
|
78
|
+
.option('--provider-id <id>', 'SMS provider ID')
|
|
79
|
+
.option('--customer-info <json>', 'Customer info as JSON string')
|
|
80
|
+
.option('-o, --output <format>', 'Output format (json|text)', 'text')
|
|
81
|
+
.option('-v, --verbose', 'Show detailed response information')
|
|
76
82
|
.action(async (message, options, command) => {
|
|
77
83
|
try {
|
|
78
84
|
const globalOptions = command.parent.opts();
|
|
@@ -82,6 +88,7 @@ program
|
|
|
82
88
|
const api = new ToothFairyAPI(
|
|
83
89
|
config.baseUrl,
|
|
84
90
|
config.aiUrl,
|
|
91
|
+
config.aiStreamUrl,
|
|
85
92
|
config.apiKey,
|
|
86
93
|
config.workspaceId,
|
|
87
94
|
globalOptions.verbose || options.verbose
|
|
@@ -93,12 +100,12 @@ program
|
|
|
93
100
|
try {
|
|
94
101
|
customerInfo = JSON.parse(options.customerInfo);
|
|
95
102
|
} catch (error) {
|
|
96
|
-
console.error(chalk.red(
|
|
103
|
+
console.error(chalk.red('Invalid JSON in customer-info'));
|
|
97
104
|
process.exit(1);
|
|
98
105
|
}
|
|
99
106
|
}
|
|
100
107
|
|
|
101
|
-
const spinner = ora(
|
|
108
|
+
const spinner = ora('Sending message to agent...').start();
|
|
102
109
|
|
|
103
110
|
const response = await api.sendMessageToAgent(
|
|
104
111
|
message,
|
|
@@ -111,36 +118,36 @@ program
|
|
|
111
118
|
|
|
112
119
|
spinner.stop();
|
|
113
120
|
|
|
114
|
-
if (options.output ===
|
|
121
|
+
if (options.output === 'json') {
|
|
115
122
|
console.log(JSON.stringify(response, null, 2));
|
|
116
123
|
} else {
|
|
117
124
|
const agentResp = response.agentResponse;
|
|
118
125
|
|
|
119
126
|
if (options.verbose) {
|
|
120
127
|
// Verbose mode: show all details
|
|
121
|
-
console.log(chalk.green.bold(
|
|
128
|
+
console.log(chalk.green.bold('Message sent successfully!'));
|
|
122
129
|
console.log();
|
|
123
130
|
|
|
124
131
|
const data = [
|
|
125
|
-
[
|
|
126
|
-
[
|
|
127
|
-
[
|
|
132
|
+
['Field', 'Value'],
|
|
133
|
+
['Chat ID', response.chatId],
|
|
134
|
+
['Message ID', response.messageId],
|
|
128
135
|
];
|
|
129
136
|
|
|
130
137
|
console.log(
|
|
131
138
|
table(data, {
|
|
132
139
|
header: {
|
|
133
|
-
alignment:
|
|
134
|
-
content:
|
|
140
|
+
alignment: 'center',
|
|
141
|
+
content: 'Response Details',
|
|
135
142
|
},
|
|
136
143
|
})
|
|
137
144
|
);
|
|
138
145
|
|
|
139
146
|
// Show full agent response
|
|
140
|
-
console.log(chalk.blue.bold(
|
|
141
|
-
console.log(
|
|
147
|
+
console.log(chalk.blue.bold('Agent Response (Full):'));
|
|
148
|
+
console.log('ā'.repeat(50));
|
|
142
149
|
console.log(JSON.stringify(agentResp, null, 2));
|
|
143
|
-
console.log(
|
|
150
|
+
console.log('ā'.repeat(50));
|
|
144
151
|
} else {
|
|
145
152
|
// Default mode: show only the clean agent text
|
|
146
153
|
if (agentResp.contents && agentResp.contents.content) {
|
|
@@ -153,7 +160,7 @@ program
|
|
|
153
160
|
// Fallback if no recognizable text format
|
|
154
161
|
console.log(
|
|
155
162
|
chalk.yellow(
|
|
156
|
-
|
|
163
|
+
'No text response found. Use --verbose for full details.'
|
|
157
164
|
)
|
|
158
165
|
);
|
|
159
166
|
}
|
|
@@ -165,24 +172,272 @@ program
|
|
|
165
172
|
}
|
|
166
173
|
});
|
|
167
174
|
|
|
175
|
+
// Send Stream command
|
|
176
|
+
program
|
|
177
|
+
.command('send-stream')
|
|
178
|
+
.description(
|
|
179
|
+
'Send a message to a ToothFairy AI agent with real-time streaming response'
|
|
180
|
+
)
|
|
181
|
+
.argument('<message>', 'message to send')
|
|
182
|
+
.requiredOption('--agent-id <id>', 'Agent ID to send message to')
|
|
183
|
+
.option('--phone-number <number>', 'Phone number for SMS channel')
|
|
184
|
+
.option('--customer-id <id>', 'Customer ID')
|
|
185
|
+
.option('--provider-id <id>', 'SMS provider ID')
|
|
186
|
+
.option('--customer-info <json>', 'Customer info as JSON string')
|
|
187
|
+
.option('-o, --output <format>', 'Output format (json|text)', 'text')
|
|
188
|
+
.option('-v, --verbose', 'Show detailed streaming information')
|
|
189
|
+
.option('--show-progress', 'Show agent processing status updates')
|
|
190
|
+
.action(async (message, options, command) => {
|
|
191
|
+
/*
|
|
192
|
+
STREAMING BEHAVIOR EXPLAINED:
|
|
193
|
+
|
|
194
|
+
š STATUS UPDATES: The agent goes through several processing phases:
|
|
195
|
+
|
|
196
|
+
⢠'connected': Connection established with streaming server
|
|
197
|
+
⢠'init': Agent initialization started
|
|
198
|
+
⢠'initial_setup_completed': Basic setup and context loading finished
|
|
199
|
+
⢠'tools_processing_completed': Any required tools/functions processed
|
|
200
|
+
⢠'replying': Agent begins generating the actual response (text starts streaming)
|
|
201
|
+
⢠'updating_memory': Agent updates conversation memory
|
|
202
|
+
⢠'memory_updated': Memory update completed
|
|
203
|
+
⢠'complete': Stream finished successfully
|
|
204
|
+
|
|
205
|
+
š TEXT STREAMING: Once the agent reaches 'replying' status, you'll see the response
|
|
206
|
+
text being built progressively, word by word, just like ChatGPT or similar AI assistants.
|
|
207
|
+
|
|
208
|
+
š” TIP: Use --show-progress to see detailed status updates, or --verbose for full debug info.
|
|
209
|
+
*/
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
const globalOptions = command.parent.opts();
|
|
213
|
+
const config = loadConfig(globalOptions.config);
|
|
214
|
+
validateConfiguration(config);
|
|
215
|
+
|
|
216
|
+
const api = new ToothFairyAPI(
|
|
217
|
+
config.baseUrl,
|
|
218
|
+
config.aiUrl,
|
|
219
|
+
config.aiStreamUrl,
|
|
220
|
+
config.apiKey,
|
|
221
|
+
config.workspaceId,
|
|
222
|
+
globalOptions.verbose || options.verbose
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// Parse customer info if provided
|
|
226
|
+
let customerInfo = {};
|
|
227
|
+
if (options.customerInfo) {
|
|
228
|
+
try {
|
|
229
|
+
customerInfo = JSON.parse(options.customerInfo);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error(chalk.red('Invalid JSON in customer-info'));
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
console.log(
|
|
237
|
+
chalk.cyan(`Streaming message to agent ${options.agentId}...`)
|
|
238
|
+
);
|
|
239
|
+
console.log(chalk.dim(`Message: ${message}`));
|
|
240
|
+
console.log();
|
|
241
|
+
|
|
242
|
+
// Initialize variables for streaming
|
|
243
|
+
let currentText = '';
|
|
244
|
+
let finalResponse = null;
|
|
245
|
+
let processingStatus = null;
|
|
246
|
+
|
|
247
|
+
const formatStatusMessage = (status) => {
|
|
248
|
+
const statusMessages = {
|
|
249
|
+
connected: 'š Connected to streaming server',
|
|
250
|
+
init: 'š Initializing agent...',
|
|
251
|
+
initial_setup_completed: 'ā
Agent setup completed',
|
|
252
|
+
tools_processing_completed: 'š ļø Tools processing finished',
|
|
253
|
+
replying: 'š Agent is thinking and responding...',
|
|
254
|
+
updating_memory: 'š¾ Updating conversation memory...',
|
|
255
|
+
memory_updated: 'ā
Memory updated successfully',
|
|
256
|
+
complete: 'š Response complete!',
|
|
257
|
+
};
|
|
258
|
+
return statusMessages[status] || `š Status: ${status}`;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
if (options.output === 'json') {
|
|
262
|
+
// JSON mode: collect all events and output at the end
|
|
263
|
+
const allEvents = [];
|
|
264
|
+
|
|
265
|
+
await api.sendMessageToAgentStream(
|
|
266
|
+
message,
|
|
267
|
+
options.agentId,
|
|
268
|
+
options.phoneNumber,
|
|
269
|
+
options.customerId,
|
|
270
|
+
options.providerId,
|
|
271
|
+
customerInfo,
|
|
272
|
+
(eventType, eventData) => {
|
|
273
|
+
allEvents.push({ event_type: eventType, event_data: eventData });
|
|
274
|
+
|
|
275
|
+
if (eventType === 'error') {
|
|
276
|
+
console.error(
|
|
277
|
+
chalk.red(
|
|
278
|
+
`Streaming error: ${eventData.message || 'Unknown error'}`
|
|
279
|
+
)
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
console.log(JSON.stringify(allEvents, null, 2));
|
|
286
|
+
} else {
|
|
287
|
+
// Text mode: show live streaming
|
|
288
|
+
let currentSpinner = null;
|
|
289
|
+
|
|
290
|
+
// Function to update display
|
|
291
|
+
const updateDisplay = (text, type = 'response') => {
|
|
292
|
+
if (currentSpinner) {
|
|
293
|
+
currentSpinner.stop();
|
|
294
|
+
currentSpinner = null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Clear previous lines if we're updating the same response
|
|
298
|
+
if (text && type === 'response') {
|
|
299
|
+
// For real-time streaming, we overwrite the current line
|
|
300
|
+
process.stdout.write('\r\x1b[K'); // Clear current line
|
|
301
|
+
process.stdout.write(chalk.green('š§ Response: ') + text);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
try {
|
|
306
|
+
await api.sendMessageToAgentStream(
|
|
307
|
+
message,
|
|
308
|
+
options.agentId,
|
|
309
|
+
options.phoneNumber,
|
|
310
|
+
options.customerId,
|
|
311
|
+
options.providerId,
|
|
312
|
+
customerInfo,
|
|
313
|
+
(eventType, eventData) => {
|
|
314
|
+
if (options.verbose) {
|
|
315
|
+
// Verbose mode: show all event details
|
|
316
|
+
console.log(
|
|
317
|
+
chalk.dim(
|
|
318
|
+
`\nEvent: ${eventType} | Data: ${JSON.stringify(
|
|
319
|
+
eventData,
|
|
320
|
+
null,
|
|
321
|
+
2
|
|
322
|
+
)}`
|
|
323
|
+
)
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (eventType === 'status') {
|
|
328
|
+
if (eventData.status === 'connected') {
|
|
329
|
+
if (options.showProgress) {
|
|
330
|
+
console.log(
|
|
331
|
+
chalk.green('š Connected to streaming server')
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
} else if (eventData.status === 'complete') {
|
|
335
|
+
if (options.showProgress) {
|
|
336
|
+
console.log(
|
|
337
|
+
chalk.green('\nš Stream completed successfully!')
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
} else if (eventType === 'progress') {
|
|
342
|
+
// Agent processing status updates
|
|
343
|
+
const newStatus = eventData.processing_status;
|
|
344
|
+
if (newStatus && newStatus !== processingStatus) {
|
|
345
|
+
processingStatus = newStatus;
|
|
346
|
+
if (options.showProgress) {
|
|
347
|
+
const statusMsg = formatStatusMessage(processingStatus);
|
|
348
|
+
if (currentSpinner) {
|
|
349
|
+
currentSpinner.stop();
|
|
350
|
+
}
|
|
351
|
+
currentSpinner = ora(chalk.yellow(statusMsg)).start();
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
} else if (eventType === 'data') {
|
|
355
|
+
// Streaming text data
|
|
356
|
+
if (eventData.text) {
|
|
357
|
+
currentText = eventData.text;
|
|
358
|
+
updateDisplay(currentText.trim());
|
|
359
|
+
}
|
|
360
|
+
} else if (eventType === 'complete') {
|
|
361
|
+
// Final response with all metadata
|
|
362
|
+
finalResponse = eventData;
|
|
363
|
+
if (currentText) {
|
|
364
|
+
// Ensure we have a clean final display - just add the final emoji
|
|
365
|
+
if (currentSpinner) {
|
|
366
|
+
currentSpinner.stop();
|
|
367
|
+
currentSpinner = null;
|
|
368
|
+
}
|
|
369
|
+
process.stdout.write('\r\x1b[K'); // Clear current line
|
|
370
|
+
process.stdout.write(chalk.blue('šŖ ') + currentText.trim());
|
|
371
|
+
}
|
|
372
|
+
} else if (eventType === 'error') {
|
|
373
|
+
const errorMsg = eventData.message || 'Unknown streaming error';
|
|
374
|
+
if (currentSpinner) {
|
|
375
|
+
currentSpinner.stop();
|
|
376
|
+
}
|
|
377
|
+
console.error(chalk.red(`\nā Error: ${errorMsg}`));
|
|
378
|
+
process.exit(1);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
// Clean up spinner after streaming completes
|
|
384
|
+
if (currentSpinner) {
|
|
385
|
+
currentSpinner.stop();
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
console.log(); // Add newline after streaming
|
|
389
|
+
|
|
390
|
+
if (options.verbose && finalResponse) {
|
|
391
|
+
// Show final response metadata in verbose mode
|
|
392
|
+
console.log(chalk.cyan.bold('š Final Response Metadata'));
|
|
393
|
+
console.log('ā'.repeat(40));
|
|
394
|
+
|
|
395
|
+
if (finalResponse.metadata_parsed) {
|
|
396
|
+
const metadata = finalResponse.metadata_parsed;
|
|
397
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
398
|
+
if (key !== 'agent_processing_status') {
|
|
399
|
+
// Already shown during streaming
|
|
400
|
+
console.log(chalk.cyan(`${key}:`), String(value));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
console.log('ā'.repeat(40));
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (!currentText) {
|
|
408
|
+
console.log(chalk.yellow('No text response received from agent.'));
|
|
409
|
+
}
|
|
410
|
+
} catch (streamError) {
|
|
411
|
+
if (currentSpinner) {
|
|
412
|
+
currentSpinner.stop();
|
|
413
|
+
}
|
|
414
|
+
throw streamError;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
} catch (error) {
|
|
418
|
+
console.error(chalk.red(`Error during streaming: ${error.message}`));
|
|
419
|
+
process.exit(1);
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
|
|
168
423
|
// Search command
|
|
169
424
|
program
|
|
170
|
-
.command(
|
|
171
|
-
.description(
|
|
172
|
-
.argument(
|
|
425
|
+
.command('search')
|
|
426
|
+
.description('Search for documents in the knowledge hub')
|
|
427
|
+
.argument('<query>', 'search query text')
|
|
173
428
|
.option(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
429
|
+
'-k, --top-k <number>',
|
|
430
|
+
'Number of documents to retrieve (1-50)',
|
|
431
|
+
'10'
|
|
177
432
|
)
|
|
178
433
|
.option(
|
|
179
|
-
|
|
180
|
-
|
|
434
|
+
'--status <status>',
|
|
435
|
+
'Filter by document status (published|suspended)'
|
|
181
436
|
)
|
|
182
|
-
.option(
|
|
183
|
-
.option(
|
|
184
|
-
.option(
|
|
185
|
-
.option(
|
|
437
|
+
.option('--document-id <id>', 'Search within specific document ID')
|
|
438
|
+
.option('--topics <topics>', 'Comma-separated topic IDs to filter by')
|
|
439
|
+
.option('-o, --output <format>', 'Output format (json|text)', 'text')
|
|
440
|
+
.option('-v, --verbose', 'Show detailed search information')
|
|
186
441
|
.action(async (query, options, command) => {
|
|
187
442
|
try {
|
|
188
443
|
const globalOptions = command.parent.opts();
|
|
@@ -192,6 +447,7 @@ program
|
|
|
192
447
|
const api = new ToothFairyAPI(
|
|
193
448
|
config.baseUrl,
|
|
194
449
|
config.aiUrl,
|
|
450
|
+
config.aiStreamUrl,
|
|
195
451
|
config.apiKey,
|
|
196
452
|
config.workspaceId,
|
|
197
453
|
globalOptions.verbose || options.verbose
|
|
@@ -201,7 +457,7 @@ program
|
|
|
201
457
|
const topK = parseInt(options.topK);
|
|
202
458
|
if (isNaN(topK) || topK < 1 || topK > 50) {
|
|
203
459
|
console.error(
|
|
204
|
-
chalk.red(
|
|
460
|
+
chalk.red('Error: --top-k must be a number between 1 and 50')
|
|
205
461
|
);
|
|
206
462
|
process.exit(1);
|
|
207
463
|
}
|
|
@@ -209,7 +465,7 @@ program
|
|
|
209
465
|
// Build metadata filters
|
|
210
466
|
const metadata = {};
|
|
211
467
|
if (options.status) {
|
|
212
|
-
if (![
|
|
468
|
+
if (!['published', 'suspended'].includes(options.status)) {
|
|
213
469
|
console.error(
|
|
214
470
|
chalk.red(
|
|
215
471
|
'Error: --status must be either "published" or "suspended"'
|
|
@@ -224,7 +480,7 @@ program
|
|
|
224
480
|
}
|
|
225
481
|
if (options.topics) {
|
|
226
482
|
const topicList = options.topics
|
|
227
|
-
.split(
|
|
483
|
+
.split(',')
|
|
228
484
|
.map((t) => t.trim())
|
|
229
485
|
.filter((t) => t);
|
|
230
486
|
if (topicList.length > 0) {
|
|
@@ -232,7 +488,7 @@ program
|
|
|
232
488
|
}
|
|
233
489
|
}
|
|
234
490
|
|
|
235
|
-
const spinner = ora(
|
|
491
|
+
const spinner = ora('Searching knowledge hub...').start();
|
|
236
492
|
|
|
237
493
|
const results = await api.searchDocuments(
|
|
238
494
|
query,
|
|
@@ -242,7 +498,7 @@ program
|
|
|
242
498
|
|
|
243
499
|
spinner.stop();
|
|
244
500
|
|
|
245
|
-
if (options.output ===
|
|
501
|
+
if (options.output === 'json') {
|
|
246
502
|
console.log(JSON.stringify(results, null, 2));
|
|
247
503
|
} else {
|
|
248
504
|
// Handle different response formats - results might be array or dict
|
|
@@ -251,22 +507,22 @@ program
|
|
|
251
507
|
: results.results || [];
|
|
252
508
|
|
|
253
509
|
if (!documents || documents.length === 0) {
|
|
254
|
-
console.log(chalk.yellow(
|
|
510
|
+
console.log(chalk.yellow('No documents found for your query'));
|
|
255
511
|
return;
|
|
256
512
|
}
|
|
257
513
|
|
|
258
514
|
console.log(chalk.green.bold(`Found ${documents.length} document(s)`));
|
|
259
|
-
console.log(
|
|
515
|
+
console.log('='.repeat(50));
|
|
260
516
|
|
|
261
|
-
documents.forEach((doc
|
|
517
|
+
documents.forEach((doc) => {
|
|
262
518
|
const score = doc.cosinesim || 0;
|
|
263
|
-
const docId = doc.doc_id || doc.chunk_id ||
|
|
519
|
+
const docId = doc.doc_id || doc.chunk_id || 'N/A';
|
|
264
520
|
|
|
265
521
|
// Extract text content directly from document
|
|
266
|
-
const textContent = doc.raw_text ||
|
|
267
|
-
const docStatus = doc.status ||
|
|
522
|
+
const textContent = doc.raw_text || 'No content available';
|
|
523
|
+
const docStatus = doc.status || 'unknown';
|
|
268
524
|
const docTopics = doc.topics || [];
|
|
269
|
-
const docTitle = doc.title ||
|
|
525
|
+
const docTitle = doc.title || 'Untitled';
|
|
270
526
|
|
|
271
527
|
console.log(
|
|
272
528
|
chalk.cyan.bold(`\n${docTitle}`),
|
|
@@ -276,16 +532,16 @@ program
|
|
|
276
532
|
if (options.verbose) {
|
|
277
533
|
// Verbose mode: show all details
|
|
278
534
|
const data = [
|
|
279
|
-
[
|
|
280
|
-
[
|
|
281
|
-
[
|
|
282
|
-
[
|
|
283
|
-
[
|
|
284
|
-
[
|
|
535
|
+
['Field', 'Value'],
|
|
536
|
+
['Document ID', docId],
|
|
537
|
+
['Title', docTitle],
|
|
538
|
+
['Relevance Score', score.toFixed(4)],
|
|
539
|
+
['Status', docStatus],
|
|
540
|
+
['Topics', docTopics.length > 0 ? docTopics.join(', ') : 'None'],
|
|
285
541
|
[
|
|
286
|
-
|
|
542
|
+
'Content Preview',
|
|
287
543
|
textContent.length > 200
|
|
288
|
-
? textContent.substring(0, 200) +
|
|
544
|
+
? textContent.substring(0, 200) + '...'
|
|
289
545
|
: textContent,
|
|
290
546
|
],
|
|
291
547
|
];
|
|
@@ -293,13 +549,13 @@ program
|
|
|
293
549
|
console.log(table(data));
|
|
294
550
|
} else {
|
|
295
551
|
// Default mode: show clean content
|
|
296
|
-
console.log(
|
|
552
|
+
console.log('ā'.repeat(50));
|
|
297
553
|
const displayContent =
|
|
298
554
|
textContent.length > 500
|
|
299
|
-
? textContent.substring(0, 500) +
|
|
555
|
+
? textContent.substring(0, 500) + '...'
|
|
300
556
|
: textContent;
|
|
301
557
|
console.log(displayContent);
|
|
302
|
-
console.log(
|
|
558
|
+
console.log('ā'.repeat(50));
|
|
303
559
|
}
|
|
304
560
|
});
|
|
305
561
|
}
|
|
@@ -311,9 +567,9 @@ program
|
|
|
311
567
|
|
|
312
568
|
// Chats command
|
|
313
569
|
program
|
|
314
|
-
.command(
|
|
315
|
-
.description(
|
|
316
|
-
.option(
|
|
570
|
+
.command('chats')
|
|
571
|
+
.description('List all chats in the workspace')
|
|
572
|
+
.option('-o, --output <format>', 'Output format (json|text)', 'text')
|
|
317
573
|
.action(async (options, command) => {
|
|
318
574
|
try {
|
|
319
575
|
const globalOptions = command.parent.opts();
|
|
@@ -323,23 +579,24 @@ program
|
|
|
323
579
|
const api = new ToothFairyAPI(
|
|
324
580
|
config.baseUrl,
|
|
325
581
|
config.aiUrl,
|
|
582
|
+
config.aiStreamUrl,
|
|
326
583
|
config.apiKey,
|
|
327
584
|
config.workspaceId,
|
|
328
585
|
globalOptions.verbose || options.verbose
|
|
329
586
|
);
|
|
330
587
|
|
|
331
|
-
const spinner = ora(
|
|
588
|
+
const spinner = ora('Fetching chats...').start();
|
|
332
589
|
const chatsData = await api.getAllChats();
|
|
333
590
|
spinner.stop();
|
|
334
591
|
|
|
335
|
-
if (options.output ===
|
|
592
|
+
if (options.output === 'json') {
|
|
336
593
|
console.log(JSON.stringify(chatsData, null, 2));
|
|
337
594
|
} else {
|
|
338
595
|
if (
|
|
339
596
|
!chatsData ||
|
|
340
597
|
(Array.isArray(chatsData) && chatsData.length === 0)
|
|
341
598
|
) {
|
|
342
|
-
console.log(chalk.yellow(
|
|
599
|
+
console.log(chalk.yellow('No chats found'));
|
|
343
600
|
return;
|
|
344
601
|
}
|
|
345
602
|
|
|
@@ -347,22 +604,22 @@ program
|
|
|
347
604
|
? chatsData
|
|
348
605
|
: chatsData.items || [];
|
|
349
606
|
|
|
350
|
-
const data = [[
|
|
607
|
+
const data = [['Chat ID', 'Name', 'Customer ID', 'Created']];
|
|
351
608
|
|
|
352
609
|
chatList.forEach((chat) => {
|
|
353
610
|
data.push([
|
|
354
|
-
chat.id ||
|
|
355
|
-
chat.name ||
|
|
356
|
-
chat.customerId ||
|
|
357
|
-
chat.createdAt ||
|
|
611
|
+
chat.id || 'N/A',
|
|
612
|
+
chat.name || 'N/A',
|
|
613
|
+
chat.customerId || 'N/A',
|
|
614
|
+
chat.createdAt || 'N/A',
|
|
358
615
|
]);
|
|
359
616
|
});
|
|
360
617
|
|
|
361
618
|
console.log(
|
|
362
619
|
table(data, {
|
|
363
620
|
header: {
|
|
364
|
-
alignment:
|
|
365
|
-
content:
|
|
621
|
+
alignment: 'center',
|
|
622
|
+
content: 'Workspace Chats',
|
|
366
623
|
},
|
|
367
624
|
})
|
|
368
625
|
);
|
|
@@ -375,10 +632,10 @@ program
|
|
|
375
632
|
|
|
376
633
|
// Chat command (get specific chat)
|
|
377
634
|
program
|
|
378
|
-
.command(
|
|
379
|
-
.description(
|
|
380
|
-
.argument(
|
|
381
|
-
.option(
|
|
635
|
+
.command('chat')
|
|
636
|
+
.description('Get details of a specific chat')
|
|
637
|
+
.argument('<chat-id>', 'Chat ID to retrieve')
|
|
638
|
+
.option('-o, --output <format>', 'Output format (json|text)', 'text')
|
|
382
639
|
.action(async (chatId, options, command) => {
|
|
383
640
|
try {
|
|
384
641
|
const globalOptions = command.parent.opts();
|
|
@@ -388,6 +645,7 @@ program
|
|
|
388
645
|
const api = new ToothFairyAPI(
|
|
389
646
|
config.baseUrl,
|
|
390
647
|
config.aiUrl,
|
|
648
|
+
config.aiStreamUrl,
|
|
391
649
|
config.apiKey,
|
|
392
650
|
config.workspaceId,
|
|
393
651
|
globalOptions.verbose || options.verbose
|
|
@@ -397,17 +655,17 @@ program
|
|
|
397
655
|
const chatData = await api.getChat(chatId);
|
|
398
656
|
spinner.stop();
|
|
399
657
|
|
|
400
|
-
if (options.output ===
|
|
658
|
+
if (options.output === 'json') {
|
|
401
659
|
console.log(JSON.stringify(chatData, null, 2));
|
|
402
660
|
} else {
|
|
403
|
-
console.log(chalk.green.bold(
|
|
661
|
+
console.log(chalk.green.bold('Chat Details'));
|
|
404
662
|
console.log();
|
|
405
663
|
|
|
406
|
-
const data = [[
|
|
664
|
+
const data = [['Field', 'Value']];
|
|
407
665
|
|
|
408
666
|
Object.entries(chatData).forEach(([key, value]) => {
|
|
409
667
|
let displayValue = value;
|
|
410
|
-
if (typeof value ===
|
|
668
|
+
if (typeof value === 'object' && value !== null) {
|
|
411
669
|
displayValue = JSON.stringify(value, null, 2);
|
|
412
670
|
}
|
|
413
671
|
data.push([key, String(displayValue)]);
|
|
@@ -423,31 +681,31 @@ program
|
|
|
423
681
|
|
|
424
682
|
// Config show command
|
|
425
683
|
program
|
|
426
|
-
.command(
|
|
427
|
-
.description(
|
|
684
|
+
.command('config-show')
|
|
685
|
+
.description('Show current configuration')
|
|
428
686
|
.action(async (options, command) => {
|
|
429
687
|
try {
|
|
430
688
|
const globalOptions = command.parent.opts();
|
|
431
689
|
const config = loadConfig(globalOptions.config);
|
|
432
690
|
|
|
433
691
|
const data = [
|
|
434
|
-
[
|
|
435
|
-
[
|
|
436
|
-
[
|
|
692
|
+
['Setting', 'Value'],
|
|
693
|
+
['Base URL', config.baseUrl],
|
|
694
|
+
['AI URL', config.aiUrl],
|
|
437
695
|
[
|
|
438
|
-
|
|
696
|
+
'API Key',
|
|
439
697
|
config.apiKey
|
|
440
|
-
? `${
|
|
441
|
-
:
|
|
698
|
+
? `${'*'.repeat(20)}...${config.apiKey.slice(-4)}`
|
|
699
|
+
: 'Not set',
|
|
442
700
|
],
|
|
443
|
-
[
|
|
701
|
+
['Workspace ID', config.workspaceId],
|
|
444
702
|
];
|
|
445
703
|
|
|
446
704
|
console.log(
|
|
447
705
|
table(data, {
|
|
448
706
|
header: {
|
|
449
|
-
alignment:
|
|
450
|
-
content:
|
|
707
|
+
alignment: 'center',
|
|
708
|
+
content: 'Current Configuration',
|
|
451
709
|
},
|
|
452
710
|
})
|
|
453
711
|
);
|
|
@@ -459,196 +717,196 @@ program
|
|
|
459
717
|
|
|
460
718
|
// Help guide command
|
|
461
719
|
program
|
|
462
|
-
.command(
|
|
463
|
-
.description(
|
|
720
|
+
.command('help-guide')
|
|
721
|
+
.description('Show detailed help and usage examples')
|
|
464
722
|
.action(() => {
|
|
465
|
-
console.log(chalk.cyan.bold(
|
|
466
|
-
console.log(
|
|
723
|
+
console.log(chalk.cyan.bold('š ToothFairy AI CLI - Quick Start Guide'));
|
|
724
|
+
console.log('='.repeat(50));
|
|
467
725
|
|
|
468
|
-
console.log(chalk.green.bold(
|
|
469
|
-
console.log(
|
|
726
|
+
console.log(chalk.green.bold('\nš Getting Started'));
|
|
727
|
+
console.log('1. First, configure your credentials:');
|
|
470
728
|
console.log(
|
|
471
729
|
chalk.dim(
|
|
472
|
-
|
|
730
|
+
' tf configure --api-key YOUR_KEY --workspace-id YOUR_WORKSPACE'
|
|
473
731
|
)
|
|
474
732
|
);
|
|
475
733
|
|
|
476
|
-
console.log(
|
|
734
|
+
console.log('\n2. Send a message to an agent:');
|
|
477
735
|
console.log(
|
|
478
736
|
chalk.dim(' tf send "Hello, I need help" --agent-id YOUR_AGENT_ID')
|
|
479
737
|
);
|
|
480
738
|
|
|
481
|
-
console.log(
|
|
739
|
+
console.log('\n3. Search the knowledge hub:');
|
|
482
740
|
console.log(chalk.dim(' tf search "AI configuration help"'));
|
|
483
741
|
|
|
484
|
-
console.log(
|
|
742
|
+
console.log('\n4. Explore your workspace:');
|
|
485
743
|
console.log(
|
|
486
|
-
chalk.dim(
|
|
744
|
+
chalk.dim(' tf chats # List all conversations')
|
|
487
745
|
);
|
|
488
746
|
console.log(
|
|
489
|
-
chalk.dim(
|
|
747
|
+
chalk.dim(' tf config-show # View current settings')
|
|
490
748
|
);
|
|
491
749
|
|
|
492
|
-
console.log(chalk.blue.bold(
|
|
750
|
+
console.log(chalk.blue.bold('\nš¬ Agent Communication Examples'));
|
|
493
751
|
|
|
494
752
|
const agentExamples = [
|
|
495
753
|
[
|
|
496
|
-
|
|
754
|
+
'Simple message',
|
|
497
755
|
'tf send "What are your hours?" --agent-id "info-agent"',
|
|
498
756
|
],
|
|
499
757
|
[
|
|
500
|
-
|
|
758
|
+
'With customer info',
|
|
501
759
|
'tf send "Schedule appointment" --agent-id "scheduler" --customer-info \'{"name": "John"}\'',
|
|
502
760
|
],
|
|
503
|
-
[
|
|
761
|
+
['Verbose output', 'tf send "Hello" --agent-id "agent-123" --verbose'],
|
|
504
762
|
[
|
|
505
|
-
|
|
763
|
+
'JSON for scripting',
|
|
506
764
|
'tf send "Help" --agent-id "agent-123" --output json',
|
|
507
765
|
],
|
|
508
766
|
];
|
|
509
767
|
|
|
510
|
-
const agentData = [[
|
|
768
|
+
const agentData = [['Use Case', 'Command'], ...agentExamples];
|
|
511
769
|
console.log(
|
|
512
770
|
table(agentData, {
|
|
513
771
|
header: {
|
|
514
|
-
alignment:
|
|
515
|
-
content:
|
|
772
|
+
alignment: 'center',
|
|
773
|
+
content: 'Agent Communication',
|
|
516
774
|
},
|
|
517
775
|
})
|
|
518
776
|
);
|
|
519
777
|
|
|
520
|
-
console.log(chalk.magenta.bold(
|
|
778
|
+
console.log(chalk.magenta.bold('\nš Knowledge Hub Search Examples'));
|
|
521
779
|
|
|
522
780
|
const searchExamples = [
|
|
523
|
-
[
|
|
524
|
-
[
|
|
525
|
-
[
|
|
781
|
+
['Basic search', 'tf search "AI agent configuration"'],
|
|
782
|
+
['Filter by status', 'tf search "machine learning" --status published'],
|
|
783
|
+
['Limit results', 'tf search "troubleshooting" --top-k 3'],
|
|
526
784
|
[
|
|
527
|
-
|
|
785
|
+
'Topic filtering',
|
|
528
786
|
'tf search "automation" --topics "topic_123,topic_456"',
|
|
529
787
|
],
|
|
530
|
-
[
|
|
531
|
-
[
|
|
532
|
-
[
|
|
788
|
+
['Specific document', 'tf search "settings" --document-id "doc_550..."'],
|
|
789
|
+
['Verbose details', 'tf search "deployment" --verbose'],
|
|
790
|
+
['JSON output', 'tf search "API docs" --output json'],
|
|
533
791
|
];
|
|
534
792
|
|
|
535
|
-
const searchData = [[
|
|
793
|
+
const searchData = [['Use Case', 'Command'], ...searchExamples];
|
|
536
794
|
console.log(
|
|
537
795
|
table(searchData, {
|
|
538
796
|
header: {
|
|
539
|
-
alignment:
|
|
540
|
-
content:
|
|
797
|
+
alignment: 'center',
|
|
798
|
+
content: 'Knowledge Hub Search',
|
|
541
799
|
},
|
|
542
800
|
})
|
|
543
801
|
);
|
|
544
802
|
|
|
545
|
-
console.log(chalk.green.bold(
|
|
803
|
+
console.log(chalk.green.bold('\nš Workspace Management Examples'));
|
|
546
804
|
|
|
547
805
|
const mgmtExamples = [
|
|
548
|
-
[
|
|
549
|
-
[
|
|
550
|
-
[
|
|
551
|
-
[
|
|
806
|
+
['List all chats', 'tf chats'],
|
|
807
|
+
['View chat details', 'tf chat CHAT_ID'],
|
|
808
|
+
['Show config', 'tf config-show'],
|
|
809
|
+
['Detailed help', 'tf help-guide'],
|
|
552
810
|
];
|
|
553
811
|
|
|
554
|
-
const mgmtData = [[
|
|
812
|
+
const mgmtData = [['Use Case', 'Command'], ...mgmtExamples];
|
|
555
813
|
console.log(
|
|
556
814
|
table(mgmtData, {
|
|
557
815
|
header: {
|
|
558
|
-
alignment:
|
|
559
|
-
content:
|
|
816
|
+
alignment: 'center',
|
|
817
|
+
content: 'Workspace Management',
|
|
560
818
|
},
|
|
561
819
|
})
|
|
562
820
|
);
|
|
563
821
|
|
|
564
|
-
console.log(chalk.yellow.bold(
|
|
822
|
+
console.log(chalk.yellow.bold('\nš§ Configuration Options'));
|
|
565
823
|
const configOptions = [
|
|
566
|
-
[
|
|
824
|
+
['Method', 'Description', 'Example'],
|
|
567
825
|
[
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
826
|
+
'Environment',
|
|
827
|
+
'Set environment variables',
|
|
828
|
+
'export TF_API_KEY=your_key',
|
|
571
829
|
],
|
|
572
830
|
[
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
831
|
+
'Config file',
|
|
832
|
+
'Use ~/.toothfairy/config.yml',
|
|
833
|
+
'api_key: your_key\\nworkspace_id: your_workspace',
|
|
576
834
|
],
|
|
577
835
|
[
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
836
|
+
'CLI arguments',
|
|
837
|
+
'Pass config file path',
|
|
838
|
+
'tf --config /path/to/config.yml send ...',
|
|
581
839
|
],
|
|
582
840
|
];
|
|
583
841
|
|
|
584
842
|
console.log(table(configOptions));
|
|
585
843
|
|
|
586
|
-
console.log(chalk.red.bold(
|
|
844
|
+
console.log(chalk.red.bold('\nā ļø Common Issues & Solutions'));
|
|
587
845
|
const issues = [
|
|
588
|
-
[
|
|
846
|
+
['Issue', 'Solution'],
|
|
589
847
|
[
|
|
590
|
-
|
|
591
|
-
|
|
848
|
+
'Configuration incomplete',
|
|
849
|
+
'Run: tf configure --api-key YOUR_KEY --workspace-id YOUR_WORKSPACE',
|
|
592
850
|
],
|
|
593
851
|
[
|
|
594
|
-
|
|
595
|
-
|
|
852
|
+
'No text response found',
|
|
853
|
+
'Use --verbose flag to see full response details',
|
|
596
854
|
],
|
|
597
|
-
[
|
|
855
|
+
['Agent not responding', 'Check agent-id is correct and agent is active'],
|
|
598
856
|
[
|
|
599
|
-
|
|
600
|
-
|
|
857
|
+
'Network errors',
|
|
858
|
+
'Verify API endpoints are accessible and credentials are valid',
|
|
601
859
|
],
|
|
602
860
|
];
|
|
603
861
|
|
|
604
862
|
console.log(table(issues));
|
|
605
863
|
|
|
606
|
-
console.log(chalk.cyan.bold(
|
|
607
|
-
console.log(
|
|
864
|
+
console.log(chalk.cyan.bold('\nš Search Filtering Guide'));
|
|
865
|
+
console.log('Knowledge Hub search supports powerful filtering options:');
|
|
608
866
|
console.log(
|
|
609
|
-
|
|
610
|
-
chalk.cyan(
|
|
611
|
-
|
|
867
|
+
'⢠' +
|
|
868
|
+
chalk.cyan('--status') +
|
|
869
|
+
': Filter documents by \'published\' or \'suspended\' status'
|
|
612
870
|
);
|
|
613
871
|
console.log(
|
|
614
|
-
|
|
615
|
-
chalk.cyan(
|
|
616
|
-
|
|
872
|
+
'⢠' +
|
|
873
|
+
chalk.cyan('--topics') +
|
|
874
|
+
': Use topic IDs from ToothFairyAI (comma-separated)'
|
|
617
875
|
);
|
|
618
876
|
console.log(
|
|
619
|
-
|
|
877
|
+
'⢠' + chalk.cyan('--document-id') + ': Search within a specific document'
|
|
620
878
|
);
|
|
621
879
|
console.log(
|
|
622
|
-
|
|
880
|
+
'⢠' + chalk.cyan('--top-k') + ': Control number of results (1-50)'
|
|
623
881
|
);
|
|
624
882
|
console.log(
|
|
625
|
-
|
|
883
|
+
'⢠' + chalk.cyan('--verbose') + ': Show relevance scores and metadata'
|
|
626
884
|
);
|
|
627
885
|
|
|
628
|
-
console.log(chalk.magenta.bold(
|
|
886
|
+
console.log(chalk.magenta.bold('\nš More Help'));
|
|
629
887
|
console.log(
|
|
630
|
-
|
|
888
|
+
'⢠Use ' + chalk.cyan('tf COMMAND --help') + ' for command-specific help'
|
|
631
889
|
);
|
|
632
890
|
console.log(
|
|
633
|
-
|
|
634
|
-
chalk.cyan(
|
|
635
|
-
|
|
891
|
+
'⢠Use ' +
|
|
892
|
+
chalk.cyan('--verbose') +
|
|
893
|
+
' flag to see detailed request/response information'
|
|
636
894
|
);
|
|
637
895
|
console.log(
|
|
638
|
-
|
|
896
|
+
'⢠Use ' + chalk.cyan('--output json') + ' for machine-readable output'
|
|
639
897
|
);
|
|
640
898
|
console.log(
|
|
641
|
-
|
|
899
|
+
'⢠Configuration is loaded from: environment variables ā ~/.toothfairy/config.yml ā CLI args'
|
|
642
900
|
);
|
|
643
901
|
|
|
644
|
-
console.log(chalk.green.bold(
|
|
902
|
+
console.log(chalk.green.bold('\n⨠Pro Tips'));
|
|
645
903
|
const tips = [
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
904
|
+
'š¾ Save time: Configure once with \'tf configure\', then just use \'tf send\' and \'tf search\'',
|
|
905
|
+
'š Debug issues: Use \'--verbose\' to see full API responses and troubleshoot',
|
|
906
|
+
'š Scripting: Use \'--output json\' and tools like \'jq\' to parse responses',
|
|
907
|
+
'ā” Quick tests: Only --agent-id is required for send, only query for search',
|
|
908
|
+
'šÆ Better search: Use --status, --topics, and --document-id for targeted results',
|
|
909
|
+
'š§ Multiple environments: Use different config files with \'--config\' flag',
|
|
652
910
|
];
|
|
653
911
|
|
|
654
912
|
tips.forEach((tip) => {
|
|
@@ -657,7 +915,7 @@ program
|
|
|
657
915
|
|
|
658
916
|
console.log(
|
|
659
917
|
chalk.dim(
|
|
660
|
-
|
|
918
|
+
'\nToothFairy CLI v1.0.0 - For more help, visit the documentation'
|
|
661
919
|
)
|
|
662
920
|
);
|
|
663
921
|
});
|