kash-shell 0.3.17__py3-none-any.whl → 0.3.18__py3-none-any.whl

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 (37) hide show
  1. kash/actions/core/minify_html.py +41 -0
  2. kash/commands/base/show_command.py +11 -1
  3. kash/config/colors.py +6 -2
  4. kash/docs/markdown/topics/a1_what_is_kash.md +52 -23
  5. kash/docs/markdown/topics/a2_installation.md +17 -30
  6. kash/docs/markdown/topics/a3_getting_started.md +5 -19
  7. kash/exec/action_exec.py +1 -1
  8. kash/exec/fetch_url_metadata.py +9 -0
  9. kash/exec/precondition_registry.py +3 -3
  10. kash/file_storage/file_store.py +18 -1
  11. kash/llm_utils/llm_features.py +5 -1
  12. kash/llm_utils/llms.py +18 -8
  13. kash/media_base/media_cache.py +48 -24
  14. kash/media_base/media_services.py +63 -14
  15. kash/media_base/services/local_file_media.py +9 -1
  16. kash/model/items_model.py +4 -5
  17. kash/model/media_model.py +9 -1
  18. kash/model/params_model.py +9 -3
  19. kash/utils/common/function_inspect.py +97 -1
  20. kash/utils/common/testing.py +58 -0
  21. kash/utils/common/url_slice.py +329 -0
  22. kash/utils/file_utils/file_formats.py +1 -1
  23. kash/utils/text_handling/markdown_utils.py +424 -16
  24. kash/web_gen/templates/base_styles.css.jinja +137 -15
  25. kash/web_gen/templates/base_webpage.html.jinja +13 -17
  26. kash/web_gen/templates/components/toc_scripts.js.jinja +319 -0
  27. kash/web_gen/templates/components/toc_styles.css.jinja +284 -0
  28. kash/web_gen/templates/components/tooltip_scripts.js.jinja +730 -0
  29. kash/web_gen/templates/components/tooltip_styles.css.jinja +482 -0
  30. kash/web_gen/templates/content_styles.css.jinja +13 -8
  31. kash/web_gen/templates/simple_webpage.html.jinja +15 -481
  32. kash/workspaces/workspaces.py +10 -1
  33. {kash_shell-0.3.17.dist-info → kash_shell-0.3.18.dist-info}/METADATA +75 -72
  34. {kash_shell-0.3.17.dist-info → kash_shell-0.3.18.dist-info}/RECORD +37 -30
  35. {kash_shell-0.3.17.dist-info → kash_shell-0.3.18.dist-info}/WHEEL +0 -0
  36. {kash_shell-0.3.17.dist-info → kash_shell-0.3.18.dist-info}/entry_points.txt +0 -0
  37. {kash_shell-0.3.17.dist-info → kash_shell-0.3.18.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,41 @@
1
+ from kash.exec import kash_action
2
+ from kash.exec.preconditions import has_fullpage_html_body
3
+ from kash.model import Format, Item, Param
4
+ from kash.utils.errors import InvalidInput
5
+ from kash.workspaces.workspaces import current_ws
6
+
7
+
8
+ @kash_action(
9
+ precondition=has_fullpage_html_body,
10
+ params=(
11
+ Param("no_js_min", "Disable JS minification", bool),
12
+ Param("no_css_min", "Disable CSS minification", bool),
13
+ ),
14
+ )
15
+ def minify_html(item: Item) -> Item:
16
+ """
17
+ Minify an HTML item's content using [html-minifier-terser](https://github.com/terser/html-minifier-terser).
18
+
19
+ Also supports Tailwind CSS v4 compilation and inlining, if any Tailwind
20
+ CSS v4 CDN script tags are found.
21
+
22
+ The terser minification seems a bit slower but more robust than
23
+ [minify-html](https://github.com/wilsonzlin/minify-html).
24
+ """
25
+ from minify_tw_html import minify_tw_html
26
+
27
+ if not item.store_path:
28
+ raise InvalidInput(f"Missing store path: {item}")
29
+
30
+ ws = current_ws()
31
+ input_path = ws.base_dir / item.store_path
32
+
33
+ output_item = item.derived_copy(format=Format.html, body=None)
34
+ output_path = ws.target_path_for(output_item)
35
+
36
+ minify_tw_html(input_path, output_path)
37
+
38
+ output_item.body = output_path.read_text()
39
+ output_item.external_path = str(output_path) # Indicate item is already saved.
40
+
41
+ return output_item
@@ -1,6 +1,7 @@
1
1
  from kash.config.logger import get_logger
2
2
  from kash.config.text_styles import STYLE_HINT
3
3
  from kash.exec import assemble_path_args, kash_command
4
+ from kash.exec_model.shell_model import ShellResult
4
5
  from kash.model.paths_model import StorePath
5
6
  from kash.shell.output.shell_output import cprint
6
7
  from kash.shell.utils.native_utils import ViewMode, terminal_show_image, view_file_native
@@ -19,7 +20,8 @@ def show(
19
20
  thumbnail: bool = False,
20
21
  browser: bool = False,
21
22
  plain: bool = False,
22
- ) -> None:
23
+ noselect: bool = False,
24
+ ) -> ShellResult:
23
25
  """
24
26
  Show the contents of a file if one is given, or the first file if multiple files
25
27
  are selected. Will try to use native apps or web browser to display the file if
@@ -33,6 +35,7 @@ def show(
33
35
  :param thumbnail: If there is a thumbnail image, show it too.
34
36
  :param browser: Force display with your default web browser.
35
37
  :param plain: Use plain view in the console (this is `bat`'s `plain` style).
38
+ :param noselect: Disable default behavior where `show` also will `select` the file.
36
39
  """
