projectmemory-cli 0.1.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.
- projectmemory_cli/__init__.py +3 -0
- projectmemory_cli/api/__init__.py +1 -0
- projectmemory_cli/api/auth.py +94 -0
- projectmemory_cli/api/client.py +135 -0
- projectmemory_cli/api/errors.py +124 -0
- projectmemory_cli/api/sessions.py +25 -0
- projectmemory_cli/app.py +80 -0
- projectmemory_cli/commands/__init__.py +1 -0
- projectmemory_cli/commands/auth.py +163 -0
- projectmemory_cli/commands/commits.py +147 -0
- projectmemory_cli/commands/config.py +56 -0
- projectmemory_cli/commands/doctor.py +375 -0
- projectmemory_cli/commands/issues.py +234 -0
- projectmemory_cli/commands/memory.py +235 -0
- projectmemory_cli/commands/next_action.py +282 -0
- projectmemory_cli/commands/notes.py +231 -0
- projectmemory_cli/commands/projects.py +325 -0
- projectmemory_cli/commands/resume.py +240 -0
- projectmemory_cli/commands/sessions.py +277 -0
- projectmemory_cli/commands/tasks.py +435 -0
- projectmemory_cli/config.py +101 -0
- projectmemory_cli/credentials.py +98 -0
- projectmemory_cli/git.py +124 -0
- projectmemory_cli/main.py +4 -0
- projectmemory_cli/models/__init__.py +1 -0
- projectmemory_cli/models/api.py +226 -0
- projectmemory_cli/output.py +227 -0
- projectmemory_cli/project_context.py +244 -0
- projectmemory_cli-0.1.0.dist-info/METADATA +239 -0
- projectmemory_cli-0.1.0.dist-info/RECORD +32 -0
- projectmemory_cli-0.1.0.dist-info/WHEEL +4 -0
- projectmemory_cli-0.1.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""Issue commands: pm issue list/show/add/edit/resolve/reopen/delete."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import questionary
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from projectmemory_cli import output
|
|
10
|
+
from projectmemory_cli.api.client import APIClient
|
|
11
|
+
from projectmemory_cli.api.errors import APIError
|
|
12
|
+
from projectmemory_cli.models.api import Issue
|
|
13
|
+
from projectmemory_cli.project_context import guard_project_session
|
|
14
|
+
|
|
15
|
+
app = typer.Typer(help="Issue commands.", no_args_is_help=True)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _require_client() -> APIClient:
|
|
19
|
+
from projectmemory_cli import credentials
|
|
20
|
+
token = credentials.get_token()
|
|
21
|
+
if not token:
|
|
22
|
+
output.error("Not logged in. Run: pm login")
|
|
23
|
+
raise SystemExit(2)
|
|
24
|
+
return APIClient.from_env()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _session_client(client: APIClient, session_id: int) -> APIClient:
|
|
28
|
+
return APIClient(base_url=client.base_url, token=client.token, session_id=session_id)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _display_issue(issue: Issue) -> None:
|
|
32
|
+
status = "✓ resolved" if issue.is_resolved else "✗ open"
|
|
33
|
+
na = " [dim]→ next action[/dim]" if issue.is_next_action else ""
|
|
34
|
+
output.info(f" {status} [bold]#{issue.id}[/bold] {issue.description}{na}")
|
|
35
|
+
if issue.resolution_notes:
|
|
36
|
+
output.hint(f" Resolution: {issue.resolution_notes[:80]}")
|
|
37
|
+
if issue.github_issue:
|
|
38
|
+
gh = issue.github_issue
|
|
39
|
+
output.hint(f" GitHub #{gh.get('number', '?')}: {gh.get('state', '')}")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@app.command("list")
|
|
43
|
+
def issue_list(
|
|
44
|
+
status: str = typer.Option("open", "--status", "-s", help="open|resolved|all"),
|
|
45
|
+
open_only: bool = typer.Option(False, "--open", help="Show open issues."),
|
|
46
|
+
resolved: bool = typer.Option(False, "--resolved", help="Show resolved issues."),
|
|
47
|
+
all_issues: bool = typer.Option(False, "--all", help="Show all issues."),
|
|
48
|
+
search: Optional[str] = typer.Option(None, "--search"),
|
|
49
|
+
json: bool = typer.Option(False, "--json"),
|
|
50
|
+
project_arg: Optional[str] = typer.Option(None, "--project", "-p"),
|
|
51
|
+
) -> None:
|
|
52
|
+
"""List issues for the current project."""
|
|
53
|
+
output.configure(json_mode=json)
|
|
54
|
+
client = _require_client()
|
|
55
|
+
project, session = guard_project_session(client, project_arg=project_arg)
|
|
56
|
+
|
|
57
|
+
if all_issues:
|
|
58
|
+
status = "all"
|
|
59
|
+
elif resolved:
|
|
60
|
+
status = "resolved"
|
|
61
|
+
elif open_only:
|
|
62
|
+
status = "open"
|
|
63
|
+
|
|
64
|
+
params: dict = {"status": status}
|
|
65
|
+
if search:
|
|
66
|
+
params["search"] = search
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
data = client.get(f"/api/cli/projects/{project.id}/issues/", params=params)
|
|
70
|
+
except APIError as exc:
|
|
71
|
+
output.error(str(exc))
|
|
72
|
+
raise SystemExit(exc.exit_code)
|
|
73
|
+
|
|
74
|
+
if json:
|
|
75
|
+
output.print_json(data)
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
issues = [Issue.model_validate(i) for i in data.get("issues", [])]
|
|
79
|
+
if not issues:
|
|
80
|
+
output.hint("No issues.")
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
headers = ["#", "Status", "Description", "Next Action"]
|
|
84
|
+
rows = [
|
|
85
|
+
[str(i.id), "resolved" if i.is_resolved else "open", i.description[:60], "✓" if i.is_next_action else ""]
|
|
86
|
+
for i in issues
|
|
87
|
+
]
|
|
88
|
+
output.table(headers, rows, title=f"{project.name} — Issues")
|
|
89
|
+
counts = data.get("counts", {})
|
|
90
|
+
if counts:
|
|
91
|
+
output.hint(f"open: {counts.get('open', 0)} resolved: {counts.get('resolved', 0)}")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@app.command("show")
|
|
95
|
+
def issue_show(
|
|
96
|
+
issue_id: int = typer.Argument(...),
|
|
97
|
+
json: bool = typer.Option(False, "--json"),
|
|
98
|
+
project_arg: Optional[str] = typer.Option(None, "--project", "-p"),
|
|
99
|
+
) -> None:
|
|
100
|
+
"""Show issue details."""
|
|
101
|
+
output.configure(json_mode=json)
|
|
102
|
+
client = _require_client()
|
|
103
|
+
guard_project_session(client, project_arg=project_arg)
|
|
104
|
+
try:
|
|
105
|
+
data = client.get(f"/api/cli/issues/{issue_id}/")
|
|
106
|
+
except APIError as exc:
|
|
107
|
+
output.error(str(exc))
|
|
108
|
+
raise SystemExit(exc.exit_code)
|
|
109
|
+
if json:
|
|
110
|
+
output.print_json(data)
|
|
111
|
+
return
|
|
112
|
+
issue = Issue.model_validate(data.get("issue", {}))
|
|
113
|
+
output.blank()
|
|
114
|
+
_display_issue(issue)
|
|
115
|
+
output.blank()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@app.command("add")
|
|
119
|
+
def issue_add(
|
|
120
|
+
description: Optional[str] = typer.Argument(None, help="Issue description."),
|
|
121
|
+
next_action: bool = typer.Option(False, "--next", help="Set as Next Action."),
|
|
122
|
+
project_arg: Optional[str] = typer.Option(None, "--project", "-p"),
|
|
123
|
+
) -> None:
|
|
124
|
+
"""Report a new issue."""
|
|
125
|
+
client = _require_client()
|
|
126
|
+
project, session = guard_project_session(client, project_arg=project_arg)
|
|
127
|
+
sc = _session_client(client, session.id)
|
|
128
|
+
|
|
129
|
+
if not description:
|
|
130
|
+
description = questionary.text("Issue description:").ask()
|
|
131
|
+
if not description:
|
|
132
|
+
raise SystemExit(7)
|
|
133
|
+
|
|
134
|
+
payload: dict = {"description": description}
|
|
135
|
+
if next_action:
|
|
136
|
+
payload["set_as_next_action"] = True
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
data = sc.post(f"/api/cli/projects/{project.id}/issues/", json=payload, project_name=project.name)
|
|
140
|
+
except APIError as exc:
|
|
141
|
+
output.error(str(exc))
|
|
142
|
+
raise SystemExit(exc.exit_code)
|
|
143
|
+
issue = Issue.model_validate(data.get("issue", {}))
|
|
144
|
+
output.success(f"Issue #{issue.id} created.")
|
|
145
|
+
if next_action:
|
|
146
|
+
output.success("Set as Next Action.")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@app.command("edit")
|
|
150
|
+
def issue_edit(
|
|
151
|
+
issue_id: int = typer.Argument(...),
|
|
152
|
+
description: Optional[str] = typer.Argument(None),
|
|
153
|
+
project_arg: Optional[str] = typer.Option(None, "--project", "-p"),
|
|
154
|
+
) -> None:
|
|
155
|
+
"""Edit an issue description."""
|
|
156
|
+
client = _require_client()
|
|
157
|
+
project, session = guard_project_session(client, project_arg=project_arg)
|
|
158
|
+
sc = _session_client(client, session.id)
|
|
159
|
+
|
|
160
|
+
if not description:
|
|
161
|
+
description = questionary.text("New description:").ask()
|
|
162
|
+
if not description:
|
|
163
|
+
raise SystemExit(7)
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
sc.patch(f"/api/cli/issues/{issue_id}/", json={"description": description}, project_name=project.name)
|
|
167
|
+
except APIError as exc:
|
|
168
|
+
output.error(str(exc))
|
|
169
|
+
raise SystemExit(exc.exit_code)
|
|
170
|
+
output.success(f"Issue #{issue_id} updated.")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@app.command("resolve")
|
|
174
|
+
def issue_resolve(
|
|
175
|
+
issue_id: int = typer.Argument(...),
|
|
176
|
+
notes: Optional[str] = typer.Option(None, "--resolution", "--notes", "-n", help="Resolution notes."),
|
|
177
|
+
project_arg: Optional[str] = typer.Option(None, "--project", "-p"),
|
|
178
|
+
) -> None:
|
|
179
|
+
"""Mark an issue as resolved."""
|
|
180
|
+
client = _require_client()
|
|
181
|
+
project, session = guard_project_session(client, project_arg=project_arg)
|
|
182
|
+
sc = _session_client(client, session.id)
|
|
183
|
+
|
|
184
|
+
if notes is None:
|
|
185
|
+
notes = questionary.text("Resolution notes (optional):").ask() or ""
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
sc.post(f"/api/cli/issues/{issue_id}/resolve/", json={"resolution_notes": notes}, project_name=project.name)
|
|
189
|
+
except APIError as exc:
|
|
190
|
+
output.error(str(exc))
|
|
191
|
+
raise SystemExit(exc.exit_code)
|
|
192
|
+
output.success(f"Issue #{issue_id} resolved.")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@app.command("reopen")
|
|
196
|
+
def issue_reopen(
|
|
197
|
+
issue_id: int = typer.Argument(...),
|
|
198
|
+
project_arg: Optional[str] = typer.Option(None, "--project", "-p"),
|
|
199
|
+
) -> None:
|
|
200
|
+
"""Reopen a resolved issue."""
|
|
201
|
+
client = _require_client()
|
|
202
|
+
project, session = guard_project_session(client, project_arg=project_arg)
|
|
203
|
+
sc = _session_client(client, session.id)
|
|
204
|
+
try:
|
|
205
|
+
sc.post(f"/api/cli/issues/{issue_id}/reopen/", project_name=project.name)
|
|
206
|
+
except APIError as exc:
|
|
207
|
+
output.error(str(exc))
|
|
208
|
+
raise SystemExit(exc.exit_code)
|
|
209
|
+
output.success(f"Issue #{issue_id} reopened.")
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@app.command("delete")
|
|
213
|
+
def issue_delete(
|
|
214
|
+
issue_id: int = typer.Argument(...),
|
|
215
|
+
yes: bool = typer.Option(False, "--yes", "-y"),
|
|
216
|
+
project_arg: Optional[str] = typer.Option(None, "--project", "-p"),
|
|
217
|
+
) -> None:
|
|
218
|
+
"""Delete an issue."""
|
|
219
|
+
client = _require_client()
|
|
220
|
+
project, session = guard_project_session(client, project_arg=project_arg)
|
|
221
|
+
sc = _session_client(client, session.id)
|
|
222
|
+
|
|
223
|
+
if not yes:
|
|
224
|
+
confirmed = typer.confirm(f"Delete issue #{issue_id}?", default=False)
|
|
225
|
+
if not confirmed:
|
|
226
|
+
output.info("Cancelled.")
|
|
227
|
+
raise SystemExit(7)
|
|
228
|
+
|
|
229
|
+
try:
|
|
230
|
+
sc.delete(f"/api/cli/issues/{issue_id}/", project_name=project.name)
|
|
231
|
+
except APIError as exc:
|
|
232
|
+
output.error(str(exc))
|
|
233
|
+
raise SystemExit(exc.exit_code)
|
|
234
|
+
output.success(f"Issue #{issue_id} deleted.")
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""Memory commands: pm memory show, pm memory status set, pm memory context set, pm memory update."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
import tempfile
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
import questionary
|
|
11
|
+
import typer
|
|
12
|
+
|
|
13
|
+
from projectmemory_cli import config, output
|
|
14
|
+
from projectmemory_cli.api.client import APIClient
|
|
15
|
+
from projectmemory_cli.api.errors import APIError
|
|
16
|
+
from projectmemory_cli.models.api import Memory
|
|
17
|
+
from projectmemory_cli.project_context import guard_project_session
|
|
18
|
+
|
|
19
|
+
app = typer.Typer(help="Project Memory commands.", no_args_is_help=True)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _require_client() -> APIClient:
|
|
23
|
+
from projectmemory_cli import credentials
|
|
24
|
+
token = credentials.get_token()
|
|
25
|
+
if not token:
|
|
26
|
+
output.error("Not logged in. Run: pm login")
|
|
27
|
+
raise SystemExit(2)
|
|
28
|
+
return APIClient.from_env()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _open_in_editor(initial: str = "") -> str:
|
|
32
|
+
editor = config.get_editor()
|
|
33
|
+
with tempfile.NamedTemporaryFile(suffix=".txt", mode="w", delete=False) as f:
|
|
34
|
+
f.write(initial)
|
|
35
|
+
fname = f.name
|
|
36
|
+
try:
|
|
37
|
+
subprocess.call([editor, fname])
|
|
38
|
+
return Path(fname).read_text().strip()
|
|
39
|
+
finally:
|
|
40
|
+
Path(fname).unlink(missing_ok=True)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _display_memory(memory: Memory, project_name: str = "") -> None:
|
|
44
|
+
if project_name:
|
|
45
|
+
output.project_banner(project_name)
|
|
46
|
+
output.blank()
|
|
47
|
+
|
|
48
|
+
output.header("Next Action")
|
|
49
|
+
na = memory.next_action
|
|
50
|
+
if na.title:
|
|
51
|
+
output.info(f" {na.title}")
|
|
52
|
+
if na.linked_source:
|
|
53
|
+
src = na.linked_source
|
|
54
|
+
output.hint(f" Linked {src.type} #{src.id}")
|
|
55
|
+
else:
|
|
56
|
+
output.hint(" (none)")
|
|
57
|
+
|
|
58
|
+
output.blank()
|
|
59
|
+
output.header("Current Status")
|
|
60
|
+
if memory.current_status.title:
|
|
61
|
+
output.info(f" {memory.current_status.title}")
|
|
62
|
+
else:
|
|
63
|
+
output.hint(" (none)")
|
|
64
|
+
|
|
65
|
+
output.blank()
|
|
66
|
+
output.header("Important Context")
|
|
67
|
+
if memory.important_context.title:
|
|
68
|
+
output.info(f" {memory.important_context.title}")
|
|
69
|
+
else:
|
|
70
|
+
output.hint(" (none)")
|
|
71
|
+
|
|
72
|
+
output.blank()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@app.command("show")
|
|
76
|
+
def memory_show(
|
|
77
|
+
project_arg: Optional[str] = typer.Option(None, "--project", "-p"),
|
|
78
|
+
json: bool = typer.Option(False, "--json"),
|
|
79
|
+
) -> None:
|
|
80
|
+
"""Display the current Project Memory."""
|
|
81
|
+
client = _require_client()
|
|
82
|
+
try:
|
|
83
|
+
project, session = guard_project_session(client, project_arg=project_arg)
|
|
84
|
+
except SystemExit:
|
|
85
|
+
raise
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
data = client.get(f"/api/cli/projects/{project.id}/memory/")
|
|
89
|
+
except APIError as exc:
|
|
90
|
+
output.error(str(exc))
|
|
91
|
+
raise SystemExit(exc.exit_code)
|
|
92
|
+
|
|
93
|
+
if json:
|
|
94
|
+
output.print_json(data)
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
memory = Memory.model_validate(data.get("memory", {}))
|
|
98
|
+
_display_memory(memory, project_name=project.name)
|
|
99
|
+
if project.last_memory_updated_at:
|
|
100
|
+
output.kv("Last memory updated", output.format_ago(project.last_memory_updated_at))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# ---------------------------------------------------------------------------
|
|
104
|
+
# pm memory status set
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
status_app = typer.Typer(help="Current Status commands.", no_args_is_help=False)
|
|
108
|
+
app.add_typer(status_app, name="status")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@status_app.command("set")
|
|
112
|
+
def memory_status_set(
|
|
113
|
+
text: Optional[str] = typer.Argument(None, help="Status text."),
|
|
114
|
+
editor: bool = typer.Option(False, "--editor", "-e", help="Open in editor."),
|
|
115
|
+
stdin: bool = typer.Option(False, "--stdin", help="Read from stdin."),
|
|
116
|
+
project_arg: Optional[str] = typer.Option(None, "--project", "-p"),
|
|
117
|
+
) -> None:
|
|
118
|
+
"""Set the Current Status."""
|
|
119
|
+
client = _require_client()
|
|
120
|
+
project, session = guard_project_session(client, project_arg=project_arg)
|
|
121
|
+
session_client = APIClient(base_url=client.base_url, token=client.token, session_id=session.id)
|
|
122
|
+
|
|
123
|
+
if stdin:
|
|
124
|
+
text = sys.stdin.read().strip()
|
|
125
|
+
elif editor:
|
|
126
|
+
text = _open_in_editor(text or "")
|
|
127
|
+
elif not text:
|
|
128
|
+
text = questionary.text("Current Status:").ask() or ""
|
|
129
|
+
|
|
130
|
+
if not text:
|
|
131
|
+
output.info("No changes made.")
|
|
132
|
+
raise SystemExit(0)
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
session_client.patch(
|
|
136
|
+
f"/api/cli/projects/{project.id}/memory/",
|
|
137
|
+
json={"current_status": {"title": text}},
|
|
138
|
+
project_name=project.name,
|
|
139
|
+
)
|
|
140
|
+
except APIError as exc:
|
|
141
|
+
output.error(str(exc))
|
|
142
|
+
raise SystemExit(exc.exit_code)
|
|
143
|
+
output.success(f"Current Status updated.")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# ---------------------------------------------------------------------------
|
|
147
|
+
# pm memory context set
|
|
148
|
+
# ---------------------------------------------------------------------------
|
|
149
|
+
|
|
150
|
+
context_app = typer.Typer(help="Important Context commands.", no_args_is_help=False)
|
|
151
|
+
app.add_typer(context_app, name="context")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@context_app.command("set")
|
|
155
|
+
def memory_context_set(
|
|
156
|
+
text: Optional[str] = typer.Argument(None, help="Context text."),
|
|
157
|
+
editor: bool = typer.Option(False, "--editor", "-e", help="Open in editor."),
|
|
158
|
+
stdin: bool = typer.Option(False, "--stdin", help="Read from stdin."),
|
|
159
|
+
project_arg: Optional[str] = typer.Option(None, "--project", "-p"),
|
|
160
|
+
) -> None:
|
|
161
|
+
"""Set the Important Context."""
|
|
162
|
+
client = _require_client()
|
|
163
|
+
project, session = guard_project_session(client, project_arg=project_arg)
|
|
164
|
+
session_client = APIClient(base_url=client.base_url, token=client.token, session_id=session.id)
|
|
165
|
+
|
|
166
|
+
if stdin:
|
|
167
|
+
text = sys.stdin.read().strip()
|
|
168
|
+
elif editor:
|
|
169
|
+
text = _open_in_editor(text or "")
|
|
170
|
+
elif not text:
|
|
171
|
+
text = questionary.text("Important Context:").ask() or ""
|
|
172
|
+
|
|
173
|
+
if not text:
|
|
174
|
+
output.info("No changes made.")
|
|
175
|
+
raise SystemExit(0)
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
session_client.patch(
|
|
179
|
+
f"/api/cli/projects/{project.id}/memory/",
|
|
180
|
+
json={"important_context": {"title": text}},
|
|
181
|
+
project_name=project.name,
|
|
182
|
+
)
|
|
183
|
+
except APIError as exc:
|
|
184
|
+
output.error(str(exc))
|
|
185
|
+
raise SystemExit(exc.exit_code)
|
|
186
|
+
output.success("Important Context updated.")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# ---------------------------------------------------------------------------
|
|
190
|
+
# pm memory update (interactive menu)
|
|
191
|
+
# ---------------------------------------------------------------------------
|
|
192
|
+
|
|
193
|
+
@app.command("update")
|
|
194
|
+
def memory_update(
|
|
195
|
+
project_arg: Optional[str] = typer.Option(None, "--project", "-p"),
|
|
196
|
+
) -> None:
|
|
197
|
+
"""Interactively update Project Memory fields."""
|
|
198
|
+
client = _require_client()
|
|
199
|
+
project, session = guard_project_session(client, project_arg=project_arg)
|
|
200
|
+
session_client = APIClient(base_url=client.base_url, token=client.token, session_id=session.id)
|
|
201
|
+
|
|
202
|
+
choice = questionary.select(
|
|
203
|
+
"Update:",
|
|
204
|
+
choices=[
|
|
205
|
+
questionary.Choice("Next Action", value="next"),
|
|
206
|
+
questionary.Choice("Current Status", value="status"),
|
|
207
|
+
questionary.Choice("Important Context", value="context"),
|
|
208
|
+
questionary.Choice("Open Issues", value="issues"),
|
|
209
|
+
],
|
|
210
|
+
).ask()
|
|
211
|
+
|
|
212
|
+
if not choice:
|
|
213
|
+
raise SystemExit(7)
|
|
214
|
+
|
|
215
|
+
if choice == "next":
|
|
216
|
+
from projectmemory_cli.commands.next_action import next_set
|
|
217
|
+
next_set(text=None, project_arg=project_arg)
|
|
218
|
+
elif choice == "status":
|
|
219
|
+
memory_status_set(text=None, editor=False, stdin=False, project_arg=project_arg)
|
|
220
|
+
elif choice == "context":
|
|
221
|
+
memory_context_set(text=None, editor=False, stdin=False, project_arg=project_arg)
|
|
222
|
+
elif choice == "issues":
|
|
223
|
+
from projectmemory_cli.commands.issues import issue_add, issue_list
|
|
224
|
+
action = questionary.select(
|
|
225
|
+
"Open Issues:",
|
|
226
|
+
choices=[
|
|
227
|
+
questionary.Choice("List open issues", value="list"),
|
|
228
|
+
questionary.Choice("Add issue", value="add"),
|
|
229
|
+
questionary.Choice("Cancel", value="cancel"),
|
|
230
|
+
],
|
|
231
|
+
).ask()
|
|
232
|
+
if action == "list":
|
|
233
|
+
issue_list(status="open", open_only=True, resolved=False, all_issues=False, search=None, json=False, project_arg=project_arg)
|
|
234
|
+
elif action == "add":
|
|
235
|
+
issue_add(description=None, next_action=False, project_arg=project_arg)
|