@mintlify/cli 4.0.1083 → 4.0.1085

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,524 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { jsx as _jsx } from "react/jsx-runtime";
11
+ import { addLog, ErrorLog, SpinnerLog, removeLastLog } from '@mintlify/previewing';
12
+ import chalk from 'chalk';
13
+ import { Text } from 'ink';
14
+ import { getConfigValue } from '../config.js';
15
+ import { terminate } from '../helpers.js';
16
+ import { getBucketThreads, getBuckets, getConversations, getFeedback, getFeedbackByPage, getKpi, getSearches, } from './client.js';
17
+ import { num, truncate } from './format.js';
18
+ import { formatBarChart, formatOutput, resolveFormat } from './output.js';
19
+ const withSubdomain = (yargs) => yargs.option('subdomain', {
20
+ type: 'string',
21
+ default: getConfigValue('subdomain'),
22
+ description: 'Documentation subdomain (default: mint config set subdomain)',
23
+ });
24
+ function defaultFrom() {
25
+ const configured = getConfigValue('dateFrom');
26
+ if (configured)
27
+ return configured;
28
+ const d = new Date();
29
+ d.setDate(d.getDate() - 7);
30
+ return d.toISOString().slice(0, 10);
31
+ }
32
+ function defaultTo() {
33
+ var _a;
34
+ return (_a = getConfigValue('dateTo')) !== null && _a !== void 0 ? _a : new Date().toISOString().slice(0, 10);
35
+ }
36
+ const withDates = (yargs) => yargs
37
+ .option('from', {
38
+ type: 'string',
39
+ default: defaultFrom(),
40
+ description: 'Start date (YYYY-MM-DD)',
41
+ })
42
+ .option('to', {
43
+ type: 'string',
44
+ default: defaultTo(),
45
+ description: 'End date (YYYY-MM-DD)',
46
+ });
47
+ const withFormat = (yargs) => yargs
48
+ .option('format', {
49
+ type: 'string',
50
+ choices: ['table', 'plain', 'json', 'graph'],
51
+ description: 'Output format (table=pretty, plain=pipeable, json=raw)',
52
+ })
53
+ .option('agent', {
54
+ type: 'boolean',
55
+ description: 'Agent-friendly output (equivalent to --format json)',
56
+ });
57
+ const withAll = (yargs) => withFormat(withDates(withSubdomain(yargs)));
58
+ function output(format, text) {
59
+ if (format === 'table') {
60
+ addLog(_jsx(Text, { children: text }));
61
+ }
62
+ else {
63
+ process.stdout.write(text + '\n');
64
+ }
65
+ }
66
+ export const analyticsBuilder = (yargs) => yargs
67
+ .command('stats', 'display KPI numbers (views, visitors, searches)', (yargs) => withAll(yargs)
68
+ .option('agents', { type: 'boolean', description: 'Show only agent traffic' })
69
+ .option('humans', { type: 'boolean', description: 'Show only human traffic' })
70
+ .option('page', { type: 'string', description: 'Filter to a specific page path' }), (argv) => __awaiter(void 0, void 0, void 0, function* () {
71
+ var _a, _b;
72
+ const format = resolveFormat(argv);
73
+ try {
74
+ if (format === 'table')
75
+ addLog(_jsx(SpinnerLog, { message: "Fetching analytics..." }));
76
+ const kpi = yield getKpi({ dateFrom: argv.from, dateTo: argv.to, page: argv.page }, argv.subdomain);
77
+ if (format === 'table')
78
+ removeLastLog();
79
+ if (format === 'json') {
80
+ output(format, JSON.stringify(kpi, null, 2));
81
+ yield terminate(0);
82
+ return;
83
+ }
84
+ if (format === 'plain') {
85
+ const lines = [
86
+ ['METRIC', 'HUMAN', 'AGENT', 'TOTAL'].join('\t'),
87
+ ['Views', kpi.humanViews, kpi.agentViews, kpi.humanViews + kpi.agentViews].join('\t'),
88
+ [
89
+ 'Visitors',
90
+ kpi.humanVisitors,
91
+ kpi.agentVisitors,
92
+ kpi.humanVisitors + kpi.agentVisitors,
93
+ ].join('\t'),
94
+ ['Searches', kpi.humanSearches, '', kpi.humanSearches].join('\t'),
95
+ ['Feedback', kpi.humanFeedback, '', kpi.humanFeedback].join('\t'),
96
+ [
97
+ 'Assistant',
98
+ kpi.humanAssistant,
99
+ kpi.agentMcpSearches,
100
+ kpi.humanAssistant + kpi.agentMcpSearches,
101
+ ].join('\t'),
102
+ ];
103
+ output(format, lines.join('\n'));
104
+ yield terminate(0);
105
+ return;
106
+ }
107
+ if (format === 'graph') {
108
+ const label = (_a = argv.subdomain) !== null && _a !== void 0 ? _a : 'default';
109
+ const lines = [];
110
+ lines.push(chalk.bold(`\nAnalytics \u2014 ${label} (${argv.from} to ${argv.to})\n`));
111
+ lines.push(chalk.bold(' Human vs Agent\n'));
112
+ lines.push(formatBarChart([
113
+ { label: 'Human Views', value: kpi.humanViews, color: 'cyan' },
114
+ { label: 'Agent Views', value: kpi.agentViews, color: 'magenta' },
115
+ { label: 'Human Visitors', value: kpi.humanVisitors, color: 'cyan' },
116
+ { label: 'Agent Visitors', value: kpi.agentVisitors, color: 'magenta' },
117
+ ]));
118
+ lines.push(chalk.bold('\n\n Engagement\n'));
119
+ lines.push(formatBarChart([
120
+ { label: 'Searches', value: kpi.humanSearches, color: 'yellow' },
121
+ { label: 'Feedback', value: kpi.humanFeedback, color: 'green' },
122
+ { label: 'Assistant (web)', value: kpi.humanAssistant, color: 'blue' },
123
+ { label: 'Assistant (API)', value: kpi.agentMcpSearches, color: 'magenta' },
124
+ ]));
125
+ output('table', lines.join('\n'));
126
+ yield terminate(0);
127
+ return;
128
+ }
129
+ const agentOnly = argv.agents && !argv.humans;
130
+ const humanOnly = argv.humans && !argv.agents;
131
+ const showHuman = !agentOnly;
132
+ const showAgent = !humanOnly;
133
+ const showTotal = showHuman && showAgent;
134
+ const lines = [];
135
+ const label = (_b = argv.subdomain) !== null && _b !== void 0 ? _b : 'default';
136
+ lines.push(chalk.bold(`\nAnalytics \u2014 ${label} (${argv.from} to ${argv.to})\n`));
137
+ if (argv.page) {
138
+ lines.push(` Page: ${argv.page}\n`);
139
+ }
140
+ lines.push(chalk.bold(' Views'));
141
+ if (showHuman)
142
+ lines.push(` Human: ${chalk.cyan(num(kpi.humanViews).padStart(8))}`);
143
+ if (showAgent)
144
+ lines.push(` Agent: ${chalk.magenta(num(kpi.agentViews).padStart(8))}`);
145
+ if (showTotal)
146
+ lines.push(` Total: ${num(kpi.humanViews + kpi.agentViews).padStart(8)}`);
147
+ lines.push(chalk.bold('\n Visitors'));
148
+ if (showHuman)
149
+ lines.push(` Human: ${chalk.cyan(num(kpi.humanVisitors).padStart(8))}`);
150
+ if (showAgent)
151
+ lines.push(` Agent: ${chalk.magenta(num(kpi.agentVisitors).padStart(8))}`);
152
+ if (showTotal)
153
+ lines.push(` Total: ${num(kpi.humanVisitors + kpi.agentVisitors).padStart(8)}`);
154
+ lines.push(`\n Searches: ${chalk.bold(num(kpi.humanSearches))}`);
155
+ lines.push(` Feedback: ${chalk.bold(num(kpi.humanFeedback))}`);
156
+ lines.push(` Assistant: ${chalk.bold(num(kpi.humanAssistant))} web, ${chalk.bold(num(kpi.agentMcpSearches))} API`);
157
+ output(format, lines.join('\n'));
158
+ yield terminate(0);
159
+ }
160
+ catch (err) {
161
+ if (format === 'table')
162
+ removeLastLog();
163
+ addLog(_jsx(ErrorLog, { message: err instanceof Error ? err.message : 'unknown error' }));
164
+ yield terminate(1);
165
+ }
166
+ }))
167
+ .command('search', 'display search analytics', (yargs) => withAll(yargs)
168
+ .option('query', { type: 'string', description: 'Filter by search query substring' })
169
+ .option('page', { type: 'string', description: 'Filter by top clicked page' }), (argv) => __awaiter(void 0, void 0, void 0, function* () {
170
+ var _a, _b;
171
+ const format = resolveFormat(argv);
172
+ try {
173
+ if (format === 'table')
174
+ addLog(_jsx(SpinnerLog, { message: "Fetching search analytics..." }));
175
+ const data = yield getSearches({ dateFrom: argv.from, dateTo: argv.to }, argv.subdomain);
176
+ if (format === 'table')
177
+ removeLastLog();
178
+ let rows = data.searches;
179
+ if (argv.query) {
180
+ const q = argv.query.toLowerCase();
181
+ rows = rows.filter((r) => r.searchQuery.toLowerCase().includes(q));
182
+ }
183
+ if (argv.page) {
184
+ rows = rows.filter((r) => { var _a; return (_a = r.topClickedPage) === null || _a === void 0 ? void 0 : _a.includes(argv.page); });
185
+ }
186
+ const headers = ['Query', 'Hits', 'CTR', 'Top Clicked Page', 'Last Searched'];
187
+ const tableRows = rows.map((r) => [
188
+ truncate(r.searchQuery, 30),
189
+ num(r.hits),
190
+ r.ctr.toFixed(1) + '%',
191
+ truncate(r.topClickedPage || '\u2014', 30),
192
+ r.lastSearchedAt.slice(0, 10),
193
+ ]);
194
+ if (format === 'json') {
195
+ output(format, JSON.stringify(data, null, 2));
196
+ }
197
+ else if (format === 'graph') {
198
+ const label = (_a = argv.subdomain) !== null && _a !== void 0 ? _a : 'default';
199
+ const lines = [];
200
+ lines.push(chalk.bold(`\nSearch Queries \u2014 ${label} (${argv.from} to ${argv.to})\n`));
201
+ lines.push(formatBarChart(rows.slice(0, 20).map((r) => ({
202
+ label: truncate(r.searchQuery, 25),
203
+ value: r.hits,
204
+ color: 'yellow',
205
+ }))));
206
+ output('table', lines.join('\n'));
207
+ }
208
+ else if (format === 'plain') {
209
+ output(format, formatOutput(format, headers, tableRows, data));
210
+ }
211
+ else {
212
+ const label = (_b = argv.subdomain) !== null && _b !== void 0 ? _b : 'default';
213
+ const lines = [];
214
+ lines.push(chalk.bold(`\nSearch Analytics \u2014 ${label} (${argv.from} to ${argv.to})`));
215
+ lines.push(`Total Searches: ${chalk.bold(num(data.totalSearches))}\n`);
216
+ lines.push(formatOutput(format, headers, tableRows, data));
217
+ output(format, lines.join('\n'));
218
+ }
219
+ yield terminate(0);
220
+ }
221
+ catch (err) {
222
+ if (format === 'table')
223
+ removeLastLog();
224
+ addLog(_jsx(ErrorLog, { message: err instanceof Error ? err.message : 'unknown error' }));
225
+ yield terminate(1);
226
+ }
227
+ }))
228
+ .command('feedback', 'display feedback analytics', (yargs) => withAll(yargs)
229
+ .option('type', {
230
+ type: 'string',
231
+ choices: ['code', 'page'],
232
+ description: 'Feedback type: code snippets or page-level aggregation',
233
+ })
234
+ .option('page', { type: 'string', description: 'Filter to a specific page path' }), (argv) => __awaiter(void 0, void 0, void 0, function* () {
235
+ var _a, _b, _c;
236
+ const format = resolveFormat(argv);
237
+ try {
238
+ if (format === 'table')
239
+ addLog(_jsx(SpinnerLog, { message: "Fetching feedback..." }));
240
+ if (argv.type === 'page') {
241
+ const data = yield getFeedbackByPage({ dateFrom: argv.from, dateTo: argv.to }, argv.subdomain);
242
+ if (format === 'table')
243
+ removeLastLog();
244
+ let rows = data.feedback;
245
+ if (argv.page)
246
+ rows = rows.filter((r) => r.path.includes(argv.page));
247
+ const headers = ['Path', 'Thumbs Up', 'Thumbs Down', 'Code', 'Total'];
248
+ const tableRows = rows.map((r) => [
249
+ truncate(r.path, 40),
250
+ num(r.thumbsUp),
251
+ num(r.thumbsDown),
252
+ num(r.code),
253
+ num(r.total),
254
+ ]);
255
+ if (format === 'json') {
256
+ output(format, JSON.stringify(data, null, 2));
257
+ }
258
+ else if (format === 'graph') {
259
+ const label = (_a = argv.subdomain) !== null && _a !== void 0 ? _a : 'default';
260
+ const lines = [];
261
+ lines.push(chalk.bold(`\nFeedback by Page \u2014 ${label} (${argv.from} to ${argv.to})\n`));
262
+ lines.push(formatBarChart(rows.slice(0, 20).map((r) => ({
263
+ label: truncate(r.path, 25),
264
+ value: r.total,
265
+ color: 'green',
266
+ }))));
267
+ output('table', lines.join('\n'));
268
+ }
269
+ else {
270
+ const label = (_b = argv.subdomain) !== null && _b !== void 0 ? _b : 'default';
271
+ const lines = [];
272
+ if (format === 'table')
273
+ lines.push(chalk.bold(`\nFeedback \u2014 ${label} (${argv.from} to ${argv.to})\n`));
274
+ lines.push(formatOutput(format, headers, tableRows, data));
275
+ if (format === 'table' && data.hasMore)
276
+ lines.push(chalk.dim('\n (more results available)'));
277
+ output(format, lines.join('\n'));
278
+ }
279
+ }
280
+ else {
281
+ const source = argv.type === 'code' ? 'code' : undefined;
282
+ const data = yield getFeedback({ dateFrom: argv.from, dateTo: argv.to, source }, argv.subdomain);
283
+ if (format === 'table')
284
+ removeLastLog();
285
+ let rows = data.feedback;
286
+ if (argv.page)
287
+ rows = rows.filter((r) => r.path.includes(argv.page));
288
+ const headers = ['ID', 'Path', 'Status', 'Source', 'Comment', 'Created'];
289
+ const tableRows = rows.map((r) => {
290
+ var _a, _b, _c;
291
+ return [
292
+ r.id.slice(0, 8),
293
+ truncate(r.path, 30),
294
+ r.status,
295
+ r.source,
296
+ truncate((_a = r.comment) !== null && _a !== void 0 ? _a : '\u2014', 30),
297
+ (_c = (_b = r.createdAt) === null || _b === void 0 ? void 0 : _b.slice(0, 10)) !== null && _c !== void 0 ? _c : '\u2014',
298
+ ];
299
+ });
300
+ if (format === 'json') {
301
+ output(format, JSON.stringify(data, null, 2));
302
+ }
303
+ else {
304
+ const label = (_c = argv.subdomain) !== null && _c !== void 0 ? _c : 'default';
305
+ const lines = [];
306
+ if (format === 'table')
307
+ lines.push(chalk.bold(`\nFeedback \u2014 ${label} (${argv.from} to ${argv.to})\n`));
308
+ lines.push(formatOutput(format, headers, tableRows, data));
309
+ if (format === 'table' && data.hasMore)
310
+ lines.push(chalk.dim('\n (more results available)'));
311
+ output(format, lines.join('\n'));
312
+ }
313
+ }
314
+ yield terminate(0);
315
+ }
316
+ catch (err) {
317
+ if (format === 'table')
318
+ removeLastLog();
319
+ addLog(_jsx(ErrorLog, { message: err instanceof Error ? err.message : 'unknown error' }));
320
+ yield terminate(1);
321
+ }
322
+ }))
323
+ .command('conversation', 'view assistant conversation analytics', (yargs) => yargs
324
+ .command('list', 'list assistant conversations', (yargs) => withAll(yargs).option('page', {
325
+ type: 'string',
326
+ description: 'Filter conversations mentioning this page in sources',
327
+ }), (argv) => __awaiter(void 0, void 0, void 0, function* () {
328
+ var _a;
329
+ const format = resolveFormat(argv);
330
+ try {
331
+ if (format === 'table')
332
+ addLog(_jsx(SpinnerLog, { message: "Fetching conversations..." }));
333
+ const data = yield getConversations({ dateFrom: argv.from, dateTo: argv.to }, argv.subdomain);
334
+ if (format === 'table')
335
+ removeLastLog();
336
+ let conversations = data.conversations;
337
+ if (argv.page) {
338
+ conversations = conversations.filter((c) => c.sources.some((s) => s.url.includes(argv.page)));
339
+ }
340
+ const headers = ['ID', 'Timestamp', 'Query', 'Category'];
341
+ const tableRows = conversations.map((c) => [
342
+ c.id,
343
+ c.timestamp.slice(0, 19).replace('T', ' '),
344
+ truncate(c.query, 40),
345
+ c.queryCategory || '\u2014',
346
+ ]);
347
+ if (format === 'json') {
348
+ output(format, JSON.stringify(data, null, 2));
349
+ }
350
+ else {
351
+ const label = (_a = argv.subdomain) !== null && _a !== void 0 ? _a : 'default';
352
+ const lines = [];
353
+ if (format === 'table')
354
+ lines.push(chalk.bold(`\nConversations \u2014 ${label} (${argv.from} to ${argv.to})\n`));
355
+ lines.push(formatOutput(format, headers, tableRows, data));
356
+ if (format === 'table' && data.hasMore)
357
+ lines.push(chalk.dim('\n (more results available)'));
358
+ output(format, lines.join('\n'));
359
+ }
360
+ yield terminate(0);
361
+ }
362
+ catch (err) {
363
+ if (format === 'table')
364
+ removeLastLog();
365
+ addLog(_jsx(ErrorLog, { message: err instanceof Error ? err.message : 'unknown error' }));
366
+ yield terminate(1);
367
+ }
368
+ }))
369
+ .command('view <id>', 'view a single conversation', (yargs) => withFormat(withSubdomain(yargs)).positional('id', {
370
+ type: 'string',
371
+ demandOption: true,
372
+ description: 'Conversation ID',
373
+ }), (argv) => __awaiter(void 0, void 0, void 0, function* () {
374
+ const format = resolveFormat(argv);
375
+ try {
376
+ if (format === 'table')
377
+ addLog(_jsx(SpinnerLog, { message: "Fetching conversation..." }));
378
+ const today = new Date().toISOString().slice(0, 10);
379
+ let conversation;
380
+ let cursor;
381
+ for (let i = 0; i < 10 && !conversation; i++) {
382
+ const data = yield getConversations({
383
+ dateFrom: '2020-01-01',
384
+ dateTo: today,
385
+ limit: 100,
386
+ cursor,
387
+ }, argv.subdomain);
388
+ conversation = data.conversations.find((c) => c.id === argv.id);
389
+ if (!data.nextCursor)
390
+ break;
391
+ cursor = data.nextCursor;
392
+ }
393
+ if (format === 'table')
394
+ removeLastLog();
395
+ if (!conversation) {
396
+ addLog(_jsx(ErrorLog, { message: `Conversation ${argv.id} not found` }));
397
+ yield terminate(1);
398
+ return;
399
+ }
400
+ if (format === 'json' || format === 'plain') {
401
+ output(format, JSON.stringify(conversation, null, 2));
402
+ }
403
+ else {
404
+ const lines = [];
405
+ lines.push(chalk.bold(`\nConversation ${conversation.id}\n`));
406
+ lines.push(` Timestamp: ${conversation.timestamp}`);
407
+ lines.push(` Category: ${conversation.queryCategory || '\u2014'}`);
408
+ lines.push(chalk.bold('\n Query:'));
409
+ lines.push(` ${conversation.query}`);
410
+ lines.push(chalk.bold('\n Response:'));
411
+ for (const line of conversation.response.split('\n')) {
412
+ lines.push(` ${line}`);
413
+ }
414
+ if (conversation.sources.length > 0) {
415
+ lines.push(chalk.bold('\n Sources:'));
416
+ for (const src of conversation.sources) {
417
+ lines.push(` ${src.title} \u2014 ${chalk.dim(src.url)}`);
418
+ }
419
+ }
420
+ output(format, lines.join('\n'));
421
+ }
422
+ yield terminate(0);
423
+ }
424
+ catch (err) {
425
+ if (format === 'table')
426
+ removeLastLog();
427
+ addLog(_jsx(ErrorLog, { message: err instanceof Error ? err.message : 'unknown error' }));
428
+ yield terminate(1);
429
+ }
430
+ }))
431
+ .command('buckets', 'view conversation category buckets', (yargs) => yargs
432
+ .command('list', 'list conversation buckets', (yargs) => withAll(yargs), (argv) => __awaiter(void 0, void 0, void 0, function* () {
433
+ var _a, _b;
434
+ const format = resolveFormat(argv);
435
+ try {
436
+ if (format === 'table')
437
+ addLog(_jsx(SpinnerLog, { message: "Fetching conversation buckets..." }));
438
+ const data = yield getBuckets({ dateFrom: argv.from, dateTo: argv.to }, argv.subdomain);
439
+ if (format === 'table')
440
+ removeLastLog();
441
+ const headers = ['ID', 'Label', 'Count', 'Last Asked'];
442
+ const tableRows = data.data.map((b) => [
443
+ b.id.slice(0, 12),
444
+ truncate(b.questionSummary, 50),
445
+ num(b.size),
446
+ b.lastAsked ? b.lastAsked.slice(0, 10) : '\u2014',
447
+ ]);
448
+ if (format === 'json') {
449
+ output(format, JSON.stringify(data, null, 2));
450
+ }
451
+ else if (format === 'graph') {
452
+ const label = (_a = argv.subdomain) !== null && _a !== void 0 ? _a : 'default';
453
+ const lines = [];
454
+ lines.push(chalk.bold(`\nConversation Buckets \u2014 ${label} (${argv.from} to ${argv.to})\n`));
455
+ lines.push(formatBarChart(data.data.slice(0, 20).map((b) => ({
456
+ label: truncate(b.questionSummary, 30),
457
+ value: b.size,
458
+ color: 'blue',
459
+ }))));
460
+ output('table', lines.join('\n'));
461
+ }
462
+ else {
463
+ const label = (_b = argv.subdomain) !== null && _b !== void 0 ? _b : 'default';
464
+ const lines = [];
465
+ if (format === 'table')
466
+ lines.push(chalk.bold(`\nConversation Buckets \u2014 ${label} (${argv.from} to ${argv.to})\n`));
467
+ lines.push(formatOutput(format, headers, tableRows, data));
468
+ if (format === 'table')
469
+ lines.push(chalk.dim(`\n Total: ${data.pagination.total}`));
470
+ output(format, lines.join('\n'));
471
+ }
472
+ yield terminate(0);
473
+ }
474
+ catch (err) {
475
+ if (format === 'table')
476
+ removeLastLog();
477
+ addLog(_jsx(ErrorLog, { message: err instanceof Error ? err.message : 'unknown error' }));
478
+ yield terminate(1);
479
+ }
480
+ }))
481
+ .command('view <id>', 'view conversations in a bucket', (yargs) => withAll(yargs).positional('id', {
482
+ type: 'string',
483
+ demandOption: true,
484
+ description: 'Bucket ID',
485
+ }), (argv) => __awaiter(void 0, void 0, void 0, function* () {
486
+ const format = resolveFormat(argv);
487
+ try {
488
+ if (format === 'table')
489
+ addLog(_jsx(SpinnerLog, { message: "Fetching bucket conversations..." }));
490
+ const data = yield getBucketThreads(argv.id, { dateFrom: argv.from, dateTo: argv.to }, argv.subdomain);
491
+ if (format === 'table')
492
+ removeLastLog();
493
+ if (format === 'json') {
494
+ output(format, JSON.stringify(data, null, 2));
495
+ }
496
+ else {
497
+ const headers = ['Thread ID', 'Query', 'Length', 'Feedback', 'Created'];
498
+ const tableRows = data.data.map((t) => [
499
+ t.id.slice(0, 12),
500
+ truncate(t.firstUserMessage || '\u2014', 40),
501
+ num(t.length),
502
+ `+${t.feedback.up} -${t.feedback.down}`,
503
+ t.createdAt.slice(0, 10),
504
+ ]);
505
+ const lines = [];
506
+ if (format === 'table')
507
+ lines.push(chalk.bold(`\nBucket ${argv.id}\n`));
508
+ lines.push(formatOutput(format, headers, tableRows, data));
509
+ if (format === 'table' && data.pagination.hasMore)
510
+ lines.push(chalk.dim('\n (more results available)'));
511
+ output(format, lines.join('\n'));
512
+ }
513
+ yield terminate(0);
514
+ }
515
+ catch (err) {
516
+ if (format === 'table')
517
+ removeLastLog();
518
+ addLog(_jsx(ErrorLog, { message: err instanceof Error ? err.message : 'unknown error' }));
519
+ yield terminate(1);
520
+ }
521
+ }))
522
+ .demandCommand(1, 'specify a subcommand: list or view'))
523
+ .demandCommand(1, 'specify a subcommand: list, view, or buckets'))
524
+ .demandCommand(1, 'specify a subcommand: stats, search, feedback, or conversation');
@@ -0,0 +1,74 @@
1
+ import chalk from 'chalk';
2
+ export function resolveFormat(argv) {
3
+ if (argv.agent || process.env.CLAUDECODE === '1')
4
+ return 'json';
5
+ if (argv.format === 'table' ||
6
+ argv.format === 'plain' ||
7
+ argv.format === 'json' ||
8
+ argv.format === 'graph')
9
+ return argv.format;
10
+ return 'plain';
11
+ }
12
+ export function formatPlainTable(headers, rows) {
13
+ if (rows.length === 0)
14
+ return '';
15
+ const colWidths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => { var _a; return ((_a = r[i]) !== null && _a !== void 0 ? _a : '').length; })));
16
+ const headerLine = headers.map((h, i) => h.toUpperCase().padEnd(colWidths[i])).join('\t');
17
+ const bodyLines = rows.map((row) => row.map((cell, i) => cell.padEnd(colWidths[i])).join('\t'));
18
+ return [headerLine, ...bodyLines].join('\n');
19
+ }
20
+ export function formatPrettyTable(headers, rows) {
21
+ if (rows.length === 0)
22
+ return chalk.dim(' No data found.');
23
+ const colWidths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => { var _a; return ((_a = r[i]) !== null && _a !== void 0 ? _a : '').length; })));
24
+ const headerLine = headers.map((h, i) => chalk.bold(h.padEnd(colWidths[i]))).join(' ');
25
+ const separator = chalk.dim(colWidths.map((w) => '\u2500'.repeat(w)).join('\u2500\u2500'));
26
+ const bodyLines = rows.map((row) => row.map((cell, i) => cell.padEnd(colWidths[i])).join(' '));
27
+ return [headerLine, separator, ...bodyLines].join('\n');
28
+ }
29
+ function gradientBar(len, rgb) {
30
+ let result = '';
31
+ for (let i = 0; i < len; i++) {
32
+ const t = len === 1 ? 1 : i / (len - 1);
33
+ const dim = 0.3 + t * 0.7;
34
+ const r = Math.round(rgb[0] * dim);
35
+ const g = Math.round(rgb[1] * dim);
36
+ const b = Math.round(rgb[2] * dim);
37
+ result += chalk.rgb(r, g, b)('\u2588');
38
+ }
39
+ return result;
40
+ }
41
+ const COLOR_MAP = {
42
+ cyan: [0, 255, 255],
43
+ magenta: [255, 0, 255],
44
+ yellow: [255, 255, 0],
45
+ green: [0, 255, 100],
46
+ blue: [80, 140, 255],
47
+ red: [255, 80, 80],
48
+ };
49
+ export function formatBarChart(items, opts = {}) {
50
+ var _a;
51
+ if (items.length === 0)
52
+ return chalk.dim(' No data found.');
53
+ const maxWidth = (_a = opts.maxWidth) !== null && _a !== void 0 ? _a : 40;
54
+ const maxVal = Math.max(...items.map((i) => i.value), 1);
55
+ const maxLabel = Math.max(...items.map((i) => i.label.length));
56
+ const maxValStr = Math.max(...items.map((i) => i.value.toLocaleString('en-US').length));
57
+ return items
58
+ .map((item) => {
59
+ var _a, _b;
60
+ const barLen = Math.round((item.value / maxVal) * maxWidth);
61
+ const rgb = (_b = COLOR_MAP[(_a = item.color) !== null && _a !== void 0 ? _a : 'cyan']) !== null && _b !== void 0 ? _b : COLOR_MAP.cyan;
62
+ const bar = barLen > 0 ? gradientBar(barLen, rgb) : '';
63
+ const pad = ' '.repeat(maxWidth - barLen);
64
+ return ` ${item.label.padEnd(maxLabel)} ${bar}${pad} ${item.value.toLocaleString('en-US').padStart(maxValStr)}`;
65
+ })
66
+ .join('\n');
67
+ }
68
+ export function formatOutput(format, headers, rows, jsonData) {
69
+ if (format === 'json')
70
+ return JSON.stringify(jsonData, null, 2);
71
+ if (format === 'plain')
72
+ return formatPlainTable(headers, rows);
73
+ return formatPrettyTable(headers, rows);
74
+ }
@@ -0,0 +1 @@
1
+ export {};
package/bin/cli.js CHANGED
@@ -16,7 +16,9 @@ import path from 'path';
16
16
  import yargs from 'yargs';
