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
14 changes: 11 additions & 3 deletions .issueflows/04-designs-and-guides/graphify-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,16 @@ The original implementation assumed `graphify <path> [flags…]` was the canonic

**Fixes landed in this iteration:**

- `_build_graphify_argv` translates `issue-flow build [args…]` into `graphify <subcommand> <project_root> [args…]`. Default subcommand is `extract` (full AST + semantic LLM build, matches the natural meaning of "build the graph"). A leading recognized build subcommand (`extract`, `update`, `watch`, `cluster-only`, `check-update`) overrides the default.
- `_build_graphify_argv` translates `issue-flow build [args…]` into `graphify <subcommand> <project_root> [args…]`. A leading recognized build subcommand (`extract`, `update`, `watch`, `cluster-only`, `check-update`) overrides the default.
- `project_dir` on the Typer `build` command became `-C` / `--project-dir` (modeled on `git -C`) so positional args flow into `_build_graphify_argv` untouched. Without this change, `issue-flow build update` failed because Typer eagerly bound `update` to the `project_dir` positional and the path-existence check rejected it.
- All scaffolded docs (`commands/build.md.j2`, `skills/issueflow_build/SKILL.md.j2`, `rules/issueflow-rules.mdc.j2`, `docs/cursor-issue-workflow.md.j2`, `commands/issue-close.md.j2`) and the README now describe real graphify subcommands and the `-C` option. The `graphify .` fallback (which never worked) is replaced everywhere with `graphify extract .`.
- All scaffolded docs (`commands/build.md.j2`, `skills/issueflow_build/SKILL.md.j2`, `rules/issueflow-rules.mdc.j2`, `docs/cursor-issue-workflow.md.j2`, `commands/issue-close.md.j2`) and the README now describe real graphify subcommands and the `-C` option. The `graphify .` fallback (which never worked) is replaced everywhere with a valid subcommand invocation.

**Why this slipped through originally:** `run_build` was tested with `subprocess.run` mocked to a no-op, so the test suite never observed graphify's actual argv parser. The new tests exercise the argv-construction function directly with realistic subcommand combinations. Future graphify integrations should always include at least one test that uses real graphify subcommand names (or a contract test that mirrors `graphify --help`).
### 4. Default subcommand is `update`, not `extract`

The first cut of this fix used `extract` as the default subcommand (it is the most thorough build). Real-world testing immediately surfaced a follow-on bug: `graphify extract` requires an LLM API key (Gemini / Anthropic / OpenAI / Kimi) or `--backend ollama`, so `issue-flow build` failed with `error: no LLM API key found` on any fresh machine. **Cursor's own LLM is not available to subprocesses** — there is no API graphify could hook to reuse it — so we cannot paper over this with a clever shim.

The fix: default to `update` (AST-only). It produces the full `graphify-out/` directory (`graph.json`, `graph.html`, `GRAPH_REPORT.md`) on a fresh repo, runs in seconds, and explicitly states "no LLM needed" in its own output. Power users who want the deeper semantic pass pick `extract` explicitly (`issue-flow build extract`) and configure a backend.

**Trade-off:** the default graph misses semantic relationships an LLM would surface (cross-file calls inferred from intent, doc/paper integration, image extraction). That is the right trade for a default that "just works" — graphify's own help text recommends the same upgrade path ("Tip: set GEMINI_API_KEY … to use Gemini for semantic extraction").

