Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .issueflows/03-solved-issues/issue18_original.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Issue #18: better documentation on dependencies

Source: https://github.com/jepegit/issue-flow/issues/18

## Original issue text

issueflow requires several dependencies. It is probably OK to assume all users have git installed. But other dependencies, especially gh should be better documented. Including how to install them, and maybe also make sure that issueflow init checks if all dependencies are present, if not issue a warning with description on how to install it (and ask for confirmation before proceeding).
122 changes: 122 additions & 0 deletions .issueflows/03-solved-issues/issue18_plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Plan for issue #18: better documentation on dependencies

## Goal

Make issue-flow's external CLI dependencies (especially GitHub CLI `gh`)
obvious to users, both in the README and at install time: `issue-flow init`
should detect missing dependencies, print install guidance, and require
confirmation before continuing.

## Constraints

- Stay proportional — small utility change, no new runtime deps.
- Preserve existing `run_init` / `run_update` behavior for the happy path
(all deps present) so current tests keep passing without edits.
- Use the existing `rich.Console` for output (matches current style) and
`typer.confirm` (Typer is already a dependency) for the prompt.
- Detection must be safe on Windows, macOS, Linux — use `shutil.which` only
(no subprocess calls that could hang or prompt for creds).
- No hard failure when a dependency is missing and the user opts in — this
is a warning, not a gate. Missing deps block only if the user declines
at the prompt.
- Non-interactive callers (CI, tests) must be able to bypass the prompt
without hanging. Prefer an explicit opt-out flag plus auto-skip when
stdin is not a TTY.
- Keep the dependency list narrow and accurate: only tools issue-flow's
scaffolded workflow actually invokes (`git`, `gh`). Do not add `uv` to
the runtime check — `uv` is an install-time prerequisite, not something
the scaffold itself shells out to, so it belongs in README only.

## Approach

1. **Add a dependency check helper** in a small new module
`src/issue_flow/dependencies.py`:
- A typed record per dependency: `name`, `command`, `purpose`,
`install_hints` (dict of platform → short hint), `docs_url`.
- `check_dependencies() -> list[MissingDependency]` using
`shutil.which`. Returns a list of missing items (empty list when all
present).
- `format_missing_report(missing, console)` — renders a `rich` panel or
plain block listing what is missing, why it matters, and how to
install it on Windows / macOS / Linux, plus a docs link.

2. **Wire the check into `run_init`** (`src/issue_flow/init.py`) at the
top, before `_create_issueflow_dirs`:
- Call `check_dependencies()`.
- If anything is missing:
- Print the report.
- If the new `--skip-dep-check` flag is set, or stdin is not a TTY
(i.e. `not sys.stdin.isatty()`), continue without prompting (emit
a one-line note that the check was skipped / non-interactive).
- Otherwise call `typer.confirm("Continue anyway?", default=False)`.
If the user declines, exit cleanly via `raise typer.Exit(code=1)`.
- If everything is present, optionally print a single dim
"All dependencies detected" line (keeps noise low).

3. **Extend the CLI** (`src/issue_flow/cli.py`) on `init`:
- Add `--skip-dep-check / --no-skip-dep-check` option (default
`False`). Pass through to `run_init`.
- `run_init` signature becomes
`run_init(project_root, force=False, skip_dep_check=False)`.
- `run_update` gets the same treatment so that `issue-flow update`
surfaces missing deps too (same helper, same flag).

