issue-flow 0.1.0__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.1.0/LICENSE +21 -0
- issue_flow-0.1.0/PKG-INFO +147 -0
- issue_flow-0.1.0/README.md +105 -0
- issue_flow-0.1.0/pyproject.toml +41 -0
- issue_flow-0.1.0/src/issue_flow/__init__.py +3 -0
- issue_flow-0.1.0/src/issue_flow/cli.py +44 -0
- issue_flow-0.1.0/src/issue_flow/config.py +76 -0
- issue_flow-0.1.0/src/issue_flow/init.py +83 -0
- issue_flow-0.1.0/src/issue_flow/py.typed +0 -0
- issue_flow-0.1.0/src/issue_flow/templates/__init__.py +0 -0
- issue_flow-0.1.0/src/issue_flow/templates/commands/__init__.py +0 -0
- issue_flow-0.1.0/src/issue_flow/templates/commands/issue-close.md.j2 +36 -0
- issue_flow-0.1.0/src/issue_flow/templates/commands/issue-init.md.j2 +81 -0
- issue_flow-0.1.0/src/issue_flow/templates/commands/issue-start.md.j2 +16 -0
- issue_flow-0.1.0/src/issue_flow/templates/docs/__init__.py +0 -0
- issue_flow-0.1.0/src/issue_flow/templates/docs/cursor-issue-workflow.md.j2 +83 -0
- issue_flow-0.1.0/src/issue_flow/templates/rules/__init__.py +0 -0
- issue_flow-0.1.0/src/issue_flow/templates/rules/issueflow-rules.mdc.j2 +86 -0
- issue_flow-0.1.0/src/issue_flow/templating.py +79 -0
issue_flow-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jan Petter Maehlen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: issue-flow
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Agents should behave. Let them follow the issue flow.
|
|
5
|
+
Keywords: cursor,ai,agents,issue-tracking,workflow
|
|
6
|
+
Author: jepegit
|
|
7
|
+
Author-email: jepegit <jepe@ife.no>
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2026 Jan Petter Maehlen
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
Classifier: Development Status :: 3 - Alpha
|
|
30
|
+
Classifier: Intended Audience :: Developers
|
|
31
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
33
|
+
Requires-Dist: jinja2>=3.1.6
|
|
34
|
+
Requires-Dist: python-dotenv>=1.2.2
|
|
35
|
+
Requires-Dist: rich>=14.3.3
|
|
36
|
+
Requires-Dist: typer>=0.24.1
|
|
37
|
+
Requires-Python: >=3.13
|
|
38
|
+
Project-URL: Homepage, https://github.com/jepegit/issue-flow
|
|
39
|
+
Project-URL: Repository, https://github.com/jepegit/issue-flow
|
|
40
|
+
Project-URL: Issues, https://github.com/jepegit/issue-flow/issues
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
|
|
43
|
+
# issue-flow
|
|
44
|
+
|
|
45
|
+
Agents should behave. Let them follow the issue flow.
|
|
46
|
+
|
|
47
|
+
**issue-flow** scaffolds a lightweight issue-tracking workflow into your project so that Cursor AI agents can pick up GitHub issues, plan work, and land PRs in a consistent way.
|
|
48
|
+
|
|
49
|
+
## What it does
|
|
50
|
+
|
|
51
|
+
Running `issue-flow init` in your project root creates:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
your-project/
|
|
55
|
+
.issueflows/
|
|
56
|
+
00-tools/ # Helper scripts for agents
|
|
57
|
+
01-current-issues/ # Active issue markdown files
|
|
58
|
+
02-partly-solved-issues/ # Parked / in-progress issues
|
|
59
|
+
03-solved-issues/ # Completed issues archive
|
|
60
|
+
.cursor/
|
|
61
|
+
commands/
|
|
62
|
+
issue-init.md # /issue-init — fetch a GitHub issue locally
|
|
63
|
+
issue-start.md # /issue-start — plan and implement
|
|
64
|
+
issue-close.md # /issue-close — test, commit, push, PR
|
|
65
|
+
rules/
|
|
66
|
+
issueflow-rules.mdc # Always-on Cursor rule for the workflow
|
|
67
|
+
docs/
|
|
68
|
+
cursor-issue-workflow.md # Human-readable overview of the workflow
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The three Cursor slash commands give agents a repeatable flow:
|
|
72
|
+
|
|
73
|
+
1. `/issue-init 42` — pulls GitHub issue #42 into `.issueflows/01-current-issues/` and archives older issues.
|
|
74
|
+
2. `/issue-start` — reads the issue file, plans, and implements.
|
|
75
|
+
3. `/issue-close` — runs tests, updates status files, commits, pushes, and opens a PR.
|
|
76
|
+
|
|
77
|
+
## Installation
|
|
78
|
+
|
|
79
|
+
Requires Python 3.13+ and [uv](https://docs.astral.sh/uv/).
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
uv tool install issue-flow
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Or add it as a dev dependency to your project:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
uv add --dev issue-flow
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Quick start
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
cd your-project
|
|
95
|
+
issue-flow init
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
That's it. Open the project in Cursor and use `/issue-init`, `/issue-start`, `/issue-close`.
|
|
99
|
+
|
|
100
|
+
## Usage
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
issue-flow init [PROJECT_DIR] [--force]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
| Argument / Option | Description |
|
|
107
|
+
|---|---|
|
|
108
|
+
| `PROJECT_DIR` | Project root directory. Defaults to `.` (current directory). |
|
|
109
|
+
| `--force`, `-f` | Overwrite existing files instead of skipping them. |
|
|
110
|
+
|
|
111
|
+
Running `init` a second time is safe — existing files are skipped unless `--force` is passed.
|
|
112
|
+
|
|
113
|
+
## Configuration
|
|
114
|
+
|
|
115
|
+
issue-flow reads a `.env` file from the project root (via python-dotenv). The following environment variables are supported:
|
|
116
|
+
|
|
117
|
+
| Variable | Default | Description |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| `ISSUEFLOW_DIR` | `.issueflows` | Name of the issue-tracking directory. |
|
|
120
|
+
| `ISSUEFLOW_CURSOR_DIR` | `.cursor` | Name of the Cursor config directory. |
|
|
121
|
+
| `ISSUEFLOW_DOCS_DIR` | `docs` | Where to write the workflow documentation file. |
|
|
122
|
+
|
|
123
|
+
## Development
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
git clone https://github.com/jepegit/issue-flow.git
|
|
127
|
+
cd issue-flow
|
|
128
|
+
uv sync
|
|
129
|
+
|
|
130
|
+
# Run tests
|
|
131
|
+
uv run pytest
|
|
132
|
+
|
|
133
|
+
# Lint
|
|
134
|
+
uv run ruff check src/ tests/
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Future plans
|
|
138
|
+
|
|
139
|
+
- **Multi-tool support** — generate config for other AI coding tools (Claude Code, Windsurf, etc.) in addition to Cursor.
|
|
140
|
+
- **`issue-flow status`** — show a dashboard of current, partly-solved, and solved issues.
|
|
141
|
+
- **Custom templates** — let users supply their own Jinja2 templates to tailor slash commands and rules to their team's conventions.
|
|
142
|
+
- **Git hook integration** — optionally move issue files on commit based on status markers.
|
|
143
|
+
- **GitHub Actions workflow** — ship a reusable action that syncs issue state between `.issueflows/` and GitHub issue labels/milestones.
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# issue-flow
|
|
2
|
+
|
|
3
|
+
Agents should behave. Let them follow the issue flow.
|
|
4
|
+
|
|
5
|
+
**issue-flow** scaffolds a lightweight issue-tracking workflow into your project so that Cursor AI agents can pick up GitHub issues, plan work, and land PRs in a consistent way.
|
|
6
|
+
|
|
7
|
+
## What it does
|
|
8
|
+
|
|
9
|
+
Running `issue-flow init` in your project root creates:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
your-project/
|
|
13
|
+
.issueflows/
|
|
14
|
+
00-tools/ # Helper scripts for agents
|
|
15
|
+
01-current-issues/ # Active issue markdown files
|
|
16
|
+
02-partly-solved-issues/ # Parked / in-progress issues
|
|
17
|
+
03-solved-issues/ # Completed issues archive
|
|
18
|
+
.cursor/
|
|
19
|
+
commands/
|
|
20
|
+
issue-init.md # /issue-init — fetch a GitHub issue locally
|
|
21
|
+
issue-start.md # /issue-start — plan and implement
|
|
22
|
+
issue-close.md # /issue-close — test, commit, push, PR
|
|
23
|
+
rules/
|
|
24
|
+
issueflow-rules.mdc # Always-on Cursor rule for the workflow
|
|
25
|
+
docs/
|
|
26
|
+
cursor-issue-workflow.md # Human-readable overview of the workflow
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The three Cursor slash commands give agents a repeatable flow:
|
|
30
|
+
|
|
31
|
+
1. `/issue-init 42` — pulls GitHub issue #42 into `.issueflows/01-current-issues/` and archives older issues.
|
|
32
|
+
2. `/issue-start` — reads the issue file, plans, and implements.
|
|
33
|
+
3. `/issue-close` — runs tests, updates status files, commits, pushes, and opens a PR.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
Requires Python 3.13+ and [uv](https://docs.astral.sh/uv/).
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uv tool install issue-flow
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or add it as a dev dependency to your project:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
uv add --dev issue-flow
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quick start
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
cd your-project
|
|
53
|
+
issue-flow init
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
That's it. Open the project in Cursor and use `/issue-init`, `/issue-start`, `/issue-close`.
|
|
57
|
+
|
|
58
|
+
## Usage
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
issue-flow init [PROJECT_DIR] [--force]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
| Argument / Option | Description |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `PROJECT_DIR` | Project root directory. Defaults to `.` (current directory). |
|
|
67
|
+
| `--force`, `-f` | Overwrite existing files instead of skipping them. |
|
|
68
|
+
|
|
69
|
+
Running `init` a second time is safe — existing files are skipped unless `--force` is passed.
|
|
70
|
+
|
|
71
|
+
## Configuration
|
|
72
|
+
|
|
73
|
+
issue-flow reads a `.env` file from the project root (via python-dotenv). The following environment variables are supported:
|
|
74
|
+
|
|
75
|
+
| Variable | Default | Description |
|
|
76
|
+
|---|---|---|
|
|
77
|
+
| `ISSUEFLOW_DIR` | `.issueflows` | Name of the issue-tracking directory. |
|
|
78
|
+
| `ISSUEFLOW_CURSOR_DIR` | `.cursor` | Name of the Cursor config directory. |
|
|
79
|
+
| `ISSUEFLOW_DOCS_DIR` | `docs` | Where to write the workflow documentation file. |
|
|
80
|
+
|
|
81
|
+
## Development
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
git clone https://github.com/jepegit/issue-flow.git
|
|
85
|
+
cd issue-flow
|
|
86
|
+
uv sync
|
|
87
|
+
|
|
88
|
+
# Run tests
|
|
89
|
+
uv run pytest
|
|
90
|
+
|
|
91
|
+
# Lint
|
|
92
|
+
uv run ruff check src/ tests/
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Future plans
|
|
96
|
+
|
|
97
|
+
- **Multi-tool support** — generate config for other AI coding tools (Claude Code, Windsurf, etc.) in addition to Cursor.
|
|
98
|
+
- **`issue-flow status`** — show a dashboard of current, partly-solved, and solved issues.
|
|
99
|
+
- **Custom templates** — let users supply their own Jinja2 templates to tailor slash commands and rules to their team's conventions.
|
|
100
|
+
- **Git hook integration** — optionally move issue files on commit based on status markers.
|
|
101
|
+
- **GitHub Actions workflow** — ship a reusable action that syncs issue state between `.issueflows/` and GitHub issue labels/milestones.
|
|
102
|
+
|
|
103
|
+
## License
|
|
104
|
+
|
|
105
|
+
See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "issue-flow"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Agents should behave. Let them follow the issue flow."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { file = "LICENSE" }
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "jepegit", email = "jepe@ife.no" }
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
keywords = ["cursor", "ai", "agents", "issue-tracking", "workflow"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 3 - Alpha",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"Topic :: Software Development :: Build Tools",
|
|
16
|
+
"Programming Language :: Python :: 3.13",
|
|
17
|
+
]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"jinja2>=3.1.6",
|
|
20
|
+
"python-dotenv>=1.2.2",
|
|
21
|
+
"rich>=14.3.3",
|
|
22
|
+
"typer>=0.24.1",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/jepegit/issue-flow"
|
|
27
|
+
Repository = "https://github.com/jepegit/issue-flow"
|
|
28
|
+
Issues = "https://github.com/jepegit/issue-flow/issues"
|
|
29
|
+
|
|
30
|
+
[project.scripts]
|
|
31
|
+
issue-flow = "issue_flow.cli:main"
|
|
32
|
+
|
|
33
|
+
[build-system]
|
|
34
|
+
requires = ["uv_build>=0.10.2,<0.11.0"]
|
|
35
|
+
build-backend = "uv_build"
|
|
36
|
+
|
|
37
|
+
[dependency-groups]
|
|
38
|
+
dev = [
|
|
39
|
+
"pytest>=9.0.2",
|
|
40
|
+
"ruff>=0.15.9",
|
|
41
|
+
]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Command-line interface for issue-flow."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(
|
|
10
|
+
name="issue-flow",
|
|
11
|
+
add_completion=False,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@app.callback()
|
|
16
|
+
def _callback() -> None:
|
|
17
|
+
"""Agents should behave. Let them follow the issue flow."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@app.command()
|
|
21
|
+
def init(
|
|
22
|
+
project_dir: Path = typer.Argument(
|
|
23
|
+
default=Path("."),
|
|
24
|
+
help="Project root directory (defaults to current directory).",
|
|
25
|
+
exists=True,
|
|
26
|
+
file_okay=False,
|
|
27
|
+
resolve_path=True,
|
|
28
|
+
),
|
|
29
|
+
force: bool = typer.Option(
|
|
30
|
+
False,
|
|
31
|
+
"--force",
|
|
32
|
+
"-f",
|
|
33
|
+
help="Overwrite existing files without asking.",
|
|
34
|
+
),
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Scaffold issue-flow directories and Cursor config files in a project."""
|
|
37
|
+
from issue_flow.init import run_init
|
|
38
|
+
|
|
39
|
+
run_init(project_root=project_dir, force=force)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def main() -> None:
|
|
43
|
+
"""Entry point for the `issue-flow` console script."""
|
|
44
|
+
app()
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Configuration for issue-flow, backed by .env files and environment variables."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from dotenv import load_dotenv
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Load .env from the current working directory (the user's project root).
|
|
13
|
+
# This runs at import time so that all downstream code sees the env vars.
|
|
14
|
+
load_dotenv(override=False)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class Settings:
|
|
19
|
+
"""Runtime settings for issue-flow.
|
|
20
|
+
|
|
21
|
+
Values come from environment variables (prefixed with ISSUEFLOW_) with
|
|
22
|
+
sensible defaults. A .env file in the project root is loaded automatically.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
issueflows_dir: str = field(
|
|
26
|
+
default_factory=lambda: os.getenv("ISSUEFLOW_DIR", ".issueflows")
|
|
27
|
+
)
|
|
28
|
+
cursor_dir: str = field(
|
|
29
|
+
default_factory=lambda: os.getenv("ISSUEFLOW_CURSOR_DIR", ".cursor")
|
|
30
|
+
)
|
|
31
|
+
docs_dir: str = field(
|
|
32
|
+
default_factory=lambda: os.getenv("ISSUEFLOW_DOCS_DIR", "docs")
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Subdirectory names inside .issueflows/
|
|
36
|
+
tools_folder: str = "00-tools"
|
|
37
|
+
current_issues_folder: str = "01-current-issues"
|
|
38
|
+
partly_solved_folder: str = "02-partly-solved-issues"
|
|
39
|
+
solved_folder: str = "03-solved-issues"
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def issueflows_subdirs(self) -> list[str]:
|
|
43
|
+
return [
|
|
44
|
+
self.tools_folder,
|
|
45
|
+
self.current_issues_folder,
|
|
46
|
+
self.partly_solved_folder,
|
|
47
|
+
self.solved_folder,
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
def template_context(self, project_root: Path) -> dict[str, str]:
|
|
51
|
+
"""Build the Jinja2 template context dictionary."""
|
|
52
|
+
project_name = _detect_project_name(project_root)
|
|
53
|
+
return {
|
|
54
|
+
"issueflows_dir": self.issueflows_dir,
|
|
55
|
+
"cursor_dir": self.cursor_dir,
|
|
56
|
+
"docs_dir": self.docs_dir,
|
|
57
|
+
"tools_folder": self.tools_folder,
|
|
58
|
+
"current_issues_folder": self.current_issues_folder,
|
|
59
|
+
"partly_solved_folder": self.partly_solved_folder,
|
|
60
|
+
"solved_folder": self.solved_folder,
|
|
61
|
+
"project_name": project_name,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _detect_project_name(project_root: Path) -> str:
|
|
66
|
+
"""Try to read the project name from pyproject.toml, fall back to dir name."""
|
|
67
|
+
pyproject = project_root / "pyproject.toml"
|
|
68
|
+
if pyproject.exists():
|
|
69
|
+
for line in pyproject.read_text(encoding="utf-8").splitlines():
|
|
70
|
+
stripped = line.strip()
|
|
71
|
+
if stripped.startswith("name") and "=" in stripped:
|
|
72
|
+
# e.g. name = "my-project"
|
|
73
|
+
value = stripped.split("=", 1)[1].strip().strip('"').strip("'")
|
|
74
|
+
if value:
|
|
75
|
+
return value
|
|
76
|
+
return project_root.resolve().name
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Implementation of the `issue-flow init` command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
|
|
9
|
+
from issue_flow.config import Settings
|
|
10
|
+
from issue_flow.templating import (
|
|
11
|
+
TEMPLATE_MANIFEST,
|
|
12
|
+
render_template,
|
|
13
|
+
resolve_output_path,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def run_init(project_root: Path, force: bool = False) -> None:
|
|
20
|
+
"""Scaffold .issueflows/ directories and .cursor/ config files.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
project_root: Absolute path to the user's project directory.
|
|
24
|
+
force: If True, overwrite existing files without asking.
|
|
25
|
+
"""
|
|
26
|
+
settings = Settings()
|
|
27
|
+
context = settings.template_context(project_root)
|
|
28
|
+
|
|
29
|
+
console.print(
|
|
30
|
+
f"\n[bold]Initializing issue-flow in [cyan]{project_root}[/cyan][/bold]\n"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# ── 1. Create .issueflows/ subdirectories ────────────────────────
|
|
34
|
+
_create_issueflow_dirs(project_root, settings)
|
|
35
|
+
|
|
36
|
+
# ── 2. Render and write template files ───────────────────────────
|
|
37
|
+
written_files: list[Path] = []
|
|
38
|
+
skipped_files: list[Path] = []
|
|
39
|
+
|
|
40
|
+
for template_name, path_template in TEMPLATE_MANIFEST:
|
|
41
|
+
relative_path = resolve_output_path(path_template, context)
|
|
42
|
+
absolute_path = project_root / relative_path
|
|
43
|
+
|
|
44
|
+
if absolute_path.exists() and not force:
|
|
45
|
+
console.print(
|
|
46
|
+
f" [yellow]skip[/yellow] {relative_path} (already exists, use --force to overwrite)"
|
|
47
|
+
)
|
|
48
|
+
skipped_files.append(relative_path)
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
rendered = render_template(template_name, context)
|
|
52
|
+
absolute_path.parent.mkdir(parents=True, exist_ok=True)
|
|
53
|
+
absolute_path.write_text(rendered, encoding="utf-8")
|
|
54
|
+
console.print(f" [green]write[/green] {relative_path}")
|
|
55
|
+
written_files.append(relative_path)
|
|
56
|
+
|
|
57
|
+
# ── 3. Summary ───────────────────────────────────────────────────
|
|
58
|
+
console.print()
|
|
59
|
+
if written_files:
|
|
60
|
+
console.print(
|
|
61
|
+
f"[bold green]Created {len(written_files)} file(s).[/bold green]"
|
|
62
|
+
)
|
|
63
|
+
if skipped_files:
|
|
64
|
+
console.print(
|
|
65
|
+
f"[bold yellow]Skipped {len(skipped_files)} existing file(s).[/bold yellow]"
|
|
66
|
+
)
|
|
67
|
+
if not written_files and not skipped_files:
|
|
68
|
+
console.print("[bold]Nothing to do.[/bold]")
|
|
69
|
+
|
|
70
|
+
console.print("\n[dim]Run [bold]/issue-init <number>[/bold] in Cursor to start tracking a GitHub issue.[/dim]\n")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _create_issueflow_dirs(project_root: Path, settings: Settings) -> None:
|
|
74
|
+
"""Create the .issueflows/ directory tree."""
|
|
75
|
+
base = project_root / settings.issueflows_dir
|
|
76
|
+
|
|
77
|
+
for subdir_name in settings.issueflows_subdirs:
|
|
78
|
+
dir_path = base / subdir_name
|
|
79
|
+
if dir_path.exists():
|
|
80
|
+
console.print(f" [dim]exists[/dim] {settings.issueflows_dir}/{subdir_name}/")
|
|
81
|
+
else:
|
|
82
|
+
dir_path.mkdir(parents=True, exist_ok=True)
|
|
83
|
+
console.print(f" [green]mkdir[/green] {settings.issueflows_dir}/{subdir_name}/")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Close out the current issue
|
|
2
|
+
|
|
3
|
+
Run this when implementation is done and you are ready to land the work.
|
|
4
|
+
|
|
5
|
+
## Input
|
|
6
|
+
|
|
7
|
+
Optional: branch name, PR title, or anything special (e.g. draft PR, skip issue doc update, commit all changes).
|
|
8
|
+
|
|
9
|
+
## Typical steps
|
|
10
|
+
|
|
11
|
+
1. **Sanity check**
|
|
12
|
+
- Run tests and any checks you rely on (e.g. `uv run pytest`).
|
|
13
|
+
- Skim the diff so the commit matches what you intend to ship.
|
|
14
|
+
|
|
15
|
+
2. **Issue tracking in the repo** (see project rules under `{{ issueflows_dir }}/{{ current_issues_folder }}`)
|
|
16
|
+
- Update the status file for this issue: clear checklist, remaining work, and use `- [x] Done` only when fully resolved.
|
|
17
|
+
- If the issue is fully resolved, move its markdown files from `{{ issueflows_dir }}/{{ current_issues_folder }}` to `{{ issueflows_dir }}/{{ solved_folder }}`. If partially resolved, move to `{{ issueflows_dir }}/{{ partly_solved_folder }}`.
|
|
18
|
+
|
|
19
|
+
3. **Commit and fix merge conflicts**
|
|
20
|
+
- Unless told to commit all, stage the right files (avoid unrelated changes).
|
|
21
|
+
- Write a commit message that states what changed and why in normal sentences.
|
|
22
|
+
- Make sure you have pulled the last changes from the default branch (e.g. `main`) and check for and fix merge conflicts.
|
|
23
|
+
|
|
24
|
+
4. **Push**
|
|
25
|
+
- Push your branch to `origin` (or the remote you use).
|
|
26
|
+
|
|
27
|
+
5. **Pull request**
|
|
28
|
+
- Open a PR against the default branch (e.g. `main`).
|
|
29
|
+
- Describe the change, how to test it, and link the GitHub issue (e.g. `Closes #123` or `Refs #123` in the PR body).
|
|
30
|
+
|
|
31
|
+
6. **After review**
|
|
32
|
+
- Address feedback, push updates, and merge when approved and CI is green.
|
|
33
|
+
|
|
34
|
+
## Output
|
|
35
|
+
|
|
36
|
+
Summarize what was committed, pushed, and the PR URL (or next step if blocked).
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Create original issue file from GitHub issue
|
|
2
|
+
|
|
3
|
+
Create an `*_original.md` file in `{{ issueflows_dir }}/{{ current_issues_folder }}` from a GitHub issue.
|
|
4
|
+
|
|
5
|
+
## Input
|
|
6
|
+
The user will provide one of:
|
|
7
|
+
- an issue number (e.g. `123`)
|
|
8
|
+
- or a full GitHub issue URL
|
|
9
|
+
|
|
10
|
+
Use the text provided after this slash command as the issue reference.
|
|
11
|
+
|
|
12
|
+
## Steps
|
|
13
|
+
|
|
14
|
+
0. Check that the required folders exist (`{{ issueflows_dir }}/{{ tools_folder }}`, `{{ issueflows_dir }}/{{ current_issues_folder }}`, `{{ issueflows_dir }}/{{ partly_solved_folder }}`, `{{ issueflows_dir }}/{{ solved_folder }}`). If not, create them after asking for permission.
|
|
15
|
+
|
|
16
|
+
1. Resolve the issue reference from the user input.
|
|
17
|
+
- If input is a full URL, extract `owner`, `repo`, and `issue number`.
|
|
18
|
+
- If input is only an issue number:
|
|
19
|
+
- derive `owner/repo` from `git remote` (prefer `origin`)
|
|
20
|
+
- support both SSH and HTTPS remote URL formats
|
|
21
|
+
- if parsing fails, ask the user for either full issue URL or `owner/repo`
|
|
22
|
+
|
|
23
|
+
2. Fetch issue data using GitHub CLI (explicit repo if needed):
|
|
24
|
+
- title
|
|
25
|
+
- body
|
|
26
|
+
- url
|
|
27
|
+
- number
|
|
28
|
+
- and confirm resolved `owner/repo`
|
|
29
|
+
|
|
30
|
+
3. Archive existing issue files already in `{{ issueflows_dir }}/{{ current_issues_folder }}` (except the current issue number).
|
|
31
|
+
- Inspect issue groups by issue number (for example `issue121_*` belongs to issue 121).
|
|
32
|
+
- Consider all files for that issue in `{{ issueflows_dir }}/{{ current_issues_folder }}` (original + status/supplementary files) as one group to move together.
|
|
33
|
+
- Decide destination:
|
|
34
|
+
- move to `{{ issueflows_dir }}/{{ solved_folder }}` if the issue is done
|
|
35
|
+
- move to `{{ issueflows_dir }}/{{ partly_solved_folder }}` if the issue is not done
|
|
36
|
+
- Determine "done" status from an explicit checkbox marker in a status file:
|
|
37
|
+
- done only if a status markdown file for that issue contains `- [x] Done` (case-insensitive match for `done`)
|
|
38
|
+
- if the checkbox is missing, unchecked (`- [ ] Done`), unclear, or no status file exists, treat as not done
|
|
39
|
+
- Never move files for the issue currently being created.
|
|
40
|
+
|
|
41
|
+
4. Create this file:
|
|
42
|
+
- `{{ issueflows_dir }}/{{ current_issues_folder }}/issue<number>_original.md`
|
|
43
|
+
5. File content format:
|
|
44
|
+
```markdown
|
|
45
|
+
# Issue #<number>: <title>
|
|
46
|
+
|
|
47
|
+
Source: <url>
|
|
48
|
+
|
|
49
|
+
## Original issue text
|
|
50
|
+
|
|
51
|
+
<body exactly as in GitHub issue>
|
|
52
|
+
```
|
|
53
|
+
6. If `gh` is not authenticated or issue fetch fails:
|
|
54
|
+
- stop and report the exact error
|
|
55
|
+
- suggest `gh auth login`
|
|
56
|
+
7. If the target file already exists:
|
|
57
|
+
- do not overwrite silently
|
|
58
|
+
- ask whether to overwrite or keep both
|
|
59
|
+
|
|
60
|
+
## Output to user
|
|
61
|
+
Report:
|
|
62
|
+
- issue number fetched
|
|
63
|
+
- repository used (`owner/repo`)
|
|
64
|
+
- file path created
|
|
65
|
+
- archive moves performed (source -> destination, grouped by issue number)
|
|
66
|
+
- whether the operation succeeded
|
|
67
|
+
|
|
68
|
+
## Constraints
|
|
69
|
+
- Preserve the issue body exactly as returned by GitHub.
|
|
70
|
+
- Use UTF-8 markdown.
|
|
71
|
+
- Allowed file modifications for this command:
|
|
72
|
+
- create/update the target `issue<number>_original.md`
|
|
73
|
+
- move pre-existing issue files from `{{ issueflows_dir }}/{{ current_issues_folder }}` to `{{ issueflows_dir }}/{{ partly_solved_folder }}` or `{{ issueflows_dir }}/{{ solved_folder }}` according to the archive rule above
|
|
74
|
+
- If `{{ issueflows_dir }}/{{ current_issues_folder }}` does not exist, report an error and stop.
|
|
75
|
+
- If archive destination directories do not exist, report an error and stop.
|
|
76
|
+
- Prefer deterministic behavior: always state which repo was resolved before writing the file.
|
|
77
|
+
|
|
78
|
+
## Example invocations
|
|
79
|
+
- `/issue-init 123`
|
|
80
|
+
- `/issue-init https://github.com/owner/repo/issues/123`
|
|
81
|
+
- `/issue-init owner/repo/#123`
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Start working with current issue
|
|
2
|
+
|
|
3
|
+
The issue should already be explained in a markdown file in `{{ issueflows_dir }}/{{ current_issues_folder }}`.
|
|
4
|
+
|
|
5
|
+
## Input
|
|
6
|
+
If additional input is added, use that for further detailed guidance
|
|
7
|
+
|
|
8
|
+
## Steps
|
|
9
|
+
|
|
10
|
+
0. If the issue markdown file is not present, or it is ambiguous which one to select, ask.
|
|
11
|
+
|
|
12
|
+
1. Plan. If not in plan mode, stop and ask for a confirmation.
|
|
13
|
+
|
|
14
|
+
2. Check that the plan is not too broad. If too broad, ask if it should be split into several parts.
|
|
15
|
+
|
|
16
|
+
3. Implement the steps of the plan
|
|
File without changes
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Cursor issue workflow (slash commands)
|
|
2
|
+
|
|
3
|
+
This repo uses three Cursor **slash commands** under `{{ cursor_dir }}/commands/` that line up with how we track GitHub issues in `{{ issueflows_dir }}/{{ current_issues_folder }}/`. Use them in order when you pick up work from GitHub and want the assistant to follow the same steps.
|
|
4
|
+
|
|
5
|
+
| Command | File | Role |
|
|
6
|
+
|--------|------|------|
|
|
7
|
+
| `/issue-init` | `issue-init.md` | Pull an issue from GitHub into the repo as a local markdown file and tidy older current issues. |
|
|
8
|
+
| `/issue-start` | `issue-start.md` | Plan and implement the work described in `{{ issueflows_dir }}/{{ current_issues_folder }}/`. |
|
|
9
|
+
| `/issue-close` | `issue-close.md` | Finish: tests, issue-folder housekeeping, commit, push, PR. |
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. `/issue-init` — capture the issue locally
|
|
14
|
+
|
|
15
|
+
**When:** You have a GitHub issue you want to work on (or archive older "current" issues before starting a new one).
|
|
16
|
+
|
|
17
|
+
**What you pass:** Either an issue number (e.g. `42`) or a full GitHub issue URL. The assistant resolves `owner/repo` from `git remote origin` when you only pass a number.
|
|
18
|
+
|
|
19
|
+
**What happens:**
|
|
20
|
+
|
|
21
|
+
- The assistant uses **GitHub CLI** (`gh`) to fetch title, body, URL, and number. You need `gh` authenticated (`gh auth login` if needed).
|
|
22
|
+
- It creates **`{{ issueflows_dir }}/{{ current_issues_folder }}/issue<number>_original.md`** with the title, source URL, and the **exact** issue body from GitHub.
|
|
23
|
+
- **Archive:** Other files already in `{{ issueflows_dir }}/{{ current_issues_folder }}/` (grouped by issue number, e.g. `issue121_*`) may be **moved** to `{{ issueflows_dir }}/{{ partly_solved_folder }}/` or `{{ issueflows_dir }}/{{ solved_folder }}/`, based on whether a status file for that issue contains a checked **Done** line (`- [x] Done`). The new issue's files are never moved as part of this step.
|
|
24
|
+
- If the target `issue<number>_original.md` already exists, the assistant should not overwrite it without asking.
|
|
25
|
+
|
|
26
|
+
**Result:** One canonical "original issue" file under `{{ issueflows_dir }}/{{ current_issues_folder }}/` plus optional archive moves. You can add or edit a separate `issue<number>_status.md` (or similar) by hand or with the assistant as work progresses.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 2. `/issue-start` — plan and implement
|
|
31
|
+
|
|
32
|
+
**When:** The issue is represented in `{{ issueflows_dir }}/{{ current_issues_folder }}/` (at minimum the `*_original.md` file) and you are ready to code.
|
|
33
|
+
|
|
34
|
+
**What you pass:** Optional extra instructions (scope, constraints, design preferences).
|
|
35
|
+
|
|
36
|
+
**What the assistant does:**
|
|
37
|
+
|
|
38
|
+
1. Confirms **which** issue file applies if several exist or things are ambiguous.
|
|
39
|
+
2. **Plans** the work. If you are not in plan mode, it should stop and ask you to confirm before large changes.
|
|
40
|
+
3. Checks the plan is **not too broad**; may suggest splitting into smaller chunks.
|
|
41
|
+
4. **Implements** the plan (code, tests, and updates to issue status docs as appropriate for the task).
|
|
42
|
+
|
|
43
|
+
**Result:** Implementation aligned with the markdown in `{{ issueflows_dir }}/{{ current_issues_folder }}/` and project rules (tests with `uv run`, dependency management with `uv`, etc.).
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 3. `/issue-close` — land the work
|
|
48
|
+
|
|
49
|
+
**When:** Implementation is done and you want to ship (commit, push, PR).
|
|
50
|
+
|
|
51
|
+
**What you pass:** Optional notes (branch name, PR title, draft PR, or "skip issue doc update").
|
|
52
|
+
|
|
53
|
+
**Typical steps the assistant follows:**
|
|
54
|
+
|
|
55
|
+
1. **Sanity check** — e.g. `uv run pytest`, review the diff.
|
|
56
|
+
2. **Issue folders** — update status markdown; use `- [x] Done` only when fully resolved. Move completed issue files from `{{ issueflows_dir }}/{{ current_issues_folder }}/` to `{{ issueflows_dir }}/{{ solved_folder }}/`, or partly done work to `{{ issueflows_dir }}/{{ partly_solved_folder }}/` (see project rules).
|
|
57
|
+
3. **Commit** — focused staging and a clear message.
|
|
58
|
+
4. **Push** — to your usual remote (e.g. `origin`).
|
|
59
|
+
5. **Pull request** — open against the default branch; link the GitHub issue (`Closes #n` / `Refs #n`).
|
|
60
|
+
6. **After review** — address comments, merge when ready.
|
|
61
|
+
|
|
62
|
+
**Result:** Short summary of commit, push, and PR link (or what is blocked).
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## End-to-end flow
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
GitHub issue
|
|
70
|
+
│ /issue-init
|
|
71
|
+
▼
|
|
72
|
+
{{ issueflows_dir }}/{{ current_issues_folder }}/issueN_original.md (+ optional status files)
|
|
73
|
+
│ /issue-start
|
|
74
|
+
▼
|
|
75
|
+
Code + tests (+ status updates during work)
|
|
76
|
+
│ /issue-close
|
|
77
|
+
▼
|
|
78
|
+
Commit → push → PR → merge
|
|
79
|
+
│
|
|
80
|
+
└── issue docs in {{ issueflows_dir }}/{{ solved_folder }}/ or {{ issueflows_dir }}/{{ partly_solved_folder }}/ when appropriate
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The command definitions are the source of truth: `{{ cursor_dir }}/commands/issue-init.md`, `issue-start.md`, and `issue-close.md`. This document is a readable overview only.
|
|
File without changes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Issue-flow workflow rules for LLMs
|
|
3
|
+
globs:
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Issue-flow best practices
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Running python
|
|
12
|
+
|
|
13
|
+
This is a python project. It uses a python environment (.venv) managed by uv.
|
|
14
|
+
|
|
15
|
+
❌ BAD:
|
|
16
|
+
```bash
|
|
17
|
+
python run_script.py
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
✅ GOOD:
|
|
21
|
+
```bash
|
|
22
|
+
uv run run_script.py
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Package Management with `uv`
|
|
26
|
+
|
|
27
|
+
**✅ Use `uv` exclusively**
|
|
28
|
+
|
|
29
|
+
- All Python dependencies **must be installed, synchronized, and locked** using `uv`.
|
|
30
|
+
- Never use `pip`, `pip-tools`, or `poetry` directly for dependency management.
|
|
31
|
+
|
|
32
|
+
**🔁 Managing Dependencies**
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Add or upgrade dependencies
|
|
36
|
+
uv add <package>
|
|
37
|
+
|
|
38
|
+
# Remove dependencies
|
|
39
|
+
uv remove <package>
|
|
40
|
+
|
|
41
|
+
# Reinstall all dependencies from lock file
|
|
42
|
+
uv sync
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**🔁 Scripts**
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Run script with proper dependencies
|
|
49
|
+
uv run script.py
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## Issue tracking structure
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
{{ project_name }}/
|
|
57
|
+
{{ issueflows_dir }}/
|
|
58
|
+
{{ tools_folder }}/
|
|
59
|
+
{{ current_issues_folder }}/
|
|
60
|
+
issueXX_original.md
|
|
61
|
+
issueXX_status.md
|
|
62
|
+
{{ partly_solved_folder }}/
|
|
63
|
+
{{ solved_folder }}/
|
|
64
|
+
pyproject.toml
|
|
65
|
+
readme.md
|
|
66
|
+
...
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
## Development information
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
### Working on issues
|
|
74
|
+
|
|
75
|
+
After each iteration, update the documents in `{{ issueflows_dir }}/{{ current_issues_folder }}` (should contain one file labelled `_original` that consists of the original issue description, and supplementary status files describing what has been done, current status, and remaining work).
|
|
76
|
+
Use an explicit status checkbox in the status file:
|
|
77
|
+
- `- [x] Done` when fully resolved
|
|
78
|
+
- `- [ ] Done` when not fully resolved
|
|
79
|
+
|
|
80
|
+
### When finishing an issue
|
|
81
|
+
|
|
82
|
+
If the issue is fully resolved (no additional subtasks present), move the original and status markdown files to `{{ issueflows_dir }}/{{ solved_folder }}`. Else, move them to `{{ issueflows_dir }}/{{ partly_solved_folder }}`.
|
|
83
|
+
|
|
84
|
+
### Scripts that can help us when working on issues
|
|
85
|
+
|
|
86
|
+
If you want, you can put small scripts etc. that you have made and think could be useful in the future in our llm tools folder: `{{ issueflows_dir }}/{{ tools_folder }}`. Also, feel free to use the tools in our llm tools folder if you find someone that could be useful.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Jinja2 template loading and rendering for issue-flow."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from importlib import resources
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from jinja2 import Environment, BaseLoader, TemplateNotFound
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# ---------------------------------------------------------------------------
|
|
12
|
+
# Custom loader that reads from the package's templates/ directory using
|
|
13
|
+
# importlib.resources so it works whether the package is installed as a
|
|
14
|
+
# directory, zip, or editable install.
|
|
15
|
+
# ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
_TEMPLATES_PACKAGE = "issue_flow.templates"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class _PackageLoader(BaseLoader):
|
|
21
|
+
"""Load Jinja2 templates shipped inside the issue_flow.templates package."""
|
|
22
|
+
|
|
23
|
+
def get_source(
|
|
24
|
+
self, environment: Environment, template: str
|
|
25
|
+
) -> tuple[str, str, callable]:
|
|
26
|
+
# template is e.g. "commands/issue-init.md.j2"
|
|
27
|
+
parts = template.replace("\\", "/").split("/")
|
|
28
|
+
package = _TEMPLATES_PACKAGE + "." + ".".join(parts[:-1]) if len(parts) > 1 else _TEMPLATES_PACKAGE
|
|
29
|
+
filename = parts[-1]
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
ref = resources.files(package).joinpath(filename)
|
|
33
|
+
source = ref.read_text(encoding="utf-8")
|
|
34
|
+
except (ModuleNotFoundError, FileNotFoundError, TypeError) as exc:
|
|
35
|
+
raise TemplateNotFound(template) from exc
|
|
36
|
+
|
|
37
|
+
# The third element is a callable that returns True if the template
|
|
38
|
+
# is still up-to-date (always True for packaged templates).
|
|
39
|
+
return source, template, lambda: True
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_environment() -> Environment:
|
|
43
|
+
"""Return a configured Jinja2 environment that loads from the package."""
|
|
44
|
+
env = Environment(
|
|
45
|
+
loader=_PackageLoader(),
|
|
46
|
+
keep_trailing_newline=True,
|
|
47
|
+
trim_blocks=False,
|
|
48
|
+
lstrip_blocks=False,
|
|
49
|
+
)
|
|
50
|
+
return env
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def render_template(template_name: str, context: dict[str, str]) -> str:
|
|
54
|
+
"""Render a single template by name and return the result string."""
|
|
55
|
+
env = get_environment()
|
|
56
|
+
template = env.get_template(template_name)
|
|
57
|
+
return template.render(context)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# ---------------------------------------------------------------------------
|
|
61
|
+
# Mapping of template name -> output path (relative to the project root).
|
|
62
|
+
# The output path may itself contain Jinja-style placeholders, so we render
|
|
63
|
+
# the path first.
|
|
64
|
+
# ---------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
# Each entry: (template_file, output_path_template)
|
|
67
|
+
# The output_path_template uses simple str.format with the context dict.
|
|
68
|
+
TEMPLATE_MANIFEST: list[tuple[str, str]] = [
|
|
69
|
+
("commands/issue-init.md.j2", "{cursor_dir}/commands/issue-init.md"),
|
|
70
|
+
("commands/issue-start.md.j2", "{cursor_dir}/commands/issue-start.md"),
|
|
71
|
+
("commands/issue-close.md.j2", "{cursor_dir}/commands/issue-close.md"),
|
|
72
|
+
("rules/issueflow-rules.mdc.j2", "{cursor_dir}/rules/issueflow-rules.mdc"),
|
|
73
|
+
("docs/cursor-issue-workflow.md.j2", "{docs_dir}/cursor-issue-workflow.md"),
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def resolve_output_path(path_template: str, context: dict[str, str]) -> Path:
|
|
78
|
+
"""Resolve a path template like '{cursor_dir}/commands/foo.md' into a Path."""
|
|
79
|
+
return Path(path_template.format(**context))
|