ShellObserver Base Interface

Observer / Hook interface for shell lifecycle events โ€” session start/end, context changes, command instrumentation, and error aggregation.

class ShellObserver(ABC) ยท Designed as a toolbox of hooks with empty default implementations. Implement only the events you need. Perfect for logging, telemetry, debugging or auditing shell interactions.

๐Ÿ” Abstract Observer Contract

The ShellObserver acts as a bridge between the shell core and external monitoring tools. It does not impose any logic; instead, it provides optional hooks that child classes can override to capture granular lifecycle events. This pattern keeps business logic decoupled and promotes clean instrumentation.

โœจ Key characteristics:
โœ… Derived from ABC (Abstract Base Class) โ€” explicit base for observers.
โœ… All methods have pass implementations โ€” no forced overrides.
โœ… Each hook receives rich contextual information: shell name, exceptions, command args, result objects.
โœ… Designed to integrate with context managers, virtual builtins, and subprocess executors.

๐Ÿ”„ Session Lifecycle Hooks

Monitor the lifespan of a shell context: from entering the with block until exit (even on exceptions).

๐Ÿ“Œ on_session_start(shell_name: str)

Triggered when entering the Shell context ('with' block). Use this to initialize logging resources, start telemetry timers, or allocate session-specific handles.

Parameters: shell_name โ†’ identifier of the current shell instance (e.g., "bash", "zsh", "custom-shell").

๐Ÿ on_session_end(shell_name: str, error: Exception = None)

Triggered when exiting the Shell context. Allows you to log whether the session ended cleanly or due to an unhandled exception. Perfect for sending session metrics or cleaning up resources.

Parameters: shell_name (str) , error โ†’ exception if any occurred, otherwise None.

โš™๏ธ Context & State Mutation

React to changes in the shell's internal state, such as working directory (CWD), environment variables, or virtual built-in commands.

๐Ÿ”„ on_context_change(key: str, value: Any)

Triggered whenever a state command (e.g., cd, export, or other virtual builtins) mutates the session context. Ideal for tracking directory traversal, environment modifications, or custom state flags in real time.

key: context property name (e.g., "cwd", "PATH", "VIRTUAL_ENV")
value: new value assigned (any type).
๐Ÿ’ก Use case: maintain a breadcrumb of directory changes, detect when an env var is unset, or sync UI with shell state.

โšก Command Execution Hooks

Instrument every external (effect) command before execution and after the result is returned.

โ–ถ๏ธ on_command_start(executable: str, final_args: list[str])

Invoked just before dispatching an effect command to the underlying operating system. Provides the fully resolved arguments as they will be executed by the adapter. Use this for command auditing, security logging, or custom pre-execution validation.

executable: binary or script name (e.g., "ls", "git", "python")
final_args: list of argument strings after shell expansion & parsing.

๐Ÿ“Š on_command_result(result: "CommandResult")

Triggered after receiving the response from an effect command. The CommandResult object encapsulates return code, stdout, stderr, and execution metadata. Ideal for metrics aggregation, failure alerts, or capturing output for logs.

result: object with attributes: .returncode, .stdout, .stderr, .execution_time (if measured).
HookTimingTypical Use
on_command_startBefore syscallaudit commands, measure latency, prevent dangerous invocations
on_command_resultAfter command finisheslog output, track error rates, store structured telemetry

โš ๏ธ Error Sink & Diagnostics

๐Ÿšจ on_error(message: str, error: Exception = None)

Central error hook triggered when a controlled or unexpected error occurs within the Core. Acts as a global error sink, keeping business logic clean. Use it to forward errors to external monitoring (Sentry, DataDog) or to aggregate internal failures for debugging.

message: human-readable error description.
error: optional exception instance for stack traces and advanced context.
๐Ÿงฉ Design philosophy: Instead of try/catch blocks scattered across the shell core, observers receive on_error calls to centralize fault reporting.

๐Ÿ’ก Practical Usage Example (Python)

Implement your own observer to track session duration, command history, and errors. The snippet below shows a concrete TelemetryObserver that overrides several hooks.

from typing import Any
from shell_observer import ShellObserver, CommandResult

class TelemetryObserver(ShellObserver):
    def on_session_start(self, shell_name: str):
        print(f"[๐Ÿ“ก] Session started: {shell_name}")
        self.command_count = 0

    def on_session_end(self, shell_name: str, error: Exception = None):
        if error:
            print(f"[โš ๏ธ] Session ended with error: {error}")
        else:
            print(f"[โœ…] Clean session end. Commands run: {self.command_count}")

    def on_context_change(self, key: str, value: Any):
        print(f"[๐ŸŒฟ] context mutated: {key} โ†’ {value}")

    def on_command_start(self, executable: str, final_args: list[str]):
        self.command_count += 1
        print(f"[โšก] exec: {executable} {' '.join(final_args)}")

    def on_command_result(self, result: CommandResult):
        if result.returncode != 0:
            print(f"[โŒ] command failed (rc={result.returncode})")
        else:
            print(f"[โœ”๏ธ] success โ†’ {len(result.stdout)} bytes out")

    def on_error(self, message: str, error: Exception = None):
        print(f"[๐Ÿ”ฅ] Core error: {message} | {error}")

# Usage inside shell context:
# with Shell(shell_name="mybash", observer=TelemetryObserver()) as sh:
#     sh.run("ls -la")

โœ… Extensibility: Because each method has a default empty implementation, your custom observer only needs to override the relevant hooks โ€” no boilerplate required.

๐Ÿ“‹ Complete Method Reference

Method signatureDescriptionWhen called
on_session_start(shell_name: str)Initialize logging / telemetry resourcesOn entry to shell context
on_session_end(shell_name, error)Cleanup & session summaryExit from with block
on_context_change(key, value)Track state mutations (CWD, env)Virtual built-in command
on_command_start(executable, final_args)Pre-execution hook for external commandsBefore OS subprocess call
on_command_result(result: CommandResult)Post-execution analysisAfter command finishes
on_error(message, error)Central error handling / forwardingOn any core exception or controlled failure
๐Ÿ’Ž Design note: The ShellObserver interface does not rely on inheritance constraints โ€” you can register multiple observers if needed, following the observer pattern.
๐Ÿงช ShellObserver โ€” part of the core instrumentation layer. Compatible with any shell adapter that implements event broadcasting. All hooks are no-op by default, ensuring zero overhead when observers are not attached.