kash-shell 0.3.18__tar.gz → 0.3.20__tar.gz

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 (309) hide show
  1. {kash_shell-0.3.18 → kash_shell-0.3.20}/.copier-answers.yml +1 -1
  2. {kash_shell-0.3.18 → kash_shell-0.3.20}/.cursor/rules/python.mdc +16 -11
  3. {kash_shell-0.3.18 → kash_shell-0.3.20}/.github/workflows/ci.yml +1 -1
  4. {kash_shell-0.3.18 → kash_shell-0.3.20}/.github/workflows/publish.yml +3 -3
  5. {kash_shell-0.3.18 → kash_shell-0.3.20}/Makefile +4 -1
  6. {kash_shell-0.3.18 → kash_shell-0.3.20}/PKG-INFO +2 -2
  7. {kash_shell-0.3.18 → kash_shell-0.3.20}/README.md +1 -1
  8. {kash_shell-0.3.18 → kash_shell-0.3.20}/development.md +12 -1
  9. kash_shell-0.3.18/src/kash/actions/core/markdownify.py → kash_shell-0.3.20/src/kash/actions/core/markdownify_html.py +3 -6
  10. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/workspace/workspace_commands.py +10 -88
  11. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/markdown/topics/a1_what_is_kash.md +1 -1
  12. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/__init__.py +3 -0
  13. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/action_exec.py +2 -2
  14. kash_shell-0.3.18/src/kash/exec/fetch_url_metadata.py → kash_shell-0.3.20/src/kash/exec/fetch_url_items.py +42 -14
  15. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/file_storage/file_store.py +7 -1
  16. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/file_storage/store_filenames.py +4 -0
  17. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/help/function_param_info.py +1 -1
  18. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/help/help_pages.py +1 -1
  19. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/help/help_printing.py +1 -1
  20. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/model/items_model.py +18 -3
  21. kash_shell-0.3.20/src/kash/utils/common/parse_docstring.py +347 -0
  22. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_content/web_extract.py +34 -15
  23. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_content/web_page_model.py +10 -1
  24. kash_shell-0.3.18/src/kash/help/docstring_utils.py +0 -111
  25. {kash_shell-0.3.18 → kash_shell-0.3.20}/.cursor/rules/general.mdc +0 -0
  26. {kash_shell-0.3.18 → kash_shell-0.3.20}/.env.template +0 -0
  27. {kash_shell-0.3.18 → kash_shell-0.3.20}/.gitignore +0 -0
  28. {kash_shell-0.3.18 → kash_shell-0.3.20}/LICENSE +0 -0
  29. {kash_shell-0.3.18 → kash_shell-0.3.20}/devtools/generate_readme.xsh +0 -0
  30. {kash_shell-0.3.18 → kash_shell-0.3.20}/devtools/lint.py +0 -0
  31. {kash_shell-0.3.18 → kash_shell-0.3.20}/devtools/profile_main.py +0 -0
  32. {kash_shell-0.3.18 → kash_shell-0.3.20}/installation.md +0 -0
  33. {kash_shell-0.3.18 → kash_shell-0.3.20}/publishing.md +0 -0
  34. {kash_shell-0.3.18 → kash_shell-0.3.20}/pyproject.toml +0 -0
  35. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/__init__.py +0 -0
  36. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/__main__.py +0 -0
  37. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/actions/__init__.py +0 -0
  38. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/actions/core/assistant_chat.py +0 -0
  39. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/actions/core/chat.py +0 -0
  40. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/actions/core/format_markdown_template.py +0 -0
  41. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/actions/core/minify_html.py +0 -0
  42. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/actions/core/readability.py +0 -0
  43. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/actions/core/render_as_html.py +0 -0
  44. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/actions/core/show_webpage.py +0 -0
  45. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/actions/core/strip_html.py +0 -0
  46. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/actions/core/summarize_as_bullets.py +0 -0
  47. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/actions/core/tabbed_webpage_config.py +0 -0
  48. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/actions/core/tabbed_webpage_generate.py +0 -0
  49. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/actions/meta/write_instructions.py +0 -0
  50. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/actions/meta/write_new_action.py +0 -0
  51. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/__init__.py +0 -0
  52. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/base/basic_file_commands.py +0 -0
  53. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/base/browser_commands.py +0 -0
  54. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/base/debug_commands.py +0 -0
  55. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/base/diff_commands.py +0 -0
  56. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/base/files_command.py +0 -0
  57. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/base/general_commands.py +0 -0
  58. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/base/logs_commands.py +0 -0
  59. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/base/model_commands.py +0 -0
  60. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/base/reformat_command.py +0 -0
  61. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/base/search_command.py +0 -0
  62. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/base/show_command.py +0 -0
  63. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/extras/parse_uv_lock.py +0 -0
  64. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/extras/utils_commands.py +0 -0
  65. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/help/assistant_commands.py +0 -0
  66. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/help/doc_commands.py +0 -0
  67. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/help/help_commands.py +0 -0
  68. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/help/logo.py +0 -0
  69. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/help/welcome.py +0 -0
  70. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/commands/workspace/selection_commands.py +0 -0
  71. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/config/__init__.py +0 -0
  72. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/config/capture_output.py +0 -0
  73. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/config/colors.py +0 -0
  74. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/config/env_settings.py +0 -0
  75. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/config/init.py +0 -0
  76. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/config/lazy_imports.py +0 -0
  77. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/config/logger.py +0 -0
  78. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/config/logger_basic.py +0 -0
  79. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/config/logo.txt +0 -0
  80. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/config/server_config.py +0 -0
  81. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/config/settings.py +0 -0
  82. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/config/setup.py +0 -0
  83. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/config/suppress_warnings.py +0 -0
  84. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/config/text_styles.py +0 -0
  85. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/__init__.py +0 -0
  86. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/all_docs.py +0 -0
  87. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/load_actions_info.py +0 -0
  88. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/load_api_docs.py +0 -0
  89. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/load_help_topics.py +0 -0
  90. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/load_source_code.py +0 -0
  91. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/markdown/api_docs_template.md +0 -0
  92. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/markdown/assistant_instructions_template.md +0 -0
  93. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/markdown/readme_template.md +0 -0
  94. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/markdown/topics/a2_installation.md +0 -0
  95. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/markdown/topics/a3_getting_started.md +0 -0
  96. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/markdown/topics/a4_elements.md +0 -0
  97. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/markdown/topics/a5_tips_for_use_with_other_tools.md +0 -0
  98. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/markdown/topics/b0_philosophy_of_kash.md +0 -0
  99. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/markdown/topics/b1_kash_overview.md +0 -0
  100. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/markdown/topics/b2_workspace_and_file_formats.md +0 -0
  101. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/markdown/topics/b3_modern_shell_tool_recommendations.md +0 -0
  102. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/markdown/topics/b4_faq.md +0 -0
  103. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/markdown/warning.md +0 -0
  104. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs/markdown/welcome.md +0 -0
  105. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs_base/docs_base.py +0 -0
  106. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs_base/load_custom_command_info.py +0 -0
  107. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs_base/load_faqs.py +0 -0
  108. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs_base/load_recipe_snippets.py +0 -0
  109. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs_base/recipes/general_system_commands.sh +0 -0
  110. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs_base/recipes/python_dev_commands.sh +0 -0
  111. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/docs_base/recipes/tldr_standard_commands.sh +0 -0
  112. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/embeddings/cosine.py +0 -0
  113. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/embeddings/embeddings.py +0 -0
  114. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/embeddings/text_similarity.py +0 -0
  115. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/action_decorators.py +0 -0
  116. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/action_registry.py +0 -0
  117. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/combiners.py +0 -0
  118. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/command_exec.py +0 -0
  119. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/command_registry.py +0 -0
  120. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/history.py +0 -0
  121. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/importing.py +0 -0
  122. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/llm_transforms.py +0 -0
  123. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/precondition_checks.py +0 -0
  124. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/precondition_registry.py +0 -0
  125. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/preconditions.py +0 -0
  126. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/resolve_args.py +0 -0
  127. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/runtime_settings.py +0 -0
  128. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec/shell_callable_action.py +0 -0
  129. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec_model/__init__.py +0 -0
  130. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec_model/args_model.py +0 -0
  131. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec_model/commands_model.py +0 -0
  132. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec_model/script_model.py +0 -0
  133. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/exec_model/shell_model.py +0 -0
  134. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/file_storage/__init__.py +0 -0
  135. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/file_storage/item_file_format.py +0 -0
  136. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/file_storage/metadata_dirs.py +0 -0
  137. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/file_storage/persisted_yaml.py +0 -0
  138. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/file_storage/store_cache_warmer.py +0 -0
  139. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/help/__init__.py +0 -0
  140. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/help/assistant.py +0 -0
  141. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/help/assistant_instructions.py +0 -0
  142. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/help/assistant_output.py +0 -0
  143. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/help/help_embeddings.py +0 -0
  144. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/help/help_lookups.py +0 -0
  145. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/help/help_types.py +0 -0
  146. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/help/recommended_commands.py +0 -0
  147. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/help/tldr_help.py +0 -0
  148. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/llm_utils/__init__.py +0 -0
  149. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/llm_utils/clean_headings.py +0 -0
  150. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/llm_utils/custom_sliding_transforms.py +0 -0
  151. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/llm_utils/fuzzy_parsing.py +0 -0
  152. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/llm_utils/init_litellm.py +0 -0
  153. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/llm_utils/llm_api_keys.py +0 -0
  154. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/llm_utils/llm_completion.py +0 -0
  155. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/llm_utils/llm_features.py +0 -0
  156. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/llm_utils/llm_messages.py +0 -0
  157. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/llm_utils/llm_names.py +0 -0
  158. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/llm_utils/llms.py +0 -0
  159. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/local_server/__init__.py +0 -0
  160. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/local_server/local_server.py +0 -0
  161. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/local_server/local_server_commands.py +0 -0
  162. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/local_server/local_server_routes.py +0 -0
  163. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/local_server/local_url_formatters.py +0 -0
  164. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/local_server/port_tools.py +0 -0
  165. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/local_server/rich_html_template.py +0 -0
  166. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/mcp/__init__.py +0 -0
  167. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/mcp/mcp_cli.py +0 -0
  168. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/mcp/mcp_main.py +0 -0
  169. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/mcp/mcp_server_commands.py +0 -0
  170. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/mcp/mcp_server_routes.py +0 -0
  171. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/mcp/mcp_server_sse.py +0 -0
  172. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/mcp/mcp_server_stdio.py +0 -0
  173. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/media_base/__init__.py +0 -0
  174. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/media_base/audio_processing.py +0 -0
  175. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/media_base/media_cache.py +0 -0
  176. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/media_base/media_services.py +0 -0
  177. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/media_base/media_tools.py +0 -0
  178. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/media_base/services/local_file_media.py +0 -0
  179. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/media_base/timestamp_citations.py +0 -0
  180. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/media_base/transcription_deepgram.py +0 -0
  181. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/media_base/transcription_format.py +0 -0
  182. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/media_base/transcription_whisper.py +0 -0
  183. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/model/__init__.py +0 -0
  184. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/model/actions_model.py +0 -0
  185. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/model/assistant_response_model.py +0 -0
  186. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/model/compound_actions_model.py +0 -0
  187. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/model/concept_model.py +0 -0
  188. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/model/exec_model.py +0 -0
  189. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/model/graph_model.py +0 -0
  190. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/model/language_list.py +0 -0
  191. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/model/llm_actions_model.py +0 -0
  192. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/model/media_model.py +0 -0
  193. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/model/operations_model.py +0 -0
  194. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/model/params_model.py +0 -0
  195. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/model/paths_model.py +0 -0
  196. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/model/preconditions_model.py +0 -0
  197. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/__init__.py +0 -0
  198. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/completions/completion_scoring.py +0 -0
  199. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/completions/completion_types.py +0 -0
  200. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/completions/shell_completions.py +0 -0
  201. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/file_icons/color_for_format.py +0 -0
  202. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/file_icons/nerd_icons.py +0 -0
  203. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/input/__init__.py +0 -0
  204. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/input/input_prompts.py +0 -0
  205. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/input/inquirer_settings.py +0 -0
  206. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/input/param_inputs.py +0 -0
  207. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/input/shell_confirm.py +0 -0
  208. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/output/__init__.py +0 -0
  209. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/output/kerm_code_utils.py +0 -0
  210. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/output/kerm_codes.py +0 -0
  211. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/output/kmarkdown.py +0 -0
  212. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/output/shell_formatting.py +0 -0
  213. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/output/shell_output.py +0 -0
  214. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/shell_main.py +0 -0
  215. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/ui/__init__.py +0 -0
  216. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/ui/shell_results.py +0 -0
  217. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/ui/shell_syntax.py +0 -0
  218. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/utils/exception_printing.py +0 -0
  219. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/utils/native_utils.py +0 -0
  220. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/utils/shell_function_wrapper.py +0 -0
  221. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/shell/version.py +0 -0
  222. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/__init__.py +0 -0
  223. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/common/__init__.py +0 -0
  224. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/common/format_utils.py +0 -0
  225. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/common/function_inspect.py +0 -0
  226. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/common/import_utils.py +0 -0
  227. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/common/lazyobject.py +0 -0
  228. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/common/obj_replace.py +0 -0
  229. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/common/parse_key_vals.py +0 -0
  230. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/common/parse_shell_args.py +0 -0
  231. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/common/stack_traces.py +0 -0
  232. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/common/task_stack.py +0 -0
  233. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/common/testing.py +0 -0
  234. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/common/type_utils.py +0 -0
  235. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/common/uniquifier.py +0 -0
  236. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/common/url.py +0 -0
  237. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/common/url_slice.py +0 -0
  238. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/errors.py +0 -0
  239. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/file_formats/chat_format.py +0 -0
  240. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/file_utils/__init__.py +0 -0
  241. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/file_utils/dir_info.py +0 -0
  242. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/file_utils/file_ext.py +0 -0
  243. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/file_utils/file_formats.py +0 -0
  244. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/file_utils/file_formats_model.py +0 -0
  245. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/file_utils/file_sort_filter.py +0 -0
  246. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/file_utils/file_walk.py +0 -0
  247. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/file_utils/filename_parsing.py +0 -0
  248. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/file_utils/ignore_files.py +0 -0
  249. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/file_utils/mtime_cache.py +0 -0
  250. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/file_utils/path_utils.py +0 -0
  251. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/lang_utils/__init__.py +0 -0
  252. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/lang_utils/capitalization.py +0 -0
  253. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/rich_custom/__init__.py +0 -0
  254. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/rich_custom/ansi_cell_len.py +0 -0
  255. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/rich_custom/rich_char_transform.py +0 -0
  256. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/rich_custom/rich_indent.py +0 -0
  257. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/rich_custom/rich_markdown_fork.py +0 -0
  258. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/text_handling/doc_normalization.py +0 -0
  259. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/text_handling/escape_html_tags.py +0 -0
  260. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/text_handling/markdown_render.py +0 -0
  261. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/text_handling/markdown_utils.py +0 -0
  262. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/text_handling/markdownify_utils.py +0 -0
  263. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/utils/text_handling/unified_diffs.py +0 -0
  264. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_content/canon_url.py +0 -0
  265. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_content/dir_store.py +0 -0
  266. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_content/file_cache_utils.py +0 -0
  267. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_content/file_processing.py +0 -0
  268. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_content/local_file_cache.py +0 -0
  269. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_content/web_extract_justext.py +0 -0
  270. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_content/web_extract_readabilipy.py +0 -0
  271. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_content/web_fetch.py +0 -0
  272. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_gen/__init__.py +0 -0
  273. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_gen/simple_webpage.py +0 -0
  274. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_gen/tabbed_webpage.py +0 -0
  275. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_gen/template_render.py +0 -0
  276. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_gen/templates/base_styles.css.jinja +0 -0
  277. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_gen/templates/base_webpage.html.jinja +0 -0
  278. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_gen/templates/components/toc_scripts.js.jinja +0 -0
  279. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_gen/templates/components/toc_styles.css.jinja +0 -0
  280. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_gen/templates/components/tooltip_scripts.js.jinja +0 -0
  281. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_gen/templates/components/tooltip_styles.css.jinja +0 -0
  282. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_gen/templates/content_styles.css.jinja +0 -0
  283. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_gen/templates/explain_view.html.jinja +0 -0
  284. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_gen/templates/item_view.html.jinja +0 -0
  285. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_gen/templates/simple_webpage.html.jinja +0 -0
  286. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/web_gen/templates/tabbed_webpage.html.jinja +0 -0
  287. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/workspaces/__init__.py +0 -0
  288. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/workspaces/param_state.py +0 -0
  289. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/workspaces/selections.py +0 -0
  290. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/workspaces/source_items.py +0 -0
  291. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/workspaces/workspace_dirs.py +0 -0
  292. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/workspaces/workspace_output.py +0 -0
  293. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/workspaces/workspace_registry.py +0 -0
  294. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/workspaces/workspaces.py +0 -0
  295. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/xonsh_custom/command_nl_utils.py +0 -0
  296. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/xonsh_custom/custom_shell.py +0 -0
  297. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/xonsh_custom/customize_prompt.py +0 -0
  298. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/xonsh_custom/load_into_xonsh.py +0 -0
  299. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/xonsh_custom/shell_load_commands.py +0 -0
  300. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/xonsh_custom/shell_which.py +0 -0
  301. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/xonsh_custom/xonsh_completers.py +0 -0
  302. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/xonsh_custom/xonsh_env.py +0 -0
  303. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/xonsh_custom/xonsh_keybindings.py +0 -0
  304. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/xonsh_custom/xonsh_modern_tools.py +0 -0
  305. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/xonsh_custom/xonsh_ranking_completer.py +0 -0
  306. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/xontrib/fnm.py +0 -0
  307. {kash_shell-0.3.18 → kash_shell-0.3.20}/src/kash/xontrib/kash_extension.py +0 -0
  308. {kash_shell-0.3.18 → kash_shell-0.3.20}/tests/test_shell.py +0 -0
  309. {kash_shell-0.3.18 → kash_shell-0.3.20}/uv.lock +0 -0
