kash-shell 0.3.33__tar.gz → 0.3.34__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 (335) hide show
  1. {kash_shell-0.3.33 → kash_shell-0.3.34}/PKG-INFO +3 -3
  2. {kash_shell-0.3.33 → kash_shell-0.3.34}/pyproject.toml +3 -2
  3. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/markdownify_html.py +1 -3
  4. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/summarize_as_bullets.py +1 -1
  5. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/model/actions_model.py +2 -2
  6. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/model/items_model.py +11 -2
  7. kash_shell-0.3.34/src/kash/utils/api_utils/multitask_gather.py +134 -0
  8. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/rich_custom/multitask_status.py +84 -10
  9. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/text_handling/markdown_footnotes.py +16 -43
  10. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/text_handling/markdown_utils.py +108 -28
  11. {kash_shell-0.3.33 → kash_shell-0.3.34}/uv.lock +78 -78
  12. kash_shell-0.3.33/src/kash/utils/api_utils/multitask_gather.py +0 -74
  13. {kash_shell-0.3.33 → kash_shell-0.3.34}/.copier-answers.yml +0 -0
  14. {kash_shell-0.3.33 → kash_shell-0.3.34}/.cursor/rules/general.mdc +0 -0
  15. {kash_shell-0.3.33 → kash_shell-0.3.34}/.cursor/rules/python.mdc +0 -0
  16. {kash_shell-0.3.33 → kash_shell-0.3.34}/.env.template +0 -0
  17. {kash_shell-0.3.33 → kash_shell-0.3.34}/.github/workflows/ci.yml +0 -0
  18. {kash_shell-0.3.33 → kash_shell-0.3.34}/.github/workflows/publish.yml +0 -0
  19. {kash_shell-0.3.33 → kash_shell-0.3.34}/.gitignore +0 -0
  20. {kash_shell-0.3.33 → kash_shell-0.3.34}/LICENSE +0 -0
  21. {kash_shell-0.3.33 → kash_shell-0.3.34}/Makefile +0 -0
  22. {kash_shell-0.3.33 → kash_shell-0.3.34}/README.md +0 -0
  23. {kash_shell-0.3.33 → kash_shell-0.3.34}/development.md +0 -0
  24. {kash_shell-0.3.33 → kash_shell-0.3.34}/devtools/generate_readme.xsh +0 -0
  25. {kash_shell-0.3.33 → kash_shell-0.3.34}/devtools/lint.py +0 -0
  26. {kash_shell-0.3.33 → kash_shell-0.3.34}/devtools/profile_main.py +0 -0
  27. {kash_shell-0.3.33 → kash_shell-0.3.34}/installation.md +0 -0
  28. {kash_shell-0.3.33 → kash_shell-0.3.34}/publishing.md +0 -0
  29. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/__init__.py +0 -0
  30. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/__main__.py +0 -0
  31. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/__init__.py +0 -0
  32. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/assistant_chat.py +0 -0
  33. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/chat.py +0 -0
  34. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/combine_docs.py +0 -0
  35. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/concat_docs.py +0 -0
  36. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/format_markdown_template.py +0 -0
  37. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/minify_html.py +0 -0
  38. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/readability.py +0 -0
  39. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/render_as_html.py +0 -0
  40. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/save_sidematter_meta.py +0 -0
  41. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/show_webpage.py +0 -0
  42. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/strip_html.py +0 -0
  43. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/tabbed_webpage_config.py +0 -0
  44. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/tabbed_webpage_generate.py +0 -0
  45. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/core/zip_sidematter.py +0 -0
  46. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/meta/write_instructions.py +0 -0
  47. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/actions/meta/write_new_action.py +0 -0
  48. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/__init__.py +0 -0
  49. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/base/basic_file_commands.py +0 -0
  50. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/base/browser_commands.py +0 -0
  51. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/base/debug_commands.py +0 -0
  52. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/base/diff_commands.py +0 -0
  53. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/base/files_command.py +0 -0
  54. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/base/general_commands.py +0 -0
  55. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/base/logs_commands.py +0 -0
  56. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/base/model_commands.py +0 -0
  57. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/base/reformat_command.py +0 -0
  58. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/base/search_command.py +0 -0
  59. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/base/show_command.py +0 -0
  60. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/extras/parse_uv_lock.py +0 -0
  61. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/extras/utils_commands.py +0 -0
  62. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/help/assistant_commands.py +0 -0
  63. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/help/doc_commands.py +0 -0
  64. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/help/help_commands.py +0 -0
  65. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/help/logo.py +0 -0
  66. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/help/welcome.py +0 -0
  67. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/workspace/selection_commands.py +0 -0
  68. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/commands/workspace/workspace_commands.py +0 -0
  69. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/config/__init__.py +0 -0
  70. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/config/capture_output.py +0 -0
  71. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/config/colors.py +0 -0
  72. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/config/env_settings.py +0 -0
  73. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/config/init.py +0 -0
  74. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/config/lazy_imports.py +0 -0
  75. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/config/logger.py +0 -0
  76. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/config/logger_basic.py +0 -0
  77. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/config/logo.txt +0 -0
  78. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/config/server_config.py +0 -0
  79. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/config/settings.py +0 -0
  80. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/config/setup.py +0 -0
  81. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/config/suppress_warnings.py +0 -0
  82. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/config/text_styles.py +0 -0
  83. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/config/unified_live.py +0 -0
  84. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/__init__.py +0 -0
  85. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/all_docs.py +0 -0
  86. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/load_actions_info.py +0 -0
  87. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/load_api_docs.py +0 -0
  88. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/load_help_topics.py +0 -0
  89. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/load_source_code.py +0 -0
  90. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/markdown/api_docs_template.md +0 -0
  91. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/markdown/assistant_instructions_template.md +0 -0
  92. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/markdown/readme_template.md +0 -0
  93. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/markdown/topics/a1_what_is_kash.md +0 -0
  94. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/markdown/topics/a2_installation.md +0 -0
  95. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/markdown/topics/a3_getting_started.md +0 -0
  96. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/markdown/topics/a4_elements.md +0 -0
  97. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/markdown/topics/a5_tips_for_use_with_other_tools.md +0 -0
  98. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/markdown/topics/b0_philosophy_of_kash.md +0 -0
  99. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/markdown/topics/b1_kash_overview.md +0 -0
  100. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/markdown/topics/b2_workspace_and_file_formats.md +0 -0
  101. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/markdown/topics/b3_modern_shell_tool_recommendations.md +0 -0
  102. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/markdown/topics/b4_faq.md +0 -0
  103. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/markdown/warning.md +0 -0
  104. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs/markdown/welcome.md +0 -0
  105. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs_base/docs_base.py +0 -0
  106. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs_base/load_custom_command_info.py +0 -0
  107. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs_base/load_faqs.py +0 -0
  108. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs_base/load_recipe_snippets.py +0 -0
  109. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs_base/recipes/general_system_commands.sh +0 -0
  110. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs_base/recipes/python_dev_commands.sh +0 -0
  111. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/docs_base/recipes/tldr_standard_commands.sh +0 -0
  112. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/embeddings/cosine.py +0 -0
  113. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/embeddings/embeddings.py +0 -0
  114. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/embeddings/text_similarity.py +0 -0
  115. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/__init__.py +0 -0
  116. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/action_decorators.py +0 -0
  117. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/action_exec.py +0 -0
  118. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/action_registry.py +0 -0
  119. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/combiners.py +0 -0
  120. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/command_exec.py +0 -0
  121. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/command_registry.py +0 -0
  122. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/fetch_url_items.py +0 -0
  123. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/history.py +0 -0
  124. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/importing.py +0 -0
  125. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/llm_transforms.py +0 -0
  126. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/precondition_checks.py +0 -0
  127. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/precondition_registry.py +0 -0
  128. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/preconditions.py +0 -0
  129. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/resolve_args.py +0 -0
  130. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/runtime_settings.py +0 -0
  131. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec/shell_callable_action.py +0 -0
  132. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec_model/__init__.py +0 -0
  133. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec_model/args_model.py +0 -0
  134. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec_model/commands_model.py +0 -0
  135. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec_model/script_model.py +0 -0
  136. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/exec_model/shell_model.py +0 -0
  137. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/file_storage/__init__.py +0 -0
  138. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/file_storage/file_store.py +0 -0
  139. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/file_storage/item_file_format.py +0 -0
  140. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/file_storage/item_id_index.py +0 -0
  141. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/file_storage/metadata_dirs.py +0 -0
  142. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/file_storage/persisted_yaml.py +0 -0
  143. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/file_storage/store_cache_warmer.py +0 -0
  144. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/file_storage/store_filenames.py +0 -0
  145. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/help/__init__.py +0 -0
  146. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/help/assistant.py +0 -0
  147. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/help/assistant_instructions.py +0 -0
  148. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/help/assistant_output.py +0 -0
  149. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/help/function_param_info.py +0 -0
  150. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/help/help_embeddings.py +0 -0
  151. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/help/help_lookups.py +0 -0
  152. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/help/help_pages.py +0 -0
  153. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/help/help_printing.py +0 -0
  154. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/help/help_types.py +0 -0
  155. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/help/recommended_commands.py +0 -0
  156. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/help/tldr_help.py +0 -0
  157. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/llm_utils/__init__.py +0 -0
  158. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/llm_utils/clean_headings.py +0 -0
  159. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/llm_utils/custom_sliding_transforms.py +0 -0
  160. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/llm_utils/fuzzy_parsing.py +0 -0
  161. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/llm_utils/init_litellm.py +0 -0
  162. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/llm_utils/llm_api_keys.py +0 -0
  163. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/llm_utils/llm_completion.py +0 -0
  164. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/llm_utils/llm_messages.py +0 -0
  165. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/llm_utils/llm_names.py +0 -0
  166. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/llm_utils/llms.py +0 -0
  167. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/local_server/__init__.py +0 -0
  168. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/local_server/local_server.py +0 -0
  169. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/local_server/local_server_commands.py +0 -0
  170. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/local_server/local_server_routes.py +0 -0
  171. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/local_server/local_url_formatters.py +0 -0
  172. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/local_server/port_tools.py +0 -0
  173. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/local_server/rich_html_template.py +0 -0
  174. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/mcp/__init__.py +0 -0
  175. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/mcp/mcp_cli.py +0 -0
  176. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/mcp/mcp_main.py +0 -0
  177. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/mcp/mcp_server_commands.py +0 -0
  178. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/mcp/mcp_server_routes.py +0 -0
  179. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/mcp/mcp_server_sse.py +0 -0
  180. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/mcp/mcp_server_stdio.py +0 -0
  181. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/media_base/__init__.py +0 -0
  182. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/media_base/audio_processing.py +0 -0
  183. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/media_base/media_cache.py +0 -0
  184. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/media_base/media_services.py +0 -0
  185. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/media_base/media_tools.py +0 -0
  186. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/media_base/services/local_file_media.py +0 -0
  187. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/media_base/timestamp_citations.py +0 -0
  188. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/media_base/transcription_deepgram.py +0 -0
  189. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/media_base/transcription_format.py +0 -0
  190. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/media_base/transcription_whisper.py +0 -0
  191. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/model/__init__.py +0 -0
  192. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/model/assistant_response_model.py +0 -0
  193. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/model/compound_actions_model.py +0 -0
  194. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/model/concept_model.py +0 -0
  195. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/model/exec_model.py +0 -0
  196. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/model/graph_model.py +0 -0
  197. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/model/language_list.py +0 -0
  198. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/model/llm_actions_model.py +0 -0
  199. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/model/media_model.py +0 -0
  200. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/model/operations_model.py +0 -0
  201. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/model/params_model.py +0 -0
  202. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/model/paths_model.py +0 -0
  203. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/model/preconditions_model.py +0 -0
  204. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/__init__.py +0 -0
  205. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/completions/completion_scoring.py +0 -0
  206. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/completions/completion_types.py +0 -0
  207. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/completions/shell_completions.py +0 -0
  208. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/file_icons/color_for_format.py +0 -0
  209. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/file_icons/nerd_icons.py +0 -0
  210. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/input/__init__.py +0 -0
  211. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/input/input_prompts.py +0 -0
  212. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/input/inquirer_settings.py +0 -0
  213. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/input/param_inputs.py +0 -0
  214. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/input/shell_confirm.py +0 -0
  215. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/output/__init__.py +0 -0
  216. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/output/kerm_code_utils.py +0 -0
  217. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/output/kerm_codes.py +0 -0
  218. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/output/kmarkdown.py +0 -0
  219. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/output/shell_formatting.py +0 -0
  220. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/output/shell_output.py +0 -0
  221. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/shell_main.py +0 -0
  222. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/ui/__init__.py +0 -0
  223. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/ui/shell_results.py +0 -0
  224. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/ui/shell_syntax.py +0 -0
  225. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/utils/exception_printing.py +0 -0
  226. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/utils/native_utils.py +0 -0
  227. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/utils/shell_function_wrapper.py +0 -0
  228. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/shell/version.py +0 -0
  229. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/__init__.py +0 -0
  230. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/api_utils/api_retries.py +0 -0
  231. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/api_utils/cache_requests_limited.py +0 -0
  232. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/api_utils/gather_limited.py +0 -0
  233. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/api_utils/http_utils.py +0 -0
  234. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/api_utils/progress_protocol.py +0 -0
  235. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/__init__.py +0 -0
  236. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/format_utils.py +0 -0
  237. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/function_inspect.py +0 -0
  238. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/import_utils.py +0 -0
  239. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/lazyobject.py +0 -0
  240. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/obj_replace.py +0 -0
  241. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/parse_docstring.py +0 -0
  242. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/parse_key_vals.py +0 -0
  243. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/parse_shell_args.py +0 -0
  244. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/s3_utils.py +0 -0
  245. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/stack_traces.py +0 -0
  246. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/task_stack.py +0 -0
  247. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/testing.py +0 -0
  248. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/type_utils.py +0 -0
  249. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/uniquifier.py +0 -0
  250. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/url.py +0 -0
  251. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/common/url_slice.py +0 -0
  252. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/errors.py +0 -0
  253. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/file_formats/chat_format.py +0 -0
  254. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/file_utils/__init__.py +0 -0
  255. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/file_utils/csv_utils.py +0 -0
  256. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/file_utils/dir_info.py +0 -0
  257. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/file_utils/file_ext.py +0 -0
  258. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/file_utils/file_formats.py +0 -0
  259. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/file_utils/file_formats_model.py +0 -0
  260. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/file_utils/file_sort_filter.py +0 -0
  261. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/file_utils/file_walk.py +0 -0
  262. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/file_utils/filename_parsing.py +0 -0
  263. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/file_utils/ignore_files.py +0 -0
  264. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/file_utils/mtime_cache.py +0 -0
  265. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/file_utils/path_utils.py +0 -0
  266. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/lang_utils/__init__.py +0 -0
  267. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/lang_utils/capitalization.py +0 -0
  268. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/rich_custom/__init__.py +0 -0
  269. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/rich_custom/ansi_cell_len.py +0 -0
  270. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/rich_custom/rich_char_transform.py +0 -0
  271. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/rich_custom/rich_indent.py +0 -0
  272. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/rich_custom/rich_markdown_fork.py +0 -0
  273. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/text_handling/doc_normalization.py +0 -0
  274. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/text_handling/escape_html_tags.py +0 -0
  275. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/text_handling/markdown_render.py +0 -0
  276. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/text_handling/markdownify_utils.py +0 -0
  277. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/utils/text_handling/unified_diffs.py +0 -0
  278. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_content/canon_url.py +0 -0
  279. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_content/dir_store.py +0 -0
  280. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_content/file_cache_utils.py +0 -0
  281. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_content/file_processing.py +0 -0
  282. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_content/local_file_cache.py +0 -0
  283. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_content/web_extract.py +0 -0
  284. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_content/web_extract_justext.py +0 -0
  285. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_content/web_extract_readabilipy.py +0 -0
  286. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_content/web_fetch.py +0 -0
  287. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_content/web_page_model.py +0 -0
  288. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/__init__.py +0 -0
  289. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/tabbed_webpage.py +0 -0
  290. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/template_render.py +0 -0
  291. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/templates/base_styles.css.jinja +0 -0
  292. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/templates/base_webpage.html.jinja +0 -0
  293. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/templates/components/toc_scripts.js.jinja +0 -0
  294. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/templates/components/toc_styles.css.jinja +0 -0
  295. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/templates/components/tooltip_scripts.js.jinja +0 -0
  296. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/templates/components/tooltip_styles.css.jinja +0 -0
  297. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/templates/components/youtube_popover_scripts.js.jinja +0 -0
  298. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/templates/components/youtube_popover_styles.css.jinja +0 -0
  299. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/templates/content_styles.css.jinja +0 -0
  300. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/templates/explain_view.html.jinja +0 -0
  301. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/templates/item_view.html.jinja +0 -0
  302. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/templates/simple_webpage.html.jinja +0 -0
  303. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/templates/tabbed_webpage.html.jinja +0 -0
  304. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/templates/youtube_webpage.html.jinja +0 -0
  305. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/web_gen/webpage_render.py +0 -0
  306. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/workspaces/__init__.py +0 -0
  307. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/workspaces/param_state.py +0 -0
  308. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/workspaces/selections.py +0 -0
  309. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/workspaces/source_items.py +0 -0
  310. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/workspaces/workspace_dirs.py +0 -0
  311. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/workspaces/workspace_output.py +0 -0
  312. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/workspaces/workspace_registry.py +0 -0
  313. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/workspaces/workspaces.py +0 -0
  314. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/xonsh_custom/command_nl_utils.py +0 -0
  315. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/xonsh_custom/custom_shell.py +0 -0
  316. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/xonsh_custom/customize_prompt.py +0 -0
  317. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/xonsh_custom/load_into_xonsh.py +0 -0
  318. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/xonsh_custom/shell_load_commands.py +0 -0
  319. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/xonsh_custom/shell_which.py +0 -0
  320. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/xonsh_custom/xonsh_completers.py +0 -0
  321. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/xonsh_custom/xonsh_env.py +0 -0
  322. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/xonsh_custom/xonsh_keybindings.py +0 -0
  323. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/xonsh_custom/xonsh_modern_tools.py +0 -0
  324. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/xonsh_custom/xonsh_ranking_completer.py +0 -0
  325. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/xontrib/fnm.py +0 -0
  326. {kash_shell-0.3.33 → kash_shell-0.3.34}/src/kash/xontrib/kash_extension.py +0 -0
  327. {kash_shell-0.3.33 → kash_shell-0.3.34}/tests/file_storage/test_file_store.py +0 -0
  328. {kash_shell-0.3.33 → kash_shell-0.3.34}/tests/kash/utils/api_utils/test_gather_limited.py +0 -0
  329. {kash_shell-0.3.33 → kash_shell-0.3.34}/tests/kash/utils/file_utils/test_csv_utils.py +0 -0
  330. {kash_shell-0.3.33 → kash_shell-0.3.34}/tests/kash/utils/rich_custom/test_multitask_status.py +0 -0
  331. {kash_shell-0.3.33 → kash_shell-0.3.34}/tests/model/test_item_serialization.py +0 -0
  332. {kash_shell-0.3.33 → kash_shell-0.3.34}/tests/shell/input/interactive_input_test.py +0 -0
  333. {kash_shell-0.3.33 → kash_shell-0.3.34}/tests/test_shell.py +0 -0
  334. {kash_shell-0.3.33 → kash_shell-0.3.34}/tests/utils/text_handling/test_markdown_footnotes.py +0 -0
  335. {kash_shell-0.3.33 → kash_shell-0.3.34}/tests/web_gen/test_social_metadata.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kash-shell
