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

# Integrate Typer CLI Framework 
</gsl-header>
<gsl-block>
<gsl-purpose>
<gsl-title>

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

Integrate the Typer framework into the `obk` CLI, following best practices for modularity and testability. 
</gsl-description>
</gsl-purpose>

<gsl-inputs>
<gsl-title>

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

| Input | Notes |
| --- | --- |
| Typer CLI Framework—When to Use It, and When to Pass | Article on Typer’s pros, cons, and recommended usage patterns |
| Why One-Line Manual Tests Should Precede Automation | Article on manual-first, interface-driven test writing |
| OBK/Codex Standard Prompt Template | Template for tasks, specs, and workflows |

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

<gsl-outputs>
<gsl-title>

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

* **Converted the CLI to use Typer**
    
    * Explicit command names and help messages.
        
    * Added a new `greet` command with an `--excited` flag.
        
    * Execution errors now result in clean exits.
        
* **Testing**
    
    * Added a test to verify the new `greet --excited` behavior.
        
    * Documented manual CLI tests in the prompt specification for traceability.
    
</gsl-description>
</gsl-outputs>

<gsl-workflows>
<gsl-title>

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

#### How to Add a New Command (Typer CLI)

##### 1. Implement the Command Logic

Add a private method for your command in `src/obk/cli.py`:

```python
def _cmd_greet(self, name: str, excited: bool = False) -> None:
    if excited:
        typer.echo(f"Hello, {name}!!!")
    else:
        typer.echo(f"Hello, {name}.")
```



##### 2. Register the Command

Register your method as a Typer command in `src/obk/cli.py`:

```python
self.app.command(
    name="greet",
    help="Greet by name with optional excitement",
    short_help="Greet by name",
)(self._cmd_greet)
```



##### 3. (Optional) Add Services

For reusable logic or dependencies:

* Create a class in `src/obk/services.py`.
* Expose it via the DI container (`src/obk/containers.py`).



##### 4. Write CLI Tests

In `tests/test_cli.py`, add a subprocess-style test to verify the command:

```python
result = subprocess.run(
    [sys.executable, "-m", "obk", "greet", "Ada", "--excited"],
    capture_output=True,
    text=True,
    check=True,
    cwd=tmp_path,
    env=env,
)
assert result.stdout.strip() == "Hello, Ada!!!"
```

##### 5. Lint, Format, and Run Tests

Before submitting, **run all quality checks**:

```sh
ruff .
black .
pytest
```

##### 6. Document (Optional)

Update the README to mention the new command so users know it exists.



#### **Why This Works**

* **Modular & Testable:** Each command is a self-contained method.
* **Extensible:** Typer makes it easy to add, organize, and test new commands.
* **Scalable:** Adding new features and dependency-injected services is straightforward.



**Summary Workflow**

1. Define logic in `src/obk/cli.py`
2. Register via `self.app.command(...)`
3. Add services if needed
4. Write subprocess tests
5. Lint, format, and test
6. Update documentation


This pattern keeps your CLI maintainable and easy to extend.

</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 run `obk greet Ada`
</gsl-performed-action>
<gsl-expected-result>

    The console displays "Hello, Ada.".
</gsl-expected-result>
</gsl-acceptance-test>

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

2. When I run `obk --help`
</gsl-performed-action>
<gsl-expected-result>

    The help output includes the 'greet' command, and its options, including the 'excited' flag.
</gsl-expected-result>
</gsl-acceptance-test>

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

3. When I run `obk greet Ada --excited`
</gsl-performed-action>
<gsl-expected-result>
    
    The console displays "Hello, Ada!!!".
</gsl-expected-result>
</gsl-acceptance-test>

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

4. When I run `obk divide 4 2`
</gsl-performed-action>
<gsl-expected-result>

    The console displays "2.0".
</gsl-expected-result>
</gsl-acceptance-test>
<gsl-acceptance-test id="5">
<gsl-performed-action>

5. When I run `obk divide 1 0`
</gsl-performed-action>
<gsl-expected-result>

    The process exits with code 2 and error output includes "cannot divide by zero".
</gsl-expected-result>
</gsl-acceptance-test>

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

6. When I run `obk hello-world -h`
</gsl-performed-action>
<gsl-expected-result>

    The help output for 'hello-world' command includes 'Print hello world'.
</gsl-expected-result>
</gsl-acceptance-test>

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

7. When I run `obk hello-world`
</gsl-performed-action>
<gsl-expected-result>

    The console displays "hello world".
</gsl-expected-result>
</gsl-acceptance-test>

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

8. When I run `obk fail`
</gsl-performed-action>
<gsl-expected-result>

    The process exits with code 1 and error output includes "fatal".
</gsl-expected-result>
</gsl-acceptance-test>

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

9. When I override the Greeter in the DI container and run `obk greet Ada`
</gsl-performed-action>
<gsl-expected-result>

    The output displays the value returned by the mock Greeter, confirming the override works for the `greet` command.
</gsl-expected-result>
</gsl-acceptance-test>

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

10. When I override the Divider in the DI container and run `obk divide 1 3`
</gsl-performed-action>
<gsl-expected-result>

    The output displays the value returned by the mock Divider, confirming the override works for the `divide` command.
</gsl-expected-result>
</gsl-acceptance-test>

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

11. When I run `obk --logfile custom.log divide 4 2`
</gsl-performed-action>
<gsl-expected-result>

    The file "custom.log" is created and contains "Divide 4.0 by 2.0 = 2.0".
</gsl-expected-result>
</gsl-acceptance-test>
</gsl-acceptance-tests>
</gsl-block>
</gsl-prompt>
