kiku-agent 1.0.11 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/README.md +300 -495
  2. package/package.json +12 -11
  3. package/public/fonts/GoogleSans-Bold.ttf +0 -0
  4. package/public/fonts/GoogleSans-Bold.woff2 +0 -0
  5. package/public/fonts/GoogleSans-BoldItalic.ttf +0 -0
  6. package/public/fonts/GoogleSans-Italic.ttf +0 -0
  7. package/public/fonts/GoogleSans-Latin-400.woff2 +0 -0
  8. package/public/fonts/GoogleSans-Latin-500.woff2 +0 -0
  9. package/public/fonts/GoogleSans-Latin-600.woff2 +0 -0
  10. package/public/fonts/GoogleSans-Latin-700.woff2 +0 -0
  11. package/public/fonts/GoogleSans-Medium.ttf +0 -0
  12. package/public/fonts/GoogleSans-Medium.woff2 +0 -0
  13. package/public/fonts/GoogleSans-Regular.ttf +0 -0
  14. package/public/fonts/GoogleSans-Regular.woff2 +0 -0
  15. package/public/fonts/GoogleSans-Thai-400.woff2 +0 -0
  16. package/public/fonts/GoogleSans-Thai-500.woff2 +0 -0
  17. package/public/fonts/GoogleSans-Thai-600.woff2 +0 -0
  18. package/public/fonts/GoogleSans-Thai-700.woff2 +0 -0
  19. package/public/fonts/NotoSansThai-Bold.woff2 +0 -0
  20. package/public/fonts/NotoSansThai-Regular.woff2 +0 -0
  21. package/public/fonts/google-sans-official.css +900 -0
  22. package/public/images/0040jbadgy1id6am4yxqfj61s82dn1kx02.jpg +0 -0
  23. package/public/images/0040jbadgy1id6am708pbj62c0340qv602.jpg +0 -0
  24. package/public/images/0040jbadgy1id6amb81uqj62c0340x6q02.jpg +0 -0
  25. package/public/images/0040jbadgy1id6amcqte3j62c0340qv602.jpg +0 -0
  26. package/public/images/0040jbadgy1id6amdg3xcj62b232wqv502.jpg +0 -0
  27. package/public/images/0040jbadgy1id6amexv23j620v2pau0x02.jpg +0 -0
  28. package/public/images/0040jbadly8icq3if7twwj60qy0qy0uc02.jpg +0 -0
  29. package/public/images/008ET7Legy1ig9s89nri5j30i20n1gn4.jpg +0 -0
  30. package/public/images/008ET7Legy1ig9s8a2lmlj30i20ny409.jpg +0 -0
  31. package/public/images/008ET7Legy1ig9s8ay1hsj30i20nk3zv.jpg +0 -0
  32. package/public/images/008ET7Legy1ig9s8bab6sj30i20o7go1.jpg +0 -0
  33. package/public/images/008ET7Legy1ig9s8blqsxj30i20oiq4r.jpg +0 -0
  34. package/public/images/008ET7Legy1ig9s8cbxkzj30i20mxmzh.jpg +0 -0
  35. package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-399915.jpg +0 -0
  36. package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-424041.jpg +0 -0
  37. package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-588166.jpg +0 -0
  38. package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-672198.jpg +0 -0
  39. package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-700522.jpg +0 -0
  40. package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026820-839722.jpg +0 -0
  41. package/public/images/douyin-update/thumbs//346/212/226/351/237/2632026825-460586.jpg +0 -0
  42. package/public/images/douyin-update/thumbs//346/212/226/351/237/263202694-689407.jpg +0 -0
  43. package/public/images/douyin-update//346/212/226/351/237/2632026820-399915.mp4 +0 -0
  44. package/public/images/douyin-update//346/212/226/351/237/2632026820-424041.mp4 +0 -0
  45. package/public/images/douyin-update//346/212/226/351/237/2632026820-588166.mp4 +0 -0
  46. package/public/images/douyin-update//346/212/226/351/237/2632026820-672198.mp4 +0 -0
  47. package/public/images/douyin-update//346/212/226/351/237/2632026820-700522.mp4 +0 -0
  48. package/public/images/douyin-update//346/212/226/351/237/2632026820-839722.mp4 +0 -0
  49. package/public/images/douyin-update//346/212/226/351/237/2632026825-460586.mp4 +0 -0
  50. package/public/images/douyin-update//346/212/226/351/237/263202694-689407.mp4 +0 -0
  51. package/public/images/general/0040jbadgy1id6am4yxqfj61s82dn1kx02.jpg +0 -0
  52. package/public/images/general/0040jbadgy1id6am708pbj62c0340qv602.jpg +0 -0
  53. package/public/images/general/0040jbadgy1id6amb81uqj62c0340x6q02.jpg +0 -0
  54. package/public/images/general/0040jbadgy1id6amcqte3j62c0340qv602.jpg +0 -0
  55. package/public/images/general/0040jbadgy1id6amdg3xcj62b232wqv502.jpg +0 -0
  56. package/public/images/general/0040jbadgy1id6amexv23j620v2pau0x02.jpg +0 -0
  57. package/public/images/general/0040jbadly8icq3if7twwj60qy0qy0uc02.jpg +0 -0
  58. package/public/images/general/008ET7Legy1ig9s89nri5j30i20n1gn4.jpg +0 -0
  59. package/public/images/general/008ET7Legy1ig9s8a2lmlj30i20ny409.jpg +0 -0
  60. package/public/images/general/008ET7Legy1ig9s8ay1hsj30i20nk3zv.jpg +0 -0
  61. package/public/images/general/008ET7Legy1ig9s8bab6sj30i20o7go1.jpg +0 -0
  62. package/public/images/general/008ET7Legy1ig9s8blqsxj30i20oiq4r.jpg +0 -0
  63. package/public/images/general/008ET7Legy1ig9s8cbxkzj30i20mxmzh.jpg +0 -0
  64. package/public/images/general/jujingyi_portrait.jpg +0 -0
  65. package/public/images/jujingyi_portrait.jpg +0 -0
  66. package/public/images/series/a-millenniums-wait/poster.jpg +0 -0
  67. package/public/images/series/beauty-of-resilience/poster.jpg +0 -0
  68. package/public/images/series/detective-samoyeds/poster.jpg +0 -0
  69. package/public/images/series/everyday/poster.jpg +0 -0
  70. package/public/images/series/fu-rong-ancient-painting/poster.jpg +0 -0
  71. package/public/images/series/in-a-class-of-her-own/poster.jpg +0 -0
  72. package/public/images/series/in-blossom/poster.jpg +0 -0
  73. package/public/images/series/legend-of-yunxi/poster.jpg +0 -0
  74. package/public/images/series/lets-fight/poster-2.jpg +0 -0
  75. package/public/images/series/lets-fight/poster.jpg +0 -0
  76. package/public/images/series/love-emergency/poster.jpg +0 -0
  77. package/public/images/series/love-under-the-full-moon/poster.jpg +0 -0
  78. package/public/images/series/money-bastion/poster.jpg +0 -0
  79. package/public/images/series/mr-swimmer/poster.jpg +0 -0
  80. package/public/images/series/novoland-castle-in-the-sky/poster.jpg +0 -0
  81. package/public/images/series/please-give-me-a-pair-of-wings/poster.jpg +0 -0
  82. package/public/images/series/rebirth-for-you/poster.jpg +0 -0
  83. package/public/images/series/sword-and-fairy-4/poster.jpg +0 -0
  84. package/public/images/series/the-blooms-at-ruyi-pavilion/poster.jpg +0 -0
  85. package/public/images/series/the-eternal-fragrance/poster-2.jpg +0 -0
  86. package/public/images/series/the-eternal-fragrance/poster.jpg +0 -0
  87. package/public/images/series/the-fallen-flowers/poster.jpg +0 -0
  88. package/public/images/series/the-legend-of-white-snake/poster.jpg +0 -0
  89. package/public/images/series/veil-of-shadows/poster.jpg +0 -0
  90. package/public/images/series/wanhua-sin/poster-2.jpg +0 -0
  91. package/public/images/series/wanhua-sin/poster.jpg +0 -0
  92. package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj10yogj62u03s0npe02.jpg +0 -0
  93. package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj2pdvyj61li25jkjl02.jpg +0 -0
  94. package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj71177j62672w9e8302.jpg +0 -0
  95. package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj7twpfj62192197rw02.jpg +0 -0
  96. package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj8jfu8j62c03401kx02.jpg +0 -0
  97. package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkj9dhdsj622k2l77wh02.jpg +0 -0
  98. package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkja31s4j62bw3407r802.jpg +0 -0
  99. package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkjcf331j61402ey1kx02.jpg +0 -0
  100. package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkjdx8gqj62602pihdt02.jpg +0 -0
  101. package/public/images/weibo-update/41/345/210/206/351/222/237/345/211/215/0040jbadgy1igrkjfvxnzj62432n57wh02.jpg +0 -0
  102. package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixniv2yj627y4fxb2b02.jpg +0 -0
  103. package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixsvbdaj625k2vfu1102.jpg +0 -0
  104. package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixxxdaoj62c03501l202.jpg +0 -0
  105. package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3ixzeef9j62c0340npd02.jpg +0 -0
  106. package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3iy0vs6tj626t4rbhdu02.jpg +0 -0
  107. package/public/images/weibo-update/8-14-17-16/0040jbadgy1ig3iy21wwuj62bt34hx6q02.jpg +0 -0
  108. package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwly4wr85j62z629y7wh02.jpg +0 -0
  109. package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwly67oilj623u2t5npd02.jpg +0 -0
  110. package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwly9qtw8j62c0340u0x02.jpg +0 -0
  111. package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlyem4m4j63402c01kz02.jpg +0 -0
  112. package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlygkoq4j627s2yeavd02.jpg +0 -0
  113. package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlyjh0daj62bw340nm102.jpg +0 -0
  114. package/public/images/weibo-update/8-8-17-39/0040jbadgy1ifwlylejlhj62bw3401kx02.jpg +0 -0
  115. package/public/images/weibo-update/today/0040jbadgy1igaiaa8b7fj627j2y67wh02.jpg +0 -0
  116. package/public/images/weibo-update/today/0040jbadgy1igaiagw3ezj61s02ebu0x02.jpg +0 -0
  117. package/public/images/weibo-update/today/0040jbadgy1igaiaivng3j61rv2ebx6p02.jpg +0 -0
  118. package/public/images/weibo-update/today/0040jbadgy1igaiajuixvj62bw340qv502.jpg +0 -0
  119. package/public/images/weibo-update/today/0040jbadgy1igaiapyartj62bw340hdt02.jpg +0 -0
  120. package/public/images/weibo-update/today/0040jbadgy1igaibkwu4ij61vy2iq1kx02.jpg +0 -0
  121. package/public/images/weibo-update/today/0040jbadgy1igaic1fvkpj61u92eo1ky02.jpg +0 -0
  122. package/public/images/weibo-update/today/0040jbadgy1igaic3cruhj62822ywb2902.jpg +0 -0
  123. package/public/manifest.json +25 -0
  124. package/public/mcp-logo-128.png +0 -0
  125. package/public/mcp-logo-64.png +0 -0
  126. package/public/mcp-logo.svg +16 -0
  127. package/server/agent/agentCore.js +542 -74
  128. package/server/agent/appPaths.js +1 -0
  129. package/server/agent/audioPlayer.js +4 -2
  130. package/server/agent/citations.js +294 -0
  131. package/server/agent/clarificationAnalytics.js +477 -0
  132. package/server/agent/clarifyingQuestions.js +1089 -0
  133. package/server/agent/cli.js +549 -125
  134. package/server/agent/commandFeatures.js +1 -0
  135. package/server/agent/commandRegistry.js +946 -879
  136. package/server/agent/commands.js +546 -272
  137. package/server/agent/config.js +581 -412
  138. package/server/agent/contextCompactor.js +2 -2
  139. package/server/agent/debate.js +235 -0
  140. package/server/agent/eventBus.js +3 -0
  141. package/server/agent/feedbackLearning.js +217 -0
  142. package/server/agent/llmClient.js +512 -394
  143. package/server/agent/mcpClient.js +182 -32
  144. package/server/agent/memory.js +283 -224
  145. package/server/agent/modelManager.js +79 -39
  146. package/server/agent/profiles.js +178 -0
  147. package/server/agent/publicTools.js +22 -0
  148. package/server/agent/queryDetails.js +68 -8
  149. package/server/agent/quizData.js +398 -384
  150. package/server/agent/research.js +121 -79
  151. package/server/agent/sanitizer.js +104 -13
  152. package/server/agent/session.js +319 -11
  153. package/server/agent/subagent.js +8 -3
  154. package/server/agent/suggestionTui.js +294 -0
  155. package/server/agent/suggestions.js +69 -3
  156. package/server/agent/telemetry.js +119 -0
  157. package/server/agent/terminalImage.js +146 -27
  158. package/server/agent/theme.js +17 -0
  159. package/server/agent/toolRouter.js +16 -1
  160. package/server/agent/tools.js +424 -225
  161. package/server/agent/ui.js +486 -64
  162. package/server/agent/version.js +2 -4
  163. package/server/agent/webSearch.js +111 -11
  164. package/server/agent/youtubeAuth.js +24 -6
  165. package/server/apiRouter.js +4222 -0
  166. package/server/mcp/data/discography.js +25 -16
  167. package/server/mcp/data/gallery.js +308 -20
  168. package/server/mcp/data/profile.js +74 -44
  169. package/server/mcp/data/timeline.js +1 -1
  170. package/server/mcp/data/works.js +51 -17
  171. package/server/mcp/dataAdapter.js +5 -3
  172. package/server/mcp/handler.js +5 -11
  173. package/server/mcp/prompts.js +86 -0
  174. package/server/mcp/security.js +18 -15
  175. package/server/mcp/server.js +7 -2
  176. package/server/mcp/tools.js +66 -2
  177. package/server/web-chat/core/browserLauncher.js +18 -0
  178. package/server/web-chat/core/connection.js +194 -0
  179. package/server/web-chat/core/conversationContext.js +82 -0
  180. package/server/web-chat/core/directCatalogEngine.js +196 -0
  181. package/server/web-chat/core/dramaKnowledge.js +142 -0
  182. package/server/web-chat/core/errorHandler.js +51 -0
  183. package/server/web-chat/core/followUpEngine.js +182 -0
  184. package/server/web-chat/core/futureEventGuard.js +80 -0
  185. package/server/web-chat/core/memorySnapshot.js +12 -0
  186. package/server/web-chat/core/security.js +146 -0
  187. package/server/web-chat/core/toolInspector.js +52 -0
  188. package/server/web-chat/router.js +120 -0
  189. package/server/web-chat/routes/chatRoute.js +397 -0
  190. package/server/web-chat/routes/clarifyConfigRoute.js +98 -0
  191. package/server/web-chat/routes/clarifyRoute.js +83 -0
  192. package/server/web-chat/routes/compareRoute.js +79 -0
  193. package/server/web-chat/routes/exportRoute.js +59 -0
  194. package/server/web-chat/routes/feedbackRoute.js +57 -0
  195. package/server/web-chat/routes/newsRoute.js +33 -0
  196. package/server/web-chat/routes/quizRoute.js +11 -0
  197. package/server/web-chat/routes/staticRoute.js +103 -0
  198. package/server/web-chat/routes/statusRoute.js +52 -0
  199. package/server/web-chat/routes/suggestRoute.js +77 -0
  200. package/server/web-chat/routes/timelineRoute.js +34 -0
  201. package/server/web-chat/routes/verifyRoute.js +57 -0
  202. package/server/web-chat/server.js +116 -0
  203. package/server/web-chat/ui/htmlBuilder.js +91 -0
  204. package/server/web-chat/ui/previews/agentBubble.preview.html +134 -0
  205. package/server/web-chat/ui/scripts/agentBubbleSystem.js +320 -0
  206. package/server/web-chat/ui/scripts/client.js +8397 -0
  207. package/server/web-chat/ui/scripts/kikuDb.js +177 -0
  208. package/server/web-chat/ui/scripts/suggestionsData.js +118 -0
  209. package/server/web-chat/ui/scripts/webchatI18n.js +291 -0
  210. package/server/web-chat/ui/styles/main.css +8568 -0
  211. package/server/web-chat/ui/templates/body.html +796 -0
  212. package/server/webChatHtml.js +1 -0
  213. package/server/webChatServer.js +6 -0
  214. package/src/data/discographyData.js +892 -866
  215. package/src/data/galleryData.js +1805 -561
  216. package/src/data/profileData.js +79 -2
  217. package/src/data/socialUpdatesData.js +106 -47
  218. package/src/data/timelineData.js +1 -1
  219. package/src/data/worksData.js +25 -4
  220. package/tokens.css +97 -0
  221. package/server/agent/updateManager.js +0 -456
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import readline from 'node:readline';
4
+ import { fileURLToPath } from 'node:url';
4
5
  import { stdin as input } from 'node:process';
