kiku-agent 1.0.9 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +7 -6
- package/server/agent/agentCore.js +116 -66
- package/server/agent/appPaths.js +2 -0
- package/server/agent/cli.js +457 -248
- package/server/agent/commandRegistry.js +370 -41
- package/server/agent/commands.js +217 -45
- package/server/agent/config.js +367 -216
- package/server/agent/doctor.js +1 -1
- package/server/agent/errors.js +205 -0
- package/server/agent/llmClient.js +369 -351
- package/server/agent/queryDetails.js +234 -182
- package/server/agent/session.js +410 -244
- package/server/agent/terminalImage.js +108 -2
- package/server/agent/ui.js +54 -44
- package/src/assets/images/0040jbadgy1igaiagw3ezj61s02ebu0x02.jpg +0 -0
- package/src/assets/images/0040jbadgy1igaiaivng3j61rv2ebx6p02.jpg +0 -0
- package/src/assets/images/0040jbadgy1igaiajuixvj62bw340qv502.jpg +0 -0
- package/src/assets/images/0040jbadgy1igdxrcipx4j61k422wtpp02.jpg +0 -0
- package/src/assets/images/0040jbadgy1igdxrg3cq5j62bw340u0x02.jpg +0 -0
- package/src/assets/images/0040jbadgy1igdy9hpg8aj62c0340hdt02.jpg +0 -0
|
@@ -196,7 +196,7 @@ export function renderJpegFile(filePath, options = {}) {
|
|
|
196
196
|
return renderJpegToAnsi(readFileSync(filePath), options);
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
function discoverImages(directory) {
|
|
199
|
+
export function discoverImages(directory) {
|
|
200
200
|
try {
|
|
201
201
|
return readdirSync(directory, { withFileTypes: true })
|
|
202
202
|
.filter(entry => entry.isFile() && /\.jpe?g$/i.test(entry.name))
|
|
@@ -207,6 +207,89 @@ function discoverImages(directory) {
|
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
+
export function renderTwoImagesSideBySide(filePath1, filePath2, {
|
|
211
|
+
columns = process.stdout.columns || 80,
|
|
212
|
+
rows = process.stdout.rows || 24,
|
|
213
|
+
maxRows = 14,
|
|
214
|
+
} = {}) {
|
|
215
|
+
const availableWidth = Math.max(20, Math.floor(columns));
|
|
216
|
+
const sideBySide = availableWidth >= 76;
|
|
217
|
+
const singleMaxWidth = sideBySide ? Math.min(34, Math.floor((availableWidth - 8) / 2)) : Math.min(50, availableWidth - 4);
|
|
218
|
+
|
|
219
|
+
const img1 = renderJpegFile(filePath1, {
|
|
220
|
+
columns: singleMaxWidth + 4,
|
|
221
|
+
rows,
|
|
222
|
+
maxWidth: singleMaxWidth,
|
|
223
|
+
maxRows,
|
|
224
|
+
indent: '',
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const img2 = renderJpegFile(filePath2, {
|
|
228
|
+
columns: singleMaxWidth + 4,
|
|
229
|
+
rows,
|
|
230
|
+
maxWidth: singleMaxWidth,
|
|
231
|
+
maxRows,
|
|
232
|
+
indent: '',
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
if (!sideBySide) {
|
|
236
|
+
return [
|
|
237
|
+
...img1.map(line => ` ${line}`),
|
|
238
|
+
'',
|
|
239
|
+
...img2.map(line => ` ${line}`),
|
|
240
|
+
];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const maxLines = Math.max(img1.length, img2.length);
|
|
244
|
+
const combined = [];
|
|
245
|
+
const gap = ' ';
|
|
246
|
+
|
|
247
|
+
for (let i = 0; i < maxLines; i++) {
|
|
248
|
+
const line1 = img1[i] || '';
|
|
249
|
+
const line2 = img2[i] || '';
|
|
250
|
+
combined.push(` ${line1}${gap}${line2}`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return combined;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function getWelcomeGalleryLines(count = 2, {
|
|
257
|
+
directory = DEFAULT_IMAGE_DIRECTORY,
|
|
258
|
+
env = process.env,
|
|
259
|
+
isTTY = process.stdout.isTTY,
|
|
260
|
+
columns = process.stdout.columns || 80,
|
|
261
|
+
rows = process.stdout.rows || 24,
|
|
262
|
+
} = {}) {
|
|
263
|
+
const images = discoverImages(directory);
|
|
264
|
+
if (images.length === 0 || !supportsTerminalImages({ env, isTTY })) return [];
|
|
265
|
+
|
|
266
|
+
if (count >= 2 && images.length >= 2) {
|
|
267
|
+
try {
|
|
268
|
+
const pairLines = renderTwoImagesSideBySide(images[0], images[1], { columns, rows, maxRows: 14 });
|
|
269
|
+
return [
|
|
270
|
+
'',
|
|
271
|
+
` \x1b[1m\x1b[38;2;255;126;179m◆ JU JINGYI • 鞠婧祎 GALLERY\x1b[0m \x1b[38;2;148;163;184m• Featured Visuals (1 & 2 / ${images.length})\x1b[0m`,
|
|
272
|
+
...pairLines,
|
|
273
|
+
'',
|
|
274
|
+
];
|
|
275
|
+
} catch {
|
|
276
|
+
return [];
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
const singleLines = renderJpegFile(images[0], { columns, rows, maxWidth: 44, maxRows: 14 });
|
|
282
|
+
return [
|
|
283
|
+
'',
|
|
284
|
+
` \x1b[1m\x1b[38;2;255;126;179m◆ JU JINGYI • 鞠婧祎 GALLERY\x1b[0m \x1b[38;2;148;163;184m• Featured Visual (1/${images.length})\x1b[0m`,
|
|
285
|
+
...singleLines,
|
|
286
|
+
'',
|
|
287
|
+
];
|
|
288
|
+
} catch {
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
210
293
|
export function createTerminalImageGallery({
|
|
211
294
|
directory = DEFAULT_IMAGE_DIRECTORY,
|
|
212
295
|
frequency = 3,
|
|
@@ -222,10 +305,32 @@ export function createTerminalImageGallery({
|
|
|
222
305
|
return {
|
|
223
306
|
enabled,
|
|
224
307
|
imageCount: images.length,
|
|
225
|
-
maybeRender(completedQueries = 0, { force = false } = {}) {
|
|
308
|
+
maybeRender(completedQueries = 0, { force = false, count = 1 } = {}) {
|
|
226
309
|
const cadence = Math.max(1, Math.floor(frequency));
|
|
227
310
|
if (!enabled || (!force && (completedQueries <= 0 || completedQueries % cadence !== 0))) return [];
|
|
228
311
|
|
|
312
|
+
if (count >= 2 && images.length >= 2) {
|
|
313
|
+
const idx1 = imageIndex % images.length;
|
|
314
|
+
const idx2 = (imageIndex + 1) % images.length;
|
|
315
|
+
const path1 = images[idx1];
|
|
316
|
+
const path2 = images[idx2];
|
|
317
|
+
try {
|
|
318
|
+
const pairLines = renderTwoImagesSideBySide(path1, path2, {
|
|
319
|
+
columns: getColumns(),
|
|
320
|
+
rows: getRows(),
|
|
321
|
+
});
|
|
322
|
+
imageIndex = (imageIndex + 2) % images.length;
|
|
323
|
+
return [
|
|
324
|
+
'',
|
|
325
|
+
` \x1b[1m\x1b[38;2;255;126;179m◆ KIKU GALLERY\x1b[0m \x1b[38;2;148;163;184m• Photos ${idx1 + 1} & ${idx2 + 1}/${images.length}\x1b[0m`,
|
|
326
|
+
...pairLines,
|
|
327
|
+
'',
|
|
328
|
+
];
|
|
329
|
+
} catch {
|
|
330
|
+
return [];
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
229
334
|
const currentIndex = imageIndex % images.length;
|
|
230
335
|
const filePath = images[currentIndex];
|
|
231
336
|
try {
|
|
@@ -246,3 +351,4 @@ export function createTerminalImageGallery({
|
|
|
246
351
|
},
|
|
247
352
|
};
|
|
248
353
|
}
|
|
354
|
+
|
package/server/agent/ui.js
CHANGED
|
@@ -130,9 +130,13 @@ function getGraphemeDisplayWidth(segment) {
|
|
|
130
130
|
if (
|
|
131
131
|
(code >= 0x1100 && code <= 0x11FF) ||
|
|
132
132
|
(code >= 0x2E80 && code <= 0x9FFF) ||
|
|
133
|
+
(code >= 0x3000 && code <= 0x303F) ||
|
|
133
134
|
(code >= 0xAC00 && code <= 0xD7A3) ||
|
|
134
135
|
(code >= 0xF900 && code <= 0xFAFF) ||
|
|
135
|
-
(code >= 0xFE30 && code <= 0xFE4F)
|
|
136
|
+
(code >= 0xFE30 && code <= 0xFE4F) ||
|
|
137
|
+
(code >= 0xFF01 && code <= 0xFF60) ||
|
|
138
|
+
(code >= 0xFFE0 && code <= 0xFFE6) ||
|
|
139
|
+
(code >= 0x20000 && code <= 0x3FFFF)
|
|
136
140
|
) {
|
|
137
141
|
width += 2;
|
|
138
142
|
} else {
|
|
@@ -297,7 +301,7 @@ export function wrapText(text, maxWidth = 72) {
|
|
|
297
301
|
|
|
298
302
|
|
|
299
303
|
export function stripAnsi(str) {
|
|
300
|
-
return str.replace(/\x1b
|
|
304
|
+
return String(str ?? '').replace(/\x1b(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1b\\))/g, '');
|
|
301
305
|
}
|
|
302
306
|
|
|
303
307
|
export function sanitizeTerminalText(value, maxLength = 2000) {
|
|
@@ -751,8 +755,8 @@ export function formatTerminalMarkdown(text) {
|
|
|
751
755
|
if (!text) return '';
|
|
752
756
|
return text
|
|
753
757
|
.replace(/^###\s*(.+)$/gm, `${C.bold}${C.gold}✦ $1${C.reset}`)
|
|
754
|
-
.replace(/^##\s*(.+)$/gm,
|
|
755
|
-
.replace(/^#\s*(.+)$/gm,
|
|
758
|
+
.replace(/^##\s*(.+)$/gm, `${C.bold}${C.roseGlow}■ $1${C.reset}`)
|
|
759
|
+
.replace(/^#\s*(.+)$/gm, `${C.bold}${C.sakura}━━━ $1 ━━━${C.reset}`)
|
|
756
760
|
.replace(/^>\s*(.+)$/gm, ` ${C.slate}│${C.reset} ${C.italic}${C.white}$1${C.reset}`)
|
|
757
761
|
.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, `${C.bold}${C.cyberCyan}$1${C.reset} ➔ ${C.underline}${C.roseGlow}$2${C.reset}`)
|
|
758
762
|
.replace(/\*{3}([^*]+)\*{3}/g, `${C.bold}${C.italic}${C.white}$1${C.reset}`)
|
|
@@ -854,7 +858,7 @@ export function formatMarkdownLines(text, maxWidth = getFullWidth() - 8) {
|
|
|
854
858
|
return result;
|
|
855
859
|
}
|
|
856
860
|
|
|
857
|
-
export function getWelcomeHeroLines() {
|
|
861
|
+
export function getWelcomeHeroLines() {
|
|
858
862
|
const p = C.sakura;
|
|
859
863
|
const r = C.roseGlow;
|
|
860
864
|
const g = C.gold;
|
|
@@ -866,8 +870,8 @@ export function getWelcomeHeroLines() {
|
|
|
866
870
|
const b = C.bold;
|
|
867
871
|
const d = C.dim;
|
|
868
872
|
const rst = C.reset;
|
|
869
|
-
const boxWidth = Math.min(84, Math.max(50, getFullWidth() - 4));
|
|
870
|
-
const compact = getFullWidth() < 120;
|
|
873
|
+
const boxWidth = Math.min(84, Math.max(50, getFullWidth() - 4));
|
|
874
|
+
const compact = getFullWidth() < 120;
|
|
871
875
|
const headerTitle = 'Suggested Topics & Queries';
|
|
872
876
|
const topDashCount = Math.max(2, boxWidth - getStringDisplayWidth(headerTitle) - 6);
|
|
873
877
|
const innerWidth = boxWidth - 2;
|
|
@@ -880,28 +884,30 @@ export function getWelcomeHeroLines() {
|
|
|
880
884
|
return ` ${s}│${rst} ${icon} ${tag}${rst}${' '.repeat(Math.max(0, tagWidth - getStringDisplayWidth(tag)))} ${w}${text}${rst}${' '.repeat(pad)}${s}│${rst}`;
|
|
881
885
|
};
|
|
882
886
|
|
|
883
|
-
const heroLines = compact ? [
|
|
884
|
-
` ${
|
|
885
|
-
` ${
|
|
886
|
-
` ${
|
|
887
|
-
] : [
|
|
888
|
-
` ${p}██╗ ██╗██╗██╗ ██╗██╗ ██╗ █████╗ ██╗${rst}
|
|
889
|
-
` ${p}██║ ██╔╝██║██║ ██╔╝██║ ██║ ██╔══██╗██║${rst}
|
|
890
|
-
` ${r}█████═╝ ██║█████═╝ ██║ ██║ ███████║██║${rst}
|
|
891
|
-
` ${r}██╔═██╗ ██║██╔═██╗ ██║ ██║ ██╔══██╗██║${rst}
|
|
892
|
-
` ${p}██║ ╚██╗██║██║ ╚██╗╚██████╔╝ ██║ ██║██║${rst}`,
|
|
893
|
-
` ${p}╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝${rst}`,
|
|
894
|
-
];
|
|
895
|
-
const shortcutLines = compact ? [
|
|
896
|
-
` ${s}
|
|
897
|
-
` ${b}${w}[
|
|
898
|
-
] : [
|
|
899
|
-
` ${s}
|
|
900
|
-
]
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
887
|
+
const heroLines = compact ? [
|
|
888
|
+
` ${p}██╗ ██╗██╗██╗ ██╗██╗ ██╗ █████╗ ██╗${rst}`,
|
|
889
|
+
` ${r}█████═╝ ██║█████═╝ ██║ ██║ ███████║██║${rst}`,
|
|
890
|
+
` ${p}██║ ██║██║██║ ██║╚██████╔╝ ██║ ██║██║${rst}`,
|
|
891
|
+
] : [
|
|
892
|
+
` ${p}██╗ ██╗██╗██╗ ██╗██╗ ██╗ █████╗ ██╗${rst}`,
|
|
893
|
+
` ${p}██║ ██╔╝██║██║ ██╔╝██║ ██║ ██╔══██╗██║${rst}`,
|
|
894
|
+
` ${r}█████═╝ ██║█████═╝ ██║ ██║ ███████║██║${rst}`,
|
|
895
|
+
` ${r}██╔═██╗ ██║██╔═██╗ ██║ ██║ ██╔══██╗██║${rst}`,
|
|
896
|
+
` ${p}██║ ╚██╗██║██║ ╚██╗╚██████╔╝ ██║ ██║██║${rst}`,
|
|
897
|
+
` ${p}╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝${rst}`,
|
|
898
|
+
];
|
|
899
|
+
const shortcutLines = compact ? [
|
|
900
|
+
` ${s}Keys: ${b}${w}[Tab]${rst} ${s}Cmds • ${b}${w}[↑/↓]${rst} ${s}Hist • ${b}${w}[F1]${rst} ${s}Help • ${b}${w}[F2]${rst} ${s}Details${rst}`,
|
|
901
|
+
` ${b}${w}[F3]${rst} ${s}Think • ${b}${w}[F4]${rst} ${s}Tools • ${b}${w}[F5]${rst} ${s}Copy • ${b}${w}[Ctrl+C]${rst} ${s}Exit${rst}`,
|
|
902
|
+
] : [
|
|
903
|
+
` ${s}Keys: ${b}${w}[Tab]${rst} ${s}Autocomplete • ${b}${w}[↑/↓]${rst} ${s}History • ${b}${w}[PgUp/PgDn]${rst} ${s}Scroll • ${b}${w}[Ctrl+C]${rst} ${s}Exit${rst}`,
|
|
904
|
+
` ${s}F-Keys: ${b}${w}[F1]${rst} ${s}Help • ${b}${w}[F2]${rst} ${s}Details • ${b}${w}[F3]${rst} ${s}Thinking • ${b}${w}[F4]${rst} ${s}Tools • ${b}${w}[F5]${rst} ${s}Copy • ${b}${w}[F6]${rst} ${s}Gallery • ${b}${w}[F7]${rst} ${s}Theme${rst}`,
|
|
905
|
+
` ${s}Ctrl: ${b}${w}[Ctrl+T]${rst} ${s}Toggle Think • ${b}${w}[Ctrl+R]${rst} ${s}Retry • ${b}${w}[Ctrl+S]${rst} ${s}Save • ${b}${w}[Ctrl+G]${rst} ${s}Gallery${rst}`,
|
|
906
|
+
];
|
|
907
|
+
|
|
908
|
+
return [
|
|
909
|
+
...(compact ? [] : ['']),
|
|
910
|
+
...heroLines,
|
|
905
911
|
'',
|
|
906
912
|
` ${s}╭──${rst} ${b}${g}${headerTitle}${rst} ${s}${'─'.repeat(topDashCount)}╮${rst}`,
|
|
907
913
|
padItem(p + '◆', '2026 Works', '"What are Ju Jingyi\'s 2026 upcoming dramas?"'),
|
|
@@ -910,7 +916,7 @@ export function getWelcomeHeroLines() {
|
|
|
910
916
|
padItem(l + '◆', 'Tools / MCP', '"@search_works 2026, /mcp list, /stats, /help"'),
|
|
911
917
|
` ${s}╰${'─'.repeat(boxWidth)}╯${rst}`,
|
|
912
918
|
'',
|
|
913
|
-
...shortcutLines,
|
|
919
|
+
...shortcutLines,
|
|
914
920
|
'',
|
|
915
921
|
];
|
|
916
922
|
}
|
|
@@ -1068,19 +1074,23 @@ export function formatTaskPlanBanner(plan) {
|
|
|
1068
1074
|
}).join('\n');
|
|
1069
1075
|
}
|
|
1070
1076
|
|
|
1077
|
+
let isAlternateScreenActive = false;
|
|
1078
|
+
|
|
1071
1079
|
export function enterAlternateScreen() {
|
|
1072
|
-
if (process.stdout.isTTY) {
|
|
1080
|
+
if (process.stdout.isTTY && !isAlternateScreenActive) {
|
|
1081
|
+
isAlternateScreenActive = true;
|
|
1073
1082
|
_rawStdoutWrite('\x1b[?1049h\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1005l\x1b[?1006l\x1b[?1015l\x1b[?25l\x1b[H\x1b[2J');
|
|
1074
1083
|
}
|
|
1075
1084
|
}
|
|
1076
1085
|
|
|
1077
1086
|
export function exitAlternateScreen() {
|
|
1078
|
-
if (process.stdout.isTTY) {
|
|
1079
|
-
|
|
1087
|
+
if (process.stdout.isTTY && isAlternateScreenActive) {
|
|
1088
|
+
isAlternateScreenActive = false;
|
|
1089
|
+
_rawStdoutWrite('\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1005l\x1b[?1006l\x1b[?1015l\x1b[?1049l\x1b[?25h\x1b[0m');
|
|
1080
1090
|
}
|
|
1081
1091
|
}
|
|
1082
1092
|
|
|
1083
|
-
export function getTuiViewportLayout(termRows = 24) {
|
|
1093
|
+
export function getTuiViewportLayout(termRows = 24) {
|
|
1084
1094
|
const parsedRows = Number(termRows);
|
|
1085
1095
|
const rows = Math.max(10, Number.isFinite(parsedRows) ? Math.floor(parsedRows) : 24);
|
|
1086
1096
|
const viewportStartRow = 1;
|
|
@@ -1095,16 +1105,16 @@ export function getTuiViewportLayout(termRows = 24) {
|
|
|
1095
1105
|
statusBarRow,
|
|
1096
1106
|
promptRow,
|
|
1097
1107
|
separatorRow,
|
|
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 {
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
export function getNewOutputScrollOffset(addedLineCount, termRows = 24, currentOffset = 0) {
|
|
1112
|
+
const added = Math.max(0, Number.isFinite(Number(addedLineCount)) ? Math.floor(Number(addedLineCount)) : 0);
|
|
1113
|
+
if (currentOffset > 0) return currentOffset + added;
|
|
1114
|
+
return Math.max(0, added - getTuiViewportLayout(termRows).viewportHeight);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
export class VirtualScreenBuffer {
|
|
1108
1118
|
constructor(width = 80, height = 24, writer = null) {
|
|
1109
1119
|
this.width = Math.max(10, width);
|
|
1110
1120
|
this.height = Math.max(5, height);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|