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
@@ -2,15 +2,15 @@ import readline from 'node:readline';
2
2
  import { stdin as input, stdout as output } from 'node:process';
3
3
  import { eventBus, Events } from './eventBus.js';
4
4
  import { AgentStates } from './agentState.js';
5
- import { ansiBackground, ansiForeground, getThemePalette } from './theme.js';
5
+ import { ansiBackground, ansiForeground, getThemePalette, isColorDisabled } from './theme.js';
6
6
 
7
7
 
8
8
  export const C = Object.freeze({
9
- reset: '\x1b[0m',
10
- bold: '\x1b[1m',
11
- dim: '\x1b[2m',
12
- italic: '\x1b[3m',
13
- underline: '\x1b[4m',
9
+ get reset() { return isColorDisabled() ? '' : '\x1b[0m'; },
10
+ get bold() { return isColorDisabled() ? '' : '\x1b[1m'; },
11
+ get dim() { return isColorDisabled() ? '' : '\x1b[2m'; },
12
+ get italic() { return isColorDisabled() ? '' : '\x1b[3m'; },
13
+ get underline() { return isColorDisabled() ? '' : '\x1b[4m'; },
14
14
 
15
15
  get sakura() { return ansiForeground(getThemePalette().primary); },
16
16
  get roseGlow() { return ansiForeground(getThemePalette().secondary); },
@@ -221,6 +221,105 @@ export function sliceLineForDisplay(str, cursor, maxWidth) {
221
221
  return { displayLine, cursorVisualOffset };
222
222
  }
223
223
 
224
+ export function getMultilinePromptLayout(text, cursor = 0, maxWidth = 80, maxRows = 6) {
225
+ const safeWidth = Math.max(10, Number(maxWidth) || 80);
226
+ const safeMaxRows = Math.max(1, Math.min(10, Number(maxRows) || 6));
227
+ const rawParagraphs = String(text || '').split('\n');
228
+ const lines = [];
229
+ let cursorRow = 0;
230
+ let cursorCol = 0;
231
+ let runningCharCount = 0;
232
+
233
+ for (let pIdx = 0; pIdx < rawParagraphs.length; pIdx++) {
234
+ const paragraph = rawParagraphs[pIdx];
235
+ const segments = getGraphemeSegments(paragraph);
236
+ let currentLineSegments = [];
237
+ let currentLineWidth = 0;
238
+ let lineStartIndex = runningCharCount;
239
+
240
+ for (let sIdx = 0; sIdx < segments.length; sIdx++) {
241
+ const seg = segments[sIdx];
242
+ const segWidth = getStringDisplayWidth(seg.segment);
243
+
244
+ if (currentLineWidth + segWidth > safeWidth && currentLineSegments.length > 0) {
245
+ const lineText = currentLineSegments.map(s => s.segment).join('');
246
+ lines.push({
247
+ text: lineText,
248
+ width: currentLineWidth,
249
+ start: lineStartIndex,
250
+ end: lineStartIndex + lineText.length,
251
+ });
252
+ lineStartIndex += lineText.length;
253
+ currentLineSegments = [];
254
+ currentLineWidth = 0;
255
+ }
256
+
257
+ currentLineSegments.push(seg);
258
+ currentLineWidth += segWidth;
259
+ }
260
+
261
+ const lineText = currentLineSegments.map(s => s.segment).join('');
262
+ lines.push({
263
+ text: lineText,
264
+ width: currentLineWidth,
265
+ start: lineStartIndex,
266
+ end: lineStartIndex + lineText.length,
267
+ });
268
+
269
+ runningCharCount += paragraph.length;
270
+ if (pIdx < rawParagraphs.length - 1) {
271
+ runningCharCount += 1;
272
+ }
273
+ }
274
+
275
+ const safeCursor = Math.max(0, Math.min((text || '').length, cursor));
276
+ let foundCursor = false;
277
+
278
+ for (let i = 0; i < lines.length; i++) {
279
+ const line = lines[i];
280
+ const isLastLine = i === lines.length - 1;
281
+ if (safeCursor >= line.start && (safeCursor <= line.end || isLastLine)) {
282
+ cursorRow = i;
283
+ const textBeforeCursorInLine = (text || '').slice(line.start, safeCursor);
284
+ cursorCol = getStringDisplayWidth(textBeforeCursorInLine);
285
+ foundCursor = true;
286
+ break;
287
+ }
288
+ }
289
+
290
+ if (!foundCursor) {
291
+ cursorRow = Math.max(0, lines.length - 1);
292
+ cursorCol = lines[cursorRow] ? lines[cursorRow].width : 0;
293
+ }
294
+
295
+ let visibleStart = 0;
296
+ let visibleEnd = lines.length;
297
+
298
+ if (lines.length > safeMaxRows) {
299
+ if (cursorRow >= safeMaxRows) {
300
+ visibleStart = cursorRow - safeMaxRows + 1;
301
+ visibleEnd = cursorRow + 1;
302
+ } else {
303
+ visibleStart = 0;
304
+ visibleEnd = safeMaxRows;
305
+ }
306
+ }
307
+
308
+ const visibleLines = lines.slice(visibleStart, visibleEnd);
309
+ const displayCursorRow = cursorRow - visibleStart;
310
+
311
+ return {
312
+ lines,
313
+ visibleLines: visibleLines.map(l => l.text),
314
+ visibleStart,
315
+ totalLines: lines.length,
316
+ cursorRow: displayCursorRow,
317
+ cursorCol,
318
+ hasMoreAbove: visibleStart > 0,
319
+ hasMoreBelow: visibleEnd < lines.length,
320
+ };
321
+ }
322
+
224
323
  export function getFullWidth() {
225
324
  return process.stdout.columns || 80;
226
325
  }
@@ -232,61 +331,86 @@ export function wrapText(text, maxWidth = 72) {
232
331
  const lines = [];
233
332
  const rawParagraphs = text.split('\n');
234
333
 
235
- const tokenize = para => {
236
- const raw = para.split(/\s+/).filter(Boolean);
237
- const tokens = [];
238
- for (let i = 0; i < raw.length; i++) {
239
- if (raw[i] === '•' && i + 1 < raw.length) {
240
- tokens.push('• ' + raw[i + 1]);
241
- i++;
242
- } else {
243
- tokens.push(raw[i]);
244
- }
245
- }
246
- return tokens;
247
- };
248
-
249
334
  for (const para of rawParagraphs) {
250
335
  if (!para.trim()) {
251
336
  lines.push('');
252
337
  continue;
253
338
  }
254
339
 
255
- const words = tokenize(para);
340
+ const plainPara = stripAnsi(para);
341
+ const spaceMatch = plainPara.match(/^(\s+)/);
342
+ const initialIndent = spaceMatch ? spaceMatch[1] : '';
343
+
344
+ let continuationIndent = initialIndent;
345
+ const bulletMatch = plainPara.match(/^(\s*)([↳•\-\*]|\[\d+\])\s+/);
346
+ if (bulletMatch) {
347
+ continuationIndent = ' '.repeat(Math.min(12, getStringDisplayWidth(bulletMatch[0])));
348
+ } else if (initialIndent) {
349
+ continuationIndent = initialIndent;
350
+ }
351
+
352
+ const rawWords = para.trim().split(/\s+/).filter(Boolean);
353
+ const words = [];
354
+ for (let i = 0; i < rawWords.length; i++) {
355
+ if (rawWords[i] === '•' && i + 1 < rawWords.length) {
356
+ words.push('• ' + rawWords[i + 1]);
357
+ i++;
358
+ } else {
359
+ words.push(rawWords[i]);
360
+ }
361
+ }
362
+
256
363
  const paraLines = [];
257
- let currentLine = '';
364
+ let currentLine = initialIndent;
365
+ let activeAnsi = '';
258
366
 
259
367
  for (const word of words) {
260
368
  if (!word) continue;
261
- const testLine = currentLine ? (currentLine + ' ' + word) : word;
369
+ const testLine = currentLine.trim() ? (currentLine + ' ' + word) : (currentLine + word);
262
370
  const testWidth = getStringDisplayWidth(testLine);
263
371
 
264
372
  if (testWidth <= maxWidth) {
265
373
  currentLine = testLine;
266
374
  } else {
267
- if (currentLine) {
268
- paraLines.push(currentLine);
269
- currentLine = word;
375
+ if (currentLine.trim()) {
376
+ paraLines.push(currentLine + (activeAnsi && !currentLine.endsWith('\x1b[0m') ? '\x1b[0m' : ''));
377
+ currentLine = continuationIndent + activeAnsi + word;
270
378
  } else {
271
- let chunk = '';
379
+ let chunk = continuationIndent + activeAnsi;
272
380
  for (const ch of word) {
273
381
  if (getStringDisplayWidth(chunk + ch) <= maxWidth) {
274
382
  chunk += ch;
275
383
  } else {
276
- paraLines.push(chunk);
277
- chunk = ch;
384
+ if (chunk.trim()) {
385
+ paraLines.push(chunk + (activeAnsi && !chunk.endsWith('\x1b[0m') ? '\x1b[0m' : ''));
386
+ }
387
+ chunk = continuationIndent + activeAnsi + ch;
278
388
  }
279
389
  }
280
390
  currentLine = chunk;
281
391
  }
282
392
  }
393
+
394
+ const ansiMatches = word.match(/\x1b\[[0-9;]*[a-zA-Z]/g);
395
+ if (ansiMatches) {
396
+ for (const m of ansiMatches) {
397
+ if (m === '\x1b[0m' || m === '\x1b[m') {
398
+ activeAnsi = '';
399
+ } else {
400
+ activeAnsi += m;
401
+ }
402
+ }
403
+ }
404
+ }
405
+
406
+ if (currentLine.trim()) {
407
+ paraLines.push(currentLine);
283
408
  }
284
- if (currentLine) paraLines.push(currentLine);
285
409
 
286
- if (paraLines.length >= 2 && !/\s/.test(paraLines[paraLines.length - 1].trim())) {
410
+ if (paraLines.length >= 2 && !/\s/.test(stripAnsi(paraLines[paraLines.length - 1]).trim())) {
287
411
  const last = paraLines[paraLines.length - 1];
288
412
  const prev = paraLines[paraLines.length - 2];
289
- const candidate = prev + ' ' + last;
413
+ const candidate = prev + ' ' + last.trim();
290
414
  if (getStringDisplayWidth(candidate) <= maxWidth) {
291
415
  paraLines[paraLines.length - 2] = candidate;
292
416
  paraLines.pop();
@@ -300,6 +424,7 @@ export function wrapText(text, maxWidth = 72) {
300
424
 
301
425
 
302
426
 
427
+
303
428
  export function stripAnsi(str) {
304
429
  return String(str ?? '').replace(/\x1b(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1b\\))/g, '');
305
430
  }
@@ -340,13 +465,13 @@ export function fitStyledText(styledText, maxWidth) {
340
465
 
341
466
  export function printSolidBannerRow(styledText, targetWidth, requestedBg = C.bgPanel, indent = '') {
342
467
  const terminalWidth = getFullWidth();
343
- const rowWidth = Math.max(1, Math.min(targetWidth || terminalWidth, terminalWidth) - 1);
344
- const contentWidth = Math.max(0, rowWidth - getStringDisplayWidth(indent));
345
- const fitted = fitStyledText(styledText, contentWidth);
346
- const background = requestedBg === C.bgPanelHeader ? C.bgPanelHeader : C.bgPanel;
468
+ const rowWidth = Math.max(1, (targetWidth || terminalWidth) - getStringDisplayWidth(indent));
469
+ const fitted = fitStyledText(styledText, rowWidth);
470
+ const pad = Math.max(0, rowWidth - getStringDisplayWidth(fitted));
471
+ const background = requestedBg === C.bgPanelHeader ? C.bgPanelHeader : (requestedBg || C.bgPanel);
347
472
  const fixedStyled = fitted.replace(/\x1b\[0m/g, C.reset + background + C.white);
348
473
 
349
- console.log(indent + background + C.white + fixedStyled + '\x1b[0K' + C.reset);
474
+ console.log(indent + background + C.white + fixedStyled + ' '.repeat(pad) + C.reset);
350
475
  }
351
476
 
352
477
  let activeTuiPrompt = null;
@@ -464,11 +589,11 @@ export function formatSolidPanelLines({ title, lines, width, indent = '', header
464
589
  const text = typeof raw === 'string' ? raw : (raw?.text ?? '');
465
590
  const sublines = String(text).split('\n');
466
591
  for (const subline of sublines) {
467
- const isBoxTable = /^\s*[\x1b\[0-9;]*[┌├└│]/.test(subline);
592
+ const isBoxTable = /^\s*(\x1b\[[0-9;]*[a-zA-Z])*[┌├└│]/.test(subline) || /^[┌├└│]/.test(stripAnsi(subline).trimStart());
468
593
  const wrappedSublines = isBoxTable ? [subline] : wrapText(subline, maxContentWidth);
469
594
  for (const wrapped of (wrappedSublines.length > 0 ? wrappedSublines : [''])) {
470
595
  const rowText = isBoxTable ? (wrapped.startsWith(indent) ? wrapped : `${indent}${wrapped}`) : `${indent} ${wrapped}`;
471
- const fitted = fitStyledText(rowText, w);
596
+ const fitted = isBoxTable ? rowText : fitStyledText(rowText, w);
472
597
  const pad = Math.max(0, w - getStringDisplayWidth(fitted));
473
598
  const themed = fitted.replace(/\x1b\[0m/g, C.reset + bodyBg + C.white);
474
599
  rows.push(`${bodyBg}${C.white}${themed}${' '.repeat(pad)}${C.reset}`);
@@ -642,7 +767,7 @@ export function formatRichCardLines({
642
767
  const normStatus = String(status || 'info').toLowerCase();
643
768
  const st = STATUS_THEMES[normStatus] || STATUS_THEMES.info;
644
769
  const termWidth = width || getFullWidth();
645
- const blockWidth = Math.max(30, Math.min(84, termWidth - getStringDisplayWidth(indent) - 2));
770
+ const blockWidth = Math.max(30, termWidth - (indent ? getStringDisplayWidth(indent) : 0));
646
771
  const rows = [];
647
772
 
648
773
  const icon = st.icon;
@@ -695,15 +820,32 @@ export function printRichStatusCard({ status, title, lines = [], footer = '', wi
695
820
  console.log(`\n${rows.join('\n')}\n`);
696
821
  }
697
822
 
823
+ export function cleanTableCell(raw) {
824
+ if (!raw) return '';
825
+ return String(raw)
826
+ .replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, (m, text, url) => {
827
+ const t = text.trim();
828
+ const u = url.trim();
829
+ return t === u ? u : `${t} (${u})`;
830
+ })
831
+ .replace(/\*\*/g, '')
832
+ .replace(/`/g, '')
833
+ .replace(/\s+/g, ' ')
834
+ .trim();
835
+ }
836
+
698
837
  export function formatRichTable(headers = [], rows = [], maxWidth = getFullWidth() - 4) {
699
838
  if (!headers.length && !rows.length) return [];
700
839
  const colCount = Math.max(headers.length, ...rows.map(r => r.length));
701
- const colWidths = new Array(colCount).fill(3);
840
+ const colWidths = new Array(colCount).fill(4);
841
+
842
+ const cleanHeaders = headers.map(cleanTableCell);
843
+ const cleanRows = rows.map(r => r.map(cleanTableCell));
702
844
 
703
845
  for (let c = 0; c < colCount; c++) {
704
- const headerLen = getStringDisplayWidth(headers[c] || '');
846
+ const headerLen = getStringDisplayWidth(cleanHeaders[c] || '');
705
847
  colWidths[c] = Math.max(colWidths[c], headerLen);
706
- for (const row of rows) {
848
+ for (const row of cleanRows) {
707
849
  const cellLen = getStringDisplayWidth(row[c] || '');
708
850
  colWidths[c] = Math.max(colWidths[c], cellLen);
709
851
  }
@@ -725,10 +867,14 @@ export function formatRichTable(headers = [], rows = [], maxWidth = getFullWidth
725
867
 
726
868
  output.push(`${C.slate}${topBorder}${C.reset}`);
727
869
 
728
- if (headers.length > 0) {
870
+ if (cleanHeaders.length > 0) {
729
871
  const headerCells = [];
730
872
  for (let c = 0; c < colCount; c++) {
731
- const text = fitStyledText(headers[c] || '', colWidths[c]);
873
+ const rawText = cleanHeaders[c] || '';
874
+ const textLen = getStringDisplayWidth(rawText);
875
+ const text = textLen > colWidths[c] && colWidths[c] > 3
876
+ ? fitStyledText(rawText, colWidths[c] - 1) + '…'
877
+ : fitStyledText(rawText, colWidths[c]);
732
878
  const pad = Math.max(0, colWidths[c] - getStringDisplayWidth(text));
733
879
  headerCells.push(`${C.bold}${C.gold}${text}${C.reset}${' '.repeat(pad)}`);
734
880
  }
@@ -736,11 +882,14 @@ export function formatRichTable(headers = [], rows = [], maxWidth = getFullWidth
736
882
  output.push(`${C.slate}${midBorder}${C.reset}`);
737
883
  }
738
884
 
739
- for (const row of rows) {
885
+ for (const row of cleanRows) {
740
886
  const rowCells = [];
741
887
  for (let c = 0; c < colCount; c++) {
742
888
  const cellRaw = row[c] || '—';
743
- const text = fitStyledText(cellRaw, colWidths[c]);
889
+ const cellLen = getStringDisplayWidth(cellRaw);
890
+ const text = cellLen > colWidths[c] && colWidths[c] > 3
891
+ ? fitStyledText(cellRaw, colWidths[c] - 1) + '…'
892
+ : fitStyledText(cellRaw, colWidths[c]);
744
893
  const pad = Math.max(0, colWidths[c] - getStringDisplayWidth(text));
745
894
  rowCells.push(`${C.white}${text}${C.reset}${' '.repeat(pad)}`);
746
895
  }
@@ -754,6 +903,8 @@ export function formatRichTable(headers = [], rows = [], maxWidth = getFullWidth
754
903
  export function formatTerminalMarkdown(text) {
755
904
  if (!text) return '';
756
905
  return text
906
+ .replace(/^#####?\s*(.+)$/gm, ` ${C.bold}${C.lavender}• $1${C.reset}`)
907
+ .replace(/^####\s*(.+)$/gm, `${C.bold}${C.cyberCyan}▸ $1${C.reset}`)
757
908
  .replace(/^###\s*(.+)$/gm, `${C.bold}${C.gold}✦ $1${C.reset}`)
758
909
  .replace(/^##\s*(.+)$/gm, `${C.bold}${C.roseGlow}■ $1${C.reset}`)
759
910
  .replace(/^#\s*(.+)$/gm, `${C.bold}${C.sakura}━━━ $1 ━━━${C.reset}`)
@@ -763,7 +914,8 @@ export function formatTerminalMarkdown(text) {
763
914
  .replace(/\*\*([^*]+)\*\*/g, `${C.bold}${C.white}$1${C.reset}`)
764
915
  .replace(/(?<!\*)\*([^\*\n]+)\*(?!\*)/g, `${C.italic}${C.white}$1${C.reset}`)
765
916
  .replace(/`([^`]+)`/g, `${C.cyberCyan}$1${C.reset}`)
766
- .replace(/^(\s*)[*•-]\s+/gm, `$1 ${C.sakura}•${C.reset} `)
917
+ .replace(/^(\s*)(\d+)\.\s+/gm, `$1 ${C.gold}$2.${C.reset} `)
918
+ .replace(/^(\s*)[*•+-]\s+/gm, `$1 ${C.sakura}•${C.reset} `)
767
919
  .replace(/^\s*[-*_]{3,}\s*$/gm, ` ${C.slate}${'─'.repeat(Math.max(20, getFullWidth() - 6))}${C.reset}`);
768
920
  }
769
921
 
@@ -784,7 +936,7 @@ export function parseMarkdownTableRow(text) {
784
936
  export function isMarkdownTableSeparator(cells) {
785
937
  return Array.isArray(cells)
786
938
  && cells.length > 0
787
- && cells.every(cell => /^:?-{3,}:?$/.test(cell.replace(/\s+/g, '')));
939
+ && cells.every(cell => /^:?-+:?$/.test(cell.replace(/\s+/g, '')));
788
940
  }
789
941
 
790
942
  export function expandMarkdownLinks(text) {
@@ -821,8 +973,8 @@ export function formatMarkdownLines(text, maxWidth = getFullWidth() - 8) {
821
973
  const cells = parseMarkdownTableRow(rawLine);
822
974
 
823
975
  if (pendingHeader) {
824
- if (cells && cells.length === pendingHeader.length && isMarkdownTableSeparator(cells)) {
825
- tableHeaders = pendingHeader.map(c => c.replace(/\*\*/g, '').replace(/`/g, ''));
976
+ if (cells && isMarkdownTableSeparator(cells)) {
977
+ tableHeaders = pendingHeader.map(cleanTableCell);
826
978
  pendingHeader = null;
827
979
  tableRows = [];
828
980
  continue;
@@ -833,8 +985,12 @@ export function formatMarkdownLines(text, maxWidth = getFullWidth() - 8) {
833
985
  }
834
986
 
835
987
  if (tableHeaders) {
836
- if (cells && cells.length === tableHeaders.length && !isMarkdownTableSeparator(cells)) {
837
- tableRows.push(cells.map(c => c.replace(/\*\*/g, '').replace(/`/g, '')));
988
+ if (cells && !isMarkdownTableSeparator(cells)) {
989
+ const normalizedRow = new Array(tableHeaders.length).fill('');
990
+ for (let c = 0; c < tableHeaders.length; c++) {
991
+ normalizedRow[c] = cleanTableCell(cells[c] || '');
992
+ }
993
+ tableRows.push(normalizedRow);
838
994
  continue;
839
995
  } else {
840
996
  flushTable();
@@ -914,7 +1070,7 @@ export function getWelcomeHeroLines() {
914
1070
  padItem(c + '◆', 'Music & OST', '"OSTs from In Blossom & Ruyi Pavilion, /play Ginkgo"'),
915
1071
  padItem(e + '◆', 'Endorsements', '"Versace Global Ambassador & fashion partnerships"'),
916
1072
  padItem(l + '◆', 'Tools / MCP', '"@search_works 2026, /mcp list, /stats, /help"'),
917
- ` ${s}╰${'─'.repeat(boxWidth)}╯${rst}`,
1073
+ ` ${s}╰${'─'.repeat(innerWidth)}╯${rst}`,
918
1074
  '',
919
1075
  ...shortcutLines,
920
1076
  '',
@@ -961,12 +1117,11 @@ export function setupOutputInterception(tuiPrompt, _isProcessingFn) {
961
1117
  if (tuiPrompt && tuiPrompt.screenBuffer && !tuiPrompt.isDisposed && !isTuiWriting) {
962
1118
  const str = String(chunk);
963
1119
  if (
964
- str.startsWith('\x1b[H') ||
965
1120
  str.startsWith('\x1b[?1049') ||
966
- str.startsWith('\x1b[?25') ||
967
1121
  str.startsWith('\x1b[?1000') ||
968
1122
  str.startsWith('\x1b[?1002') ||
969
- str.startsWith('\x1b[?1006')
1123
+ str.startsWith('\x1b[?1006') ||
1124
+ str.startsWith('\x1b[?1015')
970
1125
  ) {
971
1126
  return _rawStdoutWrite(chunk, encoding, callback);
972
1127
  }
@@ -1025,7 +1180,7 @@ export function formatTaskPlanBanner(plan) {
1025
1180
  const barEmpty = 10 - barFilled;
1026
1181
  const progressBar = `[${'█'.repeat(barFilled)}${'░'.repeat(barEmpty)}] ${pct}%`;
1027
1182
 
1028
- const termWidth = Math.min(80, Math.max(20, getFullWidth() - 2));
1183
+ const termWidth = Math.max(20, getFullWidth());
1029
1184
  const bg = C.bgPanel;
1030
1185
  const bgHeader = C.bgPanelHeader;
1031
1186
 
@@ -1077,6 +1232,7 @@ export function formatTaskPlanBanner(plan) {
1077
1232
  let isAlternateScreenActive = false;
1078
1233
 
1079
1234
  export function enterAlternateScreen() {
1235
+ if (isColorDisabled()) return;
1080
1236
  if (process.stdout.isTTY && !isAlternateScreenActive) {
1081
1237
  isAlternateScreenActive = true;
1082
1238
  _rawStdoutWrite('\x1b[?1049h\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1005l\x1b[?1006l\x1b[?1015l\x1b[?25l\x1b[H\x1b[2J');
@@ -1090,20 +1246,27 @@ export function exitAlternateScreen() {
1090
1246
  }
1091
1247
  }
1092
1248
 
1093
- export function getTuiViewportLayout(termRows = 24) {
1249
+ export function getTuiViewportLayout(termRows = 24, promptLinesCount = 1) {
1094
1250
  const parsedRows = Number(termRows);
1095
1251
  const rows = Math.max(10, Number.isFinite(parsedRows) ? Math.floor(parsedRows) : 24);
1096
- const viewportStartRow = 1;
1097
- const statusBarRow = rows - 4;
1098
- const promptRow = rows - 2;
1252
+ const extraPromptRows = Math.max(0, Math.min(Math.floor(rows / 3), (Number(promptLinesCount) || 1) - 1));
1253
+ const promptHeight = 1 + extraPromptRows;
1099
1254
  const separatorRow = rows - 1;
1255
+ const promptStartRow = (rows - 2) - extraPromptRows;
1256
+ const promptEndRow = promptStartRow + promptHeight - 1;
1257
+ const statusBarRow = (rows - 4) - extraPromptRows;
1258
+ const viewportStartRow = 1;
1259
+ const viewportHeight = Math.max(1, statusBarRow - viewportStartRow);
1100
1260
 
1101
1261
  return {
1102
1262
  termRows: rows,
1103
1263
  viewportStartRow,
1104
- viewportHeight: Math.max(1, statusBarRow - viewportStartRow),
1264
+ viewportHeight,
1105
1265
  statusBarRow,
1106
- promptRow,
1266
+ promptRow: promptStartRow,
1267
+ promptStartRow,
1268
+ promptEndRow,
1269
+ promptHeight,
1107
1270
  separatorRow,
1108
1271
  };
1109
1272
  }
@@ -1215,4 +1378,263 @@ export class VirtualScreenBuffer {
1215
1378
  }
1216
1379
  }
1217
1380
 
1381
+ function getMilestonePalette(era = '', year = '') {
1382
+ if (isColorDisabled()) {
1383
+ return {
1384
+ yearBg: '',
1385
+ yearFg: '',
1386
+ badgeBg: '',
1387
+ badgeFg: '',
1388
+ tagBg: '',
1389
+ tagFg: '',
1390
+ };
1391
+ }
1392
+
1393
+ const eraLower = String(era || '').toLowerCase();
1394
+ const yearNum = parseInt(year, 10) || 0;
1395
+
1396
+
1397
+ if (eraLower.includes('snh') || (yearNum >= 2013 && yearNum <= 2017)) {
1398
+ return {
1399
+ yearBg: '\x1b[48;2;180;36;85m',
1400
+ yearFg: '\x1b[38;2;255;255;255m',
1401
+ badgeBg: '\x1b[48;2;60;25;40m',
1402
+ badgeFg: '\x1b[38;2;255;185;210m',
1403
+ tagBg: '\x1b[48;2;40;22;32m',
1404
+ tagFg: '\x1b[38;2;230;170;190m',
1405
+ };
1406
+ }
1407
+
1408
+
1409
+ if (eraLower.includes('solo') || eraLower.includes('star48') || (yearNum >= 2018 && yearNum <= 2023)) {
1410
+ return {
1411
+ yearBg: '\x1b[48;2;115;45;160m',
1412
+ yearFg: '\x1b[38;2;255;255;255m',
1413
+ badgeBg: '\x1b[48;2;55;30;70m',
1414
+ badgeFg: '\x1b[38;2;225;175;255m',
1415
+ tagBg: '\x1b[48;2;38;22;48m',
1416
+ tagFg: '\x1b[38;2;200;160;230m',
1417
+ };
1418
+ }
1419
+
1420
+
1421
+ return {
1422
+ yearBg: '\x1b[48;2;18;110;120m',
1423
+ yearFg: '\x1b[38;2;255;255;255m',
1424
+ badgeBg: '\x1b[48;2;20;50;55m',
1425
+ badgeFg: '\x1b[38;2;130;240;250m',
1426
+ tagBg: '\x1b[48;2;16;35;40m',
1427
+ tagFg: '\x1b[38;2;110;200;210m',
1428
+ };
1429
+ }
1430
+
1431
+ export function formatCareerTimelineGraphic(items = [], width = getFullWidth()) {
1432
+ const termWidth = Math.max(40, width);
1433
+ const rows = [];
1434
+
1435
+ const headerBg = isColorDisabled() ? '' : '\x1b[48;2;65;20;42m';
1436
+ const headerFg = isColorDisabled() ? '' : '\x1b[38;2;255;220;235m';
1437
+
1438
+ rows.push(`${headerBg}${headerFg}${C.bold} ◆ JU JINGYI • 鞠婧祎 CAREER MILESTONES TIMELINE ${C.reset}`);
1439
+ rows.push(`${C.dim} Artistic eras, breakthrough performances, and landmark milestones${C.reset}`);
1440
+ rows.push('');
1441
+
1442
+ for (let i = 0; i < items.length; i++) {
1443
+ const item = items[i];
1444
+ const pal = getMilestonePalette(item.era, item.year);
1445
+
1446
+ const yearStr = item.year ? ` ${item.year} ` : ' ERA ';
1447
+ const yearBadge = `${pal.yearBg}${pal.yearFg}${C.bold}${yearStr}${C.reset}`;
1448
+
1449
+ const titleText = `${C.white}${C.bold}${item.title || item.titleEn || ''}${C.reset}`;
1450
+ const cnText = item.titleCn ? ` ${C.slate}(${item.titleCn})${C.reset}` : '';
1451
+ const badgeText = item.badge
1452
+ ? ` ${pal.badgeBg}${pal.badgeFg}${C.bold} ✨ ${item.badge} ${C.reset}`
1453
+ : '';
1454
+
1455
+ rows.push(` ${yearBadge} ${titleText}${cnText}${badgeText}`);
1456
+
1457
+ const metaParts = [];
1458
+ if (item.category) metaParts.push(item.category);
1459
+ if (item.date) metaParts.push(item.date);
1460
+ if (metaParts.length > 0) {
1461
+ const metaText = metaParts.join(' • ');
1462
+ rows.push(` ${pal.tagBg}${pal.tagFg} ${metaText} ${C.reset}`);
1463
+ }
1464
+
1465
+ if (item.summary) {
1466
+ const wrapped = wrapText(item.summary, termWidth - 8);
1467
+ for (const wLine of wrapped) {
1468
+ rows.push(` ${C.slate}${wLine}${C.reset}`);
1469
+ }
1470
+ }
1471
+
1472
+ if (Array.isArray(item.highlights) && item.highlights.length > 0) {
1473
+ for (const h of item.highlights) {
1474
+ const cleanH = String(h || '').replace(/^[•\-\*]\s*/, '');
1475
+ const wrappedH = wrapText(cleanH, termWidth - 10);
1476
+ for (let hi = 0; hi < wrappedH.length; hi++) {
1477
+ if (hi === 0) {
1478
+ rows.push(` ${C.sakura}•${C.reset} ${C.white}${wrappedH[hi]}${C.reset}`);
1479
+ } else {
1480
+ rows.push(` ${C.slate}${wrappedH[hi]}${C.reset}`);
1481
+ }
1482
+ }
1483
+ }
1484
+ }
1485
+
1486
+ if (i < items.length - 1) {
1487
+ rows.push('');
1488
+ }
1489
+ }
1490
+
1491
+ return rows;
1492
+ }
1493
+
1494
+
1495
+ export function formatDebateMatrix(debateResult, width = getFullWidth()) {
1496
+ const termWidth = Math.max(40, width);
1497
+ const rows = [];
1498
+ const r = debateResult || {};
1499
+
1500
+ rows.push(`${C.bold}${C.sakura}◆ MULTI-AGENT ADVERSARIAL DEBATE MATRIX${C.reset}`);
1501
+ rows.push(`${C.slate}${'═'.repeat(Math.min(termWidth - 2, 70))}${C.reset}`);
1502
+ rows.push(`${C.bold}${C.gold}Topic / Claim:${C.reset} ${C.white}${r.topic || 'N/A'}${C.reset}`);
1503
+ rows.push('');
1504
+
1505
+ rows.push(`${C.bold}${C.emerald}┌── ADVOCATE (Corroborating Primary Evidence) ────────────────────┐${C.reset}`);
1506
+ const advLines = formatMarkdownLines(r.advocate || 'No evidence provided.', termWidth - 8);
1507
+ for (const al of advLines) {
1508
+ rows.push(` ${C.slate}│${C.reset} ${al}`);
1509
+ }
1510
+ rows.push(`${C.bold}${C.emerald}└──${'─'.repeat(Math.min(termWidth - 6, 62))}┘${C.reset}`);
1511
+ rows.push('');
1512
+
1513
+ rows.push(`${C.bold}${C.roseGlow}┌── CRITIC (Counter-Evidence & Skeptic Findings) ─────────────────┐${C.reset}`);
1514
+ const critLines = formatMarkdownLines(r.critic || 'No counter-evidence provided.', termWidth - 8);
1515
+ for (const cl of critLines) {
1516
+ rows.push(` ${C.slate}│${C.reset} ${cl}`);
1517
+ }
1518
+ rows.push(`${C.bold}${C.roseGlow}└──${'─'.repeat(Math.min(termWidth - 6, 62))}┘${C.reset}`);
1519
+ rows.push('');
1520
+
1521
+ rows.push(`${C.bold}${C.cyberCyan}┌── NEUTRAL JUDGE SYNTHESIS & VERDICT ────────────────────────────┐${C.reset}`);
1522
+ const judgeLines = formatMarkdownLines(r.verdict || 'Synthesis unavailable.', termWidth - 8);
1523
+ for (const jl of judgeLines) {
1524
+ rows.push(` ${C.slate}│${C.reset} ${jl}`);
1525
+ }
1526
+ rows.push(`${C.bold}${C.cyberCyan}└──${'─'.repeat(Math.min(termWidth - 6, 62))}┘${C.reset}`);
1527
+
1528
+ return rows;
1529
+ }
1530
+
1531
+ export function getCategoryBadge(category = '') {
1532
+ const norm = String(category || '').trim().toUpperCase();
1533
+ switch (norm) {
1534
+ case 'DRAMA':
1535
+ return { icon: '🌸', label: 'DRAMA', color: C.lavender, bg: '\x1b[48;2;48;32;72m' };
1536
+ case 'MUSIC':
1537
+ return { icon: '🎵', label: 'MUSIC', color: C.sakura, bg: '\x1b[48;2;72;32;56m' };
1538
+ case 'FASHION':
1539
+ return { icon: '✨', label: 'FASHION', color: C.cyberCyan, bg: '\x1b[48;2;24;48;64m' };
1540
+ case 'MILESTONE':
1541
+ case 'AWARDS':
1542
+ return { icon: '🌸', label: 'AWARDS', color: C.gold, bg: '\x1b[48;2;64;52;24m' };
1543
+ case 'ANALYSIS':
1544
+ return { icon: '📖', label: 'ANALYSIS', color: C.emerald, bg: '\x1b[48;2;24;56;36m' };
1545
+ case 'TRIVIA':
1546
+ return { icon: '💬', label: 'TRIVIA', color: C.amber, bg: '\x1b[48;2;64;40;20m' };
1547
+ default:
1548
+ return { icon: '✨', label: norm || 'TOPIC', color: C.white, bg: '\x1b[48;2;40;40;48m' };
1549
+ }
1550
+ }
1551
+
1552
+ export function formatWorkerProgressMatrix({ workers = [], batchId = '', elapsed = '0.0', termWidth = getFullWidth() } = {}) {
1553
+ const rows = [];
1554
+ const headerBg = isColorDisabled() ? '' : '\x1b[48;2;65;20;42m';
1555
+ const headerFg = isColorDisabled() ? '' : '\x1b[38;2;255;220;235m';
1556
+
1557
+ rows.push(` ${headerBg}${headerFg}${C.bold} 🌸 CONCURRENT RESEARCH SUBAGENTS [${workers.length} Workers • ${elapsed}s] ${C.reset}`);
1558
+ rows.push('');
1559
+
1560
+ for (const w of workers) {
1561
+ const badge = getCategoryBadge(w.category);
1562
+ let statePill = '';
1563
+ let stateText = 'Working';
1564
+ if (w.state === 'complete' || w.state === 'done') {
1565
+ stateText = 'Done';
1566
+ statePill = isColorDisabled() ? '[Done]' : '\x1b[48;2;20;65;35m\x1b[38;2;80;240;140m\x1b[1m Done \x1b[0m';
1567
+ } else if (w.state === 'error' || w.state === 'failed') {
1568
+ stateText = 'Error';
1569
+ statePill = isColorDisabled() ? '[Error]' : '\x1b[48;2;70;20;25m\x1b[38;2;255;120;120m\x1b[1m Error \x1b[0m';
1570
+ } else {
1571
+ stateText = 'Working';
1572
+ statePill = isColorDisabled() ? '[Working]' : '\x1b[48;2;20;45;65m\x1b[38;2;80;210;255m\x1b[1m Working \x1b[0m';
1573
+ }
1574
+
1575
+ const catPill = isColorDisabled()
1576
+ ? `[${badge.icon} ${badge.label}]`
1577
+ : `${badge.bg}${badge.color}${C.bold} ${badge.icon} ${badge.label} ${C.reset}`;
1578
+ const toolLabel = w.currentTool ? `> ${w.currentTool}` : (w.statusMessage || w.question || '');
1579
+ const cleanTool = stripAnsi(toolLabel).slice(0, 32);
1580
+
1581
+ rows.push(` ${statePill} ${C.bold}W${w.index || 1} [${badge.icon} ${badge.label}]${C.reset} ${C.white}${cleanTool}${C.reset} ${C.dim}${stateText}${C.reset}`);
1582
+ }
1583
+
1584
+ rows.push('');
1585
+ return rows;
1586
+ }
1587
+
1588
+ export function formatExecutiveFindings({ batchResult, termWidth = getFullWidth() } = {}) {
1589
+ const width = Math.max(50, termWidth - 4);
1590
+ const rows = [];
1591
+ const results = batchResult?.results || [];
1592
+ const total = batchResult?.tasksCount || results.length;
1593
+ const succeeded = batchResult?.successfulCount ?? results.filter(r => Boolean(r.answer)).length;
1594
+ const elapsed = batchResult?.elapsed || '0.0';
1595
+
1596
+ const headerBg = isColorDisabled() ? '' : '\x1b[48;2;65;20;42m';
1597
+ const headerFg = isColorDisabled() ? '' : '\x1b[38;2;255;220;235m';
1598
+
1599
+ rows.push('');
1600
+ rows.push(` ${headerBg}${headerFg}${C.bold} 🌸 KIKU EXECUTIVE RESEARCH FINDINGS [${succeeded}/${total} Succeeded • ${elapsed}s] ${C.reset}`);
1601
+ rows.push('');
1602
+
1603
+ for (const r of results) {
1604
+ const badge = getCategoryBadge(r.category);
1605
+ const catPill = isColorDisabled()
1606
+ ? `[${badge.icon} ${badge.label}]`
1607
+ : `${badge.bg}${badge.color}${C.bold} ${badge.icon} ${badge.label} ${C.reset}`;
1608
+ const workerTag = isColorDisabled()
1609
+ ? `[Worker ${r.index || 1}]`
1610
+ : `\x1b[48;2;38;30;46m\x1b[38;2;220;200;240m [Worker ${r.index || 1}] \x1b[0m`;
1611
+
1612
+ rows.push(` ${workerTag} ${catPill} ${C.bold}${C.white}[Worker ${r.index || 1}] ${badge.label}: ${r.question}${C.reset}`);
1613
+
1614
+ if (r.answer) {
1615
+ const formattedLines = formatMarkdownLines(r.answer, width - 8);
1616
+ for (const line of formattedLines) {
1617
+ rows.push(` ${line}`);
1618
+ }
1619
+ if (r.toolCalls?.length > 0) {
1620
+ const evidencePill = isColorDisabled()
1621
+ ? '📖 Grounded Evidence:'
1622
+ : '\x1b[48;2;22;42;30m\x1b[38;2;120;230;160m 📖 Grounded Evidence \x1b[0m';
1623
+ rows.push(` ${evidencePill} ${C.dim}Grounded Evidence: ${r.toolCalls.length} tool calls executed successfully.${C.reset}`);
1624
+ }
1625
+ } else {
1626
+ const errorPill = isColorDisabled()
1627
+ ? '[x] Research failed:'
1628
+ : '\x1b[48;2;60;18;22m\x1b[38;2;255;120;120m [x] Research failed \x1b[0m';
1629
+ rows.push(` ${errorPill} ${C.red}${r.error || 'No answer generated'}${C.reset}`);
1630
+ }
1631
+
1632
+ rows.push('');
1633
+ }
1634
+
1635
+ rows.push(` ${C.dim}Tips: Use /sources to review citations • Type /session export to save report as markdown/HTML${C.reset}`);
1636
+ return rows;
1637
+ }
1638
+
1218
1639
  export { _rawStdoutWrite as rawWrite, _rawConsoleLog as rawLog };
1640
+