code-ollama 0.17.0 → 0.18.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.
@@ -1,4 +1,4 @@
1
- import { A as PLAN_GENERATION_INSTRUCTION, C as withSystemMessage, D as ASSISTANT, E as getTheme, F as APPROVE, I as REJECT, L as VERSION, M as LABEL, N as PLAN, O as SYSTEM, P as SAFE, R as LIST, S as resetSystemMessage, T as LIST$1, _ as setClearHandler, a as tick, b as loadConfig, c as appendMessage, d as deleteSessionIfEmpty, f as listSessions, g as reset, h as clear, i as WRITE_TOOLS, j as AUTO, k as USER, l as createSession, m as updateSessionModel, n as READ_TOOLS, o as color, p as loadSession, r as TOOLS, s as write, t as executeTool, u as deleteSession, v as listModels, w as HEADER_PREFIX, x as saveConfig, y as streamChat } from "../cli.js";
1
+ import { A as SYSTEM, B as REJECT, C as resetSystemMessage, D as LIST$1, E as WARNING, F as AUTO, H as LIST, I as LABEL, L as PLAN, M as PLAN_GENERATION_INSTRUCTION, N as BACK, O as getTheme, P as CATALOG, R as SAFE, S as saveConfig, T as HEADER_PREFIX, V as VERSION, _ as deleteModel, a as color, b as streamChat, c as createSession, d as listSessions, f as loadSession, g as setClearHandler, h as reset, i as WRITE_TOOLS, j as USER, k as ASSISTANT, l as deleteSession, m as clear, n as READ_TOOLS, o as write, p as updateSessionModel, r as TOOLS, s as appendMessage, t as executeTool, u as deleteSessionIfEmpty, v as listModels, w as withSystemMessage, x as loadConfig, y as pullModel, z as APPROVE } from "../cli.js";
2
2
  import { readdirSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { join, relative } from "node:path";
@@ -6,7 +6,7 @@ import { exec } from "node:child_process";
6
6
  import { Box, Static, Text, render, useApp, useInput, useStdout } from "ink";
7
7
  import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
8
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
- import { Select, Spinner } from "@inkjs/ui";
9
+ import { ProgressBar, Select, Spinner } from "@inkjs/ui";
10
10
  import { Marked } from "marked";
11
11
  import { markedTerminal } from "marked-terminal";
12
12
  //#region src/components/CodeBlock/CodeBlock.tsx
@@ -630,9 +630,13 @@ function ToolApproval({ toolCall, onDecision, theme = getTheme() }) {
630
630
  onChange: handleChange,
631
631
  onCancel: handleEscape,
632
632
  children: [
633
- /* @__PURE__ */ jsx(Text, {
633
+ /* @__PURE__ */ jsxs(Text, {
634
634
  color: theme.colors.warning,
635
- children: "Tool requires approval ⚠️ "
635
+ children: [
636
+ "Tool requires approval ",
637
+ WARNING,
638
+ " "
639
+ ]
636
640
  }),
637
641
  /* @__PURE__ */ jsxs(Box, {
638
642
  flexDirection: "column",
@@ -817,8 +821,57 @@ function CommandMenu({ input, onSubmit }) {
817
821
  });
818
822
  }
819
823
  //#endregion
824
+ //#region src/components/Suggestions.tsx
825
+ var DEFAULT_MAX_VISIBLE_OPTIONS = 5;
826
+ function Suggestions({ options, isDisabled = false, maxVisibleOptions = DEFAULT_MAX_VISIBLE_OPTIONS, resetKey, onHighlight, onSelect }) {
827
+ const [focusedIndex, setFocusedIndex] = useState(0);
828
+ useEffect(() => {
829
+ setFocusedIndex(0);
830
+ }, [resetKey]);
831
+ useEffect(() => {
832
+ if (!options.length) {
833
+ setFocusedIndex(0);
834
+ onHighlight?.(null);
835
+ return;
836
+ }
837
+ setFocusedIndex((currentIndex) => Math.min(currentIndex, options.length - 1));
838
+ }, [onHighlight, options]);
839
+ useEffect(() => {
840
+ onHighlight?.(options[focusedIndex] ?? null);
841
+ }, [
842
+ focusedIndex,
843
+ onHighlight,
844
+ options
845
+ ]);
846
+ useInput((_, key) => {
847
+ if (isDisabled || !options.length) return;
848
+ if (key.downArrow) {
849
+ setFocusedIndex((currentIndex) => Math.min(currentIndex + 1, options.length - 1));
850
+ return;
851
+ }
852
+ if (key.upArrow) {
853
+ setFocusedIndex((currentIndex) => Math.max(currentIndex - 1, 0));
854
+ return;
855
+ }
856
+ if (key.tab || key.return) onSelect(options[focusedIndex]);
857
+ });
858
+ if (!options.length) return null;
859
+ const visibleStart = Math.min(Math.max(0, focusedIndex - maxVisibleOptions + 1), Math.max(0, options.length - maxVisibleOptions));
860
+ return /* @__PURE__ */ jsx(Box, {
861
+ flexDirection: "column",
862
+ children: options.slice(visibleStart, visibleStart + maxVisibleOptions).map((option, index) => {
863
+ return /* @__PURE__ */ jsx(Box, {
864
+ marginLeft: 2,
865
+ children: /* @__PURE__ */ jsx(Text, {
866
+ color: visibleStart + index === focusedIndex ? "cyan" : void 0,
867
+ children: option.label
868
+ })
869
+ }, option.label);
870
+ })
871
+ });
872
+ }
873
+ //#endregion
820
874
  //#region src/components/Chat/FileSuggestions.tsx
821
- var MAX_VISIBLE_OPTIONS = 5;
822
875
  var MENTION_PATTERN = /(^|.)@(\S+)/;
823
876
  var RIPGREP_MAX_BUFFER = 10 * 1024 * 1024;
824
877
  function normalizePath(filePath) {
@@ -897,7 +950,6 @@ async function listProjectFiles(rootDir) {
897
950
  }
898
951
  function FileSuggestions({ input, isDisabled = false, onChange, onSelect }) {
899
952
  const [filePaths, setFilePaths] = useState([]);
900
- const [focusedIndex, setFocusedIndex] = useState(0);
901
953
  useEffect(() => {
902
954
  async function loadProjectFiles() {
903
955
  setFilePaths(await listProjectFiles(process.cwd()));
@@ -910,55 +962,29 @@ function FileSuggestions({ input, isDisabled = false, onChange, onSelect }) {
910
962
  const normalizedQuery = mentionMatch.query.toLowerCase();
911
963
  return filePaths.filter((filePath) => filePath.toLowerCase().includes(normalizedQuery));
912
964
  }, [filePaths, mentionMatch]);
913
- useEffect(() => {
914
- setFocusedIndex(0);
915
- }, [input]);
916
- useEffect(() => {
917
- if (!options.length) {
918
- setFocusedIndex(0);
919
- return;
920
- }
921
- setFocusedIndex((currentIndex) => Math.min(currentIndex, options.length - 1));
922
- }, [options]);
923
965
  useEffect(() => {
924
966
  if (!onChange) return;
925
- if (!mentionMatch || !options.length) {
926
- onChange(null);
927
- return;
928
- }
929
- onChange(buildNextInput(input, options[focusedIndex]).value);
967
+ if (!mentionMatch || !options.length) onChange(null);
930
968
  }, [
931
- focusedIndex,
932
- input,
933
969
  mentionMatch,
934
970
  onChange,
935
971
  options
936
972
  ]);
937
- useInput((_, key) => {
938
- if (isDisabled || !options.length) return;
939
- if (key.downArrow) {
940
- setFocusedIndex((currentIndex) => Math.min(currentIndex + 1, options.length - 1));
941
- return;
942
- }
943
- if (key.upArrow) {
944
- setFocusedIndex((currentIndex) => Math.max(currentIndex - 1, 0));
945
- return;
946
- }
947
- if (key.tab || key.return) onSelect(buildNextInput(input, options[focusedIndex]));
948
- });
949
973
  if (!mentionMatch || !options.length) return null;
950
- const visibleStart = Math.min(Math.max(0, focusedIndex - MAX_VISIBLE_OPTIONS + 1), Math.max(0, options.length - MAX_VISIBLE_OPTIONS));
951
- return /* @__PURE__ */ jsx(Box, {
952
- flexDirection: "column",
953
- children: options.slice(visibleStart, visibleStart + MAX_VISIBLE_OPTIONS).map((option, index) => {
954
- return /* @__PURE__ */ jsx(Box, {
955
- marginLeft: 2,
956
- children: /* @__PURE__ */ jsx(Text, {
957
- color: visibleStart + index === focusedIndex ? "cyan" : void 0,
958
- children: option
959
- })
960
- }, option);
961
- })
974
+ return /* @__PURE__ */ jsx(Suggestions, {
975
+ isDisabled,
976
+ options: options.map((option) => ({
977
+ label: option,
978
+ value: option
979
+ })),
980
+ resetKey: input,
981
+ onHighlight: (option) => {
982
+ // v8 ignore next
983
+ onChange?.(option ? buildNextInput(input, option.value).value : null);
984
+ },
985
+ onSelect: (option) => {
986
+ onSelect(buildNextInput(input, option.value));
987
+ }
962
988
  });
963
989
  }
964
990
  //#endregion
@@ -1485,7 +1511,7 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
1485
1511
  marginBottom: 1,
1486
1512
  children: /* @__PURE__ */ jsx(Text, {
1487
1513
  color: theme.colors.error,
1488
- children: interruptReason === INTERRUPT_REASON.REJECTED ? "❗ Tool call rejected." : "❗ Execution interrupted."
1514
+ children: interruptReason === INTERRUPT_REASON.REJECTED ? `❗ Tool call rejected.` : `❗ Execution interrupted.`
1489
1515
  })
1490
1516
  }),
1491
1517
  !pendingPlan && !pendingToolCall && /* @__PURE__ */ jsx(Box, {
@@ -1588,7 +1614,7 @@ function Header({ model, onLoad, theme = getTheme() }) {
1588
1614
  /* @__PURE__ */ jsxs(Text, {
1589
1615
  color: theme.colors.secondary,
1590
1616
  dimColor: true,
1591
- children: [" ", "to switch"]
1617
+ children: [" ", "to manage"]
1592
1618
  })
1593
1619
  ]
1594
1620
  }),
@@ -1602,48 +1628,608 @@ function Header({ model, onLoad, theme = getTheme() }) {
1602
1628
  });
1603
1629
  }
1604
1630
  //#endregion
1605
- //#region src/components/ModelPicker.tsx
1606
- function ModelPicker({ currentModel, onSelect, onClose, theme = getTheme() }) {
1607
- const [options, setOptions] = useState([]);
1608
- const [error, setError] = useState(null);
1609
- const handleChange = useCallback((model) => {
1610
- onSelect({ model });
1611
- }, [onSelect]);
1612
- useInput(async (_input, key) => {
1613
- if (!error && options.length && key.return) {
1614
- await tick();
1615
- onClose();
1631
+ //#region src/components/ModelManager/ModelSuggestions.tsx
1632
+ function rankCatalogMatch(entry, normalizedInput) {
1633
+ const normalizedValue = entry.value.toLowerCase();
1634
+ const normalizedLabel = entry.label.toLowerCase();
1635
+ // v8 ignore start
1636
+ switch (true) {
1637
+ case normalizedValue.startsWith(normalizedInput): return 0;
1638
+ case normalizedLabel.startsWith(normalizedInput): return 1;
1639
+ case normalizedValue.includes(normalizedInput): return 2;
1640
+ case normalizedLabel.includes(normalizedInput): return 3;
1641
+ default: return Number.MAX_SAFE_INTEGER;
1642
+ }
1643
+ // v8 ignore stop
1644
+ }
1645
+ function ModelSuggestions({ catalog, input, isDisabled = false, onHighlight, onSelect }) {
1646
+ const normalizedInput = input.trim().toLowerCase();
1647
+ return /* @__PURE__ */ jsx(Suggestions, {
1648
+ isDisabled,
1649
+ options: useMemo(() => {
1650
+ if (!normalizedInput) return [];
1651
+ return catalog.filter((entry) => entry.value.toLowerCase().includes(normalizedInput) || entry.label.toLowerCase().includes(normalizedInput)).sort((left, right) => rankCatalogMatch(left, normalizedInput) - rankCatalogMatch(right, normalizedInput) || left.label.localeCompare(right.label)).map((entry) => ({
1652
+ label: entry.value,
1653
+ value: entry.value
1654
+ }));
1655
+ }, [catalog, normalizedInput]),
1656
+ resetKey: input,
1657
+ /* v8 ignore next */
1658
+ onHighlight: (option) => onHighlight?.(option?.value ?? null),
1659
+ onSelect: (option) => {
1660
+ onSelect(option.value);
1661
+ }
1662
+ });
1663
+ }
1664
+ //#endregion
1665
+ //#region src/components/ModelManager/types.ts
1666
+ var View = /* @__PURE__ */ function(View) {
1667
+ View["Menu"] = "menu";
1668
+ View["Switch"] = "switch";
1669
+ View["Download"] = "download";
1670
+ View["CustomDownload"] = "custom-download";
1671
+ View["Downloading"] = "downloading";
1672
+ View["Delete"] = "delete";
1673
+ View["DeleteConfirm"] = "delete-confirm";
1674
+ return View;
1675
+ }({});
1676
+ var MenuAction = /* @__PURE__ */ function(MenuAction) {
1677
+ MenuAction["Switch"] = "switch";
1678
+ MenuAction["Download"] = "download";
1679
+ MenuAction["Delete"] = "delete";
1680
+ MenuAction["Cancel"] = "cancel";
1681
+ return MenuAction;
1682
+ }({});
1683
+ var DownloadAction = /* @__PURE__ */ function(DownloadAction) {
1684
+ DownloadAction["Custom"] = "custom";
1685
+ return DownloadAction;
1686
+ }({});
1687
+ var ConfirmDeleteAction = /* @__PURE__ */ function(ConfirmDeleteAction) {
1688
+ ConfirmDeleteAction["Delete"] = "delete";
1689
+ return ConfirmDeleteAction;
1690
+ }({});
1691
+ //#endregion
1692
+ //#region src/components/ModelManager/utils.ts
1693
+ function buildMenuOptions() {
1694
+ return [
1695
+ {
1696
+ label: "Switch model",
1697
+ value: MenuAction.Switch
1698
+ },
1699
+ {
1700
+ label: "Download model",
1701
+ value: MenuAction.Download
1702
+ },
1703
+ {
1704
+ label: "Delete model",
1705
+ value: MenuAction.Delete
1706
+ },
1707
+ {
1708
+ label: "Cancel",
1709
+ value: MenuAction.Cancel
1616
1710
  }
1711
+ ];
1712
+ }
1713
+ function buildInstalledModelOptions(models, currentModel) {
1714
+ const nextModels = [...models];
1715
+ if (nextModels.includes(currentModel)) {
1716
+ nextModels.splice(nextModels.indexOf(currentModel), 1);
1717
+ nextModels.unshift(currentModel);
1718
+ }
1719
+ return nextModels.map((model) => ({
1720
+ label: model === currentModel ? `${model} (current model)` : model,
1721
+ value: model
1722
+ }));
1723
+ }
1724
+ function buildDownloadOptions(installedModels) {
1725
+ const installedModelSet = new Set(installedModels);
1726
+ const availableCatalog = CATALOG.filter(({ value, alias }) => !installedModelSet.has(value) && !(alias && installedModelSet.has(alias)));
1727
+ return [
1728
+ {
1729
+ label: "Enter custom model...",
1730
+ value: DownloadAction.Custom
1731
+ },
1732
+ ...availableCatalog.map(({ label, value }) => ({
1733
+ label,
1734
+ value
1735
+ })),
1736
+ BACK
1737
+ ];
1738
+ }
1739
+ function getNoticeColor(tone, theme) {
1740
+ switch (tone) {
1741
+ case "error": return theme.colors.error;
1742
+ case "success": return theme.colors.status;
1743
+ default: return theme.colors.secondary;
1744
+ }
1745
+ }
1746
+ function formatBytes(bytes) {
1747
+ if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
1748
+ const units = [
1749
+ "B",
1750
+ "KB",
1751
+ "MB",
1752
+ "GB",
1753
+ "TB"
1754
+ ];
1755
+ let value = bytes;
1756
+ let index = 0;
1757
+ while (value >= 1024 && index < units.length - 1) {
1758
+ value /= 1024;
1759
+ index += 1;
1760
+ }
1761
+ const fractionDigits = value >= 10 || index === 0 ? 0 : 1;
1762
+ return `${value.toFixed(fractionDigits)} ${units[index]}`;
1763
+ }
1764
+ function isAbortError(error) {
1765
+ return error instanceof Error && error.name === "AbortError";
1766
+ }
1767
+ function mergeDownloadProgress(previous, model, status, completed, total) {
1768
+ const nextCompleted = typeof completed === "number" && Number.isFinite(completed) && completed >= 0 ? completed : null;
1769
+ const nextTotal = typeof total === "number" && Number.isFinite(total) && total > 0 ? total : null;
1770
+ if (nextTotal !== null && nextCompleted !== null) return {
1771
+ model,
1772
+ status,
1773
+ completed: nextCompleted,
1774
+ total: nextTotal
1775
+ };
1776
+ const hasPreviousProgress = previous?.model === model && previous.total > 0;
1777
+ return {
1778
+ model,
1779
+ status,
1780
+ completed: hasPreviousProgress ? previous.completed : 0,
1781
+ total: hasPreviousProgress ? previous.total : 0
1782
+ };
1783
+ }
1784
+ //#endregion
1785
+ //#region src/components/ModelManager/ModelCustomDownloadView.tsx
1786
+ function ModelCustomDownloadView({ downloadDraft, notice, theme, onDraftChange, onHighlight, onSelectSuggestion, onSubmit }) {
1787
+ const renderNotice = () => notice ? /* @__PURE__ */ jsx(Text, {
1788
+ color: getNoticeColor(notice.tone, theme),
1789
+ children: notice.text
1790
+ }) : null;
1791
+ return /* @__PURE__ */ jsxs(Box, {
1792
+ flexDirection: "column",
1793
+ children: [
1794
+ /* @__PURE__ */ jsx(Text, { children: "Enter an Ollama model name to download." }),
1795
+ /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, { children: "> " }), /* @__PURE__ */ jsx(TextInput, {
1796
+ value: downloadDraft,
1797
+ placeholder: "name:tag",
1798
+ wrapIndent: 2,
1799
+ onChange: onDraftChange,
1800
+ onSubmit
1801
+ })] }),
1802
+ /* @__PURE__ */ jsx(ModelSuggestions, {
1803
+ catalog: [],
1804
+ input: downloadDraft,
1805
+ onHighlight,
1806
+ onSelect: onSelectSuggestion
1807
+ }),
1808
+ renderNotice(),
1809
+ /* @__PURE__ */ jsx(Text, {
1810
+ color: theme.colors.secondary,
1811
+ dimColor: true,
1812
+ children: "Press Enter to download, Esc or Ctrl+C to go back."
1813
+ })
1814
+ ]
1815
+ });
1816
+ }
1817
+ //#endregion
1818
+ //#region src/components/ModelManager/ModelDeleteConfirmView.tsx
1819
+ function ModelDeleteConfirmView({ deleteCandidate, isDeleting, notice, theme, onCancel, onConfirm }) {
1820
+ if (isDeleting) return /* @__PURE__ */ jsx(Spinner, { label: `Deleting model ${deleteCandidate}...` });
1821
+ const renderNotice = () => notice ? /* @__PURE__ */ jsx(Text, {
1822
+ color: getNoticeColor(notice.tone, theme),
1823
+ children: notice.text
1824
+ }) : null;
1825
+ return /* @__PURE__ */ jsxs(SelectPrompt, {
1826
+ options: [{
1827
+ label: `Yes, delete ${deleteCandidate}`,
1828
+ value: ConfirmDeleteAction.Delete
1829
+ }, {
1830
+ ...BACK,
1831
+ label: "No"
1832
+ }],
1833
+ onCancel,
1834
+ onChange: onConfirm,
1835
+ children: [
1836
+ /* @__PURE__ */ jsxs(Text, { children: [
1837
+ WARNING,
1838
+ " Delete model",
1839
+ " ",
1840
+ /* @__PURE__ */ jsx(Text, {
1841
+ color: theme.colors.model,
1842
+ children: deleteCandidate
1843
+ }),
1844
+ "?"
1845
+ ] }),
1846
+ renderNotice(),
1847
+ /* @__PURE__ */ jsx(SelectPromptHint, { message: "This action cannot be undone" })
1848
+ ]
1849
+ });
1850
+ }
1851
+ //#endregion
1852
+ //#region src/components/ModelManager/ModelDeleteView.tsx
1853
+ function ModelDeleteView({ currentModel, installedModels, isLoading, notice, theme, onCancel, onSelect }) {
1854
+ if (isLoading) return /* @__PURE__ */ jsx(Spinner, { label: "Loading models..." });
1855
+ return /* @__PURE__ */ jsxs(SelectPrompt, {
1856
+ options: [...buildInstalledModelOptions(installedModels.filter((model) => model !== currentModel), currentModel), BACK],
1857
+ onCancel,
1858
+ onChange: (value) => {
1859
+ if (value === BACK.value) onCancel();
1860
+ else onSelect(value);
1861
+ },
1862
+ children: [
1863
+ /* @__PURE__ */ jsxs(Text, { children: [
1864
+ "Delete an installed model (current model",
1865
+ " ",
1866
+ /* @__PURE__ */ jsx(Text, {
1867
+ color: theme.colors.model,
1868
+ children: currentModel
1869
+ }),
1870
+ " cannot be deleted)."
1871
+ ] }),
1872
+ notice && /* @__PURE__ */ jsx(Text, {
1873
+ color: getNoticeColor(notice.tone, theme),
1874
+ children: notice.text
1875
+ }),
1876
+ /* @__PURE__ */ jsx(SelectPromptHint, { message: "Delete models" })
1877
+ ]
1617
1878
  });
1879
+ }
1880
+ //#endregion
1881
+ //#region src/components/ModelManager/ModelDownloadingView.tsx
1882
+ function ModelDownloadingView({ progress, theme, onCancel }) {
1883
+ const percent = progress.total > 0 && Number.isFinite(progress.completed) && Number.isFinite(progress.total) ? Math.round(progress.completed / progress.total * 100) : null;
1884
+ return /* @__PURE__ */ jsxs(Box, {
1885
+ flexDirection: "column",
1886
+ children: [
1887
+ /* @__PURE__ */ jsxs(Text, { children: [
1888
+ "Downloading model:",
1889
+ " ",
1890
+ /* @__PURE__ */ jsx(Text, {
1891
+ color: theme.colors.model,
1892
+ children: progress.model
1893
+ })
1894
+ ] }),
1895
+ /* @__PURE__ */ jsx(Text, { children: progress.status }),
1896
+ percent !== null ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Text, { children: [
1897
+ percent,
1898
+ "% (",
1899
+ formatBytes(progress.completed),
1900
+ " /",
1901
+ " ",
1902
+ formatBytes(progress.total),
1903
+ ")"
1904
+ ] }), /* @__PURE__ */ jsx(ProgressBar, { value: Math.max(0, Math.min(100, percent)) })] }) : /* @__PURE__ */ jsx(Text, {
1905
+ color: theme.colors.secondary,
1906
+ dimColor: true,
1907
+ children: "Progress details unavailable. Waiting for Ollama updates..."
1908
+ }),
1909
+ /* @__PURE__ */ jsx(SelectPrompt, {
1910
+ options: [{
1911
+ label: "Cancel download",
1912
+ value: "cancel-download"
1913
+ }],
1914
+ onCancel,
1915
+ onChange: onCancel,
1916
+ children: /* @__PURE__ */ jsx(SelectPromptHint, { message: "Press Enter, Esc, or Ctrl+C to cancel" })
1917
+ })
1918
+ ]
1919
+ });
1920
+ }
1921
+ //#endregion
1922
+ //#region src/components/ModelManager/ModelDownloadView.tsx
1923
+ function ModelDownloadView({ installedModels, notice, theme, onCancel, onChange }) {
1924
+ return /* @__PURE__ */ jsxs(SelectPrompt, {
1925
+ options: buildDownloadOptions(installedModels),
1926
+ onCancel,
1927
+ onChange: (value) => {
1928
+ switch (value) {
1929
+ case "custom":
1930
+ onChange("custom");
1931
+ break;
1932
+ case "back":
1933
+ onCancel();
1934
+ break;
1935
+ default:
1936
+ onChange(value);
1937
+ break;
1938
+ }
1939
+ },
1940
+ children: [
1941
+ /* @__PURE__ */ jsx(Text, { children: "Choose a model to download or use a custom model name." }),
1942
+ notice && /* @__PURE__ */ jsx(Text, {
1943
+ color: getNoticeColor(notice.tone, theme),
1944
+ children: notice.text
1945
+ }),
1946
+ /* @__PURE__ */ jsx(SelectPromptHint, { message: "Download models" })
1947
+ ]
1948
+ });
1949
+ }
1950
+ //#endregion
1951
+ //#region src/components/ModelManager/ModelSwitchView.tsx
1952
+ function ModelSwitchView({ currentModel, installedModels, isLoading, onCancel, onSelect }) {
1953
+ if (isLoading) return /* @__PURE__ */ jsx(Spinner, { label: "Loading models..." });
1954
+ return /* @__PURE__ */ jsx(SelectPrompt, {
1955
+ options: [...buildInstalledModelOptions(installedModels, currentModel), BACK],
1956
+ onCancel,
1957
+ onChange: (value) => {
1958
+ if (value === BACK.value) onCancel();
1959
+ else onSelect(value);
1960
+ },
1961
+ children: /* @__PURE__ */ jsx(SelectPromptHint, { message: "Switch models" })
1962
+ });
1963
+ }
1964
+ //#endregion
1965
+ //#region src/components/ModelManager/ModelManager.tsx
1966
+ function ModelManager({ currentModel, onSelect, onClose, theme = getTheme() }) {
1967
+ const [view, setView] = useState(View.Menu);
1968
+ const [installedModels, setInstalledModels] = useState([]);
1969
+ const [isLoadingModels, setIsLoadingModels] = useState(true);
1970
+ const [loadError, setLoadError] = useState(null);
1971
+ const [notice, setNotice] = useState(null);
1972
+ const [downloadDraft, setDownloadDraft] = useState("");
1973
+ const [highlightedSuggestion, setHighlightedSuggestion] = useState(null);
1974
+ const [downloadProgress, setDownloadProgress] = useState(null);
1975
+ const [deleteCandidate, setDeleteCandidate] = useState(null);
1976
+ const [isDeleting, setIsDeleting] = useState(false);
1977
+ const isDeletingRef = useRef(false);
1978
+ const pullRef = useRef(null);
1979
+ const loadInstalledModels = useCallback(async () => {
1980
+ setIsLoadingModels(true);
1981
+ setLoadError(null);
1982
+ try {
1983
+ setInstalledModels(await listModels());
1984
+ } catch (error) {
1985
+ setLoadError(error instanceof Error ? error.message : /* v8 ignore next */ String(error));
1986
+ } finally {
1987
+ setIsLoadingModels(false);
1988
+ }
1989
+ }, []);
1618
1990
  useEffect(() => {
1619
- async function load() {
1620
- try {
1621
- const models = await listModels();
1622
- if (models.includes(currentModel)) {
1623
- models.splice(models.indexOf(currentModel), 1);
1624
- models.unshift(currentModel);
1625
- }
1626
- setOptions(models.map((model) => ({
1627
- label: model,
1628
- value: model
1629
- })));
1630
- } catch (error) {
1631
- setError(error instanceof Error ? error.message : String(error));
1991
+ loadInstalledModels();
1992
+ }, [loadInstalledModels]);
1993
+ const resetDownloadState = useCallback(() => {
1994
+ setDownloadDraft("");
1995
+ setHighlightedSuggestion(null);
1996
+ setDownloadProgress(null);
1997
+ pullRef.current = null;
1998
+ }, []);
1999
+ const handleBackToMenu = useCallback(() => {
2000
+ setNotice(null);
2001
+ setDeleteCandidate(null);
2002
+ setIsDeleting(false);
2003
+ isDeletingRef.current = false;
2004
+ resetDownloadState();
2005
+ setView(View.Menu);
2006
+ }, [resetDownloadState]);
2007
+ const cancelActivePull = useCallback(() => {
2008
+ pullRef.current?.abort();
2009
+ }, []);
2010
+ useInput((input, key) => {
2011
+ if (view === View.CustomDownload && (key.escape || key.ctrl && input === "c")) {
2012
+ setNotice(null);
2013
+ setHighlightedSuggestion(null);
2014
+ setView(View.Download);
2015
+ return;
2016
+ }
2017
+ if (view === View.Downloading && (key.escape || key.ctrl && input === "c")) cancelActivePull();
2018
+ });
2019
+ const handleMenuChange = useCallback((value) => {
2020
+ setNotice(null);
2021
+ switch (value) {
2022
+ case "switch":
2023
+ setView(View.Switch);
2024
+ break;
2025
+ case "download":
2026
+ setView(View.Download);
2027
+ break;
2028
+ case "delete":
2029
+ setView(View.Delete);
2030
+ break;
2031
+ default: onClose();
2032
+ }
2033
+ }, [onClose]);
2034
+ const handleSwitchChange = useCallback((model) => {
2035
+ onSelect({ model });
2036
+ }, [onSelect]);
2037
+ const startPull = useCallback(async (model) => {
2038
+ const normalizedModel = model.trim();
2039
+ if (!normalizedModel) {
2040
+ setNotice({
2041
+ tone: "error",
2042
+ text: `❗ Enter a model name to download`
2043
+ });
2044
+ return;
2045
+ }
2046
+ if (installedModels.includes(normalizedModel)) {
2047
+ setNotice({
2048
+ tone: "info",
2049
+ text: `${normalizedModel} is already installed`
2050
+ });
2051
+ return;
2052
+ }
2053
+ setNotice(null);
2054
+ setDownloadProgress({
2055
+ model: normalizedModel,
2056
+ status: "Starting download...",
2057
+ completed: 0,
2058
+ total: 0
2059
+ });
2060
+ setView(View.Downloading);
2061
+ try {
2062
+ const pull = await pullModel(normalizedModel);
2063
+ pullRef.current = pull;
2064
+ for await (const update of pull) setDownloadProgress((previous) => {
2065
+ return mergeDownloadProgress(previous, normalizedModel, update.status, update.completed, update.total);
2066
+ });
2067
+ pullRef.current = null;
2068
+ resetDownloadState();
2069
+ await loadInstalledModels();
2070
+ setNotice({
2071
+ tone: "success",
2072
+ text: `✅ ${normalizedModel} downloaded successfully`
2073
+ });
2074
+ setView(View.Menu);
2075
+ } catch (error) {
2076
+ pullRef.current = null;
2077
+ if (isAbortError(error)) {
2078
+ setNotice({
2079
+ tone: "error",
2080
+ text: `❌ Download canceled for ${normalizedModel}`
2081
+ });
2082
+ setDownloadProgress(null);
2083
+ setView(View.Download);
2084
+ return;
1632
2085
  }
2086
+ setNotice({
2087
+ tone: "error",
2088
+ text: `❗ Error downloading model: ${error instanceof Error ? error.message : /* v8 ignore next */ String(error)}`
2089
+ });
2090
+ setDownloadProgress(null);
2091
+ setView(View.CustomDownload);
1633
2092
  }
1634
- load();
1635
- }, [currentModel]);
1636
- if (error) return /* @__PURE__ */ jsxs(Text, {
1637
- color: theme.colors.error,
1638
- children: ["Error loading models: ", error]
2093
+ }, [
2094
+ installedModels,
2095
+ loadInstalledModels,
2096
+ resetDownloadState
2097
+ ]);
2098
+ const handleDownloadChange = useCallback((value) => {
2099
+ if (value === "custom") {
2100
+ setNotice(null);
2101
+ setView(View.CustomDownload);
2102
+ return;
2103
+ }
2104
+ // v8 ignore next 3
2105
+ if (value === "back") {
2106
+ handleBackToMenu();
2107
+ return;
2108
+ }
2109
+ setDownloadDraft(value);
2110
+ startPull(value);
2111
+ }, [handleBackToMenu, startPull]);
2112
+ const handleCustomDownloadSubmit = useCallback((value) => {
2113
+ const nextValue = highlightedSuggestion ?? value.trim();
2114
+ setDownloadDraft(nextValue);
2115
+ startPull(nextValue);
2116
+ }, [highlightedSuggestion, startPull]);
2117
+ const handleDeleteChange = useCallback((model) => {
2118
+ setNotice(null);
2119
+ setDeleteCandidate(model);
2120
+ setView(View.DeleteConfirm);
2121
+ }, []);
2122
+ const handleDeleteConfirm = useCallback(async (value) => {
2123
+ if (isDeletingRef.current) return;
2124
+ if (value === "back") {
2125
+ setView(View.Delete);
2126
+ return;
2127
+ }
2128
+ // v8 ignore next 3
2129
+ if (!deleteCandidate) {
2130
+ setView(View.Delete);
2131
+ return;
2132
+ }
2133
+ try {
2134
+ isDeletingRef.current = true;
2135
+ setIsDeleting(true);
2136
+ await deleteModel(deleteCandidate);
2137
+ await loadInstalledModels();
2138
+ setNotice({
2139
+ tone: "success",
2140
+ text: `✅ ${deleteCandidate} deleted successfully`
2141
+ });
2142
+ isDeletingRef.current = false;
2143
+ setIsDeleting(false);
2144
+ setDeleteCandidate(null);
2145
+ setView(View.Delete);
2146
+ } catch (error) {
2147
+ isDeletingRef.current = false;
2148
+ setIsDeleting(false);
2149
+ setNotice({
2150
+ tone: "error",
2151
+ text: `❗ Error deleting model: ${error instanceof Error ? error.message : /* v8 ignore next */ String(error)}`
2152
+ });
2153
+ setView(View.Delete);
2154
+ }
2155
+ }, [deleteCandidate, loadInstalledModels]);
2156
+ const renderNotice = () => notice ? /* @__PURE__ */ jsx(Text, {
2157
+ color: notice.tone === "error" ? theme.colors.error : notice.tone === "success" ? theme.colors.status : theme.colors.secondary,
2158
+ children: notice.text
2159
+ }) : null;
2160
+ if (loadError && view !== View.Menu) return /* @__PURE__ */ jsxs(Box, {
2161
+ flexDirection: "column",
2162
+ children: [/* @__PURE__ */ jsxs(Text, {
2163
+ color: theme.colors.error,
2164
+ children: ["Error loading models: ", loadError]
2165
+ }), /* @__PURE__ */ jsx(Text, {
2166
+ color: theme.colors.secondary,
2167
+ dimColor: true,
2168
+ children: "Press Esc to go back."
2169
+ })]
1639
2170
  });
1640
- if (!options.length) return /* @__PURE__ */ jsx(Spinner, { label: "Loading models..." });
1641
- return /* @__PURE__ */ jsx(SelectPrompt, {
1642
- options,
1643
- defaultValue: currentModel,
1644
- onChange: handleChange,
2171
+ if (view === View.Downloading && downloadProgress) return /* @__PURE__ */ jsx(ModelDownloadingView, {
2172
+ progress: downloadProgress,
2173
+ theme,
2174
+ onCancel: cancelActivePull
2175
+ });
2176
+ if (view === View.CustomDownload) return /* @__PURE__ */ jsx(ModelCustomDownloadView, {
2177
+ downloadDraft,
2178
+ notice,
2179
+ theme,
2180
+ onDraftChange: setDownloadDraft,
2181
+ onHighlight: setHighlightedSuggestion,
2182
+ onSelectSuggestion: (value) => {
2183
+ setDownloadDraft(value);
2184
+ setHighlightedSuggestion(value);
2185
+ },
2186
+ onSubmit: handleCustomDownloadSubmit
2187
+ });
2188
+ if (view === View.Switch) return /* @__PURE__ */ jsx(ModelSwitchView, {
2189
+ currentModel,
2190
+ installedModels,
2191
+ isLoading: isLoadingModels,
2192
+ onCancel: handleBackToMenu,
2193
+ onSelect: handleSwitchChange
2194
+ });
2195
+ if (view === View.Download) return /* @__PURE__ */ jsx(ModelDownloadView, {
2196
+ installedModels,
2197
+ notice,
2198
+ theme,
2199
+ onCancel: handleBackToMenu,
2200
+ onChange: handleDownloadChange
2201
+ });
2202
+ if (view === View.Delete) return /* @__PURE__ */ jsx(ModelDeleteView, {
2203
+ currentModel,
2204
+ installedModels,
2205
+ isLoading: isLoadingModels,
2206
+ notice,
2207
+ theme,
2208
+ onCancel: handleBackToMenu,
2209
+ onSelect: handleDeleteChange
2210
+ });
2211
+ if (view === View.DeleteConfirm && deleteCandidate) return /* @__PURE__ */ jsx(ModelDeleteConfirmView, {
2212
+ deleteCandidate,
2213
+ isDeleting,
2214
+ notice,
2215
+ theme,
2216
+ onCancel: () => {
2217
+ setView(View.Delete);
2218
+ },
2219
+ onConfirm: handleDeleteConfirm
2220
+ });
2221
+ return /* @__PURE__ */ jsxs(SelectPrompt, {
2222
+ options: buildMenuOptions(),
1645
2223
  onCancel: onClose,
1646
- children: /* @__PURE__ */ jsx(SelectPromptHint, { message: "Select a model" })
2224
+ onChange: handleMenuChange,
2225
+ children: [
2226
+ /* @__PURE__ */ jsxs(Text, { children: ["Current model: ", /* @__PURE__ */ jsx(Text, {
2227
+ color: theme.colors.model,
2228
+ children: currentModel
2229
+ })] }),
2230
+ renderNotice(),
2231
+ /* @__PURE__ */ jsx(SelectPromptHint, { message: "Manage models" })
2232
+ ]
1647
2233
  });
1648
2234
  }
1649
2235
  //#endregion
@@ -1751,7 +2337,6 @@ function SearchSettings({ currentUrl, onClose, onSave, theme = getTheme() }) {
1751
2337
  //#endregion
1752
2338
  //#region src/components/SessionManager.tsx
1753
2339
  var ACTION = {
1754
- BACK: "back",
1755
2340
  CLOSE: "close",
1756
2341
  DELETE_MENU: "delete-menu",
1757
2342
  DELETE_PREFIX: "delete:",
@@ -1779,16 +2364,10 @@ function SessionManager({ currentSessionId, onClose, onDelete, onNew, onOpen, th
1779
2364
  const options = view === "open" ? [...sessions.filter(({ id }) => id !== currentSessionId).map((session) => ({
1780
2365
  label: formatSessionLabel(session, maxLabelWidth),
1781
2366
  value: `${ACTION.OPEN_PREFIX}${session.id}`
1782
- })), {
1783
- label: "Back",
1784
- value: ACTION.BACK
1785
- }] : view === "delete" ? [...sessions.filter(({ id }) => id !== currentSessionId).map((session) => ({
2367
+ })), BACK] : view === "delete" ? [...sessions.filter(({ id }) => id !== currentSessionId).map((session) => ({
1786
2368
  label: formatSessionLabel(session, maxLabelWidth, "Delete "),
1787
2369
  value: `${ACTION.DELETE_PREFIX}${session.id}`
1788
- })), {
1789
- label: "Back",
1790
- value: ACTION.BACK
1791
- }] : [
2370
+ })), BACK] : [
1792
2371
  {
1793
2372
  label: "New session",
1794
2373
  value: ACTION.NEW
@@ -1820,7 +2399,7 @@ function SessionManager({ currentSessionId, onClose, onDelete, onNew, onOpen, th
1820
2399
  case value === ACTION.OPEN_MENU:
1821
2400
  setView("open");
1822
2401
  break;
1823
- case value === ACTION.BACK:
2402
+ case value === BACK.value:
1824
2403
  setView("main");
1825
2404
  break;
1826
2405
  case value.startsWith(ACTION.DELETE_PREFIX):
@@ -1972,7 +2551,7 @@ function ThemeSettings({ currentTheme, onClose, onPreview, onSave }) {
1972
2551
  //#region src/components/App/constants.ts
1973
2552
  var SCREEN = /* @__PURE__ */ function(SCREEN) {
1974
2553
  SCREEN["CHAT"] = "chat";
1975
- SCREEN["MODEL_PICKER"] = "model-picker";
2554
+ SCREEN["MODEL_MANAGER"] = "model-manager";
1976
2555
  SCREEN["SEARCH_SETTINGS"] = "search-settings";
1977
2556
  SCREEN["SESSION_MANAGER"] = "session-manager";
1978
2557
  SCREEN["THEME_SETTINGS"] = "theme-settings";
@@ -1996,7 +2575,7 @@ function useScreenRouter() {
1996
2575
  setScreen(SCREEN.SESSION_MANAGER);
1997
2576
  break;
1998
2577
  case "/model":
1999
- setScreen(SCREEN.MODEL_PICKER);
2578
+ setScreen(SCREEN.MODEL_MANAGER);
2000
2579
  break;
2001
2580
  case "/search":
2002
2581
  setScreen(SCREEN.SEARCH_SETTINGS);
@@ -2049,8 +2628,6 @@ function useSessionManager({ sessionId, model, commandColor }) {
2049
2628
  }, []);
2050
2629
  return {
2051
2630
  activeSession,
2052
- sessionRef,
2053
- setActiveSession,
2054
2631
  setSession,
2055
2632
  handleCreateSession: useCallback(() => {
2056
2633
  const nextSession = createSession(modelRef.current);
@@ -2089,14 +2666,12 @@ function useSessionManager({ sessionId, model, commandColor }) {
2089
2666
  //#region src/components/App/hooks/useThemeSettings.ts
2090
2667
  function useThemeSettings({ currentTheme, onUpdateConfig, setScreen }) {
2091
2668
  const [previewThemeId, setPreviewThemeId] = useState(null);
2092
- const activeThemeId = previewThemeId ?? currentTheme;
2093
- const activeTheme = getTheme(activeThemeId);
2669
+ const activeTheme = getTheme(previewThemeId ?? currentTheme);
2094
2670
  const handleThemePreview = useCallback((themeId) => {
2095
2671
  setPreviewThemeId(themeId);
2096
2672
  }, []);
2097
2673
  return {
2098
2674
  activeTheme,
2099
- activeThemeId,
2100
2675
  handleThemeClose: useCallback(() => {
2101
2676
  setPreviewThemeId(null);
2102
2677
  setScreen(SCREEN.CHAT);
@@ -2106,7 +2681,6 @@ function useThemeSettings({ currentTheme, onUpdateConfig, setScreen }) {
2106
2681
  setPreviewThemeId(null);
2107
2682
  onUpdateConfig({ theme: themeId });
2108
2683
  }, [onUpdateConfig]),
2109
- previewThemeId,
2110
2684
  setPreviewThemeId
2111
2685
  };
2112
2686
  }
@@ -2181,8 +2755,8 @@ function App({ sessionId }) {
2181
2755
  }, [handleCreateSession, setScreen]);
2182
2756
  let screenContent;
2183
2757
  switch (currentScreen) {
2184
- case SCREEN.MODEL_PICKER:
2185
- screenContent = /* @__PURE__ */ jsx(ModelPicker, {
2758
+ case SCREEN.MODEL_MANAGER:
2759
+ screenContent = /* @__PURE__ */ jsx(ModelManager, {
2186
2760
  currentModel: appConfig.model,
2187
2761
  onSelect: handleUpdateConfig,
2188
2762
  onClose: handleClose,
package/dist/cli.js CHANGED
@@ -19,7 +19,7 @@ var LIST$1 = [
19
19
  },
20
20
  {
21
21
  name: "/model",
22
- description: "switch the model"
22
+ description: "manage Ollama models"
23
23
  },
24
24
  {
25
25
  name: "/theme",
@@ -37,7 +37,7 @@ var LIST$1 = [
37
37
  //#endregion
38
38
  //#region package.json
39
39
  var name = "code-ollama";
40
- var version = "0.17.0";
40
+ var version = "0.18.0";
41
41
  //#endregion
42
42
  //#region src/constants/package.ts
43
43
  var NAME = name;
@@ -60,6 +60,38 @@ var LABEL = {
60
60
  plan: "Plan"
61
61
  };
62
62
  //#endregion
63
+ //#region src/constants/models.ts
64
+ /**
65
+ * @see https://ollama.com/library
66
+ */
67
+ var CATALOG = [
68
+ {
69
+ label: "Gemma 4 (gemma4:latest)",
70
+ value: "gemma4:latest",
71
+ alias: "gemma4:e4b"
72
+ },
73
+ {
74
+ label: "Granite 4 (granite4.1:8b)",
75
+ value: "granite4.1:8b"
76
+ },
77
+ {
78
+ label: "Qwen 2.5 Coder (qwen2.5-coder:latest)",
79
+ value: "qwen2.5-coder:latest",
80
+ alias: "qwen2.5-coder:7b"
81
+ },
82
+ {
83
+ label: "DeepSeek Coder V2 (deepseek-coder-v2:latest)",
84
+ value: "deepseek-coder-v2:latest",
85
+ alias: "deepseek-coder-v2:16b"
86
+ }
87
+ ];
88
+ //#endregion
89
+ //#region src/constants/option.ts
90
+ var BACK = {
91
+ label: "Back",
92
+ value: "back"
93
+ };
94
+ //#endregion
63
95
  //#region src/constants/prompt.ts
64
96
  var BASE_SYSTEM_PROMPT = `You are a coding assistant that helps users write, edit, and understand code. You have access to tools for reading files, writing files, running shell commands, searching code, and searching the web
65
97
 
@@ -259,6 +291,7 @@ var WEB_FETCH = "web_fetch";
259
291
  //#endregion
260
292
  //#region src/constants/ui.ts
261
293
  var HEADER_PREFIX = "🦙 ";
294
+ var WARNING = "⚠️";
262
295
  //#endregion
263
296
  //#region src/utils/agents.ts
264
297
  var AGENTS_FILE = "AGENTS.md";
@@ -358,6 +391,15 @@ async function listModels() {
358
391
  const { models } = await client.list();
359
392
  return models.map(({ name }) => name);
360
393
  }
394
+ async function pullModel(model) {
395
+ return client.pull({
396
+ model,
397
+ stream: true
398
+ });
399
+ }
400
+ function deleteModel(model) {
401
+ return client.delete({ model });
402
+ }
361
403
  //#endregion
362
404
  //#region src/utils/screen.ts
363
405
  var clearHandler = null;
@@ -536,9 +578,6 @@ function writeError(text) {
536
578
  process.stderr.write(text);
537
579
  }
538
580
  //#endregion
539
- //#region src/utils/time.ts
540
- var tick = (ms = 0) => new Promise((resolve) => setTimeout(resolve, ms));
541
- //#endregion
542
581
  //#region src/utils/tools/definitions.ts
543
582
  /**
544
583
  * Helper to define tool parameters
@@ -1085,7 +1124,7 @@ async function main(args = process.argv.slice(2)) {
1085
1124
  else await launchTui();
1086
1125
  }
1087
1126
  async function launchTui(sessionId) {
1088
- const { renderApp } = await import("./assets/tui-CCHcdC7V.js");
1127
+ const { renderApp } = await import("./assets/tui-4cX-bKvd.js");
1089
1128
  reset();
1090
1129
  renderApp(sessionId);
1091
1130
  }
@@ -1101,4 +1140,4 @@ function isEntrypoint(argv1 = process.argv[1]) {
1101
1140
  if (isEntrypoint()) main();
1102
1141
  // v8 ignore stop
1103
1142
  //#endregion
1104
- export { PLAN_GENERATION_INSTRUCTION as A, withSystemMessage as C, ASSISTANT as D, getTheme as E, APPROVE as F, REJECT as I, VERSION as L, LABEL as M, PLAN as N, SYSTEM as O, SAFE as P, LIST$1 as R, resetSystemMessage as S, LIST as T, setClearHandler as _, tick as a, loadConfig as b, appendMessage as c, deleteSessionIfEmpty as d, listSessions as f, reset as g, clear as h, WRITE_TOOLS as i, AUTO as j, USER as k, createSession as l, updateSessionModel as m, main, READ_TOOLS as n, color as o, loadSession as p, TOOLS as r, write as s, executeTool as t, deleteSession as u, listModels as v, HEADER_PREFIX as w, saveConfig as x, streamChat as y };
1143
+ export { SYSTEM as A, REJECT as B, resetSystemMessage as C, LIST as D, WARNING as E, AUTO as F, LIST$1 as H, LABEL as I, PLAN as L, PLAN_GENERATION_INSTRUCTION as M, BACK as N, getTheme as O, CATALOG as P, SAFE as R, saveConfig as S, HEADER_PREFIX as T, VERSION as V, deleteModel as _, color as a, streamChat as b, createSession as c, listSessions as d, loadSession as f, setClearHandler as g, reset as h, WRITE_TOOLS as i, USER as j, ASSISTANT as k, deleteSession as l, clear as m, main, READ_TOOLS as n, write as o, updateSessionModel as p, TOOLS as r, appendMessage as s, executeTool as t, deleteSessionIfEmpty as u, listModels as v, withSystemMessage as w, loadConfig as x, pullModel as y, APPROVE as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-ollama",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Ollama coding agent that runs in your terminal",
5
5
  "author": "Mark <mark@remarkablemark.org> (https://remarkablemark.org)",
6
6
  "type": "module",
@@ -65,7 +65,7 @@
65
65
  "lint-staged": "17.0.5",
66
66
  "prettier": "3.8.3",
67
67
  "publint": "0.3.21",
68
- "tsx": "4.22.0",
68
+ "tsx": "4.22.1",
69
69
  "typescript": "6.0.3",
70
70
  "typescript-eslint": "8.59.3",
71
71
  "vite": "8.0.13",