dfd
This commit is contained in:
143
CLI.py
143
CLI.py
@@ -231,9 +231,25 @@ def _get_table_title_for_command(cmd_name: str, emitted_items: Optional[List[Any
|
||||
'delete_file': 'Results',
|
||||
'check-file-status': 'Status',
|
||||
'check_file_status': 'Status',
|
||||
'get-metadata': None,
|
||||
'get_metadata': None,
|
||||
}
|
||||
|
||||
return title_map.get(cmd_name, 'Results')
|
||||
|
||||
mapped = title_map.get(cmd_name, 'Results')
|
||||
if mapped is not None:
|
||||
return mapped
|
||||
|
||||
# For metadata, derive title from first item if available
|
||||
if emitted_items:
|
||||
first = emitted_items[0]
|
||||
try:
|
||||
if isinstance(first, dict) and first.get('title'):
|
||||
return str(first.get('title'))
|
||||
if hasattr(first, 'title') and getattr(first, 'title'):
|
||||
return str(getattr(first, 'title'))
|
||||
except Exception:
|
||||
pass
|
||||
return 'Results'
|
||||
|
||||
|
||||
def _close_cli_worker_manager() -> None:
|
||||
@@ -409,9 +425,20 @@ def _get_cmdlet_names() -> List[str]:
|
||||
|
||||
def _import_cmd_module(mod_name: str):
|
||||
"""Import a cmdlet/native module from cmdlets or cmdnats packages."""
|
||||
for package in ("cmdlets", "cmdnats", None):
|
||||
# Normalize leading punctuation used in aliases (e.g., .pipe)
|
||||
normalized = (mod_name or "").strip()
|
||||
if normalized.startswith('.'):
|
||||
normalized = normalized.lstrip('.')
|
||||
# Convert hyphens to underscores to match module filenames
|
||||
normalized = normalized.replace("-", "_")
|
||||
if not normalized:
|
||||
return None
|
||||
|
||||
# Prefer native cmdnats modules first so editable installs of this package
|
||||
# don't shadow the in-repo implementations (e.g., .pipe autocomplete flags).
|
||||
for package in ("cmdnats", "cmdlets", None):
|
||||
try:
|
||||
qualified = f"{package}.{mod_name}" if package else mod_name
|
||||
qualified = f"{package}.{normalized}" if package else normalized
|
||||
return import_module(qualified)
|
||||
except ModuleNotFoundError:
|
||||
continue
|
||||
@@ -495,6 +522,15 @@ def _get_arg_choices(cmd_name: str, arg_name: str) -> List[str]:
|
||||
merged = sorted(set(provider_choices + meta_choices))
|
||||
if merged:
|
||||
return merged
|
||||
|
||||
if normalized_arg == "scrape":
|
||||
try:
|
||||
from helper.metadata_search import list_metadata_providers
|
||||
meta_providers = list_metadata_providers(_load_cli_config())
|
||||
if meta_providers:
|
||||
return sorted(meta_providers.keys())
|
||||
except Exception:
|
||||
pass
|
||||
mod = _import_cmd_module(mod_name)
|
||||
data = getattr(mod, "CMDLET", None) if mod else None
|
||||
if data:
|
||||
@@ -536,36 +572,48 @@ if (
|
||||
text = document.text_before_cursor
|
||||
tokens = text.split()
|
||||
|
||||
if not tokens:
|
||||
# Respect pipeline stages: only use tokens after the last '|'
|
||||
last_pipe = -1
|
||||
for idx, tok in enumerate(tokens):
|
||||
if tok == "|":
|
||||
last_pipe = idx
|
||||
stage_tokens = tokens[last_pipe + 1:] if last_pipe >= 0 else tokens
|
||||
|
||||
if not stage_tokens:
|
||||
for cmd in self.cmdlet_names:
|
||||
yield CompletionType(cmd, start_position=0)
|
||||
elif len(tokens) == 1:
|
||||
current = tokens[0].lower()
|
||||
return
|
||||
|
||||
# Single token at this stage -> suggest command names/keywords
|
||||
if len(stage_tokens) == 1:
|
||||
current = stage_tokens[0].lower()
|
||||
for cmd in self.cmdlet_names:
|
||||
if cmd.startswith(current):
|
||||
yield CompletionType(cmd, start_position=-len(current))
|
||||
for keyword in ["help", "exit", "quit"]:
|
||||
if keyword.startswith(current):
|
||||
yield CompletionType(keyword, start_position=-len(current))
|
||||
else:
|
||||
cmd_name = tokens[0].replace("_", "-").lower()
|
||||
current_token = tokens[-1].lower()
|
||||
prev_token = tokens[-2].lower() if len(tokens) > 1 else ""
|
||||
return
|
||||
|
||||
choices = _get_arg_choices(cmd_name, prev_token)
|
||||
if choices:
|
||||
for choice in choices:
|
||||
if choice.lower().startswith(current_token):
|
||||
yield CompletionType(choice, start_position=-len(current_token))
|
||||
return
|
||||
# Otherwise treat first token of stage as command and complete its args
|
||||
cmd_name = stage_tokens[0].replace("_", "-").lower()
|
||||
current_token = stage_tokens[-1].lower()
|
||||
prev_token = stage_tokens[-2].lower() if len(stage_tokens) > 1 else ""
|
||||
|
||||
arg_names = _get_cmdlet_args(cmd_name)
|
||||
for arg in arg_names:
|
||||
if arg.lower().startswith(current_token):
|
||||
yield CompletionType(arg, start_position=-len(current_token))
|
||||
choices = _get_arg_choices(cmd_name, prev_token)
|
||||
if choices:
|
||||
for choice in choices:
|
||||
if choice.lower().startswith(current_token):
|
||||
yield CompletionType(choice, start_position=-len(current_token))
|
||||
return
|
||||
|
||||
if "--help".startswith(current_token):
|
||||
yield CompletionType("--help", start_position=-len(current_token))
|
||||
arg_names = _get_cmdlet_args(cmd_name)
|
||||
for arg in arg_names:
|
||||
if arg.lower().startswith(current_token):
|
||||
yield CompletionType(arg, start_position=-len(current_token))
|
||||
|
||||
if "--help".startswith(current_token):
|
||||
yield CompletionType("--help", start_position=-len(current_token))
|
||||
|
||||
async def get_completions_async(self, document: Document, complete_event): # type: ignore[override]
|
||||
for completion in self.get_completions(document, complete_event):
|
||||
@@ -689,6 +737,7 @@ def _create_cmdlet_cli():
|
||||
|246813579|JKLMNOPQR|
|
||||
|369369369|STUVWXYZ0|
|
||||
|483726159|ABCDEFGHI|
|
||||
|=========+=========|
|
||||
|516273849|JKLMNOPQR|
|
||||
|639639639|STUVWXYZ0|
|
||||
|753186429|ABCDEFGHI|
|
||||
@@ -699,7 +748,7 @@ def _create_cmdlet_cli():
|
||||
print(banner)
|
||||
|
||||
# Configurable prompt
|
||||
prompt_text = ">>>|"
|
||||
prompt_text = "🜂🜄🜁🜃|"
|
||||
|
||||
# Pre-acquire Hydrus session key at startup (like hub-ui does)
|
||||
try:
|
||||
@@ -840,7 +889,6 @@ def _create_cmdlet_cli():
|
||||
return input(prompt)
|
||||
|
||||
while True:
|
||||
print("#-------------------------------------------------------------------------#")
|
||||
try:
|
||||
user_input = get_input(prompt_text).strip()
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
@@ -971,6 +1019,19 @@ def _execute_pipeline(tokens: list):
|
||||
if not stages:
|
||||
print("Invalid pipeline syntax\n")
|
||||
return
|
||||
|
||||
# If a previous stage paused for selection, attach its remaining stages when the user runs only @N
|
||||
pending_tail = ctx.get_pending_pipeline_tail() if hasattr(ctx, 'get_pending_pipeline_tail') else []
|
||||
pending_source = ctx.get_pending_pipeline_source() if hasattr(ctx, 'get_pending_pipeline_source') else None
|
||||
current_source = ctx.get_current_stage_table_source_command() if hasattr(ctx, 'get_current_stage_table_source_command') else None
|
||||
selection_only = len(stages) == 1 and stages[0] and stages[0][0].startswith('@')
|
||||
if pending_tail and selection_only:
|
||||
if pending_source and current_source and current_source == pending_source:
|
||||
stages.extend(pending_tail)
|
||||
if hasattr(ctx, 'clear_pending_pipeline_tail'):
|
||||
ctx.clear_pending_pipeline_tail()
|
||||
elif hasattr(ctx, 'clear_pending_pipeline_tail'):
|
||||
ctx.clear_pending_pipeline_tail()
|
||||
|
||||
# Load config relative to CLI root
|
||||
config = _load_cli_config()
|
||||
@@ -1044,7 +1105,9 @@ def _execute_pipeline(tokens: list):
|
||||
command_expanded = False
|
||||
selected_row_args = []
|
||||
|
||||
if source_cmd:
|
||||
skip_pipe_expansion = source_cmd == '.pipe' and len(stages) > 0
|
||||
|
||||
if source_cmd and not skip_pipe_expansion:
|
||||
# Try to find row args for the selected indices
|
||||
for idx in first_stage_selection_indices:
|
||||
row_args = ctx.get_current_stage_table_row_selection_args(idx)
|
||||
@@ -1151,6 +1214,9 @@ def _execute_pipeline(tokens: list):
|
||||
|
||||
if source_cmd == '.pipe' or source_cmd == '.adjective':
|
||||
should_expand_to_command = True
|
||||
if source_cmd == '.pipe' and (stage_index + 1 < len(stages) or stage_args):
|
||||
# When piping playlist rows to another cmdlet, prefer item-based selection
|
||||
should_expand_to_command = False
|
||||
elif source_cmd == 'search-file' and source_args and 'youtube' in source_args:
|
||||
# Special case for youtube search results: @N expands to .pipe
|
||||
if stage_index + 1 >= len(stages):
|
||||
@@ -1170,6 +1236,10 @@ def _execute_pipeline(tokens: list):
|
||||
# Single format object
|
||||
if source_cmd:
|
||||
should_expand_to_command = True
|
||||
|
||||
# If we have a source command but no piped data (paused for selection), expand to command
|
||||
if not should_expand_to_command and source_cmd and selection is not None and piped_result is None:
|
||||
should_expand_to_command = True
|
||||
|
||||
# If expanding to command, replace this stage and re-execute
|
||||
if should_expand_to_command and selection is not None:
|
||||
@@ -1360,6 +1430,27 @@ def _execute_pipeline(tokens: list):
|
||||
# Intermediate stage - thread to next stage
|
||||
piped_result = pipeline_ctx.emits
|
||||
ctx.set_last_result_table(None, pipeline_ctx.emits)
|
||||
else:
|
||||
# No output from this stage. If it presented a selectable table (e.g., format list), pause
|
||||
# and stash the remaining pipeline so @N can resume with the selection applied.
|
||||
if not is_last_stage:
|
||||
stage_table_source = ctx.get_current_stage_table_source_command()
|
||||
row_has_selection = ctx.get_current_stage_table_row_selection_args(0) is not None
|
||||
if stage_table_source and row_has_selection:
|
||||
pending_tail = stages[stage_index + 1:]
|
||||
if pending_tail and pending_tail[0] and pending_tail[0][0].startswith('@'):
|
||||
pending_tail = pending_tail[1:]
|
||||
if hasattr(ctx, 'set_pending_pipeline_tail') and pending_tail:
|
||||
ctx.set_pending_pipeline_tail(pending_tail, stage_table_source)
|
||||
elif hasattr(ctx, 'clear_pending_pipeline_tail'):
|
||||
ctx.clear_pending_pipeline_tail()
|
||||
if pipeline_session and worker_manager:
|
||||
try:
|
||||
worker_manager.log_step(pipeline_session.worker_id, "Pipeline paused for @N selection")
|
||||
except Exception:
|
||||
pass
|
||||
print("Pipeline paused: select a format with @N to continue remaining stages")
|
||||
return
|
||||
|
||||
if ret_code != 0:
|
||||
stage_status = "failed"
|
||||
|
||||
Reference in New Issue
Block a user