**Why this slipped through originally:** `run_build` was tested with `subprocess.run` mocked to a no-op, so the test suite never observed graphify's actual argv parser **and** never exercised the LLM-key precondition. The new tests exercise the argv-construction function directly with realistic subcommand combinations, and the doc / template prose now flags the API-key requirement on `extract`. Future graphify integrations should always include at least one test that uses real graphify subcommand names, and at least one end-to-end smoke against a tiny test repo to catch precondition surprises like the API-key gate.
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,14 @@ What `issue-flow` does when `graphify` is on PATH:
If graphify is not installed, both commands just print install hints and
continue — they never block.
- A new slash command `/build` (and matching `/issueflow-build` skill) wraps
`issue-flow build`. With no extra args it runs `graphify extract <project>`
(full build); pass a graphify build subcommand to pick a different action
(`issue-flow build update`, `issue-flow build watch`,
`issue-flow build cluster-only --no-viz`, …). Trailing flags forward to
the chosen subcommand verbatim.
`issue-flow build`. With no extra args it runs `graphify update <project>`
— AST-only, **no LLM API key required**, so the no-arg case "just works".
For richer semantic relationships add `extract` (`issue-flow build extract`)
and configure a backend (`GEMINI_API_KEY`, `ANTHROPIC_API_KEY`,
`OPENAI_API_KEY`, `MOONSHOT_API_KEY`, or `--backend ollama` for a local
LLM). Cursor's own LLM is not available to subprocesses, so graphify
needs its own backend. Other subcommands (`watch`, `cluster-only`, …)
pass through too; trailing flags forward verbatim.
- The scaffolded rules and `/issue-start` mention `graphify-out/GRAPH_REPORT.md`
as a recommended pre-read when the file exists. `/build` is **off-path** —
`/iflow` never auto-dispatches to it.
Expand Down Expand Up @@ -203,7 +206,7 @@ Use `update` after upgrading the **issue-flow** package to refresh the packaged
| Argument / Option | Description |
|---|---|
| `-C`, `--project-dir` | Project root directory to scan with graphify. Defaults to `.` (current directory). Modeled on `git -C` so positional args can flow into graphify untouched. |
| `...graphify subcommand + args` | Optional graphify subcommand + flags. With no extras runs `graphify extract <PROJECT_DIR>` (full AST + semantic LLM build). The first extra arg, if it is a recognized build subcommand (`extract`, `update`, `watch`, `cluster-only`, `check-update`), picks the action; trailing tokens forward verbatim. Examples: `issue-flow build update`, `issue-flow build cluster-only --no-viz`, `issue-flow build ./subdir`. |
| `...graphify subcommand + args` | Optional graphify subcommand + flags. With no extras runs `graphify update <PROJECT_DIR>` AST-only, **no LLM API key required**. The first extra arg, if it is a recognized build subcommand (`update`, `extract`, `watch`, `cluster-only`, `check-update`), picks the action; trailing tokens forward verbatim. Examples: `issue-flow build extract` (semantic LLM pass; needs `GEMINI_API_KEY` / `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / `MOONSHOT_API_KEY` or `--backend ollama`), `issue-flow build cluster-only --no-viz`, `issue-flow build ./subdir`. |

`build` requires `graphifyy` to be installed (`uv tool install graphifyy`). When the `graphify` CLI is missing, the command prints install hints and exits with code `2`. Outputs land in `graphify-out/` (`graph.html`, `GRAPH_REPORT.md`, `graph.json`).

Expand Down
21 changes: 13 additions & 8 deletions src/issue_flow/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,19 @@ def build(
) -> None:
"""Rebuild the graphify knowledge graph for the project.

With no extra arguments runs ``graphify extract <project_dir>`` (the
full AST + semantic LLM build). Override the subcommand by passing
it as the first argument: ``issue-flow build update`` (fast,
code-only re-extract), ``issue-flow build watch`` (live rebuild),
``issue-flow build cluster-only --no-viz`` (re-cluster), etc.
Trailing flags pass through verbatim. Use ``-C <dir>`` to scan a
project other than the current directory. Requires ``graphify`` to
be on ``PATH`` (install with ``uv tool install graphifyy``).
With no extra arguments runs ``graphify update <project_dir>``
(AST-only build, no LLM API key required) so first-time builds
just work. Pick a different action by passing the subcommand as
the first argument: ``issue-flow build extract`` adds the slower
semantic LLM pass for richer cross-file relationships (needs an
API key — ``GEMINI_API_KEY``, ``ANTHROPIC_API_KEY``,
``OPENAI_API_KEY``, or ``--backend ollama`` for a local LLM);
``issue-flow build watch`` runs a live rebuild;
``issue-flow build cluster-only --no-viz`` re-clusters an existing
graph. Trailing flags pass through verbatim. Use ``-C <dir>`` to
scan a project other than the current directory. Requires
``graphify`` to be on ``PATH`` (install with
``uv tool install graphifyy``).
"""
from issue_flow.graphify import run_build

Expand Down
29 changes: 19 additions & 10 deletions src/issue_flow/graphify.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,13 @@
{"extract", "update", "watch", "cluster-only", "check-update"}
)
# Default subcommand when the user runs ``issue-flow build`` without
# specifying one. ``extract`` is the full AST + semantic LLM build
# (matches the natural meaning of "build the graph").
_DEFAULT_BUILD_SUBCOMMAND: str = "extract"
# specifying one. ``update`` is the AST-only build: it produces the
# full ``graphify-out/`` (``graph.json``, ``graph.html``,
# ``GRAPH_REPORT.md``) and crucially does **not** need an LLM API key,
# so the no-arg case "just works" for first-time users. Power users
# pick ``extract`` explicitly when they want the slower semantic LLM
# pass that surfaces richer cross-file relationships.
_DEFAULT_BUILD_SUBCOMMAND: str = "update"


def _build_graphify_argv(
Expand All @@ -60,17 +64,19 @@ def _build_graphify_argv(
folder" mode — so every invocation needs an explicit subcommand.
Behavior:

* No extra args → ``graphify extract <project_root>``.
* No extra args → ``graphify update <project_root>``. ``update``
is AST-only and needs no LLM API key, so the no-arg case "just
works" for users who have not configured a backend yet.
* First arg is a recognized build subcommand (``extract``,
``update``, ``watch``, ``cluster-only``, ``check-update``) → use
it. If a positional path follows, trust it; otherwise inject
``project_root`` so graphify scans the right tree even when the
agent's cwd differs from the project root.
* First arg is anything else → assume the default subcommand
(``extract``) and treat the args as positional/flag tail. A
(``update``) and treat the args as positional/flag tail. A
first arg that does not start with ``-`` is taken as the path
the user wants graphify to scan (e.g. ``issue-flow build ./docs``
→ ``graphify extract ./docs``).
→ ``graphify update ./docs``).
"""
args = list(extra_args)

Expand Down Expand Up @@ -260,10 +266,13 @@ def run_build(

See :func:`_build_graphify_argv` for the argv-construction rules.
The short version: ``issue-flow build`` with no args invokes
``graphify extract <project_root>`` (the natural "build the graph"
action). Users can pick a different build subcommand by passing it
as the first argument (e.g. ``issue-flow build update``,
``issue-flow build cluster-only --no-viz``).
``graphify update <project_root>`` (AST-only, no LLM API key
required, produces the full ``graphify-out/`` directory). Users
who want the deeper semantic LLM pass run
``issue-flow build extract`` and configure a backend
(``GEMINI_API_KEY`` / ``ANTHROPIC_API_KEY`` / ``OPENAI_API_KEY`` /
``MOONSHOT_API_KEY`` env var, or ``--backend ollama`` for a local
LLM).

Returns ``2`` and prints install hints when graphify is missing.
Re-raises ``KeyboardInterrupt`` so users can ^C a long build.
Expand Down
24 changes: 16 additions & 8 deletions src/issue_flow/templates/commands/build.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ This is an **off-path** command — the lifecycle dispatcher (`/iflow`) never au

## Input

Optional free-form text after the command. The first word, if it is a recognized graphify build subcommand, picks the action; otherwise the default is `extract`. Trailing tokens forward verbatim. Common combinations:
Optional free-form text after the command. The first word, if it is a recognized graphify build subcommand, picks the action; otherwise the default is `update`. Trailing tokens forward verbatim. Common combinations:

- **No extra text** — full rebuild (`graphify extract <project>`); requires an LLM API key for graphify's semantic pass.
- **`update`** — fast incremental re-extract of changed code files only, no LLM (`graphify update <project>`).
- **No extra text** — AST-only build (`graphify update <project>`). **No LLM API key required**, so this works on a fresh machine. Produces the full `graphify-out/` (graph.json, graph.html, GRAPH_REPORT.md). The default.
- **`extract`** — AST + semantic LLM pass (`graphify extract <project>`). Richer cross-file relationships, but **requires an LLM backend**: set `GEMINI_API_KEY` / `GOOGLE_API_KEY`, `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or `MOONSHOT_API_KEY` — or pass `--backend ollama` for a local LLM via [Ollama](https://ollama.com). Use after large refactors or when the AST-only graph misses important relationships.
- **`watch`** — long-running file watcher that auto-rebuilds on save (`graphify watch <project>`).
- **`cluster-only`** — rerun clustering on the existing `graph.json` without re-extraction (`graphify cluster-only <project>`).
- **`./subdir`** — scan a sub-directory instead of the project root (`graphify extract ./subdir`).
- **Trailing flags** (e.g. `--no-cluster`, `--force`, `--no-viz`) — passed straight through to the chosen subcommand. See `graphify --help` for the per-subcommand flag set.
- **`./subdir`** — scan a sub-directory instead of the project root (`graphify update ./subdir`).
- **Trailing flags** (e.g. `--force`, `--no-cluster`, `--no-viz`, `--backend ollama`) — passed straight through to the chosen subcommand. See `graphify --help` for the per-subcommand flag set.

See the [graphify CLI reference](https://graphify.net/graphify-cli-commands.html) for the full subcommand and flag list.

Expand All @@ -25,15 +25,15 @@ See the [graphify CLI reference](https://graphify.net/graphify-cli-commands.html
issue-flow build
```

To pick a non-default subcommand or pass flags, append them after the project dir, e.g. `issue-flow build update` or `issue-flow build cluster-only --no-viz`. Extra args are forwarded verbatim.
This runs `graphify update .` — no API key needed. To pick a different subcommand or pass flags, append them, e.g. `issue-flow build extract` (full LLM pass), `issue-flow build cluster-only --no-viz`, or `issue-flow build extract --backend ollama` (local LLM). To scan a project other than the current directory, use `issue-flow build -C <project_dir>`. Extra args are forwarded verbatim.

2. **Fallback: call `graphify` directly** if `issue-flow` is not on PATH:

```bash
graphify extract .
graphify update .
```

`graphify` is subcommand-based — `graphify .` on its own is **not** valid and will fail with `unknown command '.'`. Always pick a subcommand (`extract`, `update`, `watch`, `cluster-only`, …).
`graphify` is subcommand-based — `graphify .` on its own is **not** valid and will fail with `unknown command '.'`. Always pick a subcommand (`update`, `extract`, `watch`, `cluster-only`, …).

3. **Verify outputs.** After a successful run there should be a `graphify-out/` folder with at least `graph.html`, `GRAPH_REPORT.md`, and `graph.json`. Skim `GRAPH_REPORT.md` once to confirm the run picked up new modules or docs.

Expand All @@ -47,6 +47,14 @@ See the [graphify CLI reference](https://graphify.net/graphify-cli-commands.html

`graphifyy` (double-y) is the official PyPI package; the CLI is still `graphify`. After installing, re-run `issue-flow init` (or `issue-flow update`) so `graphify cursor install` registers the graphify Cursor skill.

5. **If `graphify extract` complains about a missing LLM API key**, the user picked the semantic-pass subcommand without configuring a backend. Suggest one of:

- Set an API key for one of the supported backends (`GEMINI_API_KEY` / `GOOGLE_API_KEY`, `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `MOONSHOT_API_KEY`) and re-run.
- Use a local LLM: `issue-flow build extract --backend ollama` (requires [Ollama](https://ollama.com) installed and a model pulled, e.g. `ollama pull qwen2.5-coder`).
- Drop the `extract` arg and use the default `issue-flow build` (AST-only `update`, no LLM).

Cursor's own LLM is **not** available to subprocesses, so graphify cannot reuse it.

## Constraints

- Do **not** run `/build` automatically from `/issue-start`, `/issue-close`, or `/iflow`. The user opts in.
Expand Down
2 changes: 1 addition & 1 deletion src/issue_flow/templates/commands/issue-close.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Other optional notes still apply: branch name, PR title, draft PR, skip issue do
- Run tests and any checks you rely on (e.g. `uv run pytest`).
- Skim the diff so the commit matches what you intend to ship.
- Confirm that any design decisions or good-practices that emerged from this issue are captured under `{{ issueflows_dir }}/{{ designs_folder }}/` before committing. If something is missing, add it now (short markdown: context, decision, alternatives, link back to the issue).
- **Graph freshness (optional).** If this change touched the project's structure (new modules, big refactor, removed files) and `graphify-out/` exists, suggest the user run `/build` (or `issue-flow build update` for a fast, no-LLM refresh) before pushing so teammates pull a current `GRAPH_REPORT.md`. Do not run `/build` automatically — it is opt-in. Skip this bullet entirely if `graphify-out/` is not present.
- **Graph freshness (optional).** If this change touched the project's structure (new modules, big refactor, removed files) and `graphify-out/` exists, suggest the user run `/build` (default: `graphify update`, AST-only, no LLM key needed) before pushing so teammates pull a current `GRAPH_REPORT.md`. Do not run `/build` automatically — it is opt-in. Skip this bullet entirely if `graphify-out/` is not present.

2. **Optional version bump** (only if the user asked for it in the command input)
- Read `{{ agent_dir }}/skills/issueflow-version-bump/SKILL.md` and follow it.
Expand Down
Loading
Loading