Metadata-Version: 2.3
Name: codhc
Version: 0.0.5
Summary: 
Author: narumi
Author-email: narumi <toucans-cutouts0f@icloud.com>
Requires-Dist: typer>=0.24.1
Requires-Python: >=3.12
Description-Content-Type: text/markdown

# Codex Hook Command

`codhc` is a small command wrapper for Codex hooks. It runs an external command, reads the hook payload from `stdin`, and emits the JSON response that Codex expects.

This project is built for the Codex hook workflow first, especially the `Stop` hook. It is not a general shell runner and it is not a hook framework. Its job is to stay small, predictable, and easy to wire into `hooks.json`.

## Start Here

If you are using `codhc`, this is the main integration point:

```json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "uvx codhc ruff check --fix",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
```

That configuration tells Codex to run `codhc` during the `Stop` hook. `codhc` then runs the command you give it and converts the result into a Codex-compatible response.

For the broader Codex hook model, including events, matchers, and payload shapes, see [docs/HOOKS.md](docs/HOOKS.md).

## What It Does

`codhc` has a deliberately narrow contract:

- Read the hook payload from `stdin`
- Run the external command you provide
- Inspect `stop_hook_active`
- Return a hook response based on command success or failure

That narrow scope is intentional. The tool is designed to be a thin adapter, not a policy engine.

## How It Flows

The runtime model is simple:

1. Codex invokes `codhc` as a command hook.
2. Codex sends the hook payload to `stdin`.
3. `codhc` runs the external command.
4. `codhc` writes a single JSON response to `stdout`.
5. If the command fails, `codhc` forwards the captured command output to `stderr`.

This separation keeps `stdout` clean for hook JSON while preserving useful failure output. Successful runs stay quiet apart from the minimal hook result.

## Installation

Install it as a tool:

```bash
uv tool install .
codhc uv run ruff check --fix
```

Or run it with `uvx`:

```bash
uvx codhc uv run ruff check --fix
```

## CLI Usage

```bash
codhc <command> [args...]
```

Examples:

```bash
codhc uv run ruff check --fix
codhc uv run pytest -q
codhc python -m compileall src
```

If no command is provided, `codhc` exits non-zero, prints a usage message to `stderr`, and emits no hook JSON.

## Command Semantics

`codhc` executes argv exactly as it receives it.

- It does not parse shell strings.
- It does not provide subcommands.
- It does not re-tokenize your command.

Use this:

```bash
codhc uv run ruff check --fix
```

Not this:

```bash
codhc "uv run ruff check --fix"
```

## Stop Hook Behavior

The current implementation is aimed at the Codex `Stop` hook and only reads one payload field:

```json
{
  "stop_hook_active": false
}
```

If `stdin` is empty, invalid JSON, or not a JSON object, `codhc` treats that as missing hook metadata and still runs the command.

### Success

If the command exits with `0`, `codhc` emits:

```json
{
  "continue": false
}
```

This keeps successful `Stop` hooks explicit without producing a `systemMessage`, which Codex surfaces as a warning.

### First failure

If the command fails and `stop_hook_active` is not `true`, `codhc` emits:

```json
{
  "decision": "block",
  "reason": "`uv run ruff check --fix` failed. Inspect the command output, fix the issues, then stop again."
}
```

This uses the documented legacy continuation shape for `Stop` hooks.

### Failure after the continuation pass

If the command fails and `stop_hook_active` is `true`, `codhc` emits:

```json
{
  "continue": false,
  "systemMessage": "`uv run ruff check --fix` failed after the Stop continuation pass."
}
```

## I/O Contract

- `stdin`: Codex hook payload JSON
- `stdout`: one JSON response emitted by `codhc`
- `stderr`: captured command output, usually only useful on failure

`stdout` is reserved for hook JSON. External command output is not forwarded there.

## Development

Install development dependencies:

```bash
uv sync --group dev
```

Run the standard checks:

```bash
just format
just lint
just type
just test
just all
```

Run the tools directly with `uv`:

```bash
uv run pytest -v -s --cov=src tests
uv run ruff check --fix
uv run ty check
```

If you are working from this repository and want to run the package from the local checkout without installing it as a tool, use:

```bash
uvx --from . codhc uv run ruff check --fix
```

## Design Constraints

This version does not:

- validate the hook event name
- implement timeout handling itself
- inject custom environment variables
- add tool-specific behavior
- parse shell quoting or reconstruct commands

Those boundaries are deliberate. If the tool grows past them, it should do so only for a concrete hook use case.
