ispbills-icli 4.0.1 → 4.0.4
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 +6 -6
- package/package.json +1 -1
- package/src/chat.js +29 -36
package/bin/icli.js
CHANGED
|
@@ -4,18 +4,18 @@ import { stdin as input, stdout as output, argv, exit } from 'process';
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { loadConfig, configPath } from '../src/config.js';
|
|
6
6
|
import { loginFlow } from '../src/auth.js';
|
|
7
|
-
import {
|
|
7
|
+
import { inner, onResize, boxTop, boxBot, boxMid, boxRow } from '../src/layout.js';
|
|
8
8
|
import {
|
|
9
9
|
streamChat, printBanner, printSlashMenu, printUserBox,
|
|
10
10
|
SLASH_COMMANDS, slashCompleter,
|
|
11
11
|
} from '../src/chat.js';
|
|
12
12
|
|
|
13
13
|
const P = {
|
|
14
|
-
border: chalk.hex('#
|
|
15
|
-
dim: chalk.hex('#
|
|
16
|
-
muted: chalk.hex('#
|
|
17
|
-
accent: chalk.hex('#
|
|
18
|
-
accentD: chalk.hex('#
|
|
14
|
+
border: chalk.hex('#303134'),
|
|
15
|
+
dim: chalk.hex('#9AA0A6'),
|
|
16
|
+
muted: chalk.hex('#80868B'),
|
|
17
|
+
accent: chalk.hex('#8AB4F8').bold,
|
|
18
|
+
accentD: chalk.hex('#8AB4F8'),
|
|
19
19
|
bold: chalk.bold.white,
|
|
20
20
|
ok: chalk.green,
|
|
21
21
|
warn: chalk.yellow,
|
package/package.json
CHANGED
package/src/chat.js
CHANGED
|
@@ -7,13 +7,13 @@ import { cols, inner, visLen, padRight, stripAnsi,
|
|
|
7
7
|
boxTop, boxMid, boxBot, boxTopOpen, boxBotOpen,
|
|
8
8
|
boxRow, boxLine } from './layout.js';
|
|
9
9
|
|
|
10
|
-
// ──
|
|
10
|
+
// ── Gemini-style palette ───────────────────────────────────────────────────────
|
|
11
11
|
const P = {
|
|
12
|
-
border: chalk.hex('#
|
|
13
|
-
dim: chalk.hex('#
|
|
14
|
-
muted: chalk.hex('#
|
|
15
|
-
accent: chalk.hex('#
|
|
16
|
-
accentB: chalk.hex('#
|
|
12
|
+
border: chalk.hex('#303134'),
|
|
13
|
+
dim: chalk.hex('#9AA0A6'),
|
|
14
|
+
muted: chalk.hex('#80868B'),
|
|
15
|
+
accent: chalk.hex('#8AB4F8'),
|
|
16
|
+
accentB: chalk.hex('#8AB4F8').bold,
|
|
17
17
|
white: chalk.white,
|
|
18
18
|
bold: chalk.bold.white,
|
|
19
19
|
ok: chalk.green,
|
|
@@ -24,18 +24,27 @@ const P = {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
// ── Markdown ───────────────────────────────────────────────────────────────────
|
|
27
|
-
|
|
27
|
+
const termRenderer = new TerminalRenderer({
|
|
28
28
|
firstHeading: chalk.bold.white,
|
|
29
29
|
heading: chalk.bold.white,
|
|
30
30
|
strong: chalk.bold.white,
|
|
31
31
|
em: chalk.italic.hex('#9198A1'),
|
|
32
32
|
codespan: (t) => chalk.bgHex('#161b22').hex('#c9d1d9')(` ${t} `),
|
|
33
33
|
code: (t) => '\n' + t.split('\n').map(l => ' ' + chalk.hex('#a5d6ff')(l)).join('\n') + '\n',
|
|
34
|
-
blockquote: (t) => P.dim('
|
|
35
|
-
listitem: (t) => ` ${P.accent('·')} ${t.trimEnd()}`,
|
|
34
|
+
blockquote: (t) => P.dim('▌ ') + P.dim(t.trim()) + '\n',
|
|
36
35
|
hr: () => P.border('─'.repeat(inner())) + '\n',
|
|
37
36
|
link: (_h, _t, t) => chalk.hex('#58a6ff').underline(t || _h),
|
|
38
|
-
})
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Override listitem on the instance — the options.listitem is a chalk color, not a renderer fn
|
|
40
|
+
const _origListitem = termRenderer.listitem.bind(termRenderer);
|
|
41
|
+
termRenderer.listitem = function(item) {
|
|
42
|
+
const raw = _origListitem(item);
|
|
43
|
+
// Replace the default ' * ' or ' - ' bullet with our accent '·'
|
|
44
|
+
return raw.replace(/^\s*[*\-]\s/, (m) => m.replace(/[*\-]/, P.accent('·')));
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
marked.setOptions({ renderer: termRenderer });
|
|
39
48
|
|
|
40
49
|
function renderMarkdown(text) {
|
|
41
50
|
try { return marked(text).trimEnd(); } catch { return text; }
|
|
@@ -61,21 +70,12 @@ const shortModel = (m) =>
|
|
|
61
70
|
// ── Banner ────────────────────────────────────────────────────────────────────
|
|
62
71
|
export function printBanner(cfg, meta = {}) {
|
|
63
72
|
const model = meta.model ? shortModel(meta.model) : (cfg?._model ?? null);
|
|
64
|
-
const w = cols();
|
|
65
73
|
|
|
66
74
|
process.stdout.write('\n');
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
process.stdout.write(boxTop('', P.border) + '\n');
|
|
70
|
-
|
|
71
|
-
// Title row
|
|
72
|
-
const title = P.bold('iCopilot') + P.dim(' · IspBills AI Network Engineer');
|
|
73
|
-
process.stdout.write(boxRow(title, P.border) + '\n');
|
|
74
|
-
|
|
75
|
-
// Divider
|
|
75
|
+
process.stdout.write(boxTop('iCopilot', P.border) + '\n');
|
|
76
|
+
process.stdout.write(boxRow(P.dim('Gemini-style terminal mode'), P.border) + '\n');
|
|
76
77
|
process.stdout.write(boxMid(P.border) + '\n');
|
|
77
78
|
|
|
78
|
-
// Session info rows
|
|
79
79
|
const row1parts = [];
|
|
80
80
|
if (model) row1parts.push(P.dim('model ') + P.white(model));
|
|
81
81
|
if (cfg?.user?.name) row1parts.push(P.dim('user ') + P.white(cfg.user.name));
|
|
@@ -89,10 +89,8 @@ export function printBanner(cfg, meta = {}) {
|
|
|
89
89
|
process.stdout.write(boxRow(P.dim('url ') + P.accent(url), P.border) + '\n');
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
// Divider
|
|
93
92
|
process.stdout.write(boxMid(P.border) + '\n');
|
|
94
93
|
|
|
95
|
-
// Hints row
|
|
96
94
|
const hints = [
|
|
97
95
|
P.accentB('/') + P.muted(' commands'),
|
|
98
96
|
P.accentB('Tab') + P.muted(' autocomplete'),
|
|
@@ -100,8 +98,6 @@ export function printBanner(cfg, meta = {}) {
|
|
|
100
98
|
P.accentB('Ctrl+C') + P.muted(' exit'),
|
|
101
99
|
].join(P.muted(' '));
|
|
102
100
|
process.stdout.write(boxRow(hints, P.border) + '\n');
|
|
103
|
-
|
|
104
|
-
// Bottom border
|
|
105
101
|
process.stdout.write(boxBot(P.border) + '\n\n');
|
|
106
102
|
}
|
|
107
103
|
|
|
@@ -121,7 +117,7 @@ export const SLASH_COMMANDS = [
|
|
|
121
117
|
|
|
122
118
|
export function printSlashMenu() {
|
|
123
119
|
process.stdout.write('\n');
|
|
124
|
-
process.stdout.write(boxTop('
|
|
120
|
+
process.stdout.write(boxTop('Slash commands', P.border) + '\n');
|
|
125
121
|
for (const { cmd: c, hint, desc } of SLASH_COMMANDS) {
|
|
126
122
|
const key = P.accentB(c) + (hint ? P.dim(' ' + hint) : '');
|
|
127
123
|
const line = padRight(key, 22) + ' ' + P.dim(desc);
|
|
@@ -141,21 +137,21 @@ export function slashCompleter(line) {
|
|
|
141
137
|
// ── User message box ───────────────────────────────────────────────────────────
|
|
142
138
|
export function printUserBox(question) {
|
|
143
139
|
process.stdout.write('\n');
|
|
144
|
-
process.stdout.write(boxTop('You', P.
|
|
140
|
+
process.stdout.write(boxTop('You', P.border) + '\n');
|
|
145
141
|
// Word-wrap the question to fit inside the box
|
|
146
142
|
const maxW = inner();
|
|
147
143
|
const words = question.split(' ');
|
|
148
144
|
let line = '';
|
|
149
145
|
for (const word of words) {
|
|
150
146
|
if (line.length + word.length + 1 > maxW && line) {
|
|
151
|
-
process.stdout.write(boxRow(P.white(line), P.
|
|
147
|
+
process.stdout.write(boxRow(P.white(line), P.border) + '\n');
|
|
152
148
|
line = word;
|
|
153
149
|
} else {
|
|
154
150
|
line = line ? line + ' ' + word : word;
|
|
155
151
|
}
|
|
156
152
|
}
|
|
157
|
-
if (line) process.stdout.write(boxRow(P.white(line), P.
|
|
158
|
-
process.stdout.write(boxBot(P.
|
|
153
|
+
if (line) process.stdout.write(boxRow(P.white(line), P.border) + '\n');
|
|
154
|
+
process.stdout.write(boxBot(P.border) + '\n');
|
|
159
155
|
}
|
|
160
156
|
|
|
161
157
|
// ── streamChat ────────────────────────────────────────────────────────────────
|
|
@@ -194,19 +190,17 @@ export async function streamChat(cfg, messages, context = {}, opts = {}) {
|
|
|
194
190
|
let rawAnswer = '';
|
|
195
191
|
let finalResult = null;
|
|
196
192
|
|
|
197
|
-
// Open the iCopilot response box
|
|
198
193
|
function ensureBoxOpen() {
|
|
199
194
|
if (boxOpen) return;
|
|
200
195
|
boxOpen = true;
|
|
201
196
|
process.stdout.write('\n' + boxTopOpen('iCopilot', P.border) + '\n');
|
|
202
197
|
}
|
|
203
198
|
|
|
204
|
-
// Ora spinner — sits on a │ prefixed line
|
|
205
199
|
const spinner = ora({
|
|
206
200
|
text: P.dim('Thinking…'),
|
|
207
201
|
color: 'blue',
|
|
208
202
|
spinner: 'dots',
|
|
209
|
-
prefixText: P.border('│') + '
|
|
203
|
+
prefixText: P.border('│') + ' ',
|
|
210
204
|
});
|
|
211
205
|
|
|
212
206
|
const stopSpinner = () => { if (spinner.isSpinning) spinner.stop(); };
|
|
@@ -215,8 +209,7 @@ export async function streamChat(cfg, messages, context = {}, opts = {}) {
|
|
|
215
209
|
if (reasoningOpen) return;
|
|
216
210
|
stopSpinner();
|
|
217
211
|
reasoningOpen = true;
|
|
218
|
-
process.stdout.write(boxLine('', P.border) + '\n');
|
|
219
|
-
process.stdout.write(boxLine(P.dim('╌ Reasoning ') + P.border('╌'.repeat(Math.max(0, inner() - 14))), P.border) + '\n');
|
|
212
|
+
process.stdout.write(boxLine(P.dim('reasoning'), P.border) + '\n');
|
|
220
213
|
process.stdout.write(boxLine(P.border('│ '), P.border));
|
|
221
214
|
}
|
|
222
215
|
|
|
@@ -231,7 +224,7 @@ export async function streamChat(cfg, messages, context = {}, opts = {}) {
|
|
|
231
224
|
|
|
232
225
|
function closeReasoning() {
|
|
233
226
|
if (!reasoningOpen) return;
|
|
234
|
-
process.stdout.write('\n'
|
|
227
|
+
process.stdout.write('\n');
|
|
235
228
|
reasoningOpen = false;
|
|
236
229
|
}
|
|
237
230
|
|