5
6
  import { existsSync } from 'node:fs';
6
7
  import { spawn } from 'node:child_process';
@@ -15,11 +16,14 @@ import { eventBus, Events } from './eventBus.js';
15
16
  import { AgentStates } from './agentState.js';
16
17
  import { sanitizeQuery } from './sanitizer.js';
17
18
  import { isAbortError, formatExecutionErrorCard } from './errors.js';
18
- import { COMMAND_DEFINITIONS, SUB_COMMANDS, findCommandDefinition, getCommandDetails } from './commandRegistry.js';
19
+ import { COMMAND_DEFINITIONS, COMMAND_CATEGORIES, SUB_COMMANDS, findCommandDefinition, getCommandDetails } from './commandRegistry.js';
19
20
  import { ModelManager } from './modelManager.js';
20
21
  import { PACKAGE_VERSION } from './version.js';
21
- import { getTheme, getThemeName } from './theme.js';
22
+ import { getProfile, listProfiles } from './profiles.js';
23
+ import { formatCitationsBlock } from './citations.js';
24
+ import { getTheme, getThemeName, setTheme, THEME_NAMES, setColorDisabled } from './theme.js';
22
25
  import { createTerminalImageGallery, getWelcomeGalleryLines } from './terminalImage.js';
26
+ import { formatQuestionsForCLI } from './clarifyingQuestions.js';
23
27
  import {
24
28
  classifyToolResult,
25
29
  formatDetailDuration,
@@ -33,7 +37,7 @@ import {
33
37
  import {
34
38
  C, getStringDisplayWidth, getFullWidth, wrapText, stripAnsi, fitStyledText, sanitizeTerminalText,
35
39
  previousGraphemeBoundary, nextGraphemeBoundary, sliceLineForDisplay,
36
- printSolidBannerRow, printSolidPanel, formatSolidPanelLines, formatTerminalMarkdown,
40
+ printSolidBannerRow, printSolidPanel, formatSolidPanelLines, formatTerminalMarkdown, formatMarkdownLines,
37
41
  parseMarkdownTableRow, isMarkdownTableSeparator, expandMarkdownLinks,
38
42
  formatRichCardLines, printRichCard, printRichStatusCard, formatStatusBadge, formatRichTable,
39
43
  printWelcomeHero, getWelcomeHeroLines, randomFanName, setupOutputInterception,
@@ -53,9 +57,64 @@ const deferredCommands = [];
53
57
  let globalTuiPrompt = null;
54
58
  let lastQueryDetails = null;
55
59
  const queryDetails = new Map();
60
+ let globalSpinnerInterval = null;
56
61
 
57
- function parseCliArgs() {
58
- const args = process.argv.slice(2);
62
+ function startGlobalSpinner(label) {
63
+ if (globalTuiPrompt) {
64
+ if (label !== undefined && label !== null) {
65
+ globalTuiPrompt.agentState = label;
66
+ globalTuiPrompt.agentStateTime = Date.now();
67
+ }
68
+ globalTuiPrompt.render();
69
+ }
70
+ if (!globalSpinnerInterval) {
71
+ globalSpinnerInterval = setInterval(() => {
72
+ if (globalTuiPrompt && isProcessing) {
73
+ globalTuiPrompt.render();
74
+ }
75
+ }, 80);
76
+ }
77
+ }
78
+
79
+ function stopGlobalSpinner() {
80
+ if (globalSpinnerInterval) {
81
+ clearInterval(globalSpinnerInterval);
82
+ globalSpinnerInterval = null;
83
+ }
84
+ if (globalTuiPrompt) {
85
+ globalTuiPrompt.agentState = '';
86
+ globalTuiPrompt.render();
87
+ }
88
+ }
89
+
90
+ function recordQueryDiagnostics(details) {
91
+ if (!details) return null;
92
+ const num = details.number ?? nextQueryNumber++;
93
+ lastQueryDetails = {
94
+ number: num,
95
+ prompt: details.prompt || '',
96
+ answer: details.answer || '',
97
+ thinking: details.thinking || '',
98
+ tools: details.tools || [],
99
+ status: details.status || 'complete',
100
+ error: details.error || null,
101
+ provider: details.provider || 'unknown',
102
+ model: details.model || 'unknown',
103
+ language: details.language || null,
104
+ iterations: details.iterations || (details.tools?.length ? 1 : 0),
105
+ maxIterations: details.maxIterations || 3,
106
+ startedAt: details.startedAt || Date.now(),
107
+ completedAt: details.completedAt || Date.now(),
108
+ durationMs: details.durationMs || (details.completedAt && details.startedAt ? details.completedAt - details.startedAt : 0),
109
+ timestamp: Date.now(),
110
+ };
111
+ queryDetails.set(num, lastQueryDetails);
112
+ if (queryDetails.size > 50) queryDetails.delete(queryDetails.keys().next().value);
113
+ return lastQueryDetails;
114
+ }
115
+
116
+ export function parseCliArgs(customArgs = null) {
117
+ const args = customArgs || process.argv.slice(2);
59
118
  const options = {
60
119
  query: null,
61
120
  provider: null,
@@ -63,12 +122,20 @@ function parseCliArgs() {
63
122
  model: null,
64
123
  url: null,
65
124
  mcp: null,
125
+ profile: null,
66
126
  showTools: false,
67
127
  showStats: false,
68
128
  thinking: 'show',
69
129
  verbose: false,
70
130
  help: false,
71
- version: false
131
+ version: false,
132
+ print: false,
133
+ output: 'text',
134
+ plain: false,
135
+ noColor: false,
136
+ webChat: false,
137
+ port: null,
138
+ noOpen: false,
72
139
  };
73
140
 
74
141
  for (let i = 0; i < args.length; i++) {
@@ -85,8 +152,54 @@ function parseCliArgs() {
85
152
  else if (arg === '--model' || arg === '-m') options.model = args[++i];
86
153
  else if (arg === '--url' || arg === '-u') options.url = args[++i];
87
154
  else if (arg === '--query' || arg === '-q') options.query = args[++i];
155
+ else if (arg === '--profile') options.profile = args[++i];
156
+ else if (arg === '--print' || arg === '-p') options.print = true;
157
+ else if (arg === '--output' || arg === '-o') options.output = (args[++i] || 'text').toLowerCase();
158
+ else if (arg === '--plain') { options.plain = true; options.noColor = true; }
159
+ else if (arg === '--no-color') options.noColor = true;
160
+ else if (arg === '--web-chat' || arg === '--web') options.webChat = true;
161
+ else if (arg.startsWith('--web-chat=')) {
162
+ options.webChat = true;
163
+ const maybePort = Number(arg.slice('--web-chat='.length));
164
+ if (Number.isFinite(maybePort) && maybePort > 0) options.port = Math.trunc(maybePort);
165
+ }
166
+ else if (arg === '--port') {
167
+ const maybePort = Number(args[++i]);
168
+ if (!Number.isFinite(maybePort) || maybePort < 1 || maybePort > 65535) {
169
+ console.error(`Invalid --port: ${args[i]}. Expected 1-65535.`);
170
+ process.exit(3);
171
+ }
172
+ options.port = Math.trunc(maybePort);
173
+ }
174
+ else if (arg.startsWith('--port=')) {
175
+ const maybePort = Number(arg.slice('--port='.length));
176
+ if (!Number.isFinite(maybePort) || maybePort < 1 || maybePort > 65535) {
177
+ console.error(`Invalid --port: ${arg}. Expected 1-65535.`);
178
+ process.exit(3);
179
+ }
180
+ options.port = Math.trunc(maybePort);
181
+ }
182
+ else if (arg === '--no-open') options.noOpen = true;
183
+ else if (arg === '--open') options.noOpen = false;
88
184
  else if (!arg.startsWith('-') && !options.query) { options.query = args.slice(i).join(' '); break; }
185
+ else if (arg.startsWith('-')) {
186
+ console.error(`Unknown option: ${arg}`);
187
+ process.exit(3);
188
+ }
89
189
  }
190
+
191
+ if (options.output !== 'text' && options.output !== 'json') {
192
+ console.error(`Invalid --output format: ${options.output}. Supported formats: text, json`);
193
+ process.exit(3);
194
+ }
195
+
196
+ if (options.noColor || options.plain || (typeof process !== 'undefined' && process.env?.NO_COLOR)) {
197
+ setColorDisabled(true);
198
+ }
199
+ if (options.output === 'json') {
200
+ options.print = true;
201
+ }
202
+
90
203
  return options;
91
204
  }
92
205
 
@@ -236,7 +349,7 @@ function printQueryDetails(reference = '') {
236
349
  printSolidPanel({
237
350
  title: `Answer • ${queryLabel}`,
238
351
  lines: details.answer
239
- ? String(details.answer).split('\n').flatMap(line => wrapText(sanitizeTerminalText(line, 500000), width)).map(line => `${C.white}${formatTerminalMarkdown(line)}${C.reset}`)
352
+ ? formatMarkdownLines(details.answer, width - 4)
240
353
  : [`${C.dim}${C.slate}No final answer was recorded.${C.reset}`],
241
354
  });
242
355
  return;
@@ -292,7 +405,7 @@ function printQueryDetails(reference = '') {
292
405
  const copyable = getCopyableContent(details, request);
293
406
  printSolidPanel({
294
407
  title: copyable.title,
295
- lines: copyable.text.split('\n').flatMap(line => wrapText(line, width)).map(line => `${C.white}${line}${C.reset}`),
408
+ lines: formatMarkdownLines(copyable.text, width - 4),
296
409
  });
297
410
  return;
298
411
  }
@@ -338,19 +451,33 @@ function copyQueryDetails(reference = '', session = null) {
338
451
  return false;
339
452
  }
340
453
 
454
+ if (request.isSession) {
455
+ if (!session || !session.messages || session.messages.length === 0) {
456
+ console.log(`\n ${C.amber}[EMPTY] No conversation messages in the current session to copy.${C.reset}\n`);
457
+ return false;
458
+ }
459
+ } else {
460
+ const details = request.queryNumber == null
461
+ ? lastQueryDetails
462
+ : queryDetails.get(request.queryNumber);
463
+
464
+ if (!details) {
465
+ const target = request.queryNumber == null ? 'the latest query' : `Q${request.queryNumber}`;
466
+ console.log(`\n ${C.amber}[NOT FOUND] No diagnostics recorded for ${target}. Ask a question or run a tool first.${C.reset}\n`);
467
+ return false;
468
+ }
469
+ }
470
+
341
471
  const details = request.queryNumber == null
342
472
  ? lastQueryDetails
343
473
  : queryDetails.get(request.queryNumber);
344
474
 
345
- if (!details && !request.isSession && !session) {
346
- const target = request.queryNumber == null ? 'the latest query' : `Q${request.queryNumber}`;
347
- console.log(`\n ${C.amber}[NOT FOUND] No diagnostics recorded for ${target}.${C.reset}\n`);
348
- return false;
349
- }
350
-
351
475
  const copyable = getCopyableContent(details, request, session);
352
- if (!copyable || !copyable.text) {
353
- console.log(`\n ${C.amber}[EMPTY] Nothing to copy for ${request.view || 'answer'}.${C.reset}\n`);
476
+ if (!copyable || !copyable.text || !copyable.text.trim()) {
477
+ const target = request.isSession
478
+ ? 'the active session'
479
+ : (request.queryNumber == null ? `the latest query (${request.view})` : `Q${request.queryNumber} (${request.view})`);
480
+ console.log(`\n ${C.amber}[EMPTY] Nothing recorded to copy for ${target}.${C.reset}\n`);
354
481
  return false;
355
482
  }
356
483
 
@@ -437,23 +564,55 @@ export function printHelp(specificCmd = null) {
437
564
  const termWidth = getFullWidth();
438
565
  const maxContentWidth = Math.max(40, termWidth - 8);
439
566
  const helpLines = [];
440
-
441
- for (const command of COMMAND_DEFINITIONS) {
442
- const usageWidth = 30;
443
- const descWidth = maxContentWidth - usageWidth - 2;
444
-
445
- if (command.usage.length > 28 || command.desc.length > descWidth) {
446
- const wrappedDesc = wrapText(command.desc, maxContentWidth - 6);
447
- helpLines.push(`${C.emerald}${command.usage}${C.reset}`);
448
- for (const d of wrappedDesc) {
449
- helpLines.push(` ${C.dim}↳${C.reset} ${C.white}${d}${C.reset}`);
567
+ const cmdMap = new Map(COMMAND_DEFINITIONS.map(c => [c.name, c]));
568
+ const categories = Array.isArray(COMMAND_CATEGORIES) && COMMAND_CATEGORIES.length > 0
569
+ ? COMMAND_CATEGORIES
570
+ : [{ id: 'all', name: 'All Commands', icon: '✦', commands: COMMAND_DEFINITIONS.map(c => c.name) }];
571
+
572
+ for (const cat of categories) {
573
+ helpLines.push(`${C.bold}${C.sakura}${cat.icon || '✦'} ${cat.name}${C.reset}`);
574
+ for (const cmdName of cat.commands) {
575
+ const command = cmdMap.get(cmdName);
576
+ if (!command) continue;
577
+ const usageWidth = 30;
578
+ const descWidth = maxContentWidth - usageWidth - 4;
579
+
580
+ if (command.usage.length > 28 || command.desc.length > descWidth) {
581
+ const wrappedDesc = wrapText(command.desc, maxContentWidth - 8);
582
+ helpLines.push(` ${C.emerald}${command.usage}${C.reset}`);
583
+ for (const d of wrappedDesc) {
584
+ helpLines.push(` ${C.dim}↳${C.reset} ${C.white}${d}${C.reset}`);
585
+ }
586
+ } else {
587
+ helpLines.push(` ${C.emerald}${command.usage.padEnd(usageWidth)}${C.reset} ${C.white}${command.desc}${C.reset}`);
450
588
  }
451
- } else {
452
- helpLines.push(`${C.emerald}${command.usage.padEnd(usageWidth)}${C.reset} ${C.white}${command.desc}${C.reset}`);
453
589
  }
590
+ helpLines.push('');
454
591
  }
455
592
 
456
- helpLines.push('', `${C.dim}Tip: Press Tab for auto-completion • Type /help <command> (e.g. /help suggestion, /help mcp, /help provider) for detailed subcommand breakdowns.${C.reset}`);
593
+ if (helpLines[helpLines.length - 1] === '') helpLines.pop();
594
+ helpLines.push('', `${C.dim}Tip: Press Tab for auto-completion • Type /help <command> (e.g. /help session, /help provider, /help mcp) for detailed subcommand breakdowns.${C.reset}`);
595
+
596
+ helpLines.push('', `${C.bold}${C.sakura}✦ CLI Flags & Options${C.reset}`);
597
+ helpLines.push(` ${C.emerald}${"-q, --query <text>".padEnd(28)}${C.reset} ${C.white}Run a one-shot query${C.reset}`);
598
+ helpLines.push(` ${C.emerald}${"-p, --print".padEnd(28)}${C.reset} ${C.white}Headless non-interactive print mode (clean text output)${C.reset}`);
599
+ helpLines.push(` ${C.emerald}${"-o, --output <format>".padEnd(28)}${C.reset} ${C.white}Output format: text (default) or json${C.reset}`);
600
+ helpLines.push(` ${C.emerald}${"-P, --provider <name>".padEnd(28)}${C.reset} ${C.white}Select provider (lmstudio, openai, gemini, groq, etc.)${C.reset}`);
601
+ helpLines.push(` ${C.emerald}${"--profile <name>".padEnd(28)}${C.reset} ${C.white}Load configuration profile (default, fast, deep-reasoning, cloud, gemini)${C.reset}`);
602
+ helpLines.push(` ${C.emerald}${"-m, --model <id>".padEnd(28)}${C.reset} ${C.white}Model ID override${C.reset}`);
603
+ helpLines.push(` ${C.emerald}${"-k, --api-key <key>".padEnd(28)}${C.reset} ${C.white}Provider API key override${C.reset}`);
604
+ helpLines.push(` ${C.emerald}${"-u, --url <url>".padEnd(28)}${C.reset} ${C.white}Provider base URL override${C.reset}`);
605
+ helpLines.push(` ${C.emerald}${"--plain".padEnd(28)}${C.reset} ${C.white}Disable ANSI styles, colors, and alternate screen${C.reset}`);
606
+ helpLines.push(` ${C.emerald}${"--no-color".padEnd(28)}${C.reset} ${C.white}Disable ANSI colors (honors NO_COLOR env)${C.reset}`);
607
+ helpLines.push(` ${C.emerald}${"--thinking <mode>".padEnd(28)}${C.reset} ${C.white}Show or hide reasoning process (show | hide)${C.reset}`);
608
+ helpLines.push(` ${C.emerald}${"-t, --tools".padEnd(28)}${C.reset} ${C.white}List all registered tools${C.reset}`);
609
+ helpLines.push(` ${C.emerald}${"-s, --stats".padEnd(28)}${C.reset} ${C.white}Show system and catalog stats${C.reset}`);
610
+ helpLines.push(` ${C.emerald}${"--web-chat, --web".padEnd(28)}${C.reset} ${C.white}Start standalone web chatbot (default port 3300)${C.reset}`);
611
+ helpLines.push(` ${C.emerald}${"--port <n>".padEnd(28)}${C.reset} ${C.white}Web chatbot port 1-65535 (or --web-chat=<port>)${C.reset}`);
612
+ helpLines.push(` ${C.emerald}${"--no-open, --open".padEnd(28)}${C.reset} ${C.white}Skip or force auto-open browser for --web-chat${C.reset}`);
613
+ helpLines.push(` ${C.emerald}${"-v, --verbose".padEnd(28)}${C.reset} ${C.white}Verbose logging and thinking${C.reset}`);
614
+ helpLines.push(` ${C.emerald}${"-V, --version".padEnd(28)}${C.reset} ${C.white}Print version${C.reset}`);
615
+ helpLines.push(` ${C.emerald}${"-h, --help".padEnd(28)}${C.reset} ${C.white}Show this help message${C.reset}`);
457
616
 
458
617
  printSolidPanel({
459
618
  title: `${UI_ICONS.help} Kiku AI Agent — ${COMMAND_DEFINITIONS.length} Commands`,
@@ -694,6 +853,7 @@ class InteractiveTuiPrompt {
694
853
  this._cleanupInput();
695
854
  this.line = '';
696
855
  this.cursor = 0;
856
+ this.ghostText = '';
697
857
  this.popoverActive = false;
698
858
  this.popoverItems = [];
699
859
  if (this.recordInputHistory && result && result.trim()) this.history.push(result);
@@ -1002,8 +1162,15 @@ class InteractiveTuiPrompt {
1002
1162
  if (isF3) { this.finish('/details thinking'); return; }
1003
1163
  if (isF4) { this.finish('/details tools'); return; }
1004
1164
  if (isF5) { this.finish('/copy'); return; }
1005
- if (isF6) { this.finish('/gallery'); return; }
1006
- if (isF7) { this.finish('/theme next'); return; }
1165
+ if (isF6) { this.finish('/timeline'); return; }
1166
+ if (isF7) {
1167
+ const current = getThemeName();
1168
+ const currentIndex = THEME_NAMES.indexOf(current);
1169
+ const nextIndex = (currentIndex + 1) % THEME_NAMES.length;
1170
+ setTheme(THEME_NAMES[nextIndex]);
1171
+ this.render();
1172
+ return;
1173
+ }
1007
1174
  if (isF8) { this.finish('/stats'); return; }
1008
1175
  if (isF9) { this.finish('/provider'); return; }
1009
1176
  if (isF10) { this.finish('/mcp list'); return; }
@@ -1014,12 +1181,21 @@ class InteractiveTuiPrompt {
1014
1181
  const isCtrlR = (key?.ctrl && key?.name === 'r') || str === '\x12';
1015
1182
  const isCtrlS = (key?.ctrl && key?.name === 's') || str === '\x13';
1016
1183
  const isCtrlG = (key?.ctrl && key?.name === 'g') || str === '\x07';
1184
+ const isCtrlK = (key?.ctrl && key?.name === 'k') || str === '\x0b';
1185
+ const isCtrlP = (key?.ctrl && key?.name === 'p') || str === '\x10';
1017
1186
 
1018
1187
  if (isCtrlL) { this.render(); return; }
1019
1188
  if (isCtrlT) { this.finish('/thinking toggle'); return; }
1020
1189
  if (isCtrlR) { this.finish('/retry'); return; }
1021
1190
  if (isCtrlS) { this.finish('/save'); return; }
1022
1191
  if (isCtrlG) { this.finish('/gallery'); return; }
1192
+ if (isCtrlK || isCtrlP) {
1193
+ this.line = '/';
1194
+ this.cursor = 1;
1195
+ this.updateState();
1196
+ this.render();
1197
+ return;
1198
+ }
1023
1199
 
1024
1200
  if (key?.name === 'tab' || str === '\t') {
1025
1201
  if (this.popoverActive && this.popoverItems.length > 0) {
@@ -1443,29 +1619,11 @@ async function executeQueryWithUI(agent, prompt, isThinkingVisible = false, sign
1443
1619
  };
1444
1620
 
1445
1621
  const startSpinner = (label) => {
1446
- if (globalTuiPrompt) {
1447
- globalTuiPrompt.agentState = label;
1448
- globalTuiPrompt.agentStateTime = Date.now();
1449
- globalTuiPrompt.render();
1450
- }
1451
- if (!spinnerInterval) {
1452
- spinnerInterval = setInterval(() => {
1453
- if (globalTuiPrompt && isProcessing) {
1454
- globalTuiPrompt.render();
1455
- }
1456
- }, 80);
1457
- }
1622
+ startGlobalSpinner(label);
1458
1623
  };
1459
1624
 
1460
1625
  const stopSpinner = () => {
1461
- if (spinnerInterval) {
1462
- clearInterval(spinnerInterval);
1463
- spinnerInterval = null;
1464
- }
1465
- if (globalTuiPrompt) {
1466
- globalTuiPrompt.agentState = '';
1467
- globalTuiPrompt.render();
1468
- }
1626
+ stopGlobalSpinner();
1469
1627
  };
1470
1628
 
1471
1629
  let thinkingShown = false;
@@ -1479,12 +1637,13 @@ async function executeQueryWithUI(agent, prompt, isThinkingVisible = false, sign
1479
1637
  let finalMeta = {};
1480
1638
 
1481
1639
  const persistQueryDetails = (status, error = null) => {
1482
- lastQueryDetails = {
1640
+ recordQueryDiagnostics({
1483
1641
  number: queryNumber,
1484
1642
  prompt,
1485
1643
  answer: finalAnswer,
1486
1644
  thinking: accumulatedThinking,
1487
1645
  tools: recordedTools,
1646
+ citations: agent?.lastCitations || [],
1488
1647
  status,
1489
1648
  error: error ? String(error.message || error) : null,
1490
1649
  provider: agent.client?.providerName || agent.client?.provider || 'unknown',
@@ -1495,12 +1654,7 @@ async function executeQueryWithUI(agent, prompt, isThinkingVisible = false, sign
1495
1654
  startedAt: queryStartedAt,
1496
1655
  completedAt: Date.now(),
1497
1656
  durationMs: finalMeta.durationMs ?? (Date.now() - queryStartedAt),
1498
- timestamp: Date.now(),
1499
- };
1500
- if (queryNumber != null) {
1501
- queryDetails.set(queryNumber, lastQueryDetails);
1502
- if (queryDetails.size > 50) queryDetails.delete(queryDetails.keys().next().value);
1503
- }
1657
+ });
1504
1658
  };
1505
1659
 
1506
1660
  startSpinner(`${C.slate}Thinking…${C.reset}`);
@@ -1508,6 +1662,19 @@ async function executeQueryWithUI(agent, prompt, isThinkingVisible = false, sign
1508
1662
  try {
1509
1663
  const returnedAnswer = await agent.ask(prompt, {
1510
1664
  signal,
1665
+ onClarifyingQuestions: (questions) => {
1666
+ stopSpinner();
1667
+ console.log(formatQuestionsForCLI(questions, C));
1668
+ },
1669
+ onClarifyingAnswer: (queryId) => {
1670
+ return new Promise((resolve) => {
1671
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1672
+ rl.question(`${C.gold}> ${C.reset}`, (answer) => {
1673
+ rl.close();
1674
+ resolve(answer.trim());
1675
+ });
1676
+ });
1677
+ },
1511
1678
  onStepStart: iteration => { completedIterations = Math.max(completedIterations, Number(iteration) || 0); },
1512
1679
  onReasoningStart: () => {
1513
1680
  startSpinner(`${C.slate}Thinking…${C.reset}`);
@@ -1624,6 +1791,16 @@ async function executeQueryWithUI(agent, prompt, isThinkingVisible = false, sign
1624
1791
  if (!finalAnswer && returnedAnswer) finalAnswer = String(returnedAnswer);
1625
1792
  persistQueryDetails('complete');
1626
1793
  if (responsePanelStarted) console.log('');
1794
+ if (agent?.lastCitations?.length > 0) {
1795
+ const citLines = agent.lastCitations.map(c => {
1796
+ const yearStr = c.year ? ` [${c.year}]` : '';
1797
+ const catStr = c.category ? ` • ${c.category}` : '';
1798
+ const srcStr = c.url ? ` • ${c.url}` : ` • ${c.source}`;
1799
+ return ` ${C.gold}[${c.id}]${C.reset} ${C.bold}${c.title}${C.reset}${C.dim}${yearStr}${catStr}${srcStr}${C.reset}`;
1800
+ });
1801
+ console.log(`${C.dim}📖 Sources & Citations (${agent.lastCitations.length}):${C.reset}`);
1802
+ console.log(citLines.join('\n') + '\n');
1803
+ }
1627
1804
  } catch (err) {
1628
1805
  stopSpinner();
1629
1806
  persistQueryDetails(isAbortError(err) ? 'aborted' : 'error', err);
@@ -1641,44 +1818,267 @@ async function executeQueryWithUI(agent, prompt, isThinkingVisible = false, sign
1641
1818
  }
1642
1819
  }
1643
1820
 
1821
+ async function readStdin() {
1822
+ if (process.stdin.isTTY) return null;
1823
+ return new Promise((resolve) => {
1824
+ let data = '';
1825
+ let hasData = false;
1826
+ const timer = setTimeout(() => {
1827
+ if (!hasData) resolve(null);
1828
+ }, 120);
1829
+ timer.unref();
1830
+
1831
+ process.stdin.setEncoding('utf8');
1832
+ process.stdin.on('data', chunk => {
1833
+ hasData = true;
1834
+ data += chunk;
1835
+ });
1836
+ process.stdin.on('end', () => {
1837
+ clearTimeout(timer);
1838
+ resolve(data.trim() || null);
1839
+ });
1840
+ process.stdin.on('error', () => {
1841
+ clearTimeout(timer);
1842
+ resolve(null);
1843
+ });
1844
+ });
1845
+ }
1846
+
1847
+ async function executeHeadlessQuery(opts, query) {
1848
+ const startTime = Date.now();
1849
+
1850
+ if (opts.profile) {
1851
+ const p = getProfile(opts.profile);
1852
+ if (!p) {
1853
+ console.error(`Profile "${opts.profile}" not found. Run kiku-agent with /profile list or check available profiles.`);
1854
+ process.exit(3);
1855
+ }
1856
+ opts.provider = opts.provider || p.provider;
1857
+ opts.model = opts.model || p.model;
1858
+ if (p.thinking && !process.argv.includes('--thinking')) opts.thinking = p.thinking;
1859
+ }
1860
+
1861
+ const client = new LLMClient({
1862
+ provider: opts.provider || config.activeProvider,
1863
+ baseUrl: opts.url,
1864
+ apiKey: opts.apiKey,
1865
+ model: opts.model
1866
+ });
1867
+
1868
+ let agent = null;
1869
+ let answer = '';
1870
+ let thinking = '';
1871
+ const recordedTools = [];
1872
+ let executionError = null;
1873
+
1874
+ try {
1875
+ const modelManager = new ModelManager(client, { requestedModel: opts.model || client.model });
1876
+ const modelResolution = await modelManager.resolve({ requestedModel: opts.model || client.model });
1877
+
1878
+ if (!modelResolution.connected || !modelResolution.selectedModel) {
1879
+ throw new Error(modelResolution.error || `Cannot reach model server or no model found for ${client.providerName}`);
1880
+ }
1881
+
1882
+ agent = new KikuAgent({ client, thinkingMode: opts.thinking });
1883
+ await agent.ready();
1884
+
1885
+ answer = await agent.run(query, {
1886
+ onReasoningDelta: (chunk) => {
1887
+ thinking += chunk;
1888
+ },
1889
+ onToolStart: (name, args) => {
1890
+ recordedTools.push({ name, args, status: 'running' });
1891
+ },
1892
+ onToolResult: (name, result, duration) => {
1893
+ const t = recordedTools.find(tool => tool.name === name && tool.status === 'running');
1894
+ if (t) {
1895
+ t.status = 'done';
1896
+ t.result = result;
1897
+ t.duration = duration;
1898
+ } else {
1899
+ recordedTools.push({ name, result, duration, status: 'done' });
1900
+ }
1901
+ }
1902
+ });
1903
+ } catch (err) {
1904
+ executionError = err;
1905
+ }
1906
+
1907
+ const durationMs = Date.now() - startTime;
1908
+ const isError = Boolean(executionError);
1909
+
1910
+ if (opts.output === 'json') {
1911
+ const payload = {
1912
+ version: PACKAGE_VERSION,
1913
+ sessionId: agent?.session?.id || null,
1914
+ query,
1915
+ status: isError ? 'error' : 'success',
1916
+ answer: isError ? '' : (answer || '').trim(),
1917
+ ...(opts.thinking === 'show' ? { thinking: (thinking || '').trim() } : {}),
1918
+ tools: recordedTools.map(t => ({
1919
+ name: t.name,
1920
+ durationMs: t.duration || 0,
1921
+ status: t.status || 'done'
1922
+ })),
1923
+ citations: agent?.lastCitations || [],
1924
+ metrics: {
1925
+ durationMs,
1926
+ iterations: recordedTools.length ? 1 : 0,
1927
+ tokens: agent?.lastTelemetry?.tokens || agent?.session?.lastQueryTokens || {
1928
+ prompt: 0,
1929
+ completion: 0,
1930
+ reasoning: 0,
1931
+ total: 0
1932
+ },
1933
+ costUsd: agent?.lastTelemetry?.costUsd ?? agent?.session?.lastQueryCostUsd ?? 0
1934
+ }
1935
+ };
1936
+ if (isError) {
1937
+ payload.error = executionError.message;
1938
+ }
1939
+ console.log(JSON.stringify(payload, null, 2));
1940
+ process.exit(isError ? 2 : 0);
1941
+ }
1942
+
1943
+
1944
+ if (isError) {
1945
+ console.error(`Error: ${executionError.message}`);
1946
+ process.exit(2);
1947
+ }
1948
+
1949
+ if (opts.thinking === 'show' && thinking.trim()) {
1950
+ console.log(`[Thinking]\n${thinking.trim()}\n`);
1951
+ }
1952
+ console.log((answer || '').trim());
1953
+ if (agent?.lastCitations?.length) {
1954
+ console.log('\n' + formatCitationsBlock(agent.lastCitations, { lang: 'auto' }));
1955
+ }
1956
+ process.exit(0);
1957
+ }
1958
+
1959
+ async function runWebChatStandalone(opts) {
1960
+ const { startWebChatServer, stopWebChatServer, openBrowser } = await import('../webChatServer.js');
1961
+ const port = opts.port || Number(process.env.PORT_WEB_CHAT) || 3300;
1962
+
1963
+ const client = new LLMClient({
1964
+ provider: opts.provider || config.activeProvider,
1965
+ baseUrl: opts.url,
1966
+ apiKey: opts.apiKey,
1967
+ model: opts.model,
1968
+ });
1969
+ const agent = new KikuAgent({ client, thinkingMode: opts.thinking });
1970
+
1971
+ const res = await startWebChatServer({ port, agent });
1972
+ if (!opts.noOpen) {
1973
+ try { openBrowser(res.url); } catch { /* browser is best-effort */ }
1974
+ }
1975
+
1976
+ if (!opts.plain && !opts.noColor) printWelcomeHero();
1977
+ printRichCard({
1978
+ title: res.alreadyRunning ? 'Web Chatbot Already Running' : 'Kiku Web Chatbot Ready',
1979
+ status: 'connected',
1980
+ lines: [
1981
+ `• URL: ${res.url}`,
1982
+ `• Port: ${res.port}${res.alreadyRunning ? ' (existing server reused)' : ''}`,
1983
+ `• Provider: ${client.providerName || client.provider} • Model: ${client.model}`,
1984
+ `• Browser: ${opts.noOpen ? 'skipped (--no-open)' : 'opened automatically'}`,
1985
+ '• Press Ctrl+C to stop the server.',
1986
+ '• Endpoints: / /api/chat /api/status /api/v1/timeline',
1987
+ ],
1988
+ });
1989
+
1990
+ const shutdown = async () => {
1991
+ try { await stopWebChatServer(); } catch { /* best-effort */ }
1992
+ try { await agent.endSession?.(); } catch { /* best-effort */ }
1993
+ console.log(`\n ${C.sakura}Web chatbot stopped • 再见!${C.reset}\n`);
1994
+ process.exit(0);
1995
+ };
1996
+ process.once('SIGINT', shutdown);
1997
+ process.once('SIGTERM', shutdown);
1998
+
1999
+ // Keep the process alive until Ctrl+C.
2000
+ await new Promise(() => {});
2001
+ }
2002
+
1644
2003
  async function main() {
1645
- const opts = parseCliArgs();
1646
-
1647
- if (opts.version) { console.log(PACKAGE_VERSION); process.exit(0); }
1648
- if (opts.help) { printWelcomeHero(); printHelp(); process.exit(0); }
1649
- if (opts.showTools) {
1650
- printWelcomeHero();
1651
- console.log(`\n ${C.bold}${C.sakura}Tools (${toolsSchema.length})${C.reset}\n`);
1652
- for (const [i, t] of toolsSchema.entries()) {
1653
- console.log(` ${C.gold}${String(i + 1).padStart(2)}${C.reset} ${C.cyberCyan}${t.function.name}${C.reset} — ${C.dim}${t.function.description.slice(0, 60)}${C.reset}`);
1654
- }
1655
- console.log('');
1656
- process.exit(0);
1657
- }
2004
+ const opts = parseCliArgs();
1658
2005
 
1659
- if (opts.showStats) { printWelcomeHero(); const r = await executeTool('get_system_stats', {}); console.log(JSON.stringify(r, null, 2)); process.exit(0); }
2006
+ if (!opts.query && !process.stdin.isTTY) {
2007
+ const pipedInput = await readStdin();
2008
+ if (pipedInput) {
2009
+ opts.query = pipedInput;
2010
+ opts.print = true;
2011
+ }
2012
+ }
1660
2013
 
1661
- if (opts.query) {
1662
- printWelcomeHero();
1663
- const client = new LLMClient({
1664
- provider: opts.provider || config.activeProvider,
1665
- baseUrl: opts.url,
1666
- apiKey: opts.apiKey,
1667
- model: opts.model
1668
- });
1669
- const modelManager = new ModelManager(client, { requestedModel: opts.model || client.model });
1670
- const modelResolution = await modelManager.resolve({ requestedModel: opts.model || client.model });
1671
- if (!modelResolution.connected || !modelResolution.selectedModel) {
1672
- throw new Error(modelResolution.error || `No available chat model found for ${client.providerName}`);
1673
- }
1674
- if (modelResolution.usedFallback) {
1675
- console.log(` ${C.amber}Model ${modelResolution.requestedModel} is unavailable; using ${modelResolution.selectedModel}.${C.reset}\n`);
1676
- }
1677
- console.log(` ${C.bold}${C.gold}QUERY${C.reset} ${C.white}${opts.query}${C.reset}\n`);
1678
- const agent = new KikuAgent({ client, thinkingMode: opts.thinking });
1679
- await executeQueryWithUI(agent, opts.query, agent.session.thinkingMode === 'show', null, 1);
1680
- process.exit(0);
1681
- }
2014
+ if (opts.profile) {
2015
+ const p = getProfile(opts.profile);
2016
+ if (!p) {
2017
+ console.error(`Profile "${opts.profile}" not found. Run kiku-agent with /profile list to view available profiles.`);
2018
+ process.exit(3);
2019
+ }
2020
+ opts.provider = opts.provider || p.provider;
2021
+ opts.model = opts.model || p.model;
2022
+ if (p.thinking && !process.argv.includes('--thinking')) {
2023
+ opts.thinking = p.thinking;
2024
+ }
2025
+ }
2026
+
2027
+ if (opts.version) { console.log(PACKAGE_VERSION); process.exit(0); }
2028
+ if (opts.help) {
2029
+ if (!opts.plain && !opts.noColor) printWelcomeHero();
2030
+ printHelp();
2031
+ process.exit(0);
2032
+ }
2033
+ if (opts.showTools) {
2034
+ if (!opts.plain && !opts.noColor) printWelcomeHero();
2035
+ console.log(`\n ${C.bold}${C.sakura}Tools (${toolsSchema.length})${C.reset}\n`);
2036
+ for (const [i, t] of toolsSchema.entries()) {
2037
+ console.log(` ${C.gold}${String(i + 1).padStart(2)}${C.reset} ${C.cyberCyan}${t.function.name}${C.reset} — ${C.dim}${t.function.description.slice(0, 60)}${C.reset}`);
2038
+ }
2039
+ console.log('');
2040
+ process.exit(0);
2041
+ }
2042
+
2043
+ if (opts.showStats) {
2044
+ if (!opts.plain && !opts.noColor) printWelcomeHero();
2045
+ const r = await executeTool('get_system_stats', {});
2046
+ console.log(JSON.stringify(r, null, 2));
2047
+ process.exit(0);
2048
+ }
2049
+
2050
+ if (opts.webChat) {
2051
+ await runWebChatStandalone(opts);
2052
+ return;
2053
+ }
2054
+
2055
+ if (opts.query) {
2056
+ if (opts.print || opts.output === 'json') {
2057
+ await executeHeadlessQuery(opts, opts.query);
2058
+ return;
2059
+ }
2060
+
2061
+ printWelcomeHero();
2062
+ const client = new LLMClient({
2063
+ provider: opts.provider || config.activeProvider,
2064
+ baseUrl: opts.url,
2065
+ apiKey: opts.apiKey,
2066
+ model: opts.model
2067
+ });
2068
+ const modelManager = new ModelManager(client, { requestedModel: opts.model || client.model });
2069
+ const modelResolution = await modelManager.resolve({ requestedModel: opts.model || client.model });
2070
+ if (!modelResolution.connected || !modelResolution.selectedModel) {
2071
+ console.error(modelResolution.error || `No available chat model found for ${client.providerName}`);
2072
+ process.exit(2);
2073
+ }
2074
+ if (modelResolution.usedFallback) {
2075
+ console.log(` ${C.amber}Model ${modelResolution.requestedModel} is unavailable; using ${modelResolution.selectedModel}.${C.reset}\n`);
2076
+ }
2077
+ console.log(` ${C.bold}${C.gold}QUERY${C.reset} ${C.white}${opts.query}${C.reset}\n`);
2078
+ const agent = new KikuAgent({ client, thinkingMode: opts.thinking });
2079
+ await executeQueryWithUI(agent, opts.query, agent.session.thinkingMode === 'show', null, 1);
2080
+ process.exit(0);
2081
+ }
1682
2082
 
1683
2083
 
1684
2084
  try { process.stdout.write('\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1005l\x1b[?1006l\x1b[?1015l'); } catch { }
@@ -1969,9 +2369,11 @@ async function main() {
1969
2369
  copyQueryDetails: (ref) => copyQueryDetails(ref, agent?.session),
1970
2370
  printWelcome: printWelcomeHero,
1971
2371
  printAd,
1972
- showTerminalImage: (count = 2) => {
1973
- const num = Number(count) || 2;
1974
- const imageLines = terminalGallery.maybeRender(queryCount, { force: true, count: num });
2372
+ showTerminalImage: (options = 2) => {
2373
+ const opts = typeof options === 'number'
2374
+ ? { count: options, force: true }
2375
+ : (typeof options === 'object' && options !== null ? { force: true, ...options } : { count: 2, force: true });
2376
+ const imageLines = terminalGallery.maybeRender(queryCount, opts);
1975
2377
  if (imageLines.length > 0) tuiPrompt.addViewportLines(imageLines);
1976
2378
  return imageLines.length > 0;
1977
2379
  },
@@ -1984,12 +2386,13 @@ async function main() {
1984
2386
  clearQueryQueue: () => { queryQueue.length = 0; },
1985
2387
  getQueryQueueLength: () => queryQueue.length,
1986
2388
  get isProcessing() { return isProcessing; },
2389
+ recordQueryDiagnostics: (details) => recordQueryDiagnostics(details),
1987
2390
  setIsProcessing: (val, label = '') => {
1988
2391
  isProcessing = Boolean(val);
1989
- if (globalTuiPrompt) {
1990
- globalTuiPrompt.agentState = label || '';
1991
- if (label) globalTuiPrompt.agentStateTime = Date.now();
1992
- globalTuiPrompt.render();
2392
+ if (isProcessing) {
2393
+ startGlobalSpinner(label || 'Processing…');
2394
+ } else {
2395
+ stopGlobalSpinner();
1993
2396
  }
1994
2397
  },
1995
2398
  setAgentState: (label, time = Date.now()) => {
@@ -1998,6 +2401,9 @@ async function main() {
1998
2401
  globalTuiPrompt.agentStateTime = time;
1999
2402
  globalTuiPrompt.render();
2000
2403
  }
2404
+ if (label && isProcessing) {
2405
+ startGlobalSpinner();
2406
+ }
2001
2407
  },
2002
2408
  };
2003
2409
 
@@ -2114,20 +2520,27 @@ async function main() {
2114
2520
  const extra = parts.slice(2).join(' ');
2115
2521
 
2116
2522
  let result;
2523
+ activeAbortController = new AbortController();
2117
2524
  try {
2118
2525
  result = await routeCommand(trimmed, {
2119
2526
  ...cmdCtx,
2527
+ signal: activeAbortController.signal,
2120
2528
  sub,
2121
2529
  extra,
2122
2530
  parts,
2123
2531
  });
2124
2532
  } catch (cmdErr) {
2125
- printRichCard({
2126
- title: 'Command Error',
2127
- status: 'error',
2128
- lines: [cmdErr.message || String(cmdErr)],
2129
- });
2533
+ if (!isAbortError(cmdErr)) {
2534
+ printRichCard({
2535
+ title: 'Command Error',
2536
+ status: 'error',
2537
+ lines: [cmdErr.message || String(cmdErr)],
2538
+ });
2539
+ }
2130
2540
  continue;
2541
+ } finally {
2542
+ activeAbortController = null;
2543
+ cmdCtx.setIsProcessing(false);
2131
2544
  }
2132
2545
 
2133
2546
  if (result === 'EXIT') {
@@ -2250,24 +2663,35 @@ async function main() {
2250
2663
  });
2251
2664
  }
2252
2665
 
2253
- process.on('exit', () => {
2254
- try { globalTuiPrompt?.dispose(); } catch { }
2255
- exitAlternateScreen();
2256
- });
2257
- process.on('uncaughtException', (err) => {
2258
- try { globalTuiPrompt?.dispose(); } catch { }
2259
- exitAlternateScreen();
2260
- console.error(`\n Uncaught: ${err.message}`);
2261
- process.exit(1);
2262
- });
2263
- process.on('SIGINT', () => {
2264
- try { globalTuiPrompt?.dispose(); } catch { }
2265
- exitAlternateScreen();
2266
- process.exit(130);
2267
- });
2666
+ const currentScriptPath = fileURLToPath(import.meta.url);
2667
+ const isDirectRun = process.argv[1] && (
2668
+ process.argv[1] === currentScriptPath ||
2669
+ process.argv[1].endsWith('server/agent/cli.js') ||
2670
+ process.argv[1].endsWith('server\\agent\\cli.js') ||
2671
+ process.argv[1].endsWith('bin/kiku-agent') ||
2672
+ process.argv[1].endsWith('bin\\kiku-agent')
2673
+ );
2674
+
2675
+ if (isDirectRun) {
2676
+ process.on('exit', () => {
2677
+ try { globalTuiPrompt?.dispose(); } catch { }
2678
+ exitAlternateScreen();
2679
+ });
2680
+ process.on('uncaughtException', (err) => {
2681
+ try { globalTuiPrompt?.dispose(); } catch { }
2682
+ exitAlternateScreen();
2683
+ console.error(`\n Uncaught: ${err.message}`);
2684
+ process.exit(1);
2685
+ });
2686
+ process.on('SIGINT', () => {
2687
+ try { globalTuiPrompt?.dispose(); } catch { }
2688
+ exitAlternateScreen();
2689
+ process.exit(130);
2690
+ });
2268
2691
 
2269
- main().catch(err => {
2270
- exitAlternateScreen();
2271
- console.error(`\n ${C.red}Fatal: ${err.message}${C.reset}\n`);
2272
- process.exit(1);
2273
- });
2692
+ main().catch(err => {
2693
+ exitAlternateScreen();
2694
+ console.error(`\n ${C.red}Fatal: ${err.message}${C.reset}\n`);
2695
+ process.exit(1);
2696
+ });
2697
+ }