kash-shell 0.3.0__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/__init__.py +2 -0
- kash/__main__.py +4 -0
- kash/actions/__init__.py +55 -0
- kash/actions/core/assistant_chat.py +45 -0
- kash/actions/core/chat.py +90 -0
- kash/actions/core/format_markdown_template.py +92 -0
- kash/actions/core/markdownify.py +29 -0
- kash/actions/core/readability.py +27 -0
- kash/actions/core/show_webpage.py +28 -0
- kash/actions/core/strip_html.py +28 -0
- kash/actions/core/summarize_as_bullets.py +53 -0
- kash/actions/core/webpage_config.py +21 -0
- kash/actions/core/webpage_generate.py +29 -0
- kash/actions/meta/write_instructions.py +39 -0
- kash/actions/meta/write_new_action.py +157 -0
- kash/commands/__init__.py +21 -0
- kash/commands/base/basic_file_commands.py +183 -0
- kash/commands/base/browser_commands.py +62 -0
- kash/commands/base/debug_commands.py +214 -0
- kash/commands/base/diff_commands.py +90 -0
- kash/commands/base/files_command.py +408 -0
- kash/commands/base/general_commands.py +104 -0
- kash/commands/base/global_state_commands.py +41 -0
- kash/commands/base/logs_commands.py +92 -0
- kash/commands/base/reformat_command.py +54 -0
- kash/commands/base/search_command.py +65 -0
- kash/commands/base/show_command.py +69 -0
- kash/commands/extras/utils_commands.py +27 -0
- kash/commands/help/assistant_commands.py +97 -0
- kash/commands/help/doc_commands.py +226 -0
- kash/commands/help/help_commands.py +133 -0
- kash/commands/workspace/selection_commands.py +200 -0
- kash/commands/workspace/workspace_commands.py +640 -0
- kash/concepts/concept_formats.py +23 -0
- kash/concepts/embeddings.py +130 -0
- kash/concepts/text_similarity.py +108 -0
- kash/config/__init__.py +4 -0
- kash/config/api_keys.py +84 -0
- kash/config/capture_output.py +77 -0
- kash/config/colors.py +279 -0
- kash/config/init.py +18 -0
- kash/config/lazy_imports.py +22 -0
- kash/config/logger.py +355 -0
- kash/config/logger_basic.py +35 -0
- kash/config/logo.txt +4 -0
- kash/config/logo_fancy.txt +4 -0
- kash/config/server_config.py +51 -0
- kash/config/settings.py +196 -0
- kash/config/setup.py +51 -0
- kash/config/suppress_warnings.py +27 -0
- kash/config/text_styles.py +426 -0
- kash/docs/__init__.py +0 -0
- kash/docs/all_docs.py +58 -0
- kash/docs/load_actions_info.py +28 -0
- kash/docs/load_api_docs.py +13 -0
- kash/docs/load_help_topics.py +47 -0
- kash/docs/load_source_code.py +125 -0
- kash/docs/markdown/api_docs_template.md +42 -0
- kash/docs/markdown/assistant_instructions_template.md +114 -0
- kash/docs/markdown/readme_template.md +26 -0
- kash/docs/markdown/topics/a1_what_is_kash.md +76 -0
- kash/docs/markdown/topics/a2_progress.md +96 -0
- kash/docs/markdown/topics/a3_installation.md +119 -0
- kash/docs/markdown/topics/a4_getting_started.md +300 -0
- kash/docs/markdown/topics/a5_tips_for_use_with_other_tools.md +83 -0
- kash/docs/markdown/topics/b0_philosophy_of_kash.md +177 -0
- kash/docs/markdown/topics/b1_kash_overview.md +124 -0
- kash/docs/markdown/topics/b2_workspace_and_file_formats.md +61 -0
- kash/docs/markdown/topics/b3_modern_shell_tool_recommendations.md +83 -0
- kash/docs/markdown/topics/b4_faq.md +166 -0
- kash/docs/markdown/warning.md +7 -0
- kash/docs/markdown/welcome.md +9 -0
- kash/docs_base/docs_base.py +85 -0
- kash/docs_base/load_custom_command_info.py +27 -0
- kash/docs_base/load_faqs.py +48 -0
- kash/docs_base/load_recipe_snippets.py +48 -0
- kash/docs_base/recipes/general_system_commands.ksh +10 -0
- kash/docs_base/recipes/python_dev_commands.ksh +7 -0
- kash/docs_base/recipes/tldr_standard_commands.ksh +2144 -0
- kash/errors.py +176 -0
- kash/exec/__init__.py +16 -0
- kash/exec/action_decorators.py +412 -0
- kash/exec/action_exec.py +457 -0
- kash/exec/action_registry.py +123 -0
- kash/exec/combiners.py +127 -0
- kash/exec/command_exec.py +34 -0
- kash/exec/command_registry.py +72 -0
- kash/exec/fetch_url_metadata.py +71 -0
- kash/exec/history.py +44 -0
- kash/exec/llm_transforms.py +121 -0
- kash/exec/precondition_checks.py +71 -0
- kash/exec/precondition_registry.py +43 -0
- kash/exec/preconditions.py +152 -0
- kash/exec/resolve_args.py +123 -0
- kash/exec/shell_callable_action.py +90 -0
- kash/exec_model/__init__.py +0 -0
- kash/exec_model/args_model.py +93 -0
- kash/exec_model/commands_model.py +163 -0
- kash/exec_model/script_model.py +161 -0
- kash/exec_model/shell_model.py +21 -0
- kash/file_storage/__init__.py +0 -0
- kash/file_storage/file_store.py +642 -0
- kash/file_storage/item_file_format.py +152 -0
- kash/file_storage/metadata_dirs.py +108 -0
- kash/file_storage/mtime_cache.py +108 -0
- kash/file_storage/persisted_yaml.py +37 -0
- kash/file_storage/store_cache_warmer.py +37 -0
- kash/file_storage/store_filenames.py +53 -0
- kash/form_input/__init__.py +0 -0
- kash/form_input/prompt_input.py +44 -0
- kash/help/__init__.py +0 -0
- kash/help/assistant.py +324 -0
- kash/help/assistant_instructions.py +68 -0
- kash/help/assistant_output.py +43 -0
- kash/help/docstring_utils.py +111 -0
- kash/help/function_param_info.py +44 -0
- kash/help/help_embeddings.py +85 -0
- kash/help/help_lookups.py +60 -0
- kash/help/help_pages.py +122 -0
- kash/help/help_printing.py +169 -0
- kash/help/help_types.py +247 -0
- kash/help/recommended_commands.py +143 -0
- kash/help/tldr_help.py +296 -0
- kash/llm_utils/__init__.py +0 -0
- kash/llm_utils/chat_format.py +413 -0
- kash/llm_utils/clean_headings.py +65 -0
- kash/llm_utils/fuzzy_parsing.py +119 -0
- kash/llm_utils/language_models.py +178 -0
- kash/llm_utils/llm_completion.py +172 -0
- kash/llm_utils/llm_messages.py +36 -0
- kash/local_server/__init__.py +2 -0
- kash/local_server/local_server.py +183 -0
- kash/local_server/local_server_commands.py +55 -0
- kash/local_server/local_server_routes.py +306 -0
- kash/local_server/local_url_formatters.py +169 -0
- kash/local_server/port_tools.py +67 -0
- kash/local_server/rich_html_template.py +12 -0
- kash/mcp/__init__.py +2 -0
- kash/mcp/mcp_main.py +67 -0
- kash/mcp/mcp_server_commands.py +57 -0
- kash/mcp/mcp_server_routes.py +256 -0
- kash/mcp/mcp_server_sse.py +143 -0
- kash/mcp/mcp_server_stdio.py +45 -0
- kash/media_base/__init__.py +0 -0
- kash/media_base/audio_processing.py +27 -0
- kash/media_base/media_cache.py +178 -0
- kash/media_base/media_services.py +112 -0
- kash/media_base/media_tools.py +48 -0
- kash/media_base/services/local_file_media.py +165 -0
- kash/media_base/speech_transcription.py +224 -0
- kash/media_base/timestamp_citations.py +80 -0
- kash/model/__init__.py +73 -0
- kash/model/actions_model.py +633 -0
- kash/model/assistant_response_model.py +87 -0
- kash/model/compound_actions_model.py +188 -0
- kash/model/graph_model.py +92 -0
- kash/model/items_model.py +821 -0
- kash/model/language_list.py +39 -0
- kash/model/llm_actions_model.py +63 -0
- kash/model/media_model.py +124 -0
- kash/model/operations_model.py +176 -0
- kash/model/params_model.py +435 -0
- kash/model/paths_model.py +458 -0
- kash/model/preconditions_model.py +98 -0
- kash/shell/__init__.py +0 -0
- kash/shell/completions/completion_scoring.py +280 -0
- kash/shell/completions/completion_types.py +154 -0
- kash/shell/completions/shell_completions.py +277 -0
- kash/shell/file_icons/color_for_format.py +70 -0
- kash/shell/file_icons/nerd_icons.py +946 -0
- kash/shell/output/__init__.py +0 -0
- kash/shell/output/kerm_code_utils.py +59 -0
- kash/shell/output/kerm_codes.py +588 -0
- kash/shell/output/kmarkdown.py +117 -0
- kash/shell/output/shell_output.py +477 -0
- kash/shell/ui/__init__.py +0 -0
- kash/shell/ui/shell_results.py +118 -0
- kash/shell/ui/shell_syntax.py +26 -0
- kash/shell/utils/exception_printing.py +50 -0
- kash/shell/utils/native_utils.py +240 -0
- kash/shell/utils/osc_utils.py +95 -0
- kash/shell/utils/shell_function_wrapper.py +204 -0
- kash/shell/utils/sys_tool_deps.py +289 -0
- kash/shell/utils/terminal_images.py +133 -0
- kash/shell_main.py +67 -0
- kash/text_handling/custom_sliding_transforms.py +266 -0
- kash/text_handling/doc_normalization.py +64 -0
- kash/text_handling/markdown_util.py +167 -0
- kash/text_handling/unified_diffs.py +138 -0
- kash/utils/__init__.py +4 -0
- kash/utils/common/__init__.py +4 -0
- kash/utils/common/atomic_var.py +147 -0
- kash/utils/common/format_utils.py +81 -0
- kash/utils/common/function_inspect.py +178 -0
- kash/utils/common/import_utils.py +89 -0
- kash/utils/common/lazyobject.py +144 -0
- kash/utils/common/obj_replace.py +78 -0
- kash/utils/common/parse_key_vals.py +85 -0
- kash/utils/common/parse_shell_args.py +348 -0
- kash/utils/common/stack_traces.py +49 -0
- kash/utils/common/string_replace.py +93 -0
- kash/utils/common/string_template.py +101 -0
- kash/utils/common/task_stack.py +162 -0
- kash/utils/common/type_utils.py +137 -0
- kash/utils/common/uniquifier.py +95 -0
- kash/utils/common/url.py +155 -0
- kash/utils/file_utils/__init__.py +3 -0
- kash/utils/file_utils/dir_size.py +48 -0
- kash/utils/file_utils/file_ext.py +86 -0
- kash/utils/file_utils/file_formats.py +134 -0
- kash/utils/file_utils/file_formats_model.py +408 -0
- kash/utils/file_utils/file_sort_filter.py +235 -0
- kash/utils/file_utils/file_walk.py +163 -0
- kash/utils/file_utils/filename_parsing.py +99 -0
- kash/utils/file_utils/git_tools.py +19 -0
- kash/utils/file_utils/ignore_files.py +166 -0
- kash/utils/file_utils/path_utils.py +36 -0
- kash/utils/lang_utils/__init__.py +0 -0
- kash/utils/lang_utils/capitalization.py +128 -0
- kash/utils/lang_utils/inflection.py +18 -0
- kash/utils/rich_custom/__init__.py +3 -0
- kash/utils/rich_custom/ansi_cell_len.py +72 -0
- kash/utils/rich_custom/rich_char_transform.py +89 -0
- kash/utils/rich_custom/rich_indent.py +69 -0
- kash/utils/rich_custom/rich_markdown_fork.py +771 -0
- kash/version.py +31 -0
- kash/web_content/canon_url.py +24 -0
- kash/web_content/dir_store.py +103 -0
- kash/web_content/file_cache_utils.py +117 -0
- kash/web_content/local_file_cache.py +247 -0
- kash/web_content/web_extract.py +55 -0
- kash/web_content/web_extract_justext.py +86 -0
- kash/web_content/web_extract_readabilipy.py +23 -0
- kash/web_content/web_fetch.py +101 -0
- kash/web_content/web_page_model.py +28 -0
- kash/web_gen/__init__.py +4 -0
- kash/web_gen/tabbed_webpage.py +149 -0
- kash/web_gen/template_render.py +29 -0
- kash/web_gen/templates/base_styles.css.jinja +192 -0
- kash/web_gen/templates/base_webpage.html.jinja +124 -0
- kash/web_gen/templates/content_styles.css.jinja +194 -0
- kash/web_gen/templates/explain_view.html.jinja +49 -0
- kash/web_gen/templates/item_view.html.jinja +294 -0
- kash/web_gen/templates/tabbed_webpage.html.jinja +49 -0
- kash/workspaces/__init__.py +13 -0
- kash/workspaces/param_state.py +24 -0
- kash/workspaces/selections.py +333 -0
- kash/workspaces/source_items.py +88 -0
- kash/workspaces/workspace_importing.py +56 -0
- kash/workspaces/workspace_names.py +33 -0
- kash/workspaces/workspace_output.py +154 -0
- kash/workspaces/workspace_registry.py +78 -0
- kash/workspaces/workspaces.py +197 -0
- kash/xonsh_custom/custom_shell.py +366 -0
- kash/xonsh_custom/customize_prompt.py +197 -0
- kash/xonsh_custom/customize_xonsh.py +112 -0
- kash/xonsh_custom/shell_load_commands.py +152 -0
- kash/xonsh_custom/shell_which.py +64 -0
- kash/xonsh_custom/xonsh_completers.py +715 -0
- kash/xonsh_custom/xonsh_env.py +28 -0
- kash/xonsh_custom/xonsh_modern_tools.py +56 -0
- kash/xonsh_custom/xonsh_ranking_completer.py +152 -0
- kash/xontrib/fnm.py +120 -0
- kash/xontrib/kash_extension.py +61 -0
- kash_shell-0.3.0.dist-info/METADATA +757 -0
- kash_shell-0.3.0.dist-info/RECORD +269 -0
- kash_shell-0.3.0.dist-info/WHEEL +4 -0
- kash_shell-0.3.0.dist-info/entry_points.txt +3 -0
- kash_shell-0.3.0.dist-info/licenses/LICENSE +664 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
from flowmark import Wrap, fill_text
|
|
2
|
+
|
|
3
|
+
from kash.actions.meta.write_instructions import write_instructions
|
|
4
|
+
from kash.config.logger import get_logger
|
|
5
|
+
from kash.errors import ApiResultError, InvalidInput
|
|
6
|
+
from kash.exec import kash_action
|
|
7
|
+
from kash.exec.preconditions import is_instructions
|
|
8
|
+
from kash.help.assistant import assist_preamble, assistance_structured
|
|
9
|
+
from kash.help.assistant_output import print_assistant_response
|
|
10
|
+
from kash.llm_utils.chat_format import ChatHistory, ChatMessage, ChatRole
|
|
11
|
+
from kash.llm_utils.language_models import LLMName
|
|
12
|
+
from kash.model import (
|
|
13
|
+
LLM,
|
|
14
|
+
ONE_OR_NO_ARGS,
|
|
15
|
+
ActionInput,
|
|
16
|
+
ActionResult,
|
|
17
|
+
Format,
|
|
18
|
+
ItemType,
|
|
19
|
+
Message,
|
|
20
|
+
TitleTemplate,
|
|
21
|
+
common_params,
|
|
22
|
+
)
|
|
23
|
+
from kash.utils.common.lazyobject import lazyobject
|
|
24
|
+
from kash.utils.common.type_utils import not_none
|
|
25
|
+
|
|
26
|
+
log = get_logger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@lazyobject
|
|
30
|
+
def write_action_instructions() -> str:
|
|
31
|
+
from kash.docs.load_source_code import load_source_code
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
"""
|
|
35
|
+
You are a senior software engineer who writes clean code exactly
|
|
36
|
+
conforming with existing practice using the kash framework, which you understand
|
|
37
|
+
clearly. You always want to write actions that are helpful and reusable
|
|
38
|
+
as command-line operations or as Python libraries, since kash makes
|
|
39
|
+
commands and actions available to use both ways.
|
|
40
|
+
|
|
41
|
+
You need to write Python code to implement an action or a command in kash.
|
|
42
|
+
|
|
43
|
+
Guidelines:
|
|
44
|
+
|
|
45
|
+
- Provide Python code in the python_code field.
|
|
46
|
+
|
|
47
|
+
- Choose a command implementation (with @kash_command decorating a function) if the
|
|
48
|
+
operation is very simple and does not have files (Items) to output. To just read
|
|
49
|
+
a file and show information, or print something, use a command. Choose an
|
|
50
|
+
action implementation (with @kash_action decorating a class) if the operation
|
|
51
|
+
has files (Items) to output.
|
|
52
|
+
|
|
53
|
+
- Add non-code commentary in the response_text field explaining any issues or
|
|
54
|
+
assumptions you made while writing the code.
|
|
55
|
+
|
|
56
|
+
- If desired behavior of the code is not clear from the description, add
|
|
57
|
+
comment placeholders in the code so it can be filled in later.
|
|
58
|
+
|
|
59
|
+
- If the user gives other random context, like a previous conversation, consider
|
|
60
|
+
how you would implement a command or action that would generalize the operation
|
|
61
|
+
being discussed. Add parameters to the command or action if it makes sense
|
|
62
|
+
a user would want them, and use defaults as appropriate.
|
|
63
|
+
|
|
64
|
+
- Look at the example below. Commonly, you will subclass PerItemAction
|
|
65
|
+
for simple actions that work on one item at a time. Subclass LLMAction
|
|
66
|
+
if it is simply a transformation of the input using an LLM.
|
|
67
|
+
.
|
|
68
|
+
To illustrate, here are a couple examples of the correct format for an action that
|
|
69
|
+
strips HTML tags:
|
|
70
|
+
"""
|
|
71
|
+
+ load_source_code().example_action_src.replace("{", "{{").replace("}", "}}")
|
|
72
|
+
+ """
|
|
73
|
+
|
|
74
|
+
And here are a couple simple command implementations:
|
|
75
|
+
"""
|
|
76
|
+
+ load_source_code().example_command_src.replace("{", "{{").replace("}", "}}")
|
|
77
|
+
+ """
|
|
78
|
+
|
|
79
|
+
Next you will get context or a request from the user on a problem, or
|
|
80
|
+
simply a previous discussion with an assistant describing a partial solution
|
|
81
|
+
but from which you can understand what action or command would help solve
|
|
82
|
+
the problem.
|
|
83
|
+
|
|
84
|
+
After thinking about the context below, describe a useful command or action
|
|
85
|
+
that is appropriate for the problem, and then give the Python code for it.
|
|
86
|
+
"""
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@kash_action(
|
|
91
|
+
expected_args=ONE_OR_NO_ARGS,
|
|
92
|
+
precondition=is_instructions,
|
|
93
|
+
cacheable=False,
|
|
94
|
+
interactive_input=True,
|
|
95
|
+
params=common_params("model"),
|
|
96
|
+
title_template=TitleTemplate("Action: {title}"),
|
|
97
|
+
)
|
|
98
|
+
def write_new_action(input: ActionInput, model: LLMName = LLM.default_structured) -> ActionResult:
|
|
99
|
+
"""
|
|
100
|
+
Create a new kash action or command in Python, based some problem or context or
|
|
101
|
+
a description of the features.
|
|
102
|
+
|
|
103
|
+
If no input is provided, will start an interactive chat to get
|
|
104
|
+
|
|
105
|
+
Or `write_instructions` to create a chat to use as input for this action.
|
|
106
|
+
"""
|
|
107
|
+
if not input.items:
|
|
108
|
+
# Start a chat to collect the action description.
|
|
109
|
+
# FIXME: Consider generalizing this so an action can declare an input action to collect its input.
|
|
110
|
+
chat_result = write_instructions(ActionInput.empty())
|
|
111
|
+
if not chat_result.items:
|
|
112
|
+
raise InvalidInput("No chat input provided")
|
|
113
|
+
|
|
114
|
+
action_description_item = chat_result.items[0]
|
|
115
|
+
else:
|
|
116
|
+
action_description_item = input.items[0]
|
|
117
|
+
|
|
118
|
+
# Manually check precondition since we might have created the item
|
|
119
|
+
is_instructions.check(action_description_item, "action `write_new_action`")
|
|
120
|
+
|
|
121
|
+
chat_history = ChatHistory()
|
|
122
|
+
|
|
123
|
+
instructions_message = ChatHistory.from_yaml(not_none(action_description_item.body)).messages[0]
|
|
124
|
+
|
|
125
|
+
# Give the LLM full context on kash APIs.
|
|
126
|
+
# But we do this here lazily to prevent circular dependencies.
|
|
127
|
+
system_message = Message(assist_preamble(is_structured=True, skip_api_docs=False))
|
|
128
|
+
chat_history.extend(
|
|
129
|
+
[
|
|
130
|
+
ChatMessage(ChatRole.system, system_message),
|
|
131
|
+
ChatMessage(ChatRole.user, str(write_action_instructions)),
|
|
132
|
+
instructions_message,
|
|
133
|
+
]
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
assistant_response = assistance_structured(chat_history.as_chat_completion(), model)
|
|
137
|
+
|
|
138
|
+
print_assistant_response(assistant_response, model)
|
|
139
|
+
|
|
140
|
+
if not assistant_response.python_code:
|
|
141
|
+
raise ApiResultError("No Python code provided in the response.")
|
|
142
|
+
|
|
143
|
+
body = assistant_response.python_code
|
|
144
|
+
# Put the instructions in an actual comment at the top of the file.
|
|
145
|
+
action_comments = "(This action was written by kash `write_new_action`.)\n\n" + str(
|
|
146
|
+
instructions_message.content
|
|
147
|
+
)
|
|
148
|
+
comment = fill_text(action_comments, text_wrap=Wrap.WRAP_FULL, extra_indent="# ")
|
|
149
|
+
commented_body = "\n\n".join(filter(None, [comment, body]))
|
|
150
|
+
|
|
151
|
+
result_item = action_description_item.derived_copy(
|
|
152
|
+
type=ItemType.extension,
|
|
153
|
+
format=Format.python,
|
|
154
|
+
body=commented_body,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
return ActionResult([result_item])
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Import all command modules to ensure commands are registered.
|
|
2
|
+
|
|
3
|
+
import kash.commands.base.basic_file_commands # noqa: F401
|
|
4
|
+
import kash.commands.base.browser_commands # noqa: F401
|
|
5
|
+
import kash.commands.base.debug_commands # noqa: F401
|
|
6
|
+
import kash.commands.base.diff_commands # noqa: F401
|
|
7
|
+
import kash.commands.base.files_command # noqa: F401
|
|
8
|
+
import kash.commands.base.general_commands # noqa: F401
|
|
9
|
+
import kash.commands.base.global_state_commands # noqa: F401
|
|
10
|
+
import kash.commands.base.logs_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
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from frontmatter_format import fmf_read_raw, fmf_strip_frontmatter
|
|
4
|
+
from prettyfmt import fmt_lines
|
|
5
|
+
from strif import copyfile_atomic
|
|
6
|
+
|
|
7
|
+
from kash.config.logger import get_logger
|
|
8
|
+
from kash.config.text_styles import STYLE_EMPH
|
|
9
|
+
from kash.errors import InvalidInput
|
|
10
|
+
from kash.exec import assemble_path_args, kash_command, resolve_path_arg
|
|
11
|
+
from kash.shell.output.shell_output import (
|
|
12
|
+
PadStyle,
|
|
13
|
+
PrintHooks,
|
|
14
|
+
Wrap,
|
|
15
|
+
cprint,
|
|
16
|
+
print_status,
|
|
17
|
+
print_style,
|
|
18
|
+
)
|
|
19
|
+
from kash.shell.utils.native_utils import edit_files, native_trash
|
|
20
|
+
from kash.shell.utils.native_utils import tail_file as native_tail_file
|
|
21
|
+
from kash.utils.common.format_utils import fmt_loc
|
|
22
|
+
from kash.utils.file_utils.file_formats_model import detect_file_format
|
|
23
|
+
from kash.workspaces.workspace_output import print_file_info
|
|
24
|
+
|
|
25
|
+
log = get_logger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@kash_command
|
|
29
|
+
def cbcopy(path: str | None = None, raw: bool = False) -> None:
|
|
30
|
+
"""
|
|
31
|
+
Copy the contents of a file (or the first file in the selection) to the OS-native
|
|
32
|
+
clipboard.
|
|
33
|
+
|
|
34
|
+
:param raw: Copy the full exact contents of the file. Otherwise frontmatter is omitted.
|
|
35
|
+
"""
|
|
36
|
+
# TODO: Get this to work for images!
|
|
37
|
+
import pyperclip
|
|
38
|
+
|
|
39
|
+
input_paths = assemble_path_args(path)
|
|
40
|
+
input_path = input_paths[0]
|
|
41
|
+
|
|
42
|
+
format = detect_file_format(input_path)
|
|
43
|
+
if not format or not format.is_text:
|
|
44
|
+
raise InvalidInput(f"Cannot copy non-text files to clipboard: {fmt_loc(input_path)}")
|
|
45
|
+
|
|
46
|
+
if raw:
|
|
47
|
+
with open(input_path) as f:
|
|
48
|
+
content = f.read()
|
|
49
|
+
|
|
50
|
+
pyperclip.copy(content)
|
|
51
|
+
print_status(
|
|
52
|
+
"Copied raw contents of file to clipboard (%s chars):\n%s",
|
|
53
|
+
len(content),
|
|
54
|
+
fmt_lines([fmt_loc(input_path)]),
|
|
55
|
+
)
|
|
56
|
+
else:
|
|
57
|
+
content, metadata_str = fmf_read_raw(input_path)
|
|
58
|
+
pyperclip.copy(content)
|
|
59
|
+
skip_msg = ""
|
|
60
|
+
if metadata_str:
|
|
61
|
+
skip_msg = f", skipping {len(metadata_str)} chars of frontmatter"
|
|
62
|
+
print_status(
|
|
63
|
+
"Copied contents of file to clipboard (%s chars%s):\n%s",
|
|
64
|
+
len(content),
|
|
65
|
+
skip_msg,
|
|
66
|
+
fmt_lines([fmt_loc(input_path)]),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@kash_command
|
|
71
|
+
def edit(path: str | None = None, all: bool = False) -> None:
|
|
72
|
+
"""
|
|
73
|
+
Edit the contents of a file using the user's default editor (or defaulting to nano).
|
|
74
|
+
|
|
75
|
+
:param all: Normally edits only the first file given. This passes all files to the editor.
|
|
76
|
+
"""
|
|
77
|
+
input_paths = assemble_path_args(path)
|
|
78
|
+
if not all:
|
|
79
|
+
input_paths = [input_paths[0]]
|
|
80
|
+
|
|
81
|
+
edit_files(*input_paths)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@kash_command
|
|
85
|
+
def file_info(*paths: str, size_summary: bool = False, format: bool = False) -> None:
|
|
86
|
+
"""
|
|
87
|
+
Show info about a file. By default this includes a summary of the size and HTML
|
|
88
|
+
structure of the items at the given paths (for text documents) and the detected
|
|
89
|
+
mime type.
|
|
90
|
+
|
|
91
|
+
:param size_summary: Only show size summary (words, sentences, paragraphs for a text document).
|
|
92
|
+
:param format: Only show detected file format.
|
|
93
|
+
"""
|
|
94
|
+
if not size_summary and not format:
|
|
95
|
+
size_summary = format = True
|
|
96
|
+
|
|
97
|
+
# FIXME: Ensure this yields absolute paths for global workspace store paths
|
|
98
|
+
input_paths = assemble_path_args(*paths)
|
|
99
|
+
for input_path in input_paths:
|
|
100
|
+
cprint(f"{fmt_loc(input_path)}:", style=STYLE_EMPH, text_wrap=Wrap.NONE)
|
|
101
|
+
with print_style(PadStyle.INDENT):
|
|
102
|
+
print_file_info(input_path, show_size_details=size_summary, show_format=format)
|
|
103
|
+
PrintHooks.spacer()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@kash_command
|
|
107
|
+
def rename(path: str, new_path: str) -> None:
|
|
108
|
+
"""
|
|
109
|
+
Rename a file or item. Creates any new parent paths as needed.
|
|
110
|
+
Note this may invalidate relations that point to the old store path.
|
|
111
|
+
|
|
112
|
+
TODO: Add an option here to update all relations in the workspace.
|
|
113
|
+
"""
|
|
114
|
+
from_path, to_path = assemble_path_args(path, new_path)
|
|
115
|
+
to_path.parent.mkdir(parents=True, exist_ok=True)
|
|
116
|
+
os.rename(from_path, to_path)
|
|
117
|
+
|
|
118
|
+
print_status(f"Renamed: {fmt_loc(from_path)} -> {fmt_loc(to_path)}")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@kash_command
|
|
122
|
+
def copy(*paths: str) -> None:
|
|
123
|
+
"""
|
|
124
|
+
Copy the items at the given paths to the target path.
|
|
125
|
+
"""
|
|
126
|
+
if len(paths) < 2:
|
|
127
|
+
raise InvalidInput("Must provide at least one source path and a target path")
|
|
128
|
+
|
|
129
|
+
src_paths = [resolve_path_arg(path) for path in paths[:-1] if path]
|
|
130
|
+
dest_path = resolve_path_arg(paths[-1])
|
|
131
|
+
|
|
132
|
+
if len(src_paths) == 1 and dest_path.is_dir():
|
|
133
|
+
dest_path = dest_path / src_paths[0].name
|
|
134
|
+
elif len(src_paths) > 1 and not dest_path.is_dir():
|
|
135
|
+
raise InvalidInput(f"Cannot copy multiple files to a file target: {dest_path}")
|
|
136
|
+
|
|
137
|
+
for src_path in src_paths:
|
|
138
|
+
copyfile_atomic(src_path, dest_path, make_parents=True)
|
|
139
|
+
|
|
140
|
+
print_status(
|
|
141
|
+
f"Copied:\n{fmt_lines(fmt_loc(p) for p in src_paths)}\n->\n{fmt_lines([fmt_loc(dest_path)])}",
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@kash_command
|
|
146
|
+
def trash(*paths: str) -> None:
|
|
147
|
+
"""
|
|
148
|
+
Trash the items at the given paths. Uses OS-native trash or recycle bin on Mac, Windows, or Linux.
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
resolved_paths = assemble_path_args(*paths)
|
|
152
|
+
native_trash(*resolved_paths)
|
|
153
|
+
print_status(f"Deleted (check trash or recycling bin to recover):\n{fmt_lines(resolved_paths)}")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@kash_command
|
|
157
|
+
def strip_frontmatter(*paths: str) -> None:
|
|
158
|
+
"""
|
|
159
|
+
Strip the frontmatter from the given files.
|
|
160
|
+
"""
|
|
161
|
+
input_paths = assemble_path_args(*paths)
|
|
162
|
+
|
|
163
|
+
for path in input_paths:
|
|
164
|
+
log.message("Stripping frontmatter from: %s", fmt_loc(path))
|
|
165
|
+
fmf_strip_frontmatter(path)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@kash_command
|
|
169
|
+
def tail_file(path: str, follow: bool = False) -> None:
|
|
170
|
+
"""
|
|
171
|
+
Tail a file. With colorization using bat if available, otherwise using less.
|
|
172
|
+
If `follow` is True, follows the file as it grows.
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
native_tail_file(path, follow=follow)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@kash_command
|
|
179
|
+
def follow_file(path: str) -> None:
|
|
180
|
+
"""
|
|
181
|
+
Same as `tail_file --follow`.
|
|
182
|
+
"""
|
|
183
|
+
native_tail_file(path, follow=True)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import urllib.parse
|
|
2
|
+
|
|
3
|
+
from kash.exec import kash_command
|
|
4
|
+
from kash.shell.utils.native_utils import native_open_url
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@kash_command
|
|
8
|
+
def browser(url_or_query: str) -> None:
|
|
9
|
+
"""
|
|
10
|
+
Open a URL or query in the browser.
|
|
11
|
+
"""
|
|
12
|
+
native_open_url(url_or_query)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@kash_command
|
|
16
|
+
def web_search_google(query: str) -> None:
|
|
17
|
+
"""
|
|
18
|
+
Open browser with a Google for a query.
|
|
19
|
+
"""
|
|
20
|
+
native_open_url(f"https://www.google.com/search?q={urllib.parse.quote(query)}")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@kash_command
|
|
24
|
+
def web_search_duckduckgo(query: str) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Open browser with a DuckDuckGo search for a query.
|
|
27
|
+
"""
|
|
28
|
+
native_open_url(f"https://duckduckgo.com/?q={urllib.parse.quote(query)}")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@kash_command
|
|
32
|
+
def web_search_bing(query: str) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Open browser with a Bing search for a query.
|
|
35
|
+
"""
|
|
36
|
+
native_open_url(f"https://www.bing.com/search?q={urllib.parse.quote(query)}")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@kash_command
|
|
40
|
+
def web_search_youtube(query: str) -> None:
|
|
41
|
+
"""
|
|
42
|
+
Open browser with a YouTube search for a query.
|
|
43
|
+
"""
|
|
44
|
+
params = {"search_query": query}
|
|
45
|
+
url = f"https://www.youtube.com/results?{urllib.parse.urlencode(params)}"
|
|
46
|
+
native_open_url(url)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@kash_command
|
|
50
|
+
def web_search_amazon(query: str) -> None:
|
|
51
|
+
"""
|
|
52
|
+
Open browser with an Amazon search for a query.
|
|
53
|
+
"""
|
|
54
|
+
native_open_url(f"https://www.amazon.com/s?k={urllib.parse.quote(query)}")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@kash_command
|
|
58
|
+
def web_search_perplexity(query: str) -> None:
|
|
59
|
+
"""
|
|
60
|
+
Open browser with a Perplexity search for a query.
|
|
61
|
+
"""
|
|
62
|
+
native_open_url(f"https://www.perplexity.ai/search?q={urllib.parse.quote(query)}")
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
|
|
3
|
+
import rich
|
|
4
|
+
from flowmark import Wrap
|
|
5
|
+
from strif import single_line
|
|
6
|
+
|
|
7
|
+
from kash.config.logger import get_logger
|
|
8
|
+
from kash.config.text_styles import STYLE_HINT, STYLE_KEY
|
|
9
|
+
from kash.errors import InvalidInput
|
|
10
|
+
from kash.exec import kash_command
|
|
11
|
+
from kash.help import tldr_help
|
|
12
|
+
from kash.help.function_param_info import annotate_param_info
|
|
13
|
+
from kash.help.recommended_commands import RECOMMENDED_TLDR_COMMANDS
|
|
14
|
+
from kash.model.params_model import Param
|
|
15
|
+
from kash.shell.output.kerm_codes import IframePopover, TextTooltip
|
|
16
|
+
from kash.shell.output.shell_output import PrintHooks, console_pager, cprint, format_name_and_value
|
|
17
|
+
|
|
18
|
+
log = get_logger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@kash_command
|
|
22
|
+
def kerm_text_tooltip(text: str) -> None:
|
|
23
|
+
"""
|
|
24
|
+
Show a tooltip in the Kerm terminal.
|
|
25
|
+
"""
|
|
26
|
+
tooltip = TextTooltip(text=text)
|
|
27
|
+
print(tooltip.as_osc(), end="")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@kash_command
|
|
31
|
+
def kerm_iframe_popover(url: str) -> None:
|
|
32
|
+
"""
|
|
33
|
+
Show an iframe popover in the Kerm terminal.
|
|
34
|
+
"""
|
|
35
|
+
popover = IframePopover(url=url)
|
|
36
|
+
print(popover.as_osc(), end="")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@kash_command
|
|
40
|
+
def dump_tldr_snippets() -> None:
|
|
41
|
+
"""
|
|
42
|
+
Dev task to run occasionally: Dump a few hundred TLDR snippet examples to a
|
|
43
|
+
standard script file for use in kash tab completion.
|
|
44
|
+
"""
|
|
45
|
+
tldr_help.dump_all_tldr_snippets()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@kash_command
|
|
49
|
+
def debug_tldr() -> None:
|
|
50
|
+
"""
|
|
51
|
+
Debug task to dump TLDR snippets for a few commands.
|
|
52
|
+
"""
|
|
53
|
+
log.message("TLDR cache dir: %s", tldr_help._cache_dir)
|
|
54
|
+
log.message("TLDR cache timestamp file: %s", tldr_help._timestamp_file)
|
|
55
|
+
log.message("TLDR cache location: %s", tldr_help._cache_location("en"))
|
|
56
|
+
log.message("Should update TLDR cache: %s", tldr_help._should_update_cache())
|
|
57
|
+
|
|
58
|
+
with console_pager():
|
|
59
|
+
for command in RECOMMENDED_TLDR_COMMANDS:
|
|
60
|
+
cprint(f"{command}", style=STYLE_KEY)
|
|
61
|
+
cprint(f"description: {tldr_help.tldr_description(command)}")
|
|
62
|
+
cprint(f"help: {tldr_help.tldr_help(command)}")
|
|
63
|
+
cprint()
|
|
64
|
+
cprint("parsed snippets:")
|
|
65
|
+
for snippet in tldr_help.tldr_snippets(command):
|
|
66
|
+
cprint(f"# {snippet.comment}")
|
|
67
|
+
cprint(f"{snippet.command_line}")
|
|
68
|
+
cprint()
|
|
69
|
+
cprint()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@kash_command
|
|
73
|
+
def debug_completions() -> None:
|
|
74
|
+
"""
|
|
75
|
+
Dump info on all xonsh completers.
|
|
76
|
+
"""
|
|
77
|
+
from xonsh.built_ins import XSH
|
|
78
|
+
|
|
79
|
+
def name_of(completer: Callable) -> str:
|
|
80
|
+
module_name = completer.__module__ if hasattr(completer, "__module__") else "?"
|
|
81
|
+
if hasattr(completer, "__name__"):
|
|
82
|
+
return f"{module_name}.{completer.__name__}"
|
|
83
|
+
elif hasattr(completer, "__class__"):
|
|
84
|
+
return f"{module_name}.{completer.__class__.__name__}"
|
|
85
|
+
else:
|
|
86
|
+
return str(completer)
|
|
87
|
+
|
|
88
|
+
def to_str(completer: Callable) -> str:
|
|
89
|
+
return f"{name_of(completer)} (non_exclusive={getattr(completer, 'non_exclusive', False)})"
|
|
90
|
+
|
|
91
|
+
cprint(f"{len(XSH.completers)} completers registered:")
|
|
92
|
+
PrintHooks.spacer()
|
|
93
|
+
|
|
94
|
+
for completer in XSH.completers.values():
|
|
95
|
+
cprint(to_str(completer), text_wrap=Wrap.NONE, style=STYLE_KEY)
|
|
96
|
+
if completer.__doc__:
|
|
97
|
+
cprint(
|
|
98
|
+
f"{single_line(completer.__doc__)}", text_wrap=Wrap.WRAP_INDENT, style=STYLE_HINT
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@kash_command
|
|
103
|
+
def debug_help() -> None:
|
|
104
|
+
"""
|
|
105
|
+
Debug task to dump help for all commands and actions.
|
|
106
|
+
"""
|
|
107
|
+
from kash.docs.all_docs import all_docs
|
|
108
|
+
|
|
109
|
+
cprint(f"all_docs: {all_docs}", style=STYLE_KEY)
|
|
110
|
+
|
|
111
|
+
cprint("\nhelp_topics:", style=STYLE_KEY)
|
|
112
|
+
|
|
113
|
+
for topic in all_docs.help_topics.__dict__.keys():
|
|
114
|
+
cprint(f"{topic}", text_wrap=Wrap.NONE)
|
|
115
|
+
|
|
116
|
+
cprint("\nfaqs:")
|
|
117
|
+
for faq in all_docs.faqs:
|
|
118
|
+
cprint(f"{faq}", text_wrap=Wrap.NONE)
|
|
119
|
+
|
|
120
|
+
cprint("\ncustom_command_infos:", style=STYLE_KEY)
|
|
121
|
+
for cmd in all_docs.custom_command_infos:
|
|
122
|
+
cprint(f"{cmd}", text_wrap=Wrap.NONE)
|
|
123
|
+
|
|
124
|
+
cprint("\nstd_command_infos:", style=STYLE_KEY)
|
|
125
|
+
for cmd in all_docs.std_command_infos:
|
|
126
|
+
cprint(f"{cmd}", text_wrap=Wrap.NONE)
|
|
127
|
+
|
|
128
|
+
cprint("\naction_infos:", style=STYLE_KEY)
|
|
129
|
+
for action in all_docs.action_infos:
|
|
130
|
+
cprint(f"{action}", text_wrap=Wrap.NONE)
|
|
131
|
+
|
|
132
|
+
# cprint("\nrecipe_snippets:", style=STYLE_KEY)
|
|
133
|
+
# for snippet in all_docs.recipe_snippets:
|
|
134
|
+
# cprint(f"{snippet}", text_wrap=Wrap.NONE)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@kash_command
|
|
138
|
+
def debug_command(command_or_action: str) -> None:
|
|
139
|
+
"""
|
|
140
|
+
Debug task to dump info on a command or action.
|
|
141
|
+
"""
|
|
142
|
+
from kash.exec.action_registry import get_all_actions_defaults
|
|
143
|
+
from kash.exec.command_registry import get_all_commands
|
|
144
|
+
|
|
145
|
+
def dump_params(param_info: list[Param]) -> None:
|
|
146
|
+
for param in param_info:
|
|
147
|
+
cprint(format_name_and_value(param.display, str(param)))
|
|
148
|
+
cprint()
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
command = get_all_commands()[command_or_action]
|
|
152
|
+
param_info = annotate_param_info(command)
|
|
153
|
+
cprint(format_name_and_value("Command", f"`{command_or_action}`"))
|
|
154
|
+
cprint(format_name_and_value("Docstring", command.__doc__ or ""))
|
|
155
|
+
cprint()
|
|
156
|
+
dump_params(param_info)
|
|
157
|
+
cprint()
|
|
158
|
+
rich.inspect(command)
|
|
159
|
+
except KeyError:
|
|
160
|
+
try:
|
|
161
|
+
action = get_all_actions_defaults()[command_or_action]
|
|
162
|
+
cprint(format_name_and_value("Action", f"`{command_or_action}`"))
|
|
163
|
+
cprint(format_name_and_value("Description", action.description or ""))
|
|
164
|
+
cprint()
|
|
165
|
+
dump_params(list(action.params))
|
|
166
|
+
cprint()
|
|
167
|
+
rich.inspect(action.tool_json_schema(), title="JSON Schema", docs=False)
|
|
168
|
+
cprint()
|
|
169
|
+
rich.inspect(action, title="Action Instance", docs=False)
|
|
170
|
+
except KeyError:
|
|
171
|
+
raise InvalidInput(f"No info found for {command_or_action}")
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@kash_command
|
|
175
|
+
def reload_system() -> None:
|
|
176
|
+
"""
|
|
177
|
+
Experimental! Reload the kash package and all its submodules. Also restarts
|
|
178
|
+
the local the local server. Not perfect! But sometimes useful for development.
|
|
179
|
+
"""
|
|
180
|
+
import kash
|
|
181
|
+
from kash.local_server.local_server import restart_local_server
|
|
182
|
+
from kash.utils.common.import_utils import recursive_reload
|
|
183
|
+
|
|
184
|
+
module = kash
|
|
185
|
+
exclude = ["kash.xontrib.kash_extension"] # Don't reload the kash initialization.
|
|
186
|
+
|
|
187
|
+
def filter_func(name: str) -> bool:
|
|
188
|
+
if exclude:
|
|
189
|
+
for excluded_module in exclude:
|
|
190
|
+
if name == excluded_module or name.startswith(excluded_module + "."):
|
|
191
|
+
log.info("Excluding reloading module: %s", name)
|
|
192
|
+
return False
|
|
193
|
+
return True
|
|
194
|
+
|
|
195
|
+
package_names = recursive_reload(module, filter_func=filter_func)
|
|
196
|
+
log.info("Reloaded modules: %s", ", ".join(package_names))
|
|
197
|
+
log.message("Reloaded %s modules from %s.", len(package_names), module.__name__)
|
|
198
|
+
|
|
199
|
+
restart_local_server()
|
|
200
|
+
|
|
201
|
+
# TODO Re-register commands and actions.
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@kash_command
|
|
205
|
+
def reload_commands_and_actions() -> None:
|
|
206
|
+
"""
|
|
207
|
+
Reload all commands and actions. This can be needed to register newly imported
|
|
208
|
+
Python files that define commands or actions with the shell.
|
|
209
|
+
"""
|
|
210
|
+
from kash.xonsh_custom.customize_xonsh import reload_shell_commands_and_actions
|
|
211
|
+
from kash.xonsh_custom.shell_load_commands import log_command_action_info
|
|
212
|
+
|
|
213
|
+
reload_shell_commands_and_actions()
|
|
214
|
+
log_command_action_info()
|