37
40
  view_mode = (
38
41
  ViewMode.console
@@ -63,9 +66,16 @@ def show(
63
66
  view_file_native(ws.base_dir / input_path, view_mode=view_mode, plain=plain)
64
67
  else:
65
68
  view_file_native(input_path, view_mode=view_mode, plain=plain)
69
+ if not noselect:
70
+ from kash.commands.workspace.selection_commands import select
71
+
72
+ select(input_path)
73
+ return ShellResult(show_selection=True)
66
74
  except (InvalidInput, InvalidState):
67
75
  if path:
68
76
  # If path is absolute or we couldbn't get a selection, just show the file.
69
77
  view_file_native(path, view_mode=view_mode)
70
78
  else:
71
79
  raise InvalidInput("No selection")
80
+
81
+ return ShellResult(show_selection=False)
kash/config/colors.py CHANGED
@@ -139,14 +139,16 @@ web_light_translucent = SimpleNamespace(
139
139
  bg_header=hsl_to_hex("hsla(188, 42%, 70%, 0.2)"),
140
140
  bg_alt=hsl_to_hex("hsla(39, 24%, 90%, 0.3)"),
141
141
  bg_alt_solid=hsl_to_hex("hsla(39, 24%, 97%, 1)"),
142
+ bg_selected=hsl_to_hex("hsla(188, 44%, 94%, 0.95)"),
142
143
  text=hsl_to_hex("hsl(188, 39%, 11%)"),
143
144
  code=hsl_to_hex("hsl(44, 38%, 23%)"),
144
145
  border=hsl_to_hex("hsl(188, 8%, 50%)"),
145
146
  border_hint=hsl_to_hex("hsla(188, 8%, 72%, 0.3)"),
146
147
  border_accent=hsl_to_hex("hsla(305, 18%, 65%, 0.85)"),
147
148
  hover=hsl_to_hex("hsl(188, 12%, 84%)"),
148
- hover_bg=hsl_to_hex("hsla(188, 12%, 94%, 0.8)"),
149
+ hover_bg=hsl_to_hex("hsla(188, 44%, 94%, 1)"),
149
150
  hint=hsl_to_hex("hsl(188, 11%, 65%)"),
151
+ hint_strong=hsl_to_hex("hsl(188, 11%, 46%)"),
150
152
  hint_gentle=hsl_to_hex("hsla(188, 11%, 65%, 0.2)"),
151
153
  tooltip_bg=hsl_to_hex("hsla(188, 6%, 37%, 0.7)"),
152
154
  popover_bg=hsl_to_hex("hsla(188, 6%, 37%, 0.7)"),
@@ -170,14 +172,16 @@ web_dark_translucent = SimpleNamespace(
170
172
  bg_header=hsl_to_hex("hsla(188, 42%, 20%, 0.3)"),
171
173
  bg_alt=hsl_to_hex("hsla(220, 14%, 12%, 0.5)"),
172
174
  bg_alt_solid=hsl_to_hex("hsl(220, 14%, 12%)"),
175
+ bg_selected=hsl_to_hex("hsla(188, 12%, 50%, 0.95)"),
173
176
  text=hsl_to_hex("hsl(188, 10%, 90%)"),
174
177
  code=hsl_to_hex("hsl(44, 38%, 72%)"),
175
178
  border=hsl_to_hex("hsl(188, 8%, 25%)"),
176
179
  border_hint=hsl_to_hex("hsla(188, 8%, 35%, 0.3)"),
177
180
  border_accent=hsl_to_hex("hsla(305, 30%, 55%, 0.85)"),
178
181
  hover=hsl_to_hex("hsl(188, 12%, 35%)"),
179
- hover_bg=hsl_to_hex("hsla(188, 20%, 25%, 0.4)"),
182
+ hover_bg=hsl_to_hex("hsla(188, 12%, 40%, 0.95)"),
180
183
  hint=hsl_to_hex("hsl(188, 11%, 55%)"),
184
+ hint_strong=hsl_to_hex("hsl(188, 11%, 72%)"),
181
185
  hint_gentle=hsl_to_hex("hsla(188, 11%, 55%, 0.2)"),
182
186
  tooltip_bg=hsl_to_hex("hsla(188, 6%, 20%, 0.9)"),
183
187
  popover_bg=hsl_to_hex("hsla(188, 6%, 20%, 0.9)"),
@@ -3,35 +3,64 @@
3
3
  > “*Simple should be simple.
4
4
  > Complex should be possible.*” —Alan Kay
5
5
 
6
- Kash (“Knowledge Agent SHell”) is an **interactive, AI-native command-line** shell for
7
- practical knowledge tasks.
6
+ Kash (“Knowledge Agent SHell”) is an experiment in making software tasks more modular,
7
+ exploratory, and flexible using Python and current AI tools.
8
+
9
+ The philosophy behind kash is similar to Unix shell tools: simple commands that can be
10
+ combined in flexible and powerful ways.
11
+ It operates on "items" such as URLs, files, or Markdown notes within a workspace
12
+ directory.
13
+
14
+ You can use Kash as an **interactive, AI-native command-line** shell for practical
15
+ knowledge tasks. It's also **a Python library** that lets you convert a simple Python
16
+ function into a command and an MCP tool, so it integrates with other tools like
17
+ Anthropic Desktop or Cursor.
18
+
19
+ It's new and still has some rough edges, but it's now working well enough it is feeling
20
+ quite powerful. It now serves as a replacement for my usual shell (previously bash or
21
+ zsh). I use it routinely to remix, combine, and interactively explore and then gradually
22
+ automate complex tasks by composing AI tools, APIs, and libraries.
23
+ And last but not least, the same framework lets me build other tools (like
24
+ [textpress](https://github.com/jlevy/textpress)).
8
25
 
9
- It's also **a Python library** that lets you convert a simple Python function into a
10
- command and an MCP tool, so it integrates with other tools like Anthropic Desktop or
11
- Cursor.
26
+ And of course, kash can read its own functionality and enhance itself by writing new
27
+ actions.
12
28
 
13
- You can think of it a kind of power-tool for technical users who want to use Python and
14
- APIs, a kind of hybrid between an AI assistant, a shell, and a developer tool like
15
- Cursor or Claude Code.
29
+ ### Kash Packages
16
30
 
17
- It's my attempt at finding a way to remix, combine, and interactively explore and then
18
- gradually automate complex tasks by composing AI tools, APIs, and libraries.
31
+ The [kash-shell](https://github.com/jlevy/kash) package is the base package and includes
32
+ the Python framework, a few core utilities, and the Kash command-line shell.
19
33
 
20
- And of course, kash can read its own functionality and enhance itself by writing new
21
- actions.
34
+ Additional actions for handling more complex tasks like converting documents and
35
+ transcribing, researching, or annotating videos, are in the
36
+ [kash-docs](https://github.com/jlevy/kash-docs) and
37
+ [kash-media](https://github.com/jlevy/kash-docs) packages, all available on PyPI and
38
+ quick to install via uv.
22
39
 
23
40
  ### Key Concepts
24
41
 
25
- - **Actions:** The core of Kash are **Kash actions**. By decorating a Python function,
26
- you can turn it into an action, which makes it more flexible and powerful, able to
27
- work with file inputs stored and outputs in a given directory, also called a
28
- **workspace**.
42
+ - **Actions:** The core of Kash are **actions**. By decorating a Python function with
43
+ `@kash_action`, you can turn it into an action, which makes it more flexible and
44
+ powerful. It can then be used like a command line command as well as a Python function
45
+ or an MCP tool.
46
+
47
+ - **Workspaces:** A key element of Kash is that it does most nontrivial work in the
48
+ context of a **workspace**. A workspace is just a directory of files that have a few
49
+ conventions to make it easier to maintain context and perform actions.
50
+ A bit like how Git repos work, it has a `.kash/` directory that holds metadata and
51
+ cached content. The rest can be anything, but is typically directories of resources
52
+ (like .docx or .pdf or links to web pages) or content, typically Markdown files with
53
+ YAML frontmatter. All text files use
54
+ [frontmatter-format](https://github.com/jlevy/frontmatter-format) so have easy-to-read
55
+ YAML metadata that includes not just title or description, but also the names of the
56
+ actions that created it.
29
57
 
30
58
  - **Compositionality:** An action is composable with other actions simply as a Python
31
- function, so complex (like transcribing and annotating a video) actions can be built
32
- from simpler actions (like downloading and caching a YouTube video, identifying the
33
- speakers in a transcript, etc.). The goal is to reduce the "interstitial complexity"
34
- of combining tools, so it's easy for you (or an LLM!) to combine tools in flexible and
59
+ function, so complex operations (for example, transcribing and annotating a video and
60
+ publishing it on a website) actions can be built from simpler actions (say downloading
61
+ and caching a YouTube video, identifying the speakers in a transcript, formatting it
62
+ as pretty HTML, etc.). The goal is to reduce the "interstitial complexity" of
63
+ combining tools, so it's easy for you (or an LLM!) to combine tools in flexible and
35
64
  powerful ways.
36
65
 
37
66
  - **Command-line usage:** In addition to using the function in other libraries and
@@ -40,9 +69,6 @@ actions.
40
69
  video. In kash you have **smart tab completions**, **Python expressions**, and an **LLM
41
70
  assistant** built into the shell.
42
71
 
43
- - **MCP support:** Finally, an action is also an **MCP tool server** so you can use it
44
- in any MCP client, like Anthropic Desktop or Cursor.
45
-
46
72
  - **Support for any API:** Kash is tool agnostic and runs locally, on file inputs in
47
73
  simple formats, so you own and manage your data and workspaces however you like.
48
74
  You can use it with any models or APIs you like, and is already set up to use the APIs
@@ -51,6 +77,9 @@ actions.
51
77
  **Perplexity**, **Firecrawl**, **Exa**, and any Python libraries.
52
78
  There is also some experimental support for **LlamaIndex** and **ChromaDB**.
53
79
 
80
+ - **MCP support:** Finally, an action is also an **MCP tool server** so you can use it
81
+ in any MCP client, like Anthropic Desktop or Cursor.
82
+
54
83
  ### What Can Kash Do?
55
84
 
56
85
  You can use kash actions to do deep research, transcribe videos, summarize and organize
@@ -2,9 +2,9 @@
2
2
 
3
3
  ### Running the Kash Shell
4
4
 
5
- Kash offers a shell environment based on [xonsh](https://xon.sh/) augmented with an LLM
6
- assistant and a variety of other enhanced commands and customizations.
7
- If you've used a bash or Python shell before, xonsh is very intuitive.
5
+ Kash offers a shell environment based on [xonsh](https://xon.sh/) augmented with a bunch
6
+ of enhanced commands and customizations.
7
+ If you've used a bash or Python shell before, it should be very intuitive.
8
8
 
9
9
  Within the kash shell, you get a full environment with all actions and commands.
10
10
  You also get intelligent auto-complete, a built-in assistant to help you perform tasks,
@@ -38,30 +38,17 @@ These are for `kash-media` but you can use a `kash-shell` for a more basic setup
38
38
  Kash is easiest to use via [**uv**](https://docs.astral.sh/uv/), the new package
39
39
  manager for Python. `uv` replaces traditional use of `pyenv`, `pipx`, `poetry`, `pip`,
40
40
  etc. Installing `uv` also ensures you get a compatible version of Python.
41
-
42
- If you don't have `uv` installed, a quick way to install it is:
43
-
41
+ See [uv's docs](https://docs.astral.sh/uv/getting-started/installation/) for other
42
+ installation methods and platforms.
43
+ Usually you just want to run:
44
44
  ```shell
45
45
  curl -LsSf https://astral.sh/uv/install.sh | sh
46
46
  ```
47
47
 
48
- For macOS, you prefer [brew](https://brew.sh/) you can install or upgrade uv with:
49
-
50
- ```shell
51
- brew update
52
- brew install uv
53
- ```
54
- See [uv's docs](https://docs.astral.sh/uv/getting-started/installation/) for other
55
- installation methods and platforms.
56
-
57
48
  2. **Install additional command-line tools:**
58
49
 
59
50
  In addition to Python, it's highly recommended to install a few other dependencies to
60
- make more tools and commands work: `ripgrep` (for search), `bat` (for prettier file
61
- display), `eza` (a much improved version of `ls`), `hexyl` (a much improved hex
62
- viewer), `imagemagick` (for image display in modern terminals), `libmagic` (for file
63
- type detection), `ffmpeg` (for audio and video conversions)
64
-
51
+ make more tools and commands work.
65
52
  For macOS, you can again use brew:
66
53
 
67
54
  ```shell
@@ -82,22 +69,22 @@ These are for `kash-media` but you can use a `kash-shell` for a more basic setup
82
69
 
83
70
  For Windows or other platforms, see the uv instructions.
84
71
 
72
+ Kash auto-detects and uses `ripgrep` (for search), `bat` (for prettier file display),
73
+ `eza` (a much improved version of `ls`), `hexyl` (a much improved hex viewer),
74
+ `imagemagick` (for image display in modern terminals), `libmagic` (for file type
75
+ detection), `ffmpeg` (for audio and video conversions)
76
+
85
77
  3. **Install kash or a kash kit:**
86
78
 
79
+ For a more meaningful demo, use an enhanced version of kash that also has various
80
+ media tools (like yt-dlp and Deepgram support):
81
+
87
82
  ```shell
88
- uv tool install kash-media --python=3.13
83
+ uv tool install kash-media --upgrade --python=3.13
89
84
  ```
90
85
 
91
86
  Other versions of Python should work but 3.13 is recommended.
92
- For a setup without the media tools, use `kash-shell` instead.
93
-
94
- If you've installed an older version and want to be sure you have the latest shell,
95
- you may want to add `--upgrade --force` to be sure you get the latest version of the
96
- kit.
97
-
98
- ```shell
99
- uv tool install kash-media --python=3.13 --upgrade --force
100
- ```
87
+ For a setup without the media tools, just install `kash-shell` instead.
101
88
 
102
89
  4. **Set up API keys:**
103
90
 
@@ -15,25 +15,11 @@ Type `help` for the full documentation.
15
15
  The simplest way to illustrate how to use kash is by example.
16
16
  You can go through the commands below a few at a time, trying each one.
17
17
 
18
- This is a "real" example that uses a bunch of libraries.
18
+ This is a "real" example that uses ffmpeg and a few other libraries.
19
19
  So to get it to work you must install not just the main shell but the kash "media kit"
20
20
  with extra dependencies.
21
-
22
- You need the following tools:
23
-
24
- ```shell
25
- # On MacOS:
26
- brew install ripgrep bat eza hexyl imagemagick libmagic ffmpeg
27
- # On Linux:
28
- apt install ripgrep bat eza hexyl imagemagick libmagic ffmpeg
29
- ```
30
-
31
- Then install the `kash-media`, which includes kash-shell and many other libs like yt-dlp
32
- for YouTube handling:
33
-
34
- ```shell
35
- uv tool install kash-media
36
- ```
21
+ This is discussed in [the installation instructions](#installation-steps).
22
+ If you don't have these already installed, you can add these tools:
37
23
 
38
24
  Then run `kash` to start.
39
25
 
@@ -152,7 +138,7 @@ show_webpage
152
138
  show_webpage --help
153
139
 
154
140
  # And you can actually how this works by looking at its source:
155
- source_code show_webpage
141
+ show_webpage --show_source
156
142
 
157
143
  # What if something isn't working right?
158
144
  # Sometimes we may want to browse more detailed system logs:
@@ -171,7 +157,7 @@ transcribe_format https://www.youtube.com/watch?v=_8djNYprRDI
171
157
 
172
158
  # Getting a little fancier, this one adds little paragraph annotations and
173
159
  # a nicer summary at the top:
174
- transcribe_annotate_summarize https://www.youtube.com/watch?v=_8djNYprRDI
160
+ transcribe_annotate https://www.youtube.com/watch?v=_8djNYprRDI
175
161
 
176
162
  show_webpage
177
163
  ```
kash/exec/action_exec.py CHANGED
@@ -102,7 +102,7 @@ def log_action(action: Action, action_input: ActionInput, operation: Operation):
102
102
  """
103
103
  PrintHooks.before_log_action_run()
104
104
  log.message("%s Action: `%s`", EMOJI_START, action.name)
105
- log.message("Running: `%s`", operation.command_line(with_options=True))
105
+ log.info("Running: `%s`", operation.command_line(with_options=True))
106
106
  if len(action.param_value_summary()) > 0:
107
107
  log.message("Parameters:\n%s", action.param_value_summary_str())
108
108
  log.info("Operation is: %s", operation)
@@ -5,6 +5,7 @@ from kash.model.items_model import Item, ItemType
5
5
  from kash.model.paths_model import StorePath
6
6
  from kash.utils.common.format_utils import fmt_loc
7
7
  from kash.utils.common.url import Url, is_url
8
+ from kash.utils.common.url_slice import add_slice_to_url, parse_url_slice
8
9
  from kash.utils.errors import InvalidInput
9
10
 
10
11
  log = get_logger(__name__)
@@ -55,6 +56,14 @@ def fetch_url_item_metadata(item: Item, refetch: bool = False) -> Item:
55
56
  media_metadata = get_media_metadata(url)
56
57
  if media_metadata:
57
58
  fetched_item = Item.from_media_metadata(media_metadata)
59
+ # Preserve and canonicalize any slice suffix on the URL.
60
+ _base_url, slice = parse_url_slice(item.url)
61
+ if slice:
62
+ new_url = add_slice_to_url(media_metadata.url, slice)
63
+ if new_url != item.url:
64
+ log.message("Updated URL from metadata and added slice: %s", new_url)
65
+ fetched_item.url = new_url
66
+
58
67
  fetched_item = item.merged_copy(fetched_item)
59
68
  else:
60
69
  page_data = fetch_extract(url, refetch=refetch)
@@ -39,7 +39,7 @@ def kash_precondition(func: Callable[[Item], bool]) -> Precondition:
39
39
 
40
40
  def get_all_preconditions() -> dict[str, Precondition]:
41
41
  """
42
- Returns a copy of all registered preconditions.
42
+ Returns a copy of all registered preconditions (in alphabetical order).
43
43
  """
44
- # Return a copy for safety.
45
- return dict(_preconditions.copy())
44
+ # Return a copy for safety, sorted by key.
45
+ return dict(sorted(_preconditions.copy().items()))
@@ -83,6 +83,11 @@ class FileStore(Workspace):
83
83
  def base_dir(self) -> Path:
84
84
  return self.base_dir_path
85
85
 
86
+ @property
87
+ @override
88
+ def assets_dir(self) -> Path:
89
+ return self.base_dir / "assets"
90
+
86
91
  @synchronized
87
92
  @log_calls(level="warning", if_slower_than=2.0)
88
93
  def reload(self, auto_init: bool = True):
@@ -340,6 +345,18 @@ class FileStore(Workspace):
340
345
 
341
346
  return StorePath(store_path), old_store_path
342
347
 
348
+ def target_path_for(self, item: Item) -> Path:
349
+ """
350
+ Get an the absolute path for an item. Use this if you need to work around the
351
+ usual save mechanism and write directly to the store yourself, at the location
352
+ the item usually would be saved.
353
+
354
+ If you write to this path, then set the item's `external_path` to indicate it's
355
+ already saved.
356
+ """
357
+ store_path, _old_store_path = self.store_path_for(item)
358
+ return self.base_dir / store_path
359
+
343
360
  def _tmp_path_for(self, item: Item) -> StorePath:
344
361
  """
345
362
  Find a path for an item in the tmp directory.
@@ -468,7 +485,7 @@ class FileStore(Workspace):
468
485
  item.store_path = str(store_path)
469
486
  self._id_index_item(store_path)
470
487
 
471
- log.message("%s Saved item:\n%s", EMOJI_SAVED, fmt_lines([fmt_loc(store_path)]))
488
+ log.message("%s Saved item: %s", EMOJI_SAVED, fmt_loc(store_path))
472
489
  return store_path
473
490
 
474
491
  @log_calls(level="debug")
@@ -56,13 +56,17 @@ FEATURES = {
56
56
  }
57
57
 
58
58
  preferred_llms: list[LLMName] = [
59
+ LLM.o4_mini,
60
+ LLM.o3,
59
61
  LLM.o3_mini,
60
62
  LLM.o1_mini,
61
63
  LLM.o1,
62
64
  LLM.gpt_4o_mini,
63
65
  LLM.gpt_4o,
64
66
  LLM.gpt_4,
67
+ LLM.claude_4_sonnet,
68
+ LLM.claude_4_opus,
65
69
  LLM.claude_3_7_sonnet,
66
- LLM.claude_3_5_sonnet,
67
70
  LLM.claude_3_5_haiku,
71
+ LLM.gemini_2_5_pro_preview_05_06,
68
72
  ]
kash/llm_utils/llms.py CHANGED
@@ -13,32 +13,42 @@ class LLM(LLMName, Enum):
13
13
  """
14
14
 
15
15
  # https://platform.openai.com/docs/models
16
- o1_mini = LLMName("o1-mini")
17
- o1 = LLMName("o1")
16
+ o4_mini = LLMName("o4-mini")
18
17
  o3 = LLMName("o3")
19
18
  o3_mini = LLMName("o3-mini")
20
- o4_mini = LLMName("o4-mini")
19
+ o1 = LLMName("o1")
20
+ o1_mini = LLMName("o1-mini")
21
+ o1_pro = LLMName("o1-pro")
21
22
  o1_preview = LLMName("o1-preview")
22
- gpt_4o_mini = LLMName("gpt-4o-mini")
23
+ gpt_4_1 = LLMName("gpt-4.1")
23
24
  gpt_4o = LLMName("gpt-4o")
25
+ gpt_4o_mini = LLMName("gpt-4o-mini")
24
26
  gpt_4 = LLMName("gpt-4")
25
- gpt_4_1 = LLMName("gpt-4.1")
27
+
26
28
  gpt_4_1_mini = LLMName("gpt-4.1-mini")
27
29
  gpt_4_1_nano = LLMName("gpt-4.1-nano")
28
30
 
29
31
  # https://docs.anthropic.com/en/docs/about-claude/models/all-models
32
+ claude_4_opus = LLMName("claude-opus-4-20250514")
33
+ claude_4_sonnet = LLMName("claude-sonnet-4-20250514")
30
34
  claude_3_7_sonnet = LLMName("claude-3-7-sonnet-latest")
31
- claude_3_5_sonnet = LLMName("claude-3-5-sonnet-latest")
32
35
  claude_3_5_haiku = LLMName("claude-3-5-haiku-latest")
33
36
 
34
37
  # https://ai.google.dev/gemini-api/docs/models
35
- gemini_2_5_pro_exp_03_25 = LLMName("gemini/gemini-2.5-pro-exp-03-25")
38
+ gemini_2_5_pro_preview_06_05 = LLMName("gemini/gemini-2.5-pro-preview-06-05")
39
+ gemini_2_5_pro_preview_05_06 = LLMName("gemini/gemini-2.5-pro-preview-05-06")
40
+ gemini_2_5_pro_preview_03_25 = LLMName("gemini/gemini-2.5-pro-preview-03-25")
41
+ gemini_2_5_flash_preview = LLMName("gemini-2.5-flash-preview-05-20")
36
42
  gemini_2_0_flash = LLMName("gemini/gemini-2_0-flash")
37
43
  gemini_2_0_flash_lite = LLMName("gemini/gemini-2.0-flash-lite")
38
44
  gemini_2_0_pro_exp_02_05 = LLMName("gemini/gemini-2.0-pro-exp-02-05")
39
45
 
40
46
  # https://docs.x.ai/docs/models
41
- xai_grok_2 = LLMName("xai/grok-2-latest")
47
+ xai_grok_3 = LLMName("xai/grok-3")
48
+ xai_grok_3_fast = LLMName("xai/grok-3-fast")
49
+ xai_grok_3_mini = LLMName("xai/grok-3-mini")
50
+ xai_grok_3_mini_fast = LLMName("xai/grok-3-mini-fast")
51
+ xai_grok_2 = LLMName("xai/grok-2")
42
52
 
43
53
  # https://api-docs.deepseek.com/quick_start/pricing
44
54
  deepseek_chat = LLMName("deepseek/deepseek-chat")