kash-shell 0.3.14__py3-none-any.whl → 0.3.16__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/actions/core/render_as_html.py +3 -1
- kash/actions/core/tabbed_webpage_generate.py +3 -1
- kash/commands/workspace/selection_commands.py +22 -0
- kash/commands/workspace/workspace_commands.py +11 -5
- kash/config/colors.py +87 -13
- kash/exec/action_exec.py +23 -4
- kash/exec/precondition_registry.py +5 -3
- kash/exec/preconditions.py +7 -2
- kash/exec/resolve_args.py +2 -2
- kash/file_storage/file_store.py +87 -54
- kash/file_storage/item_file_format.py +0 -2
- kash/local_server/local_server_routes.py +1 -1
- kash/model/actions_model.py +5 -1
- kash/model/items_model.py +39 -38
- kash/shell/completions/shell_completions.py +1 -1
- kash/utils/file_utils/file_formats_model.py +15 -7
- kash/utils/text_handling/doc_normalization.py +7 -0
- kash/web_gen/simple_webpage.py +4 -1
- kash/web_gen/tabbed_webpage.py +8 -3
- kash/web_gen/templates/base_styles.css.jinja +48 -5
- kash/web_gen/templates/base_webpage.html.jinja +128 -0
- kash/web_gen/templates/item_view.html.jinja +4 -2
- kash/web_gen/templates/simple_webpage.html.jinja +24 -0
- kash/xontrib/kash_extension.py +3 -2
- {kash_shell-0.3.14.dist-info → kash_shell-0.3.16.dist-info}/METADATA +1 -1
- {kash_shell-0.3.14.dist-info → kash_shell-0.3.16.dist-info}/RECORD +29 -29
- {kash_shell-0.3.14.dist-info → kash_shell-0.3.16.dist-info}/WHEEL +0 -0
- {kash_shell-0.3.14.dist-info → kash_shell-0.3.16.dist-info}/entry_points.txt +0 -0
- {kash_shell-0.3.14.dist-info → kash_shell-0.3.16.dist-info}/licenses/LICENSE +0 -0
|
@@ -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(
|
|
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(
|
|
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,
|
|
@@ -7,6 +7,7 @@ from strif import copyfile_atomic
|
|
|
7
7
|
|
|
8
8
|
from kash.config.logger import get_logger
|
|
9
9
|
from kash.exec import kash_command
|
|
10
|
+
from kash.exec.resolve_args import assemble_path_args
|
|
10
11
|
from kash.exec_model.shell_model import ShellResult
|
|
11
12
|
from kash.model.paths_model import StorePath
|
|
12
13
|
from kash.shell.ui.shell_results import shell_print_selection_history
|
|
@@ -30,6 +31,7 @@ def select(
|
|
|
30
31
|
clear_all: bool = False,
|
|
31
32
|
clear_future: bool = False,
|
|
32
33
|
refresh: bool = False,
|
|
34
|
+
no_check: bool = False,
|
|
33
35
|
) -> ShellResult:
|
|
34
36
|
"""
|
|
35
37
|
Set or show the current selection.
|
|
@@ -51,6 +53,7 @@ def select(
|
|
|
51
53
|
:param clear_all: Clear the full selection history.
|
|
52
54
|
:param clear_future: Clear all selections from history after the current one.
|
|
53
55
|
:param refresh: Refresh the current selection to drop any paths that no longer exist.
|
|
56
|
+
:param no_check: Do not check if the paths exist.
|
|
54
57
|
"""
|
|
55
58
|
ws = current_ws()
|
|
56
59
|
|
|
@@ -68,6 +71,10 @@ def select(
|
|
|
68
71
|
raise InvalidInput("Cannot combine multiple flags")
|
|
69
72
|
if paths and any(exclusive_flags):
|
|
70
73
|
raise InvalidInput("Cannot combine paths with other flags")
|
|
74
|
+
if not no_check:
|
|
75
|
+
for path in paths:
|
|
76
|
+
if not Path(ws.base_dir / path).exists():
|
|
77
|
+
raise InvalidInput(f"Path does not exist: {fmt_loc(path)}")
|
|
71
78
|
|
|
72
79
|
if paths:
|
|
73
80
|
store_paths = [StorePath(path) for path in paths]
|
|
@@ -203,3 +210,18 @@ def save(parent: str | None = None, to: str | None = None, no_frontmatter: bool
|
|
|
203
210
|
for store_path in store_paths:
|
|
204
211
|
target_path = target_dir / basename(store_path)
|
|
205
212
|
copy_file(store_path, target_path)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@kash_command
|
|
216
|
+
def show_parent_dir(*paths: str) -> None:
|
|
217
|
+
"""
|
|
218
|
+
Show the parent directory of the first item in the current selection.
|
|
219
|
+
"""
|
|
220
|
+
from kash.commands.base.show_command import show
|
|
221
|
+
|
|
222
|
+
input_paths = assemble_path_args(*paths)
|
|
223
|
+
if not input_paths:
|
|
224
|
+
raise InvalidInput("No paths provided")
|
|
225
|
+
|
|
226
|
+
input_path = current_ws().resolve_to_abs_path(input_paths[0])
|
|
227
|
+
show(input_path.parent)
|
|
@@ -59,6 +59,7 @@ from kash.utils.errors import InvalidInput
|
|
|
59
59
|
from kash.utils.file_formats.chat_format import tail_chat_history
|
|
60
60
|
from kash.utils.file_utils.dir_info import is_nonempty_dir
|
|
61
61
|
from kash.utils.file_utils.file_formats_model import Format
|
|
62
|
+
from kash.utils.text_handling.doc_normalization import can_normalize
|
|
62
63
|
from kash.web_content.file_cache_utils import cache_file
|
|
63
64
|
from kash.workspaces import (
|
|
64
65
|
current_ws,
|
|
@@ -189,11 +190,13 @@ def cache_content(*urls_or_paths: str, refetch: bool = False) -> None:
|
|
|
189
190
|
|
|
190
191
|
|
|
191
192
|
@kash_command
|
|
192
|
-
def download(*urls_or_paths: str, refetch: bool = False) -> ShellResult:
|
|
193
|
+
def download(*urls_or_paths: str, refetch: bool = False, no_format: bool = False) -> ShellResult:
|
|
193
194
|
"""
|
|
194
195
|
Download a URL or resource. Uses cached content if available, unless `refetch` is true.
|
|
195
196
|
Inputs can be URLs or paths to URL resources.
|
|
196
197
|
Creates both resource and document versions for text content.
|
|
198
|
+
|
|
199
|
+
:param no_format: If true, do not also normalize Markdown content.
|
|
197
200
|
"""
|
|
198
201
|
ws = current_ws()
|
|
199
202
|
saved_paths = []
|
|
@@ -236,11 +239,13 @@ def download(*urls_or_paths: str, refetch: bool = False) -> ShellResult:
|
|
|
236
239
|
mime_type=mime_type,
|
|
237
240
|
original_filename=original_filename,
|
|
238
241
|
)
|
|
242
|
+
# For initial content, do not format or add frontmatter.
|
|
239
243
|
store_path = ws.save(resource_item, no_frontmatter=True, no_format=True)
|
|
240
244
|
saved_paths.append(store_path)
|
|
245
|
+
select(store_path)
|
|
241
246
|
|
|
242
|
-
# Also create a doc version for text content
|
|
243
|
-
if resource_item.format and resource_item.format
|
|
247
|
+
# Also create a doc version for text content if we want to normalize formatting.
|
|
248
|
+
if resource_item.format and can_normalize(resource_item.format) and not no_format:
|
|
244
249
|
doc_item = Item.from_external_path(
|
|
245
250
|
cache_result.content.path,
|
|
246
251
|
ItemType.doc,
|
|
@@ -248,8 +253,10 @@ def download(*urls_or_paths: str, refetch: bool = False) -> ShellResult:
|
|
|
248
253
|
mime_type=mime_type,
|
|
249
254
|
original_filename=original_filename,
|
|
250
255
|
)
|
|
251
|
-
|
|
256
|
+
# Now use default formatting and frontmatter.
|
|
257
|
+
doc_store_path = ws.save(doc_item)
|
|
252
258
|
saved_paths.append(doc_store_path)
|
|
259
|
+
select(doc_store_path)
|
|
253
260
|
|
|
254
261
|
print_status(
|
|
255
262
|
"Downloaded %s %s:\n%s",
|
|
@@ -257,7 +264,6 @@ def download(*urls_or_paths: str, refetch: bool = False) -> ShellResult:
|
|
|
257
264
|
plural("item", len(saved_paths)),
|
|
258
265
|
fmt_lines(saved_paths),
|
|
259
266
|
)
|
|
260
|
-
select(*saved_paths)
|
|
261
267
|
|
|
262
268
|
return ShellResult(show_selection=True)
|
|
263
269
|
|
kash/config/colors.py
CHANGED
|
@@ -148,12 +148,40 @@ web_light_translucent = SimpleNamespace(
|
|
|
148
148
|
tooltip_bg=hsl_to_hex("hsla(188, 6%, 37%, 0.7)"),
|
|
149
149
|
popover_bg=hsl_to_hex("hsla(188, 6%, 37%, 0.7)"),
|
|
150
150
|
bright=hsl_to_hex("hsl(134, 43%, 60%)"),
|
|
151
|
+
success=hsl_to_hex("hsl(134, 43%, 60%)"),
|
|
151
152
|
selection="hsla(225, 61%, 82%, 0.80)",
|
|
152
153
|
scrollbar=hsl_to_hex("hsla(189, 12%, 55%, 0.9)"),
|
|
153
154
|
scrollbar_hover=hsl_to_hex("hsla(190, 12%, 38%, 0.9)"),
|
|
154
155
|
)
|
|
155
156
|
|
|
156
157
|
|
|
158
|
+
# Web dark colors
|
|
159
|
+
web_dark_translucent = SimpleNamespace(
|
|
160
|
+
primary=hsl_to_hex("hsl(188, 40%, 62%)"),
|
|
161
|
+
primary_light=hsl_to_hex("hsl(188, 50%, 72%)"),
|
|
162
|
+
secondary=hsl_to_hex("hsl(188, 12%, 65%)"),
|
|
163
|
+
bg=hsl_to_hex("hsla(220, 14%, 7%, 0.95)"),
|
|
164
|
+
bg_solid=hsl_to_hex("hsl(220, 14%, 7%)"),
|
|
165
|
+
bg_header=hsl_to_hex("hsla(188, 42%, 20%, 0.3)"),
|
|
166
|
+
bg_alt=hsl_to_hex("hsla(220, 14%, 12%, 0.5)"),
|
|
167
|
+
bg_alt_solid=hsl_to_hex("hsl(220, 14%, 12%)"),
|
|
168
|
+
text=hsl_to_hex("hsl(188, 20%, 90%)"),
|
|
169
|
+
border=hsl_to_hex("hsl(188, 8%, 25%)"),
|
|
170
|
+
border_hint=hsl_to_hex("hsla(188, 8%, 35%, 0.7)"),
|
|
171
|
+
border_accent=hsl_to_hex("hsla(305, 30%, 55%, 0.85)"),
|
|
172
|
+
hover=hsl_to_hex("hsl(188, 12%, 35%)"),
|
|
173
|
+
hover_bg=hsl_to_hex("hsla(188, 20%, 25%, 0.4)"),
|
|
174
|
+
hint=hsl_to_hex("hsl(188, 11%, 55%)"),
|
|
175
|
+
tooltip_bg=hsl_to_hex("hsla(188, 6%, 20%, 0.9)"),
|
|
176
|
+
popover_bg=hsl_to_hex("hsla(188, 6%, 20%, 0.9)"),
|
|
177
|
+
bright=hsl_to_hex("hsl(134, 43%, 60%)"),
|
|
178
|
+
success=hsl_to_hex("hsl(134, 43%, 60%)"),
|
|
179
|
+
selection=hsl_to_hex("hsla(225, 61%, 40%, 0.40)"),
|
|
180
|
+
scrollbar=hsl_to_hex("hsla(189, 12%, 35%, 0.9)"),
|
|
181
|
+
scrollbar_hover=hsl_to_hex("hsla(190, 12%, 50%, 0.9)"),
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
157
185
|
rich_terminal_dark = TerminalTheme(
|
|
158
186
|
hex_to_int(terminal_dark.background),
|
|
159
187
|
hex_to_int(terminal_dark.foreground),
|
|
@@ -211,9 +239,6 @@ rich_terminal_light = TerminalTheme(
|
|
|
211
239
|
# We default to light colors for Rich content in HTML.
|
|
212
240
|
rich_terminal = rich_terminal_light
|
|
213
241
|
|
|
214
|
-
# Only support light web colors for now.
|
|
215
|
-
web = web_light_translucent
|
|
216
|
-
|
|
217
242
|
# Logical colors
|
|
218
243
|
logical = SimpleNamespace(
|
|
219
244
|
concept_dark=terminal.green_dark,
|
|
@@ -234,18 +259,23 @@ logical = SimpleNamespace(
|
|
|
234
259
|
)
|
|
235
260
|
|
|
236
261
|
|
|
237
|
-
def consolidate_color_vars(
|
|
262
|
+
def consolidate_color_vars(
|
|
263
|
+
overrides: dict[str, str] | None = None, web_colors: SimpleNamespace | None = None
|
|
264
|
+
) -> dict[str, str]:
|
|
238
265
|
"""
|
|
239
266
|
Consolidate all color variables into a single dictionary with appropriate prefixes.
|
|
240
267
|
Terminal variables have no prefix, while web and logical variables have "color-" prefix.
|
|
241
268
|
"""
|
|
242
269
|
if overrides is None:
|
|
243
270
|
overrides = {}
|
|
271
|
+
if web_colors is None:
|
|
272
|
+
web_colors = web_light_translucent
|
|
273
|
+
|
|
244
274
|
return {
|
|
245
275
|
# Terminal variables (no prefix)
|
|
246
276
|
**terminal.__dict__,
|
|
247
277
|
# Web and logical variables with "color-" prefix
|
|
248
|
-
**{f"color-{k}": v for k, v in
|
|
278
|
+
**{f"color-{k}": v for k, v in web_colors.__dict__.items()},
|
|
249
279
|
**{f"color-{k}": v for k, v in logical.__dict__.items()},
|
|
250
280
|
# Overrides take precedence (assume they already have correct prefixes)
|
|
251
281
|
**overrides,
|
|
@@ -262,19 +292,63 @@ def normalize_var_names(variables: dict[str, str]) -> dict[str, str]:
|
|
|
262
292
|
|
|
263
293
|
def generate_css_vars(overrides: dict[str, str] | None = None) -> str:
|
|
264
294
|
"""
|
|
265
|
-
Generate CSS variables for
|
|
295
|
+
Generate CSS variables for terminal and both light and dark themes.
|
|
266
296
|
"""
|
|
267
297
|
if overrides is None:
|
|
268
298
|
overrides = {}
|
|
269
|
-
normalized_vars = normalize_var_names(consolidate_color_vars(overrides))
|
|
270
299
|
|
|
271
|
-
#
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
300
|
+
# Get base variables (terminal colors stay the same)
|
|
301
|
+
base_vars = normalize_var_names({k: v for k, v in terminal.__dict__.items()})
|
|
302
|
+
|
|
303
|
+
# Get light theme color variables
|
|
304
|
+
light_color_vars = normalize_var_names(
|
|
305
|
+
{f"color-{k}": v for k, v in web_light_translucent.__dict__.items()}
|
|
306
|
+
)
|
|
307
|
+
light_color_vars.update(
|
|
308
|
+
normalize_var_names({f"color-{k}": v for k, v in logical.__dict__.items()})
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
# Get dark theme color variables
|
|
312
|
+
dark_color_vars = normalize_var_names(
|
|
313
|
+
{f"color-{k}": v for k, v in web_dark_translucent.__dict__.items()}
|
|
314
|
+
)
|
|
315
|
+
dark_color_vars.update(
|
|
316
|
+
normalize_var_names({f"color-{k}": v for k, v in logical.__dict__.items()})
|
|
317
|
+
)
|
|
276
318
|
|
|
277
|
-
|
|
319
|
+
# Apply overrides
|
|
320
|
+
if overrides:
|
|
321
|
+
normalized_overrides = normalize_var_names(overrides)
|
|
322
|
+
light_color_vars.update(normalized_overrides)
|
|
323
|
+
dark_color_vars.update(normalized_overrides)
|
|
324
|
+
|
|
325
|
+
# Generate CSS
|
|
326
|
+
css_parts = []
|
|
327
|
+
|
|
328
|
+
# Root with all variables (defaults to light)
|
|
329
|
+
css_parts.append(":root {")
|
|
330
|
+
css_parts.extend(f" --{k}: {v};" for k, v in base_vars.items())
|
|
331
|
+
css_parts.extend(f" --{k}: {v};" for k, v in light_color_vars.items())
|
|
332
|
+
css_parts.append("}\n")
|
|
333
|
+
|
|
334
|
+
# Light theme (only color- variables)
|
|
335
|
+
css_parts.append('[data-theme="light"] {')
|
|
336
|
+
css_parts.extend(f" --{k}: {v};" for k, v in light_color_vars.items())
|
|
337
|
+
css_parts.append("}\n")
|
|
338
|
+
|
|
339
|
+
# Dark theme (only color- variables)
|
|
340
|
+
css_parts.append('[data-theme="dark"] {')
|
|
341
|
+
css_parts.extend(f" --{k}: {v};" for k, v in dark_color_vars.items())
|
|
342
|
+
css_parts.append("}\n")
|
|
343
|
+
|
|
344
|
+
# Print media
|
|
345
|
+
css_parts.append("@media print {")
|
|
346
|
+
css_parts.append(' :root, [data-theme="dark"] {')
|
|
347
|
+
css_parts.extend(f" --{k}: {v} !important;" for k, v in light_color_vars.items())
|
|
348
|
+
css_parts.append(" }")
|
|
349
|
+
css_parts.append("}")
|
|
350
|
+
|
|
351
|
+
return "\n".join(css_parts)
|
|
278
352
|
|
|
279
353
|
|
|
280
354
|
if __name__ == "__main__":
|
kash/exec/action_exec.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import time
|
|
2
2
|
from dataclasses import replace
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
|
|
4
|
-
from prettyfmt import fmt_lines, plural
|
|
5
|
+
from prettyfmt import fmt_lines, fmt_path, plural
|
|
5
6
|
|
|
6
7
|
from kash.config.logger import get_logger
|
|
7
8
|
from kash.config.text_styles import (
|
|
@@ -287,6 +288,14 @@ def save_action_result(
|
|
|
287
288
|
Save the result of an action to the workspace. Handles skipping duplicates and
|
|
288
289
|
archiving old inputs, if appropriate, based on hints in the ActionResult.
|
|
289
290
|
"""
|
|
291
|
+
# If an action returned an external path, we should confirm it exists or it is probably
|
|
292
|
+
# a bug in the action.
|
|
293
|
+
for item in result.items:
|
|
294
|
+
if item.external_path and not Path(item.external_path).exists():
|
|
295
|
+
raise InvalidOutput(
|
|
296
|
+
f"External path returned by action does not exist: {fmt_path(item.external_path)}"
|
|
297
|
+
)
|
|
298
|
+
|
|
290
299
|
input_items = action_input.items
|
|
291
300
|
|
|
292
301
|
skipped_paths = []
|
|
@@ -377,7 +386,11 @@ def run_action_with_caching(
|
|
|
377
386
|
# Run it!
|
|
378
387
|
result = run_action_operation(context, action_input, operation)
|
|
379
388
|
result_store_paths, archived_store_paths = save_action_result(
|
|
380
|
-
ws,
|
|
389
|
+
ws,
|
|
390
|
+
result,
|
|
391
|
+
action_input,
|
|
392
|
+
as_tmp=settings.tmp_output,
|
|
393
|
+
no_format=settings.no_format,
|
|
381
394
|
)
|
|
382
395
|
|
|
383
396
|
PrintHooks.before_done_message()
|
|
@@ -448,13 +461,19 @@ def run_action_with_shell_context(
|
|
|
448
461
|
|
|
449
462
|
# As a special case for convenience, if the action expects no args, ignore any pre-selected inputs.
|
|
450
463
|
if action.expected_args == NO_ARGS and from_selection:
|
|
451
|
-
log.message(
|
|
464
|
+
log.message(
|
|
465
|
+
"Not using current selection since action `%s` expects no args.",
|
|
466
|
+
action_name,
|
|
467
|
+
)
|
|
452
468
|
args.clear()
|
|
453
469
|
|
|
454
470
|
if args:
|
|
455
471
|
source_str = "provided args" if provided_args else "selection"
|
|
456
472
|
log.message(
|
|
457
|
-
"Using %s as inputs to action `%s`:\n%s",
|
|
473
|
+
"Using %s as inputs to action `%s`:\n%s",
|
|
474
|
+
source_str,
|
|
475
|
+
action_name,
|
|
476
|
+
fmt_lines(args),
|
|
458
477
|
)
|
|
459
478
|
|
|
460
479
|
# Get items for each input arg.
|
|
@@ -16,11 +16,13 @@ def kash_precondition(func: Callable[[Item], bool]) -> Precondition:
|
|
|
16
16
|
"""
|
|
17
17
|
Decorator to register a function as a Precondition.
|
|
18
18
|
The function should return a bool and/or raise `PreconditionFailure`.
|
|
19
|
+
Returns an actual Precondition object, not the function, so that it's possible to
|
|
20
|
+
do precondition algebra, e.g. `is_file & has_body`.
|
|
19
21
|
|
|
20
22
|
Example:
|
|
21
|
-
@
|
|
22
|
-
def
|
|
23
|
-
return item.
|
|
23
|
+
@kash_precondition
|
|
24
|
+
def has_body(item: Item) -> bool:
|
|
25
|
+
return item.has_body()
|
|
24
26
|
"""
|
|
25
27
|
precondition = Precondition(func)
|
|
26
28
|
|
kash/exec/preconditions.py
CHANGED
|
@@ -104,6 +104,11 @@ def has_html_body(item: Item) -> bool:
|
|
|
104
104
|
return bool(has_body(item) and item.format and item.format.is_html)
|
|
105
105
|
|
|
106
106
|
|
|
107
|
+
@kash_precondition
|
|
108
|
+
def has_html_compatible_body(item: Item) -> bool:
|
|
109
|
+
return bool(has_body(item) and item.format and item.format.is_html_compatible)
|
|
110
|
+
|
|
111
|
+
|
|
107
112
|
@kash_precondition
|
|
108
113
|
def has_markdown_body(item: Item) -> bool:
|
|
109
114
|
return bool(has_body(item) and item.format and item.format.is_markdown)
|
|
@@ -136,14 +141,14 @@ def is_markdown_with_html(item: Item) -> bool:
|
|
|
136
141
|
|
|
137
142
|
@kash_precondition
|
|
138
143
|
def is_markdown_template(item: Item) -> bool:
|
|
139
|
-
return
|
|
144
|
+
return has_markdown_body(item) and contains_curly_vars(item)
|
|
140
145
|
|
|
141
146
|
|
|
142
147
|
@kash_precondition
|
|
143
148
|
def is_markdown_list(item: Item) -> bool:
|
|
144
149
|
try:
|
|
145
150
|
return (
|
|
146
|
-
|
|
151
|
+
has_markdown_body(item)
|
|
147
152
|
and item.body is not None
|
|
148
153
|
and len(extract_bullet_points(item.body)) >= 2
|
|
149
154
|
)
|
kash/exec/resolve_args.py
CHANGED
|
@@ -42,7 +42,7 @@ def resolve_path_arg(path_str: UnresolvedPath) -> Path | StorePath:
|
|
|
42
42
|
return path
|
|
43
43
|
else:
|
|
44
44
|
try:
|
|
45
|
-
store_path = current_ws().
|
|
45
|
+
store_path = current_ws().resolve_to_store_path(path)
|
|
46
46
|
if store_path:
|
|
47
47
|
return store_path
|
|
48
48
|
else:
|
|
@@ -110,7 +110,7 @@ def resolvable_paths(paths: Sequence[StorePath | Path]) -> list[StorePath]:
|
|
|
110
110
|
current workspace.
|
|
111
111
|
"""
|
|
112
112
|
ws = current_ws()
|
|
113
|
-
resolvable = list(filter(None, (ws.
|
|
113
|
+
resolvable = list(filter(None, (ws.resolve_to_store_path(p) for p in paths)))
|
|
114
114
|
return resolvable
|
|
115
115
|
|
|
116
116
|
|