17
17
  import { hideBin } from 'yargs/helpers';
18
18
  import { accessibilityCheck } from './accessibilityCheck.js';
19
+ import { analyticsBuilder } from './analytics/index.js';
19
20
  import { setTelemetryEnabled } from './config.js';
21
+ import { getConfigValue, setConfigValue, clearConfigValue } from './config.js';
20
22
  import { CMD_EXEC_PATH } from './constants.js';
21
23
  import { checkPort, checkNodeVersion, autoUpgradeIfNeeded, getVersions, suppressConsoleWarnings, terminate, } from './helpers.js';
22
24
  import { init } from './init.js';
@@ -260,6 +262,59 @@ export const cli = ({ packageName = 'mint' }) => {
260
262
  yield login();
261
263
  yield terminate(0);
262
264
  }))
265
+ .command('config', 'manage CLI configuration', (yargs) => yargs
266
+ .command('set <key> <value>', 'set a configuration value', (yargs) => yargs
267
+ .positional('key', {
268
+ type: 'string',
269
+ demandOption: true,
270
+ description: 'Config key (e.g. subdomain)',
271
+ })
272
+ .positional('value', {
273
+ type: 'string',
274
+ demandOption: true,
275
+ description: 'Config value',
276
+ }), (argv) => __awaiter(void 0, void 0, void 0, function* () {
277
+ const validKeys = ['subdomain', 'dateFrom', 'dateTo'];
278
+ if (!validKeys.includes(argv.key)) {
279
+ addLog(_jsx(ErrorLog, { message: `Unknown config key: "${argv.key}". Valid keys: ${validKeys.join(', ')}` }));
280
+ yield terminate(1);
281
+ return;
282
+ }
283
+ yield setConfigValue(argv.key, argv.value);
284
+ addLog(_jsx(SuccessLog, { message: `${argv.key} = "${argv.value}"` }));
285
+ yield terminate(0);
286
+ }))
287
+ .command('get <key>', 'get a configuration value', (yargs) => yargs.positional('key', {
288
+ type: 'string',
289
+ demandOption: true,
290
+ description: 'Config key (e.g. subdomain, dateFrom, dateTo)',
291
+ }), (argv) => __awaiter(void 0, void 0, void 0, function* () {
292
+ const validKeys = ['subdomain', 'dateFrom', 'dateTo'];
293
+ if (!validKeys.includes(argv.key)) {
294
+ addLog(_jsx(ErrorLog, { message: `Unknown config key: "${argv.key}". Valid keys: ${validKeys.join(', ')}` }));
295
+ yield terminate(1);
296
+ return;
297
+ }
298
+ const val = getConfigValue(argv.key);
299
+ addLog(_jsx(Text, { children: val !== null && val !== void 0 ? val : 'not set' }));
300
+ yield terminate(0);
301
+ }))
302
+ .command('clear <key>', 'remove a configuration value', (yargs) => yargs.positional('key', {
303
+ type: 'string',
304
+ demandOption: true,
305
+ description: 'Config key (e.g. subdomain, dateFrom, dateTo)',
306
+ }), (argv) => __awaiter(void 0, void 0, void 0, function* () {
307
+ const validKeys = ['subdomain', 'dateFrom', 'dateTo'];
308
+ if (!validKeys.includes(argv.key)) {
309
+ addLog(_jsx(ErrorLog, { message: `Unknown config key: "${argv.key}". Valid keys: ${validKeys.join(', ')}` }));
310
+ yield terminate(1);
311
+ return;
312
+ }
313
+ yield clearConfigValue(argv.key);
314
+ addLog(_jsx(SuccessLog, { message: `${argv.key} cleared` }));
315
+ yield terminate(0);
316
+ }))
317
+ .demandCommand(1, 'specify a subcommand: set, get, or clear'))
263
318
  .command('update', 'update the CLI to the latest version', () => undefined, () => __awaiter(void 0, void 0, void 0, function* () {
264
319
  yield update({ packageName });
265
320
  yield terminate(0);
@@ -337,6 +392,7 @@ export const cli = ({ packageName = 'mint' }) => {
337
392
  yield terminate(1);
338
393
  }
339
394
  }))
395
+ .command('analytics', 'view analytics for your documentation', analyticsBuilder)
340
396
  // Print the help menu when the user enters an invalid command.
341
397
  .strictCommands()
342
398
  .demandCommand(1, 'unknown command. see above for the list of supported commands.')