kash-shell 0.3.13__tar.gz → 0.3.15__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 (300) hide show
  1. {kash_shell-0.3.13 → kash_shell-0.3.15}/PKG-INFO +1 -1
  2. {kash_shell-0.3.13 → kash_shell-0.3.15}/pyproject.toml +1 -1
  3. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/actions/core/markdownify.py +7 -4
  4. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/actions/core/readability.py +4 -3
  5. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/actions/core/render_as_html.py +2 -2
  6. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/actions/core/show_webpage.py +2 -2
  7. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/workspace/selection_commands.py +22 -0
  8. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/workspace/workspace_commands.py +45 -22
  9. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/action_exec.py +25 -7
  10. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/precondition_registry.py +5 -3
  11. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/preconditions.py +31 -6
  12. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/resolve_args.py +2 -2
  13. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/file_storage/file_store.py +48 -12
  14. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/model/items_model.py +13 -8
  15. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/model/operations_model.py +14 -0
  16. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/utils/native_utils.py +2 -2
  17. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/common/url.py +80 -3
  18. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/file_utils/file_formats.py +3 -2
  19. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/file_utils/file_formats_model.py +37 -49
  20. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/text_handling/doc_normalization.py +7 -0
  21. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_content/local_file_cache.py +28 -5
  22. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_gen/templates/base_styles.css.jinja +7 -3
  23. {kash_shell-0.3.13 → kash_shell-0.3.15}/uv.lock +188 -175
  24. kash_shell-0.3.13/src/kash/workspaces/workspace_importing.py +0 -56
  25. {kash_shell-0.3.13 → kash_shell-0.3.15}/.copier-answers.yml +0 -0
  26. {kash_shell-0.3.13 → kash_shell-0.3.15}/.env.template +0 -0
  27. {kash_shell-0.3.13 → kash_shell-0.3.15}/.github/workflows/ci.yml +0 -0
  28. {kash_shell-0.3.13 → kash_shell-0.3.15}/.github/workflows/publish.yml +0 -0
  29. {kash_shell-0.3.13 → kash_shell-0.3.15}/.gitignore +0 -0
  30. {kash_shell-0.3.13 → kash_shell-0.3.15}/LICENSE +0 -0
  31. {kash_shell-0.3.13 → kash_shell-0.3.15}/Makefile +0 -0
  32. {kash_shell-0.3.13 → kash_shell-0.3.15}/README.md +0 -0
  33. {kash_shell-0.3.13 → kash_shell-0.3.15}/development.md +0 -0
  34. {kash_shell-0.3.13 → kash_shell-0.3.15}/devtools/generate_readme.xsh +0 -0
  35. {kash_shell-0.3.13 → kash_shell-0.3.15}/devtools/lint.py +0 -0
  36. {kash_shell-0.3.13 → kash_shell-0.3.15}/devtools/profile_main.py +0 -0
  37. {kash_shell-0.3.13 → kash_shell-0.3.15}/installation.md +0 -0
  38. {kash_shell-0.3.13 → kash_shell-0.3.15}/publishing.md +0 -0
  39. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/__init__.py +0 -0
  40. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/__main__.py +0 -0
  41. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/actions/__init__.py +0 -0
  42. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/actions/core/assistant_chat.py +0 -0
  43. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/actions/core/chat.py +0 -0
  44. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/actions/core/format_markdown_template.py +0 -0
  45. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/actions/core/strip_html.py +0 -0
  46. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/actions/core/summarize_as_bullets.py +0 -0
  47. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/actions/core/tabbed_webpage_config.py +0 -0
  48. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/actions/core/tabbed_webpage_generate.py +0 -0
  49. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/actions/meta/write_instructions.py +0 -0
  50. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/actions/meta/write_new_action.py +0 -0
  51. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/__init__.py +0 -0
  52. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/base/basic_file_commands.py +0 -0
  53. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/base/browser_commands.py +0 -0
  54. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/base/debug_commands.py +0 -0
  55. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/base/diff_commands.py +0 -0
  56. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/base/files_command.py +0 -0
  57. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/base/general_commands.py +0 -0
  58. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/base/logs_commands.py +0 -0
  59. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/base/model_commands.py +0 -0
  60. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/base/reformat_command.py +0 -0
  61. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/base/search_command.py +0 -0
  62. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/base/show_command.py +0 -0
  63. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/extras/parse_uv_lock.py +0 -0
  64. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/extras/utils_commands.py +0 -0
  65. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/help/assistant_commands.py +0 -0
  66. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/help/doc_commands.py +0 -0
  67. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/help/help_commands.py +0 -0
  68. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/help/logo.py +0 -0
  69. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/commands/help/welcome.py +0 -0
  70. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/config/__init__.py +0 -0
  71. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/config/capture_output.py +0 -0
  72. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/config/colors.py +0 -0
  73. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/config/env_settings.py +0 -0
  74. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/config/init.py +0 -0
  75. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/config/lazy_imports.py +0 -0
  76. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/config/logger.py +0 -0
  77. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/config/logger_basic.py +0 -0
  78. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/config/logo.txt +0 -0
  79. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/config/server_config.py +0 -0
  80. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/config/settings.py +0 -0
  81. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/config/setup.py +0 -0
  82. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/config/suppress_warnings.py +0 -0
  83. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/config/text_styles.py +0 -0
  84. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/__init__.py +0 -0
  85. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/all_docs.py +0 -0
  86. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/load_actions_info.py +0 -0
  87. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/load_api_docs.py +0 -0
  88. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/load_help_topics.py +0 -0
  89. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/load_source_code.py +0 -0
  90. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/markdown/api_docs_template.md +0 -0
  91. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/markdown/assistant_instructions_template.md +0 -0
  92. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/markdown/readme_template.md +0 -0
  93. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/markdown/topics/a1_what_is_kash.md +0 -0
  94. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/markdown/topics/a2_installation.md +0 -0
  95. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/markdown/topics/a3_getting_started.md +0 -0
  96. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/markdown/topics/a4_elements.md +0 -0
  97. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/markdown/topics/a5_tips_for_use_with_other_tools.md +0 -0
  98. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/markdown/topics/b0_philosophy_of_kash.md +0 -0
  99. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/markdown/topics/b1_kash_overview.md +0 -0
  100. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/markdown/topics/b2_workspace_and_file_formats.md +0 -0
  101. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/markdown/topics/b3_modern_shell_tool_recommendations.md +0 -0
  102. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/markdown/topics/b4_faq.md +0 -0
  103. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/markdown/warning.md +0 -0
  104. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs/markdown/welcome.md +0 -0
  105. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs_base/docs_base.py +0 -0
  106. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs_base/load_custom_command_info.py +0 -0
  107. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs_base/load_faqs.py +0 -0
  108. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs_base/load_recipe_snippets.py +0 -0
  109. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs_base/recipes/general_system_commands.sh +0 -0
  110. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs_base/recipes/python_dev_commands.sh +0 -0
  111. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/docs_base/recipes/tldr_standard_commands.sh +0 -0
  112. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/embeddings/cosine.py +0 -0
  113. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/embeddings/embeddings.py +0 -0
  114. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/embeddings/text_similarity.py +0 -0
  115. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/__init__.py +0 -0
  116. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/action_decorators.py +0 -0
  117. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/action_registry.py +0 -0
  118. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/combiners.py +0 -0
  119. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/command_exec.py +0 -0
  120. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/command_registry.py +0 -0
  121. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/fetch_url_metadata.py +0 -0
  122. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/history.py +0 -0
  123. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/importing.py +0 -0
  124. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/llm_transforms.py +0 -0
  125. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/precondition_checks.py +0 -0
  126. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/runtime_settings.py +0 -0
  127. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec/shell_callable_action.py +0 -0
  128. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec_model/__init__.py +0 -0
  129. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec_model/args_model.py +0 -0
  130. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec_model/commands_model.py +0 -0
  131. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec_model/script_model.py +0 -0
  132. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/exec_model/shell_model.py +0 -0
  133. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/file_storage/__init__.py +0 -0
  134. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/file_storage/item_file_format.py +0 -0
  135. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/file_storage/metadata_dirs.py +0 -0
  136. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/file_storage/persisted_yaml.py +0 -0
  137. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/file_storage/store_cache_warmer.py +0 -0
  138. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/file_storage/store_filenames.py +0 -0
  139. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/help/__init__.py +0 -0
  140. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/help/assistant.py +0 -0
  141. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/help/assistant_instructions.py +0 -0
  142. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/help/assistant_output.py +0 -0
  143. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/help/docstring_utils.py +0 -0
  144. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/help/function_param_info.py +0 -0
  145. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/help/help_embeddings.py +0 -0
  146. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/help/help_lookups.py +0 -0
  147. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/help/help_pages.py +0 -0
  148. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/help/help_printing.py +0 -0
  149. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/help/help_types.py +0 -0
  150. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/help/recommended_commands.py +0 -0
  151. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/help/tldr_help.py +0 -0
  152. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/llm_utils/__init__.py +0 -0
  153. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/llm_utils/clean_headings.py +0 -0
  154. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/llm_utils/custom_sliding_transforms.py +0 -0
  155. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/llm_utils/fuzzy_parsing.py +0 -0
  156. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/llm_utils/init_litellm.py +0 -0
  157. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/llm_utils/llm_api_keys.py +0 -0
  158. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/llm_utils/llm_completion.py +0 -0
  159. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/llm_utils/llm_features.py +0 -0
  160. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/llm_utils/llm_messages.py +0 -0
  161. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/llm_utils/llm_names.py +0 -0
  162. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/llm_utils/llms.py +0 -0
  163. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/local_server/__init__.py +0 -0
  164. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/local_server/local_server.py +0 -0
  165. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/local_server/local_server_commands.py +0 -0
  166. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/local_server/local_server_routes.py +0 -0
  167. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/local_server/local_url_formatters.py +0 -0
  168. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/local_server/port_tools.py +0 -0
  169. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/local_server/rich_html_template.py +0 -0
  170. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/mcp/__init__.py +0 -0
  171. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/mcp/mcp_cli.py +0 -0
  172. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/mcp/mcp_main.py +0 -0
  173. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/mcp/mcp_server_commands.py +0 -0
  174. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/mcp/mcp_server_routes.py +0 -0
  175. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/mcp/mcp_server_sse.py +0 -0
  176. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/mcp/mcp_server_stdio.py +0 -0
  177. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/media_base/__init__.py +0 -0
  178. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/media_base/audio_processing.py +0 -0
  179. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/media_base/media_cache.py +0 -0
  180. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/media_base/media_services.py +0 -0
  181. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/media_base/media_tools.py +0 -0
  182. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/media_base/services/local_file_media.py +0 -0
  183. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/media_base/timestamp_citations.py +0 -0
  184. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/media_base/transcription_deepgram.py +0 -0
  185. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/media_base/transcription_format.py +0 -0
  186. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/media_base/transcription_whisper.py +0 -0
  187. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/model/__init__.py +0 -0
  188. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/model/actions_model.py +0 -0
  189. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/model/assistant_response_model.py +0 -0
  190. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/model/compound_actions_model.py +0 -0
  191. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/model/concept_model.py +0 -0
  192. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/model/exec_model.py +0 -0
  193. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/model/graph_model.py +0 -0
  194. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/model/language_list.py +0 -0
  195. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/model/llm_actions_model.py +0 -0
  196. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/model/media_model.py +0 -0
  197. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/model/params_model.py +0 -0
  198. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/model/paths_model.py +0 -0
  199. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/model/preconditions_model.py +0 -0
  200. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/__init__.py +0 -0
  201. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/completions/completion_scoring.py +0 -0
  202. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/completions/completion_types.py +0 -0
  203. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/completions/shell_completions.py +0 -0
  204. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/file_icons/color_for_format.py +0 -0
  205. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/file_icons/nerd_icons.py +0 -0
  206. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/input/__init__.py +0 -0
  207. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/input/input_prompts.py +0 -0
  208. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/input/inquirer_settings.py +0 -0
  209. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/input/param_inputs.py +0 -0
  210. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/input/shell_confirm.py +0 -0
  211. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/output/__init__.py +0 -0
  212. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/output/kerm_code_utils.py +0 -0
  213. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/output/kerm_codes.py +0 -0
  214. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/output/kmarkdown.py +0 -0
  215. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/output/shell_formatting.py +0 -0
  216. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/output/shell_output.py +0 -0
  217. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/shell_main.py +0 -0
  218. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/ui/__init__.py +0 -0
  219. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/ui/shell_results.py +0 -0
  220. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/ui/shell_syntax.py +0 -0
  221. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/utils/exception_printing.py +0 -0
  222. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/utils/shell_function_wrapper.py +0 -0
  223. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/shell/version.py +0 -0
  224. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/__init__.py +0 -0
  225. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/common/__init__.py +0 -0
  226. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/common/format_utils.py +0 -0
  227. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/common/function_inspect.py +0 -0
  228. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/common/import_utils.py +0 -0
  229. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/common/lazyobject.py +0 -0
  230. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/common/obj_replace.py +0 -0
  231. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/common/parse_key_vals.py +0 -0
  232. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/common/parse_shell_args.py +0 -0
  233. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/common/stack_traces.py +0 -0
  234. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/common/task_stack.py +0 -0
  235. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/common/type_utils.py +0 -0
  236. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/common/uniquifier.py +0 -0
  237. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/errors.py +0 -0
  238. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/file_formats/chat_format.py +0 -0
  239. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/file_utils/__init__.py +0 -0
  240. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/file_utils/dir_info.py +0 -0
  241. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/file_utils/file_ext.py +0 -0
  242. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/file_utils/file_sort_filter.py +0 -0
  243. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/file_utils/file_walk.py +0 -0
  244. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/file_utils/filename_parsing.py +0 -0
  245. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/file_utils/ignore_files.py +0 -0
  246. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/file_utils/mtime_cache.py +0 -0
  247. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/file_utils/path_utils.py +0 -0
  248. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/lang_utils/__init__.py +0 -0
  249. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/lang_utils/capitalization.py +0 -0
  250. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/rich_custom/__init__.py +0 -0
  251. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/rich_custom/ansi_cell_len.py +0 -0
  252. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/rich_custom/rich_char_transform.py +0 -0
  253. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/rich_custom/rich_indent.py +0 -0
  254. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/rich_custom/rich_markdown_fork.py +0 -0
  255. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/text_handling/escape_html_tags.py +0 -0
  256. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/text_handling/markdown_render.py +0 -0
  257. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/text_handling/markdown_utils.py +0 -0
  258. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/text_handling/markdownify_utils.py +0 -0
  259. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/utils/text_handling/unified_diffs.py +0 -0
  260. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_content/canon_url.py +0 -0
  261. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_content/dir_store.py +0 -0
  262. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_content/file_cache_utils.py +0 -0
  263. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_content/file_processing.py +0 -0
  264. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_content/web_extract.py +0 -0
  265. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_content/web_extract_justext.py +0 -0
  266. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_content/web_extract_readabilipy.py +0 -0
  267. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_content/web_fetch.py +0 -0
  268. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_content/web_page_model.py +0 -0
  269. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_gen/__init__.py +0 -0
  270. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_gen/simple_webpage.py +0 -0
  271. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_gen/tabbed_webpage.py +0 -0
  272. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_gen/template_render.py +0 -0
  273. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_gen/templates/base_webpage.html.jinja +0 -0
  274. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_gen/templates/content_styles.css.jinja +0 -0
  275. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_gen/templates/explain_view.html.jinja +0 -0
  276. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_gen/templates/item_view.html.jinja +0 -0
  277. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_gen/templates/simple_webpage.html.jinja +0 -0
  278. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/web_gen/templates/tabbed_webpage.html.jinja +0 -0
  279. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/workspaces/__init__.py +0 -0
  280. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/workspaces/param_state.py +0 -0
  281. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/workspaces/selections.py +0 -0
  282. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/workspaces/source_items.py +0 -0
  283. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/workspaces/workspace_dirs.py +0 -0
  284. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/workspaces/workspace_output.py +0 -0
  285. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/workspaces/workspace_registry.py +0 -0
  286. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/workspaces/workspaces.py +0 -0
  287. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/xonsh_custom/command_nl_utils.py +0 -0
  288. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/xonsh_custom/custom_shell.py +0 -0
  289. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/xonsh_custom/customize_prompt.py +0 -0
  290. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/xonsh_custom/load_into_xonsh.py +0 -0
  291. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/xonsh_custom/shell_load_commands.py +0 -0
  292. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/xonsh_custom/shell_which.py +0 -0
  293. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/xonsh_custom/xonsh_completers.py +0 -0
  294. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/xonsh_custom/xonsh_env.py +0 -0
  295. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/xonsh_custom/xonsh_keybindings.py +0 -0
  296. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/xonsh_custom/xonsh_modern_tools.py +0 -0
  297. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/xonsh_custom/xonsh_ranking_completer.py +0 -0
  298. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/xontrib/fnm.py +0 -0
  299. {kash_shell-0.3.13 → kash_shell-0.3.15}/src/kash/xontrib/kash_extension.py +0 -0
  300. {kash_shell-0.3.13 → kash_shell-0.3.15}/tests/test_shell.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kash-shell
