ikie-cli 0.1.12 → 0.1.14
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/dist/agent.js +4 -3
- package/dist/repl.js +2 -2
- package/dist/theme.d.ts +12 -3
- package/dist/theme.js +146 -37
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -530,12 +530,13 @@ export class Agent {
|
|
|
530
530
|
if (name === 'edit_file' && !result.startsWith('Error')) {
|
|
531
531
|
const oldStr = String(input.old_string ?? '');
|
|
532
532
|
const newStr = String(input.new_string ?? '');
|
|
533
|
-
|
|
533
|
+
const path = typeof input.path === 'string' ? input.path : undefined;
|
|
534
|
+
block = toolDiffBlock(oldStr, newStr, ms, { path, indent: this.indent });
|
|
534
535
|
}
|
|
535
536
|
else {
|
|
536
|
-
block = toolOutputBlock(result, ms);
|
|
537
|
+
block = toolOutputBlock(result, ms, this.indent);
|
|
537
538
|
}
|
|
538
|
-
process.stdout.write(`${
|
|
539
|
+
process.stdout.write(`${block}\n`);
|
|
539
540
|
return result;
|
|
540
541
|
}
|
|
541
542
|
catch (err) {
|
package/dist/repl.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as readline from 'node:readline';
|
|
2
2
|
import { execSync, exec } from 'child_process';
|
|
3
3
|
import { restoreStdinListeners } from './agent.js';
|
|
4
|
-
import { c, PROMPT, CONTINUE_PROMPT, printPromptHeader, modeTag, drawBanner, infoLine, successLine, errorLine, THEMES, setTheme, stripAnsi, contextRing, } from './theme.js';
|
|
4
|
+
import { c, PROMPT, CONTINUE_PROMPT, PROMPT_ARROW, printPromptHeader, modeTag, drawBanner, infoLine, successLine, errorLine, THEMES, setTheme, stripAnsi, contextRing, } from './theme.js';
|
|
5
5
|
import { renderMarkdown } from './renderer.js';
|
|
6
6
|
import { loadAllMemory } from './memory.js';
|
|
7
7
|
import { HOME_DIR, saveConfig, DEFAULT_MODEL, IKIE_HOST, IKIE_API_BASE, isLoggedIn, getApiKey } from './config.js';
|
|
@@ -985,7 +985,7 @@ export async function startREPL(agent, config, projectContext, oneShot) {
|
|
|
985
985
|
const pct = tokens / contextWindow;
|
|
986
986
|
const ring = tokens > 0 ? contextRing(Math.min(pct, 1)) : '';
|
|
987
987
|
const prompt = ring
|
|
988
|
-
? `${c.primary('╰─')} ${ring} ${c.primary('
|
|
988
|
+
? `${c.primary('╰─')} ${ring} ${c.primary(PROMPT_ARROW + ' ')}`
|
|
989
989
|
: PROMPT;
|
|
990
990
|
if (imageState.pending.length) {
|
|
991
991
|
const labels = imageState.pending.map(image => `[Image #${image.id}]`).join(' ');
|
package/dist/theme.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export declare function contextRing(pct: number): string;
|
|
|
44
44
|
export declare function printPromptHeader(mode?: 'agent' | 'plan'): void;
|
|
45
45
|
export declare const PROMPT: string;
|
|
46
46
|
export declare const CONTINUE_PROMPT: string;
|
|
47
|
+
export declare const PROMPT_ARROW: string;
|
|
47
48
|
export declare class InlineSpinner {
|
|
48
49
|
private timer;
|
|
49
50
|
private delayTimer;
|
|
@@ -61,9 +62,16 @@ export declare class InlineSpinner {
|
|
|
61
62
|
}
|
|
62
63
|
export declare function toolLine(name: string, args: string): string;
|
|
63
64
|
/** Multi-line output block shown after a tool runs. */
|
|
64
|
-
export declare function toolOutputBlock(result: string, ms: number): string;
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
export declare function toolOutputBlock(result: string, ms: number, indent?: string): string;
|
|
66
|
+
interface DiffOpts {
|
|
67
|
+
path?: string;
|
|
68
|
+
indent?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Render an edit as a polished unified diff: line-number gutter, a few lines of
|
|
72
|
+
* surrounding context pulled from the file, and full-width muted red/green rows.
|
|
73
|
+
*/
|
|
74
|
+
export declare function toolDiffBlock(oldStr: string, newStr: string, ms: number, opts?: DiffOpts): string;
|
|
67
75
|
export declare function toolSuccessLine(ms: number, preview?: string): string;
|
|
68
76
|
export declare function toolErrorLine(msg: string): string;
|
|
69
77
|
export declare function successLine(msg: string): string;
|
|
@@ -71,3 +79,4 @@ export declare function errorLine(msg: string): string;
|
|
|
71
79
|
export declare function warnLine(msg: string): string;
|
|
72
80
|
export declare function infoLine(msg: string): string;
|
|
73
81
|
export declare function permissionPrompt(toolName: string, preview: string): string;
|
|
82
|
+
export {};
|
package/dist/theme.js
CHANGED
|
@@ -268,15 +268,20 @@ export function contextRing(pct) {
|
|
|
268
268
|
const color = pct < 0.7 ? c.success : pct < 0.85 ? c.warning : c.error;
|
|
269
269
|
return `${color(char)} ${color(`${Math.round(pct * 100)}%`)}`;
|
|
270
270
|
}
|
|
271
|
+
const IS_WIN = process.platform === 'win32';
|
|
272
|
+
const CH = IS_WIN
|
|
273
|
+
? { tl: '+-', prompt: '\\->', cont: '| ', arrow: '>', dot: '●' }
|
|
274
|
+
: { tl: '╭─', prompt: '╰─❯', cont: '│ ', arrow: '❯', dot: '●' };
|
|
271
275
|
export function printPromptHeader(mode = 'agent') {
|
|
272
276
|
const cwdName = basename(process.cwd()) || '/';
|
|
273
277
|
const branch = getGitBranchFast();
|
|
274
278
|
const gitSegment = branch ? ` ${c.muted('on')} ${c.secondary(branch)}` : '';
|
|
275
279
|
const themeSegment = ` ${c.muted('theme')} ${c.secondary(activeTheme.name)}`;
|
|
276
|
-
process.stdout.write(`\n${c.primary(
|
|
280
|
+
process.stdout.write(`\n${c.primary(CH.tl)} ${c.primary.bold('ikie')} ${c.muted('·')} ${modeTag(mode)}${gitSegment}${themeSegment} ${c.muted('in')} ${c.accent(cwdName)}\n`);
|
|
277
281
|
}
|
|
278
|
-
export const PROMPT = c.primary(
|
|
279
|
-
export const CONTINUE_PROMPT = c.primary(
|
|
282
|
+
export const PROMPT = c.primary(`${CH.prompt} `);
|
|
283
|
+
export const CONTINUE_PROMPT = c.primary(CH.cont);
|
|
284
|
+
export const PROMPT_ARROW = CH.arrow;
|
|
280
285
|
export class InlineSpinner {
|
|
281
286
|
timer = null;
|
|
282
287
|
delayTimer = null;
|
|
@@ -370,55 +375,159 @@ export function toolLine(name, args) {
|
|
|
370
375
|
return `${tint('●')} ${c.white.bold(verb + badge)}${c.dim('(')}${c.muted(args)}${c.dim(')')}`;
|
|
371
376
|
}
|
|
372
377
|
/** Multi-line output block shown after a tool runs. */
|
|
373
|
-
export function toolOutputBlock(result, ms) {
|
|
378
|
+
export function toolOutputBlock(result, ms, indent = ' ') {
|
|
374
379
|
const time = c.muted(formatDuration(ms));
|
|
375
380
|
const lines = result.split('\n').filter(l => l.trim() !== '');
|
|
376
381
|
if (!lines.length)
|
|
377
|
-
return
|
|
382
|
+
return `${indent}${c.muted('⎿')} ${time}`;
|
|
378
383
|
const MAX = 5;
|
|
379
384
|
const shown = lines.slice(0, MAX);
|
|
380
385
|
const hidden = lines.length - MAX;
|
|
386
|
+
const cont = indent + ' ';
|
|
381
387
|
const out = [];
|
|
382
|
-
out.push(
|
|
388
|
+
out.push(`${indent}${c.muted('⎿')} ${time} ${c.dim(clampLine(shown[0]))}`);
|
|
383
389
|
for (let i = 1; i < shown.length; i++) {
|
|
384
|
-
out.push(
|
|
390
|
+
out.push(`${cont}${c.dim(clampLine(shown[i]))}`);
|
|
385
391
|
}
|
|
386
392
|
if (hidden > 0) {
|
|
387
|
-
out.push(
|
|
393
|
+
out.push(`${cont}${c.muted(`… +${hidden} lines`)}`);
|
|
388
394
|
}
|
|
389
395
|
return out.join('\n');
|
|
390
396
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
397
|
+
// ── Diff rendering ────────────────────────────────────────────────────────────
|
|
398
|
+
// Diff palette (256-color, muted — chalk degrades gracefully on low-color TTYs).
|
|
399
|
+
const diffColors = {
|
|
400
|
+
delBg: chalk.bgAnsi256(52), // deep red
|
|
401
|
+
insBg: chalk.bgAnsi256(22), // deep green
|
|
402
|
+
delGutter: chalk.ansi256(210),
|
|
403
|
+
insGutter: chalk.ansi256(151),
|
|
404
|
+
onBg: chalk.ansi256(255),
|
|
405
|
+
ctxGutter: chalk.ansi256(240),
|
|
406
|
+
ctxCode: chalk.ansi256(248),
|
|
407
|
+
};
|
|
408
|
+
/** Classic LCS line diff — interleaves equal/removed/added like a real unified diff. */
|
|
409
|
+
function lineDiff(a, b) {
|
|
410
|
+
const n = a.length, m = b.length;
|
|
411
|
+
const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
|
412
|
+
for (let i = n - 1; i >= 0; i--) {
|
|
413
|
+
for (let j = m - 1; j >= 0; j--) {
|
|
414
|
+
dp[i][j] = a[i] === b[j] ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
404
417
|
const out = [];
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
418
|
+
let i = 0, j = 0;
|
|
419
|
+
while (i < n && j < m) {
|
|
420
|
+
if (a[i] === b[j]) {
|
|
421
|
+
out.push({ t: 'eq', s: a[i] });
|
|
422
|
+
i++;
|
|
423
|
+
j++;
|
|
424
|
+
}
|
|
425
|
+
else if (dp[i + 1][j] >= dp[i][j + 1]) {
|
|
426
|
+
out.push({ t: 'del', s: a[i] });
|
|
427
|
+
i++;
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
out.push({ t: 'ins', s: b[j] });
|
|
431
|
+
j++;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
while (i < n)
|
|
435
|
+
out.push({ t: 'del', s: a[i++] });
|
|
436
|
+
while (j < m)
|
|
437
|
+
out.push({ t: 'ins', s: b[j++] });
|
|
438
|
+
return out;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Render an edit as a polished unified diff: line-number gutter, a few lines of
|
|
442
|
+
* surrounding context pulled from the file, and full-width muted red/green rows.
|
|
443
|
+
*/
|
|
444
|
+
export function toolDiffBlock(oldStr, newStr, ms, opts = {}) {
|
|
445
|
+
const indent = opts.indent ?? ' ';
|
|
446
|
+
const cols = Math.max(40, (process.stdout.columns ?? 100));
|
|
447
|
+
const time = c.muted(formatDuration(ms));
|
|
448
|
+
const oldLines = oldStr.split('\n');
|
|
449
|
+
const newLines = newStr.split('\n');
|
|
450
|
+
const ops = lineDiff(oldLines, newLines);
|
|
451
|
+
const nDel = ops.filter(o => o.t === 'del').length;
|
|
452
|
+
const nIns = ops.filter(o => o.t === 'ins').length;
|
|
453
|
+
// Anchor line numbers + context lines by locating the new text in the file.
|
|
454
|
+
let anchor = 1;
|
|
455
|
+
let fileLines = null;
|
|
456
|
+
const CTX = 3;
|
|
457
|
+
if (opts.path) {
|
|
458
|
+
try {
|
|
459
|
+
const content = readFileSync(opts.path, 'utf-8');
|
|
460
|
+
const idx = content.indexOf(newStr);
|
|
461
|
+
if (idx >= 0) {
|
|
462
|
+
anchor = (content.slice(0, idx).match(/\n/g)?.length ?? 0) + 1;
|
|
463
|
+
fileLines = content.split('\n');
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
catch { /* no context — fall back to anchorless diff */ }
|
|
413
467
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
468
|
+
const summaryParts = [];
|
|
469
|
+
if (nIns)
|
|
470
|
+
summaryParts.push(`Added ${nIns} line${nIns === 1 ? '' : 's'}`);
|
|
471
|
+
if (nDel)
|
|
472
|
+
summaryParts.push(`removed ${nDel} line${nDel === 1 ? '' : 's'}`);
|
|
473
|
+
const summary = summaryParts.join(', ') || 'no changes';
|
|
474
|
+
// Width of the line-number gutter.
|
|
475
|
+
const maxNum = anchor + Math.max(oldLines.length, newLines.length) + CTX;
|
|
476
|
+
const gw = String(maxNum).length;
|
|
477
|
+
const rowsRaw = [];
|
|
478
|
+
// Leading context (lines just before the edit).
|
|
479
|
+
if (fileLines) {
|
|
480
|
+
for (let k = Math.max(1, anchor - CTX); k < anchor; k++) {
|
|
481
|
+
rowsRaw.push({ kind: 'eq', num: k, text: fileLines[k - 1] ?? '' });
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
// The diff body — track separate old/new line counters off the same anchor.
|
|
485
|
+
let oldNum = anchor, newNum = anchor;
|
|
486
|
+
for (const op of ops) {
|
|
487
|
+
if (op.t === 'eq') {
|
|
488
|
+
rowsRaw.push({ kind: 'eq', num: newNum, text: op.s });
|
|
489
|
+
oldNum++;
|
|
490
|
+
newNum++;
|
|
491
|
+
}
|
|
492
|
+
else if (op.t === 'del') {
|
|
493
|
+
rowsRaw.push({ kind: 'del', num: oldNum, text: op.s });
|
|
494
|
+
oldNum++;
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
rowsRaw.push({ kind: 'ins', num: newNum, text: op.s });
|
|
498
|
+
newNum++;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
// Trailing context (lines just after the edit).
|
|
502
|
+
if (fileLines) {
|
|
503
|
+
const afterStart = newNum;
|
|
504
|
+
for (let k = afterStart; k < afterStart + CTX && k <= fileLines.length; k++) {
|
|
505
|
+
rowsRaw.push({ kind: 'eq', num: k, text: fileLines[k - 1] ?? '' });
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
const out = [];
|
|
509
|
+
out.push(`${indent}${c.muted('⎿')} ${time} ${c.dim(summary)}`);
|
|
510
|
+
const MAX = 16;
|
|
511
|
+
const codeWidth = cols - indent.length - gw - 4; // gutter + " x " marker + space
|
|
512
|
+
const render = rowsRaw.slice(0, MAX);
|
|
513
|
+
for (const r of render) {
|
|
514
|
+
const numStr = String(r.num).padStart(gw);
|
|
515
|
+
const code = r.text.length > codeWidth ? r.text.slice(0, codeWidth - 1) + '…' : r.text;
|
|
516
|
+
if (r.kind === 'eq') {
|
|
517
|
+
out.push(`${indent}${diffColors.ctxGutter(numStr)} ${diffColors.ctxCode(code)}`);
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
const marker = r.kind === 'del' ? '-' : '+';
|
|
521
|
+
const bg = r.kind === 'del' ? diffColors.delBg : diffColors.insBg;
|
|
522
|
+
const gutter = r.kind === 'del' ? diffColors.delGutter : diffColors.insGutter;
|
|
523
|
+
const plainLen = numStr.length + 3 + code.length; // num + " x " + code
|
|
524
|
+
const pad = Math.max(0, cols - indent.length - plainLen);
|
|
525
|
+
const inner = `${gutter(numStr)} ${gutter(marker)} ${diffColors.onBg(code)}${' '.repeat(pad)}`;
|
|
526
|
+
out.push(`${indent}${bg(inner)}`);
|
|
527
|
+
}
|
|
419
528
|
}
|
|
420
|
-
if (
|
|
421
|
-
out.push(
|
|
529
|
+
if (rowsRaw.length > MAX) {
|
|
530
|
+
out.push(`${indent}${c.muted(`… +${rowsRaw.length - MAX} lines`)}`);
|
|
422
531
|
}
|
|
423
532
|
return out.join('\n');
|
|
424
533
|
}
|
|
@@ -456,5 +565,5 @@ export function permissionPrompt(toolName, preview) {
|
|
|
456
565
|
`${c.error.bold('n')} ${c.muted('deny')} ` +
|
|
457
566
|
`${c.info.bold('a')} ${c.muted('always')} ` +
|
|
458
567
|
`${c.muted.bold('!')} ${c.muted('never')}\n` +
|
|
459
|
-
` ${c.muted(
|
|
568
|
+
` ${c.muted(CH.arrow)} `);
|
|
460
569
|
}
|