kiku-agent 1.0.5 → 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 +46 -19
- package/package.json +11 -1
- package/server/agent/agentCore.js +164 -55
- package/server/agent/cli.js +1315 -701
- package/server/agent/commandRegistry.js +436 -74
- package/server/agent/commands.js +640 -68
- package/server/agent/config.js +64 -48
- package/server/agent/doctor.js +7 -6
- package/server/agent/llmClient.js +26 -19
- package/server/agent/mcpClient.js +436 -364
- package/server/agent/modelManager.js +30 -13
- 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/agent/youtubeAuth.js +1 -2
- 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
|
|
|
@@ -1013,7 +1198,7 @@ export const handlers = {
|
|
|
1013
1198
|
|
|
1014
1199
|
if (!targetProvider) {
|
|
1015
1200
|
console.log(`\n ${C.amber}Usage: /provider use <provider_name> [model_id]${C.reset}`);
|
|
1016
|
-
console.log(` ${C.dim}Example: /provider use groq
|
|
1201
|
+
console.log(` ${C.dim}Example: /provider use groq openai/gpt-oss-120b${C.reset}\n`);
|
|
1017
1202
|
return 'HANDLED';
|
|
1018
1203
|
}
|
|
1019
1204
|
|
|
@@ -1021,10 +1206,16 @@ export const handlers = {
|
|
|
1021
1206
|
const options = targetModel ? { model: targetModel } : {};
|
|
1022
1207
|
const info = ctx.client.switchProvider(targetProvider, options);
|
|
1023
1208
|
if (ctx.agent?.client) ctx.agent.client.switchProvider(targetProvider, options);
|
|
1024
|
-
if (ctx.modelManager) {
|
|
1025
|
-
ctx.modelManager.client = ctx.client;
|
|
1026
|
-
ctx.modelManager.requestedModel = info.model;
|
|
1027
|
-
|
|
1209
|
+
if (ctx.modelManager) {
|
|
1210
|
+
ctx.modelManager.client = ctx.client;
|
|
1211
|
+
ctx.modelManager.requestedModel = info.model;
|
|
1212
|
+
const resolution = await ctx.modelManager.resolve({ requestedModel: info.model });
|
|
1213
|
+
if (resolution.connected && resolution.usedFallback) {
|
|
1214
|
+
console.log(` ${C.amber}• Requested model unavailable; using ${resolution.selectedModel}${C.reset}`);
|
|
1215
|
+
} else if (!resolution.connected) {
|
|
1216
|
+
console.log(` ${C.amber}• Provider is not ready: ${resolution.error}${C.reset}`);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1028
1219
|
ctx.baseUrl = info.baseUrl;
|
|
1029
1220
|
|
|
1030
1221
|
console.log(`\n ${C.emerald}[OK] Switched provider to ${C.bold}${info.providerName}${C.reset} (${info.provider})`);
|
|
@@ -1288,7 +1479,11 @@ export const handlers = {
|
|
|
1288
1479
|
const mcpManager = ctx.agent?.mcpManager;
|
|
1289
1480
|
|
|
1290
1481
|
if (!mcpManager) {
|
|
1291
|
-
|
|
1482
|
+
printRichCard({
|
|
1483
|
+
title: 'MCP Error',
|
|
1484
|
+
status: 'error',
|
|
1485
|
+
lines: ['MCP Client Manager is unavailable.'],
|
|
1486
|
+
});
|
|
1292
1487
|
return 'HANDLED';
|
|
1293
1488
|
}
|
|
1294
1489
|
|
|
@@ -1300,9 +1495,10 @@ export const handlers = {
|
|
|
1300
1495
|
} else {
|
|
1301
1496
|
for (const [idx, t] of tools.entries()) {
|
|
1302
1497
|
const f = t.function;
|
|
1303
|
-
const
|
|
1304
|
-
|
|
1305
|
-
|
|
1498
|
+
const toolName = sanitizeTerminalText(f.name, 128) || 'unknown_tool';
|
|
1499
|
+
const params = Object.keys(f.parameters?.properties || {}).map(param => sanitizeTerminalText(param, 80)).join(', ');
|
|
1500
|
+
lines.push(`${C.gold}${idx + 1}.${C.reset} ${C.bold}${C.cyberCyan}${toolName}${C.reset}${C.slate}(${params})${C.reset}`);
|
|
1501
|
+
const desc = sanitizeTerminalText(f.description || 'No description provided');
|
|
1306
1502
|
const wrapped = wrapText(desc, Math.max(30, getFullWidth() - 8));
|
|
1307
1503
|
for (const dLine of wrapped) {
|
|
1308
1504
|
lines.push(` ${C.dim}${dLine}${C.reset}`);
|
|
@@ -1316,7 +1512,7 @@ export const handlers = {
|
|
|
1316
1512
|
return 'HANDLED';
|
|
1317
1513
|
}
|
|
1318
1514
|
|
|
1319
|
-
if (action === 'connect') {
|
|
1515
|
+
if (action === 'connect' || action === 'add') {
|
|
1320
1516
|
const parts = (ctx.parts || []).slice(2);
|
|
1321
1517
|
const name = parts[0];
|
|
1322
1518
|
const targetUrlOrType = parts[1];
|
|
@@ -1325,11 +1521,11 @@ export const handlers = {
|
|
|
1325
1521
|
printSolidPanel({
|
|
1326
1522
|
title: 'MCP Connect Usage',
|
|
1327
1523
|
lines: [
|
|
1328
|
-
`${C.bold}Connect
|
|
1524
|
+
`${C.bold}Connect & Save Remote HTTP MCP:${C.reset}`,
|
|
1329
1525
|
` ${C.cyberCyan}/mcp connect <name> <url>${C.reset}`,
|
|
1330
1526
|
` ${C.dim}Example: /mcp connect kiku https://jujingyi-kikuuu.vercel.app/mcp${C.reset}`,
|
|
1331
1527
|
'',
|
|
1332
|
-
`${C.bold}Connect
|
|
1528
|
+
`${C.bold}Connect & Save Stdio MCP Process:${C.reset}`,
|
|
1333
1529
|
` ${C.cyberCyan}/mcp connect <name> stdio <command> [args...]${C.reset}`,
|
|
1334
1530
|
` ${C.dim}Example: /mcp connect local-kiku stdio node ./server/mcp/stdio.js${C.reset}`,
|
|
1335
1531
|
]
|
|
@@ -1357,11 +1553,12 @@ export const handlers = {
|
|
|
1357
1553
|
const res = await ctx.agent.connectMcp(name, serverConfig);
|
|
1358
1554
|
|
|
1359
1555
|
runner.done({
|
|
1360
|
-
title: `MCP Server Connected • "${name}"`,
|
|
1556
|
+
title: `MCP Server Connected & Saved • "${name}"`,
|
|
1361
1557
|
lines: [
|
|
1362
1558
|
`${C.gold}Server:${C.reset} ${C.bold}${C.white}${name}${C.reset} ${C.dim}(${serverConfig.type})${C.reset}`,
|
|
1363
1559
|
`${C.gold}Target:${C.reset} ${C.cyberCyan}${targetUrlOrType}${C.reset}`,
|
|
1364
1560
|
`${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}`,
|
|
1561
|
+
`${C.gold}Persisted:${C.reset} ${C.emerald}Saved to configuration (auto-reconnects on start)${C.reset}`,
|
|
1365
1562
|
`${C.gold}Integration:${C.reset} ${C.dim}Tools prefixed with ${C.cyberCyan}mcp_${name}_*${C.dim} and auto-routed to agent${C.reset}`,
|
|
1366
1563
|
'',
|
|
1367
1564
|
`${C.dim}Tip: Run /mcp tools to inspect all registered tools or /mcp status for overview.${C.reset}`
|
|
@@ -1375,7 +1572,81 @@ export const handlers = {
|
|
|
1375
1572
|
return 'HANDLED';
|
|
1376
1573
|
}
|
|
1377
1574
|
|
|
1378
|
-
if (action === '
|
|
1575
|
+
if (action === 'save') {
|
|
1576
|
+
const target = (ctx.extra || 'global').toLowerCase().trim();
|
|
1577
|
+
const saveRes = await ctx.agent.saveMcpConfig(target);
|
|
1578
|
+
if (saveRes.success) {
|
|
1579
|
+
printRichCard({
|
|
1580
|
+
title: 'MCP Configuration Saved',
|
|
1581
|
+
status: 'ok',
|
|
1582
|
+
lines: [
|
|
1583
|
+
`• Target: ${target === 'workspace' ? 'Workspace (.mcp.json)' : 'Global config (mcp_servers.json)'}`,
|
|
1584
|
+
`• File: ${saveRes.filePath}`,
|
|
1585
|
+
`• Total servers persisted: ${saveRes.count}`,
|
|
1586
|
+
],
|
|
1587
|
+
});
|
|
1588
|
+
} else {
|
|
1589
|
+
printRichCard({
|
|
1590
|
+
title: 'MCP Save Failed',
|
|
1591
|
+
status: 'error',
|
|
1592
|
+
lines: [saveRes.error || 'Failed to write MCP config'],
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
return 'HANDLED';
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
if (action === 'reload' || action === 'reconnect') {
|
|
1599
|
+
const target = (ctx.extra || '').trim();
|
|
1600
|
+
printRichCard({
|
|
1601
|
+
title: 'Reloading MCP Configuration',
|
|
1602
|
+
status: 'autostart',
|
|
1603
|
+
lines: [`Reconnecting saved MCP servers...`],
|
|
1604
|
+
});
|
|
1605
|
+
try {
|
|
1606
|
+
await ctx.agent.reconnectMcp(target || 'all');
|
|
1607
|
+
const active = ctx.agent.mcpManager.servers.size;
|
|
1608
|
+
printRichCard({
|
|
1609
|
+
title: 'MCP Servers Reconnected',
|
|
1610
|
+
status: 'ok',
|
|
1611
|
+
lines: [
|
|
1612
|
+
`Active live servers: ${active}`,
|
|
1613
|
+
`Total tools available: ${ctx.agent.mcpManager.getToolsSchema().length}`,
|
|
1614
|
+
],
|
|
1615
|
+
});
|
|
1616
|
+
} catch (err) {
|
|
1617
|
+
printRichCard({
|
|
1618
|
+
title: 'MCP Reconnect Error',
|
|
1619
|
+
status: 'error',
|
|
1620
|
+
lines: [err.message],
|
|
1621
|
+
});
|
|
1622
|
+
}
|
|
1623
|
+
return 'HANDLED';
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
if (action === 'remove' || action === 'rm' || action === 'del') {
|
|
1627
|
+
const name = (ctx.extra || '').trim();
|
|
1628
|
+
if (!name) {
|
|
1629
|
+
console.log(`\n ${C.amber}Usage: /mcp remove <server_name>${C.reset}\n`);
|
|
1630
|
+
return 'HANDLED';
|
|
1631
|
+
}
|
|
1632
|
+
const success = await ctx.agent.removeMcp(name);
|
|
1633
|
+
if (success) {
|
|
1634
|
+
printRichCard({
|
|
1635
|
+
title: 'MCP Server Removed',
|
|
1636
|
+
status: 'ok',
|
|
1637
|
+
lines: [`Removed "${name}" from saved configuration and disconnected.`],
|
|
1638
|
+
});
|
|
1639
|
+
} else {
|
|
1640
|
+
printRichCard({
|
|
1641
|
+
title: 'MCP Server Not Found',
|
|
1642
|
+
status: 'warning',
|
|
1643
|
+
lines: [`Server "${name}" was not found in saved configuration.`],
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
return 'HANDLED';
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
if (action === 'disconnect') {
|
|
1379
1650
|
const name = (ctx.extra || '').trim();
|
|
1380
1651
|
if (!name) {
|
|
1381
1652
|
console.log(`\n ${C.amber}Usage: /mcp disconnect <server_name>${C.reset}\n`);
|
|
@@ -1383,45 +1654,81 @@ export const handlers = {
|
|
|
1383
1654
|
}
|
|
1384
1655
|
const success = await ctx.agent.disconnectMcp(name);
|
|
1385
1656
|
if (success) {
|
|
1386
|
-
|
|
1657
|
+
printRichCard({
|
|
1658
|
+
title: 'MCP Server Disconnected',
|
|
1659
|
+
status: 'info',
|
|
1660
|
+
lines: [
|
|
1661
|
+
`Disconnected session for "${name}".`,
|
|
1662
|
+
`Config remains saved. Run /mcp reconnect ${name} to reconnect.`,
|
|
1663
|
+
],
|
|
1664
|
+
});
|
|
1387
1665
|
} else {
|
|
1388
|
-
|
|
1666
|
+
printRichCard({
|
|
1667
|
+
title: 'MCP Server Not Found',
|
|
1668
|
+
status: 'warning',
|
|
1669
|
+
lines: [`MCP server "${name}" is not currently connected.`],
|
|
1670
|
+
});
|
|
1389
1671
|
}
|
|
1390
1672
|
return 'HANDLED';
|
|
1391
1673
|
}
|
|
1392
1674
|
|
|
1393
1675
|
const status = mcpManager.getStatus();
|
|
1394
1676
|
const lines = [
|
|
1395
|
-
`${C.gold}Active Servers:${C.reset} ${C.cyberCyan}${status.activeServersCount}${C.reset} • ${C.gold}Total
|
|
1677
|
+
`${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
1678
|
''
|
|
1397
1679
|
];
|
|
1398
1680
|
|
|
1399
1681
|
if (status.servers.length > 0) {
|
|
1400
|
-
lines.push(`${C.bold}${C.lavender}
|
|
1682
|
+
lines.push(`${C.bold}${C.lavender}Configured MCP Servers:${C.reset}`);
|
|
1401
1683
|
for (const s of status.servers) {
|
|
1402
|
-
|
|
1684
|
+
const serverName = sanitizeTerminalText(s.name, 128) || 'unnamed';
|
|
1685
|
+
const serverType = sanitizeTerminalText(s.type, 80) || 'unknown';
|
|
1686
|
+
if (s.status === 'connected') {
|
|
1687
|
+
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`);
|
|
1688
|
+
} else if (s.status === 'saved_offline') {
|
|
1689
|
+
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}`);
|
|
1690
|
+
} else {
|
|
1691
|
+
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}`);
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
const connectedTools = mcpManager.getToolsSchema();
|
|
1696
|
+
if (connectedTools.length > 0) {
|
|
1697
|
+
lines.push('', `${C.bold}${C.lavender}Available MCP Tool Commands (${connectedTools.length}):${C.reset}`);
|
|
1698
|
+
const commandList = connectedTools
|
|
1699
|
+
.map(tool => `@${sanitizeTerminalText(tool.function?.name, 128) || 'unknown_tool'}`)
|
|
1700
|
+
.join(' • ');
|
|
1701
|
+
for (const commandLine of wrapText(commandList, Math.max(30, getFullWidth() - 8))) {
|
|
1702
|
+
lines.push(` ${C.cyberCyan}${commandLine}${C.reset}`);
|
|
1703
|
+
}
|
|
1704
|
+
lines.push(`${C.dim}Use /mcp tools for parameters and full descriptions.${C.reset}`);
|
|
1403
1705
|
}
|
|
1404
1706
|
} else {
|
|
1405
|
-
lines.push(`${C.dim}No
|
|
1406
|
-
lines.push(`Connect with: ${C.cyberCyan}/mcp connect <name> <url>${C.reset}`);
|
|
1707
|
+
lines.push(`${C.dim}No MCP servers configured.${C.reset}`);
|
|
1708
|
+
lines.push(`Connect and save with: ${C.cyberCyan}/mcp connect <name> <url>${C.reset}`);
|
|
1407
1709
|
}
|
|
1408
1710
|
|
|
1711
|
+
lines.push('', `${C.dim}Commands: /mcp connect <name> <url> | /mcp reconnect [all] | /mcp save | /mcp remove <name> | /mcp tools${C.reset}`);
|
|
1712
|
+
|
|
1409
1713
|
printSolidPanel({
|
|
1410
|
-
title: `Model Context Protocol (MCP)
|
|
1714
|
+
title: `Model Context Protocol (MCP) Manager • ${status.activeServersCount} Active / ${status.totalConfiguredCount} Saved`,
|
|
1411
1715
|
lines,
|
|
1412
|
-
headerBg: '\x1b[48;2;48;54;70m', bg: '\x1b[48;2;36;40;52m'
|
|
1413
1716
|
});
|
|
1414
1717
|
return 'HANDLED';
|
|
1415
1718
|
},
|
|
1416
1719
|
|
|
1417
1720
|
'/ping': async (ctx) => {
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1721
|
+
try {
|
|
1722
|
+
const start = Date.now();
|
|
1723
|
+
const check = await ctx.client.checkConnection();
|
|
1724
|
+
const ms = Date.now() - start;
|
|
1725
|
+
if (check.connected) {
|
|
1726
|
+
console.log(`\n ${C.emerald}● Connected${C.reset} ${C.dim}(${ms}ms)${C.reset} — ${C.white}${check.activeModel || check.model || 'Ready'}${C.reset}\n`);
|
|
1727
|
+
} else {
|
|
1728
|
+
console.log(`\n ${C.red}● Disconnected${C.reset} ${C.dim}(${ms}ms)${C.reset} — ${C.amber}${check.error || 'Server unreachable'}${C.reset}\n`);
|
|
1729
|
+
}
|
|
1730
|
+
} catch (err) {
|
|
1731
|
+
console.log(`\n ${C.red}[PING ERROR] ${err.message || String(err)}${C.reset}\n`);
|
|
1425
1732
|
}
|
|
1426
1733
|
return 'HANDLED';
|
|
1427
1734
|
},
|
|
@@ -1444,15 +1751,19 @@ export const handlers = {
|
|
|
1444
1751
|
},
|
|
1445
1752
|
|
|
1446
1753
|
'/doctor': async (ctx) => {
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1754
|
+
try {
|
|
1755
|
+
const { runDoctor } = await import('./doctor.js');
|
|
1756
|
+
const report = await runDoctor({ client: ctx.client });
|
|
1757
|
+
printSolidPanel({
|
|
1758
|
+
title: `Kiku Doctor • ${report.ok ? 'READY' : 'ACTION REQUIRED'}`,
|
|
1759
|
+
lines: report.checks.map(check => {
|
|
1760
|
+
const marker = check.ok ? '[OK]' : check.required ? '[FAIL]' : '[OPTIONAL]';
|
|
1761
|
+
return `${marker} ${check.id}: ${check.detail}`;
|
|
1762
|
+
}),
|
|
1763
|
+
});
|
|
1764
|
+
} catch (err) {
|
|
1765
|
+
console.log(`\n ${C.red}[DOCTOR ERROR] ${err.message || String(err)}${C.reset}\n`);
|
|
1766
|
+
}
|
|
1456
1767
|
return 'HANDLED';
|
|
1457
1768
|
},
|
|
1458
1769
|
|
|
@@ -1460,7 +1771,8 @@ export const handlers = {
|
|
|
1460
1771
|
printSolidPanel({
|
|
1461
1772
|
title: 'Version',
|
|
1462
1773
|
lines: [
|
|
1463
|
-
`${C.gold}
|
|
1774
|
+
`${C.gold}Package:${C.reset} ${C.white}${PACKAGE_NAME}${C.reset}`,
|
|
1775
|
+
`${C.gold}Version:${C.reset} ${C.white}${PACKAGE_VERSION}${C.reset}`,
|
|
1464
1776
|
`${C.gold}Node:${C.reset} ${C.white}${process.version}${C.reset}`,
|
|
1465
1777
|
`${C.gold}Platform:${C.reset} ${C.white}${process.platform} ${process.arch}${C.reset}`,
|
|
1466
1778
|
],
|
|
@@ -1468,6 +1780,186 @@ export const handlers = {
|
|
|
1468
1780
|
return 'HANDLED';
|
|
1469
1781
|
},
|
|
1470
1782
|
|
|
1783
|
+
'/update': async (ctx) => {
|
|
1784
|
+
const manager = ctx.updateManager || new UpdateManager();
|
|
1785
|
+
const action = (ctx.sub || 'check').toLowerCase();
|
|
1786
|
+
const parts = ctx.parts || [];
|
|
1787
|
+
const allowMajor = parts.includes('--allow-major');
|
|
1788
|
+
const allowDeprecated = parts.includes('--allow-deprecated');
|
|
1789
|
+
const positional = parts.slice(2).filter(part => !part.startsWith('--'));
|
|
1790
|
+
const flags = parts.slice(2).filter(part => part.startsWith('--'));
|
|
1791
|
+
|
|
1792
|
+
const printUpdateHelp = () => printSolidPanel({
|
|
1793
|
+
title: 'Kiku Agent Update',
|
|
1794
|
+
lines: [
|
|
1795
|
+
`${C.bold}${C.white}/update${C.reset} ${C.dim}or /update check — check the configured channel${C.reset}`,
|
|
1796
|
+
`${C.bold}${C.white}/update install [latest|next|canary|x.y.z]${C.reset}`,
|
|
1797
|
+
`${C.bold}${C.white}/update channel [latest|next|canary]${C.reset}`,
|
|
1798
|
+
`${C.bold}${C.white}/update rollback${C.reset} ${C.dim}— reinstall the previously recorded version${C.reset}`,
|
|
1799
|
+
'',
|
|
1800
|
+
`${C.dim}Major releases require --allow-major. Deprecated releases require --allow-deprecated.${C.reset}`,
|
|
1801
|
+
],
|
|
1802
|
+
});
|
|
1803
|
+
|
|
1804
|
+
const statusLines = status => {
|
|
1805
|
+
const changeLabel = status.change === 'upgrade'
|
|
1806
|
+
? `${C.emerald}UPDATE AVAILABLE${C.reset}`
|
|
1807
|
+
: status.change === 'downgrade'
|
|
1808
|
+
? `${C.amber}OLDER RELEASE${C.reset}`
|
|
1809
|
+
: `${C.emerald}UP TO DATE${C.reset}`;
|
|
1810
|
+
const lines = [
|
|
1811
|
+
`${C.gold}Current:${C.reset} ${C.white}${status.currentVersion}${C.reset}`,
|
|
1812
|
+
`${C.gold}Target:${C.reset} ${C.white}${status.targetVersion}${C.reset} ${C.dim}(${status.target})${C.reset}`,
|
|
1813
|
+
`${C.gold}Status:${C.reset} ${changeLabel}`,
|
|
1814
|
+
`${C.gold}Installation:${C.reset} ${C.white}${status.installMode.label}${C.reset}`,
|
|
1815
|
+
`${C.gold}Node:${C.reset} ${status.nodeCompatible ? C.emerald + 'Compatible' : C.red + 'Incompatible'}${C.reset}${status.nodeRange ? ` ${C.dim}(requires ${status.nodeRange})${C.reset}` : ''}`,
|
|
1816
|
+
`${C.gold}Integrity:${C.reset} ${status.integrity ? C.emerald + 'Published with npm integrity metadata' : C.amber + 'Not reported'}${C.reset}`,
|
|
1817
|
+
];
|
|
1818
|
+
if (status.majorChange) lines.push(`${C.amber}[MAJOR] This release crosses a major-version boundary.${C.reset}`);
|
|
1819
|
+
if (status.deprecated) lines.push(`${C.red}[DEPRECATED] ${status.deprecated}${C.reset}`);
|
|
1820
|
+
lines.push('', `${C.bold}${C.lavender}What's new:${C.reset}`);
|
|
1821
|
+
if (status.releaseNotes.length > 0) {
|
|
1822
|
+
for (const note of status.releaseNotes) lines.push(` ${C.sakura}•${C.reset} ${C.white}${note}${C.reset}`);
|
|
1823
|
+
} else {
|
|
1824
|
+
lines.push(` ${C.dim}No release summary was published for this version.${C.reset}`);
|
|
1825
|
+
}
|
|
1826
|
+
if (status.releaseUrl) lines.push('', `${C.dim}Release: ${status.releaseUrl}${C.reset}`);
|
|
1827
|
+
return lines;
|
|
1828
|
+
};
|
|
1829
|
+
|
|
1830
|
+
try {
|
|
1831
|
+
const allowedFlags = action === 'install'
|
|
1832
|
+
? ['--allow-major', '--allow-deprecated']
|
|
1833
|
+
: action === 'rollback'
|
|
1834
|
+
? ['--allow-deprecated']
|
|
1835
|
+
: [];
|
|
1836
|
+
if (flags.some(flag => !allowedFlags.includes(flag))) {
|
|
1837
|
+
throw new UpdateError('Unknown option. Run /update help for supported arguments.', 'INVALID_OPTION');
|
|
1838
|
+
}
|
|
1839
|
+
if (positional.length > 1 || ((action === 'rollback' || action === 'help') && positional.length > 0)) {
|
|
1840
|
+
throw new UpdateError('Too many update arguments. Run /update help for usage.', 'INVALID_ARGUMENTS');
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
if (action === 'help') {
|
|
1844
|
+
printUpdateHelp();
|
|
1845
|
+
return 'HANDLED';
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
if (action === 'channel') {
|
|
1849
|
+
const requested = positional[0];
|
|
1850
|
+
if (!requested) {
|
|
1851
|
+
const channel = await manager.getChannel();
|
|
1852
|
+
printSolidPanel({
|
|
1853
|
+
title: 'Update Channel',
|
|
1854
|
+
lines: [
|
|
1855
|
+
`${C.gold}Current:${C.reset} ${C.white}${channel}${C.reset}`,
|
|
1856
|
+
`${C.dim}Available: ${UPDATE_CHANNELS.join(', ')}${C.reset}`,
|
|
1857
|
+
],
|
|
1858
|
+
});
|
|
1859
|
+
return 'HANDLED';
|
|
1860
|
+
}
|
|
1861
|
+
const channel = await manager.setChannel(requested);
|
|
1862
|
+
printSolidPanel({
|
|
1863
|
+
title: 'Update Channel Changed',
|
|
1864
|
+
lines: [
|
|
1865
|
+
`${C.emerald}[OK] Future /update checks will use ${C.bold}${channel}${C.reset}${C.emerald}.${C.reset}`,
|
|
1866
|
+
`${C.dim}This changes only the release channel; it does not install anything.${C.reset}`,
|
|
1867
|
+
],
|
|
1868
|
+
});
|
|
1869
|
+
return 'HANDLED';
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
if (action === 'install') {
|
|
1873
|
+
const target = positional[0];
|
|
1874
|
+
console.log(`\n ${C.cyberCyan}[UPDATE] Resolving and validating the requested npm release...${C.reset}\n`);
|
|
1875
|
+
const result = await manager.install(target, {
|
|
1876
|
+
allowMajor,
|
|
1877
|
+
allowDeprecated,
|
|
1878
|
+
onProgress: message => { if (message) console.log(` ${C.dim}${message}${C.reset}`); },
|
|
1879
|
+
});
|
|
1880
|
+
if (result.outcome === 'manual') {
|
|
1881
|
+
printSolidPanel({
|
|
1882
|
+
title: 'Manual Update Required',
|
|
1883
|
+
lines: [
|
|
1884
|
+
...statusLines(result),
|
|
1885
|
+
'',
|
|
1886
|
+
`${C.amber}This installation type is not modified automatically.${C.reset}`,
|
|
1887
|
+
`${C.gold}Run:${C.reset} ${C.white}${result.command}${C.reset}`,
|
|
1888
|
+
],
|
|
1889
|
+
});
|
|
1890
|
+
return 'HANDLED';
|
|
1891
|
+
}
|
|
1892
|
+
if (result.outcome === 'current') {
|
|
1893
|
+
printSolidPanel({ title: 'Kiku Agent Is Up to Date', lines: statusLines(result) });
|
|
1894
|
+
return 'HANDLED';
|
|
1895
|
+
}
|
|
1896
|
+
printSolidPanel({
|
|
1897
|
+
title: 'Update Installed Successfully',
|
|
1898
|
+
lines: [
|
|
1899
|
+
`${C.emerald}[OK] ${PACKAGE_NAME} ${result.installedVersion} was installed and verified.${C.reset}`,
|
|
1900
|
+
...(!result.historyRecorded ? [`${C.amber}[WARNING] Rollback history could not be saved.${C.reset}`] : []),
|
|
1901
|
+
`${C.dim}This session will close. Start kiku-agent again to load the new code.${C.reset}`,
|
|
1902
|
+
`${C.dim}Restart MCP hosts to load the matching kiku-mcp server version.${C.reset}`,
|
|
1903
|
+
],
|
|
1904
|
+
});
|
|
1905
|
+
try { await ctx.agent?.endSession?.(); } catch {}
|
|
1906
|
+
return 'EXIT';
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
if (action === 'rollback') {
|
|
1910
|
+
console.log(`\n ${C.cyberCyan}[UPDATE] Resolving the previously installed version...${C.reset}\n`);
|
|
1911
|
+
const result = await manager.rollback({ allowDeprecated });
|
|
1912
|
+
if (result.outcome === 'manual') {
|
|
1913
|
+
printSolidPanel({
|
|
1914
|
+
title: 'Manual Rollback Required',
|
|
1915
|
+
lines: [
|
|
1916
|
+
`${C.amber}Run the following command outside Kiku Agent:${C.reset}`,
|
|
1917
|
+
`${C.white}${result.command}${C.reset}`,
|
|
1918
|
+
],
|
|
1919
|
+
});
|
|
1920
|
+
return 'HANDLED';
|
|
1921
|
+
}
|
|
1922
|
+
if (result.outcome === 'current') {
|
|
1923
|
+
printSolidPanel({ title: 'Rollback Not Needed', lines: statusLines(result) });
|
|
1924
|
+
return 'HANDLED';
|
|
1925
|
+
}
|
|
1926
|
+
printSolidPanel({
|
|
1927
|
+
title: 'Rollback Installed Successfully',
|
|
1928
|
+
lines: [
|
|
1929
|
+
`${C.emerald}[OK] Restored ${PACKAGE_NAME} ${result.installedVersion}.${C.reset}`,
|
|
1930
|
+
...(!result.historyRecorded ? [`${C.amber}[WARNING] Rollback history could not be saved.${C.reset}`] : []),
|
|
1931
|
+
`${C.dim}This session will close. Restart Kiku Agent and any MCP host.${C.reset}`,
|
|
1932
|
+
],
|
|
1933
|
+
});
|
|
1934
|
+
try { await ctx.agent?.endSession?.(); } catch {}
|
|
1935
|
+
return 'EXIT';
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
if (action === 'check' || action === 'status') {
|
|
1939
|
+
const status = await manager.check(positional[0]);
|
|
1940
|
+
const lines = statusLines(status);
|
|
1941
|
+
if (status.updateAvailable) {
|
|
1942
|
+
lines.push('', `${C.cyberCyan}Run /update install${status.target === 'latest' ? '' : ` ${status.target}`} to update.${C.reset}`);
|
|
1943
|
+
}
|
|
1944
|
+
printSolidPanel({ title: 'Kiku Agent Update', lines });
|
|
1945
|
+
return 'HANDLED';
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
printUpdateHelp();
|
|
1949
|
+
return 'HANDLED';
|
|
1950
|
+
} catch (error) {
|
|
1951
|
+
const safeMessage = error instanceof UpdateError ? error.message : 'The update operation failed unexpectedly.';
|
|
1952
|
+
printSolidPanel({
|
|
1953
|
+
title: 'Update Failed',
|
|
1954
|
+
lines: [
|
|
1955
|
+
`${C.red}[${error.code || 'UPDATE_ERROR'}] ${safeMessage}${C.reset}`,
|
|
1956
|
+
`${C.dim}No session, memory, or environment files were changed.${C.reset}`,
|
|
1957
|
+
],
|
|
1958
|
+
});
|
|
1959
|
+
return 'HANDLED';
|
|
1960
|
+
}
|
|
1961
|
+
},
|
|
1962
|
+
|
|
1471
1963
|
'/system': async () => {
|
|
1472
1964
|
const os = await import('node:os');
|
|
1473
1965
|
const mem = process.memoryUsage();
|
|
@@ -1564,11 +2056,16 @@ export const handlers = {
|
|
|
1564
2056
|
'/login': async (ctx) => {
|
|
1565
2057
|
console.log(`\n ${C.cyberCyan}YouTube Login${C.reset}\n`);
|
|
1566
2058
|
resetCookieCheck();
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
2059
|
+
ctx.setIsProcessing?.(true, 'YouTube Login');
|
|
2060
|
+
try {
|
|
2061
|
+
const result = await ensureYouTubeCookies({ timeout: 180000 });
|
|
2062
|
+
if (result.ok) {
|
|
2063
|
+
console.log(` ${C.emerald}[OK] Session active (${result.source}).${C.reset}\n`);
|
|
2064
|
+
} else {
|
|
2065
|
+
console.log(` ${C.red}[ERROR] Not available: ${result.reason}${C.reset}\n`);
|
|
2066
|
+
}
|
|
2067
|
+
} finally {
|
|
2068
|
+
ctx.setIsProcessing?.(false);
|
|
1572
2069
|
}
|
|
1573
2070
|
return 'HANDLED';
|
|
1574
2071
|
},
|
|
@@ -1612,19 +2109,23 @@ export const handlers = {
|
|
|
1612
2109
|
},
|
|
1613
2110
|
|
|
1614
2111
|
'/stats': async () => {
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
2112
|
+
try {
|
|
2113
|
+
const result = await executeTool('get_system_stats', {});
|
|
2114
|
+
const s = result.stats || {};
|
|
2115
|
+
printSolidPanel({
|
|
2116
|
+
title: `Database Overview`,
|
|
2117
|
+
lines: [
|
|
2118
|
+
`${C.gold}Works:${C.reset} ${C.cyberCyan}${s.totalWorks || 0}${C.reset}`,
|
|
2119
|
+
`${C.gold}Songs:${C.reset} ${C.cyberCyan}${s.totalUniqueSongs || 0}${C.reset}`,
|
|
2120
|
+
`${C.gold}Brands:${C.reset} ${C.cyberCyan}${s.brandEndorsements || 0}${C.reset}`,
|
|
2121
|
+
`${C.gold}Awards:${C.reset} ${C.cyberCyan}${s.totalAwards || 0}${C.reset}`,
|
|
2122
|
+
`${C.gold}Timeline:${C.reset} ${C.cyberCyan}${s.timelineMilestones || 0}${C.reset}`
|
|
2123
|
+
],
|
|
2124
|
+
headerBg: '\x1b[48;2;48;54;70m', bg: '\x1b[48;2;36;40;52m'
|
|
2125
|
+
});
|
|
2126
|
+
} catch (err) {
|
|
2127
|
+
console.log(`\n ${C.red}[STATS ERROR] ${err.message || String(err)}${C.reset}\n`);
|
|
2128
|
+
}
|
|
1628
2129
|
return 'HANDLED';
|
|
1629
2130
|
},
|
|
1630
2131
|
|
|
@@ -1635,6 +2136,70 @@ export const handlers = {
|
|
|
1635
2136
|
return 'HANDLED';
|
|
1636
2137
|
},
|
|
1637
2138
|
|
|
2139
|
+
'/cancel': async (ctx) => {
|
|
2140
|
+
if (typeof ctx.cancelActiveQuery === 'function') {
|
|
2141
|
+
const ok = ctx.cancelActiveQuery();
|
|
2142
|
+
if (ok) {
|
|
2143
|
+
printRichCard({
|
|
2144
|
+
title: 'Query Cancelled',
|
|
2145
|
+
status: 'cancel',
|
|
2146
|
+
lines: ['Active query and queued tasks were cancelled.'],
|
|
2147
|
+
});
|
|
2148
|
+
} else {
|
|
2149
|
+
printRichCard({
|
|
2150
|
+
title: 'No Active Query',
|
|
2151
|
+
status: 'info',
|
|
2152
|
+
lines: ['There is no query currently running.'],
|
|
2153
|
+
});
|
|
2154
|
+
}
|
|
2155
|
+
return 'HANDLED';
|
|
2156
|
+
}
|
|
2157
|
+
if (ctx.agent && typeof ctx.agent.cancelResearch === 'function') {
|
|
2158
|
+
ctx.agent.cancelResearch();
|
|
2159
|
+
}
|
|
2160
|
+
return 'HANDLED';
|
|
2161
|
+
},
|
|
2162
|
+
|
|
2163
|
+
'/copy': async (ctx) => {
|
|
2164
|
+
if (typeof ctx.copyQueryDetails === 'function') {
|
|
2165
|
+
const reference = [ctx.sub, ctx.extra].filter(Boolean).join(' ');
|
|
2166
|
+
ctx.copyQueryDetails(reference);
|
|
2167
|
+
return 'HANDLED';
|
|
2168
|
+
}
|
|
2169
|
+
const { getCopyableContent, copyToClipboard, parseDetailsRequest } = await import('./queryDetails.js');
|
|
2170
|
+
const reference = [ctx.sub, ctx.extra].filter(Boolean).join(' ');
|
|
2171
|
+
const request = parseDetailsRequest(reference);
|
|
2172
|
+
const details = ctx.agent?.lastQueryDetails || null;
|
|
2173
|
+
if (!details) {
|
|
2174
|
+
printRichCard({
|
|
2175
|
+
title: 'No Diagnostics Recorded',
|
|
2176
|
+
status: 'warning',
|
|
2177
|
+
lines: ['No completed model queries are available to copy.'],
|
|
2178
|
+
});
|
|
2179
|
+
return 'HANDLED';
|
|
2180
|
+
}
|
|
2181
|
+
const copyable = getCopyableContent(details, request);
|
|
2182
|
+
if (!copyable?.text) {
|
|
2183
|
+
printRichCard({
|
|
2184
|
+
title: 'Nothing to Copy',
|
|
2185
|
+
status: 'warning',
|
|
2186
|
+
lines: ['The requested query section is empty.'],
|
|
2187
|
+
});
|
|
2188
|
+
return 'HANDLED';
|
|
2189
|
+
}
|
|
2190
|
+
const copied = copyToClipboard(copyable.text);
|
|
2191
|
+
printRichCard({
|
|
2192
|
+
title: copied ? 'Copied to Clipboard' : 'Copy Content Ready',
|
|
2193
|
+
status: copied ? 'ok' : 'info',
|
|
2194
|
+
lines: [
|
|
2195
|
+
`• Target: ${copyable.title}`,
|
|
2196
|
+
`• Characters: ${copyable.text.length} chars (${copyable.text.split('\n').length} lines)`,
|
|
2197
|
+
...(copied ? ['• Status: Copied to system clipboard (Ready to paste with Ctrl+V / Cmd+V)'] : []),
|
|
2198
|
+
],
|
|
2199
|
+
});
|
|
2200
|
+
return 'HANDLED';
|
|
2201
|
+
},
|
|
2202
|
+
|
|
1638
2203
|
'/ads': async (ctx) => {
|
|
1639
2204
|
ctx.printAd();
|
|
1640
2205
|
return 'HANDLED';
|
|
@@ -1643,6 +2208,8 @@ export const handlers = {
|
|
|
1643
2208
|
|
|
1644
2209
|
const ALIASES = {
|
|
1645
2210
|
'/quit': '/exit', '/q': '/exit',
|
|
2211
|
+
'/abort': '/cancel',
|
|
2212
|
+
'/cp': '/copy', '/clip': '/copy', '/clipboard': '/copy',
|
|
1646
2213
|
'/?': '/help', '/commands': '/help', '/menu': '/help',
|
|
1647
2214
|
'/hist': '/history', '/h': '/history',
|
|
1648
2215
|
'/sess': '/session', '/sessions': '/session',
|
|
@@ -1650,6 +2217,7 @@ const ALIASES = {
|
|
|
1650
2217
|
'/detail': '/details', '/d': '/details',
|
|
1651
2218
|
'/language': '/lang',
|
|
1652
2219
|
'/themes': '/theme',
|
|
2220
|
+
'/suggest': '/suggestion', '/suggestions': '/suggestion',
|
|
1653
2221
|
'/exec': '/tool', '/call': '/tool',
|
|
1654
2222
|
'/toollist': '/tools',
|
|
1655
2223
|
'/metrics': '/stats', '/catalog': '/stats',
|
|
@@ -1690,5 +2258,9 @@ export async function routeCommand(rawInput, ctx) {
|
|
|
1690
2258
|
return 'UNKNOWN';
|
|
1691
2259
|
}
|
|
1692
2260
|
|
|
2261
|
+
if (sub.toLowerCase() === 'help' && rawCmd !== '/help') {
|
|
2262
|
+
return handlers['/help']({ ...ctx, sub: rawCmd, extra: '' });
|
|
2263
|
+
}
|
|
2264
|
+
|
|
1693
2265
|
return await handler({ ...ctx, sub, extra, parts });
|
|
1694
2266
|
}
|