issuedb 1.0.0__py3-none-any.whl
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.
- issuedb/__init__.py +9 -0
- issuedb/cli.py +520 -0
- issuedb/database.py +194 -0
- issuedb/models.py +136 -0
- issuedb/py.typed +0 -0
- issuedb/repository.py +467 -0
- issuedb-1.0.0.dist-info/METADATA +414 -0
- issuedb-1.0.0.dist-info/RECORD +12 -0
- issuedb-1.0.0.dist-info/WHEEL +5 -0
- issuedb-1.0.0.dist-info/entry_points.txt +2 -0
- issuedb-1.0.0.dist-info/licenses/LICENSE +21 -0
- issuedb-1.0.0.dist-info/top_level.txt +1 -0
issuedb/__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""IssueDB - A command-line issue tracking system for software development projects."""
|
|
2
|
+
|
|
3
|
+
__version__ = "0.1.0"
|
|
4
|
+
__author__ = "Your Name"
|
|
5
|
+
__email__ = "your.email@example.com"
|
|
6
|
+
|
|
7
|
+
from issuedb.models import Issue, Priority, Status
|
|
8
|
+
|
|
9
|
+
__all__ = ["Issue", "Priority", "Status"]
|
issuedb/cli.py
ADDED
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
"""Command-line interface for IssueDB."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
from typing import Any, Optional
|
|
7
|
+
|
|
8
|
+
from issuedb.models import Issue, Priority, Status
|
|
9
|
+
from issuedb.repository import IssueRepository
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CLI:
|
|
13
|
+
"""Command-line interface handler."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, db_path: Optional[str] = None) -> None:
|
|
16
|
+
"""Initialize CLI with repository.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
db_path: Optional path to database file.
|
|
20
|
+
"""
|
|
21
|
+
self.repo = IssueRepository(db_path)
|
|
22
|
+
|
|
23
|
+
def format_output(self, data: Any, as_json: bool = False) -> str:
|
|
24
|
+
"""Format output for display.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
data: Data to format (Issue, list of Issues, dict, etc).
|
|
28
|
+
as_json: If True, output as JSON.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Formatted string output.
|
|
32
|
+
"""
|
|
33
|
+
if as_json:
|
|
34
|
+
if isinstance(data, Issue):
|
|
35
|
+
return json.dumps(data.to_dict(), indent=2)
|
|
36
|
+
elif isinstance(data, list) and all(isinstance(i, Issue) for i in data):
|
|
37
|
+
return json.dumps([i.to_dict() for i in data], indent=2)
|
|
38
|
+
elif isinstance(data, dict):
|
|
39
|
+
return json.dumps(data, indent=2)
|
|
40
|
+
else:
|
|
41
|
+
return json.dumps(data, indent=2)
|
|
42
|
+
else:
|
|
43
|
+
if isinstance(data, Issue):
|
|
44
|
+
return self._format_issue(data)
|
|
45
|
+
elif isinstance(data, list) and all(isinstance(i, Issue) for i in data):
|
|
46
|
+
if not data:
|
|
47
|
+
return "No issues found."
|
|
48
|
+
return "\n\n".join(self._format_issue(i) for i in data)
|
|
49
|
+
elif isinstance(data, dict):
|
|
50
|
+
return self._format_dict(data)
|
|
51
|
+
else:
|
|
52
|
+
return str(data)
|
|
53
|
+
|
|
54
|
+
def _format_issue(self, issue: Issue) -> str:
|
|
55
|
+
"""Format a single issue for display.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
issue: Issue to format.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Formatted string.
|
|
62
|
+
"""
|
|
63
|
+
lines = [
|
|
64
|
+
f"ID: {issue.id}",
|
|
65
|
+
f"Title: {issue.title}",
|
|
66
|
+
f"Project: {issue.project}",
|
|
67
|
+
f"Status: {issue.status.value}",
|
|
68
|
+
f"Priority: {issue.priority.value}",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
if issue.description:
|
|
72
|
+
lines.append(f"Description: {issue.description}")
|
|
73
|
+
|
|
74
|
+
lines.extend(
|
|
75
|
+
[
|
|
76
|
+
f"Created: {issue.created_at.strftime('%Y-%m-%d %H:%M:%S')}",
|
|
77
|
+
f"Updated: {issue.updated_at.strftime('%Y-%m-%d %H:%M:%S')}",
|
|
78
|
+
]
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return "\n".join(lines)
|
|
82
|
+
|
|
83
|
+
def _format_dict(self, data: dict) -> str:
|
|
84
|
+
"""Format a dictionary for display.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
data: Dictionary to format.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Formatted string.
|
|
91
|
+
"""
|
|
92
|
+
lines = []
|
|
93
|
+
for key, value in data.items():
|
|
94
|
+
formatted_key = key.replace("_", " ").title()
|
|
95
|
+
lines.append(f"{formatted_key}: {value}")
|
|
96
|
+
return "\n".join(lines)
|
|
97
|
+
|
|
98
|
+
def create_issue(
|
|
99
|
+
self,
|
|
100
|
+
title: str,
|
|
101
|
+
project: str,
|
|
102
|
+
description: Optional[str] = None,
|
|
103
|
+
priority: str = "medium",
|
|
104
|
+
status: str = "open",
|
|
105
|
+
as_json: bool = False,
|
|
106
|
+
) -> str:
|
|
107
|
+
"""Create a new issue.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
title: Issue title.
|
|
111
|
+
project: Project name.
|
|
112
|
+
description: Optional description.
|
|
113
|
+
priority: Priority level.
|
|
114
|
+
status: Initial status.
|
|
115
|
+
as_json: Output as JSON.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Formatted output.
|
|
119
|
+
"""
|
|
120
|
+
issue = Issue(
|
|
121
|
+
title=title,
|
|
122
|
+
project=project,
|
|
123
|
+
description=description,
|
|
124
|
+
priority=Priority.from_string(priority),
|
|
125
|
+
status=Status.from_string(status),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
created_issue = self.repo.create_issue(issue)
|
|
129
|
+
return self.format_output(created_issue, as_json)
|
|
130
|
+
|
|
131
|
+
def list_issues(
|
|
132
|
+
self,
|
|
133
|
+
project: Optional[str] = None,
|
|
134
|
+
status: Optional[str] = None,
|
|
135
|
+
priority: Optional[str] = None,
|
|
136
|
+
limit: Optional[int] = None,
|
|
137
|
+
as_json: bool = False,
|
|
138
|
+
) -> str:
|
|
139
|
+
"""List issues with filters.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
project: Filter by project.
|
|
143
|
+
status: Filter by status.
|
|
144
|
+
priority: Filter by priority.
|
|
145
|
+
limit: Maximum number of issues.
|
|
146
|
+
as_json: Output as JSON.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Formatted output.
|
|
150
|
+
"""
|
|
151
|
+
issues = self.repo.list_issues(
|
|
152
|
+
project=project, status=status, priority=priority, limit=limit
|
|
153
|
+
)
|
|
154
|
+
return self.format_output(issues, as_json)
|
|
155
|
+
|
|
156
|
+
def get_issue(self, issue_id: int, as_json: bool = False) -> str:
|
|
157
|
+
"""Get a specific issue.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
issue_id: Issue ID.
|
|
161
|
+
as_json: Output as JSON.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Formatted output.
|
|
165
|
+
|
|
166
|
+
Raises:
|
|
167
|
+
ValueError: If issue not found.
|
|
168
|
+
"""
|
|
169
|
+
issue = self.repo.get_issue(issue_id)
|
|
170
|
+
if not issue:
|
|
171
|
+
raise ValueError(f"Issue {issue_id} not found")
|
|
172
|
+
return self.format_output(issue, as_json)
|
|
173
|
+
|
|
174
|
+
def update_issue(self, issue_id: int, as_json: bool = False, **updates) -> str:
|
|
175
|
+
"""Update an issue.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
issue_id: Issue ID.
|
|
179
|
+
as_json: Output as JSON.
|
|
180
|
+
**updates: Fields to update.
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
Formatted output.
|
|
184
|
+
|
|
185
|
+
Raises:
|
|
186
|
+
ValueError: If issue not found.
|
|
187
|
+
"""
|
|
188
|
+
issue = self.repo.update_issue(issue_id, **updates)
|
|
189
|
+
if not issue:
|
|
190
|
+
raise ValueError(f"Issue {issue_id} not found")
|
|
191
|
+
return self.format_output(issue, as_json)
|
|
192
|
+
|
|
193
|
+
def delete_issue(self, issue_id: int, as_json: bool = False) -> str:
|
|
194
|
+
"""Delete an issue.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
issue_id: Issue ID.
|
|
198
|
+
as_json: Output as JSON.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
Formatted output.
|
|
202
|
+
|
|
203
|
+
Raises:
|
|
204
|
+
ValueError: If issue not found.
|
|
205
|
+
"""
|
|
206
|
+
if not self.repo.delete_issue(issue_id):
|
|
207
|
+
raise ValueError(f"Issue {issue_id} not found")
|
|
208
|
+
|
|
209
|
+
result = {"message": f"Issue {issue_id} deleted successfully"}
|
|
210
|
+
return self.format_output(result, as_json)
|
|
211
|
+
|
|
212
|
+
def get_next_issue(
|
|
213
|
+
self, project: Optional[str] = None, status: Optional[str] = None, as_json: bool = False
|
|
214
|
+
) -> str:
|
|
215
|
+
"""Get next issue to work on.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
project: Filter by project.
|
|
219
|
+
status: Filter by status.
|
|
220
|
+
as_json: Output as JSON.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
Formatted output.
|
|
224
|
+
"""
|
|
225
|
+
issue = self.repo.get_next_issue(project=project, status=status)
|
|
226
|
+
if not issue:
|
|
227
|
+
result = {"message": "No issues found matching criteria"}
|
|
228
|
+
return self.format_output(result, as_json)
|
|
229
|
+
return self.format_output(issue, as_json)
|
|
230
|
+
|
|
231
|
+
def search_issues(
|
|
232
|
+
self,
|
|
233
|
+
keyword: str,
|
|
234
|
+
project: Optional[str] = None,
|
|
235
|
+
limit: Optional[int] = None,
|
|
236
|
+
as_json: bool = False,
|
|
237
|
+
) -> str:
|
|
238
|
+
"""Search issues by keyword.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
keyword: Search keyword.
|
|
242
|
+
project: Filter by project.
|
|
243
|
+
limit: Maximum results.
|
|
244
|
+
as_json: Output as JSON.
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
Formatted output.
|
|
248
|
+
"""
|
|
249
|
+
issues = self.repo.search_issues(keyword=keyword, project=project, limit=limit)
|
|
250
|
+
return self.format_output(issues, as_json)
|
|
251
|
+
|
|
252
|
+
def clear_project(self, project: str, confirm: bool = False, as_json: bool = False) -> str:
|
|
253
|
+
"""Clear all issues for a project.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
project: Project name.
|
|
257
|
+
confirm: Safety confirmation.
|
|
258
|
+
as_json: Output as JSON.
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
Formatted output.
|
|
262
|
+
|
|
263
|
+
Raises:
|
|
264
|
+
ValueError: If not confirmed.
|
|
265
|
+
"""
|
|
266
|
+
if not confirm:
|
|
267
|
+
raise ValueError("Must use --confirm flag to clear project")
|
|
268
|
+
|
|
269
|
+
count = self.repo.clear_project(project)
|
|
270
|
+
result = {"message": f"Cleared {count} issues from project {project}"}
|
|
271
|
+
return self.format_output(result, as_json)
|
|
272
|
+
|
|
273
|
+
def get_audit_logs(
|
|
274
|
+
self,
|
|
275
|
+
issue_id: Optional[int] = None,
|
|
276
|
+
project: Optional[str] = None,
|
|
277
|
+
as_json: bool = False,
|
|
278
|
+
) -> str:
|
|
279
|
+
"""Get audit logs.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
issue_id: Filter by issue ID.
|
|
283
|
+
project: Filter by project.
|
|
284
|
+
as_json: Output as JSON.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
Formatted output.
|
|
288
|
+
"""
|
|
289
|
+
logs = self.repo.get_audit_logs(issue_id=issue_id, project=project)
|
|
290
|
+
|
|
291
|
+
if as_json:
|
|
292
|
+
return json.dumps([log.to_dict() for log in logs], indent=2)
|
|
293
|
+
else:
|
|
294
|
+
if not logs:
|
|
295
|
+
return "No audit logs found."
|
|
296
|
+
|
|
297
|
+
lines = []
|
|
298
|
+
for log in logs:
|
|
299
|
+
lines.append("-" * 50)
|
|
300
|
+
lines.append(f"Timestamp: {log.timestamp.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
301
|
+
lines.append(f"Issue ID: {log.issue_id}")
|
|
302
|
+
lines.append(f"Project: {log.project}")
|
|
303
|
+
lines.append(f"Action: {log.action}")
|
|
304
|
+
|
|
305
|
+
if log.field_name:
|
|
306
|
+
lines.append(f"Field: {log.field_name}")
|
|
307
|
+
lines.append(f"Old Value: {log.old_value}")
|
|
308
|
+
lines.append(f"New Value: {log.new_value}")
|
|
309
|
+
elif log.action == "CREATE":
|
|
310
|
+
lines.append(f"Created: {log.new_value}")
|
|
311
|
+
elif log.action == "DELETE":
|
|
312
|
+
lines.append(f"Deleted: {log.old_value}")
|
|
313
|
+
|
|
314
|
+
return "\n".join(lines)
|
|
315
|
+
|
|
316
|
+
def get_info(self, as_json: bool = False) -> str:
|
|
317
|
+
"""Get database information.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
as_json: Output as JSON.
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
Formatted output.
|
|
324
|
+
"""
|
|
325
|
+
info = self.repo.db.get_database_info()
|
|
326
|
+
return self.format_output(info, as_json)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def main() -> None:
|
|
330
|
+
"""Main entry point for the CLI."""
|
|
331
|
+
parser = argparse.ArgumentParser(
|
|
332
|
+
prog="issuedb-cli",
|
|
333
|
+
description="Command-line issue tracking system for software development projects",
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
parser.add_argument(
|
|
337
|
+
"--db",
|
|
338
|
+
help="Path to database file (default: ~/.issuedb/issuedb.sqlite)",
|
|
339
|
+
default=None,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
parser.add_argument(
|
|
343
|
+
"--json",
|
|
344
|
+
action="store_true",
|
|
345
|
+
help="Output results in JSON format",
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
349
|
+
|
|
350
|
+
# Create command
|
|
351
|
+
create_parser = subparsers.add_parser("create", help="Create a new issue")
|
|
352
|
+
create_parser.add_argument("-t", "--title", required=True, help="Issue title")
|
|
353
|
+
create_parser.add_argument("-p", "--project", required=True, help="Project name")
|
|
354
|
+
create_parser.add_argument("-d", "--description", help="Issue description")
|
|
355
|
+
create_parser.add_argument(
|
|
356
|
+
"--priority",
|
|
357
|
+
choices=["low", "medium", "high", "critical"],
|
|
358
|
+
default="medium",
|
|
359
|
+
help="Priority level",
|
|
360
|
+
)
|
|
361
|
+
create_parser.add_argument(
|
|
362
|
+
"--status",
|
|
363
|
+
choices=["open", "in-progress", "closed"],
|
|
364
|
+
default="open",
|
|
365
|
+
help="Initial status",
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
# List command
|
|
369
|
+
list_parser = subparsers.add_parser("list", help="List issues")
|
|
370
|
+
list_parser.add_argument("-p", "--project", help="Filter by project")
|
|
371
|
+
list_parser.add_argument("-s", "--status", help="Filter by status (open, in-progress, closed)")
|
|
372
|
+
list_parser.add_argument("--priority", help="Filter by priority (low, medium, high, critical)")
|
|
373
|
+
list_parser.add_argument("-l", "--limit", type=int, help="Maximum number of issues")
|
|
374
|
+
|
|
375
|
+
# Get command
|
|
376
|
+
get_parser = subparsers.add_parser("get", help="Get issue details")
|
|
377
|
+
get_parser.add_argument("id", type=int, help="Issue ID")
|
|
378
|
+
|
|
379
|
+
# Update command
|
|
380
|
+
update_parser = subparsers.add_parser("update", help="Update an issue")
|
|
381
|
+
update_parser.add_argument("id", type=int, help="Issue ID")
|
|
382
|
+
update_parser.add_argument("-t", "--title", help="New title")
|
|
383
|
+
update_parser.add_argument("-p", "--project", help="New project")
|
|
384
|
+
update_parser.add_argument("-d", "--description", help="New description")
|
|
385
|
+
update_parser.add_argument(
|
|
386
|
+
"--priority",
|
|
387
|
+
choices=["low", "medium", "high", "critical"],
|
|
388
|
+
help="New priority",
|
|
389
|
+
)
|
|
390
|
+
update_parser.add_argument(
|
|
391
|
+
"-s",
|
|
392
|
+
"--status",
|
|
393
|
+
choices=["open", "in-progress", "closed"],
|
|
394
|
+
help="New status",
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
# Delete command
|
|
398
|
+
delete_parser = subparsers.add_parser("delete", help="Delete an issue")
|
|
399
|
+
delete_parser.add_argument("id", type=int, help="Issue ID")
|
|
400
|
+
|
|
401
|
+
# Get-next command
|
|
402
|
+
next_parser = subparsers.add_parser(
|
|
403
|
+
"get-next", help="Get next issue to work on (FIFO by priority)"
|
|
404
|
+
)
|
|
405
|
+
next_parser.add_argument("-p", "--project", help="Filter by project")
|
|
406
|
+
next_parser.add_argument("-s", "--status", help="Filter by status (defaults to 'open')")
|
|
407
|
+
|
|
408
|
+
# Search command
|
|
409
|
+
search_parser = subparsers.add_parser("search", help="Search issues by keyword")
|
|
410
|
+
search_parser.add_argument("-k", "--keyword", required=True, help="Search keyword")
|
|
411
|
+
search_parser.add_argument("-p", "--project", help="Filter by project")
|
|
412
|
+
search_parser.add_argument("-l", "--limit", type=int, help="Maximum results")
|
|
413
|
+
|
|
414
|
+
# Clear command
|
|
415
|
+
clear_parser = subparsers.add_parser("clear", help="Clear all issues for a project")
|
|
416
|
+
clear_parser.add_argument("-p", "--project", required=True, help="Project name")
|
|
417
|
+
clear_parser.add_argument("--confirm", action="store_true", help="Confirm deletion (required)")
|
|
418
|
+
|
|
419
|
+
# Audit command
|
|
420
|
+
audit_parser = subparsers.add_parser("audit", help="View audit logs")
|
|
421
|
+
audit_parser.add_argument("-i", "--issue", type=int, help="Filter by issue ID")
|
|
422
|
+
audit_parser.add_argument("-p", "--project", help="Filter by project")
|
|
423
|
+
|
|
424
|
+
# Info command
|
|
425
|
+
subparsers.add_parser("info", help="Get database information")
|
|
426
|
+
|
|
427
|
+
args = parser.parse_args()
|
|
428
|
+
|
|
429
|
+
if not args.command:
|
|
430
|
+
parser.print_help()
|
|
431
|
+
sys.exit(1)
|
|
432
|
+
|
|
433
|
+
try:
|
|
434
|
+
cli = CLI(args.db)
|
|
435
|
+
|
|
436
|
+
if args.command == "create":
|
|
437
|
+
result = cli.create_issue(
|
|
438
|
+
title=args.title,
|
|
439
|
+
project=args.project,
|
|
440
|
+
description=args.description,
|
|
441
|
+
priority=args.priority,
|
|
442
|
+
status=args.status,
|
|
443
|
+
as_json=args.json,
|
|
444
|
+
)
|
|
445
|
+
print(result)
|
|
446
|
+
|
|
447
|
+
elif args.command == "list":
|
|
448
|
+
result = cli.list_issues(
|
|
449
|
+
project=args.project,
|
|
450
|
+
status=args.status,
|
|
451
|
+
priority=args.priority,
|
|
452
|
+
limit=args.limit,
|
|
453
|
+
as_json=args.json,
|
|
454
|
+
)
|
|
455
|
+
print(result)
|
|
456
|
+
|
|
457
|
+
elif args.command == "get":
|
|
458
|
+
result = cli.get_issue(args.id, as_json=args.json)
|
|
459
|
+
print(result)
|
|
460
|
+
|
|
461
|
+
elif args.command == "update":
|
|
462
|
+
updates = {}
|
|
463
|
+
if args.title:
|
|
464
|
+
updates["title"] = args.title
|
|
465
|
+
if args.project:
|
|
466
|
+
updates["project"] = args.project
|
|
467
|
+
if args.description:
|
|
468
|
+
updates["description"] = args.description
|
|
469
|
+
if args.priority:
|
|
470
|
+
updates["priority"] = args.priority
|
|
471
|
+
if args.status:
|
|
472
|
+
updates["status"] = args.status
|
|
473
|
+
|
|
474
|
+
if not updates:
|
|
475
|
+
print("Error: No updates specified", file=sys.stderr)
|
|
476
|
+
sys.exit(1)
|
|
477
|
+
|
|
478
|
+
result = cli.update_issue(args.id, as_json=args.json, **updates)
|
|
479
|
+
print(result)
|
|
480
|
+
|
|
481
|
+
elif args.command == "delete":
|
|
482
|
+
result = cli.delete_issue(args.id, as_json=args.json)
|
|
483
|
+
print(result)
|
|
484
|
+
|
|
485
|
+
elif args.command == "get-next":
|
|
486
|
+
result = cli.get_next_issue(project=args.project, status=args.status, as_json=args.json)
|
|
487
|
+
print(result)
|
|
488
|
+
|
|
489
|
+
elif args.command == "search":
|
|
490
|
+
result = cli.search_issues(
|
|
491
|
+
keyword=args.keyword,
|
|
492
|
+
project=args.project,
|
|
493
|
+
limit=args.limit,
|
|
494
|
+
as_json=args.json,
|
|
495
|
+
)
|
|
496
|
+
print(result)
|
|
497
|
+
|
|
498
|
+
elif args.command == "clear":
|
|
499
|
+
result = cli.clear_project(
|
|
500
|
+
project=args.project, confirm=args.confirm, as_json=args.json
|
|
501
|
+
)
|
|
502
|
+
print(result)
|
|
503
|
+
|
|
504
|
+
elif args.command == "audit":
|
|
505
|
+
result = cli.get_audit_logs(
|
|
506
|
+
issue_id=args.issue, project=args.project, as_json=args.json
|
|
507
|
+
)
|
|
508
|
+
print(result)
|
|
509
|
+
|
|
510
|
+
elif args.command == "info":
|
|
511
|
+
result = cli.get_info(as_json=args.json)
|
|
512
|
+
print(result)
|
|
513
|
+
|
|
514
|
+
except Exception as e:
|
|
515
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
516
|
+
sys.exit(1)
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
if __name__ == "__main__":
|
|
520
|
+
main()
|