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.
Files changed (67) hide show
  1. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/PKG-INFO +20 -4
  2. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/README.md +19 -3
  3. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/pyproject.toml +1 -1
  4. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/agent.py +174 -18
  5. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/cli.py +74 -8
  6. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/config.py +73 -7
  7. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/dependencies.py +2 -6
  8. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/gitutils.py +29 -6
  9. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/graphify.py +5 -16
  10. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/init.py +46 -28
  11. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/modes.py +104 -14
  12. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/modes.toml +2 -0
  13. issue_flow-0.4.2a3/src/issue_flow/project.py +53 -0
  14. issue_flow-0.4.2a3/src/issue_flow/step_profiles.py +126 -0
  15. issue_flow-0.4.2a3/src/issue_flow/step_profiles.toml +22 -0
  16. issue_flow-0.4.2a3/src/issue_flow/templates/commands/iflow-archive.md.j2 +63 -0
  17. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-cleanup.md.j2 +4 -0
  18. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-close.md.j2 +5 -0
  19. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-fix.md.j2 +4 -0
  20. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-graphify.md.j2 +2 -0
  21. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-init.md.j2 +4 -0
  22. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-pause.md.j2 +4 -0
  23. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-pick.md.j2 +7 -0
  24. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-plan.md.j2 +4 -0
  25. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-start.md.j2 +5 -0
  26. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-status.md.j2 +4 -0
  27. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow-yolo.md.j2 +4 -0
  28. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/iflow.md.j2 +5 -1
  29. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/designs/python-quality-tools.md.j2 +6 -1
  30. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/docs/issue-workflow.md.j2 +32 -3
  31. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/docs/this-project.md.j2 +1 -0
  32. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/rules/_body.md.j2 +16 -0
  33. issue_flow-0.4.2a3/src/issue_flow/templates/rules/issueflow-rules.mdc.j2 +9 -0
  34. issue_flow-0.4.2a3/src/issue_flow/templates/skills/_model_directive.md.j2 +22 -0
  35. issue_flow-0.4.2a3/src/issue_flow/templates/skills/_resolve_project_root.md.j2 +19 -0
  36. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/caveman/SKILL.md.j2 +1 -1
  37. issue_flow-0.4.2a3/src/issue_flow/templates/skills/iflow_archive/SKILL.md.j2 +73 -0
  38. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_cleanup/SKILL.md.j2 +7 -10
  39. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_close/SKILL.md.j2 +13 -17
  40. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_comments/SKILL.md.j2 +6 -15
  41. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_fix/SKILL.md.j2 +6 -11
  42. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_graphify/SKILL.md.j2 +4 -11
  43. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_history_update/SKILL.md.j2 +4 -11
  44. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_iflow/SKILL.md.j2 +6 -11
  45. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_init/SKILL.md.j2 +12 -24
  46. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_pause/SKILL.md.j2 +5 -8
  47. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_pick/SKILL.md.j2 +11 -12
  48. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_plan/SKILL.md.j2 +5 -9
  49. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_start/SKILL.md.j2 +6 -8
  50. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_status/SKILL.md.j2 +5 -11
  51. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_version_bump/SKILL.md.j2 +4 -9
  52. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/iflow_yolo/SKILL.md.j2 +5 -9
  53. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templating.py +2 -0
  54. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/tracking.py +59 -3
  55. issue_flow-0.4.2a2/src/issue_flow/templates/rules/issueflow-rules.mdc.j2 +0 -8
  56. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/LICENSE +0 -0
  57. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/__init__.py +0 -0
  58. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/editors.py +0 -0
  59. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/py.typed +0 -0
  60. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/__init__.py +0 -0
  61. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/commands/__init__.py +0 -0
  62. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/docs/__init__.py +0 -0
  63. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/rules/AGENTS.md.j2 +0 -0
  64. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/rules/CLAUDE.md.j2 +0 -0
  65. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/rules/__init__.py +0 -0
  66. {issue_flow-0.4.2a2 → issue_flow-0.4.2a3}/src/issue_flow/templates/skills/grill_me/SKILL.md.j2 +0 -0
  67. {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.2a2
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,6 +1,6 @@
1
1
  [project]
2
2
  name = "issue-flow"
3
- version = "0.4.2a2"
3
+ version = "0.4.2a3"
4
4
  description = "Agents should behave. Let them follow the issue flow."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -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`` / ``sweep`` /
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 six ``[issueflow]`` keys issue-flow reads from ``config.toml``.
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 six keys issue-flow reads from config.toml — ``mode``,
340
- ``skill_level``, ``caveman_default``, ``grill_me_default``, ``label_flows``,
341
- ``yolo_label`` — taking each from its ``ISSUEFLOW_*`` env var when set, else
342
- the default. Other ``ISSUEFLOW_*`` settings are environment-only and are not
343
- written here. Existing files are left untouched unless ``--force`` is passed.
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