ispbills-icli 8.7.7 → 8.7.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ispbills-icli",
3
- "version": "8.7.7",
3
+ "version": "8.7.8",
4
4
  "description": "iCli — IspBills AI network engineer in your terminal",
5
5
  "keywords": [
6
6
  "ispbills",
package/src/banner.js CHANGED
@@ -13,7 +13,7 @@ const LOGO = [
13
13
  const LOGO_WIDTH = Math.max(...LOGO.map((l) => l.length));
14
14
 
15
15
  /**
16
- * Full iCopilot banner. On narrow terminals falls back to a compact header.
16
+ * Full iPolot banner. On narrow terminals falls back to a compact header.
17
17
  * A full-width rule frames the header so it uses the whole window.
18
18
  *
19
19
  * @param {object} cfg Loaded config ({ url, user, _model }).
@@ -26,9 +26,9 @@ export function printBanner(cfg = {}, opts = {}) {
26
26
  console.log();
27
27
  if (width >= LOGO_WIDTH) {
28
28
  for (const line of LOGO) console.log(ACCENT + BOLD + line + RESET);
29
- console.log(` ${DIM}iCopilot — network engineer in your terminal${RESET}`);
29
+ console.log(` ${DIM}iPolot — network engineer in your terminal${RESET}`);
30
30
  } else {
31
- console.log(` ${BOLD}iCopilot${RESET} ${DIM}— IspBills AI terminal${RESET}`);
31
+ console.log(` ${BOLD}iPolot${RESET} ${DIM}— IspBills AI terminal${RESET}`);
32
32
  }
33
33
  console.log();
34
34
 
package/src/commands.js CHANGED
@@ -88,7 +88,7 @@ export const SLASH_COMMANDS = [
88
88
  { cmd: '/list-dirs', hint: '', desc: 'Show all allowed directories', group: 'Permissions', local: true },
89
89
  { cmd: '/sandbox', hint: '[enable|disable]', desc: 'Enable/disable local sandboxing', group: 'Permissions', local: true },
90
90
  { cmd: '/experimental', hint: '[on|off|show]', desc: 'Toggle experimental features', group: 'Permissions', local: true },
91
- { cmd: '/init', hint: '', desc: 'Initialize Copilot instructions for this repo', group: 'Permissions', local: true },
91
+ { cmd: '/init', hint: '', desc: 'Initialize .github/copilot-instructions.md for this repo', group: 'Permissions', local: true },
92
92
 
93
93
  // ── Agents & models ──
94
94
  { cmd: '/agent', hint: '', desc: 'Browse and select agents', group: 'Agents', local: true },
@@ -121,9 +121,9 @@ export const SLASH_COMMANDS = [
121
121
  { cmd: '/bug', hint: '', desc: 'Report a bug (alias for /feedback)', group: 'Help', local: true },
122
122
  { cmd: '/update', hint: '', desc: 'Update iCli to the latest version', group: 'Help', local: true },
123
123
  { cmd: '/downgrade', hint: 'VERSION', desc: 'Roll back to a specific CLI version', group: 'Help', local: true },
124
- { cmd: '/app', hint: '', desc: 'Launch the GitHub Copilot desktop app', group: 'Help', local: true },
124
+ { cmd: '/app', hint: '', desc: 'Show instructions to open the iPolot web app', group: 'Help', local: true },
125
125
  { cmd: '/user', hint: '', desc: 'Manage GitHub user list', group: 'Help', local: true },
126
- { cmd: '/login', hint: '', desc: 'Log in to Copilot', group: 'Help', local: true },
126
+ { cmd: '/login', hint: '', desc: 'Log in to iPolot', group: 'Help', local: true },
127
127
  { cmd: '/whoami', hint: '', desc: 'Show current user info', group: 'Help', local: true },
128
128
  { cmd: '/clikit', hint: '[component]', desc: 'Preview CLI UI components', group: 'Help', local: true },
129
129
  { cmd: '/extensions', hint: '[manage|mode]', desc: 'Manage CLI extensions', group: 'Help', local: true },
package/src/tui/app.js CHANGED
@@ -69,6 +69,8 @@ function approxTokens(messages) {
69
69
 
70
70
  const TOKEN_LIMIT = 100000;
71
71
  const SESSION_SORTS = ['relevance', 'created', 'name', 'last used'];
72
+ const TOOL_APPROVAL_ONCE = 'yes, proceed with this tool call';
73
+ const TOOL_APPROVAL_SESSION = '__session_allow__';
72
74
 
73
75
  function sessionAutoName(messages = []) {
74
76
  const firstUser = messages.find((m) => m?.role === 'user' && String(m.content || '').trim());
@@ -899,9 +901,9 @@ export function App({ cfg, display = {}, onExternal, onExit, initialQuestion, sh
899
901
  const toolArgs = reply.toolCall?.args || reply.toolCall?.command || '';
900
902
  const toolDesc = toolArgs ? `${toolName}: ${toolArgs}` : (reply.note || toolName);
901
903
  choiceActionRef.current = (value) => {
902
- if (value === '__session_allow__') {
904
+ if (value === TOOL_APPROVAL_SESSION) {
903
905
  cfg.allowedTools = [...(cfg.allowedTools || []), toolName];
904
- runTurn('yes, proceed with this tool call');
906
+ runTurn(TOOL_APPROVAL_ONCE);
905
907
  return;
906
908
  }
907
909
  if (value != null) runTurn(String(value));
@@ -912,9 +914,9 @@ export function App({ cfg, display = {}, onExternal, onExit, initialQuestion, sh
912
914
  sel: 0, text: '', focusText: false, allowText: true,
913
915
  isToolApproval: true,
914
916
  options: [
915
- { label: '1. Yes', value: 'yes, proceed with this tool call' },
916
- { label: '2. Yes, and approve shell for the rest of the session', value: '__session_allow__' },
917
- { label: '3. No, and tell Copilot what to do differently (Esc)', value: null },
917
+ { label: '1. Yes', value: TOOL_APPROVAL_ONCE },
918
+ { label: `2. Yes, and approve ${toolName} for the rest of the session`, value: TOOL_APPROVAL_SESSION },
919
+ { label: '3. No, and tell iPolot what to do differently (Esc)', value: null },
918
920
  ],
919
921
  });
920
922
  } else if (type === 'prompt' || type === 'choice') {
@@ -1449,7 +1451,7 @@ export function App({ cfg, display = {}, onExternal, onExit, initialQuestion, sh
1449
1451
  if (q === '/init') {
1450
1452
  const fp = resolve(process.cwd(), '.github/copilot-instructions.md');
1451
1453
  if (existsSync(fp)) {
1452
- pushNotice(`Copilot instructions already exist at ${shortenPath(fp)}. Edit it directly.`);
1454
+ pushNotice(`.github/copilot-instructions.md already exists at ${shortenPath(fp)}. Edit it directly.`);
1453
1455
  } else {
1454
1456
  runTurn('Create a .github/copilot-instructions.md file for this repository with sensible defaults for an ISP NOC tool.');
1455
1457
  }
@@ -1683,7 +1685,7 @@ export function App({ cfg, display = {}, onExternal, onExit, initialQuestion, sh
1683
1685
  return;
1684
1686
  }
1685
1687
  if (q === '/app') {
1686
- pushNotice('iCopilot web app — open your IspBills instance in the browser with /o or by typing the URL.', C.muted);
1688
+ pushNotice('iPolot web app — open your IspBills instance in the browser with /o or by typing the URL.', C.muted);
1687
1689
  return;
1688
1690
  }
1689
1691
  if (q === '/user' || q.startsWith('/user ')) {
@@ -1774,7 +1776,7 @@ export function App({ cfg, display = {}, onExternal, onExit, initialQuestion, sh
1774
1776
  if (value == null) {
1775
1777
  if (currentChoice?.isToolApproval || /⚙ Tool request|tool request|approval|shell:/i.test(`${currentChoice?.title || ''} ${currentChoice?.note || ''}`)) {
1776
1778
  setChoice({
1777
- title: 'Tell Copilot what to do differently:',
1779
+ title: 'Tell iPolot what to do differently:',
1778
1780
  note: '',
1779
1781
  sel: 0,
1780
1782
  text: '',
@@ -1836,6 +1838,13 @@ export function App({ cfg, display = {}, onExternal, onExit, initialQuestion, sh
1836
1838
  if (!choice) return;
1837
1839
  if (sessionPicker || diffState) return;
1838
1840
  if (key.escape || (key.ctrl && input === 'c')) { setChoice(null); return; }
1841
+ if (choice.isToolApproval && !choice.focusText) {
1842
+ const yesValue = choice.options.find((o) => o?.value === TOOL_APPROVAL_ONCE)?.value;
1843
+ const alwaysValue = choice.options.find((o) => o?.value === TOOL_APPROVAL_SESSION)?.value;
1844
+ if (input === 'y') { pickChoice(yesValue); return; }
1845
+ if (input === 'a') { pickChoice(alwaysValue); return; }
1846
+ if (input === 'n') { pickChoice(null); return; }
1847
+ }
1839
1848
  if (key.tab && choice.allowText) { setChoice((c) => ({ ...c, focusText: !c.focusText })); return; }
1840
1849
  if (key.upArrow) { setChoice((c) => ({ ...c, focusText: false, sel: Math.max(0, c.sel - 1) })); return; }
1841
1850
  if (key.downArrow) {
@@ -2156,7 +2165,7 @@ export function App({ cfg, display = {}, onExternal, onExit, initialQuestion, sh
2156
2165
 
2157
2166
  const liveArea = live
2158
2167
  ? html`<${Box} flexDirection="column">
2159
- <${StatusLines} lines=${live.status} busy=${busy} />
2168
+ <${StatusLines} lines=${live.status} busy=${busy} cols=${cols} />
2160
2169
  ${live.reasoning ? html`<${Reasoning} text=${live.reasoning} show=${showReasoning} />` : null}
2161
2170
  ${live.text ? html`<${AssistantMessage} text=${live.text} cols=${cols} ts=${startedAt || Date.now()} streaming=${true} />` : null}
2162
2171
  ${live.reply?.steps ? html`<${Plan} reply=${live.reply} live=${true} />` : null}
@@ -2185,7 +2194,7 @@ export function App({ cfg, display = {}, onExternal, onExit, initialQuestion, sh
2185
2194
  />
2186
2195
  ${diffState.commentInput
2187
2196
  ? html`<${Choice}
2188
- title=${`Tell Copilot what stands out about ${diffState.commentInput.file}:${diffState.commentInput.lineNumber || '?'}`}
2197
+ title=${`Tell iPolot what stands out about ${diffState.commentInput.file}:${diffState.commentInput.lineNumber || '?'}`}
2189
2198
  note=${diffState.commentInput.content}
2190
2199
  sel=${0}
2191
2200
  allowText=${true}
@@ -2258,6 +2267,7 @@ export function App({ cfg, display = {}, onExternal, onExit, initialQuestion, sh
2258
2267
  clearToken=${composerClearToken}
2259
2268
  />`}
2260
2269
 
2270
+ <${Text} color=${C.separator}>${'─'.repeat(Math.max(1, cols))}<//>
2261
2271
  <${Box} paddingX=${1} width=${cols}>
2262
2272
  <${Box} flexGrow=${1}>
2263
2273
  <${Text} color=${footerStatusColor} bold=${busy} wrap="truncate-end">${footerStatusLeft}<//>
@@ -124,7 +124,7 @@ export function TabBar({ active = 'session', cols = 80 }) {
124
124
  const label = ` ${i + 1} ${tab.label} `.padEnd(TAB_W);
125
125
  return html`
126
126
  <${Box} key=${tab.id} width=${TAB_W}>
127
- <${Text} bold=${on} color=${on ? 'cyan' : 'white'} dimColor=${!on} inverse=${on}>
127
+ <${Text} bold=${on} color=${on ? C.brand : C.muted} dimColor=${!on} inverse=${on}>
128
128
  ${label}
129
129
  <//>
130
130
  <//>`;
@@ -168,7 +168,7 @@ export function Banner({ cfg = {}, model, width = 80, compact = false, streamerM
168
168
  if (compact) {
169
169
  return html`
170
170
  <${Box} marginBottom=${1}>
171
- <${Text} color=${C.accent} bold>${glyphs.ring} iCopilot <//>
171
+ <${Text} color=${C.accent} bold>${glyphs.ring} iPolot <//>
172
172
  <${Text} color=${C.muted}>${midDot()}<//>
173
173
  <${Text} color=${C.muted}>${m}<//>
174
174
  ${cfg.user?.name ? html`<${Text} color=${C.muted}>${midDot()}<//>` : null}
@@ -183,7 +183,7 @@ export function Banner({ cfg = {}, model, width = 80, compact = false, streamerM
183
183
  <//>
184
184
 
185
185
  <${Box} marginTop=${1} justifyContent="center">
186
- <${Text} color=${C.accent} bold>${glyphs.ring} iCopilot<//>
186
+ <${Text} color=${C.accent} bold>${glyphs.ring} iPolot<//>
187
187
  <${Text} color=${C.muted}> ${dash()} IspBills network engineer in your terminal<//>
188
188
  <//>
189
189
 
@@ -229,8 +229,9 @@ export function UserMessage({ text, cols = 80, ts }) {
229
229
  const stamp = ts ? new Date(ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }) : '';
230
230
  return html`
231
231
  <${Box} flexDirection="column" marginBottom=${1}>
232
+ <${Sep} cols=${cols} />
232
233
  <${Box} flexDirection="row" justifyContent="space-between" width=${cols} paddingX=${1}>
233
- <${Text} bold color="cyan">You<//>
234
+ <${Text} bold color=${C.brand}>You<//>
234
235
  <${Text} color=${C.muted}>${stamp}<//>
235
236
  <//>
236
237
  <${Box} paddingX=${1} marginLeft=${2} flexWrap="wrap">
@@ -264,12 +265,14 @@ function StreamingText({ text = '' }) {
264
265
 
265
266
  export function AssistantMessage({ text, cols = 80, ts, label, streaming = false }) {
266
267
  const stamp = ts ? new Date(ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }) : '';
267
- const speakerLabel = streaming ? 'iCopilot ●' : 'iCopilot';
268
268
  const bodyWidth = Math.min(cols, 100);
269
269
  return html`
270
270
  <${Box} flexDirection="column" marginBottom=${1}>
271
+ <${Sep} cols=${cols} />
271
272
  <${Box} flexDirection="row" justifyContent="space-between" width=${cols} paddingX=${1}>
272
- <${Text} bold color=${C.success}>${speakerLabel}<//>
273
+ <${Box}>
274
+ <${Text} color=${C.accent} bold>${glyphs.copilotDot} <//><${Text} bold>iPolot${streaming ? ' ●' : ''}<//>
275
+ <//>
273
276
  <${Text} color=${C.muted}>${stamp}<//>
274
277
  <//>
275
278
  <${Box} paddingX=${1} marginLeft=${2} flexDirection="column" width=${bodyWidth}>
@@ -279,18 +282,22 @@ export function AssistantMessage({ text, cols = 80, ts, label, streaming = false
279
282
  }
280
283
 
281
284
  export function ShellOutput({ command, output, cols = 80, ts, code = 0 }) {
285
+ const ok = code === 0;
286
+ const tone = ok ? C.success : C.error;
282
287
  return html`
283
288
  <${Box} flexDirection="column" marginTop=${1}>
289
+ <${Sep} cols=${cols} />
284
290
  <${SpeakerLine}
285
291
  label=${"$ Shell"}
286
- color=${code === 0 ? C.warning : C.error}
292
+ color=${tone}
287
293
  cols=${cols}
288
294
  ts=${ts}
289
- rightLabel=${code === 0 ? '' : `exit ${code}`}
295
+ rightLabel=${ok ? '' : `exit ${code}`}
290
296
  />
291
297
  <${Box} paddingX=${1} flexDirection="column">
292
298
  <${Text} color=${C.warning}>$ ${command}<//>
293
- <${Text}>${output || '(no output)'}<//>
299
+ <${Text} color=${tone}>${ok ? glyphs.check : `${glyphs.cross} exit ${code}` }<//>
300
+ <${Text} color=${ok ? C.white : tone}>${output || '(no output)'}<//>
294
301
  <//>
295
302
  <//>`;
296
303
  }
@@ -386,6 +393,7 @@ export function StatusLines({ lines = [], busy = false, navigable = false }) {
386
393
  if (!lines.length) return null;
387
394
  const lastIdx = lines.length - 1;
388
395
 
396
+
389
397
  return html`
390
398
  <${Box} flexDirection="column" marginTop=${1}>
391
399
  ${lines.map((line, i) => {
@@ -654,15 +662,17 @@ export function Thinking({ label = 'Working', startedAt }) {
654
662
  }
655
663
 
656
664
  export function Choice({ title, note, options = [], sel = 0, allowText = false, text = '', focusText = false, isToolApproval = false, cols = 80 }) {
657
- // §13: tool approval uses exact spec format with Sep-style header
665
+ // §13: tool approval uses separator-line style matching TUI.md spec exactly
658
666
  if (isToolApproval) {
659
667
  return html`
660
668
  <${Box} flexDirection="column" marginTop=${1}>
661
- <${Box} paddingX=${2} marginTop=${1}>
662
- <${Text} color=${C.accent} bold>${glyphs.bolt} Tool request<//>
669
+ <${Sep} cols=${cols} />
670
+ <${Box} paddingX=${1} marginBottom=${1}>
671
+ <${Text} color=${C.accent} bold>${glyphs.settings} Tool request<//>
663
672
  <//>
664
- ${note ? html`<${Box} paddingX=${2} marginTop=${1}><${Text} color=${C.muted}>${note}<//><//>` : null}
665
- <${Box} flexDirection="column" marginTop=${1} paddingX=${2}>
673
+ <${Sep} cols=${cols} />
674
+ ${note ? html`<${Box} paddingX=${1} marginTop=${1} marginBottom=${1}><${Text} color=${C.white}>${note}<//><//>` : null}
675
+ <${Box} flexDirection="column" marginTop=${1} paddingX=${1}>
666
676
  ${options.map((o, i) => {
667
677
  const on = !focusText && i === sel;
668
678
  const label = o.label ?? o.value ?? String(o);
@@ -676,13 +686,13 @@ export function Choice({ title, note, options = [], sel = 0, allowText = false,
676
686
  ${allowText
677
687
  ? html`<${Box} key="freetext" marginTop=${1}>
678
688
  <${Text} color=${focusText ? C.accent : C.muted}>${focusText ? glyphs.prompt + ' ' : ' '}<//>
679
- <${Text} color=${C.muted}>Tell Copilot what to do differently: <//>
689
+ <${Text} color=${C.muted}>Tell iPolot what to do differently: <//>
680
690
  <${Text} color=${C.white}>${text}<//>${focusText ? html`<${Text} inverse> <//>` : null}
681
691
  <//>`
682
692
  : null}
683
693
  <//>
684
- <${Box} marginTop=${1} paddingX=${2}>
685
- <${Text} color=${C.muted}>${glyphs.up}${glyphs.down} choose ${midDot()}Enter confirm ${midDot()}Esc cancel<//>
694
+ <${Box} marginTop=${1} paddingX=${1}>
695
+ <${Text} color=${C.muted}>y / a / n ${midDot()} ${glyphs.up}${glyphs.down} move ${midDot()} Enter confirm ${midDot()} Esc cancel<//>
686
696
  <//>
687
697
  <//>`;
688
698
  }
@@ -512,7 +512,7 @@ export function Composer({
512
512
  <//>`
513
513
  : null}
514
514
 
515
- <${Text} color=${C.muted}>${''.repeat(Math.max(1, width))}<//>
515
+ <${Text} color=${C.accent}>${'_'.repeat(Math.max(1, width))}<//>
516
516
 
517
517
  <${Box} width=${width} paddingX=${1} justifyContent="space-between">
518
518
  <${Box} flexGrow=${1}>
package/src/tui/run.js CHANGED
@@ -93,7 +93,7 @@ function exitAltScreen() {
93
93
  altScreenActive = false;
94
94
  }
95
95
 
96
- // iCopilot wordmark in block letters
96
+ // iPolot wordmark in block letters
97
97
  const LOGO = [
98
98
  ' ██╗ ██████╗ ██████╗ ██████╗ ██╗██╗ ██████╗ ████████╗',
99
99
  ' ██║██╔════╝██╔═══██╗██╔══██╗██║██║ ██╔═══██╗╚══██╔══╝',
@@ -145,8 +145,8 @@ async function animateSplash(cfg = {}) {
145
145
  const R = '\x1b[0m';
146
146
 
147
147
  const hr = `${SEP}${'─'.repeat(cols)}${R}`;
148
- const subtitle = `${ACC}◈ iCopilot${R} ${DIM}IspBills network engineer in your terminal${R}`;
149
- const subtitleLen = 10 + 2 + 46; // approx visual length
148
+ const subtitle = `${ACC}◈ iPolot${R} ${DIM}IspBills network engineer in your terminal${R}`;
149
+ const subtitleLen = subtitle.replace(/\x1b\[[0-9;]*m/g, '').length;
150
150
  const subPad = ' '.repeat(Math.max(0, Math.floor((cols - subtitleLen) / 2)));
151
151
 
152
152
  process.stdout.write(`\n${hr}\n`);
package/src/tui/theme.js CHANGED
@@ -46,6 +46,7 @@ const ASCII_EXTRA = {
46
46
  chip: '|', // chip separator
47
47
  expand: '+', // expand indicator
48
48
  collapse: '-', // collapse indicator
49
+ settings: '#', // tool-approval header (ASCII safe)
49
50
  };
50
51
  const UNICODE_EXTRA = {
51
52
  copilotDot: '◈', // spec §6: ◈ Copilot speaker label (◈ in accent/purple)
@@ -56,6 +57,7 @@ const UNICODE_EXTRA = {
56
57
  chip: '·',
57
58
  expand: '▸',
58
59
  collapse: '▾',
60
+ settings: '⚙', // spec §13: tool request header glyph
59
61
  };
60
62
 
61
63
  // Merged glyph proxy: own extras first, then ui.js base glyphs.