issue-flow 0.4.2a2__tar.gz → 0.4.2a3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/PKG-INFO +20 -4
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/README.md +19 -3
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/pyproject.toml +1 -1
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/agent.py +174 -18
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/cli.py +74 -8
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/config.py +73 -7
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/dependencies.py +2 -6
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/gitutils.py +29 -6
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/graphify.py +5 -16
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/init.py +46 -28
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/modes.py +104 -14
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/modes.toml +2 -0
- issue_flow-0.4.2a3/src/issue_flow/project.py +53 -0
- issue_flow-0.4.2a3/src/issue_flow/step_profiles.py +126 -0
- issue_flow-0.4.2a3/src/issue_flow/step_profiles.toml +22 -0
- issue_flow-0.4.2a3/src/issue_flow/templates/commands/iflow-archive.md.j2 +63 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-cleanup.md.j2 +4 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-close.md.j2 +5 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-fix.md.j2 +4 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-graphify.md.j2 +2 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-init.md.j2 +4 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-pause.md.j2 +4 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-pick.md.j2 +7 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-plan.md.j2 +4 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-start.md.j2 +5 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-status.md.j2 +4 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-yolo.md.j2 +4 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow.md.j2 +5 -1
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/designs/python-quality-tools.md.j2 +6 -1
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/docs/issue-workflow.md.j2 +32 -3
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/docs/this-project.md.j2 +1 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/rules/_body.md.j2 +16 -0
- issue_flow-0.4.2a3/src/issue_flow/templates/rules/issueflow-rules.mdc.j2 +9 -0
- issue_flow-0.4.2a3/src/issue_flow/templates/skills/_model_directive.md.j2 +22 -0
- issue_flow-0.4.2a3/src/issue_flow/templates/skills/_resolve_project_root.md.j2 +19 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/caveman/SKILL.md.j2 +1 -1
- issue_flow-0.4.2a3/src/issue_flow/templates/skills/iflow_archive/SKILL.md.j2 +73 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_cleanup/SKILL.md.j2 +7 -10
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_close/SKILL.md.j2 +13 -17
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_comments/SKILL.md.j2 +6 -15
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_fix/SKILL.md.j2 +6 -11
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_graphify/SKILL.md.j2 +4 -11
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_history_update/SKILL.md.j2 +4 -11
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_iflow/SKILL.md.j2 +6 -11
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_init/SKILL.md.j2 +12 -24
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_pause/SKILL.md.j2 +5 -8
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_pick/SKILL.md.j2 +11 -12
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_plan/SKILL.md.j2 +5 -9
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_start/SKILL.md.j2 +6 -8
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_status/SKILL.md.j2 +5 -11
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_version_bump/SKILL.md.j2 +4 -9
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_yolo/SKILL.md.j2 +5 -9
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templating.py +2 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/tracking.py +59 -3
- issue_flow-0.4.2a2/src/issue_flow/templates/rules/issueflow-rules.mdc.j2 +0 -8
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/LICENSE +0 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/__init__.py +0 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/editors.py +0 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/py.typed +0 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/__init__.py +0 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/__init__.py +0 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/docs/__init__.py +0 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/rules/AGENTS.md.j2 +0 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/rules/CLAUDE.md.j2 +0 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/rules/__init__.py +0 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/grill_me/SKILL.md.j2 +0 -0
- {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/tools/README.md.j2 +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: issue-flow
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2a3
|
|
4
4
|
Summary: Agents should behave. Let them follow the issue flow.
|
|
5
5
|
Keywords: cursor,ai,agents,issue-tracking,workflow
|
|
6
6
|
Author: jepegit
|
|
@@ -55,6 +55,7 @@ your-project/
|
|
|
55
55
|
iflow-yolo/SKILL.md
|
|
56
56
|
iflow-fix/SKILL.md
|
|
57
57
|
iflow-status/SKILL.md
|
|
58
|
+
iflow-archive/SKILL.md
|
|
58
59
|
iflow-version-bump/SKILL.md
|
|
59
60
|
iflow-history-update/SKILL.md
|
|
60
61
|
iflow-graphify/SKILL.md
|
|
@@ -83,8 +84,9 @@ Plus a few off-path commands:
|
|
|
83
84
|
- `/iflow-yolo` — all-in-one chain (`init → plan → start → close`) for small, low-risk issues, with up-front safeguards (refuses on the default branch, refuses with dirty unrelated changes, requires passing tests, single consolidated confirm).
|
|
84
85
|
- `/iflow-fix` — interactive iterative-fixes session: creates one GitHub issue + long-lived branch, then loops over many small fixes (each gets a short plan, implemented only on confirmation and recorded in `issue<N>_status.md`), ending with `/iflow-close`. Coexists with `/iflow-pick fix` (the one-shot setup). Off-path; never auto-dispatched.
|
|
85
86
|
- `/iflow-status` — **read-only** overview of where every issue stands: the local tracking state (focus / parked / solved) plus open GitHub issues cross-referenced against it. Pass `local` to skip the GitHub query. Changes nothing; off-path; never auto-dispatched.
|
|
87
|
+
- `/iflow-archive` — **condense the solved archive (destructive, gated)**: summarises selected `issue<N>_*` groups under `03-solved-issues/` into a dated `YYYY-MM-DD_archived_issues.md` file. The summary records the pre-archive git ref so every original file stays recoverable (`git show <ref>:<path>`). Deletes the source files only after one consolidated confirm. Default: archive all but the **5 most recent** solved groups; pass `keep <K>`, an explicit list of issue numbers, or `all`. Requires a **clean working tree**. Off-path; never auto-dispatched.
|
|
86
88
|
|
|
87
|
-
The **Agent Skills** under `.cursor/skills/` carry the workflows for on-demand use with `/iflow-pick`, `/iflow`, `/iflow-init`, `/iflow-plan`, `/iflow-start`, `/iflow-pause`, `/iflow-close`, `/iflow-cleanup`, `/iflow-yolo`, `/iflow-fix`, `/iflow-status`, `@iflow-version-bump` when you need only the bump steps, or `@iflow-history-update` when you need only the changelog update (see [Cursor Agent Skills](https://cursor.com/help/customization/skills)).
|
|
89
|
+
The **Agent Skills** under `.cursor/skills/` carry the workflows for on-demand use with `/iflow-pick`, `/iflow`, `/iflow-init`, `/iflow-plan`, `/iflow-start`, `/iflow-pause`, `/iflow-close`, `/iflow-cleanup`, `/iflow-yolo`, `/iflow-fix`, `/iflow-status`, `/iflow-archive`, `@iflow-version-bump` when you need only the bump steps, or `@iflow-history-update` when you need only the changelog update (see [Cursor Agent Skills](https://cursor.com/help/customization/skills)).
|
|
88
90
|
|
|
89
91
|
## Prerequisites
|
|
90
92
|
|
|
@@ -123,6 +125,16 @@ with `issue-flow init --skip-dep-check` (the same flag is available on
|
|
|
123
125
|
`issue-flow update`), and the prompt is also auto-skipped when stdin is not
|
|
124
126
|
a TTY (e.g. CI pipelines).
|
|
125
127
|
|
|
128
|
+
### Multi-root workspaces
|
|
129
|
+
|
|
130
|
+
When one Cursor workspace contains **several sibling repositories** (each with its
|
|
131
|
+
own `issue-flow init`), lifecycle commands must target the correct repo explicitly.
|
|
132
|
+
Use slash hints (`root:<path>`, `repo:<folder-name>`, `repo:owner/name`), or run
|
|
133
|
+
`issue-flow agent resolve [--from-file <active-file>] [--json]` before `git`/`gh`
|
|
134
|
+
calls. See `.issueflows/04-designs-and-guides/multi-repo-workspaces.md` in
|
|
135
|
+
scaffolded projects (or run `issue-flow update` to refresh scoped
|
|
136
|
+
`issueflow-rules.mdc` files).
|
|
137
|
+
|
|
126
138
|
### Optional: graphify integration
|
|
127
139
|
|
|
128
140
|
issue-flow has a lightweight integration with [graphify](https://iflow-graphify.net)
|
|
@@ -216,6 +228,7 @@ issue-flow status [PROJECT_DIR] [--local] [--json]
|
|
|
216
228
|
issue-flow agent state [PROJECT_DIR] [--json]
|
|
217
229
|
issue-flow agent preflight [PROJECT_DIR] [--json]
|
|
218
230
|
issue-flow agent sweep [PROJECT_DIR] [--except N] [--dry-run] [--json]
|
|
231
|
+
issue-flow agent archive N [N ...] [-C PROJECT_DIR] [--dry-run] [--json]
|
|
219
232
|
issue-flow agent capture N [-C PROJECT_DIR] [--repo OWNER/REPO] [--force] [--json]
|
|
220
233
|
```
|
|
221
234
|
|
|
@@ -291,13 +304,15 @@ project never installs `issue-flow`.
|
|
|
291
304
|
| `agent state` | Resolve the focus issue (branch-derived number wins, else the single current group), its lifecycle stage (`init`/`plan`/`start`/`close`), and the suggested next command. |
|
|
292
305
|
| `agent preflight` | Branch hygiene report: default branch, clean/dirty working tree, ahead/behind vs `origin/<default>`, and a stale-branch flag when the issue is already archived. Runs `git fetch --prune` first. |
|
|
293
306
|
| `agent sweep` | Archive `issue<N>_*` groups out of `01-current-issues/` to `03-solved-issues/` (Done) or `02-partly-solved-issues/` (not Done). Use `--except N` to keep the focus issue and `--dry-run` to preview. |
|
|
307
|
+
| `agent archive` | Mechanical deletion half of `/iflow-archive`: remove the chosen groups' files from `03-solved-issues/` and report the pre-archive HEAD sha (for the recovery ref in the summary file). Summarising into `YYYY-MM-DD_archived_issues.md` stays agent-side. Refuses when a requested issue has no solved group. `--dry-run` to preview. |
|
|
294
308
|
| `agent capture N` | Fetch GitHub issue `N` with `gh` and write `issue<N>_original.md` (the `## Original issue text` body). Prints the comments payload so the agent can triage them; comment triage stays agent-side. Use `--repo`, `--force`, `-C`. |
|
|
295
309
|
|
|
296
310
|
|
|
297
311
|
All `agent` commands accept `--json` and degrade gracefully: read-only commands
|
|
298
312
|
never hard-fail when `git`/`gh` is missing (they return partial data with a
|
|
299
313
|
note), while `agent capture` needs `gh` and exits non-zero with a hint when it
|
|
300
|
-
is unavailable or the fetch fails.
|
|
314
|
+
is unavailable or the fetch fails. `agent archive` needs a clean working tree
|
|
315
|
+
and refuses when any requested issue has no files under `03-solved-issues/`.
|
|
301
316
|
|
|
302
317
|
### When to use which
|
|
303
318
|
|
|
@@ -310,6 +325,7 @@ is unavailable or the fetch fails.
|
|
|
310
325
|
| Rebuild the graphify knowledge graph | `issue-flow graphify` |
|
|
311
326
|
| See where every issue stands (focus / parked / solved / GitHub) | `issue-flow status` |
|
|
312
327
|
| Let an agent resolve lifecycle state / sweep / capture deterministically | `issue-flow agent ...` |
|
|
328
|
+
| Condense old solved issues into a dated summary (recoverable via git) | `/iflow-archive` (summary is agent-written; `issue-flow agent archive …` for the delete step) |
|
|
313
329
|
|
|
314
330
|
|
|
315
331
|
## Modes
|
|
@@ -321,7 +337,7 @@ than you need. Two modes ship built in:
|
|
|
321
337
|
| Mode | What you get |
|
|
322
338
|
| --- | --- |
|
|
323
339
|
| `standard` (default) | The full workflow: planning, PRs, history, cleanup, graphify, and all helpers. |
|
|
324
|
-
| `simple` | A markdown-only lifecycle (capture, plan, implement, park, status). No PR/cleanup/yolo/fix/graphify automation. |
|
|
340
|
+
| `simple` | A markdown-only lifecycle (capture, plan, implement, park, status, archive). No PR/cleanup/yolo/fix/graphify automation. Includes `/iflow-archive` for condensing a large `03-solved-issues/` folder. |
|
|
325
341
|
|
|
326
342
|
```bash
|
|
327
343
|
issue-flow init --mode simple
|
|
@@ -30,6 +30,7 @@ your-project/
|
|
|
30
30
|
iflow-yolo/SKILL.md
|
|
31
31
|
iflow-fix/SKILL.md
|
|
32
32
|
iflow-status/SKILL.md
|
|
33
|
+
iflow-archive/SKILL.md
|
|
33
34
|
iflow-version-bump/SKILL.md
|
|
34
35
|
iflow-history-update/SKILL.md
|
|
35
36
|
iflow-graphify/SKILL.md
|
|
@@ -58,8 +59,9 @@ Plus a few off-path commands:
|
|
|
58
59
|
- `/iflow-yolo` — all-in-one chain (`init → plan → start → close`) for small, low-risk issues, with up-front safeguards (refuses on the default branch, refuses with dirty unrelated changes, requires passing tests, single consolidated confirm).
|
|
59
60
|
- `/iflow-fix` — interactive iterative-fixes session: creates one GitHub issue + long-lived branch, then loops over many small fixes (each gets a short plan, implemented only on confirmation and recorded in `issue<N>_status.md`), ending with `/iflow-close`. Coexists with `/iflow-pick fix` (the one-shot setup). Off-path; never auto-dispatched.
|
|
60
61
|
- `/iflow-status` — **read-only** overview of where every issue stands: the local tracking state (focus / parked / solved) plus open GitHub issues cross-referenced against it. Pass `local` to skip the GitHub query. Changes nothing; off-path; never auto-dispatched.
|
|
62
|
+
- `/iflow-archive` — **condense the solved archive (destructive, gated)**: summarises selected `issue<N>_*` groups under `03-solved-issues/` into a dated `YYYY-MM-DD_archived_issues.md` file. The summary records the pre-archive git ref so every original file stays recoverable (`git show <ref>:<path>`). Deletes the source files only after one consolidated confirm. Default: archive all but the **5 most recent** solved groups; pass `keep <K>`, an explicit list of issue numbers, or `all`. Requires a **clean working tree**. Off-path; never auto-dispatched.
|
|
61
63
|
|
|
62
|
-
The **Agent Skills** under `.cursor/skills/` carry the workflows for on-demand use with `/iflow-pick`, `/iflow`, `/iflow-init`, `/iflow-plan`, `/iflow-start`, `/iflow-pause`, `/iflow-close`, `/iflow-cleanup`, `/iflow-yolo`, `/iflow-fix`, `/iflow-status`, `@iflow-version-bump` when you need only the bump steps, or `@iflow-history-update` when you need only the changelog update (see [Cursor Agent Skills](https://cursor.com/help/customization/skills)).
|
|
64
|
+
The **Agent Skills** under `.cursor/skills/` carry the workflows for on-demand use with `/iflow-pick`, `/iflow`, `/iflow-init`, `/iflow-plan`, `/iflow-start`, `/iflow-pause`, `/iflow-close`, `/iflow-cleanup`, `/iflow-yolo`, `/iflow-fix`, `/iflow-status`, `/iflow-archive`, `@iflow-version-bump` when you need only the bump steps, or `@iflow-history-update` when you need only the changelog update (see [Cursor Agent Skills](https://cursor.com/help/customization/skills)).
|
|
63
65
|
|
|
64
66
|
## Prerequisites
|
|
65
67
|
|
|
@@ -98,6 +100,16 @@ with `issue-flow init --skip-dep-check` (the same flag is available on
|
|
|
98
100
|
`issue-flow update`), and the prompt is also auto-skipped when stdin is not
|
|
99
101
|
a TTY (e.g. CI pipelines).
|
|
100
102
|
|
|
103
|
+
### Multi-root workspaces
|
|
104
|
+
|
|
105
|
+
When one Cursor workspace contains **several sibling repositories** (each with its
|
|
106
|
+
own `issue-flow init`), lifecycle commands must target the correct repo explicitly.
|
|
107
|
+
Use slash hints (`root:<path>`, `repo:<folder-name>`, `repo:owner/name`), or run
|
|
108
|
+
`issue-flow agent resolve [--from-file <active-file>] [--json]` before `git`/`gh`
|
|
109
|
+
calls. See `.issueflows/04-designs-and-guides/multi-repo-workspaces.md` in
|
|
110
|
+
scaffolded projects (or run `issue-flow update` to refresh scoped
|
|
111
|
+
`issueflow-rules.mdc` files).
|
|
112
|
+
|
|
101
113
|
### Optional: graphify integration
|
|
102
114
|
|
|
103
115
|
issue-flow has a lightweight integration with [graphify](https://iflow-graphify.net)
|
|
@@ -191,6 +203,7 @@ issue-flow status [PROJECT_DIR] [--local] [--json]
|
|
|
191
203
|
issue-flow agent state [PROJECT_DIR] [--json]
|
|
192
204
|
issue-flow agent preflight [PROJECT_DIR] [--json]
|
|
193
205
|
issue-flow agent sweep [PROJECT_DIR] [--except N] [--dry-run] [--json]
|
|
206
|
+
issue-flow agent archive N [N ...] [-C PROJECT_DIR] [--dry-run] [--json]
|
|
194
207
|
issue-flow agent capture N [-C PROJECT_DIR] [--repo OWNER/REPO] [--force] [--json]
|
|
195
208
|
```
|
|
196
209
|
|
|
@@ -266,13 +279,15 @@ project never installs `issue-flow`.
|
|
|
266
279
|
| `agent state` | Resolve the focus issue (branch-derived number wins, else the single current group), its lifecycle stage (`init`/`plan`/`start`/`close`), and the suggested next command. |
|
|
267
280
|
| `agent preflight` | Branch hygiene report: default branch, clean/dirty working tree, ahead/behind vs `origin/<default>`, and a stale-branch flag when the issue is already archived. Runs `git fetch --prune` first. |
|
|
268
281
|
| `agent sweep` | Archive `issue<N>_*` groups out of `01-current-issues/` to `03-solved-issues/` (Done) or `02-partly-solved-issues/` (not Done). Use `--except N` to keep the focus issue and `--dry-run` to preview. |
|
|
282
|
+
| `agent archive` | Mechanical deletion half of `/iflow-archive`: remove the chosen groups' files from `03-solved-issues/` and report the pre-archive HEAD sha (for the recovery ref in the summary file). Summarising into `YYYY-MM-DD_archived_issues.md` stays agent-side. Refuses when a requested issue has no solved group. `--dry-run` to preview. |
|
|
269
283
|
| `agent capture N` | Fetch GitHub issue `N` with `gh` and write `issue<N>_original.md` (the `## Original issue text` body). Prints the comments payload so the agent can triage them; comment triage stays agent-side. Use `--repo`, `--force`, `-C`. |
|
|
270
284
|
|
|
271
285
|
|
|
272
286
|
All `agent` commands accept `--json` and degrade gracefully: read-only commands
|
|
273
287
|
never hard-fail when `git`/`gh` is missing (they return partial data with a
|
|
274
288
|
note), while `agent capture` needs `gh` and exits non-zero with a hint when it
|
|
275
|
-
is unavailable or the fetch fails.
|
|
289
|
+
is unavailable or the fetch fails. `agent archive` needs a clean working tree
|
|
290
|
+
and refuses when any requested issue has no files under `03-solved-issues/`.
|
|
276
291
|
|
|
277
292
|
### When to use which
|
|
278
293
|
|
|
@@ -285,6 +300,7 @@ is unavailable or the fetch fails.
|
|
|
285
300
|
| Rebuild the graphify knowledge graph | `issue-flow graphify` |
|
|
286
301
|
| See where every issue stands (focus / parked / solved / GitHub) | `issue-flow status` |
|
|
287
302
|
| Let an agent resolve lifecycle state / sweep / capture deterministically | `issue-flow agent ...` |
|
|
303
|
+
| Condense old solved issues into a dated summary (recoverable via git) | `/iflow-archive` (summary is agent-written; `issue-flow agent archive …` for the delete step) |
|
|
288
304
|
|
|
289
305
|
|
|
290
306
|
## Modes
|
|
@@ -296,7 +312,7 @@ than you need. Two modes ship built in:
|
|
|
296
312
|
| Mode | What you get |
|
|
297
313
|
| --- | --- |
|
|
298
314
|
| `standard` (default) | The full workflow: planning, PRs, history, cleanup, graphify, and all helpers. |
|
|
299
|
-
| `simple` | A markdown-only lifecycle (capture, plan, implement, park, status). No PR/cleanup/yolo/fix/graphify automation. |
|
|
315
|
+
| `simple` | A markdown-only lifecycle (capture, plan, implement, park, status, archive). No PR/cleanup/yolo/fix/graphify automation. Includes `/iflow-archive` for condensing a large `03-solved-issues/` folder. |
|
|
300
316
|
|
|
301
317
|
```bash
|
|
302
318
|
issue-flow init --mode simple
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Orchestrators behind the agent-facing CLI surface.
|
|
2
2
|
|
|
3
3
|
These functions back ``issue-flow status`` (human-facing, top-level) and the
|
|
4
|
-
``issue-flow agent ...`` sub-commands (``state`` / ``preflight`` / ``
|
|
5
|
-
``capture``) that exist so AI agents can ask the tool for a deterministic
|
|
4
|
+
``issue-flow agent ...`` sub-commands (``state`` / ``preflight`` / ``resolve`` /
|
|
5
|
+
``sweep`` / ``capture``) that exist so AI agents can ask the tool for a deterministic
|
|
6
6
|
answer instead of re-deriving lifecycle state by hand on every run.
|
|
7
7
|
|
|
8
8
|
Each ``run_*`` returns a process exit code and emits either a short human
|
|
@@ -21,7 +21,7 @@ from typing import Any
|
|
|
21
21
|
from rich.console import Console
|
|
22
22
|
from rich.markup import escape
|
|
23
23
|
|
|
24
|
-
from issue_flow import gitutils, modes, tracking
|
|
24
|
+
from issue_flow import gitutils, modes, project, tracking
|
|
25
25
|
from issue_flow.config import Settings
|
|
26
26
|
|
|
27
27
|
|
|
@@ -173,6 +173,83 @@ def run_preflight(project_root: Path, console: Console, as_json: bool) -> int:
|
|
|
173
173
|
return 0
|
|
174
174
|
|
|
175
175
|
|
|
176
|
+
# ---------------------------------------------------------------------------
|
|
177
|
+
# agent resolve
|
|
178
|
+
# ---------------------------------------------------------------------------
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def run_resolve(
|
|
182
|
+
project_root: Path,
|
|
183
|
+
console: Console,
|
|
184
|
+
from_file: Path | None,
|
|
185
|
+
as_json: bool,
|
|
186
|
+
) -> int:
|
|
187
|
+
"""Resolve the issue-flow project root, GitHub repo slug, and branch context."""
|
|
188
|
+
settings = Settings()
|
|
189
|
+
start = from_file if from_file is not None else project_root
|
|
190
|
+
resolved = project.find_project_root(
|
|
191
|
+
start,
|
|
192
|
+
issueflows_dir=settings.issueflows_dir,
|
|
193
|
+
current_issues_folder=settings.current_issues_folder,
|
|
194
|
+
)
|
|
195
|
+
if resolved is None and from_file is not None:
|
|
196
|
+
resolved = project.find_project_root(
|
|
197
|
+
project_root,
|
|
198
|
+
issueflows_dir=settings.issueflows_dir,
|
|
199
|
+
current_issues_folder=settings.current_issues_folder,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
repo: str | None = None
|
|
203
|
+
branch: str | None = None
|
|
204
|
+
default_branch: str | None = None
|
|
205
|
+
sibling_roots: list[str] = []
|
|
206
|
+
|
|
207
|
+
if resolved is not None:
|
|
208
|
+
owner_repo = gitutils.remote_owner_repo(resolved)
|
|
209
|
+
if owner_repo:
|
|
210
|
+
repo = f"{owner_repo[0]}/{owner_repo[1]}"
|
|
211
|
+
if gitutils.git_available():
|
|
212
|
+
branch = gitutils.current_branch(resolved)
|
|
213
|
+
default_branch = gitutils.default_branch(resolved)
|
|
214
|
+
sibling_roots = project.list_scaffolded_siblings(
|
|
215
|
+
resolved, issueflows_dir=settings.issueflows_dir
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
payload: dict[str, Any] = {
|
|
219
|
+
"project_root": str(resolved) if resolved else None,
|
|
220
|
+
"repo": repo,
|
|
221
|
+
"branch": branch,
|
|
222
|
+
"default_branch": default_branch,
|
|
223
|
+
"issueflows_dir": settings.issueflows_dir,
|
|
224
|
+
"sibling_roots": sibling_roots,
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if as_json:
|
|
228
|
+
_emit_json(console, payload)
|
|
229
|
+
return 0 if resolved is not None else 1
|
|
230
|
+
|
|
231
|
+
if resolved is None:
|
|
232
|
+
console.print(
|
|
233
|
+
"[red]No issue-flow scaffold found[/red] walking up from "
|
|
234
|
+
f"{start.resolve()}."
|
|
235
|
+
)
|
|
236
|
+
return 1
|
|
237
|
+
|
|
238
|
+
console.print(f"[bold]Project root[/bold]: {resolved}")
|
|
239
|
+
if repo:
|
|
240
|
+
console.print(f"[bold]Repo[/bold]: {repo}")
|
|
241
|
+
if branch:
|
|
242
|
+
console.print(f"[bold]Branch[/bold]: {branch}")
|
|
243
|
+
if default_branch:
|
|
244
|
+
console.print(f"[bold]Default branch[/bold]: {default_branch}")
|
|
245
|
+
if sibling_roots:
|
|
246
|
+
console.print(
|
|
247
|
+
f"[bold]Sibling scaffolds[/bold]: {len(sibling_roots)} "
|
|
248
|
+
"(run lifecycle commands once per repo)"
|
|
249
|
+
)
|
|
250
|
+
return 0
|
|
251
|
+
|
|
252
|
+
|
|
176
253
|
# ---------------------------------------------------------------------------
|
|
177
254
|
# agent sweep
|
|
178
255
|
# ---------------------------------------------------------------------------
|
|
@@ -226,6 +303,89 @@ def run_sweep(
|
|
|
226
303
|
return 0
|
|
227
304
|
|
|
228
305
|
|
|
306
|
+
# ---------------------------------------------------------------------------
|
|
307
|
+
# agent archive
|
|
308
|
+
# ---------------------------------------------------------------------------
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def run_archive(
|
|
312
|
+
project_root: Path,
|
|
313
|
+
console: Console,
|
|
314
|
+
issues: list[int],
|
|
315
|
+
dry_run: bool,
|
|
316
|
+
as_json: bool,
|
|
317
|
+
) -> int:
|
|
318
|
+
"""Delete the named solved issue groups (the mechanical half of archiving).
|
|
319
|
+
|
|
320
|
+
Summarising the issues into the dated archive file is interpretive and
|
|
321
|
+
stays agent-side; this command only removes the ``issue<N>_*`` files from
|
|
322
|
+
the solved folder and reports the pre-archive HEAD sha so the summaries
|
|
323
|
+
can record a recovery point. Refuses (exit 1) when any requested issue has
|
|
324
|
+
no group in the solved folder, so a typo never silently archives less
|
|
325
|
+
than the user confirmed.
|
|
326
|
+
"""
|
|
327
|
+
settings = Settings()
|
|
328
|
+
folders = _folders(project_root, settings)
|
|
329
|
+
|
|
330
|
+
moves, missing = tracking.plan_archive(folders["solved"], issues)
|
|
331
|
+
sha = gitutils.head_sha(project_root)
|
|
332
|
+
|
|
333
|
+
if missing:
|
|
334
|
+
msg = (
|
|
335
|
+
"no files found in "
|
|
336
|
+
f"{settings.solved_folder} for issue(s) "
|
|
337
|
+
f"{', '.join(f'#{n}' for n in missing)}; nothing was archived."
|
|
338
|
+
)
|
|
339
|
+
if as_json:
|
|
340
|
+
_emit_json(
|
|
341
|
+
console,
|
|
342
|
+
{"archived": False, "missing": missing, "error": msg},
|
|
343
|
+
)
|
|
344
|
+
else:
|
|
345
|
+
console.print(f"[red]error[/red] {msg}")
|
|
346
|
+
return 1
|
|
347
|
+
|
|
348
|
+
removed: list[Path] = []
|
|
349
|
+
if not dry_run and moves:
|
|
350
|
+
removed = tracking.apply_archive(moves)
|
|
351
|
+
|
|
352
|
+
payload = {
|
|
353
|
+
"dry_run": dry_run,
|
|
354
|
+
"head_sha": sha,
|
|
355
|
+
"issues": [
|
|
356
|
+
{
|
|
357
|
+
"issue": m.number,
|
|
358
|
+
"title": m.title,
|
|
359
|
+
"files": [p.name for p in m.files],
|
|
360
|
+
}
|
|
361
|
+
for m in moves
|
|
362
|
+
],
|
|
363
|
+
"removed": [p.name for p in removed],
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if as_json:
|
|
367
|
+
_emit_json(console, payload)
|
|
368
|
+
return 0
|
|
369
|
+
|
|
370
|
+
if not moves:
|
|
371
|
+
console.print("[dim]Nothing to archive.[/dim]")
|
|
372
|
+
return 0
|
|
373
|
+
if sha:
|
|
374
|
+
console.print(f"Pre-archive HEAD: [bold]{sha}[/bold]")
|
|
375
|
+
verb = "Would remove" if dry_run else "Removed"
|
|
376
|
+
for m in moves:
|
|
377
|
+
title = f" — {escape(m.title)}" if m.title else ""
|
|
378
|
+
console.print(
|
|
379
|
+
f" {verb} #{m.number}{title}: {', '.join(p.name for p in m.files)}"
|
|
380
|
+
)
|
|
381
|
+
if not dry_run:
|
|
382
|
+
console.print(
|
|
383
|
+
" [dim]Write the summaries into the dated archive file and "
|
|
384
|
+
"commit so the pre-archive sha stays meaningful.[/dim]"
|
|
385
|
+
)
|
|
386
|
+
return 0
|
|
387
|
+
|
|
388
|
+
|
|
229
389
|
# ---------------------------------------------------------------------------
|
|
230
390
|
# agent capture
|
|
231
391
|
# ---------------------------------------------------------------------------
|
|
@@ -289,9 +449,7 @@ def run_capture(
|
|
|
289
449
|
if target.exists() and not force:
|
|
290
450
|
msg = f"{target} already exists; pass --force to overwrite."
|
|
291
451
|
if as_json:
|
|
292
|
-
_emit_json(
|
|
293
|
-
console, {"written": False, "path": str(target), "error": msg}
|
|
294
|
-
)
|
|
452
|
+
_emit_json(console, {"written": False, "path": str(target), "error": msg})
|
|
295
453
|
else:
|
|
296
454
|
console.print(f"[yellow]exists[/yellow] {msg}")
|
|
297
455
|
return 1
|
|
@@ -352,6 +510,11 @@ def _print_config_guide(console: Console, cfg_path: Path) -> None:
|
|
|
352
510
|
"labels pick the flow (e.g. a 'yolo' label runs /iflow-yolo); re-run "
|
|
353
511
|
"'issue-flow update' so the commands re-render.[/dim]"
|
|
354
512
|
)
|
|
513
|
+
console.print(
|
|
514
|
+
" [dim]- [bold]step_directives[/bold] / [bold]model_label_flows[/bold]: "
|
|
515
|
+
"bake MODEL & EXECUTION DIRECTIVE sections into lifecycle skills; optional "
|
|
516
|
+
"label hints during /iflow-pick; re-run 'issue-flow update' after changing.[/dim]"
|
|
517
|
+
)
|
|
355
518
|
console.print(
|
|
356
519
|
" [dim]Other ISSUEFLOW_* settings are environment-only (set them in "
|
|
357
520
|
".env), not in config.toml.[/dim]"
|
|
@@ -363,7 +526,7 @@ def run_config_add(
|
|
|
363
526
|
) -> int:
|
|
364
527
|
"""Create ``.issueflows/config.toml`` seeded from ``.env`` or defaults.
|
|
365
528
|
|
|
366
|
-
Writes the
|
|
529
|
+
Writes the ``[issueflow]`` keys issue-flow reads from ``config.toml``.
|
|
367
530
|
Refuses to clobber an existing file unless ``force`` is set (which upserts
|
|
368
531
|
those keys while preserving other content).
|
|
369
532
|
"""
|
|
@@ -410,9 +573,7 @@ def run_config_add(
|
|
|
410
573
|
# ---------------------------------------------------------------------------
|
|
411
574
|
|
|
412
575
|
|
|
413
|
-
def run_status(
|
|
414
|
-
project_root: Path, console: Console, local: bool, as_json: bool
|
|
415
|
-
) -> int:
|
|
576
|
+
def run_status(project_root: Path, console: Console, local: bool, as_json: bool) -> int:
|
|
416
577
|
"""Read-only overview: focus stage, parked, solved, optional GitHub cross-ref."""
|
|
417
578
|
settings = Settings()
|
|
418
579
|
folders = _folders(project_root, settings)
|
|
@@ -432,8 +593,7 @@ def run_status(
|
|
|
432
593
|
|
|
433
594
|
parked_groups = tracking.group_issue_files(folders["partly"])
|
|
434
595
|
parked = [
|
|
435
|
-
{"number": n, "title": g.title()}
|
|
436
|
-
for n, g in sorted(parked_groups.items())
|
|
596
|
+
{"number": n, "title": g.title()} for n, g in sorted(parked_groups.items())
|
|
437
597
|
]
|
|
438
598
|
solved_numbers = sorted(tracking.group_issue_files(folders["solved"]))
|
|
439
599
|
|
|
@@ -459,9 +619,7 @@ def run_status(
|
|
|
459
619
|
return 0
|
|
460
620
|
|
|
461
621
|
|
|
462
|
-
def _github_section(
|
|
463
|
-
project_root: Path, folders: dict[str, Path]
|
|
464
|
-
) -> dict[str, Any]:
|
|
622
|
+
def _github_section(project_root: Path, folders: dict[str, Path]) -> dict[str, Any]:
|
|
465
623
|
"""Cross-reference open GitHub issues against local tracking folders."""
|
|
466
624
|
if not gitutils.gh_available():
|
|
467
625
|
return {"available": False, "reason": "gh not on PATH"}
|
|
@@ -534,9 +692,7 @@ def _render_status_text(
|
|
|
534
692
|
if github is None:
|
|
535
693
|
pass
|
|
536
694
|
elif not github.get("available"):
|
|
537
|
-
console.print(
|
|
538
|
-
f"[bold]GitHub[/bold]: unavailable ({github.get('reason')})"
|
|
539
|
-
)
|
|
695
|
+
console.print(f"[bold]GitHub[/bold]: unavailable ({github.get('reason')})")
|
|
540
696
|
else:
|
|
541
697
|
console.print(
|
|
542
698
|
f"[bold]GitHub[/bold]: {github['open_count']} open "
|
|
@@ -162,9 +162,7 @@ def update(
|
|
|
162
162
|
"""Refresh packaged editor commands, rules, and workflow doc from this package."""
|
|
163
163
|
from issue_flow.init import run_update
|
|
164
164
|
|
|
165
|
-
run_update(
|
|
166
|
-
project_root=project_dir, skip_dep_check=skip_dep_check, editors=editor
|
|
167
|
-
)
|
|
165
|
+
run_update(project_root=project_dir, skip_dep_check=skip_dep_check, editors=editor)
|
|
168
166
|
|
|
169
167
|
|
|
170
168
|
@app.command(
|
|
@@ -257,6 +255,36 @@ def agent_preflight(
|
|
|
257
255
|
raise typer.Exit(code=run_preflight(project_dir, _console, json_output))
|
|
258
256
|
|
|
259
257
|
|
|
258
|
+
@agent_app.command("resolve")
|
|
259
|
+
def agent_resolve(
|
|
260
|
+
project_dir: Path = typer.Option(
|
|
261
|
+
Path("."),
|
|
262
|
+
"--project-dir",
|
|
263
|
+
"-C",
|
|
264
|
+
help="Directory to start the scaffold walk from (defaults to cwd).",
|
|
265
|
+
exists=True,
|
|
266
|
+
file_okay=True,
|
|
267
|
+
resolve_path=True,
|
|
268
|
+
),
|
|
269
|
+
from_file: Path | None = typer.Option(
|
|
270
|
+
None,
|
|
271
|
+
"--from-file",
|
|
272
|
+
help="Start the walk from this file's directory (e.g. the active editor file).",
|
|
273
|
+
exists=True,
|
|
274
|
+
file_okay=True,
|
|
275
|
+
dir_okay=False,
|
|
276
|
+
resolve_path=True,
|
|
277
|
+
),
|
|
278
|
+
json_output: bool = typer.Option(
|
|
279
|
+
False, "--json", help="Emit a machine-readable JSON object."
|
|
280
|
+
),
|
|
281
|
+
) -> None:
|
|
282
|
+
"""Resolve project root, owner/repo, branch, and sibling scaffolds."""
|
|
283
|
+
from issue_flow.agent import run_resolve
|
|
284
|
+
|
|
285
|
+
raise typer.Exit(code=run_resolve(project_dir, _console, from_file, json_output))
|
|
286
|
+
|
|
287
|
+
|
|
260
288
|
@agent_app.command("sweep")
|
|
261
289
|
def agent_sweep(
|
|
262
290
|
project_dir: Path = _PROJECT_DIR_ARGUMENT,
|
|
@@ -281,6 +309,42 @@ def agent_sweep(
|
|
|
281
309
|
)
|
|
282
310
|
|
|
283
311
|
|
|
312
|
+
@agent_app.command("archive")
|
|
313
|
+
def agent_archive(
|
|
314
|
+
issues: list[int] = typer.Argument(
|
|
315
|
+
...,
|
|
316
|
+
help="Solved issue number(s) whose files should be removed.",
|
|
317
|
+
),
|
|
318
|
+
project_dir: Path = typer.Option(
|
|
319
|
+
Path("."),
|
|
320
|
+
"--project-dir",
|
|
321
|
+
"-C",
|
|
322
|
+
help="Project root directory (defaults to current directory).",
|
|
323
|
+
exists=True,
|
|
324
|
+
file_okay=False,
|
|
325
|
+
resolve_path=True,
|
|
326
|
+
),
|
|
327
|
+
dry_run: bool = typer.Option(
|
|
328
|
+
False, "--dry-run", help="Show planned removals without touching files."
|
|
329
|
+
),
|
|
330
|
+
json_output: bool = typer.Option(
|
|
331
|
+
False, "--json", help="Emit a machine-readable JSON object."
|
|
332
|
+
),
|
|
333
|
+
) -> None:
|
|
334
|
+
"""Remove solved issue<N>_* files (the mechanical half of /iflow-archive).
|
|
335
|
+
|
|
336
|
+
Reports the pre-archive HEAD sha so the agent-written summary file can
|
|
337
|
+
record a recovery point (``git show <sha>:<path>``). Summarising the
|
|
338
|
+
issues into the dated archive file is left to the agent. Refuses when a
|
|
339
|
+
requested issue has no files in the solved folder.
|
|
340
|
+
"""
|
|
341
|
+
from issue_flow.agent import run_archive
|
|
342
|
+
|
|
343
|
+
raise typer.Exit(
|
|
344
|
+
code=run_archive(project_dir, _console, issues, dry_run, json_output)
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
|
|
284
348
|
@agent_app.command("capture")
|
|
285
349
|
def agent_capture(
|
|
286
350
|
number: int = typer.Argument(..., help="GitHub issue number to capture."),
|
|
@@ -336,11 +400,13 @@ def config_add(
|
|
|
336
400
|
) -> None:
|
|
337
401
|
"""Create .issueflows/config.toml, seeded from .env (or issue-flow defaults).
|
|
338
402
|
|
|
339
|
-
Writes the
|
|
340
|
-
``skill_level``, ``caveman_default``, ``grill_me_default``,
|
|
341
|
-
``yolo_label``
|
|
342
|
-
|
|
343
|
-
|
|
403
|
+
Writes the ``[issueflow]`` keys issue-flow reads from ``config.toml`` —
|
|
404
|
+
``mode``, ``skill_level``, ``caveman_default``, ``grill_me_default``,
|
|
405
|
+
``label_flows``, ``yolo_label``, ``step_directives``, ``model_label_flows``,
|
|
406
|
+
``deep_model_label``, ``fast_model_label`` — taking each from its
|
|
407
|
+
``ISSUEFLOW_*`` env var when set, else the default. Other ``ISSUEFLOW_*``
|
|
408
|
+
settings are environment-only and are not written here. Existing files are
|
|
409
|
+
left untouched unless ``--force`` is passed.
|
|
344
410
|
"""
|
|
345
411
|
from issue_flow.agent import run_config_add
|
|
346
412
|
|