kash-shell 0.3.15__tar.gz → 0.3.17__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.15 → kash_shell-0.3.17}/PKG-INFO +1 -1
  2. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/actions/core/render_as_html.py +3 -1
  3. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/actions/core/tabbed_webpage_generate.py +3 -1
  4. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/base/files_command.py +2 -2
  5. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/config/colors.py +97 -15
  6. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/file_storage/file_store.py +71 -53
  7. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/file_storage/item_file_format.py +0 -2
  8. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/local_server/local_server_routes.py +1 -1
  9. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/model/actions_model.py +5 -1
  10. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/model/items_model.py +39 -38
  11. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/completions/shell_completions.py +1 -1
  12. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/file_utils/file_formats_model.py +6 -4
  13. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_gen/simple_webpage.py +4 -1
  14. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_gen/tabbed_webpage.py +8 -3
  15. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_gen/templates/base_styles.css.jinja +110 -14
  16. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_gen/templates/base_webpage.html.jinja +155 -1
  17. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_gen/templates/item_view.html.jinja +4 -2
  18. kash_shell-0.3.17/src/kash/web_gen/templates/simple_webpage.html.jinja +552 -0
  19. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_gen/templates/tabbed_webpage.html.jinja +4 -2
  20. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/xontrib/kash_extension.py +3 -2
  21. {kash_shell-0.3.15 → kash_shell-0.3.17}/uv.lock +220 -199
  22. kash_shell-0.3.15/src/kash/web_gen/templates/simple_webpage.html.jinja +0 -24
  23. {kash_shell-0.3.15 → kash_shell-0.3.17}/.copier-answers.yml +0 -0
  24. {kash_shell-0.3.15 → kash_shell-0.3.17}/.env.template +0 -0
  25. {kash_shell-0.3.15 → kash_shell-0.3.17}/.github/workflows/ci.yml +0 -0
  26. {kash_shell-0.3.15 → kash_shell-0.3.17}/.github/workflows/publish.yml +0 -0
  27. {kash_shell-0.3.15 → kash_shell-0.3.17}/.gitignore +0 -0
  28. {kash_shell-0.3.15 → kash_shell-0.3.17}/LICENSE +0 -0
  29. {kash_shell-0.3.15 → kash_shell-0.3.17}/Makefile +0 -0
  30. {kash_shell-0.3.15 → kash_shell-0.3.17}/README.md +0 -0
  31. {kash_shell-0.3.15 → kash_shell-0.3.17}/development.md +0 -0
  32. {kash_shell-0.3.15 → kash_shell-0.3.17}/devtools/generate_readme.xsh +0 -0
  33. {kash_shell-0.3.15 → kash_shell-0.3.17}/devtools/lint.py +0 -0
  34. {kash_shell-0.3.15 → kash_shell-0.3.17}/devtools/profile_main.py +0 -0
  35. {kash_shell-0.3.15 → kash_shell-0.3.17}/installation.md +0 -0
  36. {kash_shell-0.3.15 → kash_shell-0.3.17}/publishing.md +0 -0
  37. {kash_shell-0.3.15 → kash_shell-0.3.17}/pyproject.toml +0 -0
  38. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/__init__.py +0 -0
  39. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/__main__.py +0 -0
  40. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/actions/__init__.py +0 -0
  41. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/actions/core/assistant_chat.py +0 -0
  42. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/actions/core/chat.py +0 -0
  43. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/actions/core/format_markdown_template.py +0 -0
  44. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/actions/core/markdownify.py +0 -0
  45. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/actions/core/readability.py +0 -0
  46. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/actions/core/show_webpage.py +0 -0
  47. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/actions/core/strip_html.py +0 -0
  48. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/actions/core/summarize_as_bullets.py +0 -0
  49. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/actions/core/tabbed_webpage_config.py +0 -0
  50. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/actions/meta/write_instructions.py +0 -0
  51. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/actions/meta/write_new_action.py +0 -0
  52. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/__init__.py +0 -0
  53. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/base/basic_file_commands.py +0 -0
  54. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/base/browser_commands.py +0 -0
  55. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/base/debug_commands.py +0 -0
  56. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/base/diff_commands.py +0 -0
  57. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/base/general_commands.py +0 -0
  58. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/base/logs_commands.py +0 -0
  59. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/base/model_commands.py +0 -0
  60. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/base/reformat_command.py +0 -0
  61. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/base/search_command.py +0 -0
  62. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/base/show_command.py +0 -0
  63. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/extras/parse_uv_lock.py +0 -0
  64. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/extras/utils_commands.py +0 -0
  65. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/help/assistant_commands.py +0 -0
  66. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/help/doc_commands.py +0 -0
  67. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/help/help_commands.py +0 -0
  68. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/help/logo.py +0 -0
  69. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/help/welcome.py +0 -0
  70. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/workspace/selection_commands.py +0 -0
  71. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/commands/workspace/workspace_commands.py +0 -0
  72. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/config/__init__.py +0 -0
  73. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/config/capture_output.py +0 -0
  74. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/config/env_settings.py +0 -0
  75. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/config/init.py +0 -0
  76. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/config/lazy_imports.py +0 -0
  77. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/config/logger.py +0 -0
  78. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/config/logger_basic.py +0 -0
  79. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/config/logo.txt +0 -0
  80. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/config/server_config.py +0 -0
  81. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/config/settings.py +0 -0
  82. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/config/setup.py +0 -0
  83. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/config/suppress_warnings.py +0 -0
  84. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/config/text_styles.py +0 -0
  85. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/__init__.py +0 -0
  86. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/all_docs.py +0 -0
  87. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/load_actions_info.py +0 -0
  88. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/load_api_docs.py +0 -0
  89. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/load_help_topics.py +0 -0
  90. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/load_source_code.py +0 -0
  91. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/markdown/api_docs_template.md +0 -0
  92. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/markdown/assistant_instructions_template.md +0 -0
  93. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/markdown/readme_template.md +0 -0
  94. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/markdown/topics/a1_what_is_kash.md +0 -0
  95. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/markdown/topics/a2_installation.md +0 -0
  96. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/markdown/topics/a3_getting_started.md +0 -0
  97. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/markdown/topics/a4_elements.md +0 -0
  98. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/markdown/topics/a5_tips_for_use_with_other_tools.md +0 -0
  99. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/markdown/topics/b0_philosophy_of_kash.md +0 -0
  100. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/markdown/topics/b1_kash_overview.md +0 -0
  101. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/markdown/topics/b2_workspace_and_file_formats.md +0 -0
  102. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/markdown/topics/b3_modern_shell_tool_recommendations.md +0 -0
  103. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/markdown/topics/b4_faq.md +0 -0
  104. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/markdown/warning.md +0 -0
  105. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs/markdown/welcome.md +0 -0
  106. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs_base/docs_base.py +0 -0
  107. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs_base/load_custom_command_info.py +0 -0
  108. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs_base/load_faqs.py +0 -0
  109. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs_base/load_recipe_snippets.py +0 -0
  110. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs_base/recipes/general_system_commands.sh +0 -0
  111. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs_base/recipes/python_dev_commands.sh +0 -0
  112. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/docs_base/recipes/tldr_standard_commands.sh +0 -0
  113. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/embeddings/cosine.py +0 -0
  114. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/embeddings/embeddings.py +0 -0
  115. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/embeddings/text_similarity.py +0 -0
  116. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/__init__.py +0 -0
  117. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/action_decorators.py +0 -0
  118. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/action_exec.py +0 -0
  119. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/action_registry.py +0 -0
  120. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/combiners.py +0 -0
  121. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/command_exec.py +0 -0
  122. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/command_registry.py +0 -0
  123. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/fetch_url_metadata.py +0 -0
  124. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/history.py +0 -0
  125. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/importing.py +0 -0
  126. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/llm_transforms.py +0 -0
  127. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/precondition_checks.py +0 -0
  128. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/precondition_registry.py +0 -0
  129. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/preconditions.py +0 -0
  130. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/resolve_args.py +0 -0
  131. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/runtime_settings.py +0 -0
  132. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec/shell_callable_action.py +0 -0
  133. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec_model/__init__.py +0 -0
  134. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec_model/args_model.py +0 -0
  135. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec_model/commands_model.py +0 -0
  136. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec_model/script_model.py +0 -0
  137. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/exec_model/shell_model.py +0 -0
  138. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/file_storage/__init__.py +0 -0
  139. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/file_storage/metadata_dirs.py +0 -0
  140. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/file_storage/persisted_yaml.py +0 -0
  141. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/file_storage/store_cache_warmer.py +0 -0
  142. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/file_storage/store_filenames.py +0 -0
  143. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/help/__init__.py +0 -0
  144. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/help/assistant.py +0 -0
  145. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/help/assistant_instructions.py +0 -0
  146. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/help/assistant_output.py +0 -0
  147. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/help/docstring_utils.py +0 -0
  148. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/help/function_param_info.py +0 -0
  149. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/help/help_embeddings.py +0 -0
  150. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/help/help_lookups.py +0 -0
  151. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/help/help_pages.py +0 -0
  152. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/help/help_printing.py +0 -0
  153. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/help/help_types.py +0 -0
  154. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/help/recommended_commands.py +0 -0
  155. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/help/tldr_help.py +0 -0
  156. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/llm_utils/__init__.py +0 -0
  157. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/llm_utils/clean_headings.py +0 -0
  158. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/llm_utils/custom_sliding_transforms.py +0 -0
  159. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/llm_utils/fuzzy_parsing.py +0 -0
  160. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/llm_utils/init_litellm.py +0 -0
  161. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/llm_utils/llm_api_keys.py +0 -0
  162. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/llm_utils/llm_completion.py +0 -0
  163. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/llm_utils/llm_features.py +0 -0
  164. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/llm_utils/llm_messages.py +0 -0
  165. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/llm_utils/llm_names.py +0 -0
  166. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/llm_utils/llms.py +0 -0
  167. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/local_server/__init__.py +0 -0
  168. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/local_server/local_server.py +0 -0
  169. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/local_server/local_server_commands.py +0 -0
  170. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/local_server/local_url_formatters.py +0 -0
  171. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/local_server/port_tools.py +0 -0
  172. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/local_server/rich_html_template.py +0 -0
  173. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/mcp/__init__.py +0 -0
  174. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/mcp/mcp_cli.py +0 -0
  175. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/mcp/mcp_main.py +0 -0
  176. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/mcp/mcp_server_commands.py +0 -0
  177. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/mcp/mcp_server_routes.py +0 -0
  178. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/mcp/mcp_server_sse.py +0 -0
  179. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/mcp/mcp_server_stdio.py +0 -0
  180. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/media_base/__init__.py +0 -0
  181. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/media_base/audio_processing.py +0 -0
  182. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/media_base/media_cache.py +0 -0
  183. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/media_base/media_services.py +0 -0
  184. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/media_base/media_tools.py +0 -0
  185. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/media_base/services/local_file_media.py +0 -0
  186. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/media_base/timestamp_citations.py +0 -0
  187. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/media_base/transcription_deepgram.py +0 -0
  188. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/media_base/transcription_format.py +0 -0
  189. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/media_base/transcription_whisper.py +0 -0
  190. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/model/__init__.py +0 -0
  191. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/model/assistant_response_model.py +0 -0
  192. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/model/compound_actions_model.py +0 -0
  193. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/model/concept_model.py +0 -0
  194. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/model/exec_model.py +0 -0
  195. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/model/graph_model.py +0 -0
  196. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/model/language_list.py +0 -0
  197. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/model/llm_actions_model.py +0 -0
  198. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/model/media_model.py +0 -0
  199. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/model/operations_model.py +0 -0
  200. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/model/params_model.py +0 -0
  201. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/model/paths_model.py +0 -0
  202. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/model/preconditions_model.py +0 -0
  203. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/__init__.py +0 -0
  204. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/completions/completion_scoring.py +0 -0
  205. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/completions/completion_types.py +0 -0
  206. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/file_icons/color_for_format.py +0 -0
  207. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/file_icons/nerd_icons.py +0 -0
  208. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/input/__init__.py +0 -0
  209. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/input/input_prompts.py +0 -0
  210. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/input/inquirer_settings.py +0 -0
  211. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/input/param_inputs.py +0 -0
  212. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/input/shell_confirm.py +0 -0
  213. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/output/__init__.py +0 -0
  214. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/output/kerm_code_utils.py +0 -0
  215. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/output/kerm_codes.py +0 -0
  216. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/output/kmarkdown.py +0 -0
  217. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/output/shell_formatting.py +0 -0
  218. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/output/shell_output.py +0 -0
  219. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/shell_main.py +0 -0
  220. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/ui/__init__.py +0 -0
  221. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/ui/shell_results.py +0 -0
  222. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/ui/shell_syntax.py +0 -0
  223. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/utils/exception_printing.py +0 -0
  224. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/utils/native_utils.py +0 -0
  225. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/utils/shell_function_wrapper.py +0 -0
  226. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/shell/version.py +0 -0
  227. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/__init__.py +0 -0
  228. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/common/__init__.py +0 -0
  229. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/common/format_utils.py +0 -0
  230. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/common/function_inspect.py +0 -0
  231. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/common/import_utils.py +0 -0
  232. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/common/lazyobject.py +0 -0
  233. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/common/obj_replace.py +0 -0
  234. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/common/parse_key_vals.py +0 -0
  235. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/common/parse_shell_args.py +0 -0
  236. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/common/stack_traces.py +0 -0
  237. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/common/task_stack.py +0 -0
  238. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/common/type_utils.py +0 -0
  239. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/common/uniquifier.py +0 -0
  240. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/common/url.py +0 -0
  241. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/errors.py +0 -0
  242. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/file_formats/chat_format.py +0 -0
  243. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/file_utils/__init__.py +0 -0
  244. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/file_utils/dir_info.py +0 -0
  245. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/file_utils/file_ext.py +0 -0
  246. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/file_utils/file_formats.py +0 -0
  247. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/file_utils/file_sort_filter.py +0 -0
  248. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/file_utils/file_walk.py +0 -0
  249. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/file_utils/filename_parsing.py +0 -0
  250. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/file_utils/ignore_files.py +0 -0
  251. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/file_utils/mtime_cache.py +0 -0
  252. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/file_utils/path_utils.py +0 -0
  253. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/lang_utils/__init__.py +0 -0
  254. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/lang_utils/capitalization.py +0 -0
  255. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/rich_custom/__init__.py +0 -0
  256. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/rich_custom/ansi_cell_len.py +0 -0
  257. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/rich_custom/rich_char_transform.py +0 -0
  258. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/rich_custom/rich_indent.py +0 -0
  259. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/rich_custom/rich_markdown_fork.py +0 -0
  260. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/text_handling/doc_normalization.py +0 -0
  261. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/text_handling/escape_html_tags.py +0 -0
  262. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/text_handling/markdown_render.py +0 -0
  263. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/text_handling/markdown_utils.py +0 -0
  264. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/text_handling/markdownify_utils.py +0 -0
  265. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/utils/text_handling/unified_diffs.py +0 -0
  266. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_content/canon_url.py +0 -0
  267. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_content/dir_store.py +0 -0
  268. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_content/file_cache_utils.py +0 -0
  269. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_content/file_processing.py +0 -0
  270. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_content/local_file_cache.py +0 -0
  271. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_content/web_extract.py +0 -0
  272. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_content/web_extract_justext.py +0 -0
  273. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_content/web_extract_readabilipy.py +0 -0
  274. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_content/web_fetch.py +0 -0
  275. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_content/web_page_model.py +0 -0
  276. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_gen/__init__.py +0 -0
  277. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_gen/template_render.py +0 -0
  278. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_gen/templates/content_styles.css.jinja +0 -0
  279. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/web_gen/templates/explain_view.html.jinja +0 -0
  280. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/workspaces/__init__.py +0 -0
  281. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/workspaces/param_state.py +0 -0
  282. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/workspaces/selections.py +0 -0
  283. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/workspaces/source_items.py +0 -0
  284. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/workspaces/workspace_dirs.py +0 -0
  285. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/workspaces/workspace_output.py +0 -0
  286. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/workspaces/workspace_registry.py +0 -0
  287. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/workspaces/workspaces.py +0 -0
  288. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/xonsh_custom/command_nl_utils.py +0 -0
  289. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/xonsh_custom/custom_shell.py +0 -0
  290. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/xonsh_custom/customize_prompt.py +0 -0
  291. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/xonsh_custom/load_into_xonsh.py +0 -0
  292. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/xonsh_custom/shell_load_commands.py +0 -0
  293. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/xonsh_custom/shell_which.py +0 -0
  294. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/xonsh_custom/xonsh_completers.py +0 -0
  295. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/xonsh_custom/xonsh_env.py +0 -0
  296. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/xonsh_custom/xonsh_keybindings.py +0 -0
  297. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/xonsh_custom/xonsh_modern_tools.py +0 -0
  298. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/xonsh_custom/xonsh_ranking_completer.py +0 -0
  299. {kash_shell-0.3.15 → kash_shell-0.3.17}/src/kash/xontrib/fnm.py +0 -0
  300. {kash_shell-0.3.15 → kash_shell-0.3.17}/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.15