3
- Version: 0.3.33
3
+ Version: 0.3.34
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>
@@ -20,7 +20,7 @@ Requires-Dist: aiolimiter>=1.2.1
20
20
  Requires-Dist: anyio>=4.8.0
21
21
  Requires-Dist: audioop-lts>=0.2.1; python_version >= '3.13'
22
22
  Requires-Dist: cachetools>=5.5.2
23
- Requires-Dist: chopdiff>=0.2.5
23
+ Requires-Dist: chopdiff>=0.2.6
24
24
  Requires-Dist: clideps>=0.1.4
25
25
  Requires-Dist: colour>=0.1.5
26
26
  Requires-Dist: cssselect>=1.2.0
@@ -41,7 +41,7 @@ Requires-Dist: litellm>=1.74.15.post1
41
41
  Requires-Dist: markdownify>=0.13.1
42
42
  Requires-Dist: mcp-proxy>=0.5.0
43
43
  Requires-Dist: mcp>=1.6.0
44
- Requires-Dist: openai>=1.66.3
44
+ Requires-Dist: openai==1.99.9
45
45
  Requires-Dist: pandas>=2.2.3
46
46
  Requires-Dist: patch-ng>=1.18.1
47
47
  Requires-Dist: pathspec>=0.12.1
@@ -48,7 +48,7 @@ dependencies = [
48
48
  "flowmark>=0.5.3",
49
49
  "frontmatter-format>=0.2.3",
50
50
  "sidematter-format>=0.0.5",
51
- "chopdiff>=0.2.5",
51
+ "chopdiff>=0.2.6",
52
52
  "clideps>=0.1.4",
53
53
  "tminify>=0.1.6",
54
54
  # Shell and file essentials:
@@ -84,7 +84,7 @@ dependencies = [
84
84
  "curl-cffi>=0.11.4",
85
85
  # LLM and API essentials:
86
86
  "tiktoken>=0.9.0",
87
- "openai>=1.66.3",
87
+ "openai==1.99.9", # FIXME: Pinning for now due to import errors (ImportError: cannot import name 'ResponseTextConfig' from 'openai.types.responses.response')
88
88
  "litellm>=1.74.15.post1",
89
89
  "pyrate-limiter>=3.7.0",
90
90
  "aiolimiter>=1.2.1",
@@ -130,6 +130,7 @@ kash-mcp = "kash.mcp.mcp_cli:main"
130
130
 
131
131
  [tool.uv.sources]
132
132
  # For local development:
133
+ # flowmark = { path = "../flowmark", editable = true }
133
134
  # clideps = { path = "../clideps", editable = true }
134
135
  # tminify = { path = "../tminify", editable = true }
135
136
  # chopdiff = { path = "../chopdiff", editable = true }
@@ -13,9 +13,7 @@ from kash.web_content.web_extract_readabilipy import extract_text_readabilipy
13
13
  log = get_logger(__name__)
14
14
 
15
15
 
16
- @kash_action(
17
- precondition=is_url_resource | has_html_body, output_format=Format.markdown, mcp_tool=True
18
- )
16
+ @kash_action(precondition=is_url_resource | has_html_body, output_format=Format.markdown)
19
17
  def markdownify_html(item: Item) -> Item:
20
18
  """
21
19
  Converts raw HTML or the URL of an HTML page to Markdown, fetching with the content
@@ -47,7 +47,7 @@ llm_options = LLMOptions(
47
47
  )
48
48
 
49
49
 
50
- @kash_action(llm_options=llm_options, params=common_params("model"), mcp_tool=True)
50
+ @kash_action(llm_options=llm_options, params=common_params("model"))
51
51
  def summarize_as_bullets(item: Item, model: LLMName = LLM.default_standard) -> Item:
52
52
  """
53
53
  Summarize text as bullet points.
@@ -585,9 +585,9 @@ class Action(ABC):
585
585
  "type": "array",
586
586
  "items": {
587
587
  "type": "string",
588
- "description": "A path to a local file or a URL",
588
+ "description": "A URL or S3 URL or a workspace file path, e.g. https://example.com/some/file/path or s3://somebucket/some/file/path or some/file/path",
589
589
  },
590
- "description": f"Input items ({self.expected_args.as_str()})",
590
+ "description": f"A list of paths or URLs of input items ({self.expected_args.as_str()}). Use an array of length one for a single input.",
591
591
  }
592
592
 
593
593
  # Set min/max items.
@@ -7,6 +7,7 @@ from datetime import UTC, datetime
7
7
  from enum import Enum
8
8
  from pathlib import Path
9
9
  from typing import TYPE_CHECKING, Any, NotRequired, TypedDict, TypeVar, Unpack
10
+ from urllib.parse import urlparse
10
11
 
11
12
  from frontmatter_format import from_yaml_string, new_yaml
12
13
  from prettyfmt import (
@@ -570,12 +571,19 @@ class Item:
570
571
  from kash.file_storage.store_filenames import parse_item_filename
571
572
 
572
573
  # Prefer original to external, e.g. if we know the original but the external might
573
- # be a cache filename.
574
- path = self.store_path or self.original_filename or self.external_path
574
+ # be a cache filename. Also check
575
+ path = (
576
+ self.store_path
577
+ or self.original_filename
578
+ or self.external_path
579
+ or (self.url and urlparse(self.url).path)
580
+ or ""
581
+ ).strip()
575
582
  if path:
576
583
  path_name, _item_type, _format, _file_ext = parse_item_filename(Path(path).name)
577
584
  else:
578
585
  path_name = None
586
+
579
587
  return path_name
580
588
 
581
589
  def slug_name(
@@ -607,6 +615,7 @@ class Item:
607
615
 
608
616
  slug = self.slug_name()
609
617
  full_suffix = self.get_full_suffix()
618
+
610
619
  return join_suffix(slug, full_suffix)
611
620
 
612
621
  def body_heading(self, allowed_tags: tuple[str, ...] = ("h1", "h2")) -> str | None:
@@ -0,0 +1,134 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Iterable, Sequence
4
+ from dataclasses import dataclass
5
+ from typing import Any, Generic, TypeVar, cast
6
+
7
+ from strif import abbrev_list
8
+
9
+ from kash.config.logger import get_logger
10
+ from kash.config.settings import global_settings
11
+ from kash.shell.output.shell_output import multitask_status
12
+ from kash.utils.api_utils.api_retries import RetrySettings
13
+ from kash.utils.api_utils.gather_limited import FuncTask, Limit, gather_limited_sync
14
+
15
+ T = TypeVar("T")
16
+
17
+ log = get_logger(name=__name__)
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class MultitaskResult(Generic[T]):
22
+ """
23
+ Container for results from multitask_gather preserving original order.
24
+ Access `.successes` and `.errors` to get partitioned views.
25
+ """
26
+
27
+ raw_results: list[T | BaseException]
28
+
29
+ @property
30
+ def successes_or_none(self) -> list[T | None]:
31
+ """
32
+ Return a list of successes or None, aligned with the original order.
33
+ """
34
+ return [
35
+ None if isinstance(item, BaseException) else cast(T, item) for item in self.raw_results
36
+ ]
37
+
38
+ @property
39
+ def successes(self) -> list[T]:
40
+ """
41
+ Return a list of successes only. May be shorter than the original list.
42
+ """
43
+ return [cast(T, item) for item in self.raw_results if not isinstance(item, BaseException)]
44
+
45
+ @property
46
+ def errors(self) -> list[BaseException]:
47
+ """
48
+ Return a list of errors only. May be shorter than the original list.
49
+ """
50
+ return [item for item in self.raw_results if isinstance(item, BaseException)]
51
+
52
+
53
+ def _default_labeler(total: int) -> Callable[[int, Any], str]:
54
+ def labeler(i: int, _spec: Any) -> str:
55
+ return f"Task {i + 1}/{total}"
56
+
57
+ return labeler
58
+
59
+
60
+ async def multitask_gather(
61
+ tasks: Iterable[FuncTask[T]] | Sequence[FuncTask[T]],
62
+ *,
63
+ labeler: Callable[[int, Any], str] | None = None,
64
+ limit: Limit | None = None,
65
+ bucket_limits: dict[str, Limit] | None = None,
66
+ retry_settings: RetrySettings | None = None,
67
+ show_progress: bool = True,
68
+ ) -> MultitaskResult[T]:
69
+ """
70
+ Run many `FuncTask`s concurrently with shared progress UI and rate limits.
71
+
72
+ This wraps the standard pattern of creating a status context, providing a labeler,
73
+ and calling `gather_limited_sync` with common options.
74
+
75
+ - `labeler` can be omitted; a simple "Task X/Y" label will be used.
76
+ - If `limit` is not provided, defaults are taken from `global_settings()`.
77
+ - If `show_progress` is False, tasks are run without the status context.
78
+ - Exceptions are collected (using return_exceptions=True). Use properties on the
79
+ returned `MultitaskResult` to access `.successes` and `.errors`.
80
+ """
81
+
82
+ # Normalize tasks to a list for length and stable iteration
83
+ task_list: list[FuncTask[T]] = list(tasks)
84
+
85
+ # Provide a default labeler if none is supplied
86
+ effective_labeler: Callable[[int, Any], str] = (
87
+ labeler if labeler is not None else _default_labeler(len(task_list))
88
+ )
89
+
90
+ # Provide sensible default rate limits if none are supplied
91
+ effective_limit: Limit = (
92
+ limit
93
+ if limit is not None
94
+ else Limit(
95
+ rps=global_settings().limit_rps,
96
+ concurrency=global_settings().limit_concurrency,
97
+ )
98
+ )
99
+
100
+ if not show_progress:
101
+ log.warning("Running %d tasks (progress disabled)…", len(task_list))
102
+
103
+ async with multitask_status(enabled=show_progress) as status:
104
+ raw_results = cast(
105
+ list[T | BaseException],
106
+ await gather_limited_sync(
107
+ *task_list,
108
+ limit=effective_limit,
109
+ bucket_limits=bucket_limits,
110
+ status=status,
111
+ labeler=effective_labeler,
112
+ retry_settings=retry_settings,
113
+ return_exceptions=True,
114
+ ),
115
+ )
116
+
117
+ result = MultitaskResult[T](raw_results=raw_results)
118
+
119
+ if result.errors:
120
+ log.warning(
121
+ "multitask_gather: had %d errors (out of %d tasks): %s",
122
+ len(result.errors),
123
+ len(task_list),
124
+ abbrev_list(result.errors),
125
+ )
126
+ log.error(
127
+ "multitask_gather: first error (full traceback):",
128
+ exc_info=(
129
+ type(result.errors[0]),
130
+ result.errors[0],
131
+ result.errors[0].__traceback__,
132
+ ),
133
+ )
134
+ return result
@@ -72,6 +72,8 @@ RUNNING_SYMBOL = ""
72
72
  DEFAULT_LABEL_WIDTH = 40
73
73
  DEFAULT_PROGRESS_WIDTH = 20
74
74
 
75
+ MAX_DISPLAY_TASKS = 20
76
+
75
77
 
76
78
  # Calculate spinner width to maintain column alignment
77
79
  def _get_spinner_width(spinner_name: str) -> int:
@@ -101,6 +103,9 @@ class StatusSettings:
101
103
  transient: bool = True
102
104
  refresh_per_second: float = 10
103
105
  styles: StatusStyles = DEFAULT_STYLES
106
+ # Maximum number of tasks to keep visible in the live display.
107
+ # Older completed/skipped/failed tasks beyond this cap will be removed from the live view.
108
+ max_display_tasks: int = MAX_DISPLAY_TASKS
104
109
 
105
110
 
106
111
  class SpinnerStatusColumn(ProgressColumn):
@@ -298,6 +303,10 @@ class MultiTaskStatus(AbstractAsyncContextManager):
298
303
  self._task_info: dict[int, TaskInfo] = {}
299
304
  self._next_id: int = 1
300
305
  self._rich_task_ids: dict[int, TaskID] = {} # Map our IDs to Rich Progress IDs
306
+ # Track order of tasks added to the Progress so we can prune oldest completed ones
307
+ self._displayed_task_order: list[int] = []
308
+ # Track tasks pruned from the live display so we don't re-add them later
309
+ self._pruned_task_ids: set[int] = set()
301
310
 
302
311
  # Unified live integration
303
312
  self._unified_live: Any | None = None # Reference to the global unified live
@@ -442,6 +451,10 @@ class MultiTaskStatus(AbstractAsyncContextManager):
442
451
  progress_display=None,
443
452
  )
444
453
  self._rich_task_ids[task_id] = rich_task_id
454
+ self._displayed_task_order.append(task_id)
455
+
456
+ # Prune if too many tasks are visible (prefer removing completed ones)
457
+ self._prune_completed_tasks_if_needed()
445
458
 
446
459
  async def set_progress_display(self, task_id: int, display: RenderableType) -> None:
447
460
  """
@@ -536,18 +549,31 @@ class MultiTaskStatus(AbstractAsyncContextManager):
536
549
 
537
550
  # Complete the progress bar and stop spinner
538
551
  if rich_task_id is not None:
539
- total = self._progress.tasks[rich_task_id].total or 1
552
+ # Safely find the Task by id; Progress.tasks is a list, not a dict
553
+ task_obj = next((t for t in self._progress.tasks if t.id == rich_task_id), None)
554
+ if task_obj is not None and task_obj.total is not None:
555
+ total = task_obj.total
556
+ else:
557
+ total = task_info.steps_total or 1
540
558
  self._progress.update(rich_task_id, completed=total, task_info=task_info)
541
559
  else:
542
- # Task was never started, but we still need to add it to show completion
543
- rich_task_id = self._progress.add_task(
544
- "",
545
- total=task_info.steps_total,
546
- label=task_info.label,
547
- completed=task_info.steps_total,
548
- task_info=task_info,
549
- )
550
- self._rich_task_ids[task_id] = rich_task_id
560
+ # If this task was pruned from the live display, skip re-adding it
561
+ if task_id in self._pruned_task_ids:
562
+ pass
563
+ else:
564
+ # Task was never started; add a completed row so it appears once
565
+ rich_task_id = self._progress.add_task(
566
+ "",
567
+ total=task_info.steps_total,
568
+ label=task_info.label,
569
+ completed=task_info.steps_total,
570
+ task_info=task_info,
571
+ )
572
+ self._rich_task_ids[task_id] = rich_task_id
573
+ self._displayed_task_order.append(task_id)
574
+
575
+ # After finishing, prune completed tasks to respect max visible cap
576
+ self._prune_completed_tasks_if_needed()
551
577
 
552
578
  def get_task_info(self, task_id: int) -> TaskInfo | None:
553
579
  """Get additional task information."""
@@ -567,6 +593,54 @@ class MultiTaskStatus(AbstractAsyncContextManager):
567
593
  """Get console instance for additional output above progress."""
568
594
  return self._progress.console
569
595
 
596
+ def _prune_completed_tasks_if_needed(self) -> None:
597
+ """
598
+ Ensure at most `max_display_tasks` tasks are visible by removing the oldest
599
+ completed/skipped/failed tasks first. Running or waiting tasks are never
600
+ removed by this method.
601
+ Note: This method assumes it's called under self._lock.
602
+ """
603
+ max_visible = self.settings.max_display_tasks
604
+
605
+ # Nothing to prune or unlimited
606
+ if max_visible <= 0:
607
+ return
608
+
609
+ # Count visible tasks (those with a Rich task id present)
610
+ visible_task_ids = [tid for tid in self._displayed_task_order if tid in self._rich_task_ids]
611
+ excess = len(visible_task_ids) - max_visible
612
+ if excess <= 0:
613
+ return
614
+
615
+ # Build list of terminal tasks that can be pruned (oldest first)
616
+ terminal_tasks = []
617
+ for tid in self._displayed_task_order:
618
+ if tid not in self._rich_task_ids:
619
+ continue
620
+ info = self._task_info.get(tid)
621
+ if info and info.state in (
622
+ TaskState.COMPLETED,
623
+ TaskState.FAILED,
624
+ TaskState.SKIPPED,
625
+ ):
626
+ terminal_tasks.append(tid)
627
+
628
+ # Remove the oldest terminal tasks up to the excess count
629
+ tasks_to_remove = terminal_tasks[:excess]
630
+
631
+ for tid in tasks_to_remove:
632
+ rich_tid = self._rich_task_ids.pop(tid, None)
633
+ if rich_tid is not None:
634
+ # Remove from Rich progress display
635
+ self._progress.remove_task(rich_tid)
636
+ # Mark as pruned so we don't re-add on finish
637
+ self._pruned_task_ids.add(tid)
638
+
639
+ # Efficiently rebuild the displayed task order without the removed tasks
640
+ self._displayed_task_order = [
641
+ tid for tid in self._displayed_task_order if tid not in tasks_to_remove
642
+ ]
643
+
570
644
 
571
645
  ## Tests
572
646
 
@@ -1,48 +1,19 @@
1
1
  from __future__ import annotations
2
2
 
3
- import re
4
3
  from dataclasses import dataclass, field
5
4
  from typing import Any
6
5
 
7
- from flowmark import flowmark_markdown, line_wrap_by_sentence
8
6
  from marko import Markdown
7
+ from marko.block import Document
9
8
  from marko.ext import footnote
10
9
 
11
- from kash.utils.text_handling.markdown_utils import comprehensive_transform_tree
12
-
13
-
14
- def _normalize_footnotes_in_markdown(content: str) -> str:
15
- """
16
- Ensure blank lines between consecutive footnote definitions.
17
-
18
- Marko has a bug where consecutive footnotes without blank lines are parsed
19
- as a single footnote. This adds blank lines where needed.
20
- """
21
- lines = content.split("\n")
22
- result = []
23
- i = 0
24
-
25
- while i < len(lines):
26
- line = lines[i]
27
- result.append(line)
28
-
29
- # Check if this is a footnote definition
30
- if re.match(r"^\[\^[^\]]+\]:", line):
31
- # Look ahead to see if the next non-empty line is also a footnote
32
- j = i + 1
33
- while j < len(lines) and not lines[j].strip():
34
- result.append(lines[j])
35
- j += 1
36
-
37
- if j < len(lines) and re.match(r"^\[\^[^\]]+\]:", lines[j]):
38
- # Next non-empty line is also a footnote, add blank line
39
- result.append("")
40
-
41
- i = j
42
- else:
43
- i += 1
44
-
45
- return "\n".join(result)
10
+ from kash.utils.text_handling.markdown_utils import (
11
+ MARKDOWN as DEFAULT_MARKDOWN,
12
+ )
13
+ from kash.utils.text_handling.markdown_utils import (
14
+ comprehensive_transform_tree,
15
+ normalize_footnotes_in_markdown,
16
+ )
46
17
 
47
18
 
48
19
  @dataclass
@@ -81,15 +52,17 @@ class MarkdownFootnotes:
81
52
  MarkdownFootnotes instance with all footnotes indexed by ID
82
53
  """
83
54
  if markdown_parser is None:
84
- markdown_parser = flowmark_markdown(line_wrap_by_sentence(is_markdown=True))
55
+ markdown_parser = DEFAULT_MARKDOWN
85
56
 
86
57
  # Normalize to work around marko bug with consecutive footnotes
87
- normalized_content = _normalize_footnotes_in_markdown(content)
58
+ normalized_content = normalize_footnotes_in_markdown(content)
88
59
  document = markdown_parser.parse(normalized_content)
89
60
  return MarkdownFootnotes.from_document(document, markdown_parser)
90
61
 
91
62
  @staticmethod
92
- def from_document(document: Any, markdown_parser: Markdown | None = None) -> MarkdownFootnotes:
63
+ def from_document(
64
+ document: Document, markdown_parser: Markdown | None = None
65
+ ) -> MarkdownFootnotes:
93
66
  """
94
67
  Extract all footnotes from a parsed markdown document.
95
68
 
@@ -102,7 +75,7 @@ class MarkdownFootnotes:
102
75
  MarkdownFootnotes instance with all footnotes indexed by ID
103
76
  """
104
77
  if markdown_parser is None:
105
- markdown_parser = flowmark_markdown(line_wrap_by_sentence(is_markdown=True))
78
+ markdown_parser = DEFAULT_MARKDOWN
106
79
 
107
80
  footnotes_dict: dict[str, FootnoteInfo] = {}
108
81
 
@@ -206,9 +179,9 @@ def extract_footnote_references(content: str, markdown_parser: Markdown | None =
206
179
  List of unique footnote IDs that are referenced (with the ^)
207
180
  """
208
181
  if markdown_parser is None:
209
- markdown_parser = flowmark_markdown(line_wrap_by_sentence(is_markdown=True))
182
+ markdown_parser = DEFAULT_MARKDOWN
210
183
 
211
- normalized_content = _normalize_footnotes_in_markdown(content)
184
+ normalized_content = normalize_footnotes_in_markdown(content)
212
185
  document = markdown_parser.parse(normalized_content)
213
186
  references: list[str] = []
214
187
  seen: set[str] = set()