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.
- kash/actions/__init__.py +4 -4
- kash/actions/core/markdownify.py +5 -2
- kash/actions/core/readability.py +5 -2
- kash/actions/core/render_as_html.py +18 -0
- kash/actions/core/webpage_config.py +12 -4
- kash/commands/__init__.py +8 -20
- kash/commands/base/basic_file_commands.py +15 -0
- kash/commands/base/debug_commands.py +15 -2
- kash/commands/base/general_commands.py +27 -18
- kash/commands/base/logs_commands.py +1 -4
- kash/commands/base/model_commands.py +8 -8
- kash/commands/base/search_command.py +3 -2
- kash/commands/base/show_command.py +5 -3
- kash/commands/extras/parse_uv_lock.py +186 -0
- kash/commands/help/doc_commands.py +2 -31
- kash/commands/help/welcome.py +33 -0
- kash/commands/workspace/selection_commands.py +11 -6
- kash/commands/workspace/workspace_commands.py +19 -16
- kash/config/colors.py +2 -0
- kash/config/env_settings.py +72 -0
- kash/config/init.py +2 -2
- kash/config/logger.py +61 -59
- kash/config/logger_basic.py +12 -5
- kash/config/server_config.py +6 -6
- kash/config/settings.py +117 -67
- kash/config/setup.py +35 -9
- kash/config/suppress_warnings.py +30 -12
- kash/config/text_styles.py +3 -13
- kash/docs/load_api_docs.py +2 -1
- kash/docs/markdown/topics/a2_installation.md +7 -3
- kash/docs/markdown/topics/a3_getting_started.md +3 -2
- kash/docs/markdown/warning.md +3 -8
- kash/docs/markdown/welcome.md +4 -0
- kash/docs_base/load_recipe_snippets.py +1 -1
- kash/docs_base/recipes/{general_system_commands.ksh → general_system_commands.sh} +1 -1
- kash/{concepts → embeddings}/cosine.py +2 -1
- kash/embeddings/text_similarity.py +57 -0
- kash/exec/__init__.py +20 -3
- kash/exec/action_decorators.py +18 -4
- kash/exec/action_exec.py +41 -23
- kash/exec/action_registry.py +13 -48
- kash/exec/command_registry.py +2 -1
- kash/exec/fetch_url_metadata.py +4 -6
- kash/exec/importing.py +56 -0
- kash/exec/llm_transforms.py +6 -6
- kash/exec/precondition_registry.py +2 -1
- kash/exec/preconditions.py +16 -1
- kash/exec/shell_callable_action.py +33 -19
- kash/file_storage/file_store.py +23 -14
- kash/file_storage/item_file_format.py +13 -3
- kash/file_storage/metadata_dirs.py +11 -2
- kash/help/assistant.py +2 -2
- kash/help/assistant_instructions.py +2 -1
- kash/help/help_embeddings.py +2 -2
- kash/help/help_printing.py +14 -10
- kash/help/tldr_help.py +5 -3
- kash/llm_utils/clean_headings.py +1 -1
- kash/llm_utils/llm_api_keys.py +4 -4
- kash/llm_utils/llm_completion.py +2 -2
- kash/llm_utils/llm_features.py +68 -0
- kash/llm_utils/llm_messages.py +1 -2
- kash/llm_utils/llm_names.py +1 -1
- kash/llm_utils/llms.py +17 -12
- kash/local_server/__init__.py +5 -2
- kash/local_server/local_server.py +56 -46
- kash/local_server/local_server_commands.py +15 -15
- kash/local_server/local_server_routes.py +2 -2
- kash/local_server/local_url_formatters.py +1 -1
- kash/mcp/__init__.py +5 -2
- kash/mcp/mcp_cli.py +54 -17
- kash/mcp/mcp_server_commands.py +5 -6
- kash/mcp/mcp_server_routes.py +14 -11
- kash/mcp/mcp_server_sse.py +61 -34
- kash/mcp/mcp_server_stdio.py +0 -8
- kash/media_base/audio_processing.py +81 -7
- kash/media_base/media_cache.py +18 -18
- kash/media_base/media_services.py +1 -1
- kash/media_base/media_tools.py +6 -6
- kash/media_base/services/local_file_media.py +2 -2
- kash/media_base/{speech_transcription.py → transcription_deepgram.py} +25 -109
- kash/media_base/transcription_format.py +73 -0
- kash/media_base/transcription_whisper.py +38 -0
- kash/model/__init__.py +73 -5
- kash/model/actions_model.py +38 -4
- kash/model/concept_model.py +30 -0
- kash/model/items_model.py +56 -13
- kash/model/params_model.py +24 -0
- kash/shell/completions/completion_scoring.py +37 -5
- kash/shell/output/kerm_codes.py +1 -2
- kash/shell/output/shell_formatting.py +14 -4
- kash/shell/shell_main.py +2 -2
- kash/shell/utils/exception_printing.py +6 -0
- kash/shell/utils/native_utils.py +26 -20
- kash/text_handling/custom_sliding_transforms.py +12 -4
- kash/text_handling/doc_normalization.py +6 -2
- kash/text_handling/markdown_render.py +117 -0
- kash/text_handling/markdown_utils.py +204 -0
- kash/utils/common/import_utils.py +12 -3
- kash/utils/common/type_utils.py +0 -29
- kash/utils/common/url.py +80 -28
- kash/utils/errors.py +6 -0
- kash/utils/file_utils/{dir_size.py → dir_info.py} +25 -4
- kash/utils/file_utils/file_ext.py +2 -3
- kash/utils/file_utils/file_formats.py +28 -2
- kash/utils/file_utils/file_formats_model.py +50 -19
- kash/utils/file_utils/filename_parsing.py +10 -4
- kash/web_content/dir_store.py +1 -2
- kash/web_content/file_cache_utils.py +37 -10
- kash/web_content/file_processing.py +68 -0
- kash/web_content/local_file_cache.py +12 -9
- kash/web_content/web_extract.py +8 -3
- kash/web_content/web_fetch.py +12 -4
- kash/web_gen/tabbed_webpage.py +5 -2
- kash/web_gen/templates/base_styles.css.jinja +120 -14
- kash/web_gen/templates/base_webpage.html.jinja +60 -13
- kash/web_gen/templates/content_styles.css.jinja +4 -2
- kash/web_gen/templates/item_view.html.jinja +2 -2
- kash/web_gen/templates/tabbed_webpage.html.jinja +1 -2
- kash/workspaces/__init__.py +15 -2
- kash/workspaces/selections.py +18 -3
- kash/workspaces/source_items.py +4 -2
- kash/workspaces/workspace_output.py +11 -4
- kash/workspaces/workspaces.py +5 -11
- kash/xonsh_custom/command_nl_utils.py +40 -19
- kash/xonsh_custom/custom_shell.py +44 -12
- kash/xonsh_custom/customize_prompt.py +39 -21
- kash/xonsh_custom/load_into_xonsh.py +26 -27
- kash/xonsh_custom/shell_load_commands.py +2 -2
- kash/xonsh_custom/xonsh_completers.py +2 -249
- kash/xonsh_custom/xonsh_keybindings.py +282 -0
- kash/xonsh_custom/xonsh_modern_tools.py +3 -3
- kash/xontrib/kash_extension.py +5 -6
- {kash_shell-0.3.8.dist-info → kash_shell-0.3.10.dist-info}/METADATA +26 -12
- {kash_shell-0.3.8.dist-info → kash_shell-0.3.10.dist-info}/RECORD +140 -140
- {kash_shell-0.3.8.dist-info → kash_shell-0.3.10.dist-info}/entry_points.txt +1 -1
- kash/concepts/concept_formats.py +0 -23
- kash/concepts/text_similarity.py +0 -112
- kash/shell/clideps/api_keys.py +0 -99
- kash/shell/clideps/dotenv_setup.py +0 -114
- kash/shell/clideps/dotenv_utils.py +0 -89
- kash/shell/clideps/pkg_deps.py +0 -232
- kash/shell/clideps/platforms.py +0 -11
- kash/shell/clideps/terminal_features.py +0 -56
- kash/shell/utils/osc_utils.py +0 -95
- kash/shell/utils/terminal_images.py +0 -133
- kash/text_handling/markdown_util.py +0 -167
- kash/utils/common/atomic_var.py +0 -158
- kash/utils/common/string_replace.py +0 -93
- kash/utils/common/string_template.py +0 -101
- /kash/docs_base/recipes/{python_dev_commands.ksh → python_dev_commands.sh} +0 -0
- /kash/docs_base/recipes/{tldr_standard_commands.ksh → tldr_standard_commands.sh} +0 -0
- /kash/{concepts → embeddings}/embeddings.py +0 -0
- {kash_shell-0.3.8.dist-info → kash_shell-0.3.10.dist-info}/WHEEL +0 -0
- {kash_shell-0.3.8.dist-info → kash_shell-0.3.10.dist-info}/licenses/LICENSE +0 -0
kash/actions/__init__.py
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
|
+
from strif import AtomicVar
|
|
5
|
+
|
|
4
6
|
from kash.config.logger import get_logger
|
|
5
7
|
from kash.config.settings import APP_NAME
|
|
6
|
-
from kash.exec import
|
|
7
|
-
from kash.utils.common.atomic_var import AtomicVar
|
|
8
|
+
from kash.exec import import_and_register
|
|
8
9
|
from kash.utils.common.import_utils import import_namespace_modules
|
|
9
10
|
|
|
10
11
|
log = get_logger(__name__)
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
import_action_subdirs(["core", "meta"], __package__, Path(__file__).parent)
|
|
13
|
+
import_and_register(__package__, Path(__file__).parent, ["core", "meta"])
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
@dataclass(frozen=True)
|
kash/actions/core/markdownify.py
CHANGED
|
@@ -2,6 +2,7 @@ from kash.config.logger import get_logger
|
|
|
2
2
|
from kash.exec import kash_action
|
|
3
3
|
from kash.exec.preconditions import has_html_body, is_url_item
|
|
4
4
|
from kash.model import Format, Item, ItemType
|
|
5
|
+
from kash.model.params_model import common_params
|
|
5
6
|
from kash.web_content.file_cache_utils import get_url_html
|
|
6
7
|
from kash.web_content.web_extract_readabilipy import extract_text_readabilipy
|
|
7
8
|
|
|
@@ -10,16 +11,18 @@ log = get_logger(__name__)
|
|
|
10
11
|
|
|
11
12
|
@kash_action(
|
|
12
13
|
precondition=is_url_item | has_html_body,
|
|
14
|
+
params=common_params("refetch"),
|
|
13
15
|
mcp_tool=True,
|
|
14
16
|
)
|
|
15
|
-
def markdownify(item: Item) -> Item:
|
|
17
|
+
def markdownify(item: Item, refetch: bool = False) -> Item:
|
|
16
18
|
"""
|
|
17
19
|
Converts a URL or raw HTML item to Markdown, fetching with the content
|
|
18
20
|
cache if needed. Also uses readability to clean up the HTML.
|
|
19
21
|
"""
|
|
20
22
|
from markdownify import markdownify as markdownify_convert
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
expiration_sec = 0 if refetch else None
|
|
25
|
+
url, html_content = get_url_html(item, expiration_sec=expiration_sec)
|
|
23
26
|
page_data = extract_text_readabilipy(url, html_content)
|
|
24
27
|
markdown_content = markdownify_convert(page_data.clean_html)
|
|
25
28
|
|
kash/actions/core/readability.py
CHANGED
|
@@ -2,6 +2,7 @@ from kash.config.logger import get_logger
|
|
|
2
2
|
from kash.exec import kash_action
|
|
3
3
|
from kash.exec.preconditions import has_html_body, is_url_item
|
|
4
4
|
from kash.model import Format, Item, ItemType
|
|
5
|
+
from kash.model.params_model import common_params
|
|
5
6
|
from kash.web_content.file_cache_utils import get_url_html
|
|
6
7
|
from kash.web_content.web_extract_readabilipy import extract_text_readabilipy
|
|
7
8
|
|
|
@@ -10,14 +11,16 @@ log = get_logger(__name__)
|
|
|
10
11
|
|
|
11
12
|
@kash_action(
|
|
12
13
|
precondition=is_url_item | has_html_body,
|
|
14
|
+
params=common_params("refetch"),
|
|
13
15
|
mcp_tool=True,
|
|
14
16
|
)
|
|
15
|
-
def readability(item: Item) -> Item:
|
|
17
|
+
def readability(item: Item, refetch: bool = False) -> Item:
|
|
16
18
|
"""
|
|
17
19
|
Extracts clean HTML from a raw HTML item.
|
|
18
20
|
See `markdownify` to also convert to Markdown.
|
|
19
21
|
"""
|
|
20
|
-
|
|
22
|
+
expiration_sec = 0 if refetch else None
|
|
23
|
+
url, html_content = get_url_html(item, expiration_sec=expiration_sec)
|
|
21
24
|
page_data = extract_text_readabilipy(url, html_content)
|
|
22
25
|
|
|
23
26
|
output_item = item.derived_copy(
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from kash.actions.core.webpage_config import webpage_config
|
|
2
|
+
from kash.actions.core.webpage_generate import webpage_generate
|
|
3
|
+
from kash.exec import kash_action
|
|
4
|
+
from kash.exec.preconditions import has_text_body, is_html
|
|
5
|
+
from kash.model import ActionInput, ActionResult
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@kash_action(
|
|
9
|
+
precondition=is_html | has_text_body,
|
|
10
|
+
)
|
|
11
|
+
def render_as_html(input: ActionInput) -> ActionResult:
|
|
12
|
+
"""
|
|
13
|
+
Convert text, Markdown, or HTML to pretty, formatted HTML using the kash default
|
|
14
|
+
page template.
|
|
15
|
+
"""
|
|
16
|
+
config_result = webpage_config(input)
|
|
17
|
+
result = webpage_generate(ActionInput(items=config_result.items))
|
|
18
|
+
return result
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
from kash.config.logger import get_logger
|
|
2
2
|
from kash.exec import kash_action
|
|
3
|
-
from kash.model import ActionInput, ActionResult
|
|
3
|
+
from kash.model import ActionInput, ActionResult, Param
|
|
4
4
|
from kash.utils.errors import InvalidInput
|
|
5
5
|
from kash.web_gen import tabbed_webpage
|
|
6
6
|
|
|
7
7
|
log = get_logger(__name__)
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
@kash_action(
|
|
11
|
-
|
|
10
|
+
@kash_action(
|
|
11
|
+
params=(
|
|
12
|
+
Param(
|
|
13
|
+
name="clean_headings",
|
|
14
|
+
type=bool,
|
|
15
|
+
description="Use an LLM to clean up headings.",
|
|
16
|
+
),
|
|
17
|
+
)
|
|
18
|
+
)
|
|
19
|
+
def webpage_config(input: ActionInput, clean_headings: bool = False) -> ActionResult:
|
|
12
20
|
"""
|
|
13
21
|
Set up a web page config with optional tabs for each page of content. Uses first item as the page title.
|
|
14
22
|
"""
|
|
@@ -16,6 +24,6 @@ def webpage_config(input: ActionInput) -> ActionResult:
|
|
|
16
24
|
if not item.body:
|
|
17
25
|
raise InvalidInput(f"Item must have a body: {item}")
|
|
18
26
|
|
|
19
|
-
config_item = tabbed_webpage.webpage_config(input.items)
|
|
27
|
+
config_item = tabbed_webpage.webpage_config(input.items, clean_headings)
|
|
20
28
|
|
|
21
29
|
return ActionResult([config_item])
|
kash/commands/__init__.py
CHANGED
|
@@ -1,21 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
from pathlib import Path
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import kash.commands.base.model_commands # noqa: F401
|
|
11
|
-
import kash.commands.base.reformat_command # noqa: F401
|
|
12
|
-
import kash.commands.base.search_command # noqa: F401
|
|
13
|
-
import kash.commands.base.show_command # noqa: F401
|
|
14
|
-
import kash.commands.extras.utils_commands # noqa: F401
|
|
15
|
-
import kash.commands.help.assistant_commands # noqa: F401 # noqa: F401
|
|
16
|
-
import kash.commands.help.doc_commands # noqa: F401
|
|
17
|
-
import kash.commands.help.help_commands # noqa: F401
|
|
18
|
-
import kash.commands.workspace.selection_commands # noqa: F401
|
|
19
|
-
import kash.commands.workspace.workspace_commands # noqa: F401
|
|
20
|
-
import kash.local_server.local_server_commands # noqa: F401
|
|
21
|
-
import kash.mcp.mcp_server_commands # noqa: F401
|
|
3
|
+
from kash.exec.importing import import_and_register
|
|
4
|
+
|
|
5
|
+
import_and_register(
|
|
6
|
+
__package__,
|
|
7
|
+
Path(__file__).parent,
|
|
8
|
+
["base", "extras", "help", "workspace"],
|
|
9
|
+
)
|
|
@@ -7,6 +7,7 @@ from strif import copyfile_atomic
|
|
|
7
7
|
from kash.config.logger import get_logger
|
|
8
8
|
from kash.config.text_styles import STYLE_EMPH
|
|
9
9
|
from kash.exec import assemble_path_args, kash_command, resolve_path_arg
|
|
10
|
+
from kash.model.paths_model import StorePath
|
|
10
11
|
from kash.shell.output.shell_output import (
|
|
11
12
|
PadStyle,
|
|
12
13
|
PrintHooks,
|
|
@@ -21,6 +22,7 @@ from kash.utils.common.format_utils import fmt_loc
|
|
|
21
22
|
from kash.utils.errors import InvalidInput
|
|
22
23
|
from kash.utils.file_utils.file_formats_model import detect_file_format
|
|
23
24
|
from kash.workspaces.workspace_output import print_file_info
|
|
25
|
+
from kash.workspaces.workspaces import current_ws
|
|
24
26
|
|
|
25
27
|
log = get_logger(__name__)
|
|
26
28
|
|
|
@@ -149,7 +151,20 @@ def trash(*paths: str) -> None:
|
|
|
149
151
|
"""
|
|
150
152
|
|
|
151
153
|
resolved_paths = assemble_path_args(*paths)
|
|
154
|
+
|
|
155
|
+
ws = current_ws()
|
|
156
|
+
affected_store_paths = [
|
|
157
|
+
p for p in resolved_paths if isinstance(p, StorePath) and (ws.base_dir / p).exists()
|
|
158
|
+
]
|
|
159
|
+
|
|
152
160
|
native_trash(*resolved_paths)
|
|
161
|
+
|
|
162
|
+
if affected_store_paths:
|
|
163
|
+
log.info(
|
|
164
|
+
"Refreshing current selection due to deleted store paths: %s", affected_store_paths
|
|
165
|
+
)
|
|
166
|
+
ws.selections.refresh_current(ws.base_dir)
|
|
167
|
+
|
|
153
168
|
print_status(f"Deleted (check trash or recycling bin to recover):\n{fmt_lines(resolved_paths)}")
|
|
154
169
|
|
|
155
170
|
|
|
@@ -179,7 +179,7 @@ def reload_system() -> None:
|
|
|
179
179
|
the local the local server. Not perfect! But sometimes useful for development.
|
|
180
180
|
"""
|
|
181
181
|
import kash
|
|
182
|
-
from kash.local_server.local_server import
|
|
182
|
+
from kash.local_server.local_server import restart_ui_server
|
|
183
183
|
from kash.utils.common.import_utils import recursive_reload
|
|
184
184
|
|
|
185
185
|
module = kash
|
|
@@ -197,7 +197,7 @@ def reload_system() -> None:
|
|
|
197
197
|
log.info("Reloaded modules: %s", ", ".join(package_names))
|
|
198
198
|
log.message("Reloaded %s modules from %s.", len(package_names), module.__name__)
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
restart_ui_server()
|
|
201
201
|
|
|
202
202
|
# TODO Re-register commands and actions.
|
|
203
203
|
|
|
@@ -213,3 +213,16 @@ def reload_commands_and_actions() -> None:
|
|
|
213
213
|
|
|
214
214
|
reload_shell_commands_and_actions()
|
|
215
215
|
log_command_action_info()
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@kash_command
|
|
219
|
+
def debug_exception() -> None:
|
|
220
|
+
"""
|
|
221
|
+
Useful to debug exception handling/printing in xonsh.
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
def raise_exception():
|
|
225
|
+
cprint("Raising an unexpected exception to test the exception handler.")
|
|
226
|
+
raise Exception("This is a test exception.")
|
|
227
|
+
|
|
228
|
+
raise_exception()
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
from clideps.env_vars.dotenv_setup import interactive_dotenv_setup
|
|
2
|
+
from clideps.env_vars.dotenv_utils import load_dotenv_paths
|
|
3
|
+
from clideps.env_vars.env_check import format_dotenv_check, format_env_var_check, print_env_check
|
|
4
|
+
from clideps.env_vars.env_names import get_all_common_env_names
|
|
5
|
+
from clideps.pkgs.pkg_check import pkg_check
|
|
6
|
+
from clideps.terminal.terminal_features import terminal_check
|
|
7
|
+
from flowmark import Wrap
|
|
8
|
+
from rich.text import Text
|
|
9
|
+
|
|
1
10
|
from kash.commands.base.model_commands import list_apis, list_models
|
|
2
|
-
from kash.commands.workspace.workspace_commands import list_params
|
|
3
11
|
from kash.config.logger import get_logger
|
|
4
|
-
from kash.config.settings import RECOMMENDED_API_KEYS
|
|
12
|
+
from kash.config.settings import RECOMMENDED_API_KEYS, global_settings
|
|
5
13
|
from kash.docs.all_docs import all_docs
|
|
6
14
|
from kash.exec import kash_command
|
|
7
15
|
from kash.help.tldr_help import tldr_refresh_cache
|
|
@@ -12,14 +20,6 @@ from kash.model.params_model import (
|
|
|
12
20
|
DEFAULT_STANDARD_LLM,
|
|
13
21
|
DEFAULT_STRUCTURED_LLM,
|
|
14
22
|
)
|
|
15
|
-
from kash.shell.clideps.api_keys import (
|
|
16
|
-
ApiEnvKey,
|
|
17
|
-
load_dotenv_paths,
|
|
18
|
-
print_api_key_setup,
|
|
19
|
-
)
|
|
20
|
-
from kash.shell.clideps.dotenv_setup import interactive_dotenv_setup
|
|
21
|
-
from kash.shell.clideps.pkg_deps import pkg_check
|
|
22
|
-
from kash.shell.clideps.terminal_features import terminal_check
|
|
23
23
|
from kash.shell.input.input_prompts import input_choice
|
|
24
24
|
from kash.shell.output.shell_formatting import format_name_and_value
|
|
25
25
|
from kash.shell.output.shell_output import (
|
|
@@ -48,8 +48,13 @@ def self_check(brief: bool = False) -> None:
|
|
|
48
48
|
Self-check kash setup, including termal settings, tools, and API keys.
|
|
49
49
|
"""
|
|
50
50
|
if brief:
|
|
51
|
-
terminal_check().
|
|
52
|
-
|
|
51
|
+
cprint(terminal_check().formatted())
|
|
52
|
+
cprint(Text.assemble("Dotenv files: ", format_dotenv_check()))
|
|
53
|
+
cprint(
|
|
54
|
+
Text.assemble(
|
|
55
|
+
"Env vars: ", format_env_var_check(env_vars=RECOMMENDED_API_KEYS, one_line=True)
|
|
56
|
+
)
|
|
57
|
+
)
|
|
53
58
|
check_system_tools(brief=brief)
|
|
54
59
|
tldr_refresh_cache()
|
|
55
60
|
try:
|
|
@@ -61,7 +66,7 @@ def self_check(brief: bool = False) -> None:
|
|
|
61
66
|
else:
|
|
62
67
|
version()
|
|
63
68
|
cprint()
|
|
64
|
-
terminal_check().
|
|
69
|
+
cprint(terminal_check().formatted())
|
|
65
70
|
cprint()
|
|
66
71
|
list_apis()
|
|
67
72
|
cprint()
|
|
@@ -86,9 +91,10 @@ def self_configure(all: bool = False, update: bool = False) -> None:
|
|
|
86
91
|
"""
|
|
87
92
|
Interactively configure API keys and preferred models.
|
|
88
93
|
"""
|
|
94
|
+
from kash.commands.workspace.workspace_commands import params as list_params
|
|
89
95
|
|
|
90
96
|
if all:
|
|
91
|
-
api_keys =
|
|
97
|
+
api_keys = list(set(get_all_common_env_names() + RECOMMENDED_API_KEYS))
|
|
92
98
|
else:
|
|
93
99
|
api_keys = RECOMMENDED_API_KEYS
|
|
94
100
|
# Show APIs before starting.
|
|
@@ -175,10 +181,11 @@ def reload_env() -> None:
|
|
|
175
181
|
Reload the environment variables from the .env file.
|
|
176
182
|
"""
|
|
177
183
|
|
|
178
|
-
env_paths = load_dotenv_paths()
|
|
184
|
+
env_paths = load_dotenv_paths(True, True, global_settings().system_config_dir)
|
|
179
185
|
if env_paths:
|
|
180
186
|
cprint("Reloaded environment variables")
|
|
181
|
-
|
|
187
|
+
|
|
188
|
+
print_env_check(RECOMMENDED_API_KEYS)
|
|
182
189
|
else:
|
|
183
190
|
raise InvalidState("No .env file found")
|
|
184
191
|
|
|
@@ -197,7 +204,9 @@ def kits() -> None:
|
|
|
197
204
|
else:
|
|
198
205
|
cprint("Currently imported kits:")
|
|
199
206
|
for kit in get_loaded_kits().values():
|
|
200
|
-
cprint(
|
|
207
|
+
cprint(
|
|
208
|
+
format_name_and_value(f"{kit.name} kit", str(kit.path or ""), text_wrap=Wrap.NONE)
|
|
209
|
+
)
|
|
201
210
|
|
|
202
211
|
|
|
203
212
|
@kash_command
|
|
@@ -210,5 +219,5 @@ def settings() -> None:
|
|
|
210
219
|
settings = global_settings()
|
|
211
220
|
print_h2("Global Settings")
|
|
212
221
|
for field, value in settings.__dict__.items():
|
|
213
|
-
cprint(format_name_and_value(field, str(value)))
|
|
222
|
+
cprint(format_name_and_value(field, str(value), text_wrap=Wrap.NONE))
|
|
214
223
|
PrintHooks.spacer()
|
|
@@ -8,7 +8,6 @@ from kash.config.settings import (
|
|
|
8
8
|
LogLevel,
|
|
9
9
|
atomic_global_settings,
|
|
10
10
|
global_settings,
|
|
11
|
-
server_log_file_path,
|
|
12
11
|
)
|
|
13
12
|
from kash.exec import kash_command
|
|
14
13
|
from kash.shell.output.shell_formatting import format_name_and_value
|
|
@@ -87,7 +86,5 @@ def log_settings() -> None:
|
|
|
87
86
|
cprint(format_name_and_value("log_file_level", settings.log_file_level.name))
|
|
88
87
|
cprint(format_name_and_value("log_console_level", settings.log_console_level.name))
|
|
89
88
|
cprint(
|
|
90
|
-
format_name_and_value(
|
|
91
|
-
"server_log_file", str(server_log_file_path(global_settings().local_server_port))
|
|
92
|
-
)
|
|
89
|
+
format_name_and_value("server_log_file_path", str(global_settings().local_server_log_path))
|
|
93
90
|
)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
from clideps.env_vars.dotenv_utils import env_var_is_set
|
|
1
2
|
from flowmark import Wrap
|
|
2
3
|
from rich.text import Text
|
|
3
4
|
|
|
5
|
+
from kash.config.settings import get_all_common_api_env_vars
|
|
4
6
|
from kash.exec.command_registry import kash_command
|
|
5
7
|
from kash.llm_utils import LLM, api_for_model
|
|
6
|
-
from kash.shell.clideps.api_keys import ApiEnvKey
|
|
7
|
-
from kash.shell.clideps.dotenv_utils import env_var_is_set
|
|
8
8
|
from kash.shell.output.shell_formatting import (
|
|
9
9
|
format_failure,
|
|
10
10
|
format_name_and_value,
|
|
@@ -48,11 +48,11 @@ def list_apis() -> None:
|
|
|
48
48
|
List and check configuration for all APIs.
|
|
49
49
|
"""
|
|
50
50
|
print_h2("API keys")
|
|
51
|
-
for
|
|
52
|
-
emoji = format_success_emoji(env_var_is_set(
|
|
51
|
+
for env_var in get_all_common_api_env_vars():
|
|
52
|
+
emoji = format_success_emoji(env_var_is_set(env_var))
|
|
53
53
|
message = (
|
|
54
|
-
f"API key {
|
|
55
|
-
if env_var_is_set(
|
|
56
|
-
else f"API key {
|
|
54
|
+
f"API key {env_var} found"
|
|
55
|
+
if env_var_is_set(env_var)
|
|
56
|
+
else f"API key {env_var} not found"
|
|
57
57
|
)
|
|
58
|
-
cprint(Text.assemble(emoji, format_name_and_value(
|
|
58
|
+
cprint(Text.assemble(emoji, format_name_and_value(env_var, message)))
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
|
|
3
|
+
from clideps.pkgs.pkg_check import pkg_check
|
|
4
|
+
|
|
3
5
|
from kash.config.logger import get_logger
|
|
4
6
|
from kash.exec import assemble_path_args, kash_command
|
|
5
7
|
from kash.exec_model.shell_model import ShellResult
|
|
6
|
-
from kash.shell.clideps.pkg_deps import Pkg, pkg_check
|
|
7
8
|
from kash.shell.output.shell_output import cprint
|
|
8
9
|
from kash.utils.common.parse_shell_args import shell_quote
|
|
9
10
|
from kash.utils.errors import InvalidState
|
|
@@ -33,7 +34,7 @@ def search(
|
|
|
33
34
|
:param ignore_case: Ignore case when searching.
|
|
34
35
|
:param verbose: Also print the ripgrep command line.
|
|
35
36
|
"""
|
|
36
|
-
pkg_check().require(
|
|
37
|
+
pkg_check().require("ripgrep")
|
|
37
38
|
from ripgrepy import RipGrepNotFound, Ripgrepy
|
|
38
39
|
|
|
39
40
|
resolved_paths = assemble_path_args(*paths)
|
|
@@ -18,6 +18,7 @@ def show(
|
|
|
18
18
|
native: bool = False,
|
|
19
19
|
thumbnail: bool = False,
|
|
20
20
|
browser: bool = False,
|
|
21
|
+
plain: bool = False,
|
|
21
22
|
) -> None:
|
|
22
23
|
"""
|
|
23
24
|
Show the contents of a file if one is given, or the first file if multiple files
|
|
@@ -31,10 +32,11 @@ def show(
|
|
|
31
32
|
:param native: Force display with a native app (depending on your system configuration).
|
|
32
33
|
:param thumbnail: If there is a thumbnail image, show it too.
|
|
33
34
|
:param browser: Force display with your default web browser.
|
|
35
|
+
:param plain: Use plain view in the console (this is `bat`'s `plain` style).
|
|
34
36
|
"""
|
|
35
37
|
view_mode = (
|
|
36
38
|
ViewMode.console
|
|
37
|
-
if console
|
|
39
|
+
if console or plain
|
|
38
40
|
else ViewMode.browser
|
|
39
41
|
if browser
|
|
40
42
|
else ViewMode.native
|
|
@@ -58,9 +60,9 @@ def show(
|
|
|
58
60
|
log.info("Had trouble showing thumbnail image (will skip): %s", e)
|
|
59
61
|
cprint(f"[Image: {item.thumbnail_url}]", style=STYLE_HINT)
|
|
60
62
|
|
|
61
|
-
view_file_native(ws.base_dir / input_path, view_mode=view_mode)
|
|
63
|
+
view_file_native(ws.base_dir / input_path, view_mode=view_mode, plain=plain)
|
|
62
64
|
else:
|
|
63
|
-
view_file_native(input_path, view_mode=view_mode)
|
|
65
|
+
view_file_native(input_path, view_mode=view_mode, plain=plain)
|
|
64
66
|
except (InvalidInput, InvalidState):
|
|
65
67
|
if path:
|
|
66
68
|
# If path is absolute or we couldbn't get a selection, just show the file.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import tomllib
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from packaging.tags import Tag, sys_tags
|
|
7
|
+
from packaging.utils import parse_wheel_filename
|
|
8
|
+
from prettyfmt import fmt_size_dual
|
|
9
|
+
from strif import atomic_output_file
|
|
10
|
+
|
|
11
|
+
from kash.config.logger import get_logger
|
|
12
|
+
from kash.config.text_styles import COLOR_STATUS
|
|
13
|
+
from kash.exec import kash_command
|
|
14
|
+
from kash.shell.output.shell_output import cprint
|
|
15
|
+
|
|
16
|
+
log = get_logger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def choose_wheel(wheels: list[dict], allowed: list[Tag]) -> dict | None:
|
|
20
|
+
"""
|
|
21
|
+
Pick the first wheel whose tag set intersects `allowed`
|
|
22
|
+
(highest priority wins).
|
|
23
|
+
"""
|
|
24
|
+
priority = {tag: idx for idx, tag in enumerate(allowed)}
|
|
25
|
+
best: dict | None = None
|
|
26
|
+
best_rank = float("inf")
|
|
27
|
+
|
|
28
|
+
for meta in wheels:
|
|
29
|
+
url = meta.get("url", "")
|
|
30
|
+
filename = Path(url).name if url else ""
|
|
31
|
+
if not filename:
|
|
32
|
+
raise ValueError(f"No filename found for {url}")
|
|
33
|
+
tags = parse_wheel_filename(filename)[-1]
|
|
34
|
+
|
|
35
|
+
for tag in tags:
|
|
36
|
+
rank = priority.get(tag)
|
|
37
|
+
if rank is not None and rank < best_rank:
|
|
38
|
+
best = meta
|
|
39
|
+
best_rank = rank
|
|
40
|
+
break
|
|
41
|
+
|
|
42
|
+
return best
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_platform() -> str:
|
|
46
|
+
"""
|
|
47
|
+
Get the most-specific platform tag your interpreter supports.
|
|
48
|
+
"""
|
|
49
|
+
return next(sys_tags()).platform
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def parse_uv_lock(lock_path: Path) -> pd.DataFrame:
|
|
53
|
+
"""
|
|
54
|
+
Return one row per package from a uv.lock file, selecting the best
|
|
55
|
+
matching wheel for the current interpreter or falling back to the sdist.
|
|
56
|
+
|
|
57
|
+
Columns: name, version, registry, file_type, url, hash, size, filename.
|
|
58
|
+
"""
|
|
59
|
+
with open(lock_path, "rb") as f:
|
|
60
|
+
data = tomllib.load(f)
|
|
61
|
+
|
|
62
|
+
rows: list[dict] = []
|
|
63
|
+
for pkg in data.get("package", []):
|
|
64
|
+
name = pkg.get("name")
|
|
65
|
+
version = pkg.get("version")
|
|
66
|
+
registry = pkg.get("source", {}).get("registry")
|
|
67
|
+
|
|
68
|
+
wheels = pkg.get("wheels", [])
|
|
69
|
+
selected = choose_wheel(wheels, list(sys_tags()))
|
|
70
|
+
if selected:
|
|
71
|
+
meta = selected
|
|
72
|
+
file_type = "wheel"
|
|
73
|
+
else:
|
|
74
|
+
meta = pkg.get("sdist", {})
|
|
75
|
+
file_type = "sdist"
|
|
76
|
+
|
|
77
|
+
url = meta.get("url")
|
|
78
|
+
rows.append(
|
|
79
|
+
{
|
|
80
|
+
"name": name,
|
|
81
|
+
"version": version,
|
|
82
|
+
"registry": registry,
|
|
83
|
+
"file_type": file_type,
|
|
84
|
+
"url": url,
|
|
85
|
+
"hash": meta.get("hash"),
|
|
86
|
+
"size": meta.get("size"),
|
|
87
|
+
"filename": Path(url).name if url else None,
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return pd.DataFrame(rows)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def uv_runtime_packages(
|
|
95
|
+
project_dir: str | Path = ".", no_dev: bool = False, uv_executable: str = "uv"
|
|
96
|
+
) -> list[str]:
|
|
97
|
+
"""
|
|
98
|
+
Return the *runtime* (non-dev) package names that would be installed for the
|
|
99
|
+
given project, as resolved by uv.
|
|
100
|
+
"""
|
|
101
|
+
cmd = [
|
|
102
|
+
uv_executable,
|
|
103
|
+
"export",
|
|
104
|
+
"--format",
|
|
105
|
+
"requirements-txt",
|
|
106
|
+
"--no-header",
|
|
107
|
+
"--no-annotate",
|
|
108
|
+
"--no-hashes",
|
|
109
|
+
]
|
|
110
|
+
if no_dev:
|
|
111
|
+
cmd.append("--no-dev")
|
|
112
|
+
|
|
113
|
+
result = subprocess.run(
|
|
114
|
+
cmd,
|
|
115
|
+
cwd=Path(project_dir),
|
|
116
|
+
check=True,
|
|
117
|
+
text=True,
|
|
118
|
+
capture_output=True,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
packages: list[str] = []
|
|
122
|
+
for line in result.stdout.splitlines():
|
|
123
|
+
line = line.strip()
|
|
124
|
+
if "==" not in line: # skip “-e .” and blank lines
|
|
125
|
+
continue
|
|
126
|
+
pkg_name, _ = line.split("==", maxsplit=1)
|
|
127
|
+
packages.append(pkg_name.strip())
|
|
128
|
+
|
|
129
|
+
return packages
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@kash_command
|
|
133
|
+
def uv_dep_info(
|
|
134
|
+
uv_lock: str = "uv.lock",
|
|
135
|
+
pyproject: str = "pyproject.toml",
|
|
136
|
+
save: str | None = None,
|
|
137
|
+
sort_by: str = "size",
|
|
138
|
+
) -> None:
|
|
139
|
+
"""
|
|
140
|
+
Parse a uv.lock file and print information about the packages.
|
|
141
|
+
By default, filters to show only 'main' dependencies from pyproject.toml.
|
|
142
|
+
Helpful for looking at sizes of dependencies.
|
|
143
|
+
"""
|
|
144
|
+
uv_lock_path = Path(uv_lock)
|
|
145
|
+
pyproject_path = Path(pyproject)
|
|
146
|
+
|
|
147
|
+
main_deps: list[str] | None = None
|
|
148
|
+
all_deps: list[str] = []
|
|
149
|
+
if pyproject_path.exists():
|
|
150
|
+
cprint("Reading main dependencies from with uv", style=COLOR_STATUS)
|
|
151
|
+
main_deps = uv_runtime_packages(no_dev=True)
|
|
152
|
+
all_deps = uv_runtime_packages(no_dev=False)
|
|
153
|
+
else:
|
|
154
|
+
log.warning("pyproject.toml not found: %s", pyproject_path)
|
|
155
|
+
|
|
156
|
+
cprint(f"Parsing lock file: {uv_lock_path}", style=COLOR_STATUS)
|
|
157
|
+
|
|
158
|
+
df = parse_uv_lock(uv_lock_path)
|
|
159
|
+
df = df.sort_values(by=sort_by)
|
|
160
|
+
|
|
161
|
+
if main_deps:
|
|
162
|
+
cprint(
|
|
163
|
+
f"Filtering lock file entries to include only {len(main_deps)} of {len(all_deps)} dependencies.",
|
|
164
|
+
style=COLOR_STATUS,
|
|
165
|
+
)
|
|
166
|
+
df = df[df["name"].isin(main_deps)]
|
|
167
|
+
else:
|
|
168
|
+
cprint("Showing all packages from lock file.", style=COLOR_STATUS)
|
|
169
|
+
cprint()
|
|
170
|
+
|
|
171
|
+
# Show only selected columns and full output
|
|
172
|
+
cols = ["name", "version", "file_type", "size", "filename"]
|
|
173
|
+
print(df.loc[:, cols].to_string(max_rows=None))
|
|
174
|
+
cprint() # Add a newline for separation
|
|
175
|
+
|
|
176
|
+
# Calculate and print summary stats
|
|
177
|
+
num_deps = len(df)
|
|
178
|
+
total_size = pd.Series(pd.to_numeric(df["size"], errors="coerce")).fillna(0).sum()
|
|
179
|
+
|
|
180
|
+
cprint(f"Packages listed: {num_deps}", style=COLOR_STATUS)
|
|
181
|
+
cprint(f"Total size: {fmt_size_dual(int(total_size))}", style=COLOR_STATUS)
|
|
182
|
+
|
|
183
|
+
if save:
|
|
184
|
+
with atomic_output_file(save) as temp_name:
|
|
185
|
+
df.to_csv(temp_name, index=False) # Added index=False for cleaner CSV
|
|
186
|
+
cprint(f"Saved to {save}", style=COLOR_STATUS)
|
|
@@ -1,42 +1,13 @@
|
|
|
1
|
-
from
|
|
2
|
-
from rich.console import Group
|
|
3
|
-
from rich.panel import Panel
|
|
4
|
-
|
|
5
|
-
from kash.commands.help.logo import branded_box
|
|
1
|
+
from kash.commands.help.welcome import welcome
|
|
6
2
|
from kash.config.logger import get_logger
|
|
7
|
-
from kash.config.text_styles import (
|
|
8
|
-
COLOR_HINT,
|
|
9
|
-
)
|
|
10
3
|
from kash.docs.all_docs import DocSelection, all_docs
|
|
11
4
|
from kash.exec import kash_command
|
|
12
5
|
from kash.help.help_pages import print_see_also
|
|
13
|
-
from kash.shell.output.shell_output import
|
|
14
|
-
from kash.shell.version import get_full_version_name
|
|
15
|
-
from kash.utils.rich_custom.rich_markdown_fork import Markdown
|
|
6
|
+
from kash.shell.output.shell_output import console_pager, print_markdown
|
|
16
7
|
|
|
17
8
|
log = get_logger(__name__)
|
|
18
9
|
|
|
19
10
|
|
|
20
|
-
@kash_command
|
|
21
|
-
def welcome() -> None:
|
|
22
|
-
"""
|
|
23
|
-
Print a welcome message.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
help_topics = all_docs.help_topics
|
|
27
|
-
version = get_full_version_name()
|
|
28
|
-
# Create header with logo and right-justified version
|
|
29
|
-
|
|
30
|
-
PrintHooks.before_welcome()
|
|
31
|
-
cprint(
|
|
32
|
-
branded_box(
|
|
33
|
-
Group(Markdown(help_topics.welcome)),
|
|
34
|
-
version,
|
|
35
|
-
)
|
|
36
|
-
)
|
|
37
|
-
cprint(Panel(Markdown(help_topics.warning), box=SQUARE, border_style=COLOR_HINT))
|
|
38
|
-
|
|
39
|
-
|
|
40
11
|
@kash_command
|
|
41
12
|
def manual(no_pager: bool = False, doc_selection: DocSelection = DocSelection.full) -> None:
|
|
42
13
|
"""
|