kash-shell 0.3.24__py3-none-any.whl → 0.3.26__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kash/commands/help/assistant_commands.py +4 -3
- kash/config/colors.py +5 -3
- kash/config/text_styles.py +1 -0
- kash/config/unified_live.py +251 -0
- kash/docs/markdown/assistant_instructions_template.md +3 -3
- kash/docs/markdown/topics/a1_what_is_kash.md +22 -20
- kash/docs/markdown/topics/a2_installation.md +10 -10
- kash/docs/markdown/topics/a3_getting_started.md +8 -8
- kash/docs/markdown/topics/a4_elements.md +3 -3
- kash/docs/markdown/topics/a5_tips_for_use_with_other_tools.md +12 -12
- kash/docs/markdown/topics/b0_philosophy_of_kash.md +17 -17
- kash/docs/markdown/topics/b1_kash_overview.md +7 -7
- kash/docs/markdown/topics/b2_workspace_and_file_formats.md +1 -1
- kash/docs/markdown/topics/b3_modern_shell_tool_recommendations.md +1 -1
- kash/docs/markdown/topics/b4_faq.md +7 -7
- kash/docs/markdown/welcome.md +1 -1
- kash/embeddings/embeddings.py +112 -43
- kash/embeddings/text_similarity.py +4 -7
- kash/exec/shell_callable_action.py +4 -3
- kash/help/help_embeddings.py +8 -2
- kash/llm_utils/llm_features.py +1 -1
- kash/llm_utils/llms.py +5 -7
- kash/model/graph_model.py +2 -0
- kash/model/items_model.py +3 -3
- kash/model/params_model.py +1 -1
- kash/shell/output/shell_output.py +2 -2
- kash/utils/file_utils/csv_utils.py +105 -0
- kash/utils/rich_custom/multitask_status.py +19 -5
- kash/utils/text_handling/doc_normalization.py +2 -0
- kash/web_gen/templates/base_styles.css.jinja +356 -24
- kash/web_gen/templates/base_webpage.html.jinja +11 -0
- kash/web_gen/templates/components/toc_styles.css.jinja +15 -3
- kash/web_gen/templates/components/tooltip_styles.css.jinja +1 -0
- kash/web_gen/templates/content_styles.css.jinja +23 -9
- kash/web_gen/templates/item_view.html.jinja +12 -4
- kash/web_gen/templates/simple_webpage.html.jinja +2 -2
- kash/xonsh_custom/custom_shell.py +7 -4
- {kash_shell-0.3.24.dist-info → kash_shell-0.3.26.dist-info}/METADATA +58 -55
- {kash_shell-0.3.24.dist-info → kash_shell-0.3.26.dist-info}/RECORD +42 -40
- {kash_shell-0.3.24.dist-info → kash_shell-0.3.26.dist-info}/WHEEL +0 -0
- {kash_shell-0.3.24.dist-info → kash_shell-0.3.26.dist-info}/entry_points.txt +0 -0
- {kash_shell-0.3.24.dist-info → kash_shell-0.3.26.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from kash.commands.base.basic_file_commands import trash
|
|
2
2
|
from kash.commands.workspace.selection_commands import select
|
|
3
|
-
from kash.config.logger import
|
|
4
|
-
from kash.config.text_styles import PROMPT_ASSIST
|
|
3
|
+
from kash.config.logger import get_logger
|
|
4
|
+
from kash.config.text_styles import PROMPT_ASSIST
|
|
5
|
+
from kash.config.unified_live import get_unified_live
|
|
5
6
|
from kash.docs.all_docs import DocSelection
|
|
6
7
|
from kash.exec import kash_command
|
|
7
8
|
from kash.exec_model.shell_model import ShellResult
|
|
@@ -43,7 +44,7 @@ def assist(
|
|
|
43
44
|
help()
|
|
44
45
|
return
|
|
45
46
|
|
|
46
|
-
with
|
|
47
|
+
with get_unified_live().status("Thinking…"):
|
|
47
48
|
shell_context_assistance(input, model=model, assistance_type=type)
|
|
48
49
|
|
|
49
50
|
|
kash/config/colors.py
CHANGED
|
@@ -135,11 +135,12 @@ web_light_translucent = SimpleNamespace(
|
|
|
135
135
|
secondary=hsl_to_hex("hsl(188, 12%, 28%)"),
|
|
136
136
|
tertiary=hsl_to_hex("hsl(188, 7%, 64%)"),
|
|
137
137
|
bg=hsl_to_hex("hsla(44, 6%, 100%, 0.75)"),
|
|
138
|
-
bg_solid=hsl_to_hex("
|
|
138
|
+
bg_solid=hsl_to_hex("hsl(44, 6%, 100%)"),
|
|
139
139
|
bg_header=hsl_to_hex("hsla(188, 42%, 70%, 0.2)"),
|
|
140
140
|
bg_alt=hsl_to_hex("hsla(39, 24%, 90%, 0.3)"),
|
|
141
|
-
bg_alt_solid=hsl_to_hex("
|
|
142
|
-
bg_meta_solid=hsl_to_hex("
|
|
141
|
+
bg_alt_solid=hsl_to_hex("hsl(39, 24%, 97%)"),
|
|
142
|
+
bg_meta_solid=hsl_to_hex("hsl(39, 24%, 94%)"),
|
|
143
|
+
bg_strong_solid=hsl_to_hex("hsl(39, 8%, 90%)"),
|
|
143
144
|
bg_selected=hsl_to_hex("hsla(188, 21%, 94%, 0.9)"),
|
|
144
145
|
text=hsl_to_hex("hsl(188, 39%, 11%)"),
|
|
145
146
|
code=hsl_to_hex("hsl(44, 38%, 23%)"),
|
|
@@ -174,6 +175,7 @@ web_dark_translucent = SimpleNamespace(
|
|
|
174
175
|
bg_alt=hsl_to_hex("hsla(220, 14%, 12%, 0.5)"),
|
|
175
176
|
bg_alt_solid=hsl_to_hex("hsl(220, 15%, 16%)"),
|
|
176
177
|
bg_meta_solid=hsl_to_hex("hsl(220, 14%, 25%)"),
|
|
178
|
+
bg_strong_solid=hsl_to_hex("hsl(220, 14%, 35%)"),
|
|
177
179
|
bg_selected=hsl_to_hex("hsla(188, 13%, 33%, 0.95)"),
|
|
178
180
|
text=hsl_to_hex("hsl(188, 10%, 90%)"),
|
|
179
181
|
code=hsl_to_hex("hsl(44, 38%, 72%)"),
|
kash/config/text_styles.py
CHANGED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import atexit
|
|
4
|
+
import threading
|
|
5
|
+
import time
|
|
6
|
+
from collections.abc import Generator
|
|
7
|
+
from contextlib import contextmanager
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
|
|
10
|
+
from rich.console import Console, RenderableType
|
|
11
|
+
from rich.live import Live
|
|
12
|
+
from rich.spinner import Spinner
|
|
13
|
+
from rich.text import Text
|
|
14
|
+
from rich.console import Group
|
|
15
|
+
from strif import AtomicVar
|
|
16
|
+
|
|
17
|
+
from kash.config.logger import get_console
|
|
18
|
+
from kash.config.text_styles import SPINNER, COLOR_SPINNER
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class LiveContent:
|
|
23
|
+
"""
|
|
24
|
+
Container for different types of live-updating status content.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
current_status: str | None = None # Single current status message
|
|
28
|
+
multitask_display: RenderableType | None = None
|
|
29
|
+
custom_content: list[RenderableType] = field(default_factory=list)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class UnifiedLive:
|
|
33
|
+
"""
|
|
34
|
+
Unified live display manager that handles all Rich live content in a single Live container.
|
|
35
|
+
|
|
36
|
+
This eliminates Rich's one-live-display-at-a-time limitation by
|
|
37
|
+
providing a single Live that all other components render into.
|
|
38
|
+
|
|
39
|
+
Layout structure:
|
|
40
|
+
- Status message at the top (single spinner and message)
|
|
41
|
+
- MultiTask progress displays in the middle
|
|
42
|
+
- Custom live content at the bottom
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
*,
|
|
48
|
+
console: Console | None = None,
|
|
49
|
+
transient: bool = True,
|
|
50
|
+
refresh_per_second: float = 10,
|
|
51
|
+
):
|
|
52
|
+
self.console = console or get_console()
|
|
53
|
+
self._content = LiveContent()
|
|
54
|
+
self._live = Live(
|
|
55
|
+
console=self.console,
|
|
56
|
+
transient=transient,
|
|
57
|
+
refresh_per_second=refresh_per_second,
|
|
58
|
+
)
|
|
59
|
+
self._is_active = False
|
|
60
|
+
self._lock = threading.RLock() # Thread safety for mutable state
|
|
61
|
+
self._usage_count = 0 # Track how many things are using this live display
|
|
62
|
+
|
|
63
|
+
def start(self) -> None:
|
|
64
|
+
"""Start the unified live display."""
|
|
65
|
+
with self._lock:
|
|
66
|
+
if self._is_active:
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
self._live.__enter__()
|
|
71
|
+
self._is_active = True
|
|
72
|
+
self._update_display()
|
|
73
|
+
except Exception:
|
|
74
|
+
# If starting fails, ensure we're in a clean state
|
|
75
|
+
self._is_active = False
|
|
76
|
+
raise
|
|
77
|
+
|
|
78
|
+
def stop(self) -> None:
|
|
79
|
+
"""Stop the unified live display and restore terminal state."""
|
|
80
|
+
with self._lock:
|
|
81
|
+
if not self._is_active:
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
self._is_active = False
|
|
85
|
+
try:
|
|
86
|
+
self._live.__exit__(None, None, None)
|
|
87
|
+
except Exception:
|
|
88
|
+
# Always try to restore terminal state even if Live cleanup fails
|
|
89
|
+
pass
|
|
90
|
+
finally:
|
|
91
|
+
# Force terminal state restoration
|
|
92
|
+
try:
|
|
93
|
+
# Ensure cursor is visible and terminal is in normal state
|
|
94
|
+
self.console.show_cursor()
|
|
95
|
+
if hasattr(self.console, "_buffer"):
|
|
96
|
+
self.console._buffer.clear()
|
|
97
|
+
except Exception:
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
def _increment_usage(self) -> None:
|
|
101
|
+
"""Increment usage counter (called when entering a context)."""
|
|
102
|
+
with self._lock:
|
|
103
|
+
self._usage_count += 1
|
|
104
|
+
|
|
105
|
+
def _decrement_usage(self) -> None:
|
|
106
|
+
"""Decrement usage counter and stop if unused (called when exiting a context)."""
|
|
107
|
+
with self._lock:
|
|
108
|
+
self._usage_count = max(0, self._usage_count - 1)
|
|
109
|
+
# Auto-stop if nothing is using it and it has content that would be cleared anyway
|
|
110
|
+
if self._usage_count == 0 and self._content.current_status is None:
|
|
111
|
+
self.stop()
|
|
112
|
+
|
|
113
|
+
def set_status(self, message: str | None) -> None:
|
|
114
|
+
"""Set the current status message (or None to clear it)."""
|
|
115
|
+
with self._lock:
|
|
116
|
+
self._content.current_status = message
|
|
117
|
+
self._update_display()
|
|
118
|
+
|
|
119
|
+
@contextmanager
|
|
120
|
+
def status(self, message: str, *, spinner: str = SPINNER) -> Generator[None, None, None]: # pyright: ignore[reportUnusedParameter]
|
|
121
|
+
"""
|
|
122
|
+
Context manager for showing a status message in this unified live display.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
message: Status message to display
|
|
126
|
+
spinner: Spinner type (for future animation support)
|
|
127
|
+
"""
|
|
128
|
+
self._increment_usage()
|
|
129
|
+
self.set_status(message)
|
|
130
|
+
try:
|
|
131
|
+
yield
|
|
132
|
+
finally:
|
|
133
|
+
self.set_status(None)
|
|
134
|
+
self._decrement_usage()
|
|
135
|
+
|
|
136
|
+
def set_multitask_display(self, display: RenderableType | None) -> None:
|
|
137
|
+
"""Set the multitask progress display content."""
|
|
138
|
+
with self._lock:
|
|
139
|
+
self._content.multitask_display = display
|
|
140
|
+
self._update_display()
|
|
141
|
+
|
|
142
|
+
def add_custom_content(self, content: RenderableType) -> int:
|
|
143
|
+
"""Add custom live content. Returns an ID for later removal."""
|
|
144
|
+
with self._lock:
|
|
145
|
+
self._content.custom_content.append(content)
|
|
146
|
+
self._update_display()
|
|
147
|
+
return len(self._content.custom_content) - 1
|
|
148
|
+
|
|
149
|
+
def remove_custom_content(self, content_id: int) -> None:
|
|
150
|
+
"""Remove custom content by ID."""
|
|
151
|
+
with self._lock:
|
|
152
|
+
if 0 <= content_id < len(self._content.custom_content):
|
|
153
|
+
del self._content.custom_content[content_id]
|
|
154
|
+
self._update_display()
|
|
155
|
+
|
|
156
|
+
def _update_display(self) -> None:
|
|
157
|
+
"""Update the live display with current content. Must be called with lock held."""
|
|
158
|
+
if not self._is_active:
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
renderables: list[RenderableType] = []
|
|
162
|
+
|
|
163
|
+
# Add multitask display at the top
|
|
164
|
+
if self._content.multitask_display is not None:
|
|
165
|
+
renderables.append(self._content.multitask_display)
|
|
166
|
+
|
|
167
|
+
# Add custom content in the middle
|
|
168
|
+
renderables.extend(self._content.custom_content)
|
|
169
|
+
|
|
170
|
+
# Add current status message with animated spinner at the bottom
|
|
171
|
+
if self._content.current_status is not None:
|
|
172
|
+
from rich.columns import Columns
|
|
173
|
+
|
|
174
|
+
spinner = Spinner(SPINNER, style=COLOR_SPINNER)
|
|
175
|
+
status_text = Text(self._content.current_status)
|
|
176
|
+
|
|
177
|
+
# Use Columns to display spinner and message side by side
|
|
178
|
+
status_line = Columns([spinner, status_text], padding=(0, 1))
|
|
179
|
+
renderables.append(status_line)
|
|
180
|
+
|
|
181
|
+
# Update the live display - use Group to stack vertically
|
|
182
|
+
if renderables:
|
|
183
|
+
self._live.update(Group(*renderables))
|
|
184
|
+
else:
|
|
185
|
+
# Show empty space if no content
|
|
186
|
+
self._live.update("")
|
|
187
|
+
|
|
188
|
+
@property
|
|
189
|
+
def is_active(self) -> bool:
|
|
190
|
+
"""Check if this unified live is currently active."""
|
|
191
|
+
with self._lock:
|
|
192
|
+
return self._is_active
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# Global unified live instance, auto-initialized on first access (thread-safe)
|
|
196
|
+
_global_unified_live = AtomicVar[UnifiedLive | None](None)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _cleanup_unified_live() -> None:
|
|
200
|
+
"""Clean up the global unified live display on process exit."""
|
|
201
|
+
current = _global_unified_live.value
|
|
202
|
+
if current is not None:
|
|
203
|
+
current.stop()
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
# Register cleanup handler for normal exit only
|
|
207
|
+
atexit.register(_cleanup_unified_live)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def get_unified_live() -> UnifiedLive:
|
|
211
|
+
"""
|
|
212
|
+
Get the global unified live display, auto-initializing if needed.
|
|
213
|
+
|
|
214
|
+
Always returns a valid UnifiedLive instance. Creates and starts one
|
|
215
|
+
automatically if none exists yet. Thread-safe using AtomicVar.
|
|
216
|
+
"""
|
|
217
|
+
with _global_unified_live.lock:
|
|
218
|
+
if not _global_unified_live:
|
|
219
|
+
live = UnifiedLive()
|
|
220
|
+
live.start()
|
|
221
|
+
_global_unified_live.set(live)
|
|
222
|
+
|
|
223
|
+
result = _global_unified_live.value
|
|
224
|
+
assert result
|
|
225
|
+
return result
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def has_unified_live() -> bool:
|
|
229
|
+
"""Check if there's currently an active unified live display."""
|
|
230
|
+
current = _global_unified_live.value
|
|
231
|
+
return current is not None and current.is_active
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@contextmanager
|
|
235
|
+
def unified_live_context(
|
|
236
|
+
console: Console | None = None,
|
|
237
|
+
) -> Generator[UnifiedLive, None, None]:
|
|
238
|
+
"""
|
|
239
|
+
Context manager for working with the unified live display.
|
|
240
|
+
|
|
241
|
+
Returns the global unified live instance, creating and starting it if needed.
|
|
242
|
+
The live display continues running after this context exits.
|
|
243
|
+
"""
|
|
244
|
+
# Always return the global instance, creating if needed
|
|
245
|
+
live = get_unified_live()
|
|
246
|
+
|
|
247
|
+
# Update settings if this is a new instance
|
|
248
|
+
if console is not None:
|
|
249
|
+
live.console = console
|
|
250
|
+
|
|
251
|
+
yield live
|
|
@@ -5,7 +5,7 @@ organizing knowledge.
|
|
|
5
5
|
Kash can be used as a shell, with access to common commands like `ps` and `cd`, but has
|
|
6
6
|
far more capabilities and can generate and manipulate text documents, videos, and more.
|
|
7
7
|
|
|
8
|
-
Kash is written in Python, runs on a user
|
|
8
|
+
Kash is written in Python, runs on a user’s own computer.
|
|
9
9
|
It can connect to the web to download or read content or use LLM-based tools and APIs
|
|
10
10
|
such as ones from OpenAI or Anthropic.
|
|
11
11
|
It saves all content and state to files.
|
|
@@ -69,7 +69,7 @@ necessary.
|
|
|
69
69
|
|
|
70
70
|
Always follow these guidelines:
|
|
71
71
|
|
|
72
|
-
- If you
|
|
72
|
+
- If you’re unsure of what command might help, simply say "I’m not sure how to help with
|
|
73
73
|
that. Run `help` for more about kash.`" Suggest the user run `help` to get more
|
|
74
74
|
information themselves.
|
|
75
75
|
|
|
@@ -81,7 +81,7 @@ Always follow these guidelines:
|
|
|
81
81
|
|
|
82
82
|
- If there is more than one command that might be relevant, mention all the commands
|
|
83
83
|
that might be of interest.
|
|
84
|
-
Don
|
|
84
|
+
Don’t repeatedly mention the same command.
|
|
85
85
|
Be brief!
|
|
86
86
|
|
|
87
87
|
- If they ask for a task that is not covered by the current set of actions, you may
|
|
@@ -8,15 +8,15 @@ exploratory, and flexible using Python and current AI tools.
|
|
|
8
8
|
|
|
9
9
|
The philosophy behind kash is similar to Unix shell tools: simple commands that can be
|
|
10
10
|
combined in flexible and powerful ways.
|
|
11
|
-
It operates on
|
|
11
|
+
It operates on “items” such as URLs, files, or Markdown notes within a workspace
|
|
12
12
|
directory.
|
|
13
13
|
|
|
14
14
|
You can use Kash as an **interactive, AI-native command-line** shell for practical
|
|
15
|
-
knowledge tasks. It
|
|
15
|
+
knowledge tasks. It’s also **a Python library** that lets you convert a simple Python
|
|
16
16
|
function into a command and an MCP tool, so it integrates with other tools like
|
|
17
17
|
Anthropic Desktop or Cursor.
|
|
18
18
|
|
|
19
|
-
It
|
|
19
|
+
It’s new and still has some rough edges, but it’s now working well enough it is feeling
|
|
20
20
|
quite powerful. It now serves as a replacement for my usual shell (previously bash or
|
|
21
21
|
zsh). I use it routinely to remix, combine, and interactively explore and then gradually
|
|
22
22
|
automate complex tasks by composing AI tools, APIs, and libraries.
|
|
@@ -48,19 +48,20 @@ quick to install via uv.
|
|
|
48
48
|
context of a **workspace**. A workspace is just a directory of files that have a few
|
|
49
49
|
conventions to make it easier to maintain context and perform actions.
|
|
50
50
|
A bit like how Git repos work, it has a `.kash/` directory that holds metadata and
|
|
51
|
-
cached content. The rest can be anything, but is typically directories of
|
|
52
|
-
(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
cached content. The rest can be anything, but is typically directories of content and
|
|
52
|
+
resources (often Markdown or HTML but also .docx or .pdf or links to web pages).
|
|
53
|
+
All text files use [frontmatter-format](https://github.com/jlevy/frontmatter-format)
|
|
54
|
+
so have YAML metadata that includes not just title or description, but also how it was
|
|
55
|
+
created. All Markdown files are auto-formatted with
|
|
56
|
+
[flowmark](https://github.com/jlevy/flowmark), which makes documents much easier to
|
|
57
|
+
diff and version control (and prettier to read and edit).
|
|
57
58
|
|
|
58
59
|
- **Compositionality:** An action is composable with other actions simply as a Python
|
|
59
60
|
function, so complex operations (for example, transcribing and annotating a video and
|
|
60
61
|
publishing it on a website) actions can be built from simpler actions (say downloading
|
|
61
62
|
and caching a YouTube video, identifying the speakers in a transcript, formatting it
|
|
62
|
-
as pretty HTML, etc.). The goal is to reduce the
|
|
63
|
-
combining tools, so it
|
|
63
|
+
as pretty HTML, etc.). The goal is to reduce the “interstitial complexity” of
|
|
64
|
+
combining tools, so it’s easy for you (or an LLM!) to combine tools in flexible and
|
|
64
65
|
powerful ways.
|
|
65
66
|
|
|
66
67
|
- **Command-line usage:** In addition to using the function in other libraries and
|
|
@@ -87,16 +88,16 @@ transcripts and notes, write blog posts, extract or visualize concepts, check ci
|
|
|
87
88
|
convert notes to PDFs or beautifully formatted HTML, or perform numerous other
|
|
88
89
|
content-related tasks possible by orchestrating AI tools in the right ways.
|
|
89
90
|
|
|
90
|
-
As I
|
|
91
|
+
As I’ve been building kash over the past couple months, I found I’ve found it’s not only
|
|
91
92
|
faster to do complex things, but that it has also become replacement for my usual shell.
|
|
92
|
-
It
|
|
93
|
+
It’s the power-tool I want to use alongside Cursor and ChatGPT/Claude.
|
|
93
94
|
We all know and trust shells like bash, zsh, and fish, but now I find this is much more
|
|
94
95
|
powerful for everyday usage.
|
|
95
96
|
It has little niceties, like you can just type `files` for a better listing of files or
|
|
96
97
|
`show` and it will show you a file the right way, no matter what kind of file it is.
|
|
97
|
-
You can also type something like
|
|
98
|
+
You can also type something like “? find md files” and press tab and it will list you I
|
|
98
99
|
find it is much more powerful for local usage than than bash/zsh/fish.
|
|
99
|
-
If you
|
|
100
|
+
If you’re a command-line nerd, you might like it a lot.
|
|
100
101
|
|
|
101
102
|
But my hope is that with these enhancements, the shell is also far more friendly and
|
|
102
103
|
usable by anyone reasonably technical, and does not feel so esoteric as a typical Unix
|
|
@@ -105,16 +106,17 @@ shell.
|
|
|
105
106
|
Finally, one more thing: Kash is also my way of experimenting with something else new: a
|
|
106
107
|
**terminal GUI support** that adds GUI features terminal like clickable text, buttons,
|
|
107
108
|
tooltips, and popovers in the terminal.
|
|
108
|
-
I
|
|
109
|
-
|
|
109
|
+
I’ve separately built a new desktop terminal app, Kerm, which adds support for a simple
|
|
110
|
+
“Kerm codes” protocol for such visual components, encoded as OSC codes then rendered in
|
|
110
111
|
the terminal. Because Kash supports these codes, as this develops you will get the
|
|
111
112
|
visuals of a web app layered on the flexibility of a text-based terminal.
|
|
112
113
|
|
|
113
114
|
### Is Kash Mature?
|
|
114
115
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
It’s the result of a couple months of coding and experimentation, and it’s still in
|
|
117
|
+
progress and has rough edges.
|
|
118
|
+
Please help me make it better by sharing your ideas and feedback!
|
|
119
|
+
It’s easiest to DM me at [twitter.com/ojoshe](https://x.com/ojoshe).
|
|
118
120
|
My contact info is at [github.com/jlevy](https://github.com/jlevy).
|
|
119
121
|
|
|
120
122
|
[**Please follow or DM me**](https://x.com/ojoshe) for future updates or if you have
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Kash offers a shell environment based on [xonsh](https://xon.sh/) augmented with a bunch
|
|
6
6
|
of enhanced commands and customizations.
|
|
7
|
-
If you
|
|
7
|
+
If you’ve used a bash or Python shell before, it should be very intuitive.
|
|
8
8
|
|
|
9
9
|
Within the kash shell, you get a full environment with all actions and commands.
|
|
10
10
|
You also get intelligent auto-complete, a built-in assistant to help you perform tasks,
|
|
@@ -13,7 +13,7 @@ and enhanced tab completion.
|
|
|
13
13
|
The shell is an easy way to use Kash actions, simply calling them like other shell
|
|
14
14
|
commands from the command line.
|
|
15
15
|
|
|
16
|
-
But remember that
|
|
16
|
+
But remember that’s just one way to use actions; you can also use them directly in
|
|
17
17
|
Python or from an MCP client.
|
|
18
18
|
|
|
19
19
|
### Current Kash Packages
|
|
@@ -23,7 +23,7 @@ However, some use cases require additional libraries, like video downloading too
|
|
|
23
23
|
handling, etc.
|
|
24
24
|
|
|
25
25
|
To keep kash dependencies more manageable, these additional utilities and actions are
|
|
26
|
-
packaged additional
|
|
26
|
+
packaged additional “kits”.
|
|
27
27
|
|
|
28
28
|
The examples below use video transcription from YouTube as an example.
|
|
29
29
|
To start with more full examples, I suggest starting with the `kash-media` kit.
|
|
@@ -38,7 +38,7 @@ These are for `kash-media` but you can use a `kash-shell` for a more basic setup
|
|
|
38
38
|
Kash is easiest to use via [**uv**](https://docs.astral.sh/uv/), the new package
|
|
39
39
|
manager for Python. `uv` replaces traditional use of `pyenv`, `pipx`, `poetry`, `pip`,
|
|
40
40
|
etc. Installing `uv` also ensures you get a compatible version of Python.
|
|
41
|
-
See [uv
|
|
41
|
+
See [uv’s docs](https://docs.astral.sh/uv/getting-started/installation/) for other
|
|
42
42
|
installation methods and platforms.
|
|
43
43
|
Usually you just want to run:
|
|
44
44
|
```shell
|
|
@@ -47,7 +47,7 @@ These are for `kash-media` but you can use a `kash-shell` for a more basic setup
|
|
|
47
47
|
|
|
48
48
|
2. **Install additional command-line tools:**
|
|
49
49
|
|
|
50
|
-
In addition to Python, it
|
|
50
|
+
In addition to Python, it’s highly recommended to install a few other dependencies to
|
|
51
51
|
make more tools and commands work.
|
|
52
52
|
For macOS, you can again use brew:
|
|
53
53
|
|
|
@@ -104,7 +104,7 @@ These are for `kash-media` but you can use a `kash-shell` for a more basic setup
|
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
These keys should go in the `.env` file in your current work directory or a parent or
|
|
107
|
-
your home directory (recommended if you
|
|
107
|
+
your home directory (recommended if you’ll be working in several directories, as with
|
|
108
108
|
a typical shell).
|
|
109
109
|
|
|
110
110
|
5. **Run kash:**
|
|
@@ -144,14 +144,14 @@ If you add the `--proxy` arg, it will run an MCP stdio server but connect to the
|
|
|
144
144
|
server you are running in the kash shell, by default at `localhost:4440`.
|
|
145
145
|
|
|
146
146
|
Then if you run `start_mcp_server` from the shell, your client will connect to your
|
|
147
|
-
shell, and you can actually use any
|
|
147
|
+
shell, and you can actually use any “published” kash action as an MCP tool.
|
|
148
148
|
|
|
149
|
-
Then you can for example ask your MCP client
|
|
149
|
+
Then you can for example ask your MCP client “can you transcribe this video?”
|
|
150
150
|
and give it a URL, and it will be able to call the `transcribe` action as a tool.
|
|
151
151
|
|
|
152
152
|
What is even better is that all the inputs and outputs are saved in the current kash
|
|
153
|
-
workspace, just as if you
|
|
154
|
-
This way, you don
|
|
153
|
+
workspace, just as if you’d been running these commands yourself in the shell.
|
|
154
|
+
This way, you don’t lose context or any work, and can seamlessly switch between an MCP
|
|
155
155
|
client like Cursor, the shell, and any other tools to edit the inputs or outputs of
|
|
156
156
|
actions in your workspace directory.
|
|
157
157
|
|
|
@@ -15,11 +15,11 @@ Type `help` for the full documentation.
|
|
|
15
15
|
The simplest way to illustrate how to use kash is by example.
|
|
16
16
|
You can go through the commands below a few at a time, trying each one.
|
|
17
17
|
|
|
18
|
-
This is a
|
|
19
|
-
So to get it to work you must install not just the main shell but the kash
|
|
18
|
+
This is a “real” example that uses ffmpeg and a few other libraries.
|
|
19
|
+
So to get it to work you must install not just the main shell but the kash “media kit”
|
|
20
20
|
with extra dependencies.
|
|
21
21
|
This is discussed in [the installation instructions](#installation-steps).
|
|
22
|
-
If you don
|
|
22
|
+
If you don’t have these already installed, you can add these tools:
|
|
23
23
|
|
|
24
24
|
Then run `kash` to start.
|
|
25
25
|
|
|
@@ -170,8 +170,8 @@ All of these steps are just actions.
|
|
|
170
170
|
|
|
171
171
|
### Creating a New Workspace
|
|
172
172
|
|
|
173
|
-
Although you don
|
|
174
|
-
kash. It
|
|
173
|
+
Although you don’t always need one, a *workspace* is very helpful for any real work in
|
|
174
|
+
kash. It’s just a directory of files, plus a `.kash/` directory with various logs and
|
|
175
175
|
metadata.
|
|
176
176
|
|
|
177
177
|
Note the `.kash/cache` directory contains all the downloaded videos and media you
|
|
@@ -192,7 +192,7 @@ By default, when you are not using the shell inside a workspace directory, or wh
|
|
|
192
192
|
run kash the first time, it uses the default *global workspace*.
|
|
193
193
|
|
|
194
194
|
Once you create a workspace, you can `cd` into that workspace and that will become the
|
|
195
|
-
current workspace. (If you
|
|
195
|
+
current workspace. (If you’re familiar with how the `git` command-line works in
|
|
196
196
|
conjunction with the `.git/` directory, this behavior is very similar.)
|
|
197
197
|
|
|
198
198
|
To start a new workspace, run a command like
|
|
@@ -230,7 +230,7 @@ A few of the most important commands for managing files and work are these:
|
|
|
230
230
|
|
|
231
231
|
- `workspace` shows or selects or creates a new workspace.
|
|
232
232
|
Initially you work in the default global workspace (typically at `~/Kash/workspace`)
|
|
233
|
-
but for more real work you
|
|
233
|
+
but for more real work you’ll want to create a workspace, which is a directory to hold
|
|
234
234
|
the files you are working with.
|
|
235
235
|
|
|
236
236
|
- `select` shows or sets selections, which are the set of files the next command will
|
|
@@ -244,7 +244,7 @@ A few of the most important commands for managing files and work are these:
|
|
|
244
244
|
|
|
245
245
|
- `logs` to see full logs (typically more detailed than what you see in the console).
|
|
246
246
|
|
|
247
|
-
- `history` to see recent commands you
|
|
247
|
+
- `history` to see recent commands you’ve run.
|
|
248
248
|
|
|
249
249
|
- `import_item` to add a resource such as a URL or a file to your local workspace.
|
|
250
250
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
### What is Included?
|
|
4
4
|
|
|
5
|
-
I
|
|
5
|
+
I’ve tried to build independently useful pieces that fit together in a simple way:
|
|
6
6
|
|
|
7
7
|
- The kash **action framework**:
|
|
8
8
|
|
|
@@ -75,9 +75,9 @@ I've tried to build independently useful pieces that fit together in a simple wa
|
|
|
75
75
|
OSC 8 links
|
|
76
76
|
|
|
77
77
|
- Sadly, we may have mind-boggling AI tools, but Terminals are still incredibly
|
|
78
|
-
archaic and don
|
|
78
|
+
archaic and don’t support these features well (more on this below) but I have a new
|
|
79
79
|
terminal, Kerm, that shows these as tooltips and makes every command clickable
|
|
80
|
-
(please contact me if you
|
|
80
|
+
(please contact me if you’d like an early developer preview, as I’d love feedback)
|
|
81
81
|
|
|
82
82
|
## Tools Used by Kash
|
|
83
83
|
|
|
@@ -17,14 +17,14 @@ I tried half a dozen different popular terminals on Mac
|
|
|
17
17
|
[Hyper](https://hyper.is/)). Unfortunately, none offer really good support right out of
|
|
18
18
|
the box, but I encourage you to try
|
|
19
19
|
|
|
20
|
-
✨**Would you be willing to help test something new?** If you
|
|
20
|
+
✨**Would you be willing to help test something new?** If you’ve made it this far and are
|
|
21
21
|
still reading, I have a request.
|
|
22
|
-
So alongside kash, I
|
|
22
|
+
So alongside kash, I’ve begun to build a new terminal app, **Kerm**, that has the
|
|
23
23
|
features we would want in a modern command line, such as clickable links and commands,
|
|
24
24
|
tooltips, and image support.
|
|
25
25
|
Kash also takes advantage of this support by embedding OSC 8 links.
|
|
26
26
|
It is *so* much nicer to use.
|
|
27
|
-
I
|
|
27
|
+
I’d like feedback so please [message me](https://twitter.com/ojoshe) if you’d like to
|
|
28
28
|
try it out an early dev version!
|
|
29
29
|
|
|
30
30
|
### Choosing an Editor
|
|
@@ -34,7 +34,7 @@ Kash respects the `EDITOR` environment variable if you use the `edit` command.
|
|
|
34
34
|
|
|
35
35
|
### Using on macOS
|
|
36
36
|
|
|
37
|
-
Kash calls `open` to open some files, so in general, it
|
|
37
|
+
Kash calls `open` to open some files, so in general, it’s convenient to make sure your
|
|
38
38
|
preferred editor is set up for `.yml` and `.md` files.
|
|
39
39
|
|
|
40
40
|
For convenience, a reminder on how to do this:
|
|
@@ -42,7 +42,7 @@ For convenience, a reminder on how to do this:
|
|
|
42
42
|
- In Finder, pick a `.md` or `.yml` file and hit Cmd-I (or right-click and select Get
|
|
43
43
|
Info).
|
|
44
44
|
|
|
45
|
-
- Select the editor, such as Cursor or VSCode or Obsidian, and click the
|
|
45
|
+
- Select the editor, such as Cursor or VSCode or Obsidian, and click the “Change All…”
|
|
46
46
|
button to have it apply to all files with that extension.
|
|
47
47
|
|
|
48
48
|
- Repeat with each file type.
|
|
@@ -61,23 +61,23 @@ out of the box to edit workspace files in Markdown, HTML, and YAML in kash works
|
|
|
61
61
|
Kash uses Markdown files with YAML frontmatter, which is fully compatible with
|
|
62
62
|
[Obsidian](https://obsidian.md/). Some notes:
|
|
63
63
|
|
|
64
|
-
- In Obsidian
|
|
64
|
+
- In Obsidian’s preferences, under Editor, turn on “Strict line breaks”.
|
|
65
65
|
|
|
66
|
-
- This makes the line breaks in kash
|
|
66
|
+
- This makes the line breaks in kash’s normalized Markdown output work well in Obsidian.
|
|
67
67
|
|
|
68
68
|
- Some kash files also contain HTML in Markdown.
|
|
69
|
-
This works fine, but note that only the current line
|
|
69
|
+
This works fine, but note that only the current line’s HTML is shown in Obsidian.
|
|
70
70
|
|
|
71
71
|
- Install the [Front Matter Title
|
|
72
72
|
plugin](https://github.com/snezhig/obsidian-front-matter-title):
|
|
73
73
|
|
|
74
|
-
- Go to settings, enable community plugins, search for
|
|
74
|
+
- Go to settings, enable community plugins, search for “Front Matter Title” and
|
|
75
75
|
install.
|
|
76
76
|
|
|
77
|
-
- Under
|
|
78
|
-
file explorer
|
|
77
|
+
- Under “Installed Plugins,” adjust the settings to enable “Replace shown title in
|
|
78
|
+
file explorer,” “Replace shown title in graph,” etc.
|
|
79
79
|
|
|
80
|
-
- You probably want to keep the
|
|
80
|
+
- You probably want to keep the “Replace titles in header of leaves” off so you can
|
|
81
81
|
still see original filenames if needed.
|
|
82
82
|
|
|
83
83
|
- Now titles are easy to read for all kash notes.
|