multi-agent-platform 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.
- cli/__init__.py +0 -0
- cli/action_item_escalation.py +177 -0
- cli/agent_client.py +554 -0
- cli/bridge_state.py +43 -0
- cli/commands/__init__.py +13 -0
- cli/commands/action.py +142 -0
- cli/commands/agent.py +117 -0
- cli/commands/audit.py +68 -0
- cli/commands/docs.py +179 -0
- cli/commands/experiment.py +755 -0
- cli/commands/feedback.py +106 -0
- cli/commands/notification.py +213 -0
- cli/commands/persona.py +63 -0
- cli/commands/project.py +87 -0
- cli/commands/runtime.py +105 -0
- cli/commands/topic.py +361 -0
- cli/e2e_collab.py +602 -0
- cli/git_checkpoint.py +68 -0
- cli/host_worker_types.py +151 -0
- cli/main.py +1553 -0
- cli/map_command_client.py +497 -0
- cli/participant_worker.py +255 -0
- cli/reviewer_worker.py +263 -0
- cli/runtime/__init__.py +5 -0
- cli/runtime/run_lock.py +497 -0
- cli/runtime_chat.py +317 -0
- cli/session_wake_log.py +235 -0
- cli/simple_waker.py +950 -0
- cli/table_render.py +113 -0
- cli/wake_backend.py +236 -0
- cli/worker_cycle_log.py +36 -0
- map_client/__init__.py +37 -0
- map_client/bootstrap.py +193 -0
- map_client/client.py +1045 -0
- map_client/config.py +21 -0
- map_client/errors.py +283 -0
- map_client/exceptions.py +130 -0
- map_client/plan_evidence.py +159 -0
- map_client/project_config.py +153 -0
- map_client/result_template.py +167 -0
- map_client/testing.py +27 -0
- map_mcp/__init__.py +4 -0
- map_mcp/_utils.py +28 -0
- map_mcp/auth.py +34 -0
- map_mcp/config.py +50 -0
- map_mcp/context.py +39 -0
- map_mcp/main.py +75 -0
- map_mcp/server.py +573 -0
- map_mcp/session.py +79 -0
- map_sdk/__init__.py +29 -0
- map_sdk/evidence.py +68 -0
- map_types/__init__.py +203 -0
- map_types/enums.py +199 -0
- map_types/schemas.py +1351 -0
- multi_agent_platform-0.1.0.dist-info/METADATA +298 -0
- multi_agent_platform-0.1.0.dist-info/RECORD +144 -0
- multi_agent_platform-0.1.0.dist-info/WHEEL +5 -0
- multi_agent_platform-0.1.0.dist-info/entry_points.txt +6 -0
- multi_agent_platform-0.1.0.dist-info/licenses/LICENSE +21 -0
- multi_agent_platform-0.1.0.dist-info/top_level.txt +6 -0
- server/__init__.py +0 -0
- server/__version__.py +14 -0
- server/api/__init__.py +0 -0
- server/api/action_items.py +138 -0
- server/api/agents.py +412 -0
- server/api/audit.py +54 -0
- server/api/background_tasks.py +18 -0
- server/api/common.py +117 -0
- server/api/deps.py +30 -0
- server/api/experiments.py +858 -0
- server/api/feedback.py +75 -0
- server/api/notifications.py +22 -0
- server/api/projects.py +209 -0
- server/api/router.py +25 -0
- server/api/status.py +33 -0
- server/api/topics.py +302 -0
- server/api/webhooks.py +74 -0
- server/auth/__init__.py +8 -0
- server/auth/experiment_access.py +66 -0
- server/config.py +38 -0
- server/db/__init__.py +3 -0
- server/db/base.py +5 -0
- server/db/deadlock_retry.py +146 -0
- server/db/session.py +41 -0
- server/domain/__init__.py +3 -0
- server/domain/encrypted_types.py +63 -0
- server/domain/models.py +713 -0
- server/domain/schemas.py +3 -0
- server/domain/state_machine.py +79 -0
- server/domain/topic_ack_constants.py +9 -0
- server/main.py +148 -0
- server/scripts/__init__.py +0 -0
- server/scripts/migrate_notification_unique.py +231 -0
- server/scripts/purge_audit_pollution.py +116 -0
- server/services/__init__.py +0 -0
- server/services/_lookups.py +26 -0
- server/services/acceptance_service.py +90 -0
- server/services/action_item_migration_service.py +190 -0
- server/services/action_item_service.py +200 -0
- server/services/agent_work_service.py +405 -0
- server/services/archive_lint_service.py +156 -0
- server/services/audit_service.py +457 -0
- server/services/auth.py +66 -0
- server/services/comment_service.py +173 -0
- server/services/errors.py +65 -0
- server/services/escalation_resolver.py +248 -0
- server/services/evidence_service.py +88 -0
- server/services/experiment_capabilities_service.py +277 -0
- server/services/inbound_event_service.py +111 -0
- server/services/lock_service.py +273 -0
- server/services/log_service.py +202 -0
- server/services/mention_service.py +730 -0
- server/services/notification_service.py +939 -0
- server/services/notification_stream.py +138 -0
- server/services/permissions.py +147 -0
- server/services/persona_activity_service.py +108 -0
- server/services/phase_owner_resolver.py +95 -0
- server/services/phase_service.py +381 -0
- server/services/plan_marker_service.py +235 -0
- server/services/plan_service.py +186 -0
- server/services/platform_feedback_service.py +114 -0
- server/services/project_service.py +534 -0
- server/services/project_status_service.py +132 -0
- server/services/review_service.py +707 -0
- server/services/secret_encryption.py +97 -0
- server/services/similarity_service.py +119 -0
- server/services/sse_event_schemas.py +17 -0
- server/services/status_service.py +68 -0
- server/services/template_service.py +134 -0
- server/services/text_utils.py +19 -0
- server/services/thread_activity.py +180 -0
- server/services/todo_persona_filter.py +73 -0
- server/services/todo_service.py +604 -0
- server/services/topic_ack_service.py +312 -0
- server/services/topic_action_item_ops.py +538 -0
- server/services/topic_comment_kind.py +14 -0
- server/services/topic_comment_service.py +237 -0
- server/services/topic_helpers.py +32 -0
- server/services/topic_lifecycle_service.py +478 -0
- server/services/topic_progress_service.py +40 -0
- server/services/topic_resolve_service.py +234 -0
- server/services/topic_service.py +102 -0
- server/services/topic_work_item_service.py +570 -0
- server/services/webhook_service.py +273 -0
cli/commands/topic.py
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
"""``map topic ...`` + ``map mention ...`` + ``map todo ...`` sub-apps — arch PR6.
|
|
2
|
+
|
|
3
|
+
Three sub-apps that share the "topic work items" domain:
|
|
4
|
+
|
|
5
|
+
* ``map topic ...`` — topic lifecycle (create / list / show / progress /
|
|
6
|
+
resolve / advance-round / comment / close / reopen / dismiss / read /
|
|
7
|
+
mark-seen / archive). The big one.
|
|
8
|
+
* ``map mention ...`` — personal @mention todos (dismiss / list /
|
|
9
|
+
dismiss-all / reconcile-stale stub).
|
|
10
|
+
* ``map todo ...`` — explicit_only todo partition clear router
|
|
11
|
+
(notification / action_item / my_open_topics / unread_change).
|
|
12
|
+
|
|
13
|
+
All command bodies lazy-import ``cli.main._run`` and friends to break
|
|
14
|
+
the ``cli.main ↔ cli.commands.*`` import cycle.
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import uuid
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
import typer
|
|
23
|
+
from map_client.client import MAPClient
|
|
24
|
+
|
|
25
|
+
from cli.table_render import enum_value, format_datetime, render_table, short_uuid, truncate
|
|
26
|
+
from server.domain.schemas import (
|
|
27
|
+
TopicAdvanceRound,
|
|
28
|
+
TopicCommentCreate,
|
|
29
|
+
TopicUpdate,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
topic_app = typer.Typer(help="Topic commands", rich_markup_mode=None)
|
|
33
|
+
mention_app = typer.Typer(help="Mention todo commands")
|
|
34
|
+
todo_app = typer.Typer(help="Todo partition clear routing (explicit_only buckets)")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
# topic_app
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@topic_app.command("create")
|
|
43
|
+
def topic_create(
|
|
44
|
+
title: str = typer.Option(..., "--title"),
|
|
45
|
+
project: uuid.UUID | None = typer.Option(None, "--project"),
|
|
46
|
+
project_key: str | None = typer.Option(None, "--project-key"),
|
|
47
|
+
description: str | None = typer.Option(None, "--description"),
|
|
48
|
+
) -> None:
|
|
49
|
+
from cli.main import _resolve_project, _run
|
|
50
|
+
from server.domain.schemas import TopicCreate
|
|
51
|
+
|
|
52
|
+
payload = TopicCreate(title=title, description=description)
|
|
53
|
+
|
|
54
|
+
def action(c: MAPClient):
|
|
55
|
+
pid = _resolve_project(c, project, project_key)
|
|
56
|
+
return c.create_topic(pid, payload)
|
|
57
|
+
|
|
58
|
+
_run(action)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _render_topic_table(topics: Any) -> str:
|
|
62
|
+
"""Render a list of TopicSummaryRead as a compact table."""
|
|
63
|
+
headers = ["ID", "Title", "Status", "Round", "Comments", "Exps", "Creator", "Created"]
|
|
64
|
+
rows = []
|
|
65
|
+
for t in topics:
|
|
66
|
+
rows.append([
|
|
67
|
+
short_uuid(t.id),
|
|
68
|
+
truncate(t.title, 50),
|
|
69
|
+
enum_value(t.status),
|
|
70
|
+
enum_value(t.discussion_round),
|
|
71
|
+
str(t.comment_count),
|
|
72
|
+
str(t.experiment_count),
|
|
73
|
+
truncate(t.creator_name, 20),
|
|
74
|
+
format_datetime(t.created_at),
|
|
75
|
+
])
|
|
76
|
+
return render_table(headers, rows)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@topic_app.command("list")
|
|
80
|
+
def topic_list(
|
|
81
|
+
project: uuid.UUID | None = typer.Option(None, "--project"),
|
|
82
|
+
project_key: str | None = typer.Option(None, "--project-key"),
|
|
83
|
+
status: str | None = typer.Option(None, "--status"),
|
|
84
|
+
creator: str | None = typer.Option(
|
|
85
|
+
None,
|
|
86
|
+
"--creator",
|
|
87
|
+
help="Filter by topic creator. Accepts agent_name (current project) or agent_id UUID; "
|
|
88
|
+
"alias for --creator-agent-id.",
|
|
89
|
+
),
|
|
90
|
+
creator_agent_id: uuid.UUID | None = typer.Option(
|
|
91
|
+
None,
|
|
92
|
+
"--creator-agent-id",
|
|
93
|
+
help="Filter by creator agent_id UUID. Use --creator for name-or-id shorthand.",
|
|
94
|
+
),
|
|
95
|
+
q: str | None = typer.Option(None, "--q"),
|
|
96
|
+
page: int = typer.Option(1, "--page", min=1),
|
|
97
|
+
page_size: int = typer.Option(100, "--page-size", min=1, max=100),
|
|
98
|
+
include_archived: bool = typer.Option(False, "--include-archived"),
|
|
99
|
+
) -> None:
|
|
100
|
+
"""List topics in the current project.
|
|
101
|
+
|
|
102
|
+
Defaults to a compact table view. Use ``--format yaml`` or
|
|
103
|
+
``--format json`` for full structured output (scripts / piping).
|
|
104
|
+
"""
|
|
105
|
+
from cli.main import _resolve_creator_agent_id, _resolve_project, _run
|
|
106
|
+
from server.domain.models import TopicStatus
|
|
107
|
+
|
|
108
|
+
def action(c: MAPClient):
|
|
109
|
+
pid = _resolve_project(c, project, project_key)
|
|
110
|
+
st = TopicStatus(status) if status else None
|
|
111
|
+
resolved_creator_id = _resolve_creator_agent_id(c, pid, creator, creator_agent_id)
|
|
112
|
+
return c.list_topics(
|
|
113
|
+
pid,
|
|
114
|
+
status=st,
|
|
115
|
+
creator_agent_id=resolved_creator_id,
|
|
116
|
+
q=q,
|
|
117
|
+
page=page,
|
|
118
|
+
page_size=page_size,
|
|
119
|
+
include_archived=include_archived,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
_run(action, table_renderer=_render_topic_table)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@topic_app.command("show")
|
|
126
|
+
def topic_show(topic_id: uuid.UUID = typer.Option(..., "--id")) -> None:
|
|
127
|
+
from cli.main import _run
|
|
128
|
+
|
|
129
|
+
_run(lambda c: c.get_topic(topic_id))
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@topic_app.command("progress")
|
|
133
|
+
def topic_progress() -> None:
|
|
134
|
+
"""Per-agent topic work items view (obligation + contextual); same source as todos topic buckets."""
|
|
135
|
+
from cli.main import _run
|
|
136
|
+
|
|
137
|
+
_run(lambda c: c.get_topic_progress())
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@topic_app.command("resolve")
|
|
141
|
+
def topic_resolve(
|
|
142
|
+
topic_id: uuid.UUID = typer.Option(..., "--id"),
|
|
143
|
+
resolve_file: Path = typer.Option(..., "--file"),
|
|
144
|
+
) -> None:
|
|
145
|
+
from cli.main import _load_topic_resolve_payload, _run
|
|
146
|
+
|
|
147
|
+
payload = _load_topic_resolve_payload(resolve_file)
|
|
148
|
+
_run(lambda c: c.resolve_topic(topic_id, payload))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@topic_app.command("advance-round")
|
|
152
|
+
def topic_advance_round(
|
|
153
|
+
topic_id: uuid.UUID = typer.Option(..., "--id"),
|
|
154
|
+
increment_summary: bool = typer.Option(
|
|
155
|
+
True,
|
|
156
|
+
"--increment-summary/--no-increment-summary",
|
|
157
|
+
help="Increment round_summary_count before advancing.",
|
|
158
|
+
),
|
|
159
|
+
ack_ids: str | None = typer.Option(
|
|
160
|
+
None,
|
|
161
|
+
"--ack-ids",
|
|
162
|
+
help="Host: comma-separated participant agent UUIDs already acknowledged.",
|
|
163
|
+
),
|
|
164
|
+
ack: str | None = typer.Option(
|
|
165
|
+
None,
|
|
166
|
+
"--ack",
|
|
167
|
+
help="Participant: accept, reject, or dismiss acknowledgement for the current round.",
|
|
168
|
+
),
|
|
169
|
+
) -> None:
|
|
170
|
+
from cli.main import _run
|
|
171
|
+
|
|
172
|
+
acknowledged_by: list[uuid.UUID] = []
|
|
173
|
+
if ack_ids:
|
|
174
|
+
acknowledged_by = [uuid.UUID(item.strip()) for item in ack_ids.split(",") if item.strip()]
|
|
175
|
+
payload = TopicAdvanceRound(
|
|
176
|
+
increment_summary=increment_summary,
|
|
177
|
+
acknowledged_by=acknowledged_by,
|
|
178
|
+
ack=ack, # type: ignore[arg-type]
|
|
179
|
+
)
|
|
180
|
+
_run(lambda c: c.advance_topic_round(topic_id, payload))
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@topic_app.command("comment")
|
|
184
|
+
def topic_comment(
|
|
185
|
+
topic_id: uuid.UUID = typer.Option(..., "--id"),
|
|
186
|
+
body: str | None = typer.Option(None, "--body"),
|
|
187
|
+
body_file: Path | None = typer.Option(None, "--file"),
|
|
188
|
+
parent: uuid.UUID | None = typer.Option(None, "--parent"),
|
|
189
|
+
) -> None:
|
|
190
|
+
from cli.main import _read_text_file, _run
|
|
191
|
+
|
|
192
|
+
if body is None and body_file is None:
|
|
193
|
+
typer.echo("Error: either --body or --file is required", err=True)
|
|
194
|
+
raise typer.Exit(2)
|
|
195
|
+
if body is not None and body_file is not None:
|
|
196
|
+
typer.echo("Error: use only one of --body or --file", err=True)
|
|
197
|
+
raise typer.Exit(2)
|
|
198
|
+
content = body if body is not None else _read_text_file(body_file, kind="comment")
|
|
199
|
+
payload = TopicCommentCreate(body=content, parent_id=parent)
|
|
200
|
+
_run(lambda c: c.create_topic_comment(topic_id, payload))
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
@topic_app.command("close")
|
|
204
|
+
def topic_close(topic_id: uuid.UUID = typer.Option(..., "--id")) -> None:
|
|
205
|
+
from cli.main import _run
|
|
206
|
+
|
|
207
|
+
_run(lambda c: c.close_topic(topic_id))
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@topic_app.command("reopen")
|
|
211
|
+
def topic_reopen(topic_id: uuid.UUID = typer.Option(..., "--id")) -> None:
|
|
212
|
+
from cli.main import _run
|
|
213
|
+
|
|
214
|
+
_run(lambda c: c.reopen_topic(topic_id))
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@topic_app.command("dismiss")
|
|
218
|
+
def topic_dismiss(topic_id: uuid.UUID = typer.Option(..., "--id")) -> None:
|
|
219
|
+
"""Hide an open topic from host todos until new activity (same as Web UI ✕)."""
|
|
220
|
+
from cli.main import _run
|
|
221
|
+
|
|
222
|
+
_run(lambda c: c.dismiss_topic(topic_id))
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@topic_app.command("read")
|
|
226
|
+
def topic_read(topic_id: uuid.UUID = typer.Option(..., "--id")) -> None:
|
|
227
|
+
"""Mark contextual unread changes as seen; obligations still require reply/ack/mention handling."""
|
|
228
|
+
from cli.main import _run
|
|
229
|
+
|
|
230
|
+
_run(lambda c: c.mark_topic_read(topic_id))
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@topic_app.command("mark-seen")
|
|
234
|
+
def topic_mark_seen(topic_id: uuid.UUID = typer.Option(..., "--id")) -> None:
|
|
235
|
+
"""Alias of topic read: clears contextual unread only, not reply/ack/mention obligations."""
|
|
236
|
+
from cli.main import _run
|
|
237
|
+
|
|
238
|
+
_run(lambda c: c.mark_topic_read(topic_id))
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@topic_app.command(
|
|
242
|
+
"archive",
|
|
243
|
+
epilog="Use --undo or --unarchive to restore an archived topic.",
|
|
244
|
+
)
|
|
245
|
+
def topic_archive(
|
|
246
|
+
topic_id: uuid.UUID | None = typer.Option(None, "--id", help="Topic UUID."),
|
|
247
|
+
undo: bool = typer.Option(
|
|
248
|
+
False,
|
|
249
|
+
"--undo",
|
|
250
|
+
help="Unarchive instead of archive. Equivalent to --unarchive.",
|
|
251
|
+
),
|
|
252
|
+
unarchive: bool = typer.Option(
|
|
253
|
+
False,
|
|
254
|
+
"--unarchive",
|
|
255
|
+
help="Alias of --undo: unarchive instead of archive.",
|
|
256
|
+
),
|
|
257
|
+
) -> None:
|
|
258
|
+
"""Archive (or unarchive) a topic.
|
|
259
|
+
|
|
260
|
+
Thin wrapper around ``PATCH /topics/{id}`` with ``archived=true`` (or
|
|
261
|
+
``false`` when ``--undo``/``--unarchive`` is set). Archive hides the
|
|
262
|
+
topic from ``topic list`` by default but ``topic show`` still returns
|
|
263
|
+
it including ``archived_at``. Archive is reversible — re-run with
|
|
264
|
+
``--undo`` to restore.
|
|
265
|
+
"""
|
|
266
|
+
from map_client.exceptions import MAPNotFoundError
|
|
267
|
+
|
|
268
|
+
from cli.main import _require_option_uuid, _run
|
|
269
|
+
|
|
270
|
+
topic_id = _require_option_uuid(topic_id)
|
|
271
|
+
payload = TopicUpdate(archived=not (undo or unarchive))
|
|
272
|
+
object_kind = "topic"
|
|
273
|
+
|
|
274
|
+
def action(c: MAPClient):
|
|
275
|
+
try:
|
|
276
|
+
return c.update_topic(topic_id, payload)
|
|
277
|
+
except MAPNotFoundError as exc:
|
|
278
|
+
typer.echo(
|
|
279
|
+
f"Error: {object_kind} {topic_id} not found",
|
|
280
|
+
err=True,
|
|
281
|
+
)
|
|
282
|
+
raise typer.Exit(1) from exc
|
|
283
|
+
|
|
284
|
+
_run(action)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
# ---------------------------------------------------------------------------
|
|
288
|
+
# mention_app
|
|
289
|
+
# ---------------------------------------------------------------------------
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
@mention_app.command("dismiss")
|
|
293
|
+
def mention_dismiss(
|
|
294
|
+
mention_id: uuid.UUID = typer.Option(..., "--id", help="Mention UUID from `map todos`."),
|
|
295
|
+
) -> None:
|
|
296
|
+
"""Dismiss one @mention for the current persona (removes it from `map todos`).
|
|
297
|
+
|
|
298
|
+
Idempotent: dismissing an already-dismissed mention returns the same result.
|
|
299
|
+
"""
|
|
300
|
+
from cli.main import _run
|
|
301
|
+
|
|
302
|
+
_run(lambda c: c.dismiss_mention(mention_id))
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
@mention_app.command("list")
|
|
306
|
+
def mention_list() -> None:
|
|
307
|
+
"""List open @mentions for the current persona."""
|
|
308
|
+
from cli.main import _run
|
|
309
|
+
|
|
310
|
+
_run(lambda c: c.get_todos().mentions)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
@mention_app.command("dismiss-all")
|
|
314
|
+
def mention_dismiss_all() -> None:
|
|
315
|
+
"""Dismiss all open @mentions for the current persona."""
|
|
316
|
+
from cli.main import _run
|
|
317
|
+
|
|
318
|
+
_run(lambda c: c.dismiss_all_mentions())
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
@mention_app.command("reconcile-stale")
|
|
322
|
+
def mention_reconcile_stale() -> None:
|
|
323
|
+
"""Admin stub: offline stale mention reconciliation (T1 D5 MVP — not implemented)."""
|
|
324
|
+
typer.echo(
|
|
325
|
+
"mention reconcile-stale: stub only — stale mentions are filtered in "
|
|
326
|
+
"topic-progress/todos projection; use write-path dismiss on comment."
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
# ---------------------------------------------------------------------------
|
|
331
|
+
# todo_app — explicit_only partition clear router
|
|
332
|
+
# ---------------------------------------------------------------------------
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
@todo_app.command("clear")
|
|
336
|
+
def todo_clear(
|
|
337
|
+
key: str = typer.Option(..., "--key", help="Work-item idempotency_key or partition id"),
|
|
338
|
+
) -> None:
|
|
339
|
+
"""Route explicit_only todo partitions to the canonical clear CLI (T1 D7)."""
|
|
340
|
+
from cli.main import _run
|
|
341
|
+
|
|
342
|
+
if key.startswith("notification:"):
|
|
343
|
+
notification_id = uuid.UUID(key.split(":", 1)[1])
|
|
344
|
+
_run(lambda c: c.mark_notification_read(notification_id))
|
|
345
|
+
return
|
|
346
|
+
if key.startswith("action_item:"):
|
|
347
|
+
item_id = uuid.UUID(key.split(":", 1)[1])
|
|
348
|
+
_run(lambda c: c.complete_action_item(item_id))
|
|
349
|
+
return
|
|
350
|
+
if key.startswith("my_open_topics:") or key.startswith("topic:"):
|
|
351
|
+
topic_id = uuid.UUID(key.rsplit(":", 1)[-1])
|
|
352
|
+
_run(lambda c: c.dismiss_topic(topic_id))
|
|
353
|
+
return
|
|
354
|
+
if key.startswith("unread_change:"):
|
|
355
|
+
topic_id = uuid.UUID(key.split(":", 2)[1])
|
|
356
|
+
_run(lambda c: c.mark_topic_read(topic_id))
|
|
357
|
+
return
|
|
358
|
+
raise typer.BadParameter(
|
|
359
|
+
f"unsupported todo clear key {key!r}; explicit_only: notification, action_item, "
|
|
360
|
+
"my_open_topics, unread_change"
|
|
361
|
+
)
|