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.
Files changed (3) hide show
  1. package/bin/icli.js +6 -6
  2. package/package.json +1 -1
  3. 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 { cols, inner, onResize, boxTop, boxBot, boxMid, boxRow, boxTopOpen, boxBotOpen } from '../src/layout.js';
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('#3D444D'),
15
- dim: chalk.hex('#9198A1'),
16
- muted: chalk.hex('#6e7781'),
17
- accent: chalk.hex('#4493F8').bold,
18
- accentD: chalk.hex('#4493F8'),
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ispbills-icli",
3
- "version": "4.0.1",
3
+ "version": "4.0.4",
4
4
  "description": "iCli — IspBills AI network engineer in your terminal",
5
5
  "keywords": [
6
6
  "ispbills",
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
- // ── Copilot CLI palette ────────────────────────────────────────────────────────
10
+ // ── Gemini-style palette ───────────────────────────────────────────────────────
11
11
  const P = {
12
- border: chalk.hex('#3D444D'),
13
- dim: chalk.hex('#9198A1'),
14
- muted: chalk.hex('#6e7781'),
15
- accent: chalk.hex('#4493F8'),
16
- accentB: chalk.hex('#4493F8').bold,
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
- marked.setOptions({ renderer: new TerminalRenderer({
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(' ▌ ') + P.dim(t.trim()),
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
- // Top border
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('Commands', P.border) + '\n');
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.accentB) + '\n');
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.accentB) + '\n');
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.accentB) + '\n');
158
- process.stdout.write(boxBot(P.accentB) + '\n');
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' + boxLine('', P.border) + '\n');
227
+ process.stdout.write('\n');
235
228
  reasoningOpen = false;
236
229
  }
237
230