Skip to content

Phase 1: Extract shared utilities (github.py, claude.py, hooks.py, prompts.py) #2

@rhencke

Description

@rhencke

Extract all subprocess calls to `gh` and `claude` into dedicated Python modules. No callers yet — just the modules + 100% test coverage.

New files

`kennel/github.py`

All `gh` CLI wrappers: repo info, PR CRUD, GraphQL queries, thread resolution, user status. Every function calls `subprocess.run(["gh", ...])` and returns parsed results.

Key functions:

  • `get_repo_info()`, `get_user()`, `get_default_branch()`
  • `find_issues()`, `view_issue()`, `close_issue()`, `comment_issue()`
  • `find_pr()`, `create_pr()`, `edit_pr_body()`, `pr_checks()`, `pr_ready()`, `pr_merge()`
  • `get_review_threads()`, `get_reviews()`, `resolve_thread()`
  • `set_user_status()`

`kennel/claude.py`

All `claude` CLI wrappers:

  • `one_shot(model, prompt, system_prompt=None, timeout=30) -> str` — `--print` mode
  • `start_session(model, system_prompt_file, prompt_file, stream_file) -> str` — returns session_id, streams to file
  • `resume_session(model, session_id, prompt_file) -> None`

`kennel/hooks.py`

`.claude/settings.local.json` hook management:

  • `install_hooks(work_dir, sub_dir, sync_cmd)` — adds PostCompact + PostToolUse hooks
  • `remove_hooks(work_dir, hook_cmd, sync_cmd)` — cleanup on exit

`kennel/prompts.py`

Prompt building:

  • `load_persona(sub_dir) -> str`
  • `build_prompt(sub_dir, skill_name, context) -> tuple[Path, Path]` — returns (system_file, prompt_file)
  • `generate_slug(request) -> str` — haiku call + fallback

Constraints

  • 100% test coverage (CI enforced)
  • All subprocess calls mocked in tests
  • No existing code changes — these are new standalone modules
  • Reference work.sh for the exact gh/claude invocations to wrap

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions