@grifhinz/logics-manager 2.0.4 → 2.0.5

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.
@@ -7,6 +7,8 @@ from dataclasses import dataclass
7
7
  from datetime import date
8
8
  from pathlib import Path
9
9
 
10
+ from .termstyle import colorize_help
11
+
10
12
 
11
13
  @dataclass(frozen=True)
12
14
  class DocKind:
@@ -33,6 +35,519 @@ STATUS_BY_KIND_DEFAULT = {
33
35
  "backlog": "Ready",
34
36
  "task": "Ready",
35
37
  }
38
+ HELP_FLAGS = ("-h", "--help")
39
+ LIST_KIND_CHOICES = ("all", "request", "backlog", "task")
40
+ ACTIVE_FLOW_STATUSES = {"draft", "ready", "in progress", "blocked"}
41
+ FLOW_KIND_ORDER = {"request": 0, "backlog": 1, "task": 2}
42
+
43
+
44
+ def _help_requested(argv: list[str], index: int) -> bool:
45
+ return len(argv) <= index or argv[index] in HELP_FLAGS
46
+
47
+
48
+ def _format_flag_list(flags: list[str]) -> str:
49
+ return ", ".join(flags)
50
+
51
+
52
+ def _normalize_status(value: str | None) -> str:
53
+ return " ".join(value.split()).lower() if value else ""
54
+
55
+
56
+ def _is_active_flow_doc(status: str | None) -> bool:
57
+ return _normalize_status(status) in ACTIVE_FLOW_STATUSES
58
+
59
+
60
+ @dataclass(frozen=True)
61
+ class FlowListEntry:
62
+ kind: str
63
+ path: Path
64
+ ref: str
65
+ title: str
66
+ status: str | None
67
+ progress: str | None
68
+
69
+
70
+ def _parse_flow_doc(path: Path, kind: str) -> FlowListEntry:
71
+ lines = path.read_text(encoding="utf-8").splitlines()
72
+ ref = path.stem
73
+ title = _extract_doc_title(path)
74
+ status: str | None = None
75
+ progress: str | None = None
76
+
77
+ for line in lines:
78
+ if line.startswith("> Status:"):
79
+ status = line.split(":", 1)[1].strip()
80
+ continue
81
+ if line.startswith("> Progress:"):
82
+ progress = line.split(":", 1)[1].strip()
83
+
84
+ return FlowListEntry(
85
+ kind=kind,
86
+ path=path,
87
+ ref=ref,
88
+ title=title,
89
+ status=status,
90
+ progress=progress,
91
+ )
92
+
93
+
94
+ def _collect_flow_list_entries(repo_root: Path, kind_filter: str = "all") -> list[FlowListEntry]:
95
+ entries: list[FlowListEntry] = []
96
+ for kind, doc_kind in DOC_KINDS.items():
97
+ if kind_filter != "all" and kind_filter != kind:
98
+ continue
99
+ directory = repo_root / doc_kind.directory
100
+ if not directory.is_dir():
101
+ continue
102
+ for path in sorted(directory.glob("*.md")):
103
+ entry = _parse_flow_doc(path, kind)
104
+ if _is_active_flow_doc(entry.status):
105
+ entries.append(entry)
106
+ entries.sort(key=lambda entry: (FLOW_KIND_ORDER.get(entry.kind, 99), _normalize_status(entry.status), entry.ref))
107
+ return entries
108
+
109
+
110
+ def _render_flow_list_section(title: str, entries: list[FlowListEntry], out_dir: Path) -> str:
111
+ lines: list[str] = [f"## {title}", ""]
112
+ if not entries:
113
+ lines.append("_None_")
114
+ lines.append("")
115
+ return "\n".join(lines)
116
+
117
+ lines.extend(["| Doc | Title | Status | Progress | Path |", "|---|---|---|---|---|"])
118
+ for entry in entries:
119
+ rel = entry.path.relative_to(out_dir).as_posix()
120
+ doc_link = f"[{entry.ref}]({rel})"
121
+ lines.append(f"| {doc_link} | {entry.title} | {entry.status or ''} | {entry.progress or ''} | {rel} |")
122
+ lines.append("")
123
+ return "\n".join(lines)
124
+
125
+
126
+ def flow_list_payload(repo_root: Path, *, kind: str = "all") -> dict[str, object]:
127
+ repo_root = repo_root.resolve()
128
+ entries = _collect_flow_list_entries(repo_root, kind_filter=kind)
129
+ by_kind: dict[str, list[FlowListEntry]] = {key: [] for key in ("request", "backlog", "task")}
130
+ for entry in entries:
131
+ by_kind[entry.kind].append(entry)
132
+ counts = {key: len(values) for key, values in by_kind.items()}
133
+ return {
134
+ "ok": True,
135
+ "kind": kind,
136
+ "count": len(entries),
137
+ "counts_by_kind": counts,
138
+ "entries": [
139
+ {
140
+ "kind": entry.kind,
141
+ "ref": entry.ref,
142
+ "title": entry.title,
143
+ "status": entry.status,
144
+ "progress": entry.progress,
145
+ "path": entry.path.relative_to(repo_root).as_posix(),
146
+ }
147
+ for entry in entries
148
+ ],
149
+ }
150
+
151
+
152
+ def render_flow_list(repo_root: Path, *, kind: str = "all", output_format: str = "text") -> str:
153
+ payload = flow_list_payload(repo_root, kind=kind)
154
+ if output_format == "json":
155
+ return json.dumps(payload, indent=2, sort_keys=True)
156
+
157
+ entries = [
158
+ FlowListEntry(
159
+ kind=str(item["kind"]),
160
+ path=repo_root / str(item["path"]),
161
+ ref=str(item["ref"]),
162
+ title=str(item["title"]),
163
+ status=item["status"],
164
+ progress=item["progress"],
165
+ )
166
+ for item in payload["entries"]
167
+ ]
168
+ if not entries:
169
+ return "Flow docs in progress: 0\n\n_None_"
170
+
171
+ sections: list[str] = [f"Flow docs in progress: {payload['count']}", ""]
172
+ kind_titles = {"request": "Requests", "backlog": "Backlog", "task": "Tasks"}
173
+ for key in ("request", "backlog", "task"):
174
+ if kind != "all" and kind != key:
175
+ continue
176
+ section_entries = [entry for entry in entries if entry.kind == key]
177
+ sections.append(_render_flow_list_section(f"{kind_titles[key]} ({len(section_entries)})", section_entries, repo_root))
178
+ return "\n".join(sections).rstrip()
179
+
180
+
181
+ def _build_help() -> str:
182
+ return "\n".join(
183
+ [
184
+ "Logics Flow CLI",
185
+ "Create workflow docs with stable IDs, templates, and transitions.",
186
+ "",
187
+ "Usage:",
188
+ " logics-manager flow <command> [args...]",
189
+ "",
190
+ "Commands:",
191
+ " new <request|backlog|task>",
192
+ " Create a new doc from a template.",
193
+ " Common flags: --title, --slug, --from-version, --understanding, --confidence, --status, --complexity, --theme, --progress, --format {text,json}, --dry-run",
194
+ " Request-only flags: --fixture, --smoke-test",
195
+ " Backlog/task-only flags: --auto-create-product-brief, --auto-create-adr",
196
+ "",
197
+ " list",
198
+ " List workflow docs that are still active.",
199
+ " Flags: --kind {all,request,backlog,task}, --format {text,json}",
200
+ "",
201
+ " companion <product|architecture>",
202
+ " Create a companion doc from the integrated runtime.",
203
+ " Flags: --title, --source-ref, --request-ref, --backlog-ref, --task-ref, --format {text,json}, --dry-run",
204
+ "",
205
+ " promote request-to-backlog <source>",
206
+ " Create a backlog slice from a request.",
207
+ "",
208
+ " promote backlog-to-task <source>",
209
+ " Create a task from a backlog item.",
210
+ "",
211
+ " split request <source>",
212
+ " Split a request into multiple backlog items.",
213
+ " Flags: --title (repeatable), plus the common backlog flags above.",
214
+ "",
215
+ " split backlog <source>",
216
+ " Split a backlog item into multiple tasks.",
217
+ " Flags: --title (repeatable), plus the common task flags above.",
218
+ "",
219
+ " close <request|backlog|task> <source>",
220
+ " Close a doc and propagate transitions.",
221
+ " Flags: --format {text,json}, --dry-run",
222
+ "",
223
+ " finish task <source>",
224
+ " Finish a task and verify the closure chain.",
225
+ " Flags: --format {text,json}, --dry-run",
226
+ "",
227
+ "Examples:",
228
+ ' logics-manager flow new request --title "My request"',
229
+ " logics-manager flow promote request-to-backlog req_001_my_request",
230
+ " logics-manager flow close task task_003_fix_docs --dry-run",
231
+ ]
232
+ )
233
+
234
+
235
+ def _build_new_help() -> str:
236
+ return "\n".join(
237
+ [
238
+ "Logics Flow New",
239
+ "Create a new workflow doc from a template.",
240
+ "",
241
+ "Usage:",
242
+ " logics-manager flow new <request|backlog|task> [args...]",
243
+ "",
244
+ "Kinds:",
245
+ " request",
246
+ " Generates a request doc.",
247
+ " Flags: --title, --slug, --fixture, --smoke-test, --from-version, --understanding, --confidence, --status, --complexity, --theme, --format {text,json}, --dry-run",
248
+ " backlog",
249
+ " Generates a backlog doc.",
250
+ " Flags: --title, --slug, --from-version, --understanding, --confidence, --status, --complexity, --theme, --progress, --auto-create-product-brief, --auto-create-adr, --format {text,json}, --dry-run",
251
+ " task",
252
+ " Generates a task doc.",
253
+ " Flags: --title, --slug, --from-version, --understanding, --confidence, --status, --complexity, --theme, --progress, --auto-create-product-brief, --auto-create-adr, --format {text,json}, --dry-run",
254
+ "",
255
+ "Examples:",
256
+ ' logics-manager flow new request --title "Capture migration risks"',
257
+ ' logics-manager flow new backlog --title "Break work into slices"',
258
+ ' logics-manager flow new task --title "Implement the parser"',
259
+ ]
260
+ )
261
+
262
+
263
+ def _build_new_kind_help(kind: str) -> str:
264
+ if kind == "request":
265
+ kind_title = "Request"
266
+ flags = ["--title", "--slug", "--fixture", "--smoke-test", "--from-version", "--understanding", "--confidence", "--status", "--complexity", "--theme", "--format {text,json}", "--dry-run"]
267
+ examples = [' logics-manager flow new request --title "Capture migration risks"']
268
+ elif kind == "backlog":
269
+ kind_title = "Backlog"
270
+ flags = ["--title", "--slug", "--from-version", "--understanding", "--confidence", "--status", "--complexity", "--theme", "--progress", "--auto-create-product-brief", "--auto-create-adr", "--format {text,json}", "--dry-run"]
271
+ examples = [' logics-manager flow new backlog --title "Break work into slices"']
272
+ else:
273
+ kind_title = "Task"
274
+ flags = ["--title", "--slug", "--from-version", "--understanding", "--confidence", "--status", "--complexity", "--theme", "--progress", "--auto-create-product-brief", "--auto-create-adr", "--format {text,json}", "--dry-run"]
275
+ examples = [' logics-manager flow new task --title "Implement the parser"']
276
+ return "\n".join(
277
+ [
278
+ f"Logics Flow New {kind_title}",
279
+ f"Create a new {kind.lower()} doc.",
280
+ "",
281
+ "Usage:",
282
+ f" logics-manager flow new {kind} [args...]",
283
+ "",
284
+ "Flags:",
285
+ f" {_format_flag_list(flags)}",
286
+ "",
287
+ "Examples:",
288
+ *examples,
289
+ ]
290
+ )
291
+
292
+
293
+ def _build_list_help() -> str:
294
+ return "\n".join(
295
+ [
296
+ "Logics Flow List",
297
+ "List workflow docs that are still active.",
298
+ "",
299
+ "Usage:",
300
+ " logics-manager flow list [args...]",
301
+ "",
302
+ "Flags:",
303
+ " --kind {all,request,backlog,task}",
304
+ " --format {text,json}",
305
+ "",
306
+ "Examples:",
307
+ " logics-manager flow list",
308
+ " logics-manager flow list --kind backlog",
309
+ ]
310
+ )
311
+
312
+
313
+ def _build_companion_help() -> str:
314
+ return "\n".join(
315
+ [
316
+ "Logics Flow Companion",
317
+ "Create a companion doc from the integrated runtime.",
318
+ "",
319
+ "Usage:",
320
+ " logics-manager flow companion <product|architecture> [args...]",
321
+ "",
322
+ "Kinds:",
323
+ " product",
324
+ " Create a product companion doc.",
325
+ " Flags: --title, --source-ref, --request-ref, --backlog-ref, --task-ref, --format {text,json}, --dry-run",
326
+ " architecture",
327
+ " Create an architecture companion doc.",
328
+ " Flags: --title, --source-ref, --request-ref, --backlog-ref, --task-ref, --format {text,json}, --dry-run",
329
+ "",
330
+ "Examples:",
331
+ ' logics-manager flow companion product --title "Product note"',
332
+ ' logics-manager flow companion architecture --title "Architecture note"',
333
+ ]
334
+ )
335
+
336
+
337
+ def _build_companion_kind_help(kind: str) -> str:
338
+ return "\n".join(
339
+ [
340
+ f"Logics Flow Companion {kind.title()}",
341
+ f"Create an {kind} companion doc from the integrated runtime.",
342
+ "",
343
+ "Usage:",
344
+ f" logics-manager flow companion {kind} [args...]",
345
+ "",
346
+ "Flags:",
347
+ " --title, --source-ref, --request-ref, --backlog-ref, --task-ref, --format {text,json}, --dry-run",
348
+ "",
349
+ "Examples:",
350
+ f' logics-manager flow companion {kind} --title "{kind.title()} note"',
351
+ ]
352
+ )
353
+
354
+
355
+ def _build_promote_help() -> str:
356
+ return "\n".join(
357
+ [
358
+ "Logics Flow Promote",
359
+ "Promote between workflow stages.",
360
+ "",
361
+ "Usage:",
362
+ " logics-manager flow promote <request-to-backlog|backlog-to-task> <source> [args...]",
363
+ "",
364
+ "Commands:",
365
+ " request-to-backlog <source>",
366
+ " Create a backlog slice from a request.",
367
+ " Flags: --from-version, --understanding, --confidence, --status, --complexity, --theme, --progress, --auto-create-product-brief, --auto-create-adr, --format {text,json}, --dry-run",
368
+ " backlog-to-task <source>",
369
+ " Create a task from a backlog item.",
370
+ " Flags: --from-version, --understanding, --confidence, --status, --complexity, --theme, --progress, --auto-create-product-brief, --auto-create-adr, --format {text,json}, --dry-run",
371
+ "",
372
+ "Examples:",
373
+ " logics-manager flow promote request-to-backlog req_001_capture_migration_risks",
374
+ " logics-manager flow promote backlog-to-task item_002_break_work_into_slices",
375
+ ]
376
+ )
377
+
378
+
379
+ def _build_promote_variant_help(promotion: str) -> str:
380
+ if promotion == "request-to-backlog":
381
+ title = "Request to Backlog"
382
+ summary = "Create a backlog slice from a request."
383
+ usage = " logics-manager flow promote request-to-backlog <source> [args...]"
384
+ example = " logics-manager flow promote request-to-backlog req_001_capture_migration_risks"
385
+ else:
386
+ title = "Backlog to Task"
387
+ summary = "Create a task from a backlog item."
388
+ usage = " logics-manager flow promote backlog-to-task <source> [args...]"
389
+ example = " logics-manager flow promote backlog-to-task item_002_break_work_into_slices"
390
+ return "\n".join(
391
+ [
392
+ f"Logics Flow Promote {title}",
393
+ summary,
394
+ "",
395
+ "Usage:",
396
+ usage,
397
+ "",
398
+ "Flags:",
399
+ " --from-version, --understanding, --confidence, --status, --complexity, --theme, --progress, --auto-create-product-brief, --auto-create-adr, --format {text,json}, --dry-run",
400
+ "",
401
+ "Example:",
402
+ example,
403
+ ]
404
+ )
405
+
406
+
407
+ def _build_split_help() -> str:
408
+ return "\n".join(
409
+ [
410
+ "Logics Flow Split",
411
+ "Split a request or backlog into bounded children.",
412
+ "",
413
+ "Usage:",
414
+ " logics-manager flow split <request|backlog> <source> [args...]",
415
+ "",
416
+ "Commands:",
417
+ " request <source>",
418
+ " Split a request into multiple backlog items.",
419
+ " Flags: --title (repeatable), --from-version, --understanding, --confidence, --status, --complexity, --theme, --progress, --auto-create-product-brief, --auto-create-adr, --format {text,json}, --dry-run",
420
+ " backlog <source>",
421
+ " Split a backlog item into multiple tasks.",
422
+ " Flags: --title (repeatable), --from-version, --understanding, --confidence, --status, --complexity, --theme, --progress, --auto-create-product-brief, --auto-create-adr, --format {text,json}, --dry-run",
423
+ "",
424
+ "Examples:",
425
+ " logics-manager flow split request req_001_capture_migration_risks --title \"Slice 1\" --title \"Slice 2\"",
426
+ " logics-manager flow split backlog item_002_break_work_into_slices --title \"Task 1\" --title \"Task 2\"",
427
+ ]
428
+ )
429
+
430
+
431
+ def _build_split_variant_help(split_kind: str) -> str:
432
+ if split_kind == "request":
433
+ title = "Request"
434
+ summary = "Split a request into multiple backlog items."
435
+ usage = " logics-manager flow split request <source> [args...]"
436
+ example = ' logics-manager flow split request req_001_capture_migration_risks --title "Slice 1" --title "Slice 2"'
437
+ else:
438
+ title = "Backlog"
439
+ summary = "Split a backlog item into multiple tasks."
440
+ usage = " logics-manager flow split backlog <source> [args...]"
441
+ example = ' logics-manager flow split backlog item_002_break_work_into_slices --title "Task 1" --title "Task 2"'
442
+ return "\n".join(
443
+ [
444
+ f"Logics Flow Split {title}",
445
+ summary,
446
+ "",
447
+ "Usage:",
448
+ usage,
449
+ "",
450
+ "Flags:",
451
+ " --title (repeatable), --from-version, --understanding, --confidence, --status, --complexity, --theme, --progress, --auto-create-product-brief, --auto-create-adr, --format {text,json}, --dry-run",
452
+ "",
453
+ "Example:",
454
+ example,
455
+ ]
456
+ )
457
+
458
+
459
+ def _build_close_help() -> str:
460
+ return "\n".join(
461
+ [
462
+ "Logics Flow Close",
463
+ "Close a request, backlog item, or task and propagate transitions.",
464
+ "",
465
+ "Usage:",
466
+ " logics-manager flow close <request|backlog|task> <source> [args...]",
467
+ "",
468
+ "Kinds:",
469
+ " request",
470
+ " Close a request doc.",
471
+ " Flags: --format {text,json}, --dry-run",
472
+ " backlog",
473
+ " Close a backlog doc.",
474
+ " Flags: --format {text,json}, --dry-run",
475
+ " task",
476
+ " Close a task doc.",
477
+ " Flags: --format {text,json}, --dry-run",
478
+ "",
479
+ "Examples:",
480
+ " logics-manager flow close request req_001_capture_migration_risks",
481
+ " logics-manager flow close task task_003_fix_docs --dry-run",
482
+ ]
483
+ )
484
+
485
+
486
+ def _build_close_kind_help(kind: str) -> str:
487
+ example = {
488
+ "request": " logics-manager flow close request req_001_capture_migration_risks",
489
+ "backlog": " logics-manager flow close backlog item_002_break_work_into_slices",
490
+ "task": " logics-manager flow close task task_003_fix_docs",
491
+ }[kind]
492
+ return "\n".join(
493
+ [
494
+ f"Logics Flow Close {kind.title()}",
495
+ f"Close a {kind} doc and propagate transitions.",
496
+ "",
497
+ "Usage:",
498
+ f" logics-manager flow close {kind} <source> [args...]",
499
+ "",
500
+ "Flags:",
501
+ " --format {text,json}",
502
+ " --dry-run",
503
+ "",
504
+ "Example:",
505
+ example,
506
+ ]
507
+ )
508
+
509
+
510
+ def _build_finish_help() -> str:
511
+ return "\n".join(
512
+ [
513
+ "Logics Flow Finish",
514
+ "Finish a task and verify the closure chain.",
515
+ "",
516
+ "Usage:",
517
+ " logics-manager flow finish task <source> [args...]",
518
+ "",
519
+ "Commands:",
520
+ " task <source>",
521
+ " Finish a task.",
522
+ " Flags: --format {text,json}, --dry-run",
523
+ "",
524
+ "Examples:",
525
+ " logics-manager flow finish task task_003_fix_docs",
526
+ ]
527
+ )
528
+
529
+
530
+ def _build_finish_kind_help(kind: str) -> str:
531
+ return "\n".join(
532
+ [
533
+ f"Logics Flow Finish {kind.title()}",
534
+ f"Finish a {kind} and verify the closure chain.",
535
+ "",
536
+ "Usage:",
537
+ f" logics-manager flow finish {kind} <source> [args...]",
538
+ "",
539
+ "Flags:",
540
+ " --format {text,json}",
541
+ " --dry-run",
542
+ "",
543
+ "Example:",
544
+ " logics-manager flow finish task task_003_fix_docs",
545
+ ]
546
+ )
547
+
548
+
549
+ def _print_help(text: str) -> None:
550
+ print(colorize_help(text))
36
551
 
37
552
 
38
553
  def _split_titles(raw_titles: list[str]) -> list[str]:
@@ -887,6 +1402,11 @@ def build_parser() -> argparse.ArgumentParser:
887
1402
  _add_common_doc_args(kind_parser, kind)
888
1403
  kind_parser.set_defaults(func=cmd_new)
889
1404
 
1405
+ list_parser = sub.add_parser("list", help="List workflow docs that are still active.")
1406
+ list_parser.add_argument("--kind", choices=LIST_KIND_CHOICES, default="all")
1407
+ list_parser.add_argument("--format", choices=("text", "json"), default="text")
1408
+ list_parser.set_defaults(func=cmd_list)
1409
+
890
1410
  companion_parser = sub.add_parser("companion", help="Create a companion doc from the integrated runtime.")
891
1411
  companion_sub = companion_parser.add_subparsers(dest="kind", required=True)
892
1412
  for kind in ("product", "architecture"):
@@ -1024,6 +1544,13 @@ def cmd_new(args: argparse.Namespace) -> dict[str, object]:
1024
1544
  return payload
1025
1545
 
1026
1546
 
1547
+ def cmd_list(args: argparse.Namespace) -> dict[str, object]:
1548
+ repo_root = _find_repo_root(Path.cwd())
1549
+ payload = flow_list_payload(repo_root, kind=args.kind)
1550
+ print(render_flow_list(repo_root, kind=args.kind, output_format=args.format))
1551
+ return payload
1552
+
1553
+
1027
1554
  def cmd_companion(args: argparse.Namespace) -> dict[str, object]:
