kash-shell 0.3.8__py3-none-any.whl → 0.3.10__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 (154) hide show
  1. kash/actions/__init__.py +4 -4
  2. kash/actions/core/markdownify.py +5 -2
  3. kash/actions/core/readability.py +5 -2
  4. kash/actions/core/render_as_html.py +18 -0
  5. kash/actions/core/webpage_config.py +12 -4
  6. kash/commands/__init__.py +8 -20
  7. kash/commands/base/basic_file_commands.py +15 -0
  8. kash/commands/base/debug_commands.py +15 -2
  9. kash/commands/base/general_commands.py +27 -18
  10. kash/commands/base/logs_commands.py +1 -4
  11. kash/commands/base/model_commands.py +8 -8
  12. kash/commands/base/search_command.py +3 -2
  13. kash/commands/base/show_command.py +5 -3
  14. kash/commands/extras/parse_uv_lock.py +186 -0
  15. kash/commands/help/doc_commands.py +2 -31
  16. kash/commands/help/welcome.py +33 -0
  17. kash/commands/workspace/selection_commands.py +11 -6
  18. kash/commands/workspace/workspace_commands.py +19 -16
  19. kash/config/colors.py +2 -0
  20. kash/config/env_settings.py +72 -0
  21. kash/config/init.py +2 -2
  22. kash/config/logger.py +61 -59
  23. kash/config/logger_basic.py +12 -5
  24. kash/config/server_config.py +6 -6
  25. kash/config/settings.py +117 -67
  26. kash/config/setup.py +35 -9
  27. kash/config/suppress_warnings.py +30 -12
  28. kash/config/text_styles.py +3 -13
  29. kash/docs/load_api_docs.py +2 -1
  30. kash/docs/markdown/topics/a2_installation.md +7 -3
  31. kash/docs/markdown/topics/a3_getting_started.md +3 -2
  32. kash/docs/markdown/warning.md +3 -8
  33. kash/docs/markdown/welcome.md +4 -0
  34. kash/docs_base/load_recipe_snippets.py +1 -1
  35. kash/docs_base/recipes/{general_system_commands.ksh → general_system_commands.sh} +1 -1
  36. kash/{concepts → embeddings}/cosine.py +2 -1
  37. kash/embeddings/text_similarity.py +57 -0
  38. kash/exec/__init__.py +20 -3
  39. kash/exec/action_decorators.py +18 -4
  40. kash/exec/action_exec.py +41 -23
  41. kash/exec/action_registry.py +13 -48
  42. kash/exec/command_registry.py +2 -1
  43. kash/exec/fetch_url_metadata.py +4 -6
  44. kash/exec/importing.py +56 -0
  45. kash/exec/llm_transforms.py +6 -6
  46. kash/exec/precondition_registry.py +2 -1
  47. kash/exec/preconditions.py +16 -1
  48. kash/exec/shell_callable_action.py +33 -19
  49. kash/file_storage/file_store.py +23 -14
  50. kash/file_storage/item_file_format.py +13 -3
  51. kash/file_storage/metadata_dirs.py +11 -2
  52. kash/help/assistant.py +2 -2
  53. kash/help/assistant_instructions.py +2 -1
  54. kash/help/help_embeddings.py +2 -2
  55. kash/help/help_printing.py +14 -10
  56. kash/help/tldr_help.py +5 -3
  57. kash/llm_utils/clean_headings.py +1 -1
  58. kash/llm_utils/llm_api_keys.py +4 -4
  59. kash/llm_utils/llm_completion.py +2 -2
  60. kash/llm_utils/llm_features.py +68 -0
  61. kash/llm_utils/llm_messages.py +1 -2
  62. kash/llm_utils/llm_names.py +1 -1
  63. kash/llm_utils/llms.py +17 -12
  64. kash/local_server/__init__.py +5 -2
  65. kash/local_server/local_server.py +56 -46
  66. kash/local_server/local_server_commands.py +15 -15
  67. kash/local_server/local_server_routes.py +2 -2
  68. kash/local_server/local_url_formatters.py +1 -1
  69. kash/mcp/__init__.py +5 -2
  70. kash/mcp/mcp_cli.py +54 -17
  71. kash/mcp/mcp_server_commands.py +5 -6
  72. kash/mcp/mcp_server_routes.py +14 -11
  73. kash/mcp/mcp_server_sse.py +61 -34
  74. kash/mcp/mcp_server_stdio.py +0 -8
  75. kash/media_base/audio_processing.py +81 -7
  76. kash/media_base/media_cache.py +18 -18
  77. kash/media_base/media_services.py +1 -1
  78. kash/media_base/media_tools.py +6 -6
  79. kash/media_base/services/local_file_media.py +2 -2
  80. kash/media_base/{speech_transcription.py → transcription_deepgram.py} +25 -109
  81. kash/media_base/transcription_format.py +73 -0
  82. kash/media_base/transcription_whisper.py +38 -0
  83. kash/model/__init__.py +73 -5
  84. kash/model/actions_model.py +38 -4
  85. kash/model/concept_model.py +30 -0
  86. kash/model/items_model.py +56 -13
  87. kash/model/params_model.py +24 -0
  88. kash/shell/completions/completion_scoring.py +37 -5
  89. kash/shell/output/kerm_codes.py +1 -2
  90. kash/shell/output/shell_formatting.py +14 -4
  91. kash/shell/shell_main.py +2 -2
  92. kash/shell/utils/exception_printing.py +6 -0
  93. kash/shell/utils/native_utils.py +26 -20
  94. kash/text_handling/custom_sliding_transforms.py +12 -4
  95. kash/text_handling/doc_normalization.py +6 -2
  96. kash/text_handling/markdown_render.py +117 -0
  97. kash/text_handling/markdown_utils.py +204 -0
  98. kash/utils/common/import_utils.py +12 -3
  99. kash/utils/common/type_utils.py +0 -29
  100. kash/utils/common/url.py +80 -28
  101. kash/utils/errors.py +6 -0
  102. kash/utils/file_utils/{dir_size.py → dir_info.py} +25 -4
  103. kash/utils/file_utils/file_ext.py +2 -3
  104. kash/utils/file_utils/file_formats.py +28 -2
  105. kash/utils/file_utils/file_formats_model.py +50 -19
  106. kash/utils/file_utils/filename_parsing.py +10 -4
  107. kash/web_content/dir_store.py +1 -2
  108. kash/web_content/file_cache_utils.py +37 -10
  109. kash/web_content/file_processing.py +68 -0
  110. kash/web_content/local_file_cache.py +12 -9
  111. kash/web_content/web_extract.py +8 -3
  112. kash/web_content/web_fetch.py +12 -4
  113. kash/web_gen/tabbed_webpage.py +5 -2
  114. kash/web_gen/templates/base_styles.css.jinja +120 -14
  115. kash/web_gen/templates/base_webpage.html.jinja +60 -13
  116. kash/web_gen/templates/content_styles.css.jinja +4 -2
  117. kash/web_gen/templates/item_view.html.jinja +2 -2
  118. kash/web_gen/templates/tabbed_webpage.html.jinja +1 -2
  119. kash/workspaces/__init__.py +15 -2
  120. kash/workspaces/selections.py +18 -3
  121. kash/workspaces/source_items.py +4 -2
  122. kash/workspaces/workspace_output.py +11 -4
  123. kash/workspaces/workspaces.py +5 -11
  124. kash/xonsh_custom/command_nl_utils.py +40 -19
  125. kash/xonsh_custom/custom_shell.py +44 -12
  126. kash/xonsh_custom/customize_prompt.py +39 -21
  127. kash/xonsh_custom/load_into_xonsh.py +26 -27
  128. kash/xonsh_custom/shell_load_commands.py +2 -2
  129. kash/xonsh_custom/xonsh_completers.py +2 -249
  130. kash/xonsh_custom/xonsh_keybindings.py +282 -0
  131. kash/xonsh_custom/xonsh_modern_tools.py +3 -3
  132. kash/xontrib/kash_extension.py +5 -6
  133. {kash_shell-0.3.8.dist-info → kash_shell-0.3.10.dist-info}/METADATA +26 -12
  134. {kash_shell-0.3.8.dist-info → kash_shell-0.3.10.dist-info}/RECORD +140 -140
  135. {kash_shell-0.3.8.dist-info → kash_shell-0.3.10.dist-info}/entry_points.txt +1 -1
  136. kash/concepts/concept_formats.py +0 -23
  137. kash/concepts/text_similarity.py +0 -112
  138. kash/shell/clideps/api_keys.py +0 -99
  139. kash/shell/clideps/dotenv_setup.py +0 -114
  140. kash/shell/clideps/dotenv_utils.py +0 -89
  141. kash/shell/clideps/pkg_deps.py +0 -232
  142. kash/shell/clideps/platforms.py +0 -11
  143. kash/shell/clideps/terminal_features.py +0 -56
  144. kash/shell/utils/osc_utils.py +0 -95
  145. kash/shell/utils/terminal_images.py +0 -133
  146. kash/text_handling/markdown_util.py +0 -167
  147. kash/utils/common/atomic_var.py +0 -158
  148. kash/utils/common/string_replace.py +0 -93
  149. kash/utils/common/string_template.py +0 -101
  150. /kash/docs_base/recipes/{python_dev_commands.ksh → python_dev_commands.sh} +0 -0
  151. /kash/docs_base/recipes/{tldr_standard_commands.ksh → tldr_standard_commands.sh} +0 -0
  152. /kash/{concepts → embeddings}/embeddings.py +0 -0
  153. {kash_shell-0.3.8.dist-info → kash_shell-0.3.10.dist-info}/WHEEL +0 -0
  154. {kash_shell-0.3.8.dist-info → kash_shell-0.3.10.dist-info}/licenses/LICENSE +0 -0