@@ -1,5 +1,5 @@
1
1
  # Changes here will be overwritten by Copier. Do not edit manually.
2
- _commit: v0.2.10
2
+ _commit: v0.2.13
3
3
  _src_path: gh:jlevy/simple-modern-uv
4
4
  package_author_email: joshua@cal.berkeley.edu
5
5
  package_author_name: Joshua Levy
@@ -16,10 +16,10 @@ Always use full type annotations, generics, and other modern practices.
16
16
 
17
17
  ## Project Setup and Developer Workflows
18
18
 
19
- - This project has a modern pyproject.toml file and a Makefile.
20
- Be sure you read and understand both.
19
+ - Important: BE SURE you read and understand the project setup by reading the
20
+ pyproject.toml file and the Makefile.
21
21
 
22
- - Use uv for running all code and managing dependencies.
22
+ - ALWAYS use uv for running all code and managing dependencies.
23
23
  Never use direct `pip` or `python` commands.
24
24
 
25
25
  - Use modern uv commands: `uv sync`, `uv run ...`, etc.
@@ -56,10 +56,13 @@ Always use full type annotations, generics, and other modern practices.
56
56
  - Be sure to resolve the pyright (basedpyright) linter errors as you develop and make
57
57
  changes.
58
58
 
59
- - If type checker errors are hard to resolve, check pyproject.toml to see if they are an
60
- excessively pedantic error.
61
- In special cases you may consider disabling it globally it in pyproject.toml but you
62
- *must ask for confirmation* from the user before doing this.
59
+ - If type checker errors are hard to resolve, you may add a comment `# pyright: ignore`
60
+ to disable Pyright warnings or errors but ONLY if you know they are not a real problem
61
+ and are difficult to fix.
62
+
63
+ - In special cases you may consider disabling it globally it in pyproject.toml but YOU
64
+ MUST ASK FOR CONFIRMATION from the user before globally disabling lint or type checker
65
+ rules.
63
66
 
