cc.shellback-kit 0.3.0__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 (43) hide show
  1. cc_shellback_kit-0.3.0/PKG-INFO +52 -0
  2. cc_shellback_kit-0.3.0/README.md +40 -0
  3. cc_shellback_kit-0.3.0/pyproject.toml +13 -0
  4. cc_shellback_kit-0.3.0/setup.cfg +4 -0
  5. cc_shellback_kit-0.3.0/src/cc.shellback_kit.egg-info/PKG-INFO +52 -0
  6. cc_shellback_kit-0.3.0/src/cc.shellback_kit.egg-info/SOURCES.txt +41 -0
  7. cc_shellback_kit-0.3.0/src/cc.shellback_kit.egg-info/dependency_links.txt +1 -0
  8. cc_shellback_kit-0.3.0/src/cc.shellback_kit.egg-info/requires.txt +5 -0
  9. cc_shellback_kit-0.3.0/src/cc.shellback_kit.egg-info/top_level.txt +1 -0
  10. cc_shellback_kit-0.3.0/src/cc_shellback_kit/__init__.py +34 -0
  11. cc_shellback_kit-0.3.0/src/cc_shellback_kit/capsule/Bash.py +8 -0
  12. cc_shellback_kit-0.3.0/src/cc_shellback_kit/capsule/ConsoleLogObserver.py +16 -0
  13. cc_shellback_kit-0.3.0/src/cc_shellback_kit/capsule/FileLogObserver.py +49 -0
  14. cc_shellback_kit-0.3.0/src/cc_shellback_kit/capsule/JSONFileObserver.py +63 -0
  15. cc_shellback_kit-0.3.0/src/cc_shellback_kit/capsule/MultiObserver.py +39 -0
  16. cc_shellback_kit-0.3.0/src/cc_shellback_kit/capsule/SilentObserver.py +7 -0
  17. cc_shellback_kit-0.3.0/src/cc_shellback_kit/capsule/__init__.py +16 -0
  18. cc_shellback_kit-0.3.0/src/cc_shellback_kit/core/ArgumentBuilder.py +30 -0
  19. cc_shellback_kit-0.3.0/src/cc_shellback_kit/core/Command.py +22 -0
  20. cc_shellback_kit-0.3.0/src/cc_shellback_kit/core/CommandResult.py +26 -0
  21. cc_shellback_kit-0.3.0/src/cc_shellback_kit/core/SessionContext.py +12 -0
  22. cc_shellback_kit-0.3.0/src/cc_shellback_kit/core/Shell.py +174 -0
  23. cc_shellback_kit-0.3.0/src/cc_shellback_kit/core/ShellObserver.py +56 -0
  24. cc_shellback_kit-0.3.0/src/cc_shellback_kit/core/__init__.py +16 -0
  25. cc_shellback_kit-0.3.0/tests/test_ArgumentBuilder.py +70 -0
  26. cc_shellback_kit-0.3.0/tests/test_Bash.py +72 -0
  27. cc_shellback_kit-0.3.0/tests/test_Command.py +56 -0
  28. cc_shellback_kit-0.3.0/tests/test_CommandResult.py +70 -0
  29. cc_shellback_kit-0.3.0/tests/test_ConsoleLogObserver.py +60 -0
  30. cc_shellback_kit-0.3.0/tests/test_FileLogObserver.py +91 -0
  31. cc_shellback_kit-0.3.0/tests/test_JSONFileObserver.py +89 -0
  32. cc_shellback_kit-0.3.0/tests/test_MultiObserver.py +73 -0
  33. cc_shellback_kit-0.3.0/tests/test_SessionContext.py +57 -0
  34. cc_shellback_kit-0.3.0/tests/test_ShellObserver.py +24 -0
  35. cc_shellback_kit-0.3.0/tests/test_Shell_handle_cd.py +60 -0
  36. cc_shellback_kit-0.3.0/tests/test_Shell_handle_export.py +72 -0
  37. cc_shellback_kit-0.3.0/tests/test_Shell_notify_error.py +36 -0
  38. cc_shellback_kit-0.3.0/tests/test_Shell_parse_env_vars.py +47 -0
  39. cc_shellback_kit-0.3.0/tests/test_Shell_resolve_path.py +52 -0
  40. cc_shellback_kit-0.3.0/tests/test_Shell_run.py +79 -0
  41. cc_shellback_kit-0.3.0/tests/test_Shell_run_external.py +72 -0
  42. cc_shellback_kit-0.3.0/tests/test_Shell_validate_executable.py +45 -0
  43. cc_shellback_kit-0.3.0/tests/test_SilentObserver.py +56 -0