3
+ Version: 0.3.17
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>
@@ -27,7 +27,9 @@ def render_as_html(input: ActionInput, no_title: bool = False) -> ActionResult:
27
27
  """
28
28
  if len(input.items) == 1:
29
29
  input_item = input.items[0]
30
- html_body = simple_webpage_render(input_item, add_title_h1=not no_title)
30
+ html_body = simple_webpage_render(
31
+ input_item, add_title_h1=not no_title, show_theme_toggle=True
32
+ )
31
33
  result_item = input_item.derived_copy(
32
34
  type=ItemType.export, format=Format.html, body=html_body
33
35
  )
@@ -17,7 +17,9 @@ def tabbed_webpage_generate(input: ActionInput, add_title: bool = False) -> Acti
17
17
  Generate a tabbed web page from a config item for the tabbed template.
18
18
  """
19
19
  config_item = input.items[0]
20
- html = tabbed_webpage.tabbed_webpage_generate(config_item, add_title_h1=add_title)
20
+ html = tabbed_webpage.tabbed_webpage_generate(
21
+ config_item, add_title_h1=add_title, show_theme_toggle=True
22
+ )
21
23
 
22
24
  webpage_item = Item(
23
25
  title=config_item.title,
@@ -64,7 +64,6 @@ def _print_listing_tallies(
64
64
  f"{EMOJI_WARN} {file_listing.files_ignored} files and {file_listing.dirs_ignored} dirs were ignored",
65
65
  style=COLOR_EXTRA,
66
66
  )
67
- cprint("(use --no_ignore to show hidden files)", style=STYLE_HINT)
68
67
 
69
68
  if file_listing.total_skipped > 0:
70
69
  cprint(
@@ -72,7 +71,8 @@ def _print_listing_tallies(
72
71
  f"at max_files={max_files}, max_depth={max_depth}, max_per_subdir={max_per_subdir}",
73
72
  style=COLOR_EXTRA,
74
73
  )
75
- cprint("(use --no_max to remove cutoff)", style=STYLE_HINT)
74
+ if file_listing.total_ignored + file_listing.total_skipped > 0:
75
+ cprint("(use --all to show all files)", style=STYLE_HINT)
76
76
 
77
77
 
78
78
  DEFAULT_MAX_PER_GROUP = 50
@@ -133,27 +133,63 @@ web_light_translucent = SimpleNamespace(
133
133
  primary=hsl_to_hex("hsl(188, 31%, 41%)"),
134
134
  primary_light=hsl_to_hex("hsl(188, 40%, 62%)"),
135
135
  secondary=hsl_to_hex("hsl(188, 12%, 28%)"),
136
+ tertiary=hsl_to_hex("hsl(188, 7%, 64%)"),
136
137
  bg=hsl_to_hex("hsla(44, 6%, 100%, 0.75)"),
137
138
  bg_solid=hsl_to_hex("hsla(44, 6%, 100%, 1)"),
138
139
  bg_header=hsl_to_hex("hsla(188, 42%, 70%, 0.2)"),
139
140
  bg_alt=hsl_to_hex("hsla(39, 24%, 90%, 0.3)"),
140
141
  bg_alt_solid=hsl_to_hex("hsla(39, 24%, 97%, 1)"),
141
142
  text=hsl_to_hex("hsl(188, 39%, 11%)"),
143
+ code=hsl_to_hex("hsl(44, 38%, 23%)"),
142
144
  border=hsl_to_hex("hsl(188, 8%, 50%)"),
143
- border_hint=hsl_to_hex("hsla(188, 8%, 72%, 0.7)"),
145
+ border_hint=hsl_to_hex("hsla(188, 8%, 72%, 0.3)"),
144
146
  border_accent=hsl_to_hex("hsla(305, 18%, 65%, 0.85)"),
145
147
  hover=hsl_to_hex("hsl(188, 12%, 84%)"),
146
- hover_bg=hsl_to_hex("hsla(188, 7%, 94%, 0.8)"),
148
+ hover_bg=hsl_to_hex("hsla(188, 12%, 94%, 0.8)"),
147
149
  hint=hsl_to_hex("hsl(188, 11%, 65%)"),
150
+ hint_gentle=hsl_to_hex("hsla(188, 11%, 65%, 0.2)"),
148
151
  tooltip_bg=hsl_to_hex("hsla(188, 6%, 37%, 0.7)"),
149
152
  popover_bg=hsl_to_hex("hsla(188, 6%, 37%, 0.7)"),
150
153
  bright=hsl_to_hex("hsl(134, 43%, 60%)"),
154
+ success=hsl_to_hex("hsl(134, 70%, 37%)"),
155
+ failure=hsl_to_hex("hsl(7, 70%, 37%)"),
151
156
  selection="hsla(225, 61%, 82%, 0.80)",
152
157
  scrollbar=hsl_to_hex("hsla(189, 12%, 55%, 0.9)"),
153
158
  scrollbar_hover=hsl_to_hex("hsla(190, 12%, 38%, 0.9)"),
154
159
  )
155
160
 
156
161
 
162
+ # Web dark colors
163
+ web_dark_translucent = SimpleNamespace(
164
+ primary=hsl_to_hex("hsl(188, 40%, 62%)"),
165
+ primary_light=hsl_to_hex("hsl(188, 50%, 72%)"),
166
+ secondary=hsl_to_hex("hsl(188, 12%, 65%)"),
167
+ tertiary=hsl_to_hex("hsl(188, 7%, 40%)"),
168
+ bg=hsl_to_hex("hsla(220, 14%, 7%, 0.95)"),
169
+ bg_solid=hsl_to_hex("hsl(220, 14%, 7%)"),
170
+ bg_header=hsl_to_hex("hsla(188, 42%, 20%, 0.3)"),
171
+ bg_alt=hsl_to_hex("hsla(220, 14%, 12%, 0.5)"),
172
+ bg_alt_solid=hsl_to_hex("hsl(220, 14%, 12%)"),
173
+ text=hsl_to_hex("hsl(188, 10%, 90%)"),
174
+ code=hsl_to_hex("hsl(44, 38%, 72%)"),
175
+ border=hsl_to_hex("hsl(188, 8%, 25%)"),
176
+ border_hint=hsl_to_hex("hsla(188, 8%, 35%, 0.3)"),
177
+ border_accent=hsl_to_hex("hsla(305, 30%, 55%, 0.85)"),
178
+ hover=hsl_to_hex("hsl(188, 12%, 35%)"),
179
+ hover_bg=hsl_to_hex("hsla(188, 20%, 25%, 0.4)"),
180
+ hint=hsl_to_hex("hsl(188, 11%, 55%)"),
181
+ hint_gentle=hsl_to_hex("hsla(188, 11%, 55%, 0.2)"),
182
+ tooltip_bg=hsl_to_hex("hsla(188, 6%, 20%, 0.9)"),
183
+ popover_bg=hsl_to_hex("hsla(188, 6%, 20%, 0.9)"),
184
+ bright=hsl_to_hex("hsl(134, 52%, 65%)"),
185
+ success=hsl_to_hex("hsl(134, 72%, 73%)"),
186
+ failure=hsl_to_hex("hsl(7, 46%, 53%)"),
187
+ selection=hsl_to_hex("hsla(225, 61%, 40%, 0.40)"),
188
+ scrollbar=hsl_to_hex("hsla(189, 12%, 35%, 0.9)"),
189
+ scrollbar_hover=hsl_to_hex("hsla(190, 12%, 50%, 0.9)"),
190
+ )
191
+
192
+
157
193
  rich_terminal_dark = TerminalTheme(
158
194
  hex_to_int(terminal_dark.background),
159
195
  hex_to_int(terminal_dark.foreground),
@@ -211,9 +247,6 @@ rich_terminal_light = TerminalTheme(
211
247
  # We default to light colors for Rich content in HTML.
212
248
  rich_terminal = rich_terminal_light
213
249
 
214
- # Only support light web colors for now.
215
- web = web_light_translucent
216
-
217
250
  # Logical colors
218
251
  logical = SimpleNamespace(
219
252
  concept_dark=terminal.green_dark,
@@ -234,18 +267,23 @@ logical = SimpleNamespace(
234
267
  )
235
268
 
236
269
 
237
- def consolidate_color_vars(overrides: dict[str, str] | None = None) -> dict[str, str]:
270
+ def consolidate_color_vars(
271
+ overrides: dict[str, str] | None = None, web_colors: SimpleNamespace | None = None
272
+ ) -> dict[str, str]:
238
273
  """
239
274
  Consolidate all color variables into a single dictionary with appropriate prefixes.
240
275
  Terminal variables have no prefix, while web and logical variables have "color-" prefix.
241
276
  """
242
277
  if overrides is None:
243
278
  overrides = {}
279
+ if web_colors is None:
280
+ web_colors = web_light_translucent
281
+
244
282
  return {
245
283
  # Terminal variables (no prefix)
246
284
  **terminal.__dict__,
247
285
  # Web and logical variables with "color-" prefix
248
- **{f"color-{k}": v for k, v in web.__dict__.items()},
286
+ **{f"color-{k}": v for k, v in web_colors.__dict__.items()},
249
287
  **{f"color-{k}": v for k, v in logical.__dict__.items()},
250
288
  # Overrides take precedence (assume they already have correct prefixes)
251
289
  **overrides,
@@ -262,19 +300,63 @@ def normalize_var_names(variables: dict[str, str]) -> dict[str, str]:
262
300
 
263
301
  def generate_css_vars(overrides: dict[str, str] | None = None) -> str:
264
302
  """
265
- Generate CSS variables for the terminal and web colors.
303
+ Generate CSS variables for terminal and both light and dark themes.
266
304
  """
267
305
  if overrides is None:
268
306
  overrides = {}
269
- normalized_vars = normalize_var_names(consolidate_color_vars(overrides))
270
307
 
271
- # Generate the CSS.
272
- css_variables = ":root {\n"
273
- for name, value in normalized_vars.items():
274
- css_variables += f" --{name}: {value};\n"
275
- css_variables += "}"
308
+ # Get base variables (terminal colors stay the same)
309
+ base_vars = normalize_var_names({k: v for k, v in terminal.__dict__.items()})
310
+
311
+ # Get light theme color variables
312
+ light_color_vars = normalize_var_names(
313
+ {f"color-{k}": v for k, v in web_light_translucent.__dict__.items()}
314
+ )
315
+ light_color_vars.update(
316
+ normalize_var_names({f"color-{k}": v for k, v in logical.__dict__.items()})
317
+ )
318
+
319
+ # Get dark theme color variables
320
+ dark_color_vars = normalize_var_names(
321
+ {f"color-{k}": v for k, v in web_dark_translucent.__dict__.items()}
322
+ )
323
+ dark_color_vars.update(
324
+ normalize_var_names({f"color-{k}": v for k, v in logical.__dict__.items()})
325
+ )
276
326
 
277
- return css_variables
327
+ # Apply overrides
328
+ if overrides:
329
+ normalized_overrides = normalize_var_names(overrides)
330
+ light_color_vars.update(normalized_overrides)
331
+ dark_color_vars.update(normalized_overrides)
332
+
333
+ # Generate CSS
334
+ css_parts = []
335
+
336
+ # Root with all variables (defaults to light)
337
+ css_parts.append(":root {")
338
+ css_parts.extend(f" --{k}: {v};" for k, v in base_vars.items())
339
+ css_parts.extend(f" --{k}: {v};" for k, v in light_color_vars.items())
340
+ css_parts.append("}\n")
341
+
342
+ # Light theme (only color- variables)
343
+ css_parts.append('[data-theme="light"] {')
344
+ css_parts.extend(f" --{k}: {v};" for k, v in light_color_vars.items())
345
+ css_parts.append("}\n")
346
+
347
+ # Dark theme (only color- variables)
348
+ css_parts.append('[data-theme="dark"] {')
349
+ css_parts.extend(f" --{k}: {v};" for k, v in dark_color_vars.items())
350
+ css_parts.append("}\n")
351
+
352
+ # Print media
353
+ css_parts.append("@media print {")
354
+ css_parts.append(' :root, [data-theme="dark"] {')
355
+ css_parts.extend(f" --{k}: {v} !important;" for k, v in light_color_vars.items())
356
+ css_parts.append(" }")
357
+ css_parts.append("}")
358
+
359
+ return "\n".join(css_parts)
278
360
 
279
361
 
280
362
  if __name__ == "__main__":
@@ -16,7 +16,11 @@ from kash.config.logger import get_log_settings, get_logger
16
16
  from kash.config.text_styles import EMOJI_SAVED
17
17
  from kash.file_storage.item_file_format import read_item, write_item
18
18
  from kash.file_storage.metadata_dirs import MetadataDirs
19
- from kash.file_storage.store_filenames import folder_for_type, join_suffix, parse_item_filename
19
+ from kash.file_storage.store_filenames import (
20
+ folder_for_type,
21
+ join_suffix,
22
+ parse_item_filename,
23
+ )
20
24
  from kash.model.items_model import Item, ItemId, ItemType
21
25
  from kash.model.paths_model import StorePath
22
26
  from kash.shell.output.shell_output import PrintHooks
@@ -39,7 +43,9 @@ T = TypeVar("T")
39
43
  P = ParamSpec("P")
40
44
 
41
45
 
42
- def synchronized(method: Callable[Concatenate[SelfT, P], T]) -> Callable[Concatenate[SelfT, P], T]:
46
+ def synchronized(
47
+ method: Callable[Concatenate[SelfT, P], T],
48
+ ) -> Callable[Concatenate[SelfT, P], T]:
43
49
  """
44
50
  Simple way to synchronize a few methods.
45
51
  """
@@ -142,7 +148,10 @@ class FileStore(Workspace):
142
148
  """
143
149
  name, item_type, _format, file_ext = parse_item_filename(store_path)
144
150
  if not file_ext:
145
- log.debug("Skipping file with unrecognized name or extension: %s", fmt_path(store_path))
151
+ log.debug(
152
+ "Skipping file with unrecognized name or extension: %s",
153
+ fmt_path(store_path),
154
+ )
146
155
  return None
147
156
 
148
157
  full_suffix = join_suffix(item_type.name, file_ext.name) if item_type else file_ext.name
@@ -158,11 +167,17 @@ class FileStore(Workspace):
158
167
  if old_path and old_path != store_path:
159
168
  dup_path = old_path
160
169
  log.info(
161
- "Duplicate items (%s):\n%s", item_id, fmt_lines([old_path, store_path])
170
+ "Duplicate items (%s):\n%s",
171
+ item_id,
172
+ fmt_lines([old_path, store_path]),
162
173
  )
163
174
  self.id_map[item_id] = store_path
164
- except SkippableError as e:
165
- log.warning("Could not read file, skipping: %s: %s", fmt_path(store_path), e)
175
+ except (ValueError, SkippableError) as e:
176
+ log.warning(
177
+ "Could not load file, skipping from store index: %s: %s",
178
+ fmt_path(store_path),
179
+ e,
180
+ )
166
181
 
167
182
  return dup_path
168
183
 
@@ -219,13 +234,17 @@ class FileStore(Workspace):
219
234
  @synchronized
220
235
  def _pick_filename_for(self, item: Item, *, overwrite: bool = False) -> tuple[str, str | None]:
221
236
  """
222
- Get a suitable filename for this item.
223
- If `overwrite` is true, use the the slugified title.
224
- If it is false, use the slugified title with a suffix to make it unique
225
- and in this case returns the old filename for this item, if it is different.
237
+ Get a suitable filename for this item. If `overwrite` is true, use the the slugified
238
+ title, regardless of whether it is already in the store.
239
+ If `overwrite` is false, use the slugified title with a suffix to make it unique
240
+ (and in this case also return the old filename for this item).
226
241
  """
227
242
  if overwrite:
228
- log.info("Picked default filename: %s for item: %s", item.default_filename(), item)
243
+ log.info(
244
+ "Picked default filename: %s for item: %s",
245
+ item.default_filename(),
246
+ item,
247
+ )
229
248
  return item.default_filename(), None
230
249
 
231
250
  slug = item.slug_name()
@@ -283,14 +302,13 @@ class FileStore(Workspace):
283
302
  @synchronized
284
303
  def store_path_for(
285
304
  self, item: Item, *, as_tmp: bool = False, overwrite: bool = False
286
- ) -> tuple[StorePath, bool, StorePath | None]:
305
+ ) -> tuple[StorePath, StorePath | None]:
287
306
  """
288
307
  Return the store path for an item. If the item already has a `store_path`, we use that.
289
308
  Otherwise we need to find the store path or generate a new one that seems suitable.
290
309
 
291
- Returns `store_path, found, old_store_path` where `found` indicates whether the path was
292
- already found (in the item or in the store by checking for identity) and `old_store_path`
293
- is the previous similarly named item with a different identity (or None there is none).
310
+ Returns `store_path, old_store_path` where `old_store_path` is the previous similarly
311
+ named item with a different identity (or None there is none).
294
312
 
295
313
  If `as_tmp` is true, will return a path from the temporary directory in the store.
296
314
  Normally an item is always saved to a unique store path but if `overwrite` is true,
@@ -299,17 +317,17 @@ class FileStore(Workspace):
299
317
  item_id = item.item_id()
300
318
  old_filename = None
301
319
  if as_tmp:
302
- return self._tmp_path_for(item), False, None
320
+ return self._tmp_path_for(item), None
303
321
  elif item.store_path:
304
- return StorePath(item.store_path), True, None
322
+ return StorePath(item.store_path), None
305
323
  elif item_id in self.id_map and self.exists(self.id_map[item_id]):
306
324
  # If this item has an identity and we've saved under that id before, use the same store path.
307
325
  store_path = self.id_map[item_id]
308
326
  log.info(
309
- "Found existing item with same id:\n%s",
327
+ "When picking a store path, found an existing item with same id:\n%s",
310
328
  fmt_lines([fmt_loc(store_path), item_id]),
311
329
  )
312
- return store_path, True, None
330
+ return store_path, None
313
331
  else:
314
332
  # We need to pick the path and filename.
315
333
  folder_path = folder_for_type(item.type)
@@ -320,14 +338,14 @@ class FileStore(Workspace):
320
338
  if old_filename and Path(self.base_dir / folder_path / old_filename).exists():
321
339
  old_store_path = StorePath(folder_path / old_filename)
322
340
 
323
- return StorePath(store_path), False, old_store_path
341
+ return StorePath(store_path), old_store_path
324
342
 
325
343
  def _tmp_path_for(self, item: Item) -> StorePath:
326
344
  """
327
345
  Find a path for an item in the tmp directory.
328
346
  """
329
347
  if not item.store_path:
330
- store_path, _found, _old = self.store_path_for(item, as_tmp=False)
348
+ store_path, _old = self.store_path_for(item, as_tmp=False)
331
349
  return StorePath(self.dirs.tmp_dir / store_path)
332
350
  elif (self.base_dir / item.store_path).is_relative_to(self.dirs.tmp_dir):
333
351
  return StorePath(item.store_path)
@@ -346,14 +364,14 @@ class FileStore(Workspace):
346
364
  item: Item,
347
365
  *,
348
366
  overwrite: bool = False,
349
- skip_dup_names: bool = False,
350
367
  as_tmp: bool = False,
351
368
  no_format: bool = False,
352
369
  no_frontmatter: bool = False,
353
370
  ) -> StorePath:
354
371
  """
355
372
  Save the item. Uses the `store_path` if it's already set or generates a new one.
356
- Updates `item.store_path`.
373
+ Updates `item.store_path`. An existing file can be added by having the item's
374
+ `external_path` set to a location (inside or outside the store).
357
375
 
358
376
  Unless `no_format` is true, also normalizes body text formatting (for Markdown)
359
377
  and updates the item's body to match.
@@ -363,65 +381,62 @@ class FileStore(Workspace):
363
381
  If `overwrite` is true, will overwrite a file that has the same path.
364
382
 
365
383
  If `as_tmp` is true, will save the item to a temporary file.
366
-
367
- If `skip_dup_names` is true, will skip saving if an item if an item with a
368
- matching path (based on its title) already exists.
369
384
  """
370
- if overwrite and skip_dup_names:
371
- raise ValueError("Cannot both overwrite and skip duplicate names.")
372
385
  if overwrite and as_tmp:
373
386
  raise ValueError("Cannot both overwrite and save to a temporary file.")
374
387
 
375
- # If external file already exists within the workspace, the file is already saved (without metadata).
388
+ # If external path already exists and is within the workspace, the file was
389
+ # already saved (e.g. by an action that wrote the item directly to the store).
376
390
  external_path = item.external_path and Path(item.external_path).resolve()
377
391
  if external_path and self._is_in_store(external_path):
378
- log.message("External file already saved: %s", fmt_loc(external_path))
392
+ log.info("Item with external_path already saved: %s", fmt_loc(external_path))
379
393
  rel_path = external_path.relative_to(self.base_dir)
380
- # Indicate this is really an item with a store path, not an external path.
394
+ # Indicate this is an item with a store path, not an external path.
395
+ # Keep external_path set so we know body is in that file.
381
396
  item.store_path = str(rel_path)
382
- item.external_path = None
383
397
  return StorePath(rel_path)
384
398
  else:
385
399
  # Otherwise it's still in memory or in a file outside the workspace and we need to save it.
386
- store_path, found, old_store_path = self.store_path_for(
400
+ store_path, old_store_path = self.store_path_for(
387
401
  item, as_tmp=as_tmp, overwrite=overwrite
388
402
  )
389
403
 
390
- if skip_dup_names and found:
391
- log.message(
392
- "Skipping save because an item of the same name already exists: %s",
393
- fmt_loc(store_path),
394
- )
395
- item.store_path = str(store_path)
396
- return store_path
397
-
398
404
  full_path = self.base_dir / store_path
399
405
 
400
- log.info("Saving item to %s: %s", fmt_loc(full_path), item)
406
+ supports_frontmatter = item.format and item.format.supports_frontmatter
407
+ log.info(
408
+ "Saving item in format %s (supports_frontmatter=%s) to %s: %s",
409
+ item.format,
410
+ supports_frontmatter,
411
+ fmt_loc(full_path),
412
+ item,
413
+ )
401
414
 
402
- # If we're overwriting an existing file, archive it first.
415
+ # If we're overwriting an existing file, archive it first so it is in the archive, not lost.
403
416
  if full_path.exists():
404
417
  try:
418
+ log.info(
419
+ "Previous file exists so will archive it: %s",
420
+ fmt_loc(store_path),
421
+ )
405
422
  self.archive(store_path, quiet=True)
406
423
  except Exception as e:
407
424
  log.info("Exception archiving existing file: %s", e)
408
425
 
409
426
  # Now save the new item.
410
427
  try:
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.
428
+ # For binary or unknown formats or if we're not adding frontmatter, copy the file.
413
429
  if item.external_path and (no_frontmatter or not supports_frontmatter):
430
+ log.info(
431
+ "Path is an external path, so copying: %s -> %s",
432
+ fmt_path(item.external_path),
433
+ fmt_path(full_path),
434
+ )
414
435
  copyfile_atomic(item.external_path, full_path, make_parents=True)
415
436
  else:
416
437
  # Save as a text item with frontmatter.
417
438
  if item.external_path:
418
439
  item.body = Path(item.external_path).read_text()
419
- if overwrite and full_path.exists():
420
- log.info(
421
- "Overwrite is enabled and a previous file exists so will archive it: %s",
422
- fmt_loc(store_path),
423
- )
424
- self.archive(store_path, quiet=True)
425
440
  write_item(item, full_path, normalize=not no_format)
426
441
  except OSError as e:
427
442
  log.error("Error saving item: %s", e)
@@ -545,7 +560,7 @@ class FileStore(Workspace):
545
560
  else:
546
561
  # Binary or other files we just copy over as-is, preserving the name.
547
562
  # We know the extension is recognized.
548
- store_path, _found, old_store_path = self.store_path_for(item)
563
+ store_path, old_store_path = self.store_path_for(item)
549
564
  if self.exists(store_path):
550
565
  raise FileExists(f"Resource already in store: {fmt_loc(store_path)}")
551
566
 
@@ -690,7 +705,10 @@ class FileStore(Workspace):
690
705
  for warning in self.warnings:
691
706
  log.warning("%s", warning)
692
707
 
693
- log.info("File store startup took %s.", format_duration(self.end_time - self.start_time))
708
+ log.info(
709
+ "File store startup took %s.",
710
+ format_duration(self.end_time - self.start_time),
711
+ )
694
712
  # TODO: Log more info like number of items by type.
695
713
  return True
696
714
 
@@ -28,8 +28,6 @@ def write_item(item: Item, path: Path, normalize: bool = True):
28
28
  By default normalizes formatting of the body text and updates the item's body.
29
29
  """
30
30
  item.validate()
31
- if item.is_binary:
32
- raise ValueError(f"Binary items should be external files: {item}")
33
31
  if item.format and not item.format.supports_frontmatter:
34
32
  raise ValueError(f"Item format `{item.format.value}` does not support frontmatter: {item}")
35
33
 
@@ -244,7 +244,7 @@ def _serve_item(
244
244
  media_type=mime_type,
245
245
  )
246
246
  else:
247
- display_title = item.display_title() if item else str(path)
247
+ display_title = item.pick_title() if item else str(path)
248
248
 
249
249
  # For HEAD requests, return header with mime type only.
250
250
  if request.method == "HEAD":
@@ -22,7 +22,7 @@ from kash.exec_model.shell_model import ShellResult
22
22
  from kash.llm_utils import LLM, LLMName
23
23
  from kash.llm_utils.llm_messages import Message, MessageTemplate
24
24
  from kash.model.exec_model import ExecContext
25
- from kash.model.items_model import UNTITLED, Item, ItemType
25
+ from kash.model.items_model import UNTITLED, Format, Item, ItemType
26
26
  from kash.model.operations_model import Operation, Source
27
27
  from kash.model.params_model import (
28
28
  ALL_COMMON_PARAMS,
@@ -102,6 +102,10 @@ class ActionResult:
102
102
  shell_result: ShellResult | None = None
103
103
  """Customize control of how the action's result is displayed in the shell."""
104
104
 
105
+ def get_by_format(self, *formats: Format) -> Item:
106
+ """Convenience method to get an item for actions that return multiple formats."""
107
+ return next(item for item in self.items if item.format in formats)
108
+
105
109
  def has_hints(self) -> bool:
106
110
  return bool(
107
111
  self.replaces_input or self.skip_duplicates or self.path_ops or self.shell_result