kiku-agent 1.0.6 → 1.0.8

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.
@@ -228,13 +228,28 @@ export function wrapText(text, maxWidth = 72) {
228
228
  const lines = [];
229
229
  const rawParagraphs = text.split('\n');
230
230
 
231
+ const tokenize = para => {
232
+ const raw = para.split(/\s+/).filter(Boolean);
233
+ const tokens = [];
234
+ for (let i = 0; i < raw.length; i++) {
235
+ if (raw[i] === '•' && i + 1 < raw.length) {
236
+ tokens.push('• ' + raw[i + 1]);
237
+ i++;
238
+ } else {
239
+ tokens.push(raw[i]);
240
+ }
241
+ }
242
+ return tokens;
243
+ };
244
+
231
245
  for (const para of rawParagraphs) {
232
246
  if (!para.trim()) {
233
247
  lines.push('');
234
248
  continue;
235
249
  }
236
250
 
237
- const words = para.split(/\s+/);
251
+ const words = tokenize(para);
252
+ const paraLines = [];
238
253
  let currentLine = '';
239
254
 
240
255
  for (const word of words) {
@@ -246,7 +261,7 @@ export function wrapText(text, maxWidth = 72) {
246
261
  currentLine = testLine;
247
262
  } else {
248
263
  if (currentLine) {
249
- lines.push(currentLine);
264
+ paraLines.push(currentLine);
250
265
  currentLine = word;
251
266
  } else {
252
267
  let chunk = '';
@@ -254,7 +269,7 @@ export function wrapText(text, maxWidth = 72) {
254
269
  if (getStringDisplayWidth(chunk + ch) <= maxWidth) {
255
270
  chunk += ch;
256
271
  } else {
257
- lines.push(chunk);
272
+ paraLines.push(chunk);
258
273
  chunk = ch;
259
274
  }
260
275
  }
@@ -262,9 +277,19 @@ export function wrapText(text, maxWidth = 72) {
262
277
  }
263
278
  }
264
279
  }
265
- if (currentLine) {
266
- lines.push(currentLine);
280
+ if (currentLine) paraLines.push(currentLine);
281
+
282
+ if (paraLines.length >= 2 && !/\s/.test(paraLines[paraLines.length - 1].trim())) {
283
+ const last = paraLines[paraLines.length - 1];
284
+ const prev = paraLines[paraLines.length - 2];
285
+ const candidate = prev + ' ' + last;
286
+ if (getStringDisplayWidth(candidate) <= maxWidth) {
287
+ paraLines[paraLines.length - 2] = candidate;
288
+ paraLines.pop();
289
+ }
267
290
  }
291
+
292
+ lines.push(...paraLines);
268
293
  }
269
294
  return lines;
270
295
  }
@@ -829,7 +854,7 @@ export function formatMarkdownLines(text, maxWidth = getFullWidth() - 8) {
829
854
  return result;
830
855
  }
831
856
 
832
- export function getWelcomeHeroLines() {
857
+ export function getWelcomeHeroLines() {
833
858
  const p = C.sakura;
834
859
  const r = C.roseGlow;
835
860
  const g = C.gold;
@@ -841,7 +866,8 @@ export function getWelcomeHeroLines() {
841
866
  const b = C.bold;
842
867
  const d = C.dim;
843
868
  const rst = C.reset;
844
- const boxWidth = Math.min(84, Math.max(50, getFullWidth() - 4));
869
+ const boxWidth = Math.min(84, Math.max(50, getFullWidth() - 4));
870
+ const compact = getFullWidth() < 120;
845
871
  const headerTitle = 'Suggested Topics & Queries';
846
872
  const topDashCount = Math.max(2, boxWidth - getStringDisplayWidth(headerTitle) - 6);
847
873
  const innerWidth = boxWidth - 2;
@@ -854,14 +880,28 @@ export function getWelcomeHeroLines() {
854
880
  return ` ${s}│${rst} ${icon} ${tag}${rst}${' '.repeat(Math.max(0, tagWidth - getStringDisplayWidth(tag)))} ${w}${text}${rst}${' '.repeat(pad)}${s}│${rst}`;
855
881
  };
856
882
 
857
- return [
858
- '',
859
- ` ${p}██╗ ██╗██╗██╗ ██╗██╗ ██╗ █████╗ ██╗${rst} ${b}${p}◆ Ju Jingyi • 鞠婧祎 • Mìjú Terminal Portal ◆${rst}`,
860
- ` ${p}██║ ██╔╝██║██║ ██╔╝██║ ██║ ██╔══██╗██║${rst} ${d}${s}Interactive Fan Knowledge Base & Autonomous Agent${rst}`,
861
- ` ${r}█████═╝ ██║█████═╝ ██║ ██║ ███████║██║${rst} ${d}${s}Filmography • Discography • Fashion & Awards${rst}`,
862
- ` ${r}██╔═██╗ ██║██╔═██╗ ██║ ██║ ██╔══██╗██║${rst} ${c}● Live Ready${rst} ${s}• Multilingual (CN / EN / TH)${rst}`,
863
- ` ${p}██║ ╚██╗██║██║ ╚██╗╚██████╔╝ ██║ ██║██║${rst}`,
864
- ` ${p}╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝${rst}`,
883
+ const heroLines = compact ? [
884
+ ` ${b}${p}◆ Ju Jingyi • 鞠婧祎 • Mìjú Terminal Portal ◆${rst}`,
885
+ ` ${p}██╗ ██╗██╗██╗ ██╗██╗ ██╗ █████╗ ██╗${rst}`,
886
+ ` ${r}█████═╝ ██║█████═╝ ██║ ██║ ███████║██║${rst}`,
887
+ ] : [
888
+ ` ${p}██╗ ██╗██╗██╗ ██╗██╗ ██╗ █████╗ ██╗${rst} ${b}${p}◆ Ju Jingyi • 鞠婧祎 • Mìjú Terminal Portal ◆${rst}`,
889
+ ` ${p}██║ ██╔╝██║██║ ██╔╝██║ ██║ ██╔══██╗██║${rst} ${d}${s}Interactive Fan Knowledge Base & Autonomous Agent${rst}`,
890
+ ` ${r}█████═╝ ██║█████═╝ ██║ ██║ ███████║██║${rst} ${d}${s}Filmography • Discography • Fashion & Awards${rst}`,
891
+ ` ${r}██╔═██╗ ██║██╔═██╗ ██║ ██║ ██╔══██╗██║${rst} ${c}● Live Ready${rst} ${s}• Multilingual (CN / EN / TH)${rst}`,
892
+ ` ${p}██║ ╚██╗██║██║ ╚██╗╚██████╔╝ ██║ ██║██║${rst}`,
893
+ ` ${p}╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝${rst}`,
894
+ ];
895
+ const shortcutLines = compact ? [
896
+ ` ${s}Shortcuts: ${b}${w}[Tab]${rst} ${s}Commands • ${b}${w}[↑/↓]${rst} ${s}History • ${b}${w}[F2]${rst} ${s}Details${rst}`,
897
+ ` ${b}${w}[PgUp/PgDn]${rst} ${s}Scroll • ${b}${w}[Ctrl+C]${rst} ${s}Exit${rst}`,
898
+ ] : [
899
+ ` ${s}Shortcuts: ${b}${w}[Tab]${rst} ${s}Autocomplete • ${b}${w}[↑/↓]${rst} ${s}History • ${b}${w}[PgUp/PgDn]${rst} ${s}Scroll • ${b}${w}[F2]${rst} ${s}Details • ${b}${w}[Ctrl+C]${rst} ${s}Exit${rst}`,
900
+ ];
901
+
902
+ return [
903
+ ...(compact ? [] : ['']),
904
+ ...heroLines,
865
905
  '',
866
906
  ` ${s}╭──${rst} ${b}${g}${headerTitle}${rst} ${s}${'─'.repeat(topDashCount)}╮${rst}`,
867
907
  padItem(p + '◆', '2026 Works', '"What are Ju Jingyi\'s 2026 upcoming dramas?"'),
@@ -870,7 +910,7 @@ export function getWelcomeHeroLines() {
870
910
  padItem(l + '◆', 'Tools / MCP', '"@search_works 2026, /mcp list, /stats, /help"'),
871
911
  ` ${s}╰${'─'.repeat(boxWidth)}╯${rst}`,
872
912
  '',
873
- ` ${s}Shortcuts: ${b}${w}[Tab]${rst} ${s}Autocomplete • ${b}${w}[↑/↓]${rst} ${s}History • ${b}${w}[PgUp/PgDn]${rst} ${s}Scroll • ${b}${w}[F2]${rst} ${s}Details • ${b}${w}[Ctrl+C]${rst} ${s}Exit${rst}`,
913
+ ...shortcutLines,
874
914
  '',
875
915
  ];
876
916
  }
@@ -1040,7 +1080,7 @@ export function exitAlternateScreen() {
1040
1080
  }
1041
1081
  }
1042
1082
 
1043
- export function getTuiViewportLayout(termRows = 24) {
1083
+ export function getTuiViewportLayout(termRows = 24) {
1044
1084
  const parsedRows = Number(termRows);
1045
1085
  const rows = Math.max(10, Number.isFinite(parsedRows) ? Math.floor(parsedRows) : 24);
1046
1086
  const viewportStartRow = 1;
@@ -1055,10 +1095,16 @@ export function getTuiViewportLayout(termRows = 24) {
1055
1095
  statusBarRow,
1056
1096
  promptRow,
1057
1097
  separatorRow,
1058
- };
1059
- }
1060
-
1061
- export class VirtualScreenBuffer {
1098
+ };
1099
+ }
1100
+
1101
+ export function getNewOutputScrollOffset(addedLineCount, termRows = 24, currentOffset = 0) {
1102
+ const added = Math.max(0, Number.isFinite(Number(addedLineCount)) ? Math.floor(Number(addedLineCount)) : 0);
1103
+ if (currentOffset > 0) return currentOffset + added;
1104
+ return Math.max(0, added - getTuiViewportLayout(termRows).viewportHeight);
1105
+ }
1106
+
1107
+ export class VirtualScreenBuffer {
1062
1108
  constructor(width = 80, height = 24, writer = null) {
1063
1109
  this.width = Math.max(10, width);
1064
1110
  this.height = Math.max(5, height);
@@ -8,6 +8,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
8
  const ENV_PATH = path.resolve(__dirname, '..', '..', '.env');
9
9
  const AUTH_CONFIG_PATH = path.join(KIKU_DATA_DIR, 'yt_auth.json');
10
10
  const SECURE_COOKIE_PATH = path.join(KIKU_DATA_DIR, 'cookies.txt');
11
+ loadEnv();
11
12
  const YTDLP_CMD = process.env.KIKU_YTDLP || 'yt-dlp';
12
13
 
13
14
 
@@ -374,5 +375,3 @@ export function getCookieStatus() {
374
375
  authConfigPath: AUTH_CONFIG_PATH
375
376
  };
376
377
  }
377
-
378
- loadEnv();