<?xml version="1.0" encoding="UTF-8"?>
<gsl-prompt id="20250730T062755-0400" type="feat">
<gsl-header>

# Add Classes
</gsl-header>
<gsl-block>
<gsl-purpose>
<gsl-title>

## 1.  Purpose
</gsl-title>
<gsl-description>

Implement classes in my Python project.   
</gsl-description>
</gsl-purpose>
<gsl-inputs>
<gsl-title>

## 2. Inputs
</gsl-title>
<gsl-description>

This table lists key artifacts—tools, articles, or other sources—that directly guide or constrain the OBK scaffold. Each input is chosen because it meaningfully shapes project automation, agent workflows, or architecture.

| Input | Notes |
| --- | --- |
| ChatGPT / Codex | Tool |
| **Refactoring Your Python CLI: From Functions to Classes** | Article |
| **Task Template for Codex Agents** | Article |
| **Prompt Template for Codex Agents** | Article |

</gsl-description>
</gsl-inputs>

<gsl-outputs>
<gsl-title>

## 3. Outputs
</gsl-title>
<gsl-description>

This section lists the main components produced by the OBK scaffold. Each is a concrete result needed to build, run, or review the OBK CLI project.

* Implemented service classes `Greeter` and `Divider` along with the main `ObkCLI` class to manage command dispatching and error handling.
</gsl-description>
</gsl-outputs>
<gsl-workflows>
<gsl-title>

## 4. Workflows
</gsl-title>
<gsl-description>

This section lists the main steps or processes required to complete tasks in the project.

#### 1. Define a Class

Use the `class` keyword:

```python
class Greeter:
    """Simple greeter service."""
    ...
```



#### 2. Initialize Attributes (`__init__`)

Add an initializer to set up instance state:

```python
class ObkCLI:
    def __init__(self, log_file: Path = LOG_FILE) -> None:
        self.log_file = log_file
        self.greeter = Greeter()
        self.divider = Divider()
```



#### 3. Add Methods

Define behaviors with `def`:

```python
class Greeter:
    def hello(self) -> str:
        return "hello world"
```



#### 4. Use Properties (Optional)

For computed attributes, use `@property`:

```python
class Example:
    def __init__(self, value):
        self._value = value

    @property
    def double(self):
        return self._value * 2
```



#### 5. Instantiate the Class

Create objects by calling the class:

```python
cli = ObkCLI()
cli.run(["hello-world"])
```



#### 6. Access Modifiers

Python doesn’t enforce public/private keywords. Instead:

* A single leading underscore (`_name`) signals “internal use.”
* A double underscore (`__name`) invokes name mangling for stronger privacy.



#### 7. Inheritance

Derive one class from another:

```python
class ObkError(Exception):
    """Base error for the obk CLI."""

class DivisionByZeroError(ObkError):
    """Raised when attempting to divide by zero."""
```



#### 8. Abstract Classes and Interfaces

Use `abc.ABC` and `@abstractmethod` to define required methods:

```python
from abc import ABC, abstractmethod

class Processor(ABC):
    @abstractmethod
    def process(self, data):
        pass
```

Subclasses must implement `process`. This doubles as an “interface” in Python.



#### 9. Polymorphism

Polymorphism arises when different classes provide the same method interface. The CLI’s error classes inherit from `ObkError` and can be caught together:

```python
try:
    ...
except ObkError as exc:
    handle_error(exc)
```

Or, more generally, any object with the expected method (duck typing) can be used in place of another:

```python
class Divider:
    def divide(self, a: float, b: float) -> float:
        ...
```

Any class that defines a `divide()` method with the same semantics can substitute for `Divider`.



This approach—defining classes, initializing their state, adding methods/properties, optionally using inheritance or abstract base classes—allows you to build extensible, testable components as shown in `src/obk/cli.py`.

</gsl-description>
</gsl-workflows>
<gsl-acceptance-tests>
<gsl-title>

## 5. Acceptance Tests
</gsl-title>
<gsl-acceptance-test id="1">
<gsl-performed-action>

1. When I review the project codebase after the feature is implemented
</gsl-performed-action>
<gsl-expected-result>

    The codebase contains the classes `Greeter`, `Divider`, and `ObkCLI`, each defined using the `class` keyword.
</gsl-expected-result>
</gsl-acceptance-test>

<gsl-acceptance-test id="2">
<gsl-performed-action>

2. When I inspect the `ObkCLI` class
</gsl-performed-action>
<gsl-expected-result>

    The `ObkCLI` class has an `__init__` method that initializes `log_file`, `greeter` (instance of `Greeter`), and `divider` (instance of `Divider`) attributes.
</gsl-expected-result>
</gsl-acceptance-test>

<gsl-acceptance-test id="3">
<gsl-performed-action>

3. When I call `Greeter().hello()`
</gsl-performed-action>
<gsl-expected-result>

    The method returns the string "hello world".
</gsl-expected-result>
</gsl-acceptance-test>

<gsl-acceptance-test id="4">
<gsl-performed-action>

4. When I instantiate `ObkCLI` and call its `run()` method with the argument `["hello-world"]`
</gsl-performed-action>
<gsl-expected-result>

    The CLI outputs "hello world" using the `Greeter` class.
</gsl-expected-result>
</gsl-acceptance-test>

<gsl-acceptance-test id="5">
<gsl-performed-action>

5. When I review the implementation of `Divider`
</gsl-performed-action>
<gsl-expected-result>

    The `Divider` class defines a `divide(self, a: float, b: float) -> float` method that divides `a` by `b`, raising a custom `DivisionByZeroError` if `b` is zero.
</gsl-expected-result>
</gsl-acceptance-test>

<gsl-acceptance-test id="6">
<gsl-performed-action>

6. When I attempt to divide by zero using the `Divider` class
</gsl-performed-action>
<gsl-expected-result>

    The method raises a `DivisionByZeroError`, which inherits from `ObkError`.
</gsl-expected-result>
</gsl-acceptance-test>

<gsl-acceptance-test id="7">
<gsl-performed-action>

7. When I review error handling in `ObkCLI`
</gsl-performed-action>
<gsl-expected-result>

    Errors raised by `Divider` are caught as instances of `ObkError` and handled appropriately by the CLI.
</gsl-expected-result>
</gsl-acceptance-test>

<gsl-acceptance-test id="8">
<gsl-performed-action>

8. When I review the code for inheritance and abstract class use
</gsl-performed-action>
<gsl-expected-result>

    There is at least one class (`Processor` or similar) that uses Python’s `abc.ABC` and `@abstractmethod`, with a subclass that implements the abstract method.
</gsl-expected-result>
</gsl-acceptance-test>

<gsl-acceptance-test id="9">
<gsl-performed-action>

9. When I review or run type checks (e.g., using mypy)
</gsl-performed-action>
<gsl-expected-result>

    All class attributes and methods have explicit type annotations consistent with the code samples in the workflow.
</gsl-expected-result>
</gsl-acceptance-test>

<gsl-acceptance-test id="10">
<gsl-performed-action>

10. When I run the existing test suite or sample scripts
</gsl-performed-action>
<gsl-expected-result>

    The classes (`Greeter`, `Divider`, `ObkCLI`) are importable and function as described, supporting instantiation, method calls, and exception handling as specified in the workflow.
</gsl-expected-result>
</gsl-acceptance-test>
</gsl-acceptance-tests>
</gsl-block>
</gsl-prompt>