kash/config/settings.py CHANGED
@@ -3,17 +3,18 @@ from enum import Enum
3
3
  from functools import cache
4
4
  from logging import DEBUG, ERROR, INFO, WARNING
5
5
  from pathlib import Path
6
- from typing import overload
7
6
 
8
7
  from pydantic.dataclasses import dataclass
8
+ from strif import AtomicVar
9
9
 
10
- from kash.utils.common.atomic_var import AtomicVar
10
+ from kash.config.env_settings import KashEnv
11
11
 
12
12
  APP_NAME = "kash"
13
13
 
14
14
  DOT_DIR = ".kash"
15
15
 
16
- GLOBAL_WS_NAME = "global"
16
+ GLOBAL_WS_NAME = "workspace"
17
+ """Name of the global workspace."""
17
18
 
18
19
  RECOMMENDED_API_KEYS = [
19
20
  "OPENAI_API_KEY",
@@ -23,59 +24,28 @@ RECOMMENDED_API_KEYS = [
23
24
  ]
24
25
 
25
26
 
26
- @overload
27
- def path_from_env(env_name: str, default: None) -> None: ...
28
-
29
-
30
- @overload
31
- def path_from_env(env_name: str, default: Path) -> Path: ...
32
-
33
-
34
- def path_from_env(env_name: str, default: Path | None) -> Path | None:
35
- value = os.environ.get(env_name)
36
- if value:
37
- return Path(value).expanduser().resolve()
38
- else:
39
- return default.expanduser().resolve() if default else None
40
-
41
-
42
- def get_ws_root_dir() -> Path:
43
- """Default root directory for kash workspaces."""
44
- return path_from_env("KASH_WS_ROOT", Path("~/Kash").expanduser().resolve())
45
-
46
-
47
- def get_global_ws_dir() -> Path:
48
- """Default global workspace directory."""
49
- kash_ws_dir = path_from_env("KASH_GLOBAL_WS", None)
50
- if kash_ws_dir:
51
- return kash_ws_dir
52
- else:
53
- return get_ws_root_dir() / GLOBAL_WS_NAME
54
-
55
-
56
- def get_system_logs_dir() -> Path:
57
- """Default global and system logs directory (for server logs, etc)."""
58
- return path_from_env("KASH_SYSTEM_LOGS_DIR", get_ws_root_dir() / "logs")
59
-
60
-
61
- def get_system_cache_dir() -> Path:
62
- """Default global and system cache directory (for global media, content, etc)."""
63
- return path_from_env("KASH_SYSTEM_CACHE_DIR", get_ws_root_dir() / "cache")
64
-
65
-
66
- def get_mcp_ws_dir() -> Path | None:
27
+ def get_all_common_api_env_vars() -> list[str]:
67
28
  """
68
- Get the directory for the MCP workspace, if set.
29
+ Get all the common environment variables that are recommended to be set.
69
30
  """
70
- mcp_dir = os.environ.get("KASH_MCP_WS")
71
- if mcp_dir:
72
- return Path(mcp_dir).expanduser().resolve()
73
- else:
74
- return None
75
-
76
-
77
- def get_rcfile_path() -> Path:
78
- return Path("~/.kashrc").expanduser().resolve()
31
+ from clideps.env_vars.env_names import get_all_common_env_names
32
+
33
+ return list(set(get_all_common_env_names() + RECOMMENDED_API_KEYS))
34
+
35
+
36
+ RECOMMENDED_PKGS = [
37
+ "less",
38
+ "eza",
39
+ "ripgrep",
40
+ "bat",
41
+ "zoxide",
42
+ "dust",
43
+ "duf",
44
+ "pygmentize",
45
+ "hexyl",
46
+ "ffmpeg",
47
+ "imagemagick",
48
+ ]
79
49
 
80
50
 
81
51
  MEDIA_CACHE_NAME = "media"
@@ -91,7 +61,7 @@ LOCAL_SERVER_PORT_START = 4470
91
61
  LOCAL_SERVER_PORTS_MAX = 30
92
62
 
93
63
 
94
- SERVER_LOG_FILE = str(get_system_logs_dir() / "{name}_{port}.log")
64
+ LOCAL_SERVER_LOG_NAME = "local_server"
95
65
 
96
66
 
97
67
  class LogLevel(Enum):
@@ -117,7 +87,7 @@ class LogLevel(Enum):
117
87
  return self.name
118
88
 
119
89
 
120
- DEFAULT_LOG_LEVEL = LogLevel.parse(os.environ.get("KASH_LOG_LEVEL", "warning"))
90
+ DEFAULT_LOG_LEVEL = LogLevel.parse(KashEnv.KASH_LOG_LEVEL.read_str("warning"))
121
91
 
122
92
 
123
93
  def resolve_and_create_dirs(path: Path | str, is_dir: bool = False) -> Path:
@@ -149,11 +119,15 @@ def find_in_cwd_or_parents(filename: Path | str) -> Path | None:
149
119
  return None
150
120
 
151
121
 
122
+ def _get_rcfile_path() -> Path:
123
+ return _get_system_config_dir() / "kashrc"
124
+
125
+
152
126
  def find_rcfiles() -> list[Path]:
153
127
  """
154
128
  Find active rcfiles. Currently only supports one.
155
129
  """
156
- rcfile_path = get_rcfile_path()
130
+ rcfile_path = _get_rcfile_path()
157
131
  if rcfile_path.exists():
158
132
  return [rcfile_path]
159
133
  else:
@@ -162,9 +136,30 @@ def find_rcfiles() -> list[Path]:
162
136
 
163
137
  @dataclass
164
138
  class Settings:
139
+ ws_root_dir: Path
140
+ """A default root directory for kash workspaces (typically `~/Kash`)."""
141
+
142
+ global_ws_dir: Path
143
+ """The directory for the default global workspace."""
144
+
145
+ system_config_dir: Path
146
+ """The directory for system-wide configuration files."""
147
+
165
148
  media_cache_dir: Path
166
149
  """The workspace media cache directory, for caching audio, video, and transcripts."""
167
150
 
151
+ system_logs_dir: Path
152
+ """Default global and system logs directory (for server logs, etc)."""
153
+
154
+ system_cache_dir: Path
155
+ """Default global and system cache directory (for global media, content, etc)."""
156
+
157
+ mcp_ws_dir: Path | None
158
+ """The directory for the MCP workspace, if set."""
159
+
160
+ local_server_log_path: Path
161
+ """The path to the local server log."""
162
+
168
163
  content_cache_dir: Path
169
164
  """The workspace content cache directory, for caching web or local files."""
170
165
  # TODO: Separate workspace cached content (e.g. thumbnails) vs global files
@@ -201,18 +196,60 @@ class Settings:
201
196
  """If true, use Nerd Icons in file listings. Requires a compatible font."""
202
197
 
203
198
 
204
- @cache
205
- def server_log_file_path(name: str, port: int | str) -> Path:
206
- # Use a different log file for each port (server instance).
207
- return resolve_and_create_dirs(SERVER_LOG_FILE.format(name=name, port=port))
199
+ ws_root_dir = Path("~/Kash").expanduser()
208
200
 
209
201
 
210
- # Initial default settings.
211
- _settings = AtomicVar(
212
- Settings(
202
+ def _get_ws_root_dir() -> Path:
203
+ """Default root directory for kash workspaces."""
204
+ return KashEnv.KASH_WS_ROOT.read_path(default=ws_root_dir)
205
+
206
+
207
+ def _get_global_ws_dir() -> Path:
208
+ kash_ws_dir = KashEnv.KASH_GLOBAL_WS.read_path()
209
+ if kash_ws_dir:
210
+ return kash_ws_dir
211
+ else:
212
+ return _get_ws_root_dir() / GLOBAL_WS_NAME
213
+
214
+
215
+ def get_system_logs_dir() -> Path:
216
+ return KashEnv.KASH_SYSTEM_LOGS_DIR.read_path(default=_get_ws_root_dir() / "logs")
217
+
218
+
219
+ def _get_system_config_dir() -> Path:
220
+ return Path("~/.config/kash").expanduser().resolve()
221
+
222
+
223
+ def _get_system_cache_dir() -> Path:
224
+ return KashEnv.KASH_SYSTEM_CACHE_DIR.read_path(default=_get_ws_root_dir() / "cache")
225
+
226
+
227
+ def _get_mcp_ws_dir() -> Path | None:
228
+ mcp_dir = KashEnv.KASH_MCP_WS.read_str()
229
+ if mcp_dir:
230
+ return Path(mcp_dir).expanduser().resolve()
231
+ else:
232
+ return None
233
+
234
+
235
+ @cache
236
+ def _get_local_server_log_path() -> Path:
237
+ return resolve_and_create_dirs(get_system_logs_dir() / f"{LOCAL_SERVER_LOG_NAME}.log")
238
+
239
+
240
+ def _read_settings():
241
+ return Settings(
242
+ # Essential system settings for logs, workspaces, and configs:
243
+ ws_root_dir=_get_ws_root_dir(),
244
+ global_ws_dir=_get_global_ws_dir(),
245
+ system_config_dir=_get_system_config_dir(),
246
+ system_logs_dir=get_system_logs_dir(),
247
+ system_cache_dir=_get_system_cache_dir(),
248
+ mcp_ws_dir=_get_mcp_ws_dir(),
249
+ local_server_log_path=_get_local_server_log_path(),
213
250
  # These default to the global but can be overridden by workspace settings.
214
- media_cache_dir=get_system_cache_dir() / MEDIA_CACHE_NAME,
215
- content_cache_dir=get_system_cache_dir() / CONTENT_CACHE_NAME,
251
+ media_cache_dir=_get_system_cache_dir() / MEDIA_CACHE_NAME,
252
+ content_cache_dir=_get_system_cache_dir() / CONTENT_CACHE_NAME,
216
253
  debug_assistant=True,
217
254
  default_editor="nano",
218
255
  file_log_level=LogLevel.info,
@@ -224,7 +261,20 @@ _settings = AtomicVar(
224
261
  use_kerm_codes=False,
225
262
  use_nerd_icons=True,
226
263
  )
227
- )
264
+
265
+
266
+ # Initial default settings.
267
+ _settings = AtomicVar(_read_settings())
268
+
269
+
270
+ def configure_ws_and_settings(root_dir: Path):
271
+ """
272
+ Reset and reload all settings, deriving all paths from the new workspace
273
+ root. Good if embedding kash in another app.
274
+ """
275
+ global ws_root_dir
276
+ ws_root_dir = root_dir
277
+ _settings.set(_read_settings())
228
278
 
229
279
 
230
280
  def atomic_global_settings() -> AtomicVar[Settings]:
kash/config/setup.py CHANGED
@@ -1,25 +1,51 @@
1
1
  from enum import Enum
2
2
  from functools import cache
3
+ from pathlib import Path
3
4
  from typing import Any
4
5
 
6
+ from clideps.env_vars.dotenv_utils import load_dotenv_paths
7
+
8
+ from kash.config.logger import reset_rich_logging
9
+ from kash.config.logger_basic import basic_logging_setup
10
+ from kash.config.settings import LogLevel, configure_ws_and_settings, global_settings
11
+
5
12
 
6
13
  @cache
7
- def setup(rich_logging: bool):
14
+ def kash_setup(
15
+ *,
16
+ rich_logging: bool,
17
+ kash_ws_root: Path | None = None,
18
+ log_path: Path | None = None,
19
+ level: LogLevel = LogLevel.info,
20
+ ):
8
21
  """
9
- One-time setup of essential keys, directories, and configs. Idempotent.
22
+ One-time top-level setup of essential logging, keys, directories, and configs.
23
+ Idempotent.
24
+
25
+ Can call this if embedding kash in another app.
26
+ Can be used to set the global default workspace and logs directory
27
+ and/or the default log file.
28
+ If `rich_logging` is True, then rich logging with warnings only for console use.
29
+ If `rich_logging` is False, then use basic logging to a file and stderr.
10
30
  """
11
- from kash.config.logger import reload_rich_logging_setup
12
- from kash.shell.clideps.dotenv_utils import load_dotenv_paths
13
31
  from kash.utils.common.stack_traces import add_stacktrace_handler
14
32
 
15
- if rich_logging:
16
- reload_rich_logging_setup()
33
+ add_stacktrace_handler()
17
34
 
18
- _lib_setup()
35
+ # Settings may depend on environment variables, so load them first.
36
+ load_dotenv_paths(True, True, global_settings().system_config_dir)
19
37
 
20
- add_stacktrace_handler()
38
+ # Then configure the workspace and settings before finalizing logging.
39
+ if kash_ws_root:
40
+ configure_ws_and_settings(kash_ws_root)
21
41
 
22
- load_dotenv_paths()
42
+ # Now set up logging, as it might depend on workspace root.
43
+ if rich_logging:
44
+ reset_rich_logging(log_path=log_path)
45
+ else:
46
+ basic_logging_setup(log_path=log_path, level=level)
47
+
48
+ _lib_setup()
23
49
 
24
50
 
25
51
  def _lib_setup():
@@ -1,27 +1,45 @@
1
1
  import logging
2
2
  import warnings
3
3
  from logging import LogRecord
4
+ from typing import Any
5
+ from warnings import formatwarning
6
+
7
+ FILTER_PATTERNS = [
8
+ "deprecated",
9
+ "Deprecation",
10
+ "PydanticDeprecatedSince20",
11
+ "pydantic",
12
+ # "pydub",
13
+ ]
14
+ """Warning messages to always suppress in console output."""
15
+
16
+
17
+ def should_suppress(message: Any):
18
+ return any(pattern in str(message) for pattern in FILTER_PATTERNS)
4
19
 
5
20
 
6
21
  def filter_warnings():
22
+ for pattern in FILTER_PATTERNS:
23
+ warnings.filterwarnings("ignore", message=f".*{pattern}.*")
7
24
  warnings.filterwarnings("ignore", category=DeprecationWarning)
8
- warnings.filterwarnings("ignore", message=".*deprecated.*")
9
- warnings.filterwarnings("ignore", message=".*Deprecation.*")
10
- warnings.filterwarnings("ignore", message=".*PydanticDeprecatedSince20.*")
11
- warnings.filterwarnings("ignore", module="pydub")
12
- warnings.filterwarnings("ignore", module="pydantic")
13
25
  warnings.filterwarnings("ignore", category=RuntimeWarning, module="xonsh.tools")
14
26
 
27
+ log = logging.getLogger("warnings")
28
+
29
+ def custom_showwarning(message, category, filename, lineno, _file=None, line=None):
30
+ if not should_suppress(message) and not should_suppress(category):
31
+ log.warning(formatwarning(message, category, filename, lineno, line))
32
+
33
+ # Override system default, which writes to stderr.
34
+ warnings.showwarning = custom_showwarning
35
+
15
36
 
16
37
  filter_warnings()
17
38
 
18
39
 
19
- # Doing it even more brute force since the approach above often doesn't work.
20
- def demote_warnings(record: LogRecord):
40
+ # An even more brute force approach if the approach above doesn't work.
41
+ def demote_warnings(record: LogRecord, level: int = logging.INFO):
21
42
  if record.levelno == logging.WARNING:
22
43
  # Check for any warning patterns that we're filtering in filter_warnings
23
- if any(
24
- pattern in record.msg
25
- for pattern in ["deprecated", "Deprecation", "PydanticDeprecatedSince20"]
26
- ) or any(module in str(record.pathname) for module in ["pydub", "pydantic", "xonsh.tools"]):
27
- record.levelno = logging.INFO
44
+ if should_suppress(record.msg) or should_suppress(record.module):
45
+ record.levelno = level
@@ -46,7 +46,9 @@ SPINNER = "dots12"
46
46
 
47
47
  BAT_THEME = "Coldark-Dark"
48
48
 
49
- BAT_STYLE = "header-filename,header-filesize,grid,changes"
49
+ BAT_STYLE = "header-filename,header-filesize,grid,numbers,changes"
50
+
51
+ BAT_STYLE_PLAIN = "plain"
50
52
 
51
53
 
52
54
  ## Colors
@@ -296,18 +298,6 @@ EMOJI_MSG_INDENT = "⋮"
296
298
 
297
299
  EMOJI_BREADCRUMB_SEP = "›"
298
300
 
299
- EMOJI_TRUE = "✔︎"
300
-
301
- EMOJI_FALSE = "✘"
302
-
303
-
304
- def success_emoji(value: bool, success_only: bool = False) -> str:
305
- return EMOJI_TRUE if value else " " if success_only else EMOJI_FALSE
306
-
307
-
308
- def format_success_emoji(value: bool, success_only: bool = False) -> Text:
309
- return Text(success_emoji(value, success_only), style=COLOR_SUCCESS if value else COLOR_FAILURE)
310
-
311
301
 
312
302
  ## Special headings
313
303
 
@@ -1,7 +1,8 @@
1
+ from strif import StringTemplate
2
+
1
3
  from kash.config.logger import get_logger
2
4
  from kash.docs.load_help_topics import load_help_src
3
5
  from kash.docs.load_source_code import load_source_code
4
- from kash.utils.common.string_template import StringTemplate
5
6
 
6
7
  log = get_logger(__name__)
7
8
 
@@ -65,15 +65,19 @@ These are for `kash-media` but you can use a `kash-shell` for a more basic setup
65
65
  For macOS, you can again use brew:
66
66
 
67
67
  ```shell
68
- # Install pyenv, pipx, and other tools:
69
68
  brew update
70
- brew install ripgrep bat eza hexyl imagemagick libmagic ffmpeg
69
+ brew install libmagic ffmpeg ripgrep bat eza hexyl imagemagick zoxide
71
70
  ```
72
71
 
73
72
  For Ubuntu:
74
73
 
75
74
  ```shell
76
- apt install ripgrep bat eza hexyl imagemagick libmagic ffmpeg
75
+ sudo apt-get update
76
+ sudo apt-get install -y libgl1 ffmpeg libmagic-dev
77
+ # For the additional command-line tools, pixi is better on Ubuntu:
78
+ curl -fsSL https://pixi.sh/install.sh | sh
79
+ . ~/.bashrc
80
+ pixi global install ripgrep bat eza hexyl imagemagick zoxide
77
81
  ```
78
82
 
79
83
  For Windows or other platforms, see the uv instructions.
@@ -243,8 +243,9 @@ A few of the most important commands for managing files and work are these:
243
243
  browser to view it.
244
244
 
245
245
  - `workspace` shows or selects or creates a new workspace.
246
- Initially you work in the `global` workspace but for more real work you'll want to
247
- create a workspace, which is a directory to hold the files you are working with.
246
+ Initially you work in the default global workspace (typically at `~/Kash/workspace`)
247
+ but for more real work you'll want to create a workspace, which is a directory to hold
248
+ the files you are working with.
248
249
 
249
250
  - `select` shows or sets selections, which are the set of files the next command will
250
251
  run on, within the current workspace.
@@ -1,8 +1,3 @@
1
- *Thank you for trying kash.
2
- It is new and has rough edges.
3
- I'd love to hear from you with issues, bugs, and ideas.
4
- Discuss at github.com/jlevy/kash and contact info at: github.com/jlevy*
5
-
6
- **Warning:** *This is a shell so actions and commands can be destructive or dangerous,
7
- including deleting files.
8
- Exercise caution and review commands carefully!*
1
+ **Important:** *Kash will not execute commands without confirmation.
2
+ But this is a shell so commands can be destructive or dangerous.
3
+ Review commands carefully!*
@@ -5,5 +5,9 @@ You may simply ask a question and the kash assistant will help you.
5
5
  Press **space** (or type **?**), then write your question or request.
6
6
  Use `logs` for detailed logs.
7
7
 
8
+ *I'd love to hear from you with issues, bugs, and ideas.
9
+ Discuss at github.com/jlevy/kash or contact me github.com/jlevy or x.com/ojoshe (DMs
10
+ open).*
11
+
8
12
  Try: `What is kash?`, `How can I transcribe a YouTube video?`, `getting_started`, `faq`,
9
13
  `self_check`, `self_configure`
@@ -8,7 +8,7 @@ from kash.help.help_types import RecipeScript, RecipeSnippet
8
8
 
9
9
  log = get_logger(__name__)
10
10
 
11
- RECIPE_EXT = ".ksh"
11
+ RECIPE_EXT = ".sh"
12
12
 
13
13
  RECIPES_DIR = Path(__file__).parent / "recipes"
14
14
 
@@ -1,5 +1,5 @@
1
1
 
2
- # Additional snippets not already in tldr_standard_commands.ksh
2
+ # Additional snippets not already in tldr_standard_commands.sh
3
3
 
4
4
  # Show all current processes running `python`
5
5
  procs python
@@ -1,9 +1,10 @@
1
1
  from collections.abc import Sequence
2
+ from typing import Any, TypeAlias
2
3
 
3
4
  import numpy as np
4
5
 
5
6
  # Type aliases for clarity
6
- ArrayLike = Sequence[float] | np.ndarray
7
+ ArrayLike: TypeAlias = Sequence[float] | np.ndarray[Any, Any]
7
8
 
8
9
 
9
10
  def cosine(u: ArrayLike, v: ArrayLike) -> float:
@@ -0,0 +1,57 @@
1
+ from typing import cast
2
+
3
+ import litellm
4
+ from funlog import log_calls
5
+ from litellm import embedding
6
+ from litellm.types.utils import EmbeddingResponse
7
+
8
+ from kash.config.logger import get_logger
9
+ from kash.embeddings.cosine import ArrayLike, cosine
10
+ from kash.embeddings.embeddings import Embeddings
11
+ from kash.llm_utils.llms import DEFAULT_EMBEDDING_MODEL, EmbeddingModel
12
+ from kash.utils.errors import ApiResultError
13
+
14
+ log = get_logger(__name__)
15
+
16
+
17
+ def cosine_relatedness(x: ArrayLike, y: ArrayLike) -> float:
18
+ return 1 - cosine(x, y)
19
+
20
+
21
+ @log_calls(level="info", show_return_value=False)
22
+ def embed_query(model: EmbeddingModel, query: str) -> EmbeddingResponse:
23
+ try:
24
+ response: EmbeddingResponse = cast(
25
+ EmbeddingResponse, embedding(model=model.litellm_name, input=[query])
26
+ )
27
+ except litellm.exceptions.APIError as e:
28
+ log.info("API error embedding query: %s", e)
29
+ raise ApiResultError(str(e))
30
+ if not response.data:
31
+ log.info("API error embedding query, got: %s", response)
32
+ raise ApiResultError("No embedding response data")
33
+ return response
34
+
35
+
36
+ @log_calls(level="info", show_return_value=False)
37
+ def rank_by_relatedness(
38
+ query: str,
39
+ embeddings: Embeddings,
40
+ relatedness_fn=cosine_relatedness,
41
+ model=DEFAULT_EMBEDDING_MODEL,
42
+ top_n: int = -1,
43
+ ) -> list[tuple[str, str, float]]:
44
+ """
45
+ Returns a list of strings and relatednesses, sorted from most related to least.
46
+ """
47
+ response = embed_query(model, query)
48
+
49
+ query_embedding = response.data[0]["embedding"]
50
+
51
+ scored_strings = [
52
+ (key, text, relatedness_fn(query_embedding, emb))
53
+ for key, text, emb in embeddings.as_iterable()
54
+ ]
55
+ scored_strings.sort(key=lambda x: x[2], reverse=True)
56
+
57
+ return scored_strings[:top_n]
kash/exec/__init__.py CHANGED
@@ -1,9 +1,7 @@
1
- # flake8: noqa: F401
2
-
3
1
  from kash.exec.action_decorators import kash_action, kash_action_class
4
2
  from kash.exec.action_exec import SkipItem, prepare_action_input, run_action_with_shell_context
5
- from kash.exec.action_registry import import_action_subdirs
6
3
  from kash.exec.command_registry import kash_command
4
+ from kash.exec.importing import import_and_register
7
5
  from kash.exec.llm_transforms import llm_transform_item, llm_transform_str
8
6
  from kash.exec.precondition_registry import kash_precondition
9
7
  from kash.exec.resolve_args import (
@@ -14,3 +12,22 @@ from kash.exec.resolve_args import (
14
12
  resolve_locator_arg,
15
13
  resolve_path_arg,
16
14
  )
15
+
16
+ __all__ = [
17
+ "kash_action",
18
+ "kash_action_class",
19
+ "SkipItem",
20
+ "prepare_action_input",
21
+ "run_action_with_shell_context",
22
+ "kash_command",
23
+ "import_and_register",
24
+ "llm_transform_item",
25
+ "llm_transform_str",
26
+ "kash_precondition",
27
+ "assemble_path_args",
28
+ "assemble_store_path_args",
29
+ "import_locator_args",
30
+ "resolvable_paths",
31
+ "resolve_locator_arg",
32
+ "resolve_path_arg",
33
+ ]
@@ -210,7 +210,12 @@ def kash_action(
210
210
  title_template: TitleTemplate = TitleTemplate("{title}"),
211
211
  llm_options: LLMOptions = LLMOptions(),
212
212
  override_state: State | None = None,
213
+ # Including these for completeness but usually don't want to set them globally
214
+ # in the decorator:
213
215
  rerun: bool = False,
216
+ refetch: bool = False,
217
+ tmp_output: bool = False,
218
+ no_format: bool = False,
214
219
  ) -> Callable[[AF], AF]:
215
220
  """
216
221
  A function decorator to create and register an action. The annotated function must
@@ -333,8 +338,11 @@ def kash_action(
333
338
  else:
334
339
  kw_args[fp.name] = self.get_param(fp.name)
335
340
 
336
- log.info("Action function param declarations:\n%s", fmt_lines(self.params))
337
- log.info("Action function param values:\n%s", self.param_value_summary_str())
341
+ if self.params:
342
+ log.info("Action function param declarations:\n%s", fmt_lines(self.params))
343
+ log.info("Action function param values:\n%s", self.param_value_summary_str())
344
+ else:
345
+ log.info("Action function has no declared params")
338
346
 
339
347
  log.message(
340
348
  "Action function call:\n%s",
@@ -379,11 +387,17 @@ def kash_action(
379
387
  context = provided_context
380
388
  else:
381
389
  context = ExecContext(
382
- action, current_ws().base_dir, rerun=rerun, override_state=override_state
390
+ action,
391
+ current_ws().base_dir,
392
+ rerun=rerun,
393
+ refetch=refetch,
394
+ override_state=override_state,
395
+ tmp_output=tmp_output,
396
+ no_format=no_format,
383
397
  )
384
398
 
385
399
  # Run the action.
386
- result, _, _ = run_action_with_caching(context, action_input, rerun=rerun)
400
+ result, _, _ = run_action_with_caching(context, action_input)
387
401
 
388
402
  return result
389
403