@pennyfarthing/core 7.8.1 → 7.8.2
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.
- package/package.json +2 -1
- package/pennyfarthing-dist/scripts/core/prime.sh +8 -0
- package/pennyfarthing_scripts/__init__.py +17 -0
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bellmode_hook.py +154 -0
- package/pennyfarthing_scripts/brownfield/__init__.py +35 -0
- package/pennyfarthing_scripts/brownfield/__main__.py +7 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/brownfield/cli.py +131 -0
- package/pennyfarthing_scripts/brownfield/discover.py +753 -0
- package/pennyfarthing_scripts/common/__init__.py +49 -0
- package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/common/config.py +65 -0
- package/pennyfarthing_scripts/common/output.py +180 -0
- package/pennyfarthing_scripts/config.py +21 -0
- package/pennyfarthing_scripts/git/__init__.py +29 -0
- package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/create_branches.py +439 -0
- package/pennyfarthing_scripts/git/status_all.py +310 -0
- package/pennyfarthing_scripts/hooks.py +455 -0
- package/pennyfarthing_scripts/jira/__init__.py +93 -0
- package/pennyfarthing_scripts/jira/__main__.py +10 -0
- package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/jira/bidirectional.py +561 -0
- package/pennyfarthing_scripts/jira/claim.py +211 -0
- package/pennyfarthing_scripts/jira/cli.py +150 -0
- package/pennyfarthing_scripts/jira/client.py +613 -0
- package/pennyfarthing_scripts/jira/epic.py +176 -0
- package/pennyfarthing_scripts/jira/story.py +219 -0
- package/pennyfarthing_scripts/jira/sync.py +350 -0
- package/pennyfarthing_scripts/jira_bidirectional_sync.py +37 -0
- package/pennyfarthing_scripts/jira_epic_creation.py +30 -0
- package/pennyfarthing_scripts/jira_sync.py +36 -0
- package/pennyfarthing_scripts/jira_sync_story.py +30 -0
- package/pennyfarthing_scripts/output.py +37 -0
- package/pennyfarthing_scripts/preflight/__init__.py +17 -0
- package/pennyfarthing_scripts/preflight/__main__.py +10 -0
- package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/preflight/cli.py +141 -0
- package/pennyfarthing_scripts/preflight/finish.py +382 -0
- package/pennyfarthing_scripts/pretooluse_hook.py +142 -0
- package/pennyfarthing_scripts/prime/__init__.py +38 -0
- package/pennyfarthing_scripts/prime/__main__.py +8 -0
- package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/cli.py +220 -0
- package/pennyfarthing_scripts/prime/loader.py +239 -0
- package/pennyfarthing_scripts/sprint/__init__.py +66 -0
- package/pennyfarthing_scripts/sprint/__main__.py +10 -0
- package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/archive.py +108 -0
- package/pennyfarthing_scripts/sprint/cli.py +124 -0
- package/pennyfarthing_scripts/sprint/loader.py +193 -0
- package/pennyfarthing_scripts/sprint/status.py +122 -0
- package/pennyfarthing_scripts/sprint/validator.py +405 -0
- package/pennyfarthing_scripts/sprint/work.py +192 -0
- package/pennyfarthing_scripts/story/__init__.py +67 -0
- package/pennyfarthing_scripts/story/__main__.py +10 -0
- package/pennyfarthing_scripts/story/cli.py +105 -0
- package/pennyfarthing_scripts/story/create.py +167 -0
- package/pennyfarthing_scripts/story/size.py +113 -0
- package/pennyfarthing_scripts/story/template.py +151 -0
- package/pennyfarthing_scripts/swebench.py +216 -0
- package/pennyfarthing_scripts/tests/__init__.py +1 -0
- package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/conftest.py +106 -0
- package/pennyfarthing_scripts/tests/test_brownfield.py +842 -0
- package/pennyfarthing_scripts/tests/test_cli_modules.py +245 -0
- package/pennyfarthing_scripts/tests/test_common.py +180 -0
- package/pennyfarthing_scripts/tests/test_git_utils.py +866 -0
- package/pennyfarthing_scripts/tests/test_jira_package.py +334 -0
- package/pennyfarthing_scripts/tests/test_package_structure.py +372 -0
- package/pennyfarthing_scripts/tests/test_prime.py +397 -0
- package/pennyfarthing_scripts/tests/test_sprint_package.py +236 -0
- package/pennyfarthing_scripts/tests/test_sprint_validator.py +675 -0
- package/pennyfarthing_scripts/tests/test_story_package.py +156 -0
- package/pennyfarthing_scripts/welcome_hook.py +157 -0
- package/pennyfarthing_scripts/workflow.py +183 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Claim Jira stories for work.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python -m pennyfarthing_scripts.jira claim <issue-key> [--claim]
|
|
6
|
+
|
|
7
|
+
Options:
|
|
8
|
+
--claim Actually claim (assign + move to In Progress)
|
|
9
|
+
|
|
10
|
+
Exit codes:
|
|
11
|
+
0 - Available or successfully claimed
|
|
12
|
+
1 - Assigned to someone else
|
|
13
|
+
2 - Not found or not synced
|
|
14
|
+
3 - Error (CLI not installed, etc.)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import sys
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
from pennyfarthing_scripts.jira.client import (
|
|
22
|
+
get_issue,
|
|
23
|
+
get_jira_field,
|
|
24
|
+
is_jira_cli_available,
|
|
25
|
+
update_issue_status,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def parse_args(args: list[str] | None = None) -> argparse.Namespace:
|
|
30
|
+
"""Parse command line arguments.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
args: Command line arguments (defaults to sys.argv[1:])
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Parsed arguments namespace
|
|
37
|
+
"""
|
|
38
|
+
parser = argparse.ArgumentParser(
|
|
39
|
+
description="Check availability and claim a Jira story"
|
|
40
|
+
)
|
|
41
|
+
parser.add_argument("issue_key", help="Jira issue key (e.g., MSSCI-12345)")
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"--claim",
|
|
44
|
+
action="store_true",
|
|
45
|
+
help="Actually claim (assign to self + move to In Progress)",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
return parser.parse_args(args)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def check_availability(issue_key: str) -> dict[str, Any]:
|
|
52
|
+
"""Check if a story is available for claiming.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
issue_key: Jira issue key
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Dict with available status and details
|
|
59
|
+
"""
|
|
60
|
+
if not is_jira_cli_available():
|
|
61
|
+
return {
|
|
62
|
+
"available": False,
|
|
63
|
+
"error": "Jira CLI not installed",
|
|
64
|
+
"exit_code": 3,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
issue = get_issue(issue_key)
|
|
68
|
+
if not issue:
|
|
69
|
+
return {
|
|
70
|
+
"available": False,
|
|
71
|
+
"error": f"Issue {issue_key} not found",
|
|
72
|
+
"exit_code": 2,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
assignee = get_jira_field(issue, "fields.assignee.displayName")
|
|
76
|
+
status = get_jira_field(issue, "fields.status.name", "Unknown")
|
|
77
|
+
summary = get_jira_field(issue, "fields.summary", "")
|
|
78
|
+
|
|
79
|
+
if assignee:
|
|
80
|
+
return {
|
|
81
|
+
"available": False,
|
|
82
|
+
"assigned_to": assignee,
|
|
83
|
+
"status": status,
|
|
84
|
+
"summary": summary,
|
|
85
|
+
"exit_code": 1,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
"available": True,
|
|
90
|
+
"status": status,
|
|
91
|
+
"summary": summary,
|
|
92
|
+
"exit_code": 0,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def claim_story(issue_key: str) -> dict[str, Any]:
|
|
97
|
+
"""Claim a story by assigning to self and moving to In Progress.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
issue_key: Jira issue key
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Dict with success status and details
|
|
104
|
+
"""
|
|
105
|
+
import subprocess
|
|
106
|
+
|
|
107
|
+
if not is_jira_cli_available():
|
|
108
|
+
return {
|
|
109
|
+
"success": False,
|
|
110
|
+
"error": "Jira CLI not installed",
|
|
111
|
+
"exit_code": 3,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# Check availability first
|
|
115
|
+
availability = check_availability(issue_key)
|
|
116
|
+
if not availability["available"]:
|
|
117
|
+
return {
|
|
118
|
+
"success": False,
|
|
119
|
+
"error": f"Story not available: assigned to {availability.get('assigned_to', 'unknown')}",
|
|
120
|
+
"exit_code": 1,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
actions = []
|
|
124
|
+
errors = []
|
|
125
|
+
|
|
126
|
+
# Get current user
|
|
127
|
+
result = subprocess.run(
|
|
128
|
+
["jira", "me"],
|
|
129
|
+
capture_output=True,
|
|
130
|
+
text=True,
|
|
131
|
+
)
|
|
132
|
+
if result.returncode != 0:
|
|
133
|
+
return {
|
|
134
|
+
"success": False,
|
|
135
|
+
"error": "Could not get current Jira user",
|
|
136
|
+
"exit_code": 3,
|
|
137
|
+
}
|
|
138
|
+
current_user = result.stdout.strip()
|
|
139
|
+
|
|
140
|
+
# Assign to self
|
|
141
|
+
result = subprocess.run(
|
|
142
|
+
["jira", "issue", "assign", issue_key, current_user, "--project", "MSSCI"],
|
|
143
|
+
capture_output=True,
|
|
144
|
+
text=True,
|
|
145
|
+
)
|
|
146
|
+
if result.returncode == 0:
|
|
147
|
+
actions.append(f"Assigned to {current_user}")
|
|
148
|
+
else:
|
|
149
|
+
errors.append(f"Failed to assign: {result.stderr}")
|
|
150
|
+
|
|
151
|
+
# Move to In Progress
|
|
152
|
+
if update_issue_status(issue_key, "In Progress"):
|
|
153
|
+
actions.append("Moved to In Progress")
|
|
154
|
+
else:
|
|
155
|
+
errors.append("Failed to move to In Progress")
|
|
156
|
+
|
|
157
|
+
if errors:
|
|
158
|
+
return {
|
|
159
|
+
"success": False,
|
|
160
|
+
"actions": actions,
|
|
161
|
+
"errors": errors,
|
|
162
|
+
"exit_code": 3,
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
"success": True,
|
|
167
|
+
"actions": actions,
|
|
168
|
+
"exit_code": 0,
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def main(args: list[str] | None = None) -> int:
|
|
173
|
+
"""CLI entry point.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
args: Command line arguments (defaults to sys.argv[1:])
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Exit code
|
|
180
|
+
"""
|
|
181
|
+
parsed_args = parse_args(args)
|
|
182
|
+
|
|
183
|
+
if parsed_args.claim:
|
|
184
|
+
result = claim_story(parsed_args.issue_key)
|
|
185
|
+
if result["success"]:
|
|
186
|
+
print(f"Claimed {parsed_args.issue_key}")
|
|
187
|
+
for action in result.get("actions", []):
|
|
188
|
+
print(f" {action}")
|
|
189
|
+
return 0
|
|
190
|
+
else:
|
|
191
|
+
print(f"Failed to claim: {result.get('error', 'Unknown error')}", file=sys.stderr)
|
|
192
|
+
for err in result.get("errors", []):
|
|
193
|
+
print(f" {err}", file=sys.stderr)
|
|
194
|
+
return result.get("exit_code", 1)
|
|
195
|
+
else:
|
|
196
|
+
result = check_availability(parsed_args.issue_key)
|
|
197
|
+
if result["available"]:
|
|
198
|
+
print(f"{parsed_args.issue_key}: Available")
|
|
199
|
+
print(f" Status: {result.get('status')}")
|
|
200
|
+
print(f" Summary: {result.get('summary')}")
|
|
201
|
+
return 0
|
|
202
|
+
else:
|
|
203
|
+
if result.get("assigned_to"):
|
|
204
|
+
print(f"{parsed_args.issue_key}: Assigned to {result['assigned_to']}")
|
|
205
|
+
else:
|
|
206
|
+
print(f"{parsed_args.issue_key}: {result.get('error', 'Not available')}")
|
|
207
|
+
return result.get("exit_code", 1)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
sys.exit(main())
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Jira CLI - Fan-out CLI for Jira operations.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python -m pennyfarthing_scripts.jira <subcommand> [args]
|
|
6
|
+
|
|
7
|
+
Subcommands:
|
|
8
|
+
view View issue details
|
|
9
|
+
claim Claim a story
|
|
10
|
+
sync Sync epic to Jira
|
|
11
|
+
bidirectional Bidirectional sync
|
|
12
|
+
create Create epic or story
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import argparse
|
|
16
|
+
import sys
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def view(args: list[str]) -> int:
|
|
21
|
+
"""View issue details."""
|
|
22
|
+
import subprocess
|
|
23
|
+
|
|
24
|
+
if not args:
|
|
25
|
+
print("Usage: jira view <issue-key>", file=sys.stderr)
|
|
26
|
+
return 1
|
|
27
|
+
|
|
28
|
+
result = subprocess.run(
|
|
29
|
+
["jira", "issue", "view", args[0]],
|
|
30
|
+
capture_output=False,
|
|
31
|
+
)
|
|
32
|
+
return result.returncode
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def claim(args: list[str]) -> int:
|
|
36
|
+
"""Claim a story."""
|
|
37
|
+
from pennyfarthing_scripts.jira.claim import main as claim_main
|
|
38
|
+
return claim_main(args)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def sync(args: list[str]) -> int:
|
|
42
|
+
"""Sync epic to Jira."""
|
|
43
|
+
from pennyfarthing_scripts.jira.sync import main as sync_main
|
|
44
|
+
return sync_main(args)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def bidirectional(args: list[str]) -> int:
|
|
48
|
+
"""Bidirectional sync."""
|
|
49
|
+
from pennyfarthing_scripts.jira.bidirectional import main as bidirectional_main
|
|
50
|
+
return bidirectional_main(args)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def create(args: list[str]) -> int:
|
|
54
|
+
"""Create epic or story."""
|
|
55
|
+
if not args:
|
|
56
|
+
print("Usage: jira create <epic|story> [args]", file=sys.stderr)
|
|
57
|
+
return 1
|
|
58
|
+
|
|
59
|
+
subcommand = args[0]
|
|
60
|
+
remaining = args[1:]
|
|
61
|
+
|
|
62
|
+
if subcommand == "epic":
|
|
63
|
+
from pennyfarthing_scripts.jira.epic import main as epic_main
|
|
64
|
+
return epic_main(remaining)
|
|
65
|
+
elif subcommand == "story":
|
|
66
|
+
from pennyfarthing_scripts.jira.story import main as story_main
|
|
67
|
+
return story_main(remaining)
|
|
68
|
+
else:
|
|
69
|
+
print(f"Unknown create subcommand: {subcommand}", file=sys.stderr)
|
|
70
|
+
print("Usage: jira create <epic|story> [args]", file=sys.stderr)
|
|
71
|
+
return 1
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# Subcommand registry
|
|
75
|
+
SUBCOMMANDS = {
|
|
76
|
+
"view": view,
|
|
77
|
+
"claim": claim,
|
|
78
|
+
"sync": sync,
|
|
79
|
+
"bidirectional": bidirectional,
|
|
80
|
+
"create": create,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def cli(args: list[str] | None = None) -> int:
|
|
85
|
+
"""Main CLI entry point.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
args: Command line arguments (defaults to sys.argv[1:])
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Exit code
|
|
92
|
+
"""
|
|
93
|
+
if args is None:
|
|
94
|
+
args = sys.argv[1:]
|
|
95
|
+
|
|
96
|
+
parser = argparse.ArgumentParser(
|
|
97
|
+
prog="jira",
|
|
98
|
+
description="Jira CLI for Pennyfarthing",
|
|
99
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
100
|
+
epilog="""
|
|
101
|
+
Subcommands:
|
|
102
|
+
view <key> View issue details
|
|
103
|
+
claim <key> [--claim] Check/claim a story
|
|
104
|
+
sync <epic> [opts] Sync epic to Jira
|
|
105
|
+
bidirectional [opts] Bidirectional sync
|
|
106
|
+
create epic <id> Create epic from YAML
|
|
107
|
+
create story <key> Sync single story
|
|
108
|
+
|
|
109
|
+
Examples:
|
|
110
|
+
jira view MSSCI-12345
|
|
111
|
+
jira claim MSSCI-12345 --claim
|
|
112
|
+
jira sync 63 --transition --points
|
|
113
|
+
jira bidirectional --all --dry-run
|
|
114
|
+
jira create epic epic-63
|
|
115
|
+
""",
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
parser.add_argument(
|
|
119
|
+
"subcommand",
|
|
120
|
+
nargs="?",
|
|
121
|
+
choices=list(SUBCOMMANDS.keys()),
|
|
122
|
+
help="Subcommand to run",
|
|
123
|
+
)
|
|
124
|
+
parser.add_argument(
|
|
125
|
+
"args",
|
|
126
|
+
nargs=argparse.REMAINDER,
|
|
127
|
+
help="Arguments for subcommand",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
parsed = parser.parse_args(args)
|
|
131
|
+
|
|
132
|
+
if not parsed.subcommand:
|
|
133
|
+
parser.print_help()
|
|
134
|
+
return 0
|
|
135
|
+
|
|
136
|
+
handler = SUBCOMMANDS.get(parsed.subcommand)
|
|
137
|
+
if handler:
|
|
138
|
+
return handler(parsed.args)
|
|
139
|
+
else:
|
|
140
|
+
print(f"Unknown subcommand: {parsed.subcommand}", file=sys.stderr)
|
|
141
|
+
return 1
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def main(args: list[str] | None = None) -> int:
|
|
145
|
+
"""Alias for cli()."""
|
|
146
|
+
return cli(args)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
if __name__ == "__main__":
|
|
150
|
+
sys.exit(cli())
|