64
67
  - Never change an existing comment, pydoc, or a log statement, unless it is directly
65
68
  fixing the issue you are changing, or the user has asked you to clean up the code.
@@ -204,8 +207,9 @@ Always use full type annotations, generics, and other modern practices.
204
207
  - Docstrings *should* mention any key rationale or pitfalls when using the class or
205
208
  function.
206
209
 
207
- - Don't add pydocs that just repeat information obvious from the function, variable
208
- names, or concise description.
210
+ - Avoid obvious or repetitive docstrings.
211
+ Do NOT add pydocs that just repeat in English facts that are obvious from the function
212
+ name, variable name, or types.
209
213
  That is silly and obvious and makes the code longer for no reason.
210
214
 
211
215
  - Do NOT list args and return values if they're obvious.
@@ -220,8 +224,9 @@ Always use full type annotations, generics, and other modern practices.
220
224
 
221
225
  ## General Clean Coding Practices
222
226
 
223
- - When writing a class DO NOT blindly make delegation methods for consistency.
224
- For exmaple DO NOT write methods like this:
227
+ - Avoid writing trivial wrapper functions.
228
+ For example, when writing a class DO NOT blindly make delegation methods around public
229
+ member variables. DO NOT write methods like this:
225
230
  ```python
226
231
  def reassemble(self) -> str:
227
232
  """Call the original reassemble method."""
@@ -42,7 +42,7 @@ jobs:
42
42
  uses: astral-sh/setup-uv@v5
43
43
  with:
44
44
  # Update this as needed:
45
- version: "0.7.11"
45
+ version: "0.7.13"
46
46
  enable-cache: true
47
47
  python-version: ${{ matrix.python-version }}
48
48
 
@@ -25,7 +25,7 @@ jobs:
25
25
  - name: Install uv (official Astral action)
26
26
  uses: astral-sh/setup-uv@v5
27
27
  with:
28
- version: "0.7.11"
28
+ version: "0.7.13"
29
29
  enable-cache: true
30
30
  python-version: "3.12"
31
31
 
@@ -43,5 +43,5 @@ jobs:
43
43
  - name: Build package
44
44
  run: uv build
45
45
 
46
- - name: Publish to PyPI (using uv)
47
- uses: pypa/gh-action-pypi-publish@release/v1
46
+ - name: Publish to PyPI
47
+ run: uv publish --trusted-publishing always
@@ -18,7 +18,10 @@ test:
18
18
  uv run pytest
19
19
 
20
20
  test-full:
21
- ENABLE_TESTS_LLM=1 ENABLE_TESTS_INTEGRATION=1 uv run pytest
21
+ ENABLE_TESTS_ONLINE=1 ENABLE_TESTS_INTEGRATION=1 uv run pytest
22
+
23
+ run: install lint test
24
+ uv run kash
22
25
 
23
26
  upgrade:
24
27
  uv sync --upgrade --all-extras --dev
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kash-shell
3
- Version: 0.3.18
3
+ Version: 0.3.20
4
4
  Summary: The knowledge agent shell (core)
5
5
  Project-URL: Repository, https://github.com/jlevy/kash-shell
6
6
  Author-email: Joshua Levy <joshua@cal.berkeley.edu>
@@ -115,7 +115,7 @@ the Python framework, a few core utilities, and the Kash command-line shell.
115
115
  Additional actions for handling more complex tasks like converting documents and
116
116
  transcribing, researching, or annotating videos, are in the
117
117
  [kash-docs](https://github.com/jlevy/kash-docs) and
118
- [kash-media](https://github.com/jlevy/kash-docs) packages, all available on PyPI and
118
+ [kash-media](https://github.com/jlevy/kash-media) packages, all available on PyPI and
119
119
  quick to install via uv.
120
120
 
121
121
  ### Key Concepts
@@ -43,7 +43,7 @@ the Python framework, a few core utilities, and the Kash command-line shell.
43
43
  Additional actions for handling more complex tasks like converting documents and
44
44
  transcribing, researching, or annotating videos, are in the
45
45
  [kash-docs](https://github.com/jlevy/kash-docs) and
46
- [kash-media](https://github.com/jlevy/kash-docs) packages, all available on PyPI and
46
+ [kash-media](https://github.com/jlevy/kash-media) packages, all available on PyPI and
47
47
  quick to install via uv.
48
48
 
49
49
  ### Key Concepts
@@ -21,7 +21,7 @@ The `Makefile` simply offers shortcuts to `uv` commands for developer convenienc
21
21
  # including dev dependencies and optional dependencies.
22
22
  make install
23
23
 
24
- # Run uv sync, lint, and test:
24
+ # Run uv sync, lint, and test (and also generate agent rules):
25
25
  make
26
26
 
27
27
  # Build wheel:
@@ -66,6 +66,17 @@ source .venv/bin/activate
66
66
 
67
67
  See [uv docs](https://docs.astral.sh/uv/) for details.
68
68
 
69
+ ## Agent Rules
70
+
71
+ See [.cursor/rules](.cursor/rules) for agent rules.
72
+ These are written for [Cursor](https://www.cursor.com/) but are also used by other
73
+ agents because the Makefile will generate `CLAUDE.md` and `AGENTS.md` from the same
74
+ rules.
75
+
76
+ ```shell
77
+ make agent-rules
78
+ ```
79
+
69
80
  ## IDE setup
70
81
 
71
82
  If you use VSCode or a fork like Cursor or Windsurf, you can install the following
@@ -11,13 +11,10 @@ from kash.web_content.web_extract_readabilipy import extract_text_readabilipy
11
11
  log = get_logger(__name__)
12
12
 
13
13
 
14
- @kash_action(
15
- precondition=is_url_resource | has_html_body,
16
- mcp_tool=True,
17
- )
18
- def markdownify(item: Item) -> Item:
14
+ @kash_action(precondition=is_url_resource | has_html_body, mcp_tool=True)
15
+ def markdownify_html(item: Item) -> Item:
19
16
  """
20
- Converts a URL or raw HTML item to Markdown, fetching with the content
17
+ Converts raw HTML or the URL of an HTML page to Markdown, fetching with the content
21
18
  cache if needed. Also uses readability to clean up the HTML.
22
19
  """
23
20
 
@@ -23,14 +23,12 @@ from kash.exec import (
23
23
  resolve_locator_arg,
24
24
  )
25
25
  from kash.exec.action_registry import get_all_actions_defaults
26
- from kash.exec.fetch_url_metadata import fetch_url_metadata
26
+ from kash.exec.fetch_url_items import fetch_url_item
27
27
  from kash.exec.precondition_checks import actions_matching_paths
28
28
  from kash.exec.precondition_registry import get_all_preconditions
29
- from kash.exec.preconditions import is_url_resource
30
29
  from kash.exec_model.shell_model import ShellResult
31
30
  from kash.local_server.local_url_formatters import local_url_formatter
32
31
  from kash.media_base import media_tools
33
- from kash.media_base.media_services import is_media_url
34
32
  from kash.model.items_model import Item, ItemType
35
33
  from kash.model.params_model import GLOBAL_PARAMS
36
34
  from kash.model.paths_model import StorePath, fmt_store_path
@@ -54,12 +52,11 @@ from kash.utils.common.format_utils import fmt_loc
54
52
  from kash.utils.common.obj_replace import remove_values
55
53
  from kash.utils.common.parse_key_vals import parse_key_value
56
54
  from kash.utils.common.type_utils import not_none
57
- from kash.utils.common.url import Url, is_url, parse_http_url
55
+ from kash.utils.common.url import Url
58
56
  from kash.utils.errors import InvalidInput
59
57
  from kash.utils.file_formats.chat_format import tail_chat_history
60
58
  from kash.utils.file_utils.dir_info import is_nonempty_dir
61
59
  from kash.utils.file_utils.file_formats_model import Format
62
- from kash.utils.text_handling.doc_normalization import can_normalize
63
60
  from kash.web_content.file_cache_utils import cache_file
64
61
  from kash.workspaces import (
65
62
  current_ws,
@@ -189,85 +186,6 @@ def cache_content(*urls_or_paths: str, refetch: bool = False) -> None:
189
186
  PrintHooks.spacer()
190
187
 
191
188
 
192
- @kash_command
193
- def download(*urls_or_paths: str, refetch: bool = False, no_format: bool = False) -> ShellResult:
194
- """
195
- Download a URL or resource. Uses cached content if available, unless `refetch` is true.
196
- Inputs can be URLs or paths to URL resources.
197
- Creates both resource and document versions for text content.
198
-
199
- :param no_format: If true, do not also normalize Markdown content.
200
- """
201
- ws = current_ws()
202
- saved_paths = []
203
-
204
- for url_or_path in urls_or_paths:
205
- locator = resolve_locator_arg(url_or_path)
206
- url: Url | None = None
207
-
208
- # Get the URL from the locator
209
- if not isinstance(locator, Path) and is_url(locator):
210
- url = Url(locator)
211
- elif isinstance(locator, StorePath):
212
- url_item = ws.load(locator)
213
- if is_url_resource(url_item):
214
- url = url_item.url
215
-
216
- if not url:
217
- raise InvalidInput(f"Not a URL or URL resource: {fmt_loc(locator)}")
218
-
219
- # Handle media URLs differently
220
- if is_media_url(url):
221
- log.message(
222
- "URL is a media URL, so adding as a resource and will cache media: %s", fmt_loc(url)
223
- )
224
- store_path = ws.import_item(url, as_type=ItemType.resource, reimport=refetch)
225
- saved_paths.append(store_path)
226
- media_tools.cache_media(url)
227
- else:
228
- # Cache the content first
229
- expiration_sec = 0 if refetch else None
230
- cache_result = cache_file(url, expiration_sec=expiration_sec)
231
- original_filename = Path(parse_http_url(url).path).name
232
- mime_type = cache_result.content.headers and cache_result.content.headers.mime_type
233
-
234
- # Create a resource item
235
- resource_item = Item.from_external_path(
236
- cache_result.content.path,
237
- ItemType.resource,
238
- url=url,
239
- mime_type=mime_type,
240
- original_filename=original_filename,
241
- )
242
- # For initial content, do not format or add frontmatter.
243
- store_path = ws.save(resource_item, no_frontmatter=True, no_format=True)
244
- saved_paths.append(store_path)
245
- select(store_path)
246
-
247
- # Also create a doc version for text content if we want to normalize formatting.
248
- if resource_item.format and can_normalize(resource_item.format) and not no_format:
249
- doc_item = Item.from_external_path(
250
- cache_result.content.path,
251
- ItemType.doc,
252
- url=url,
253
- mime_type=mime_type,
254
- original_filename=original_filename,
255
- )
256
- # Now use default formatting and frontmatter.
257
- doc_store_path = ws.save(doc_item)
258
- saved_paths.append(doc_store_path)
259
- select(doc_store_path)
260
-
261
- print_status(
262
- "Downloaded %s %s:\n%s",
263
- len(saved_paths),
264
- plural("item", len(saved_paths)),
265
- fmt_lines(saved_paths),
266
- )
267
-
268
- return ShellResult(show_selection=True)
269
-
270
-
271
189
  @kash_command
272
190
  def history(max: int = 30, raw: bool = False) -> None:
273
191
  """
@@ -536,10 +454,14 @@ def save_clipboard(
536
454
 
537
455
 
538
456
  @kash_command
539
- def fetch_metadata(*files_or_urls: str, refetch: bool = False) -> ShellResult:
457
+ def fetch_url(*files_or_urls: str, refetch: bool = False) -> ShellResult:
540
458
  """
541
- Fetch metadata for the given URLs or resources. Imports new URLs and saves back
542
- the fetched metadata for existing resources.
459
+ Fetch content and metadata for the given URLs or resources, saving to the
460
+ current workspace.
461
+
462
+ Imports new URLs and saves back the fetched metadata for existing resources.
463
+ Also saves a resource item with the content of the URL, either HTML, text, or
464
+ of any other type.
543
465
 
544
466
  Skips items that already have a title and description, unless `refetch` is true.
545
467
  Skips (with a warning) items that are not URL resources.
@@ -552,7 +474,7 @@ def fetch_metadata(*files_or_urls: str, refetch: bool = False) -> ShellResult:
552
474
  store_paths = []
553
475
  for locator in locators:
554
476
  try:
555
- fetched_item = fetch_url_metadata(locator, refetch=refetch)
477
+ fetched_item = fetch_url_item(locator, refetch=refetch)
556
478
  store_paths.append(fetched_item.store_path)
557
479
  except InvalidInput as e:
558
480
  log.warning(
@@ -34,7 +34,7 @@ the Python framework, a few core utilities, and the Kash command-line shell.
34
34
  Additional actions for handling more complex tasks like converting documents and
35
35
  transcribing, researching, or annotating videos, are in the
36
36
  [kash-docs](https://github.com/jlevy/kash-docs) and
37
- [kash-media](https://github.com/jlevy/kash-docs) packages, all available on PyPI and
37
+ [kash-media](https://github.com/jlevy/kash-media) packages, all available on PyPI and
38
38
  quick to install via uv.
39
39
 
40
40
  ### Key Concepts
@@ -1,6 +1,7 @@
1
1
  from kash.exec.action_decorators import kash_action, kash_action_class
2
2
  from kash.exec.action_exec import SkipItem, prepare_action_input, run_action_with_shell_context
3
3
  from kash.exec.command_registry import kash_command
4
+ from kash.exec.fetch_url_items import fetch_url_item, fetch_url_item_content
4
5
  from kash.exec.importing import import_and_register
5
6
  from kash.exec.llm_transforms import llm_transform_item, llm_transform_str
6
7
  from kash.exec.precondition_registry import kash_precondition
@@ -21,6 +22,8 @@ __all__ = [
21
22
  "prepare_action_input",
22
23
  "run_action_with_shell_context",
23
24
  "kash_command",
25
+ "fetch_url_item",
26
+ "fetch_url_item_content",
24
27
  "kash_runtime",
25
28
  "current_runtime_settings",
26
29
  "import_and_register",
@@ -43,7 +43,7 @@ def prepare_action_input(*input_args: CommandArg, refetch: bool = False) -> Acti
43
43
  URL or file resources, either finding them in the workspace or importing them.
44
44
  Also fetches metadata for URLs if they don't already have title and description.
45
45
  """
46
- from kash.exec.fetch_url_metadata import fetch_url_item_metadata
46
+ from kash.exec.fetch_url_items import fetch_url_item_content
47
47
 
48
48
  ws = current_ws()
49
49
 
@@ -55,7 +55,7 @@ def prepare_action_input(*input_args: CommandArg, refetch: bool = False) -> Acti
55
55
  if input_items:
56
56
  log.message("Assembling metadata for input items:\n%s", fmt_lines(input_items))
57
57
  input_items = [
58
- fetch_url_item_metadata(item, refetch=refetch) if is_url_resource(item) else item
58
+ fetch_url_item_content(item, refetch=refetch) if is_url_resource(item) else item
59
59
  for item in input_items
60
60
  ]
61
61
 
@@ -11,7 +11,9 @@ from kash.utils.errors import InvalidInput
11
11
  log = get_logger(__name__)
12
12
 
13
13
 
14
- def fetch_url_metadata(locator: Url | StorePath, refetch: bool = False) -> Item:
14
+ def fetch_url_item(
15
+ locator: Url | StorePath, *, save_content: bool = True, refetch: bool = False
16
+ ) -> Item:
15
17
  from kash.workspaces import current_ws
16
18
 
17
19
  ws = current_ws()
@@ -26,16 +28,23 @@ def fetch_url_metadata(locator: Url | StorePath, refetch: bool = False) -> Item:
26
28
  else:
27
29
  raise InvalidInput(f"Not a URL or URL resource: {fmt_loc(locator)}")
28
30
 
29
- return fetch_url_item_metadata(item, refetch=refetch)
31
+ return fetch_url_item_content(item, save_content=save_content, refetch=refetch)
30
32
 
31
33
 
32
- def fetch_url_item_metadata(item: Item, refetch: bool = False) -> Item:
34
+ def fetch_url_item_content(item: Item, *, save_content: bool = True, refetch: bool = False) -> Item:
33
35
  """
34
- Fetch metadata for a URL using a media service if we recognize the URL,
35
- and otherwise fetching and extracting it from the web page HTML.
36
+ Fetch content and metadata for a URL using a media service if we
37
+ recognize the URL as a known media service. Otherwise, fetch and extract the
38
+ metadata and content from the web page and save it to the URL item.
39
+
40
+ If `save_content` is true, a copy of the content is also saved as
41
+ a resource item.
42
+
43
+ The content item is returned if content was saved. Otherwise, the updated
44
+ URL item is returned.
36
45
  """
37
46
  from kash.web_content.canon_url import canonicalize_url
38
- from kash.web_content.web_extract import fetch_extract
47
+ from kash.web_content.web_extract import fetch_page_content
39
48
  from kash.workspaces import current_ws
40
49
 
41
50
  ws = current_ws()
@@ -54,28 +63,47 @@ def fetch_url_item_metadata(item: Item, refetch: bool = False) -> Item:
54
63
  # Prefer fetching metadata from media using the media service if possible.
55
64
  # Data is cleaner and YouTube for example often blocks regular scraping.
56
65
  media_metadata = get_media_metadata(url)
66
+ url_item: Item | None = None
67
+ content_item: Item | None = None
57
68
  if media_metadata:
58
- fetched_item = Item.from_media_metadata(media_metadata)
69
+ url_item = Item.from_media_metadata(media_metadata)
59
70
  # Preserve and canonicalize any slice suffix on the URL.
60
71
  _base_url, slice = parse_url_slice(item.url)
61
72
  if slice:
62
73
  new_url = add_slice_to_url(media_metadata.url, slice)
63
74
  if new_url != item.url:
64
75
  log.message("Updated URL from metadata and added slice: %s", new_url)
65
- fetched_item.url = new_url
76
+ url_item.url = new_url
66
77
 
67
- fetched_item = item.merged_copy(fetched_item)
78
+ url_item = item.merged_copy(url_item)
68
79
  else:
69
- page_data = fetch_extract(url, refetch=refetch)
70
- fetched_item = item.new_copy_with(
80
+ page_data = fetch_page_content(url, refetch=refetch, cache=save_content)
81
+ url_item = item.new_copy_with(
71
82
  title=page_data.title or item.title,
72
83
  description=page_data.description or item.description,
73
84
  thumbnail_url=page_data.thumbnail_url or item.thumbnail_url,
74
85
  )
86
+ if save_content:
87
+ assert page_data.saved_content
88
+ assert page_data.format_info
89
+ content_item = url_item.new_copy_with(
90
+ external_path=str(page_data.saved_content),
91
+ # Use the original filename, not the local cache filename (which has a hash suffix).
92
+ original_filename=item.get_filename(),
93
+ format=page_data.format_info.format,
94
+ )
95
+ ws.save(content_item)
75
96
 
76
- if not fetched_item.title:
97
+ if not url_item.title:
77
98
  log.warning("Failed to fetch page data: title is missing: %s", item.url)
78
99
 
79
- ws.save(fetched_item)
100
+ # Now save the updated URL item and also the content item if we have one.
101
+ ws.save(url_item)
102
+ assert url_item.store_path
103
+ log.debug("Saved URL item: %s", url_item.fmt_loc())
104
+ if content_item:
105
+ ws.save(content_item)
106
+ assert content_item.store_path
107
+ log.debug("Saved content item: %s", content_item.fmt_loc())
80
108
 
81
- return fetched_item
109
+ return content_item or url_item
@@ -405,6 +405,7 @@ class FileStore(Workspace):
405
405
  # If external path already exists and is within the workspace, the file was
406
406
  # already saved (e.g. by an action that wrote the item directly to the store).
407
407
  external_path = item.external_path and Path(item.external_path).resolve()
408
+ skipped_save = False
408
409
  if external_path and self._is_in_store(external_path):
409
410
  log.info("Item with external_path already saved: %s", fmt_loc(external_path))
410
411
  rel_path = external_path.relative_to(self.base_dir)
@@ -480,12 +481,17 @@ class FileStore(Workspace):
480
481
  )
481
482
  os.unlink(full_path)
482
483
  store_path = old_store_path
484
+ skipped_save = True
483
485
 
484
486
  # Update in-memory store_path only after successful save.
485
487
  item.store_path = str(store_path)
486
488
  self._id_index_item(store_path)
487
489
 
488
- log.message("%s Saved item: %s", EMOJI_SAVED, fmt_loc(store_path))
490
+ if not skipped_save:
491
+ log.message("%s Saved item: %s", EMOJI_SAVED, fmt_loc(store_path))
492
+ else:
493
+ log.info("%s Already saved: %s", EMOJI_SAVED, fmt_loc(store_path))
494
+
489
495
  return store_path
490
496
 
491
497
  @log_calls(level="debug")
@@ -30,6 +30,10 @@ def folder_for_type(item_type: ItemType) -> Path:
30
30
 
31
31
 
32
32
  def join_suffix(base_slug: str, full_suffix: str) -> str:
33
+ """
34
+ Create a store filename by joining a base slug and a full suffix, i.e. a filename
35
+ extension with or without an item type (`.html` or `.resource.html`, for example).
36
+ """
33
37
  return f"{base_slug}.{full_suffix.lstrip('.')}"
34
38
 
35
39
 
@@ -2,9 +2,9 @@ from collections.abc import Callable
2
2
  from dataclasses import replace
3
3
  from typing import Any
4
4
 
5
- from kash.help.docstring_utils import parse_docstring
6
5
  from kash.model.params_model import ALL_COMMON_PARAMS, Param
7
6
  from kash.utils.common.function_inspect import FuncParam, inspect_function_params
7
+ from kash.utils.common.parse_docstring import parse_docstring
8
8
 
9
9
 
10
10
  def _look_up_param_docs(func: Callable[..., Any], kw_params: list[FuncParam]) -> list[Param]:
@@ -3,7 +3,6 @@ from rich.text import Text
3
3
  from kash.config.logger import get_logger
4
4
  from kash.config.text_styles import STYLE_HINT
5
5
  from kash.docs.all_docs import DocSelection, all_docs
6
- from kash.help.docstring_utils import parse_docstring
7
6
  from kash.shell.output.shell_formatting import format_name_and_value
8
7
  from kash.shell.output.shell_output import (
9
8
  PrintHooks,
@@ -12,6 +11,7 @@ from kash.shell.output.shell_output import (
12
11
  print_hrule,
13
12
  print_markdown,
14
13
  )
14
+ from kash.utils.common.parse_docstring import parse_docstring
15
15
 
16
16
  log = get_logger(__name__)
17
17
 
@@ -6,7 +6,6 @@ from kash.docs.all_docs import DocSelection
6
6
  from kash.exec.action_registry import look_up_action_class
7
7
  from kash.exec.command_registry import CommandFunction, look_up_command
8
8
  from kash.help.assistant import assist_preamble, assistance_unstructured
9
- from kash.help.docstring_utils import parse_docstring
10
9
  from kash.help.function_param_info import annotate_param_info
11
10
  from kash.help.help_lookups import look_up_faq
12
11
  from kash.help.tldr_help import tldr_help
@@ -22,6 +21,7 @@ from kash.shell.output.shell_output import (
22
21
  print_help,
23
22
  print_markdown,
24
23
  )
24
+ from kash.utils.common.parse_docstring import parse_docstring
25
25
  from kash.utils.errors import InvalidInput, NoMatch
26
26
  from kash.utils.file_formats.chat_format import ChatHistory, ChatMessage, ChatRole
27
27
 
@@ -675,9 +675,21 @@ class Item:
675
675
  raise FileFormatError(f"Config item is not YAML: {self.format}: {self}")
676
676
  return from_yaml_string(self.body)
677
677
 
678
+ def get_filename(self) -> str | None:
679
+ """
680
+ Get the store or external path filename of the item, including the
681
+ file extension.
682
+ """
683
+ if self.store_path:
684
+ return Path(self.store_path).name
685
+ elif self.external_path:
686
+ return Path(self.external_path).name
687
+ else:
688
+ return None
689
+
678
690
  def get_file_ext(self) -> FileExt:
679
691
  """
680
- Get or infer file extension.
692
+ Get or infer the base file extension for the item.
681
693
  """
682
694
  if self.file_ext:
683
695
  return self.file_ext
@@ -688,7 +700,8 @@ class Item:
688
700
 
689
701
  def get_full_suffix(self) -> str:
690
702
  """
691
- Get the full file extension suffix (e.g. "note.md") for this item.
703
+ Assemble the full file extension suffix (e.g. "resource.yml") for this item.
704
+ Without a leading dot.
692
705
  """
693
706
  if self.type == ItemType.extension:
694
707
  # Python files cannot have more than one . in them.
@@ -892,12 +905,14 @@ class Item:
892
905
 
893
906
  def fmt_loc(self) -> str:
894
907
  """
895
- Formatted store path, external path, or title. For error messages etc.
908
+ Formatted store path, external path, URL, or title. Use for logging etc.
896
909
  """
897
910
  if self.store_path:
898
911
  return fmt_store_path(self.store_path)
899
912
  elif self.external_path:
900
913
  return fmt_loc(self.external_path)
914
+ elif self.url:
915
+ return fmt_loc(self.url)
901
916
  else:
902
917
  return repr(self.pick_title())
903
918