ispbills-icli 8.4.12 → 8.5.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.
- package/bin/icli.js +4 -24
- package/package.json +1 -1
- package/src/banner.js +1 -3
- package/src/client.js +0 -6
- package/src/clipboard.js +1 -35
- package/src/commands.js +8 -4
- package/src/tui/app.js +198 -194
- package/src/tui/components.js +166 -594
- package/src/tui/composer.js +18 -45
- package/src/tui/markdown.js +3 -19
- package/src/tui/run.js +1 -3
- package/src/tui/theme.js +58 -29
- package/src/logger.js +0 -23
- package/src/resume.js +0 -30
- package/src/shell.js +0 -18
- package/src/version-check.js +0 -43
package/bin/icli.js
CHANGED
|
@@ -315,33 +315,13 @@ async function main() {
|
|
|
315
315
|
// plain readline REPL when stdout/stdin is not a TTY (pipes, dumb terminals).
|
|
316
316
|
if (output.isTTY && input.isTTY) {
|
|
317
317
|
const { runTui } = await import('../src/tui/run.js');
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
// Hot-reload resume: if started with --resume <file>, restore the saved session.
|
|
321
|
-
const resumeFile = flag('--resume');
|
|
322
|
-
const resumeSession = loadResume(resumeFile);
|
|
323
|
-
|
|
324
|
-
const action = await runTui(cfg, { display, version: pkgVersion(), resumeSession });
|
|
325
|
-
|
|
326
|
-
const actionName = typeof action === 'string' ? action : action?.action;
|
|
327
|
-
if (actionName === 'logout') {
|
|
318
|
+
const action = await runTui(cfg, { display });
|
|
319
|
+
if (action === 'logout') {
|
|
328
320
|
const cleared = clearConfig();
|
|
329
321
|
console.log(`\n ${cleared ? GREEN + glyphs.check + RESET + ' Logged out — credentials cleared.' : YELLOW + 'Already logged out.' + RESET}`);
|
|
330
322
|
console.log(` ${DIM}Run ${RESET}${ACCENT}icli login${RESET}${DIM} to sign in again.${RESET}\n`);
|
|
331
|
-
} else if (
|
|
332
|
-
|
|
333
|
-
if (ok && action?.resumeFile) {
|
|
334
|
-
// Re-exec with the new binary, passing --resume so the session is restored.
|
|
335
|
-
const { execFileSync } = await import('child_process');
|
|
336
|
-
const newBin = process.argv[1]; // same script path — npm replaced the file in-place
|
|
337
|
-
const baseArgs = process.argv.slice(2).filter((a) => a !== '--resume' && a !== action.resumeFile);
|
|
338
|
-
process.stdout.write(`\n ${ACCENT}Reloading iCli with your session restored…${RESET}\n\n`);
|
|
339
|
-
try {
|
|
340
|
-
execFileSync(process.execPath, [newBin, ...baseArgs, '--resume', action.resumeFile], {
|
|
341
|
-
stdio: 'inherit',
|
|
342
|
-
});
|
|
343
|
-
} catch { /* user Ctrl+C'd the resumed session — that's fine */ }
|
|
344
|
-
}
|
|
323
|
+
} else if (action === 'update') {
|
|
324
|
+
selfUpdate();
|
|
345
325
|
}
|
|
346
326
|
return;
|
|
347
327
|
}
|
package/package.json
CHANGED
package/src/banner.js
CHANGED
|
@@ -22,7 +22,6 @@ const LOGO_WIDTH = Math.max(...LOGO.map((l) => l.length));
|
|
|
22
22
|
export function printBanner(cfg = {}, opts = {}) {
|
|
23
23
|
const width = cols();
|
|
24
24
|
const model = opts.model || cfg._model || 'IspBills AI';
|
|
25
|
-
const version = opts.version || '';
|
|
26
25
|
|
|
27
26
|
console.log();
|
|
28
27
|
if (width >= LOGO_WIDTH) {
|
|
@@ -37,11 +36,10 @@ export function printBanner(cfg = {}, opts = {}) {
|
|
|
37
36
|
parts.push(`${DIM}model${RESET} ${CYAN}${shortModel(model)}${RESET}`);
|
|
38
37
|
if (cfg.user?.name) parts.push(`${DIM}user${RESET} ${WHITE}${cfg.user.name}${RESET}`);
|
|
39
38
|
if (cfg.user?.role) parts.push(`${DIM}role${RESET} ${WHITE}${cfg.user.role}${RESET}`);
|
|
40
|
-
if (version) parts.push(`${DIM}v${RESET} ${WHITE}${version}${RESET}`);
|
|
41
39
|
console.log(' ' + parts.join(` ${GRAY}·${RESET} `));
|
|
42
40
|
if (cfg.url) console.log(` ${DIM}url${RESET} ${ACCENT}${cfg.url}${RESET}`);
|
|
43
41
|
console.log();
|
|
44
|
-
console.log(` ${DIM}Type a message to start. ${RESET}${ACCENT}/help${RESET}${DIM} for commands · ${RESET}${ACCENT}
|
|
42
|
+
console.log(` ${DIM}Type a message to start. ${RESET}${ACCENT}/help${RESET}${DIM} for commands · ${RESET}${ACCENT}Ctrl+C${RESET}${DIM} to exit${RESET}`);
|
|
45
43
|
console.log();
|
|
46
44
|
hr();
|
|
47
45
|
console.log();
|
package/src/client.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { refreshToken } from './auth.js';
|
|
2
|
-
import { logStream } from './logger.js';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Incrementally extract the answer text from the raw JSON the backend streams
|
|
@@ -161,10 +160,5 @@ export async function streamChat(cfg, messages, options = {}) {
|
|
|
161
160
|
// committed answer below what the operator already saw stream by.
|
|
162
161
|
const finalText = reply.text || '';
|
|
163
162
|
const text = finalText.length >= answerText.length ? finalText : answerText;
|
|
164
|
-
|
|
165
|
-
// Log prompt + reply to ~/.config/ispbills-icli/logs/<user>/stream.log
|
|
166
|
-
const prompt = [...messages].reverse().find((m) => m.role === 'user')?.content ?? '';
|
|
167
|
-
logStream(cfg, prompt, text);
|
|
168
|
-
|
|
169
163
|
return { reply, text, meta };
|
|
170
164
|
}
|
package/src/clipboard.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Cross-platform "copy to system clipboard" helper. Best-effort: resolves to
|
|
2
2
|
// false (rather than throwing) when no clipboard tool is available so the TUI
|
|
3
3
|
// can show a friendly notice.
|
|
4
|
-
import { spawn
|
|
4
|
+
import { spawn } from 'child_process';
|
|
5
5
|
|
|
6
6
|
function candidates() {
|
|
7
7
|
if (process.platform === 'darwin') return [['pbcopy', []]];
|
|
@@ -40,37 +40,3 @@ export async function copyToClipboard(text) {
|
|
|
40
40
|
}
|
|
41
41
|
return null;
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
// ── Read from clipboard ─────────────────────────────────────────────────────
|
|
45
|
-
|
|
46
|
-
function readCandidates() {
|
|
47
|
-
if (process.platform === 'darwin') return [['pbpaste', []]];
|
|
48
|
-
if (process.platform === 'win32') return [['powershell', ['-noprofile', '-command', 'Get-Clipboard']]];
|
|
49
|
-
return [
|
|
50
|
-
['wl-paste', ['--no-newline']],
|
|
51
|
-
['xclip', ['-selection', 'clipboard', '-o']],
|
|
52
|
-
['xsel', ['--clipboard', '--output']],
|
|
53
|
-
];
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Read text from the system clipboard.
|
|
58
|
-
* Returns the clipboard string on success, or null when no tool is available
|
|
59
|
-
* or the clipboard is empty.
|
|
60
|
-
*/
|
|
61
|
-
export function readFromClipboard() {
|
|
62
|
-
return new Promise((resolve) => {
|
|
63
|
-
const cands = readCandidates();
|
|
64
|
-
let i = 0;
|
|
65
|
-
function tryNext() {
|
|
66
|
-
if (i >= cands.length) { resolve(null); return; }
|
|
67
|
-
const [cmd, args] = cands[i++];
|
|
68
|
-
execFile(cmd, args, { timeout: 3000, maxBuffer: 1024 * 512 }, (err, stdout) => {
|
|
69
|
-
if (err) { tryNext(); return; }
|
|
70
|
-
const text = stdout.trimEnd();
|
|
71
|
-
resolve(text || null);
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
tryNext();
|
|
75
|
-
});
|
|
76
|
-
}
|
package/src/commands.js
CHANGED
|
@@ -52,12 +52,16 @@ export const SLASH_COMMANDS = [
|
|
|
52
52
|
{ cmd: '/copy', hint: '', desc: 'Copy the last AI answer to the clipboard', group: 'Memory', local: true },
|
|
53
53
|
|
|
54
54
|
// ── Session & app ──
|
|
55
|
-
{ cmd: '/
|
|
56
|
-
{ cmd: '/autopilot', hint: '', desc: 'Toggle autopilot (auto-apply fixes)',
|
|
55
|
+
{ cmd: '/plan', hint: '[prompt]', desc: 'Switch to plan mode (Shift+Tab to cycle)', group: 'Session', local: true },
|
|
56
|
+
{ cmd: '/autopilot', hint: '', desc: 'Toggle autopilot mode (auto-apply fixes)', group: 'Session', local: true },
|
|
57
57
|
{ cmd: '/model', hint: '[name]', desc: 'Show or set the AI model', group: 'Session', local: true },
|
|
58
58
|
{ cmd: '/reasoning', hint: '', desc: 'Toggle reasoning display (Ctrl+T)', group: 'Session', local: true },
|
|
59
|
-
{ cmd: '/
|
|
60
|
-
{ cmd: '/
|
|
59
|
+
{ cmd: '/context', hint: '', desc: 'Show context window token usage', group: 'Session', local: true },
|
|
60
|
+
{ cmd: '/usage', hint: '', desc: 'Show session usage metrics', group: 'Session', local: true },
|
|
61
|
+
{ cmd: '/compact', hint: '[focus]', desc: 'Compress conversation history', group: 'Session', local: true },
|
|
62
|
+
{ cmd: '/session', hint: '', desc: 'Show session info (cwd, branch, tokens)', group: 'Session', local: true },
|
|
63
|
+
{ cmd: '/share', hint: '', desc: 'Share session to a GitHub gist', group: 'Session', local: true },
|
|
64
|
+
{ cmd: '/theme', hint: '', desc: 'View current colour theme info', group: 'Session', local: true },
|
|
61
65
|
{ cmd: '/new', hint: '', desc: 'Start a fresh conversation', group: 'Session', local: true },
|
|
62
66
|
{ cmd: '/history', hint: '', desc: 'Show conversation history', group: 'Session', local: true },
|
|
63
67
|
{ cmd: '/clear', hint: '', desc: 'Clear the screen', group: 'Session', local: true },
|