kiku-agent 1.0.4 → 1.0.6
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 +25 -2
- package/package.json +12 -3
- package/server/agent/agentCore.js +164 -55
- package/server/agent/cli.js +1300 -695
- package/server/agent/commandRegistry.js +436 -74
- package/server/agent/commands.js +629 -63
- package/server/agent/doctor.js +7 -6
- package/server/agent/mcpClient.js +436 -364
- package/server/agent/queryDetails.js +182 -0
- package/server/agent/sanitizer.js +4 -4
- package/server/agent/subagent.js +1 -0
- package/server/agent/suggestions.js +346 -0
- package/server/agent/theme.js +12 -12
- package/server/agent/tools.js +118 -7
- package/server/agent/ui.js +433 -31
- package/server/agent/updateManager.js +438 -0
- package/server/agent/version.js +10 -0
- package/server/mcp/data/brands.js +9 -4
- package/server/mcp/server.js +2 -1
- package/src/data/brandsData.js +65 -0
package/server/agent/commands.js
CHANGED
|
@@ -2,10 +2,15 @@ import { toolsSchema, executeTool } from './tools.js';
|
|
|
2
2
|
import { audioPlayer } from './audioPlayer.js';
|
|
3
3
|
import { ensureYouTubeCookies, resetCookieCheck, getCookieStatus } from './youtubeAuth.js';
|
|
4
4
|
import { AgentMemory } from './memory.js';
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
C, UI_ICONS, printSolidPanel, formatTerminalMarkdown, formatMarkdownLines, randomFanName, createProgressRunner, wrapText, getFullWidth, getWelcomeHeroLines, sanitizeTerminalText,
|
|
7
|
+
formatRichCardLines, printRichCard, printRichStatusCard, formatStatusBadge, formatRichTable
|
|
8
|
+
} from './ui.js';
|
|
9
|
+
import { COMMAND_NAMES, getCommandDetails, findCommandDefinition } from './commandRegistry.js';
|
|
7
10
|
import { THEMES, THEME_NAMES, ansiBackground, ansiForeground, getThemeName, setTheme } from './theme.js';
|
|
8
11
|
import { getGpuMetrics, formatProgressBar } from './gpuMetrics.js';
|
|
12
|
+
import { PACKAGE_NAME, PACKAGE_VERSION } from './version.js';
|
|
13
|
+
import { UpdateError, UpdateManager, UPDATE_CHANNELS } from './updateManager.js';
|
|
9
14
|
|
|
10
15
|
function levenshteinDistance(a, b) {
|
|
11
16
|
const matrix = Array.from({ length: a.length + 1 }, () => Array(b.length + 1).fill(0));
|
|
@@ -87,15 +92,50 @@ export const handlers = {
|
|
|
87
92
|
},
|
|
88
93
|
|
|
89
94
|
'/help': async (ctx) => {
|
|
90
|
-
const sub = ctx.sub || ctx.extra || '';
|
|
95
|
+
const sub = (ctx.sub || ctx.extra || '').trim();
|
|
91
96
|
if (typeof ctx.printHelp === 'function') {
|
|
92
97
|
ctx.printHelp(sub);
|
|
98
|
+
return 'HANDLED';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (sub) {
|
|
102
|
+
const rawName = sub.toLowerCase();
|
|
103
|
+
const cmdName = rawName.startsWith('/') ? rawName : `/${rawName}`;
|
|
104
|
+
const definition = findCommandDefinition(cmdName);
|
|
105
|
+
const details = getCommandDetails(cmdName);
|
|
106
|
+
if (definition || details) {
|
|
107
|
+
const titleName = definition?.name || cmdName;
|
|
108
|
+
const summary = details?.summary || definition?.desc || '';
|
|
109
|
+
const usage = details?.usage || definition?.usage || '';
|
|
110
|
+
const lines = [
|
|
111
|
+
`${C.bold}${C.white}${summary}${C.reset}`,
|
|
112
|
+
'',
|
|
113
|
+
`${C.bold}${C.lavender}Usage:${C.reset} ${C.cyberCyan}${usage}${C.reset}`,
|
|
114
|
+
];
|
|
115
|
+
if (details?.subcommands?.length) {
|
|
116
|
+
lines.push('', `${C.bold}${C.roseGlow}Subcommands & Options:${C.reset}`);
|
|
117
|
+
for (const s of details.subcommands) {
|
|
118
|
+
lines.push(` ${C.bold}${C.gold}${s.cmd.padEnd(24)}${C.reset} ${C.white}${s.desc}${C.reset}`);
|
|
119
|
+
if (s.example) lines.push(` ${C.dim}↳ Example: ${C.cyberCyan}${s.example}${C.reset}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (details?.examples?.length) {
|
|
123
|
+
lines.push('', `${C.bold}${C.emerald}Quick Examples:${C.reset}`);
|
|
124
|
+
for (const ex of details.examples) {
|
|
125
|
+
lines.push(` ${C.cyberCyan}❯ ${ex}${C.reset}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
printSolidPanel({ title: `Command Help • ${titleName}`, lines });
|
|
129
|
+
return 'HANDLED';
|
|
130
|
+
}
|
|
93
131
|
}
|
|
132
|
+
|
|
133
|
+
console.log(`\n ${C.bold}Type /help <command> for detailed subcommand breakdown.${C.reset}\n`);
|
|
94
134
|
return 'HANDLED';
|
|
95
135
|
},
|
|
96
136
|
|
|
97
137
|
'/details': async (ctx) => {
|
|
98
|
-
ctx.printQueryDetails(ctx.sub
|
|
138
|
+
ctx.printQueryDetails([ctx.sub, ctx.extra].filter(Boolean).join(' '));
|
|
99
139
|
return 'HANDLED';
|
|
100
140
|
},
|
|
101
141
|
|
|
@@ -131,7 +171,7 @@ export const handlers = {
|
|
|
131
171
|
}
|
|
132
172
|
|
|
133
173
|
lines.push(`${C.bold}${C.roseGlow}Findings Report:${C.reset}`);
|
|
134
|
-
for (const fLine of
|
|
174
|
+
for (const fLine of formatMarkdownLines(latest.answer || 'No report generated.')) {
|
|
135
175
|
lines.push(` ${fLine}`);
|
|
136
176
|
}
|
|
137
177
|
|
|
@@ -172,6 +212,7 @@ export const handlers = {
|
|
|
172
212
|
});
|
|
173
213
|
|
|
174
214
|
const startTime = Date.now();
|
|
215
|
+
ctx.setIsProcessing?.(true, `Subagent: ${task.slice(0, 24)}`);
|
|
175
216
|
try {
|
|
176
217
|
const result = await ctx.agent.runSubagent(task, {
|
|
177
218
|
onProgress: event => {
|
|
@@ -200,7 +241,7 @@ export const handlers = {
|
|
|
200
241
|
}
|
|
201
242
|
|
|
202
243
|
lines.push(`${C.bold}${C.roseGlow}Synthesized Evidence:${C.reset}`);
|
|
203
|
-
for (const line of
|
|
244
|
+
for (const line of formatMarkdownLines(result.answer || 'No findings returned.')) {
|
|
204
245
|
lines.push(` ${line}`);
|
|
205
246
|
}
|
|
206
247
|
|
|
@@ -213,6 +254,8 @@ export const handlers = {
|
|
|
213
254
|
});
|
|
214
255
|
} catch (error) {
|
|
215
256
|
console.log(`\n ${C.red}[SUBAGENT ERROR] ${error.message}${C.reset}\n`);
|
|
257
|
+
} finally {
|
|
258
|
+
ctx.setIsProcessing?.(false);
|
|
216
259
|
}
|
|
217
260
|
return 'HANDLED';
|
|
218
261
|
},
|
|
@@ -306,6 +349,7 @@ export const handlers = {
|
|
|
306
349
|
});
|
|
307
350
|
|
|
308
351
|
const startTime = Date.now();
|
|
352
|
+
ctx.setIsProcessing?.(true, `Research: ${question.slice(0, 24)}`);
|
|
309
353
|
try {
|
|
310
354
|
const result = await ctx.agent.runResearch(question, {
|
|
311
355
|
onProgress: event => {
|
|
@@ -319,7 +363,7 @@ export const handlers = {
|
|
|
319
363
|
`${C.bold}${C.emerald}[DONE] Research synthesis completed in ${elapsed}s across 3 workers.${C.reset}`,
|
|
320
364
|
'',
|
|
321
365
|
`${C.bold}${C.roseGlow}Comprehensive Findings:${C.reset}`,
|
|
322
|
-
...
|
|
366
|
+
...formatMarkdownLines(result.answer || 'No findings returned.').map(l => ` ${l}`),
|
|
323
367
|
'',
|
|
324
368
|
`${C.bold}${C.lavender}Validated Sources (${result.sources.length}):${C.reset}`,
|
|
325
369
|
...(result.sources.length > 0 ? result.sources.map((s, i) => ` [${i + 1}] ${C.cyberCyan}${s}${C.reset}`) : [` ${C.dim}Catalog sources${C.reset}`]),
|
|
@@ -331,6 +375,8 @@ export const handlers = {
|
|
|
331
375
|
});
|
|
332
376
|
} catch (error) {
|
|
333
377
|
console.log(`\n ${C.red}[RESEARCH ERROR] ${error.message}${C.reset}\n`);
|
|
378
|
+
} finally {
|
|
379
|
+
ctx.setIsProcessing?.(false);
|
|
334
380
|
}
|
|
335
381
|
return 'HANDLED';
|
|
336
382
|
},
|
|
@@ -364,6 +410,7 @@ export const handlers = {
|
|
|
364
410
|
});
|
|
365
411
|
|
|
366
412
|
const startTime = Date.now();
|
|
413
|
+
ctx.setIsProcessing?.(true, `Verifying: ${claim.slice(0, 24)}`);
|
|
367
414
|
try {
|
|
368
415
|
const result = await ctx.agent.runResearch(claim, { mode: 'verify' });
|
|
369
416
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
@@ -382,7 +429,7 @@ export const handlers = {
|
|
|
382
429
|
`${C.bold}Verdict:${C.reset} ${verdictBadge} ${C.dim}(Checked in ${elapsed}s)${C.reset}`,
|
|
383
430
|
'',
|
|
384
431
|
`${C.bold}${C.roseGlow}Verification Breakdown:${C.reset}`,
|
|
385
|
-
...answerStr
|
|
432
|
+
...formatMarkdownLines(answerStr).map(l => ` ${l}`),
|
|
386
433
|
'',
|
|
387
434
|
`${C.bold}${C.lavender}Evidence & Sources (${result.sources.length}):${C.reset}`,
|
|
388
435
|
...(result.sources.length > 0 ? result.sources.map((s, i) => ` [${i + 1}] ${C.cyberCyan}${s}${C.reset}`) : [` ${C.dim}Catalog data verification${C.reset}`])
|
|
@@ -394,6 +441,8 @@ export const handlers = {
|
|
|
394
441
|
});
|
|
395
442
|
} catch (error) {
|
|
396
443
|
console.log(`\n ${C.red}[VERIFY ERROR] ${error.message}${C.reset}\n`);
|
|
444
|
+
} finally {
|
|
445
|
+
ctx.setIsProcessing?.(false);
|
|
397
446
|
}
|
|
398
447
|
return 'HANDLED';
|
|
399
448
|
},
|
|
@@ -407,6 +456,138 @@ export const handlers = {
|
|
|
407
456
|
return 'HANDLED';
|
|
408
457
|
},
|
|
409
458
|
|
|
459
|
+
'/suggestion': async (ctx) => {
|
|
460
|
+
const rawAction = (ctx.sub || 'list').toLowerCase();
|
|
461
|
+
const extra = (ctx.extra || '').trim();
|
|
462
|
+
|
|
463
|
+
if (rawAction === 'run' || rawAction === 'exec' || rawAction === 'start') {
|
|
464
|
+
const targets = extra || 'all';
|
|
465
|
+
|
|
466
|
+
printRichCard({
|
|
467
|
+
title: 'Multi-Subagent Suggestions Execution',
|
|
468
|
+
status: 'autostart',
|
|
469
|
+
lines: [
|
|
470
|
+
`• Target Questions: ${targets}`,
|
|
471
|
+
'• Dispatching bounded concurrent ResearchSubagents...',
|
|
472
|
+
],
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
const startTime = Date.now();
|
|
476
|
+
ctx.setIsProcessing?.(true, `Suggestions: ${targets.slice(0, 20)}`);
|
|
477
|
+
try {
|
|
478
|
+
const batchResult = await ctx.agent.runSuggestionBatch(targets, {
|
|
479
|
+
onProgress: event => {
|
|
480
|
+
if (event.type === 'worker-start') {
|
|
481
|
+
console.log(` ${C.cyberCyan}▸ Worker ${event.index} Started:${C.reset} ${C.bold}${event.label}${C.reset} — ${C.dim}${event.question}${C.reset}`);
|
|
482
|
+
} else if (event.type === 'tool') {
|
|
483
|
+
console.log(` ${C.dim}[Worker] Executing tool: ${event.name}...${C.reset}`);
|
|
484
|
+
} else if (event.type === 'worker-complete') {
|
|
485
|
+
console.log(` ${C.emerald}[✓] Worker ${event.index} Finished:${C.reset} ${event.label}`);
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
491
|
+
console.log('');
|
|
492
|
+
printRichCard({
|
|
493
|
+
title: `Subagent Batch Complete (${batchResult.successfulCount}/${batchResult.tasksCount} Succeeded)`,
|
|
494
|
+
status: 'ok',
|
|
495
|
+
lines: [
|
|
496
|
+
`Completed ${batchResult.tasksCount} research tasks concurrently in ${elapsed}s.`,
|
|
497
|
+
],
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
for (const res of batchResult.results) {
|
|
501
|
+
if (res.answer) {
|
|
502
|
+
const answerLines = formatMarkdownLines(res.answer);
|
|
503
|
+
printSolidPanel({
|
|
504
|
+
title: `[Worker ${res.index}] ${res.category} • ${res.question}`,
|
|
505
|
+
lines: [
|
|
506
|
+
`${C.bold}${C.cyberCyan}Question:${C.reset} ${res.question}`,
|
|
507
|
+
`${C.dim}Tool calls: ${res.toolCalls?.length || 0} • Iterations: ${res.iterations || 1}${C.reset}`,
|
|
508
|
+
'',
|
|
509
|
+
`${C.bold}${C.roseGlow}Findings:${C.reset}`,
|
|
510
|
+
...answerLines,
|
|
511
|
+
],
|
|
512
|
+
});
|
|
513
|
+
} else {
|
|
514
|
+
printRichCard({
|
|
515
|
+
title: `Worker ${res.index} Failed: ${res.category}`,
|
|
516
|
+
status: 'error',
|
|
517
|
+
lines: [
|
|
518
|
+
`Question: ${res.question}`,
|
|
519
|
+
`Error: ${res.error || 'Failed to research'}`,
|
|
520
|
+
],
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
} catch (err) {
|
|
525
|
+
printRichCard({
|
|
526
|
+
title: 'Batch Subagent Error',
|
|
527
|
+
status: 'error',
|
|
528
|
+
lines: [err.message],
|
|
529
|
+
});
|
|
530
|
+
} finally {
|
|
531
|
+
ctx.setIsProcessing?.(false);
|
|
532
|
+
}
|
|
533
|
+
return 'HANDLED';
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (rawAction === 'auto') {
|
|
537
|
+
console.log(`\n ${C.cyberCyan}Generating fresh suggestions and launching concurrent subagents...${C.reset}\n`);
|
|
538
|
+
ctx.setIsProcessing?.(true, 'Auto Suggestions');
|
|
539
|
+
try {
|
|
540
|
+
await ctx.agent.generateSuggestions({ limit: 3 });
|
|
541
|
+
} finally {
|
|
542
|
+
ctx.setIsProcessing?.(false);
|
|
543
|
+
}
|
|
544
|
+
return handlers['/suggestion']({ ...ctx, sub: 'run', extra: 'all' });
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const isTopic = rawAction === 'topic';
|
|
548
|
+
const topicName = isTopic ? extra : (rawAction !== 'list' && rawAction !== 'refresh' ? rawAction : '');
|
|
549
|
+
|
|
550
|
+
try {
|
|
551
|
+
console.log(`\n ${C.cyberCyan}✦ Fetching AI research suggestions...${C.reset}`);
|
|
552
|
+
ctx.setIsProcessing?.(true, 'Fetching Suggestions');
|
|
553
|
+
const suggestions = await ctx.agent.generateSuggestions({
|
|
554
|
+
topic: topicName,
|
|
555
|
+
limit: 5,
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
const cardLines = [
|
|
559
|
+
`${C.dim}Explore exciting Ju Jingyi topics with concurrent subagents:${C.reset}`,
|
|
560
|
+
'',
|
|
561
|
+
...suggestions.flatMap((s) => [
|
|
562
|
+
` ${C.bold}${C.gold}[${s.id}]${C.reset} ${C.bold}${C.cyberCyan}[${s.category}]${C.reset} ${C.white}${s.question}${C.reset}`,
|
|
563
|
+
` ${C.dim}↳ ${s.description}${C.reset}`,
|
|
564
|
+
]),
|
|
565
|
+
'',
|
|
566
|
+
`${C.bold}${C.roseGlow}Quick Execution Tips:${C.reset}`,
|
|
567
|
+
` • Run one task: ${C.white}/suggestion run 1${C.reset}`,
|
|
568
|
+
` • Run parallel batch: ${C.white}/suggestion run 1,2,3${C.reset}`,
|
|
569
|
+
` • Run all concurrently: ${C.white}/suggestion run all${C.reset}`,
|
|
570
|
+
` • Auto-generate & run: ${C.white}/suggestion auto${C.reset}`,
|
|
571
|
+
` • Filter by topic: ${C.white}/suggestion topic drama | music | brand | 2026${C.reset}`,
|
|
572
|
+
];
|
|
573
|
+
|
|
574
|
+
printRichCard({
|
|
575
|
+
title: `AI Research Suggestions (${suggestions.length} Questions Generated)`,
|
|
576
|
+
status: 'info',
|
|
577
|
+
lines: cardLines,
|
|
578
|
+
});
|
|
579
|
+
} catch (err) {
|
|
580
|
+
printRichCard({
|
|
581
|
+
title: 'Suggestion Error',
|
|
582
|
+
status: 'error',
|
|
583
|
+
lines: [err.message],
|
|
584
|
+
});
|
|
585
|
+
} finally {
|
|
586
|
+
ctx.setIsProcessing?.(false);
|
|
587
|
+
}
|
|
588
|
+
return 'HANDLED';
|
|
589
|
+
},
|
|
590
|
+
|
|
410
591
|
'/skill': async (ctx) => {
|
|
411
592
|
const action = (ctx.sub || 'list').toLowerCase();
|
|
412
593
|
const memory = ctx.agent.memory;
|
|
@@ -976,8 +1157,12 @@ export const handlers = {
|
|
|
976
1157
|
},
|
|
977
1158
|
|
|
978
1159
|
'/save': async (ctx) => {
|
|
979
|
-
|
|
980
|
-
|
|
1160
|
+
try {
|
|
1161
|
+
const exported = await ctx.agent.session.exportToMarkdown();
|
|
1162
|
+
console.log(`\n ${C.emerald}[OK] Exported.${C.reset} ${C.white}${exported.filePath}${C.reset}\n`);
|
|
1163
|
+
} catch (err) {
|
|
1164
|
+
console.log(`\n ${C.red}[SAVE ERROR] ${err.message || String(err)}${C.reset}\n`);
|
|
1165
|
+
}
|
|
981
1166
|
return 'HANDLED';
|
|
982
1167
|
},
|
|
983
1168
|
|
|
@@ -1288,7 +1473,11 @@ export const handlers = {
|
|
|
1288
1473
|
const mcpManager = ctx.agent?.mcpManager;
|
|
1289
1474
|
|
|
1290
1475
|
if (!mcpManager) {
|
|
1291
|
-
|
|
1476
|
+
printRichCard({
|
|
1477
|
+
title: 'MCP Error',
|
|
1478
|
+
status: 'error',
|
|
1479
|
+
lines: ['MCP Client Manager is unavailable.'],
|
|
1480
|
+
});
|
|
1292
1481
|
return 'HANDLED';
|
|
1293
1482
|
}
|
|
1294
1483
|
|
|
@@ -1300,9 +1489,10 @@ export const handlers = {
|
|
|
1300
1489
|
} else {
|
|
1301
1490
|
for (const [idx, t] of tools.entries()) {
|
|
1302
1491
|
const f = t.function;
|
|
1303
|
-
const
|
|
1304
|
-
|
|
1305
|
-
|
|
1492
|
+
const toolName = sanitizeTerminalText(f.name, 128) || 'unknown_tool';
|
|
1493
|
+
const params = Object.keys(f.parameters?.properties || {}).map(param => sanitizeTerminalText(param, 80)).join(', ');
|
|
1494
|
+
lines.push(`${C.gold}${idx + 1}.${C.reset} ${C.bold}${C.cyberCyan}${toolName}${C.reset}${C.slate}(${params})${C.reset}`);
|
|
1495
|
+
const desc = sanitizeTerminalText(f.description || 'No description provided');
|
|
1306
1496
|
const wrapped = wrapText(desc, Math.max(30, getFullWidth() - 8));
|
|
1307
1497
|
for (const dLine of wrapped) {
|
|
1308
1498
|
lines.push(` ${C.dim}${dLine}${C.reset}`);
|
|
@@ -1316,7 +1506,7 @@ export const handlers = {
|
|
|
1316
1506
|
return 'HANDLED';
|
|
1317
1507
|
}
|
|
1318
1508
|
|
|
1319
|
-
if (action === 'connect') {
|
|
1509
|
+
if (action === 'connect' || action === 'add') {
|
|
1320
1510
|
const parts = (ctx.parts || []).slice(2);
|
|
1321
1511
|
const name = parts[0];
|
|
1322
1512
|
const targetUrlOrType = parts[1];
|
|
@@ -1325,11 +1515,11 @@ export const handlers = {
|
|
|
1325
1515
|
printSolidPanel({
|
|
1326
1516
|
title: 'MCP Connect Usage',
|
|
1327
1517
|
lines: [
|
|
1328
|
-
`${C.bold}Connect
|
|
1518
|
+
`${C.bold}Connect & Save Remote HTTP MCP:${C.reset}`,
|
|
1329
1519
|
` ${C.cyberCyan}/mcp connect <name> <url>${C.reset}`,
|
|
1330
1520
|
` ${C.dim}Example: /mcp connect kiku https://jujingyi-kikuuu.vercel.app/mcp${C.reset}`,
|
|
1331
1521
|
'',
|
|
1332
|
-
`${C.bold}Connect
|
|
1522
|
+
`${C.bold}Connect & Save Stdio MCP Process:${C.reset}`,
|
|
1333
1523
|
` ${C.cyberCyan}/mcp connect <name> stdio <command> [args...]${C.reset}`,
|
|
1334
1524
|
` ${C.dim}Example: /mcp connect local-kiku stdio node ./server/mcp/stdio.js${C.reset}`,
|
|
1335
1525
|
]
|
|
@@ -1357,11 +1547,12 @@ export const handlers = {
|
|
|
1357
1547
|
const res = await ctx.agent.connectMcp(name, serverConfig);
|
|
1358
1548
|
|
|
1359
1549
|
runner.done({
|
|
1360
|
-
title: `MCP Server Connected • "${name}"`,
|
|
1550
|
+
title: `MCP Server Connected & Saved • "${name}"`,
|
|
1361
1551
|
lines: [
|
|
1362
1552
|
`${C.gold}Server:${C.reset} ${C.bold}${C.white}${name}${C.reset} ${C.dim}(${serverConfig.type})${C.reset}`,
|
|
1363
1553
|
`${C.gold}Target:${C.reset} ${C.cyberCyan}${targetUrlOrType}${C.reset}`,
|
|
1364
1554
|
`${C.gold}Capabilities:${C.reset} ${C.emerald}${res.toolsCount} Tools${C.reset} ${C.slate}•${C.reset} ${C.cyberCyan}${res.resourcesCount} Resources${C.reset} ${C.slate}•${C.reset} ${C.lavender}${res.promptsCount} Prompts${C.reset}`,
|
|
1555
|
+
`${C.gold}Persisted:${C.reset} ${C.emerald}Saved to configuration (auto-reconnects on start)${C.reset}`,
|
|
1365
1556
|
`${C.gold}Integration:${C.reset} ${C.dim}Tools prefixed with ${C.cyberCyan}mcp_${name}_*${C.dim} and auto-routed to agent${C.reset}`,
|
|
1366
1557
|
'',
|
|
1367
1558
|
`${C.dim}Tip: Run /mcp tools to inspect all registered tools or /mcp status for overview.${C.reset}`
|
|
@@ -1375,7 +1566,81 @@ export const handlers = {
|
|
|
1375
1566
|
return 'HANDLED';
|
|
1376
1567
|
}
|
|
1377
1568
|
|
|
1378
|
-
if (action === '
|
|
1569
|
+
if (action === 'save') {
|
|
1570
|
+
const target = (ctx.extra || 'global').toLowerCase().trim();
|
|
1571
|
+
const saveRes = await ctx.agent.saveMcpConfig(target);
|
|
1572
|
+
if (saveRes.success) {
|
|
1573
|
+
printRichCard({
|
|
1574
|
+
title: 'MCP Configuration Saved',
|
|
1575
|
+
status: 'ok',
|
|
1576
|
+
lines: [
|
|
1577
|
+
`• Target: ${target === 'workspace' ? 'Workspace (.mcp.json)' : 'Global config (mcp_servers.json)'}`,
|
|
1578
|
+
`• File: ${saveRes.filePath}`,
|
|
1579
|
+
`• Total servers persisted: ${saveRes.count}`,
|
|
1580
|
+
],
|
|
1581
|
+
});
|
|
1582
|
+
} else {
|
|
1583
|
+
printRichCard({
|
|
1584
|
+
title: 'MCP Save Failed',
|
|
1585
|
+
status: 'error',
|
|
1586
|
+
lines: [saveRes.error || 'Failed to write MCP config'],
|
|
1587
|
+
});
|
|
1588
|
+
}
|
|
1589
|
+
return 'HANDLED';
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
if (action === 'reload' || action === 'reconnect') {
|
|
1593
|
+
const target = (ctx.extra || '').trim();
|
|
1594
|
+
printRichCard({
|
|
1595
|
+
title: 'Reloading MCP Configuration',
|
|
1596
|
+
status: 'autostart',
|
|
1597
|
+
lines: [`Reconnecting saved MCP servers...`],
|
|
1598
|
+
});
|
|
1599
|
+
try {
|
|
1600
|
+
await ctx.agent.reconnectMcp(target || 'all');
|
|
1601
|
+
const active = ctx.agent.mcpManager.servers.size;
|
|
1602
|
+
printRichCard({
|
|
1603
|
+
title: 'MCP Servers Reconnected',
|
|
1604
|
+
status: 'ok',
|
|
1605
|
+
lines: [
|
|
1606
|
+
`Active live servers: ${active}`,
|
|
1607
|
+
`Total tools available: ${ctx.agent.mcpManager.getToolsSchema().length}`,
|
|
1608
|
+
],
|
|
1609
|
+
});
|
|
1610
|
+
} catch (err) {
|
|
1611
|
+
printRichCard({
|
|
1612
|
+
title: 'MCP Reconnect Error',
|
|
1613
|
+
status: 'error',
|
|
1614
|
+
lines: [err.message],
|
|
1615
|
+
});
|
|
1616
|
+
}
|
|
1617
|
+
return 'HANDLED';
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
if (action === 'remove' || action === 'rm' || action === 'del') {
|
|
1621
|
+
const name = (ctx.extra || '').trim();
|
|
1622
|
+
if (!name) {
|
|
1623
|
+
console.log(`\n ${C.amber}Usage: /mcp remove <server_name>${C.reset}\n`);
|
|
1624
|
+
return 'HANDLED';
|
|
1625
|
+
}
|
|
1626
|
+
const success = await ctx.agent.removeMcp(name);
|
|
1627
|
+
if (success) {
|
|
1628
|
+
printRichCard({
|
|
1629
|
+
title: 'MCP Server Removed',
|
|
1630
|
+
status: 'ok',
|
|
1631
|
+
lines: [`Removed "${name}" from saved configuration and disconnected.`],
|
|
1632
|
+
});
|
|
1633
|
+
} else {
|
|
1634
|
+
printRichCard({
|
|
1635
|
+
title: 'MCP Server Not Found',
|
|
1636
|
+
status: 'warning',
|
|
1637
|
+
lines: [`Server "${name}" was not found in saved configuration.`],
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
return 'HANDLED';
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
if (action === 'disconnect') {
|
|
1379
1644
|
const name = (ctx.extra || '').trim();
|
|
1380
1645
|
if (!name) {
|
|
1381
1646
|
console.log(`\n ${C.amber}Usage: /mcp disconnect <server_name>${C.reset}\n`);
|
|
@@ -1383,45 +1648,81 @@ export const handlers = {
|
|
|
1383
1648
|
}
|
|
1384
1649
|
const success = await ctx.agent.disconnectMcp(name);
|
|
1385
1650
|
if (success) {
|
|
1386
|
-
|
|
1651
|
+
printRichCard({
|
|
1652
|
+
title: 'MCP Server Disconnected',
|
|
1653
|
+
status: 'info',
|
|
1654
|
+
lines: [
|
|
1655
|
+
`Disconnected session for "${name}".`,
|
|
1656
|
+
`Config remains saved. Run /mcp reconnect ${name} to reconnect.`,
|
|
1657
|
+
],
|
|
1658
|
+
});
|
|
1387
1659
|
} else {
|
|
1388
|
-
|
|
1660
|
+
printRichCard({
|
|
1661
|
+
title: 'MCP Server Not Found',
|
|
1662
|
+
status: 'warning',
|
|
1663
|
+
lines: [`MCP server "${name}" is not currently connected.`],
|
|
1664
|
+
});
|
|
1389
1665
|
}
|
|
1390
1666
|
return 'HANDLED';
|
|
1391
1667
|
}
|
|
1392
1668
|
|
|
1393
1669
|
const status = mcpManager.getStatus();
|
|
1394
1670
|
const lines = [
|
|
1395
|
-
`${C.gold}Active Servers:${C.reset} ${C.cyberCyan}${status.activeServersCount}${C.reset} • ${C.gold}Total
|
|
1671
|
+
`${C.gold}Active Live Servers:${C.reset} ${C.cyberCyan}${status.activeServersCount}${C.reset} • ${C.gold}Total Configured:${C.reset} ${C.white}${status.totalConfiguredCount}${C.reset} • ${C.gold}Total Tools:${C.reset} ${C.cyberCyan}${status.totalToolsCount}${C.reset}`,
|
|
1396
1672
|
''
|
|
1397
1673
|
];
|
|
1398
1674
|
|
|
1399
1675
|
if (status.servers.length > 0) {
|
|
1400
|
-
lines.push(`${C.bold}${C.lavender}
|
|
1676
|
+
lines.push(`${C.bold}${C.lavender}Configured MCP Servers:${C.reset}`);
|
|
1401
1677
|
for (const s of status.servers) {
|
|
1402
|
-
|
|
1678
|
+
const serverName = sanitizeTerminalText(s.name, 128) || 'unnamed';
|
|
1679
|
+
const serverType = sanitizeTerminalText(s.type, 80) || 'unknown';
|
|
1680
|
+
if (s.status === 'connected') {
|
|
1681
|
+
lines.push(` ${C.emerald}● Connected:${C.reset} ${C.bold}${C.white}${serverName}${C.reset} ${C.dim}(${serverType})${C.reset} — ${C.emerald}${s.toolsCount} tools${C.reset}, ${s.resourcesCount} resources`);
|
|
1682
|
+
} else if (s.status === 'saved_offline') {
|
|
1683
|
+
lines.push(` ${C.slate}○ Saved (Offline):${C.reset} ${C.bold}${C.white}${serverName}${C.reset} ${C.dim}(${serverType})${C.reset} — ${C.dim}type /mcp reconnect ${serverName} to start${C.reset}`);
|
|
1684
|
+
} else {
|
|
1685
|
+
lines.push(` ${C.red}✕ Error:${C.reset} ${C.bold}${C.white}${serverName}${C.reset} ${C.dim}(${serverType})${C.reset} — ${C.red}${s.error || 'Connection failed'}${C.reset}`);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
const connectedTools = mcpManager.getToolsSchema();
|
|
1690
|
+
if (connectedTools.length > 0) {
|
|
1691
|
+
lines.push('', `${C.bold}${C.lavender}Available MCP Tool Commands (${connectedTools.length}):${C.reset}`);
|
|
1692
|
+
const commandList = connectedTools
|
|
1693
|
+
.map(tool => `@${sanitizeTerminalText(tool.function?.name, 128) || 'unknown_tool'}`)
|
|
1694
|
+
.join(' • ');
|
|
1695
|
+
for (const commandLine of wrapText(commandList, Math.max(30, getFullWidth() - 8))) {
|
|
1696
|
+
lines.push(` ${C.cyberCyan}${commandLine}${C.reset}`);
|
|
1697
|
+
}
|
|
1698
|
+
lines.push(`${C.dim}Use /mcp tools for parameters and full descriptions.${C.reset}`);
|
|
1403
1699
|
}
|
|
1404
1700
|
} else {
|
|
1405
|
-
lines.push(`${C.dim}No
|
|
1406
|
-
lines.push(`Connect with: ${C.cyberCyan}/mcp connect <name> <url>${C.reset}`);
|
|
1701
|
+
lines.push(`${C.dim}No MCP servers configured.${C.reset}`);
|
|
1702
|
+
lines.push(`Connect and save with: ${C.cyberCyan}/mcp connect <name> <url>${C.reset}`);
|
|
1407
1703
|
}
|
|
1408
1704
|
|
|
1705
|
+
lines.push('', `${C.dim}Commands: /mcp connect <name> <url> | /mcp reconnect [all] | /mcp save | /mcp remove <name> | /mcp tools${C.reset}`);
|
|
1706
|
+
|
|
1409
1707
|
printSolidPanel({
|
|
1410
|
-
title: `Model Context Protocol (MCP)
|
|
1708
|
+
title: `Model Context Protocol (MCP) Manager • ${status.activeServersCount} Active / ${status.totalConfiguredCount} Saved`,
|
|
1411
1709
|
lines,
|
|
1412
|
-
headerBg: '\x1b[48;2;48;54;70m', bg: '\x1b[48;2;36;40;52m'
|
|
1413
1710
|
});
|
|
1414
1711
|
return 'HANDLED';
|
|
1415
1712
|
},
|
|
1416
1713
|
|
|
1417
1714
|
'/ping': async (ctx) => {
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1715
|
+
try {
|
|
1716
|
+
const start = Date.now();
|
|
1717
|
+
const check = await ctx.client.checkConnection();
|
|
1718
|
+
const ms = Date.now() - start;
|
|
1719
|
+
if (check.connected) {
|
|
1720
|
+
console.log(`\n ${C.emerald}● Connected${C.reset} ${C.dim}(${ms}ms)${C.reset} — ${C.white}${check.activeModel || check.model || 'Ready'}${C.reset}\n`);
|
|
1721
|
+
} else {
|
|
1722
|
+
console.log(`\n ${C.red}● Disconnected${C.reset} ${C.dim}(${ms}ms)${C.reset} — ${C.amber}${check.error || 'Server unreachable'}${C.reset}\n`);
|
|
1723
|
+
}
|
|
1724
|
+
} catch (err) {
|
|
1725
|
+
console.log(`\n ${C.red}[PING ERROR] ${err.message || String(err)}${C.reset}\n`);
|
|
1425
1726
|
}
|
|
1426
1727
|
return 'HANDLED';
|
|
1427
1728
|
},
|
|
@@ -1444,15 +1745,19 @@ export const handlers = {
|
|
|
1444
1745
|
},
|
|
1445
1746
|
|
|
1446
1747
|
'/doctor': async (ctx) => {
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1748
|
+
try {
|
|
1749
|
+
const { runDoctor } = await import('./doctor.js');
|
|
1750
|
+
const report = await runDoctor({ client: ctx.client });
|
|
1751
|
+
printSolidPanel({
|
|
1752
|
+
title: `Kiku Doctor • ${report.ok ? 'READY' : 'ACTION REQUIRED'}`,
|
|
1753
|
+
lines: report.checks.map(check => {
|
|
1754
|
+
const marker = check.ok ? '[OK]' : check.required ? '[FAIL]' : '[OPTIONAL]';
|
|
1755
|
+
return `${marker} ${check.id}: ${check.detail}`;
|
|
1756
|
+
}),
|
|
1757
|
+
});
|
|
1758
|
+
} catch (err) {
|
|
1759
|
+
console.log(`\n ${C.red}[DOCTOR ERROR] ${err.message || String(err)}${C.reset}\n`);
|
|
1760
|
+
}
|
|
1456
1761
|
return 'HANDLED';
|
|
1457
1762
|
},
|
|
1458
1763
|
|
|
@@ -1460,7 +1765,8 @@ export const handlers = {
|
|
|
1460
1765
|
printSolidPanel({
|
|
1461
1766
|
title: 'Version',
|
|
1462
1767
|
lines: [
|
|
1463
|
-
`${C.gold}
|
|
1768
|
+
`${C.gold}Package:${C.reset} ${C.white}${PACKAGE_NAME}${C.reset}`,
|
|
1769
|
+
`${C.gold}Version:${C.reset} ${C.white}${PACKAGE_VERSION}${C.reset}`,
|
|
1464
1770
|
`${C.gold}Node:${C.reset} ${C.white}${process.version}${C.reset}`,
|
|
1465
1771
|
`${C.gold}Platform:${C.reset} ${C.white}${process.platform} ${process.arch}${C.reset}`,
|
|
1466
1772
|
],
|
|
@@ -1468,6 +1774,186 @@ export const handlers = {
|
|
|
1468
1774
|
return 'HANDLED';
|
|
1469
1775
|
},
|
|
1470
1776
|
|
|
1777
|
+
'/update': async (ctx) => {
|
|
1778
|
+
const manager = ctx.updateManager || new UpdateManager();
|
|
1779
|
+
const action = (ctx.sub || 'check').toLowerCase();
|
|
1780
|
+
const parts = ctx.parts || [];
|
|
1781
|
+
const allowMajor = parts.includes('--allow-major');
|
|
1782
|
+
const allowDeprecated = parts.includes('--allow-deprecated');
|
|
1783
|
+
const positional = parts.slice(2).filter(part => !part.startsWith('--'));
|
|
1784
|
+
const flags = parts.slice(2).filter(part => part.startsWith('--'));
|
|
1785
|
+
|
|
1786
|
+
const printUpdateHelp = () => printSolidPanel({
|
|
1787
|
+
title: 'Kiku Agent Update',
|
|
1788
|
+
lines: [
|
|
1789
|
+
`${C.bold}${C.white}/update${C.reset} ${C.dim}or /update check — check the configured channel${C.reset}`,
|
|
1790
|
+
`${C.bold}${C.white}/update install [latest|next|canary|x.y.z]${C.reset}`,
|
|
1791
|
+
`${C.bold}${C.white}/update channel [latest|next|canary]${C.reset}`,
|
|
1792
|
+
`${C.bold}${C.white}/update rollback${C.reset} ${C.dim}— reinstall the previously recorded version${C.reset}`,
|
|
1793
|
+
'',
|
|
1794
|
+
`${C.dim}Major releases require --allow-major. Deprecated releases require --allow-deprecated.${C.reset}`,
|
|
1795
|
+
],
|
|
1796
|
+
});
|
|
1797
|
+
|
|
1798
|
+
const statusLines = status => {
|
|
1799
|
+
const changeLabel = status.change === 'upgrade'
|
|
1800
|
+
? `${C.emerald}UPDATE AVAILABLE${C.reset}`
|
|
1801
|
+
: status.change === 'downgrade'
|
|
1802
|
+
? `${C.amber}OLDER RELEASE${C.reset}`
|
|
1803
|
+
: `${C.emerald}UP TO DATE${C.reset}`;
|
|
1804
|
+
const lines = [
|
|
1805
|
+
`${C.gold}Current:${C.reset} ${C.white}${status.currentVersion}${C.reset}`,
|
|
1806
|
+
`${C.gold}Target:${C.reset} ${C.white}${status.targetVersion}${C.reset} ${C.dim}(${status.target})${C.reset}`,
|
|
1807
|
+
`${C.gold}Status:${C.reset} ${changeLabel}`,
|
|
1808
|
+
`${C.gold}Installation:${C.reset} ${C.white}${status.installMode.label}${C.reset}`,
|
|
1809
|
+
`${C.gold}Node:${C.reset} ${status.nodeCompatible ? C.emerald + 'Compatible' : C.red + 'Incompatible'}${C.reset}${status.nodeRange ? ` ${C.dim}(requires ${status.nodeRange})${C.reset}` : ''}`,
|
|
1810
|
+
`${C.gold}Integrity:${C.reset} ${status.integrity ? C.emerald + 'Published with npm integrity metadata' : C.amber + 'Not reported'}${C.reset}`,
|
|
1811
|
+
];
|
|
1812
|
+
if (status.majorChange) lines.push(`${C.amber}[MAJOR] This release crosses a major-version boundary.${C.reset}`);
|
|
1813
|
+
if (status.deprecated) lines.push(`${C.red}[DEPRECATED] ${status.deprecated}${C.reset}`);
|
|
1814
|
+
lines.push('', `${C.bold}${C.lavender}What's new:${C.reset}`);
|
|
1815
|
+
if (status.releaseNotes.length > 0) {
|
|
1816
|
+
for (const note of status.releaseNotes) lines.push(` ${C.sakura}•${C.reset} ${C.white}${note}${C.reset}`);
|
|
1817
|
+
} else {
|
|
1818
|
+
lines.push(` ${C.dim}No release summary was published for this version.${C.reset}`);
|
|
1819
|
+
}
|
|
1820
|
+
if (status.releaseUrl) lines.push('', `${C.dim}Release: ${status.releaseUrl}${C.reset}`);
|
|
1821
|
+
return lines;
|
|
1822
|
+
};
|
|
1823
|
+
|
|
1824
|
+
try {
|
|
1825
|
+
const allowedFlags = action === 'install'
|
|
1826
|
+
? ['--allow-major', '--allow-deprecated']
|
|
1827
|
+
: action === 'rollback'
|
|
1828
|
+
? ['--allow-deprecated']
|
|
1829
|
+
: [];
|
|
1830
|
+
if (flags.some(flag => !allowedFlags.includes(flag))) {
|
|
1831
|
+
throw new UpdateError('Unknown option. Run /update help for supported arguments.', 'INVALID_OPTION');
|
|
1832
|
+
}
|
|
1833
|
+
if (positional.length > 1 || ((action === 'rollback' || action === 'help') && positional.length > 0)) {
|
|
1834
|
+
throw new UpdateError('Too many update arguments. Run /update help for usage.', 'INVALID_ARGUMENTS');
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
if (action === 'help') {
|
|
1838
|
+
printUpdateHelp();
|
|
1839
|
+
return 'HANDLED';
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
if (action === 'channel') {
|
|
1843
|
+
const requested = positional[0];
|
|
1844
|
+
if (!requested) {
|
|
1845
|
+
const channel = await manager.getChannel();
|
|
1846
|
+
printSolidPanel({
|
|
1847
|
+
title: 'Update Channel',
|
|
1848
|
+
lines: [
|
|
1849
|
+
`${C.gold}Current:${C.reset} ${C.white}${channel}${C.reset}`,
|
|
1850
|
+
`${C.dim}Available: ${UPDATE_CHANNELS.join(', ')}${C.reset}`,
|
|
1851
|
+
],
|
|
1852
|
+
});
|
|
1853
|
+
return 'HANDLED';
|
|
1854
|
+
}
|
|
1855
|
+
const channel = await manager.setChannel(requested);
|
|
1856
|
+
printSolidPanel({
|
|
1857
|
+
title: 'Update Channel Changed',
|
|
1858
|
+
lines: [
|
|
1859
|
+
`${C.emerald}[OK] Future /update checks will use ${C.bold}${channel}${C.reset}${C.emerald}.${C.reset}`,
|
|
1860
|
+
`${C.dim}This changes only the release channel; it does not install anything.${C.reset}`,
|
|
1861
|
+
],
|
|
1862
|
+
});
|
|
1863
|
+
return 'HANDLED';
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
if (action === 'install') {
|
|
1867
|
+
const target = positional[0];
|
|
1868
|
+
console.log(`\n ${C.cyberCyan}[UPDATE] Resolving and validating the requested npm release...${C.reset}\n`);
|
|
1869
|
+
const result = await manager.install(target, {
|
|
1870
|
+
allowMajor,
|
|
1871
|
+
allowDeprecated,
|
|
1872
|
+
onProgress: message => { if (message) console.log(` ${C.dim}${message}${C.reset}`); },
|
|
1873
|
+
});
|
|
1874
|
+
if (result.outcome === 'manual') {
|
|
1875
|
+
printSolidPanel({
|
|
1876
|
+
title: 'Manual Update Required',
|
|
1877
|
+
lines: [
|
|
1878
|
+
...statusLines(result),
|
|
1879
|
+
'',
|
|
1880
|
+
`${C.amber}This installation type is not modified automatically.${C.reset}`,
|
|
1881
|
+
`${C.gold}Run:${C.reset} ${C.white}${result.command}${C.reset}`,
|
|
1882
|
+
],
|
|
1883
|
+
});
|
|
1884
|
+
return 'HANDLED';
|
|
1885
|
+
}
|
|
1886
|
+
if (result.outcome === 'current') {
|
|
1887
|
+
printSolidPanel({ title: 'Kiku Agent Is Up to Date', lines: statusLines(result) });
|
|
1888
|
+
return 'HANDLED';
|
|
1889
|
+
}
|
|
1890
|
+
printSolidPanel({
|
|
1891
|
+
title: 'Update Installed Successfully',
|
|
1892
|
+
lines: [
|
|
1893
|
+
`${C.emerald}[OK] ${PACKAGE_NAME} ${result.installedVersion} was installed and verified.${C.reset}`,
|
|
1894
|
+
...(!result.historyRecorded ? [`${C.amber}[WARNING] Rollback history could not be saved.${C.reset}`] : []),
|
|
1895
|
+
`${C.dim}This session will close. Start kiku-agent again to load the new code.${C.reset}`,
|
|
1896
|
+
`${C.dim}Restart MCP hosts to load the matching kiku-mcp server version.${C.reset}`,
|
|
1897
|
+
],
|
|
1898
|
+
});
|
|
1899
|
+
try { await ctx.agent?.endSession?.(); } catch {}
|
|
1900
|
+
return 'EXIT';
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
if (action === 'rollback') {
|
|
1904
|
+
console.log(`\n ${C.cyberCyan}[UPDATE] Resolving the previously installed version...${C.reset}\n`);
|
|
1905
|
+
const result = await manager.rollback({ allowDeprecated });
|
|
1906
|
+
if (result.outcome === 'manual') {
|
|
1907
|
+
printSolidPanel({
|
|
1908
|
+
title: 'Manual Rollback Required',
|
|
1909
|
+
lines: [
|
|
1910
|
+
`${C.amber}Run the following command outside Kiku Agent:${C.reset}`,
|
|
1911
|
+
`${C.white}${result.command}${C.reset}`,
|
|
1912
|
+
],
|
|
1913
|
+
});
|
|
1914
|
+
return 'HANDLED';
|
|
1915
|
+
}
|
|
1916
|
+
if (result.outcome === 'current') {
|
|
1917
|
+
printSolidPanel({ title: 'Rollback Not Needed', lines: statusLines(result) });
|
|
1918
|
+
return 'HANDLED';
|
|
1919
|
+
}
|
|
1920
|
+
printSolidPanel({
|
|
1921
|
+
title: 'Rollback Installed Successfully',
|
|
1922
|
+
lines: [
|
|
1923
|
+
`${C.emerald}[OK] Restored ${PACKAGE_NAME} ${result.installedVersion}.${C.reset}`,
|
|
1924
|
+
...(!result.historyRecorded ? [`${C.amber}[WARNING] Rollback history could not be saved.${C.reset}`] : []),
|
|
1925
|
+
`${C.dim}This session will close. Restart Kiku Agent and any MCP host.${C.reset}`,
|
|
1926
|
+
],
|
|
1927
|
+
});
|
|
1928
|
+
try { await ctx.agent?.endSession?.(); } catch {}
|
|
1929
|
+
return 'EXIT';
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
if (action === 'check' || action === 'status') {
|
|
1933
|
+
const status = await manager.check(positional[0]);
|
|
1934
|
+
const lines = statusLines(status);
|
|
1935
|
+
if (status.updateAvailable) {
|
|
1936
|
+
lines.push('', `${C.cyberCyan}Run /update install${status.target === 'latest' ? '' : ` ${status.target}`} to update.${C.reset}`);
|
|
1937
|
+
}
|
|
1938
|
+
printSolidPanel({ title: 'Kiku Agent Update', lines });
|
|
1939
|
+
return 'HANDLED';
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
printUpdateHelp();
|
|
1943
|
+
return 'HANDLED';
|
|
1944
|
+
} catch (error) {
|
|
1945
|
+
const safeMessage = error instanceof UpdateError ? error.message : 'The update operation failed unexpectedly.';
|
|
1946
|
+
printSolidPanel({
|
|
1947
|
+
title: 'Update Failed',
|
|
1948
|
+
lines: [
|
|
1949
|
+
`${C.red}[${error.code || 'UPDATE_ERROR'}] ${safeMessage}${C.reset}`,
|
|
1950
|
+
`${C.dim}No session, memory, or environment files were changed.${C.reset}`,
|
|
1951
|
+
],
|
|
1952
|
+
});
|
|
1953
|
+
return 'HANDLED';
|
|
1954
|
+
}
|
|
1955
|
+
},
|
|
1956
|
+
|
|
1471
1957
|
'/system': async () => {
|
|
1472
1958
|
const os = await import('node:os');
|
|
1473
1959
|
const mem = process.memoryUsage();
|
|
@@ -1564,11 +2050,16 @@ export const handlers = {
|
|
|
1564
2050
|
'/login': async (ctx) => {
|
|
1565
2051
|
console.log(`\n ${C.cyberCyan}YouTube Login${C.reset}\n`);
|
|
1566
2052
|
resetCookieCheck();
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
2053
|
+
ctx.setIsProcessing?.(true, 'YouTube Login');
|
|
2054
|
+
try {
|
|
2055
|
+
const result = await ensureYouTubeCookies({ timeout: 180000 });
|
|
2056
|
+
if (result.ok) {
|
|
2057
|
+
console.log(` ${C.emerald}[OK] Session active (${result.source}).${C.reset}\n`);
|
|
2058
|
+
} else {
|
|
2059
|
+
console.log(` ${C.red}[ERROR] Not available: ${result.reason}${C.reset}\n`);
|
|
2060
|
+
}
|
|
2061
|
+
} finally {
|
|
2062
|
+
ctx.setIsProcessing?.(false);
|
|
1572
2063
|
}
|
|
1573
2064
|
return 'HANDLED';
|
|
1574
2065
|
},
|
|
@@ -1612,19 +2103,23 @@ export const handlers = {
|
|
|
1612
2103
|
},
|
|
1613
2104
|
|
|
1614
2105
|
'/stats': async () => {
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
2106
|
+
try {
|
|
2107
|
+
const result = await executeTool('get_system_stats', {});
|
|
2108
|
+
const s = result.stats || {};
|
|
2109
|
+
printSolidPanel({
|
|
2110
|
+
title: `Database Overview`,
|
|
2111
|
+
lines: [
|
|
2112
|
+
`${C.gold}Works:${C.reset} ${C.cyberCyan}${s.totalWorks || 0}${C.reset}`,
|
|
2113
|
+
`${C.gold}Songs:${C.reset} ${C.cyberCyan}${s.totalUniqueSongs || 0}${C.reset}`,
|
|
2114
|
+
`${C.gold}Brands:${C.reset} ${C.cyberCyan}${s.brandEndorsements || 0}${C.reset}`,
|
|
2115
|
+
`${C.gold}Awards:${C.reset} ${C.cyberCyan}${s.totalAwards || 0}${C.reset}`,
|
|
2116
|
+
`${C.gold}Timeline:${C.reset} ${C.cyberCyan}${s.timelineMilestones || 0}${C.reset}`
|
|
2117
|
+
],
|
|
2118
|
+
headerBg: '\x1b[48;2;48;54;70m', bg: '\x1b[48;2;36;40;52m'
|
|
2119
|
+
});
|
|
2120
|
+
} catch (err) {
|
|
2121
|
+
console.log(`\n ${C.red}[STATS ERROR] ${err.message || String(err)}${C.reset}\n`);
|
|
2122
|
+
}
|
|
1628
2123
|
return 'HANDLED';
|
|
1629
2124
|
},
|
|
1630
2125
|
|
|
@@ -1635,6 +2130,70 @@ export const handlers = {
|
|
|
1635
2130
|
return 'HANDLED';
|
|
1636
2131
|
},
|
|
1637
2132
|
|
|
2133
|
+
'/cancel': async (ctx) => {
|
|
2134
|
+
if (typeof ctx.cancelActiveQuery === 'function') {
|
|
2135
|
+
const ok = ctx.cancelActiveQuery();
|
|
2136
|
+
if (ok) {
|
|
2137
|
+
printRichCard({
|
|
2138
|
+
title: 'Query Cancelled',
|
|
2139
|
+
status: 'cancel',
|
|
2140
|
+
lines: ['Active query and queued tasks were cancelled.'],
|
|
2141
|
+
});
|
|
2142
|
+
} else {
|
|
2143
|
+
printRichCard({
|
|
2144
|
+
title: 'No Active Query',
|
|
2145
|
+
status: 'info',
|
|
2146
|
+
lines: ['There is no query currently running.'],
|
|
2147
|
+
});
|
|
2148
|
+
}
|
|
2149
|
+
return 'HANDLED';
|
|
2150
|
+
}
|
|
2151
|
+
if (ctx.agent && typeof ctx.agent.cancelResearch === 'function') {
|
|
2152
|
+
ctx.agent.cancelResearch();
|
|
2153
|
+
}
|
|
2154
|
+
return 'HANDLED';
|
|
2155
|
+
},
|
|
2156
|
+
|
|
2157
|
+
'/copy': async (ctx) => {
|
|
2158
|
+
if (typeof ctx.copyQueryDetails === 'function') {
|
|
2159
|
+
const reference = [ctx.sub, ctx.extra].filter(Boolean).join(' ');
|
|
2160
|
+
ctx.copyQueryDetails(reference);
|
|
2161
|
+
return 'HANDLED';
|
|
2162
|
+
}
|
|
2163
|
+
const { getCopyableContent, copyToClipboard, parseDetailsRequest } = await import('./queryDetails.js');
|
|
2164
|
+
const reference = [ctx.sub, ctx.extra].filter(Boolean).join(' ');
|
|
2165
|
+
const request = parseDetailsRequest(reference);
|
|
2166
|
+
const details = ctx.agent?.lastQueryDetails || null;
|
|
2167
|
+
if (!details) {
|
|
2168
|
+
printRichCard({
|
|
2169
|
+
title: 'No Diagnostics Recorded',
|
|
2170
|
+
status: 'warning',
|
|
2171
|
+
lines: ['No completed model queries are available to copy.'],
|
|
2172
|
+
});
|
|
2173
|
+
return 'HANDLED';
|
|
2174
|
+
}
|
|
2175
|
+
const copyable = getCopyableContent(details, request);
|
|
2176
|
+
if (!copyable?.text) {
|
|
2177
|
+
printRichCard({
|
|
2178
|
+
title: 'Nothing to Copy',
|
|
2179
|
+
status: 'warning',
|
|
2180
|
+
lines: ['The requested query section is empty.'],
|
|
2181
|
+
});
|
|
2182
|
+
return 'HANDLED';
|
|
2183
|
+
}
|
|
2184
|
+
const copied = copyToClipboard(copyable.text);
|
|
2185
|
+
printRichCard({
|
|
2186
|
+
title: copied ? 'Copied to Clipboard' : 'Copy Content Ready',
|
|
2187
|
+
status: copied ? 'ok' : 'info',
|
|
2188
|
+
lines: [
|
|
2189
|
+
`• Target: ${copyable.title}`,
|
|
2190
|
+
`• Characters: ${copyable.text.length} chars (${copyable.text.split('\n').length} lines)`,
|
|
2191
|
+
...(copied ? ['• Status: Copied to system clipboard (Ready to paste with Ctrl+V / Cmd+V)'] : []),
|
|
2192
|
+
],
|
|
2193
|
+
});
|
|
2194
|
+
return 'HANDLED';
|
|
2195
|
+
},
|
|
2196
|
+
|
|
1638
2197
|
'/ads': async (ctx) => {
|
|
1639
2198
|
ctx.printAd();
|
|
1640
2199
|
return 'HANDLED';
|
|
@@ -1643,6 +2202,8 @@ export const handlers = {
|
|
|
1643
2202
|
|
|
1644
2203
|
const ALIASES = {
|
|
1645
2204
|
'/quit': '/exit', '/q': '/exit',
|
|
2205
|
+
'/abort': '/cancel',
|
|
2206
|
+
'/cp': '/copy', '/clip': '/copy', '/clipboard': '/copy',
|
|
1646
2207
|
'/?': '/help', '/commands': '/help', '/menu': '/help',
|
|
1647
2208
|
'/hist': '/history', '/h': '/history',
|
|
1648
2209
|
'/sess': '/session', '/sessions': '/session',
|
|
@@ -1650,6 +2211,7 @@ const ALIASES = {
|
|
|
1650
2211
|
'/detail': '/details', '/d': '/details',
|
|
1651
2212
|
'/language': '/lang',
|
|
1652
2213
|
'/themes': '/theme',
|
|
2214
|
+
'/suggest': '/suggestion', '/suggestions': '/suggestion',
|
|
1653
2215
|
'/exec': '/tool', '/call': '/tool',
|
|
1654
2216
|
'/toollist': '/tools',
|
|
1655
2217
|
'/metrics': '/stats', '/catalog': '/stats',
|
|
@@ -1690,5 +2252,9 @@ export async function routeCommand(rawInput, ctx) {
|
|
|
1690
2252
|
return 'UNKNOWN';
|
|
1691
2253
|
}
|
|
1692
2254
|
|
|
2255
|
+
if (sub.toLowerCase() === 'help' && rawCmd !== '/help') {
|
|
2256
|
+
return handlers['/help']({ ...ctx, sub: rawCmd, extra: '' });
|
|
2257
|
+
}
|
|
2258
|
+
|
|
1693
2259
|
return await handler({ ...ctx, sub, extra, parts });
|
|
1694
2260
|
}
|