@@ -0,0 +1,52 @@
1
+ Metadata-Version: 2.4
2
+ Name: cc.shellback-kit
3
+ Version: 0.3.0
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.14
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: capsulecore-logger==0.3.0
8
+ Requires-Dist: gitchangelog>=3.0.4
9
+ Requires-Dist: pytest>=9.0.2
10
+ Requires-Dist: pytest-timeout>=2.4.0
11
+ Requires-Dist: ruff>=0.15.6
12
+
13
+ # CapsuleCore shellback
14
+
15
+ Shellback is a robust, architecturally-agnostic Python library designed to bridge terminal environments (Bash, CMD, PowerShell) with Python scripts. It provides a clean, decoupled abstraction layer to execute system commands while maintaining persistent session state and cross-platform compatibility.
16
+
17
+ Built with Hexagonal Architecture (Ports and Adapters) principles, Shellback ensures that your domain logic remains independent of the specific shell or operating system being used.
18
+
19
+ ## Usage
20
+
21
+ ```python
22
+ from cc_shellback_kit.capsule import Bash
23
+ from cc_shellback_kit.core import Command, SessionContext
24
+ from cc_shellback_kit.observers import ConsoleLogObserver
25
+
26
+ # 1. Configuramos el observador para ver la actividad en consola
27
+ observer = ConsoleLogObserver()
28
+
29
+ # 2. Iniciamos la Shell usando el manejador de contexto (with)
30
+ with Bash(observer=observer) as shell:
31
+
32
+ # --- EJECUCIÓN DE COMANDOS EXTERNOS ---
33
+ # Creamos un comando simple: 'ls -la'
34
+ cmd_list = Command("ls").add_args("-la")
35
+ result = shell.run(cmd_list)
36
+
37
+ if result.is_success():
38
+ print(f"Archivos encontrados:\n{result.standard_output}")
39
+
40
+ # --- MANEJO DE ESTADO (VIRTUAL BUILT-INS) ---
41
+ # Cambiamos de directorio (esto afecta al SessionContext, no solo al proceso)
42
+ shell.run(Command("cd").add_args("/tmp"))
43
+
44
+ # Verificamos el cambio ejecutando un 'pwd'
45
+ shell.run(Command("pwd"))
46
+
47
+ # --- VARIABLES DE ENTORNO ---
48
+ # Exportamos una variable que persistirá durante este bloque 'with'
49
+ shell.run(Command("export").add_args("APP_STAGE=development", "DEBUG=true"))
50
+ ```
51
+
52
+
@@ -0,0 +1,40 @@
1
+ # CapsuleCore shellback
2
+
3
+ Shellback is a robust, architecturally-agnostic Python library designed to bridge terminal environments (Bash, CMD, PowerShell) with Python scripts. It provides a clean, decoupled abstraction layer to execute system commands while maintaining persistent session state and cross-platform compatibility.
4
+
5
+ Built with Hexagonal Architecture (Ports and Adapters) principles, Shellback ensures that your domain logic remains independent of the specific shell or operating system being used.
6
+
7
+ ## Usage
8
+
9
+ ```python
10
+ from cc_shellback_kit.capsule import Bash
11
+ from cc_shellback_kit.core import Command, SessionContext
12
+ from cc_shellback_kit.observers import ConsoleLogObserver
13
+
14
+ # 1. Configuramos el observador para ver la actividad en consola
15
+ observer = ConsoleLogObserver()
16
+
17
+ # 2. Iniciamos la Shell usando el manejador de contexto (with)
18
+ with Bash(observer=observer) as shell:
19
+
20
+ # --- EJECUCIÓN DE COMANDOS EXTERNOS ---
21
+ # Creamos un comando simple: 'ls -la'
22
+ cmd_list = Command("ls").add_args("-la")
23
+ result = shell.run(cmd_list)
24
+
25
+ if result.is_success():
26
+ print(f"Archivos encontrados:\n{result.standard_output}")
27
+
28
+ # --- MANEJO DE ESTADO (VIRTUAL BUILT-INS) ---
29
+ # Cambiamos de directorio (esto afecta al SessionContext, no solo al proceso)
30
+ shell.run(Command("cd").add_args("/tmp"))
31
+
32
+ # Verificamos el cambio ejecutando un 'pwd'
33
+ shell.run(Command("pwd"))
34
+
35
+ # --- VARIABLES DE ENTORNO ---
36
+ # Exportamos una variable que persistirá durante este bloque 'with'
37
+ shell.run(Command("export").add_args("APP_STAGE=development", "DEBUG=true"))
38
+ ```
39
+
40
+
@@ -0,0 +1,13 @@
1
+ [project]
2
+ name = "cc.shellback-kit"
3
+ version = "0.3.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.14"
7
+ dependencies = [
8
+ "capsulecore-logger==0.3.0",
9
+ "gitchangelog>=3.0.4",
10
+ "pytest>=9.0.2",
11
+ "pytest-timeout>=2.4.0",
12
+ "ruff>=0.15.6",
13
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,52 @@
1
+ Metadata-Version: 2.4
2
+ Name: cc.shellback-kit
3
+ Version: 0.3.0
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.14
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: capsulecore-logger==0.3.0
8
+ Requires-Dist: gitchangelog>=3.0.4
9
+ Requires-Dist: pytest>=9.0.2
10
+ Requires-Dist: pytest-timeout>=2.4.0
11
+ Requires-Dist: ruff>=0.15.6
12
+
13
+ # CapsuleCore shellback
14
+
15
+ Shellback is a robust, architecturally-agnostic Python library designed to bridge terminal environments (Bash, CMD, PowerShell) with Python scripts. It provides a clean, decoupled abstraction layer to execute system commands while maintaining persistent session state and cross-platform compatibility.
16
+
17
+ Built with Hexagonal Architecture (Ports and Adapters) principles, Shellback ensures that your domain logic remains independent of the specific shell or operating system being used.
18
+
19
+ ## Usage
20
+
21
+ ```python
22
+ from cc_shellback_kit.capsule import Bash
23
+ from cc_shellback_kit.core import Command, SessionContext
24
+ from cc_shellback_kit.observers import ConsoleLogObserver
25
+
26
+ # 1. Configuramos el observador para ver la actividad en consola
27
+ observer = ConsoleLogObserver()
28
+
29
+ # 2. Iniciamos la Shell usando el manejador de contexto (with)
30
+ with Bash(observer=observer) as shell:
31
+
32
+ # --- EJECUCIÓN DE COMANDOS EXTERNOS ---
33
+ # Creamos un comando simple: 'ls -la'
34
+ cmd_list = Command("ls").add_args("-la")
35
+ result = shell.run(cmd_list)
36
+
37
+ if result.is_success():
38
+ print(f"Archivos encontrados:\n{result.standard_output}")
39
+
40
+ # --- MANEJO DE ESTADO (VIRTUAL BUILT-INS) ---
41
+ # Cambiamos de directorio (esto afecta al SessionContext, no solo al proceso)
42
+ shell.run(Command("cd").add_args("/tmp"))
43
+
44
+ # Verificamos el cambio ejecutando un 'pwd'
45
+ shell.run(Command("pwd"))
46
+
47
+ # --- VARIABLES DE ENTORNO ---
48
+ # Exportamos una variable que persistirá durante este bloque 'with'
49
+ shell.run(Command("export").add_args("APP_STAGE=development", "DEBUG=true"))
50
+ ```
51
+
52
+
@@ -0,0 +1,41 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/cc.shellback_kit.egg-info/PKG-INFO
4
+ src/cc.shellback_kit.egg-info/SOURCES.txt
5
+ src/cc.shellback_kit.egg-info/dependency_links.txt
6
+ src/cc.shellback_kit.egg-info/requires.txt
7
+ src/cc.shellback_kit.egg-info/top_level.txt
8
+ src/cc_shellback_kit/__init__.py
9
+ src/cc_shellback_kit/capsule/Bash.py
10
+ src/cc_shellback_kit/capsule/ConsoleLogObserver.py
11
+ src/cc_shellback_kit/capsule/FileLogObserver.py
12
+ src/cc_shellback_kit/capsule/JSONFileObserver.py
13
+ src/cc_shellback_kit/capsule/MultiObserver.py
14
+ src/cc_shellback_kit/capsule/SilentObserver.py
15
+ src/cc_shellback_kit/capsule/__init__.py
16
+ src/cc_shellback_kit/core/ArgumentBuilder.py
17
+ src/cc_shellback_kit/core/Command.py
18
+ src/cc_shellback_kit/core/CommandResult.py
19
+ src/cc_shellback_kit/core/SessionContext.py
20
+ src/cc_shellback_kit/core/Shell.py
21
+ src/cc_shellback_kit/core/ShellObserver.py
22
+ src/cc_shellback_kit/core/__init__.py
23
+ tests/test_ArgumentBuilder.py
24
+ tests/test_Bash.py
25
+ tests/test_Command.py
26
+ tests/test_CommandResult.py
27
+ tests/test_ConsoleLogObserver.py
28
+ tests/test_FileLogObserver.py
29
+ tests/test_JSONFileObserver.py
30
+ tests/test_MultiObserver.py
31
+ tests/test_SessionContext.py
32
+ tests/test_ShellObserver.py
33
+ tests/test_Shell_handle_cd.py
34
+ tests/test_Shell_handle_export.py
35
+ tests/test_Shell_notify_error.py
36
+ tests/test_Shell_parse_env_vars.py
37
+ tests/test_Shell_resolve_path.py
38
+ tests/test_Shell_run.py
39
+ tests/test_Shell_run_external.py
40
+ tests/test_Shell_validate_executable.py
41
+ tests/test_SilentObserver.py
@@ -0,0 +1,5 @@
1
+ capsulecore-logger==0.3.0
2
+ gitchangelog>=3.0.4
3
+ pytest>=9.0.2
4
+ pytest-timeout>=2.4.0
5
+ ruff>=0.15.6
@@ -0,0 +1 @@
1
+ cc_shellback_kit
@@ -0,0 +1,34 @@
1
+ from .capsule import (
2
+ Bash,
3
+ ConsoleLogObserver,
4
+ FileLogObserver,
5
+ SilentObserver,
6
+ JSONFileObserver,
7
+ MultiObserver,
8
+ )
9
+ from .core import (
10
+ Shell,
11
+ ArgumentBuilder,
12
+ Command,
13
+ SessionContext,
14
+ CommandResult,
15
+ CommandNotFoundError,
16
+ ShellObserver,
17
+ )
18
+
19
+
20
+ __all__ = [
21
+ "ArgumentBuilder",
22
+ "Command",
23
+ "Bash",
24
+ "Shell",
25
+ "SessionContext",
26
+ "CommandResult",
27
+ "JSONFileObserver",
28
+ "CommandNotFoundError",
29
+ "ShellObserver",
30
+ "ConsoleLogObserver",
31
+ "FileLogObserver",
32
+ "SilentObserver",
33
+ "MultiObserver",
34
+ ]
@@ -0,0 +1,8 @@
1
+ from ..core import Shell
2
+
3
+
4
+ class Bash(Shell):
5
+ def _format_command(self, executable: str, args: list[str]) -> list[str]:
6
+ # Ya no concatenamos [executable] + args aquí.
7
+ # Solo devolvemos los args tal cual, la Shell base hará el resto.
8
+ return args
@@ -0,0 +1,16 @@
1
+ from ..core import CommandResult, ShellObserver
2
+
3
+
4
+ class ConsoleLogObserver(ShellObserver):
5
+ """Muestra en consola lo que está pasando en tiempo real."""
6
+
7
+ def on_command_start(self, executable, final_args):
8
+ print(f"🛠️ Ejecutando: {' '.join(final_args)}")
9
+
10
+ def on_command_result(self, result: CommandResult):
11
+ if result.is_success():
12
+ print(f"✅ OK ({result.execution_time:.3f}s)")
13
+ else:
14
+ print(f"❌ FALLO (Código: {result.return_code})")
15
+ if result.standard_error:
16
+ print(f" Error: {result.standard_error.strip()}")
@@ -0,0 +1,49 @@
1
+ import logging
2
+ from pathlib import Path
3
+ from ..core import ShellObserver, CommandResult
4
+
5
+
6
+ class FileLogObserver(ShellObserver):
7
+ """Guarda toda la actividad de la Shell en un archivo físico."""
8
+
9
+ def __init__(self, log_path: str = "shell_activity.log"):
10
+ self.log_path = Path(log_path)
11
+
12
+ self.logger = logging.getLogger("ShellFileLogger")
13
+ self.logger.setLevel(logging.INFO)
14
+ self.logger.propagate = False # Evita que los logs salgan por consola en los tests
15
+
16
+ if self.logger.handlers:
17
+ for h in self.logger.handlers[:]:
18
+ h.close()
19
+ self.logger.removeHandler(h)
20
+
21
+ # Ahora creamos el nuevo handler con la ruta correcta
22
+ handler = logging.FileHandler(self.log_path, encoding="utf-8")
23
+ formatter = logging.Formatter("%(asctime)s | %(levelname)s | %(message)s")
24
+ handler.setFormatter(formatter)
25
+ self.logger.addHandler(handler)
26
+
27
+ def on_session_start(self, shell_name: str):
28
+ self.logger.info(f"=== INICIO DE SESIÓN: {shell_name} ===")
29
+
30
+ def on_session_end(self, shell_name: str, error: Exception = None):
31
+ if error:
32
+ self.logger.error(f"=== FIN DE SESIÓN CON ERROR: {error} ===")
33
+ else:
34
+ self.logger.info("=== FIN DE SESIÓN EXITOSO ===")
35
+
36
+ def on_command_start(self, executable: str, final_args: list[str]):
37
+ cmd_str = " ".join(final_args)
38
+ self.logger.info(f"EJECUTANDO: {cmd_str}")
39
+
40
+ def on_command_result(self, result: CommandResult):
41
+ status = "SUCCESS" if result.is_success() else f"FAILED({result.return_code})"
42
+ self.logger.info(
43
+ f"RESULTADO: {status} | "
44
+ f"DURACIÓN: {result.execution_time:.4f}s | "
45
+ f"STDOUT: {result.standard_output.strip()[:100]}..."
46
+ )
47
+
48
+ def on_error(self, message: str, error: Exception = None):
49
+ self.logger.error(f"ERROR DEL SISTEMA: {message} | Detalle: {error}")
@@ -0,0 +1,63 @@
1
+ import json
2
+ import time
3
+ from pathlib import Path
4
+ from typing import Any, List, Optional, Dict
5
+ from ..core import ShellObserver, CommandResult
6
+
7
+ class JSONFileObserver(ShellObserver):
8
+ """
9
+ Registra toda la actividad de la Shell en un archivo JSON.
10
+ Ideal para auditoría técnica y análisis de datos posterior.
11
+ """
12
+
13
+ def __init__(self, log_path: str = "shell_audit.json"):
14
+ self.log_path = Path(log_path)
15
+ # Inicializamos el archivo si no existe
16
+ if not self.log_path.exists():
17
+ self._write_logs([])
18
+
19
+ def _read_logs(self) -> List[Dict[str, Any]]:
20
+ try:
21
+ with open(self.log_path, "r", encoding="utf-8") as f:
22
+ return json.load(f)
23
+ except (json.JSONDecodeError, FileNotFoundError):
24
+ return []
25
+
26
+ def _write_logs(self, logs: List[Dict[str, Any]]):
27
+ with open(self.log_path, "w", encoding="utf-8") as f:
28
+ json.dump(logs, f, indent=4, ensure_ascii=False)
29
+
30
+ def _append_entry(self, entry: Dict[str, Any]):
31
+ logs = self._read_logs()
32
+ logs.append(entry)
33
+ self._write_logs(logs)
34
+
35
+ def on_command_result(self, result: CommandResult):
36
+ entry = {
37
+ "timestamp": time.time(),
38
+ "event": "command_executed",
39
+ "command": result.command_sent,
40
+ "success": result.is_success(),
41
+ "exit_code": result.return_code,
42
+ "duration": round(result.execution_time, 4),
43
+ "stdout_len": len(result.standard_output),
44
+ "stderr": result.standard_error.strip() if result.standard_error else None
45
+ }
46
+ self._append_entry(entry)
47
+
48
+ def on_context_change(self, key: str, value: Any):
49
+ entry = {
50
+ "timestamp": time.time(),
51
+ "event": "context_mutation",
52
+ "change": {key: str(value)}
53
+ }
54
+ self._append_entry(entry)
55
+
56
+ def on_error(self, message: str, error: Optional[Exception] = None):
57
+ entry = {
58
+ "timestamp": time.time(),
59
+ "event": "internal_error",
60
+ "message": message,
61
+ "exception": str(error) if error else None
62
+ }
63
+ self._append_entry(entry)
@@ -0,0 +1,39 @@
1
+ from typing import List, Any
2
+ from ..core import ShellObserver, CommandResult
3
+
4
+
5
+ class MultiObserver(ShellObserver):
6
+ """
7
+ Patrón Composite: Permite registrar múltiples observadores y
8
+ notificar a todos ellos de cada evento de la Shell.
9
+ """
10
+
11
+ def __init__(self, observers: List[ShellObserver] = None):
12
+ self.observers = observers or []
13
+
14
+ def add_observer(self, observer: ShellObserver):
15
+ self.observers.append(observer)
16
+
17
+ def on_session_start(self, shell_name: str):
18
+ for o in self.observers:
19
+ o.on_session_start(shell_name)
20
+
21
+ def on_session_end(self, shell_name: str, error: Exception = None):
22
+ for o in self.observers:
23
+ o.on_session_end(shell_name, error)
24
+
25
+ def on_context_change(self, key: str, value: Any):
26
+ for o in self.observers:
27
+ o.on_context_change(key, value)
28
+
29
+ def on_command_start(self, executable: str, final_args: List[str]):
30
+ for o in self.observers:
31
+ o.on_command_start(executable, final_args)
32
+
33
+ def on_command_result(self, result: CommandResult):
34
+ for o in self.observers:
35
+ o.on_command_result(result)
36
+
37
+ def on_error(self, message: str, error: Exception = None):
38
+ for o in self.observers:
39
+ o.on_error(message, error)
@@ -0,0 +1,7 @@
1
+ from ..core import ShellObserver
2
+
3
+
4
+ class SilentObserver(ShellObserver):
5
+ """No hace nada. Ideal para scripts de producción donde no quieres ruido."""
6
+
7
+ pass
@@ -0,0 +1,16 @@
1
+ from .Bash import Bash
2
+ from .ConsoleLogObserver import ConsoleLogObserver
3
+ from .FileLogObserver import FileLogObserver
4
+ from .MultiObserver import MultiObserver
5
+ from .JSONFileObserver import JSONFileObserver
6
+ from .SilentObserver import SilentObserver
7
+
8
+
9
+ __all__ = [
10
+ "Bash",
11
+ "ConsoleLogObserver",
12
+ "FileLogObserver",
13
+ "JSONFileObserver",
14
+ "MultiObserver",
15
+ "SilentObserver",
16
+ ]
@@ -0,0 +1,30 @@
1
+ from typing import Any, List
2
+
3
+ class ArgumentBuilder:
4
+ """Garantiza la consistencia de los argumentos para el Sistema Operativo."""
5
+
6
+ def __init__(self, style: str = "unix"):
7
+ self._args: List[str] = []
8
+ self.style = style
9
+
10
+ def add_arg(self, value: Any) -> "ArgumentBuilder":
11
+ """Aplana listas/tuplas y convierte todo a string, ignorando vacíos."""
12
+ if isinstance(value, (list, tuple)):
13
+ for item in value:
14
+ self.add_arg(item)
15
+ elif value is not None:
16
+ val_str = str(value).strip()
17
+ if val_str:
18
+ self._args.append(val_str)
19
+ return self
20
+
21
+ def add_flag(self, name: str, value: Any = None) -> "ArgumentBuilder":
22
+ prefix = "--" if self.style == "unix" else "/"
23
+ clean_name = name.strip().replace(" ", "_")
24
+ self.add_arg(f"{prefix}{clean_name}")
25
+ if value is not None:
26
+ self.add_arg(value)
27
+ return self
28
+
29
+ def build(self) -> List[str]:
30
+ return self._args
@@ -0,0 +1,22 @@
1
+ from .ArgumentBuilder import ArgumentBuilder
2
+
3
+ class Command:
4
+ """Representa un comando ejecutable con sus argumentos."""
5
+
6
+ def __init__(self, executable: str):
7
+ self.executable = executable
8
+ self.builder = ArgumentBuilder()
9
+
10
+ def add_args(self, *args) -> "Command":
11
+ """
12
+ Añade argumentos posicionales.
13
+ Soporta elementos sueltos o listas gracias al nuevo builder.
14
+ """
15
+ for arg in args:
16
+ self.builder.add_arg(arg)
17
+ return self
18
+
19
+ @property
20
+ def args(self) -> list[str]:
21
+ """Obtiene la lista de argumentos construida y validada."""
22
+ return self.builder.build()
@@ -0,0 +1,26 @@
1
+ from dataclasses import dataclass, field
2
+ import json
3
+ from typing import Any
4
+
5
+
6
+ @dataclass
7
+ class CommandResult:
8
+ """Encapsulates the output of an executed command."""
9
+
10
+ standard_output: str = ""
11
+ standard_error: str = ""
12
+ return_code: int = 0
13
+ execution_time: float = 0.0
14
+ command_sent: list[str] = field(default_factory=list)
15
+
16
+ def is_success(self) -> bool:
17
+ """Returns True if the command executed successfully."""
18
+ return self.return_code == 0
19
+
20
+ def json(self) -> Any:
21
+ """Parses the standard output as JSON."""
22
+ return json.loads(self.standard_output)
23
+
24
+ def __or__(self, next_command: Any) -> str:
25
+ """Enables pipe syntax."""
26
+ return self.standard_output
@@ -0,0 +1,12 @@
1
+ from dataclasses import dataclass, field
2
+ from pathlib import Path
3
+ from typing import Dict
4
+
5
+
6
+ @dataclass(frozen=True)
7
+ class SessionContext:
8
+ """Maintains persistent state across Shell executions."""
9
+
10
+ cwd: Path = field(default_factory=Path.cwd)
11
+ env: Dict[str, str] = field(default_factory=dict)
12
+ encoding: str = "utf-8"