mirror of
https://github.com/elder-plinius/P4RS3LT0NGV3.git
synced 2026-06-05 22:46:39 +02:00
add slash command shortcuts
This commit is contained in:
@@ -325,6 +325,9 @@ uv run p4rs3lt0ngv3-cli encode --transform base64 --text "Hello World"
|
||||
uv run p4rs3lt0ngv3-cli decode --transform base64 --text "SGVsbG8gV29ybGQ="
|
||||
uv run p4rs3lt0ngv3-cli auto-decode --text "SGVsbG8="
|
||||
uv run p4rs3lt0ngv3-cli agent "encode 'Attack at dawn' as caesar shift 5"
|
||||
uv run p4rs3lt0ngv3-cli /base64 Hello
|
||||
uv run p4rs3lt0ngv3-cli /base64 --decode SGVsbG8=
|
||||
uv run p4rs3lt0ngv3-cli /caesar --shift 5 "Attack at dawn"
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
+197
-2
@@ -48,6 +48,109 @@ def emit(data: Any, as_json: bool) -> None:
|
||||
print(json.dumps(data, indent=2, ensure_ascii=False))
|
||||
|
||||
|
||||
def coerce_option_value(raw: str) -> Any:
|
||||
lowered = raw.lower()
|
||||
if lowered in {"true", "false"}:
|
||||
return lowered == "true"
|
||||
try:
|
||||
return int(raw)
|
||||
except ValueError:
|
||||
try:
|
||||
return float(raw)
|
||||
except ValueError:
|
||||
return raw
|
||||
|
||||
|
||||
def parse_slash_command(argv: list[str]) -> tuple[str, dict[str, Any]] | None:
|
||||
if not argv:
|
||||
return None
|
||||
|
||||
first = argv[0]
|
||||
if not first.startswith("/") or first == "/":
|
||||
return None
|
||||
|
||||
name = first[1:]
|
||||
if not name:
|
||||
return None
|
||||
|
||||
if name == "inspect":
|
||||
if len(argv) < 2:
|
||||
raise ValueError("Usage: /inspect <transform> [--json]")
|
||||
return ("inspect", {"transform": argv[1], "json": "--json" in argv[2:]})
|
||||
|
||||
if name == "list":
|
||||
category = None
|
||||
json_mode = False
|
||||
tokens = argv[1:]
|
||||
index = 0
|
||||
while index < len(tokens):
|
||||
token = tokens[index]
|
||||
if token == "--json":
|
||||
json_mode = True
|
||||
elif token == "--category":
|
||||
if index + 1 >= len(tokens):
|
||||
raise ValueError("Missing value for --category")
|
||||
category = tokens[index + 1]
|
||||
index += 1
|
||||
else:
|
||||
raise ValueError(f"Unsupported option for /list: {token}")
|
||||
index += 1
|
||||
return ("list", {"category": category, "json": json_mode})
|
||||
|
||||
if name == "decode":
|
||||
tokens = argv[1:]
|
||||
json_mode = False
|
||||
if "--json" in tokens:
|
||||
json_mode = True
|
||||
tokens = [token for token in tokens if token != "--json"]
|
||||
return ("auto-decode", {"text": " ".join(tokens), "json": json_mode})
|
||||
|
||||
action = "encode"
|
||||
json_mode = False
|
||||
options: dict[str, Any] = {}
|
||||
text_tokens: list[str] = []
|
||||
tokens = argv[1:]
|
||||
index = 0
|
||||
|
||||
while index < len(tokens):
|
||||
token = tokens[index]
|
||||
if token == "--decode":
|
||||
action = "decode"
|
||||
elif token == "--preview":
|
||||
action = "preview"
|
||||
elif token == "--json":
|
||||
json_mode = True
|
||||
elif token == "--option":
|
||||
if index + 1 >= len(tokens):
|
||||
raise ValueError("Missing value for --option")
|
||||
options.update(parse_option_pairs([tokens[index + 1]]))
|
||||
index += 1
|
||||
elif token.startswith("--"):
|
||||
flag = token[2:]
|
||||
if "=" in flag:
|
||||
key, value = flag.split("=", 1)
|
||||
options[key] = coerce_option_value(value)
|
||||
elif index + 1 < len(tokens) and not tokens[index + 1].startswith("--"):
|
||||
options[flag] = coerce_option_value(tokens[index + 1])
|
||||
index += 1
|
||||
else:
|
||||
options[flag] = True
|
||||
else:
|
||||
text_tokens.append(token)
|
||||
index += 1
|
||||
|
||||
return (
|
||||
"transform-shortcut",
|
||||
{
|
||||
"transform": name,
|
||||
"action": action,
|
||||
"text": " ".join(text_tokens),
|
||||
"json": json_mode,
|
||||
"options": options,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(prog="p4rs3lt0ngv3-cli", description="Agent-based CLI for P4RS3LT0NGV3")
|
||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||
@@ -80,8 +183,101 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
raw_argv = list(sys.argv[1:] if argv is None else argv)
|
||||
|
||||
try:
|
||||
slash_command = parse_slash_command(raw_argv)
|
||||
except ValueError as exc:
|
||||
print(str(exc), file=sys.stderr)
|
||||
return 1
|
||||
|
||||
if slash_command is not None:
|
||||
command, payload = slash_command
|
||||
try:
|
||||
ensure_node_available()
|
||||
if command == "list":
|
||||
transforms = list_transforms()
|
||||
if payload["category"]:
|
||||
transforms = [transform for transform in transforms if transform.category == payload["category"]]
|
||||
if payload["json"]:
|
||||
emit(
|
||||
[
|
||||
{
|
||||
"key": transform.key,
|
||||
"name": transform.name,
|
||||
"category": transform.category,
|
||||
"can_decode": transform.can_decode,
|
||||
}
|
||||
for transform in transforms
|
||||
],
|
||||
True,
|
||||
)
|
||||
else:
|
||||
for transform in transforms:
|
||||
decode_flag = "decode" if transform.can_decode else "encode-only"
|
||||
print(f"{transform.key:24} {transform.category:12} {decode_flag:11} {transform.name}")
|
||||
return 0
|
||||
|
||||
if command == "inspect":
|
||||
transform = find_transform(payload["transform"])
|
||||
if not transform:
|
||||
raise BridgeError(f"Unknown transform: {payload['transform']}")
|
||||
meta = inspect_transform(transform.key)
|
||||
data = {
|
||||
"key": meta.key,
|
||||
"name": meta.name,
|
||||
"category": meta.category,
|
||||
"priority": meta.priority,
|
||||
"can_decode": meta.can_decode,
|
||||
"input_kind": meta.input_kind,
|
||||
"options": [
|
||||
{
|
||||
"id": option.id,
|
||||
"label": option.label,
|
||||
"type": option.type,
|
||||
"default": option.default,
|
||||
"min": option.min,
|
||||
"max": option.max,
|
||||
"step": option.step,
|
||||
"options": option.options,
|
||||
}
|
||||
for option in meta.configurable_options
|
||||
],
|
||||
}
|
||||
emit(data, payload["json"])
|
||||
return 0
|
||||
|
||||
if command == "auto-decode":
|
||||
result = auto_decode(payload["text"])
|
||||
emit(result or {}, payload["json"])
|
||||
return 0 if result else 1
|
||||
|
||||
if command == "transform-shortcut":
|
||||
transform = find_transform(payload["transform"])
|
||||
if not transform:
|
||||
raise BridgeError(f"Unknown transform: {payload['transform']}")
|
||||
text = payload["text"] or read_text_argument(None)
|
||||
result = run_transform(payload["action"], transform.key, text, payload["options"])
|
||||
if payload["json"]:
|
||||
emit(
|
||||
{
|
||||
"action": result.action,
|
||||
"transform": result.transform_key,
|
||||
"transform_name": result.transform_name,
|
||||
"options": result.options,
|
||||
"output": result.output,
|
||||
},
|
||||
True,
|
||||
)
|
||||
else:
|
||||
emit(result.output, False)
|
||||
return 0
|
||||
except (BridgeError, ValueError) as exc:
|
||||
print(str(exc), file=sys.stderr)
|
||||
return 1
|
||||
|
||||
parser = build_parser()
|
||||
args = parser.parse_args(argv)
|
||||
args = parser.parse_args(raw_argv)
|
||||
|
||||
try:
|
||||
ensure_node_available()
|
||||
@@ -180,4 +376,3 @@ def main(argv: list[str] | None = None) -> int:
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
||||
|
||||
@@ -88,3 +88,27 @@ def test_agent_can_chain_steps() -> None:
|
||||
payload = parse_json_output(process)
|
||||
assert payload["final_output"] == "Hi"
|
||||
assert len(payload["outputs"]) == 2
|
||||
|
||||
|
||||
def test_slash_command_encodes_text() -> None:
|
||||
process = run_cli("/base64", "Hello")
|
||||
assert process.returncode == 0, process.stderr
|
||||
assert process.stdout.strip() == "SGVsbG8="
|
||||
|
||||
|
||||
def test_slash_command_decodes_text() -> None:
|
||||
process = run_cli("/base64", "--decode", "SGVsbG8=")
|
||||
assert process.returncode == 0, process.stderr
|
||||
assert process.stdout.strip() == "Hello"
|
||||
|
||||
|
||||
def test_slash_command_supports_transform_flags() -> None:
|
||||
process = run_cli("/caesar", "--shift", "5", "Attack", "at", "dawn")
|
||||
assert process.returncode == 0, process.stderr
|
||||
assert process.stdout.strip() == "Fyyfhp fy ifbs"
|
||||
|
||||
|
||||
def test_slash_command_supports_inspect() -> None:
|
||||
process = run_cli("/inspect", "caesar", "--json")
|
||||
payload = parse_json_output(process)
|
||||
assert payload["key"] == "caesar"
|
||||
|
||||
Reference in New Issue
Block a user