3
- Version: 0.3.13
3
+ Version: 0.3.15
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>
@@ -38,7 +38,7 @@ classifiers = [
38
38
  # ---- Main dependencies ----
39
39
 
40
40
  dependencies = [
41
- "dunamai>=1.23.0",
41
+ "dunamai>=1.23.0",
42
42
  "pydantic>=2.10.6",
43
43
  "typing-extensions>=4.12.2",
44
44
  # My tools:
@@ -1,8 +1,9 @@
1
1
  from kash.config.logger import get_logger
2
2
  from kash.exec import kash_action
3
3
  from kash.exec.preconditions import has_html_body, is_url_resource
4
+ from kash.exec.runtime_settings import current_runtime_settings
4
5
  from kash.model import Format, Item
5
- from kash.model.params_model import common_params
6
+ from kash.model.items_model import ItemType
6
7
  from kash.utils.text_handling.markdownify_utils import markdownify_custom
7
8
  from kash.web_content.file_cache_utils import get_url_html
8
9
  from kash.web_content.web_extract_readabilipy import extract_text_readabilipy
@@ -12,20 +13,22 @@ log = get_logger(__name__)
12
13
 
13
14
  @kash_action(
14
15
  precondition=is_url_resource | has_html_body,
15
- params=common_params("refetch"),
16
16
  mcp_tool=True,
17
17
  )
18
- def markdownify(item: Item, refetch: bool = False) -> Item:
18
+ def markdownify(item: Item) -> Item:
19
19
  """
20
20
  Converts a URL or raw HTML item to Markdown, fetching with the content
21
21
  cache if needed. Also uses readability to clean up the HTML.
22
22
  """
23
23
 
24
+ refetch = current_runtime_settings().refetch
24
25
  expiration_sec = 0 if refetch else None
25
26
  url, html_content = get_url_html(item, expiration_sec=expiration_sec)
26
27
  page_data = extract_text_readabilipy(url, html_content)
27
28
  assert page_data.clean_html
28
29
  markdown_content = markdownify_custom(page_data.clean_html)
29
30
 
30
- output_item = item.derived_copy(format=Format.markdown, body=markdown_content)
31
+ output_item = item.derived_copy(
32
+ type=ItemType.doc, format=Format.markdown, body=markdown_content
33
+ )
31
34
  return output_item
@@ -1,8 +1,8 @@
1
1
  from kash.config.logger import get_logger
2
2
  from kash.exec import kash_action
3
3
  from kash.exec.preconditions import has_html_body, is_url_resource
4
+ from kash.exec.runtime_settings import current_runtime_settings
4
5
  from kash.model import Format, Item
5
- from kash.model.params_model import common_params
6
6
  from kash.web_content.file_cache_utils import get_url_html
7
7
  from kash.web_content.web_extract_readabilipy import extract_text_readabilipy
8
8
 
@@ -11,14 +11,15 @@ log = get_logger(__name__)
11
11
 
12
12
  @kash_action(
13
13
  precondition=is_url_resource | has_html_body,
14
- params=common_params("refetch"),
15
14
  mcp_tool=True,
16
15
  )
17
- def readability(item: Item, refetch: bool = False) -> Item:
16
+ def readability(item: Item) -> Item:
18
17
  """
19
18
  Extracts clean HTML from a raw HTML item.
20
19
  See `markdownify` to also convert to Markdown.
21
20
  """
21
+
22
+ refetch = current_runtime_settings().refetch
22
23
  expiration_sec = 0 if refetch else None
23
24
  locator, html_content = get_url_html(item, expiration_sec=expiration_sec)
24
25
  page_data = extract_text_readabilipy(locator, html_content)
@@ -1,7 +1,7 @@
1
1
  from kash.actions.core.tabbed_webpage_config import tabbed_webpage_config
2
2
  from kash.actions.core.tabbed_webpage_generate import tabbed_webpage_generate
3
3
  from kash.exec import kash_action
4
- from kash.exec.preconditions import has_full_html_page_body, has_html_body, has_simple_text_body
4
+ from kash.exec.preconditions import has_fullpage_html_body, has_html_body, has_simple_text_body
5
5
  from kash.exec_model.args_model import ONE_OR_MORE_ARGS
6
6
  from kash.model import ActionInput, ActionResult, Param
7
7
  from kash.model.items_model import ItemType
@@ -11,7 +11,7 @@ from kash.web_gen.simple_webpage import simple_webpage_render
11
11
 
12
12
  @kash_action(
13
13
  expected_args=ONE_OR_MORE_ARGS,
14
- precondition=(has_html_body | has_simple_text_body) & ~has_full_html_page_body,
14
+ precondition=(has_html_body | has_simple_text_body) & ~has_fullpage_html_body,
15
15
  params=(Param("no_title", "Don't add a title to the page body.", type=bool),),
16
16
  )
17
17
  def render_as_html(input: ActionInput, no_title: bool = False) -> ActionResult:
@@ -1,7 +1,7 @@
1
1
  from kash.actions.core.render_as_html import render_as_html
2
2
  from kash.commands.base.show_command import show
3
3
  from kash.exec import kash_action
4
- from kash.exec.preconditions import has_full_html_page_body, has_html_body, has_simple_text_body
4
+ from kash.exec.preconditions import has_fullpage_html_body, has_html_body, has_simple_text_body
5
5
  from kash.exec_model.args_model import ONE_OR_MORE_ARGS
6
6
  from kash.exec_model.commands_model import Command
7
7
  from kash.exec_model.shell_model import ShellResult
@@ -10,7 +10,7 @@ from kash.model import ActionInput, ActionResult
10
10
 
11
11
  @kash_action(
12
12
  expected_args=ONE_OR_MORE_ARGS,
13
- precondition=(has_html_body | has_simple_text_body) & ~has_full_html_page_body,
13
+ precondition=(has_html_body | has_simple_text_body) & ~has_fullpage_html_body,
14
14
  )
15
15
  def show_webpage(input: ActionInput) -> ActionResult:
16
16
  """
@@ -7,6 +7,7 @@ from strif import copyfile_atomic
7
7
 
8
8
  from kash.config.logger import get_logger
9
9
  from kash.exec import kash_command
10
+ from kash.exec.resolve_args import assemble_path_args
10
11
  from kash.exec_model.shell_model import ShellResult
11
12
  from kash.model.paths_model import StorePath
12
13
  from kash.shell.ui.shell_results import shell_print_selection_history
@@ -30,6 +31,7 @@ def select(
30
31
  clear_all: bool = False,
31
32
  clear_future: bool = False,
32
33
  refresh: bool = False,
34
+ no_check: bool = False,
33
35
  ) -> ShellResult:
34
36
  """
35
37
  Set or show the current selection.
@@ -51,6 +53,7 @@ def select(
51
53
  :param clear_all: Clear the full selection history.
52
54
  :param clear_future: Clear all selections from history after the current one.
53
55
  :param refresh: Refresh the current selection to drop any paths that no longer exist.
56
+ :param no_check: Do not check if the paths exist.
54
57
  """
55
58
  ws = current_ws()
56
59
 
@@ -68,6 +71,10 @@ def select(
68
71
  raise InvalidInput("Cannot combine multiple flags")
69
72
  if paths and any(exclusive_flags):
70
73
  raise InvalidInput("Cannot combine paths with other flags")
74
+ if not no_check:
75
+ for path in paths:
76
+ if not Path(ws.base_dir / path).exists():
77
+ raise InvalidInput(f"Path does not exist: {fmt_loc(path)}")
71
78
 
72
79
  if paths:
73
80
  store_paths = [StorePath(path) for path in paths]
@@ -203,3 +210,18 @@ def save(parent: str | None = None, to: str | None = None, no_frontmatter: bool
203
210
  for store_path in store_paths:
204
211
  target_path = target_dir / basename(store_path)
205
212
  copy_file(store_path, target_path)
213
+
214
+
215
+ @kash_command
216
+ def show_parent_dir(*paths: str) -> None:
217
+ """
218
+ Show the parent directory of the first item in the current selection.
219
+ """
220
+ from kash.commands.base.show_command import show
221
+
222
+ input_paths = assemble_path_args(*paths)
223
+ if not input_paths:
224
+ raise InvalidInput("No paths provided")
225
+
226
+ input_path = current_ws().resolve_to_abs_path(input_paths[0])
227
+ show(input_path.parent)
@@ -59,6 +59,7 @@ from kash.utils.errors import InvalidInput
59
59
  from kash.utils.file_formats.chat_format import tail_chat_history
60
60
  from kash.utils.file_utils.dir_info import is_nonempty_dir
61
61
  from kash.utils.file_utils.file_formats_model import Format
62
+ from kash.utils.text_handling.doc_normalization import can_normalize
62
63
  from kash.web_content.file_cache_utils import cache_file
63
64
  from kash.workspaces import (
64
65
  current_ws,
@@ -189,56 +190,80 @@ def cache_content(*urls_or_paths: str, refetch: bool = False) -> None:
189
190
 
190
191
 
191
192
  @kash_command
192
- def download(*urls_or_paths: str, refetch: bool = False) -> ShellResult:
193
+ def download(*urls_or_paths: str, refetch: bool = False, no_format: bool = False) -> ShellResult:
193
194
  """
194
195
  Download a URL or resource. Uses cached content if available, unless `refetch` is true.
195
196
  Inputs can be URLs or paths to URL resources.
196
- """
197
- expiration_sec = 0 if refetch else None
197
+ Creates both resource and document versions for text content.
198
198
 
199
- # TODO: Add option to include frontmatter metadata for text files.
199
+ :param no_format: If true, do not also normalize Markdown content.
200
+ """
200
201
  ws = current_ws()
201
202
  saved_paths = []
203
+
202
204
  for url_or_path in urls_or_paths:
203
205
  locator = resolve_locator_arg(url_or_path)
204
206
  url: Url | None = None
207
+
208
+ # Get the URL from the locator
205
209
  if not isinstance(locator, Path) and is_url(locator):
206
210
  url = Url(locator)
207
- if isinstance(locator, StorePath):
211
+ elif isinstance(locator, StorePath):
208
212
  url_item = ws.load(locator)
209
213
  if is_url_resource(url_item):
210
214
  url = url_item.url
215
+
211
216
  if not url:
212
217
  raise InvalidInput(f"Not a URL or URL resource: {fmt_loc(locator)}")
213
218
 
219
+ # Handle media URLs differently
214
220
  if is_media_url(url):
215
221
  log.message(
216
222
  "URL is a media URL, so adding as a resource and will cache media: %s", fmt_loc(url)
217
223
  )
218
- store_path = ws.import_item(locator, as_type=ItemType.resource)
224
+ store_path = ws.import_item(url, as_type=ItemType.resource, reimport=refetch)
225
+ saved_paths.append(store_path)
219
226
  media_tools.cache_media(url)
220
227
  else:
221
- log.message("Will cache file and save to workspace: %s", fmt_loc(url))
222
- original_filename = Path(parse_http_url(url).path).name
228
+ # Cache the content first
229
+ expiration_sec = 0 if refetch else None
223
230
  cache_result = cache_file(url, expiration_sec=expiration_sec)
224
- # If available, use the mime type to help set item file extension.
231
+ original_filename = Path(parse_http_url(url).path).name
225
232
  mime_type = cache_result.content.headers and cache_result.content.headers.mime_type
226
- item = Item.from_external_path(
233
+
234
+ # Create a resource item
235
+ resource_item = Item.from_external_path(
227
236
  cache_result.content.path,
228
237
  ItemType.resource,
238
+ url=url,
229
239
  mime_type=mime_type,
230
240
  original_filename=original_filename,
231
241
  )
232
- store_path = ws.save(item)
242
+ # For initial content, do not format or add frontmatter.
243
+ store_path = ws.save(resource_item, no_frontmatter=True, no_format=True)
233
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)
234
260
 
235
261
  print_status(
236
262
  "Downloaded %s %s:\n%s",
237
- len(urls_or_paths),
238
- plural("item", len(urls_or_paths)),
263
+ len(saved_paths),
264
+ plural("item", len(saved_paths)),
239
265
  fmt_lines(saved_paths),
240
266
  )
241
- select(*saved_paths)
242
267
 
243
268
  return ShellResult(show_selection=True)
244
269
 
@@ -483,7 +508,7 @@ def import_item(
483
508
 
484
509
 
485
510
  @kash_command
486
- def import_clipboard(
511
+ def save_clipboard(
487
512
  title: str | None = "pasted_text",
488
513
  type: ItemType = ItemType.resource,
489
514
  format: Format = Format.plaintext,
@@ -518,8 +543,6 @@ def fetch_metadata(*files_or_urls: str, refetch: bool = False) -> ShellResult:
518
543
 
519
544
  Skips items that already have a title and description, unless `refetch` is true.
520
545
  Skips (with a warning) items that are not URL resources.
521
-
522
- :param use_cache: If true, also save page in content cache.
523
546
  """
524
547
  if not files_or_urls:
525
548
  locators = assemble_store_path_args()
@@ -529,12 +552,12 @@ def fetch_metadata(*files_or_urls: str, refetch: bool = False) -> ShellResult:
529
552
  store_paths = []
530
553
  for locator in locators:
531
554
  try:
532
- if isinstance(locator, Path):
533
- raise InvalidInput()
534
555
  fetched_item = fetch_url_metadata(locator, refetch=refetch)
535
556
  store_paths.append(fetched_item.store_path)
536
- except InvalidInput:
537
- log.warning("Not a URL or URL resource, will not fetch metadata: %s", fmt_loc(locator))
557
+ except InvalidInput as e:
558
+ log.warning(
559
+ "Not a URL or URL resource, will not fetch metadata: %s: %s", fmt_loc(locator), e
560
+ )
538
561
 
539
562
  if store_paths:
540
563
  select(*store_paths)
@@ -716,7 +739,7 @@ def reset_ignore_file(append: bool = False) -> None:
716
739
  ignore_path = ws.base_dir / ws.dirs.ignore_file
717
740
  write_ignore(ignore_path, append=append)
718
741
 
719
- log.message("Rewrote kash ignore file: %s", fmt_loc(ignore_path))
742
+ log.message("Rewritten kash ignore file: %s", fmt_loc(ignore_path))
720
743
 
721
744
 
722
745
  @kash_command
@@ -1,7 +1,8 @@
1
1
  import time
2
2
  from dataclasses import replace
3
+ from pathlib import Path
3
4
 
4
- from prettyfmt import fmt_lines, plural
5
+ from prettyfmt import fmt_lines, fmt_path, plural
5
6
 
6
7
  from kash.config.logger import get_logger
7
8
  from kash.config.text_styles import (
@@ -32,7 +33,6 @@ from kash.utils.common.task_stack import task_stack
32
33
  from kash.utils.common.type_utils import not_none
33
34
  from kash.utils.errors import ContentError, InvalidOutput, get_nonfatal_exceptions
34
35
  from kash.workspaces import Selection, current_ws
35
- from kash.workspaces.workspace_importing import import_and_load
36
36
 
37
37
  log = get_logger(__name__)
38
38
 
@@ -49,7 +49,7 @@ def prepare_action_input(*input_args: CommandArg, refetch: bool = False) -> Acti
49
49
 
50
50
  # Ensure input items are already saved in the workspace and load the corresponding items.
51
51
  # This also imports any URLs.
52
- input_items = [import_and_load(ws, arg) for arg in input_args]
52
+ input_items = [ws.import_and_load(arg) for arg in input_args]
53
53
 
54
54
  # URLs should have metadata like a title and be valid, so we fetch them.
55
55
  if input_items:
@@ -288,6 +288,14 @@ def save_action_result(
288
288
  Save the result of an action to the workspace. Handles skipping duplicates and
289
289
  archiving old inputs, if appropriate, based on hints in the ActionResult.
290
290
  """
291
+ # If an action returned an external path, we should confirm it exists or it is probably
292
+ # a bug in the action.
293
+ for item in result.items:
294
+ if item.external_path and not Path(item.external_path).exists():
295
+ raise InvalidOutput(
296
+ f"External path returned by action does not exist: {fmt_path(item.external_path)}"
297
+ )
298
+
291
299
  input_items = action_input.items
292
300
 
293
301
  skipped_paths = []
@@ -378,12 +386,16 @@ def run_action_with_caching(
378
386
  # Run it!
379
387
  result = run_action_operation(context, action_input, operation)
380
388
  result_store_paths, archived_store_paths = save_action_result(
381
- ws, result, action_input, as_tmp=settings.tmp_output, no_format=settings.no_format
389
+ ws,
390
+ result,
391
+ action_input,
392
+ as_tmp=settings.tmp_output,
393
+ no_format=settings.no_format,
382
394
  )
383
395
 
384
396
  PrintHooks.before_done_message()
385
397
  log.message(
386
- "%s Done: `%s` completed with %s %s",
398
+ "%s Action: `%s` completed with %s %s",
387
399
  EMOJI_SUCCESS,
388
400
  action.name,
389
401
  len(result.items),
@@ -449,13 +461,19 @@ def run_action_with_shell_context(
449
461
 
450
462
  # As a special case for convenience, if the action expects no args, ignore any pre-selected inputs.
451
463
  if action.expected_args == NO_ARGS and from_selection:
452
- log.message("Not using current selection since action `%s` expects no args.", action_name)
464
+ log.message(
465
+ "Not using current selection since action `%s` expects no args.",
466
+ action_name,
467
+ )
453
468
  args.clear()
454
469
 
455
470
  if args:
456
471
  source_str = "provided args" if provided_args else "selection"
457
472
  log.message(
458
- "Using %s as inputs to action `%s`:\n%s", source_str, action_name, fmt_lines(args)
473
+ "Using %s as inputs to action `%s`:\n%s",
474
+ source_str,
475
+ action_name,
476
+ fmt_lines(args),
459
477
  )
460
478
 
461
479
  # Get items for each input arg.
@@ -16,11 +16,13 @@ def kash_precondition(func: Callable[[Item], bool]) -> Precondition:
16
16
  """
17
17
  Decorator to register a function as a Precondition.
18
18
  The function should return a bool and/or raise `PreconditionFailure`.
19
+ Returns an actual Precondition object, not the function, so that it's possible to
20
+ do precondition algebra, e.g. `is_file & has_body`.
19
21
 
20
22
  Example:
21
- @register_precondition
22
- def is_file(item: Item) -> bool:
23
- return item.is_file()
23
+ @kash_precondition
24
+ def has_body(item: Item) -> bool:
25
+ return item.has_body()
24
26
  """
25
27
  precondition = Precondition(func)
26
28
 
@@ -7,7 +7,7 @@ from chopdiff.html import has_timestamp
7
7
 
8
8
  from kash.exec.precondition_registry import kash_precondition
9
9
  from kash.model.items_model import Item, ItemType
10
- from kash.utils.file_utils.file_formats import is_full_html_page
10
+ from kash.utils.file_utils.file_formats import is_fullpage_html
11
11
  from kash.utils.file_utils.file_formats_model import Format
12
12
  from kash.utils.text_handling.markdown_utils import extract_bullet_points
13
13
 
@@ -22,9 +22,14 @@ def is_doc_resource(item: Item) -> bool:
22
22
  return bool(is_resource(item) and item.format and item.format.is_doc)
23
23
 
24
24
 
25
+ @kash_precondition
26
+ def is_markdown_resource(item: Item) -> bool:
27
+ return bool(is_resource(item) and item.format and item.format.is_markdown)
28
+
29
+
25
30
  @kash_precondition
26
31
  def is_html_resource(item: Item) -> bool:
27
- return bool(is_resource(item) and item.format and item.format == Format.html)
32
+ return bool(is_resource(item) and item.format and item.format.is_html)
28
33
 
29
34
 
30
35
  @kash_precondition
@@ -100,8 +105,23 @@ def has_html_body(item: Item) -> bool:
100
105
 
101
106
 
102
107
  @kash_precondition
103
- def has_full_html_page_body(item: Item) -> bool:
104
- return bool(has_html_body(item) and item.body and is_full_html_page(item.body))
108
+ def has_html_compatible_body(item: Item) -> bool:
109
+ return bool(has_body(item) and item.format and item.format.is_html_compatible)
110
+
111
+
112
+ @kash_precondition
113
+ def has_markdown_body(item: Item) -> bool:
114
+ return bool(has_body(item) and item.format and item.format.is_markdown)
115
+
116
+
117
+ @kash_precondition
118
+ def has_markdown_with_html_body(item: Item) -> bool:
119
+ return bool(has_body(item) and item.format and item.format.is_markdown_with_html)
120
+
121
+
122
+ @kash_precondition
123
+ def has_fullpage_html_body(item: Item) -> bool:
124
+ return bool(has_html_body(item) and item.body and is_fullpage_html(item.body))
105
125
 
106
126
 
107
127
  @kash_precondition
@@ -114,16 +134,21 @@ def is_markdown(item: Item) -> bool:
114
134
  return bool(has_body(item) and item.format and item.format.is_markdown)
115
135
 
116
136
 
137
+ @kash_precondition
138
+ def is_markdown_with_html(item: Item) -> bool:
139
+ return bool(has_body(item) and item.format and item.format.is_markdown_with_html)
140
+
141
+
117
142
  @kash_precondition
118
143
  def is_markdown_template(item: Item) -> bool:
119
- return is_markdown(item) and contains_curly_vars(item)
144
+ return has_markdown_body(item) and contains_curly_vars(item)
120
145
 
121
146
 
122
147
  @kash_precondition
123
148
  def is_markdown_list(item: Item) -> bool:
124
149
  try:
125
150
  return (
126
- is_markdown(item)
151
+ has_markdown_body(item)
127
152
  and item.body is not None
128
153
  and len(extract_bullet_points(item.body)) >= 2
129
154
  )
@@ -42,7 +42,7 @@ def resolve_path_arg(path_str: UnresolvedPath) -> Path | StorePath:
42
42
  return path
43
43
  else:
44
44
  try:
45
- store_path = current_ws().resolve_path(path)
45
+ store_path = current_ws().resolve_to_store_path(path)
46
46
  if store_path:
47
47
  return store_path
48
48
  else:
@@ -110,7 +110,7 @@ def resolvable_paths(paths: Sequence[StorePath | Path]) -> list[StorePath]:
110
110
  current workspace.
111
111
  """
112
112
  ws = current_ws()
113
- resolvable = list(filter(None, (ws.resolve_path(p) for p in paths)))
113
+ resolvable = list(filter(None, (ws.resolve_to_store_path(p) for p in paths)))
114
114
  return resolvable
115
115
 
116
116
 
@@ -22,7 +22,7 @@ from kash.model.paths_model import StorePath
22
22
  from kash.shell.output.shell_output import PrintHooks
23
23
  from kash.utils.common.format_utils import fmt_loc
24
24
  from kash.utils.common.uniquifier import Uniquifier
25
- from kash.utils.common.url import Locator, Url, is_url
25
+ from kash.utils.common.url import Locator, UnresolvedLocator, Url, is_url
26
26
  from kash.utils.errors import FileExists, FileNotFound, InvalidFilename, SkippableError
27
27
  from kash.utils.file_utils.file_formats_model import Format
28
28
  from kash.utils.file_utils.file_walk import walk_by_dir
@@ -182,7 +182,7 @@ class FileStore(Workspace):
182
182
  except (FileNotFoundError, InvalidFilename):
183
183
  pass
184
184
 
185
- def resolve_path(self, path: Path | StorePath) -> StorePath | None:
185
+ def resolve_to_store_path(self, path: Path | StorePath) -> StorePath | None:
186
186
  """
187
187
  Return a StorePath if the given path is within the store, otherwise None.
188
188
  If it is already a StorePath, return it unchanged.
@@ -195,6 +195,21 @@ class FileStore(Workspace):
195
195
  else:
196
196
  return None
197
197
 
198
+ def resolve_to_abs_path(self, path: Path | StorePath) -> Path:
199
+ """
200
+ Return an absolute path, resolving any store paths to within the store
201
+ and resolving other paths like regular `Path.resolve()`.
202
+ """
203
+ store_path = self.resolve_to_store_path(path)
204
+ if store_path:
205
+ return self.base_dir / store_path
206
+ elif path.is_absolute():
207
+ return path
208
+ else:
209
+ # Unspecified relative paths resolved to cwd.
210
+ # TODO: Consider if such paths might be store paths.
211
+ return path.resolve()
212
+
198
213
  def exists(self, store_path: StorePath) -> bool:
199
214
  """
200
215
  Check given store path refers to an existing file.
@@ -290,7 +305,7 @@ class FileStore(Workspace):
290
305
  elif item_id in self.id_map and self.exists(self.id_map[item_id]):
291
306
  # If this item has an identity and we've saved under that id before, use the same store path.
292
307
  store_path = self.id_map[item_id]
293
- log.warning(
308
+ log.info(
294
309
  "Found existing item with same id:\n%s",
295
310
  fmt_lines([fmt_loc(store_path), item_id]),
296
311
  )
@@ -334,6 +349,7 @@ class FileStore(Workspace):
334
349
  skip_dup_names: bool = False,
335
350
  as_tmp: bool = False,
336
351
  no_format: bool = False,
352
+ no_frontmatter: bool = False,
337
353
  ) -> StorePath:
338
354
  """
339
355
  Save the item. Uses the `store_path` if it's already set or generates a new one.
@@ -342,6 +358,8 @@ class FileStore(Workspace):
342
358
  Unless `no_format` is true, also normalizes body text formatting (for Markdown)
343
359
  and updates the item's body to match.
344
360
 
361
+ If `no_frontmatter` is true, will not add frontmatter metadata to the item.
362
+
345
363
  If `overwrite` is true, will overwrite a file that has the same path.
346
364
 
347
365
  If `as_tmp` is true, will save the item to a temporary file.
@@ -390,9 +408,14 @@ class FileStore(Workspace):
390
408
 
391
409
  # Now save the new item.
392
410
  try:
393
- if item.external_path:
411
+ supports_frontmatter = item.format and item.format.supports_frontmatter
412
+ # For binary or unknown formats or if we're not adding frontmatter, copy the file exactly.
413
+ if item.external_path and (no_frontmatter or not supports_frontmatter):
394
414
  copyfile_atomic(item.external_path, full_path, make_parents=True)
395
415
  else:
416
+ # Save as a text item with frontmatter.
417
+ if item.external_path:
418
+ item.body = Path(item.external_path).read_text()
396
419
  if overwrite and full_path.exists():
397
420
  log.info(
398
421
  "Overwrite is enabled and a previous file exists so will archive it: %s",
@@ -448,7 +471,7 @@ class FileStore(Workspace):
448
471
 
449
472
  def import_item(
450
473
  self,
451
- locator: Locator,
474
+ locator: UnresolvedLocator,
452
475
  *,
453
476
  as_type: ItemType | None = None,
454
477
  reimport: bool = False,
@@ -462,7 +485,10 @@ class FileStore(Workspace):
462
485
  """
463
486
  from kash.web_content.canon_url import canonicalize_url
464
487
 
465
- if is_url(locator):
488
+ if isinstance(locator, StorePath) and not reimport:
489
+ log.info("Store path already imported: %s", fmt_loc(locator))
490
+ return locator
491
+ elif is_url(locator):
466
492
  # Import a URL as a resource.
467
493
  orig_url = Url(str(locator))
468
494
  url = canonicalize_url(orig_url)
@@ -480,9 +506,6 @@ class FileStore(Workspace):
480
506
  else:
481
507
  store_path = self.save(item)
482
508
  return store_path
483
- elif isinstance(locator, StorePath) and not reimport:
484
- log.info("Store path already imported: %s", fmt_loc(locator))
485
- return locator
486
509
  else:
487
510
  # We have a path, possibly outside of or inside of the store.
488
511
  path = Path(locator).resolve()
@@ -553,6 +576,13 @@ class FileStore(Workspace):
553
576
  self.import_item(locator, as_type=as_type, reimport=reimport) for locator in locators
554
577
  ]
555
578
 
579
+ def import_and_load(self, locator: UnresolvedLocator) -> Item:
580
+ """
581
+ Import a locator and return the item.
582
+ """
583
+ store_path = self.import_item(locator)
584
+ return self.load(store_path)
585
+
556
586
  def _filter_selection_paths(self):
557
587
  """
558
588
  Filter out any paths that don't exist from all selections.
@@ -695,14 +725,20 @@ class FileStore(Workspace):
695
725
  dirs_ignored,
696
726
  )
697
727
 
698
- def normalize(self, store_path: StorePath) -> StorePath:
728
+ def normalize(
729
+ self,
730
+ store_path: StorePath,
731
+ *,
732
+ no_format: bool = False,
733
+ no_frontmatter: bool = False,
734
+ ) -> StorePath:
699
735
  """
700
736
  Normalize an item or all items in a folder to make sure contents are in current
701
- format.
737
+ format. This is the same as loading and saving the item.
702
738
  """
703
739
  log.info("Normalizing item: %s", fmt_path(store_path))
704
740
 
705
741
  item = self.load(store_path)
706
- new_store_path = self.save(item)
742
+ new_store_path = self.save(item, no_format=no_format, no_frontmatter=no_frontmatter)
707
743
 
708
744
  return new_store_path