1028
1555
  repo_root = _find_repo_root(Path.cwd())
1029
1556
  request_ref, backlog_ref, task_ref = _resolve_workflow_refs_for_companion(
@@ -1441,9 +1968,51 @@ def cmd_finish_task(args: argparse.Namespace) -> dict[str, object]:
1441
1968
 
1442
1969
 
1443
1970
  def main(argv: list[str]) -> int:
1971
+ if not argv or argv[0] in HELP_FLAGS:
1972
+ _print_help(_build_help())
1973
+ return 0
1974
+ if argv[0] == "new" and _help_requested(argv, 1):
1975
+ _print_help(_build_new_help())
1976
+ return 0
1977
+ if argv[0] == "new" and len(argv) > 1 and argv[1] in DOC_KINDS and _help_requested(argv, 2):
1978
+ _print_help(_build_new_kind_help(argv[1]))
1979
+ return 0
1980
+ if argv[0] == "list" and _help_requested(argv, 1):
1981
+ _print_help(_build_list_help())
1982
+ return 0
1983
+ if argv[0] == "companion" and _help_requested(argv, 1):
1984
+ _print_help(_build_companion_help())
1985
+ return 0
1986
+ if argv[0] == "companion" and len(argv) > 1 and argv[1] in {"product", "architecture"} and _help_requested(argv, 2):
1987
+ _print_help(_build_companion_kind_help(argv[1]))
1988
+ return 0
1989
+ if argv[0] == "promote" and _help_requested(argv, 1):
1990
+ _print_help(_build_promote_help())
1991
+ return 0
1992
+ if argv[0] == "promote" and len(argv) > 1 and argv[1] in {"request-to-backlog", "backlog-to-task"} and _help_requested(argv, 2):
1993
+ _print_help(_build_promote_variant_help(argv[1]))
1994
+ return 0
1995
+ if argv[0] == "split" and _help_requested(argv, 1):
1996
+ _print_help(_build_split_help())
1997
+ return 0
1998
+ if argv[0] == "split" and len(argv) > 1 and argv[1] in {"request", "backlog"} and _help_requested(argv, 2):
1999
+ _print_help(_build_split_variant_help(argv[1]))
2000
+ return 0
2001
+ if argv[0] == "close" and _help_requested(argv, 1):
2002
+ _print_help(_build_close_help())
2003
+ return 0
2004
+ if argv[0] == "close" and len(argv) > 1 and argv[1] in {"request", "backlog", "task"} and _help_requested(argv, 2):
2005
+ _print_help(_build_close_kind_help(argv[1]))
2006
+ return 0
2007
+ if argv[0] == "finish" and _help_requested(argv, 1):
2008
+ _print_help(_build_finish_help())
2009
+ return 0
2010
+ if argv[0] == "finish" and len(argv) > 1 and argv[1] == "task" and _help_requested(argv, 2):
2011
+ _print_help(_build_finish_kind_help(argv[1]))
2012
+ return 0
1444
2013
  parser = build_parser()
1445
2014
  args = parser.parse_args(argv)
1446
- if args.command not in {"new", "companion", "promote", "split", "close", "finish"}:
2015
+ if args.command not in {"new", "list", "companion", "promote", "split", "close", "finish"}:
1447
2016
  raise SystemExit("Unsupported flow subcommand for the native CLI slice.")
1448
2017
  payload = args.func(args)
1449
2018
  return 0 if isinstance(payload, dict) else 1