4. **README.md** — add a "Prerequisites" section directly before
"Installation":
- Required: `git`, `gh` (GitHub CLI).
- Recommended: `uv` (already covered by the existing install snippet,
link to https://docs.astral.sh/uv/).
- Short install pointers per OS for `gh` (Homebrew, `winget`,
`apt`, link to https://cli.github.com/). Include
`gh auth login` reminder.
- Note that `issue-flow init` now runs a dependency check and how to
bypass it (`--skip-dep-check`).

5. **HISTORY.md** — add an `[Unreleased]` bullet describing the new
dependency check and prerequisites docs (follow existing changelog
style).

## Files to touch

- `src/issue_flow/dependencies.py` — new module with
`REQUIRED_DEPENDENCIES`, `check_dependencies`, `format_missing_report`.
- `src/issue_flow/init.py` — call the check inside `run_init` and
`run_update`; thread `skip_dep_check` through.
- `src/issue_flow/cli.py` — add `--skip-dep-check` option to `init`
(and `update`, for symmetry).
- `README.md` — new "Prerequisites" section covering `git`, `gh`, `uv`
with install hints, plus mention of the init-time check.
- `HISTORY.md` — `[Unreleased]` entry.
- `tests/test_dependencies.py` — new: unit tests for
`check_dependencies` using `monkeypatch` on `shutil.which`, and for
`format_missing_report` output.
- `tests/test_init.py` — additions:
- check is skipped when all deps present (no prompt, no abort).
- missing dep + `skip_dep_check=True` continues scaffolding.
- missing dep + non-TTY continues scaffolding (simulate via monkeypatch
on `sys.stdin.isatty`).
- missing dep + user declines → `run_init` raises `typer.Exit` and
does *not* create `.cursor/` scaffold files.

## Test strategy

- `uv run pytest` — existing suite must keep passing (current tests call
`run_init(tmp_path)` which will now hit the dep check; ensure the
default behavior with real `gh`/`git` on dev machines is still green,
and for CI / machines without `gh`, tests must monkeypatch
`shutil.which` so they never prompt).
- New targeted tests in `tests/test_dependencies.py` and extra cases in
`tests/test_init.py` as listed above.
- Manual smoke: run `uv run issue-flow init /tmp/demo` with `gh`
temporarily off `PATH` to verify the warning + prompt flow, and with
`--skip-dep-check` to verify the bypass.

## Open questions

- Should the check also cover `uv` itself? Current plan says **no** (uv
isn't invoked by the scaffolded workflow at runtime — it's only an
install-time prerequisite documented in the README). Confirm.
- Flag name: `--skip-dep-check` vs `--yes` vs `--no-dep-check`. Plan
picks `--skip-dep-check` for clarity; open to the user's preference.
- Should declining the prompt exit with code `1` (plan default) or `0`
(treat as "user chose not to continue")? Plan uses `1` so CI notices.
59 changes: 59 additions & 0 deletions .issueflows/03-solved-issues/issue18_status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Status for issue #18: better documentation on dependencies

- [x] Done

## Implementation summary

All items from `issue18_plan.md` have landed on branch
`18-better-documentation-on-dependencies`:

- **New module `src/issue_flow/dependencies.py`.** Declares
`REQUIRED_DEPENDENCIES` (`git`, `gh`) with per-OS install hints and a
docs link each. Exposes `check_dependencies()` (pure `shutil.which`)
and `prompt_or_skip()` (prints the missing-deps report via `rich`,
bypasses the prompt on `skip=True` or non-TTY stdin, otherwise calls
`typer.confirm`). `uv` is deliberately not in the runtime check — it's
only called out in the README as an install-time prerequisite.
- **Wired into `init.py`.** `run_init` and `run_update` now take a new
`skip_dep_check` kwarg and call `_dependency_gate(skip_dep_check)`
before doing any scaffolding. A decline at the prompt raises
`typer.Exit(code=1)` so CI notices.
- **CLI surface (`src/issue_flow/cli.py`).** Both `init` and `update`
gained `--skip-dep-check`. Verified via `uv run issue-flow init --help`.
- **Docs.**
- `README.md`: new **Prerequisites** section above **Installation**
with `git`, `gh`, and `uv` entries, a per-OS install table for `gh`,
and a note about the new check and `--skip-dep-check` bypass. The
`issue-flow init` / `update` option tables and the Usage shell
block were updated to list the flag.
- `HISTORY.md`: new `[Unreleased]` bullet crediting issue #18.
- **Tests.**
- New `tests/conftest.py` with an autouse fixture that stubs
`check_dependencies` to return `[]` by default, so the rest of the
suite is deterministic regardless of whether the host has `git` /
`gh` on `PATH`.
- New `tests/test_dependencies.py` — 11 cases covering the dep list
scope, presence/absence detection, custom dependency lists, report
formatting, and the four `prompt_or_skip` branches (empty, skip,
non-TTY, TTY accept/decline).
- `tests/test_init.py` gained four cases: proceeds silently when all
deps present, continues with `skip_dep_check=True`, continues on
non-TTY stdin, aborts with `typer.Exit(1)` and leaves no scaffold
when the user declines.

## Verification

- `uv run pytest` → **64 passed** in 6.77s.
- `uv run ruff check src/ tests/` → **All checks passed**.
- Manual smoke:
- `uv run issue-flow init --help` shows the new `--skip-dep-check` flag.
- Forcing `gh` to be "missing" via `shutil.which` monkeypatch and
calling `run_init(..., skip_dep_check=True)` scaffolds the project
correctly (`Created 20 file(s).`).
- The same missing-dep path without the skip flag prints the full
install-hint report and reaches the `typer.confirm` prompt as
designed.

## Remaining work

None — ready for `/issue-close`.
1 change: 1 addition & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ than the GitHub release notes they link to.

## [Unreleased]

- **Dependency awareness at install time (#18).** A new `Prerequisites` section in the README documents the external CLI tools the scaffolded workflow shells out to (`git`, `gh` — with install hints per OS and a `gh auth login` reminder), and `issue-flow init` / `issue-flow update` now run a `shutil.which`-based dependency check up front. If anything is missing, the CLI prints the install hints and asks for confirmation before continuing. The prompt is auto-skipped on non-TTY stdin (CI) and can be bypassed explicitly with `--skip-dep-check`.
- `issue-flow init` now creates or extends a project `.env` with `ISSUEFLOW_*` hints (#35).
- Rename `ISSUEFLOW_CURSOR_DIR` to the more tool-agnostic `ISSUEFLOW_AGENT_DIR` (#36).
- `/issue-close` flags unrelated uncommitted changes and reminds about the issue branch after the PR is opened (#37).
Expand Down
41 changes: 39 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,41 @@ Plus a few off-path commands:

The matching **Agent Skills** (under `.cursor/skills/`) carry the same workflows for on-demand use with `/issueflow-iflow`, `/issueflow-issue-init`, `/issueflow-issue-plan`, `/issueflow-issue-start`, `/issueflow-issue-pause`, `/issueflow-issue-close`, `/issueflow-issue-cleanup`, `/issueflow-issue-yolo`, `@issueflow-version-bump` when you need only the bump steps, or `@issueflow-history-update` when you need only the changelog update (see [Cursor Agent Skills](https://cursor.com/docs/context/skills)).

## Prerequisites

issue-flow itself is a small Python CLI, but the **scaffolded slash commands
it writes into your project shell out to a few external tools**. If they are
missing, the slash commands will fail at runtime — so `issue-flow init` now
checks for them up front and prints install hints before it does anything.

Required:

- **[Git](https://git-scm.com/downloads)** — used by every slash command for
branch, fetch, status, commit, and push operations. Almost certainly already
installed if you're here, but the check covers it for completeness.
- **[GitHub CLI (`gh`)](https://cli.github.com/)** — used by `/issue-init` to
fetch issues, by `/issue-close` to open PRs, and by `/issue-cleanup` to check
PR merge status. After installing, run `gh auth login` once to authenticate.

Recommended:

- **[uv](https://docs.astral.sh/uv/)** — how issue-flow itself is meant to be
installed, and how this repo manages its own Python environment.

Quick install pointers for `gh`:

| Platform | Command |
|---|---|
| macOS (Homebrew) | `brew install gh` |
| Windows (winget) | `winget install --id GitHub.cli -e` |
| Linux (Debian/Ubuntu) | `sudo apt install gh` (or see [cli.github.com](https://cli.github.com/) for the official repo) |

If a dependency is missing, `issue-flow init` prints the installation hints
and asks whether to continue anyway. You can bypass the prompt in automation
with `issue-flow init --skip-dep-check` (the same flag is available on
`issue-flow update`), and the prompt is also auto-skipped when stdin is not
a TTY (e.g. CI pipelines).

## Installation

Requires Python 3.13+ and [uv](https://docs.astral.sh/uv/).
Expand All @@ -84,8 +119,8 @@ That's it. Open the project in Cursor and start with `/iflow` (or step through `
## Usage

```
issue-flow init [PROJECT_DIR] [--force]
issue-flow update [PROJECT_DIR]
issue-flow init [PROJECT_DIR] [--force] [--skip-dep-check]
issue-flow update [PROJECT_DIR] [--skip-dep-check]
```

### `issue-flow init`
Expand All @@ -94,6 +129,7 @@ issue-flow update [PROJECT_DIR]
|---|---|
| `PROJECT_DIR` | Project root directory. Defaults to `.` (current directory). |
| `--force`, `-f` | Overwrite generated Cursor commands, rules, and workflow doc instead of skipping them. |
| `--skip-dep-check` | Skip the external-CLI dependency check (`git`, `gh`) and the confirmation prompt that follows if anything is missing. Useful in automation. |

Running `init` again without `--force` is safe: generated scaffold files that already exist are skipped, and **issue markdown under `.issueflows/` is never touched** by `init` or `update`. When the CLI detects an existing scaffold, it reminds you about `update` and `--force`.

Expand All @@ -102,6 +138,7 @@ Running `init` again without `--force` is safe: generated scaffold files that al
| Argument / Option | Description |
|---|---|
| `PROJECT_DIR` | Project root directory. Defaults to `.` (current directory). |
| `--skip-dep-check` | Skip the external-CLI dependency check (`git`, `gh`) and the confirmation prompt that follows if anything is missing. |

Use `update` after upgrading the **issue-flow** package to refresh the packaged slash commands, Cursor rule, and `docs/cursor-issue-workflow.md` from the version you have installed. This **overwrites** those generated files (unlike a plain second `init`). It still does not modify arbitrary files under `.issueflows/` (for example your `issue*_original.md` / `issue*_status.md` files), and it creates any **new** `.issueflows/` subdirectories required by the current package.

Expand Down
22 changes: 20 additions & 2 deletions src/issue_flow/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,21 @@ def init(
"-f",
help="Overwrite existing files without asking.",
),
skip_dep_check: bool = typer.Option(
False,
"--skip-dep-check",
help=(
"Skip the external-CLI dependency check (git, gh) and the "
"confirmation prompt that follows if anything is missing."
),
),
) -> None:
"""Scaffold issue-flow directories and Cursor config files in a project."""
from issue_flow.init import run_init

run_init(project_root=project_dir, force=force)
run_init(
project_root=project_dir, force=force, skip_dep_check=skip_dep_check
)


@app.command()
Expand All @@ -48,11 +58,19 @@ def update(
file_okay=False,
resolve_path=True,
),
skip_dep_check: bool = typer.Option(
False,
"--skip-dep-check",
help=(
"Skip the external-CLI dependency check (git, gh) and the "
"confirmation prompt that follows if anything is missing."
),
),
) -> None:
"""Refresh packaged Cursor commands, rules, and workflow doc from this package."""
from issue_flow.init import run_update

run_update(project_root=project_dir)
run_update(project_root=project_dir, skip_dep_check=skip_dep_check)


def main() -> None:
Expand Down
Loading
Loading