@thegitai/cli 1.0.0-preview.2 → 1.0.0-preview.3

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.
@@ -9,10 +9,15 @@ function sanitizeModelInfo(raw) {
9
9
  const value = raw;
10
10
  const id = Number(value.id);
11
11
  const label = String(value.label ?? '').trim();
12
- if (!Number.isInteger(id) || id <= 0 || !label) {
12
+ const costRating = Number(value.costRating);
13
+ const description = String(value.description ?? '').trim();
14
+ if (!Number.isInteger(id) || id <= 0 || !label || !isCostRating(costRating)) {
13
15
  return null;
14
16
  }
15
- return { id, label };
17
+ return { id, label, costRating, description };
18
+ }
19
+ function isCostRating(value) {
20
+ return Number.isInteger(value) && value >= 1 && value <= 3;
16
21
  }
17
22
  export function getModelsCachePath(env = process.env) {
18
23
  return path.join(getClientStateDir(env), 'models.json');
@@ -1238,7 +1238,11 @@ export function buildModelPickerOptions(currentModelId, serverModels) {
1238
1238
  return serverModels.map((model) => ({
1239
1239
  id: model.id,
1240
1240
  label: model.label,
1241
- meta: model.id === currentModelId ? 'current' : '',
1241
+ publicId: model.id,
1242
+ costRating: model.costRating,
1243
+ current: model.id === currentModelId,
1244
+ disabled: false,
1245
+ note: model.description,
1242
1246
  }));
1243
1247
  }
1244
1248
  function getDefaultModelPickerIndex(currentModelId, serverModels) {
@@ -17,9 +17,9 @@ const OVERLAY_PANEL_MAX_WIDTH = 86;
17
17
  const OVERLAY_PANEL_MARGIN_LINES = 2;
18
18
  const OVERLAY_BORDER_COLOR = 'yellow';
19
19
  const OVERLAY_WARNING_COLOR = 'ansi256(208)';
20
- const MODEL_PICKER_PANEL_MAX_WIDTH = 86;
20
+ const MODEL_PICKER_PANEL_MAX_WIDTH = 120;
21
21
  const MODEL_PICKER_PANEL_MARGIN_LINES = 2;
22
- const MODEL_PICKER_BORDER_COLOR = 'gray';
22
+ const MODEL_PICKER_BORDER_COLOR = 'cyan';
23
23
  const MODEL_PICKER_ACCENT_COLOR = 'cyan';
24
24
  const MODEL_PICKER_HIGHLIGHT_BG = 'ansi256(87)';
25
25
  const MODEL_PICKER_META_INDENT = ' ';
@@ -170,8 +170,11 @@ function buildModelPickerOptions(currentModelId, serverModels) {
170
170
  return serverModels.map((model) => ({
171
171
  id: model.id,
172
172
  label: model.label,
173
- meta: model.id === currentModelId ? 'current' : '',
173
+ publicId: model.id,
174
+ costRating: model.costRating,
175
+ current: model.id === currentModelId,
174
176
  disabled: false,
177
+ note: model.description,
175
178
  }));
176
179
  }
177
180
  function getInputCommandToken(input) {
@@ -598,77 +601,110 @@ function padSpansToInnerWidth(spans, innerWidth, fill) {
598
601
  function modelPickerPanelSideLine(content, innerWidth) {
599
602
  return overlayPanelLine(content, innerWidth, MODEL_PICKER_BORDER_COLOR);
600
603
  }
604
+ const MODEL_PICKER_COST_WIDTH = 6;
605
+ const MODEL_PICKER_MODEL_WIDTH = 42;
606
+ const MODEL_PICKER_NUMBER_WIDTH = 4;
607
+ const MODEL_PICKER_SEPARATOR = ' │ ';
608
+ const MODEL_PICKER_WIDE_MIN_WIDTH = 78;
601
609
  function modelPickerTopBorder(panelWidth) {
602
- const prefix = '╭─ Models ';
603
- const suffix = '';
604
- const dashCount = Math.max(0, panelWidth - prefix.length - suffix.length);
605
- return line(span('╭─ ', { color: MODEL_PICKER_BORDER_COLOR }), span('Models', { color: MODEL_PICKER_ACCENT_COLOR, bold: true }), span(` ${'─'.repeat(dashCount)}╮`, { color: MODEL_PICKER_BORDER_COLOR }));
610
+ const fullTitle = ' TheGitAI - Model Selection ';
611
+ const compactTitle = ' Model Selection ';
612
+ const title = panelWidth >= fullTitle.length + 4 ? fullTitle : compactTitle;
613
+ const available = Math.max(0, panelWidth - 2 - [...title].length);
614
+ const left = Math.floor(available / 2);
615
+ const right = available - left;
616
+ return line(span(`╭${'─'.repeat(left)}`, { color: MODEL_PICKER_BORDER_COLOR }), span(title, { color: MODEL_PICKER_ACCENT_COLOR, bold: true }), span(`${'─'.repeat(right)}╮`, { color: MODEL_PICKER_BORDER_COLOR }));
617
+ }
618
+ function modelPickerDivider(panelWidth) {
619
+ return plainLine(`├${'─'.repeat(panelWidth - 2)}┤`, {
620
+ color: MODEL_PICKER_BORDER_COLOR,
621
+ });
622
+ }
623
+ function modelPickerCell(text, width, style = {}) {
624
+ const fitted = fitLine(text, width);
625
+ return span(`${fitted}${' '.repeat(Math.max(0, width - [...fitted].length))}`, style);
626
+ }
627
+ function modelPickerCostText(rating) {
628
+ const steps = Math.max(1, Math.min(3, Math.round(rating)));
629
+ return '$'.repeat(steps);
630
+ }
631
+ function modelPickerNotesWidth(innerWidth) {
632
+ return Math.max(18, innerWidth -
633
+ MODEL_PICKER_NUMBER_WIDTH -
634
+ MODEL_PICKER_MODEL_WIDTH -
635
+ MODEL_PICKER_COST_WIDTH -
636
+ MODEL_PICKER_SEPARATOR.length * 2);
637
+ }
638
+ function modelPickerModelSpans(option, selected, width, showCurrentTag, labelStyle, selectedStyle) {
639
+ const tag = option.current && showCurrentTag ? ' (current)' : '';
640
+ const labelWidth = Math.max(1, width - [...tag].length);
641
+ const label = fitLine(`${selected ? '▶ ' : ' '}${option.label}`, labelWidth);
642
+ const used = [...label].length + [...tag].length;
643
+ return [
644
+ span(label, { ...labelStyle, ...selectedStyle }),
645
+ span(tag, { color: 'gray', ...selectedStyle }),
646
+ span(' '.repeat(Math.max(0, width - used)), selectedStyle),
647
+ ];
606
648
  }
607
649
  function modelPickerItemLines(option, selected, innerWidth) {
608
- const highlight = { bgColor: MODEL_PICKER_HIGHLIGHT_BG };
609
- if (selected) {
610
- const titleSpans = padSpansToInnerWidth([
611
- span('▌', {
612
- color: MODEL_PICKER_ACCENT_COLOR,
613
- bold: true,
614
- ...highlight,
615
- }),
616
- span('▶ ', {
617
- color: MODEL_PICKER_ACCENT_COLOR,
618
- bold: true,
619
- ...highlight,
620
- }),
621
- span('o ', { color: MODEL_PICKER_ACCENT_COLOR, ...highlight }),
622
- span(option.label, {
623
- color: MODEL_PICKER_ACCENT_COLOR,
624
- bold: true,
625
- ...highlight,
626
- }),
627
- ], innerWidth, highlight);
628
- const lines = [modelPickerPanelSideLine(line(...titleSpans), innerWidth)];
629
- if (option.meta) {
630
- const metaSpans = padSpansToInnerWidth([
631
- span(`${MODEL_PICKER_META_INDENT}${option.meta}`, {
632
- color: 'gray',
633
- ...highlight,
634
- }),
635
- ], innerWidth, highlight);
636
- lines.push(modelPickerPanelSideLine(line(...metaSpans), innerWidth));
637
- }
638
- return lines;
650
+ const selectedStyle = selected ? { bgColor: MODEL_PICKER_HIGHLIGHT_BG } : {};
651
+ const labelStyle = option.disabled
652
+ ? { color: 'gray' }
653
+ : selected
654
+ ? { color: 'cyan', bold: true }
655
+ : {};
656
+ const numberCell = modelPickerCell(String(option.publicId), MODEL_PICKER_NUMBER_WIDTH, { color: 'cyan', bold: selected, ...selectedStyle });
657
+ const cost = modelPickerCostText(option.costRating);
658
+ if (innerWidth < MODEL_PICKER_WIDE_MIN_WIDTH) {
659
+ const modelWidth = Math.max(12, innerWidth - MODEL_PICKER_NUMBER_WIDTH - MODEL_PICKER_COST_WIDTH - 2);
660
+ const row = [
661
+ numberCell,
662
+ ...modelPickerModelSpans(option, selected, modelWidth, false, labelStyle, selectedStyle),
663
+ span(' ', selectedStyle),
664
+ modelPickerCell(cost, MODEL_PICKER_COST_WIDTH, selectedStyle),
665
+ ];
666
+ return [
667
+ modelPickerPanelSideLine(line(...padSpansToInnerWidth(row, innerWidth, selectedStyle)), innerWidth),
668
+ ];
639
669
  }
640
- const labelColor = option.disabled ? 'gray' : MODEL_PICKER_ACCENT_COLOR;
641
- const lines = [
642
- modelPickerPanelSideLine(line(span(' o ', { color: labelColor }), span(option.label, { color: labelColor, bold: !option.disabled })), innerWidth),
670
+ const row = [
671
+ numberCell,
672
+ ...modelPickerModelSpans(option, selected, MODEL_PICKER_MODEL_WIDTH, true, labelStyle, selectedStyle),
673
+ span(MODEL_PICKER_SEPARATOR, { color: 'gray', ...selectedStyle }),
674
+ modelPickerCell(cost, MODEL_PICKER_COST_WIDTH, selectedStyle),
675
+ span(MODEL_PICKER_SEPARATOR, { color: 'gray', ...selectedStyle }),
676
+ modelPickerCell(option.note, modelPickerNotesWidth(innerWidth), {
677
+ color: 'gray',
678
+ ...selectedStyle,
679
+ }),
680
+ ];
681
+ return [
682
+ modelPickerPanelSideLine(line(...padSpansToInnerWidth(row, innerWidth, selectedStyle)), innerWidth),
643
683
  ];
644
- if (option.meta) {
645
- lines.push(modelPickerPanelSideLine(line(span(`${MODEL_PICKER_META_INDENT}${option.meta}`, { color: 'gray' })), innerWidth));
646
- }
647
- return lines;
648
684
  }
649
- function modelPickerSeparatorLine(innerWidth) {
650
- return modelPickerPanelSideLine(line(span('┈'.repeat(Math.max(1, innerWidth)), { color: 'gray', dim: true })), innerWidth);
685
+ function modelPickerHeaderLine(innerWidth) {
686
+ const heading = { color: MODEL_PICKER_ACCENT_COLOR, bold: true };
687
+ if (innerWidth < MODEL_PICKER_WIDE_MIN_WIDTH) {
688
+ return line(modelPickerCell('#', MODEL_PICKER_NUMBER_WIDTH, heading), modelPickerCell('Model', Math.max(1, innerWidth - MODEL_PICKER_NUMBER_WIDTH), heading));
689
+ }
690
+ return line(modelPickerCell('#', MODEL_PICKER_NUMBER_WIDTH, heading), modelPickerCell('Model', MODEL_PICKER_MODEL_WIDTH, heading), span(MODEL_PICKER_SEPARATOR, { color: 'gray' }), modelPickerCell('Cost', MODEL_PICKER_COST_WIDTH, heading), span(MODEL_PICKER_SEPARATOR, { color: 'gray' }), modelPickerCell('Notes', modelPickerNotesWidth(innerWidth), heading));
651
691
  }
652
692
  function buildModelPickerPanel(options, selectedIndex, width) {
653
693
  const panelWidth = Math.max(28, Math.min(width, MODEL_PICKER_PANEL_MAX_WIDTH));
654
694
  const innerWidth = Math.max(1, panelWidth - 4);
655
695
  const margin = Array.from({ length: MODEL_PICKER_PANEL_MARGIN_LINES }, () => plainLine(''));
656
696
  const body = [
657
- plainLine('TheGitAI - Model Selection', {
658
- color: MODEL_PICKER_ACCENT_COLOR,
659
- bold: true,
660
- }),
661
- plainLine(''),
662
697
  modelPickerTopBorder(panelWidth),
663
- modelPickerPanelSideLine(plainLine(''), innerWidth),
698
+ modelPickerPanelSideLine(modelPickerHeaderLine(innerWidth), innerWidth),
699
+ modelPickerDivider(panelWidth),
664
700
  ];
665
701
  options.forEach((option, index) => {
666
702
  body.push(...modelPickerItemLines(option, index === selectedIndex, innerWidth));
667
- if (index < options.length - 1) {
668
- body.push(modelPickerSeparatorLine(innerWidth));
669
- }
670
703
  });
671
- body.push(modelPickerPanelSideLine(plainLine(''), innerWidth), modelPickerPanelSideLine(line(span('─'.repeat(innerWidth), { color: MODEL_PICKER_BORDER_COLOR })), innerWidth), modelPickerPanelSideLine(plainLine('↑/↓ choose • Enter select • Esc cancel', { color: 'gray' }), innerWidth), plainLine(`╰${'─'.repeat(panelWidth - 2)}╯`, { color: MODEL_PICKER_BORDER_COLOR }));
704
+ const fullHint = '↑/↓ navigate • Enter select • Esc cancel';
705
+ const compactHint = '↑/↓ • enter • esc';
706
+ const hint = [...fullHint].length <= innerWidth ? fullHint : compactHint;
707
+ body.push(modelPickerDivider(panelWidth), modelPickerPanelSideLine(plainLine(fitLine(hint, innerWidth), { color: 'gray' }), innerWidth), plainLine(`╰${'─'.repeat(panelWidth - 2)}╯`, { color: MODEL_PICKER_BORDER_COLOR }));
672
708
  return [...margin, ...body, ...margin];
673
709
  }
674
710
  function commandPaletteTopBorder(panelWidth) {
@@ -677,6 +713,9 @@ function commandPaletteTopBorder(panelWidth) {
677
713
  const dashCount = Math.max(0, panelWidth - prefix.length - suffix.length);
678
714
  return line(span('╭─ ', { color: MODEL_PICKER_BORDER_COLOR }), span('Commands', { color: MODEL_PICKER_ACCENT_COLOR, bold: true }), span(` ${'─'.repeat(dashCount)}╮`, { color: MODEL_PICKER_BORDER_COLOR }));
679
715
  }
716
+ function commandPaletteSeparatorLine(innerWidth) {
717
+ return modelPickerPanelSideLine(line(span('┈'.repeat(Math.max(1, innerWidth)), { color: 'gray', dim: true })), innerWidth);
718
+ }
680
719
  function commandPaletteItemLines(option, selected, innerWidth) {
681
720
  const highlight = { bgColor: MODEL_PICKER_HIGHLIGHT_BG };
682
721
  if (selected) {
@@ -729,7 +768,7 @@ function buildCommandPalettePanel(suggestions, selectedIndex, width) {
729
768
  suggestions.forEach((suggestion, index) => {
730
769
  body.push(...commandPaletteItemLines(suggestion, index === selectedIndex, innerWidth));
731
770
  if (index < suggestions.length - 1) {
732
- body.push(modelPickerSeparatorLine(innerWidth));
771
+ body.push(commandPaletteSeparatorLine(innerWidth));
733
772
  }
734
773
  });
735
774
  body.push(modelPickerPanelSideLine(plainLine(''), innerWidth), modelPickerPanelSideLine(line(span('─'.repeat(innerWidth), { color: MODEL_PICKER_BORDER_COLOR })), innerWidth), modelPickerPanelSideLine(plainLine('↑/↓ choose • Tab or Enter accept • Esc cancel', { color: 'gray' }), innerWidth), plainLine(`╰${'─'.repeat(panelWidth - 2)}╯`, { color: MODEL_PICKER_BORDER_COLOR }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thegitai/cli",
3
- "version": "1.0.0-preview.2",
3
+ "version": "1.0.0-preview.3",
4
4
  "description": "TheGitAI CLI client (source-visible, proprietary)",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://thegit.ai",
@@ -25,10 +25,10 @@
25
25
  "@lydell/node-pty-linux-x64": "1.1.0",
26
26
  "@lydell/node-pty-win32-arm64": "1.1.0",
27
27
  "@lydell/node-pty-win32-x64": "1.1.0",
28
- "@thegitai/tui-darwin-arm64": "1.0.0-preview.2",
29
- "@thegitai/tui-darwin-x64": "1.0.0-preview.2",
30
- "@thegitai/tui-linux-x64": "1.0.0-preview.2",
31
- "@thegitai/tui-win32-x64": "1.0.0-preview.2",
28
+ "@thegitai/tui-darwin-arm64": "1.0.0-preview.3",
29
+ "@thegitai/tui-darwin-x64": "1.0.0-preview.3",
30
+ "@thegitai/tui-linux-x64": "1.0.0-preview.3",
31
+ "@thegitai/tui-win32-x64": "1.0.0-preview.3",
32
32
  "@vscode/ripgrep": "1.18.0"
33
33
  },
34
34
  "publishConfig": {