@grifhinz/logics-manager 2.7.0 → 2.8.1
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/README.md +59 -10
- package/VERSION +1 -1
- package/clients/viewer/browser-host.js +1620 -134
- package/clients/viewer/index.html +2 -0
- package/clients/viewer/viewer.css +542 -1
- package/logics_manager/cli.py +22 -20
- package/logics_manager/flow.py +61 -1
- package/logics_manager/sync.py +47 -19
- package/logics_manager/viewer.py +1206 -20
- package/package.json +2 -1
- package/pyproject.toml +1 -1
package/logics_manager/flow.py
CHANGED
|
@@ -15,6 +15,7 @@ from .flow_evidence import structured_validation_line as _structured_validation_
|
|
|
15
15
|
from .index import index_payload
|
|
16
16
|
from .lint import expected_workflow_mermaid_signature, lint_payload
|
|
17
17
|
from .path_utils import ensure_relative_to
|
|
18
|
+
from .sync import read_logics_doc_payload
|
|
18
19
|
from .termstyle import colorize_help
|
|
19
20
|
|
|
20
21
|
|
|
@@ -206,6 +207,10 @@ def _build_help() -> str:
|
|
|
206
207
|
" List workflow docs that are still active.",
|
|
207
208
|
" Flags: --kind {all,request,backlog,task}, --format {text,json}",
|
|
208
209
|
"",
|
|
210
|
+
" show <ref>",
|
|
211
|
+
" Show a bounded workflow document view.",
|
|
212
|
+
" Flags: --max-chars, --section, --format {text,json}",
|
|
213
|
+
"",
|
|
209
214
|
" companion <product|architecture>",
|
|
210
215
|
" Create a companion doc from the integrated runtime.",
|
|
211
216
|
" Flags: --title, --source-ref, --request-ref, --backlog-ref, --task-ref, --format {text,json}, --dry-run",
|
|
@@ -251,6 +256,7 @@ def _build_help() -> str:
|
|
|
251
256
|
"Examples:",
|
|
252
257
|
' logics-manager flow new request --title "My request"',
|
|
253
258
|
" logics-manager flow deliver --from-product prod_017_delivery_loop",
|
|
259
|
+
" logics-manager flow show req_001_my_request",
|
|
254
260
|
" logics-manager flow validate-closeout task_003_fix_docs",
|
|
255
261
|
" logics-manager flow repair gates task_003_fix_docs",
|
|
256
262
|
" logics-manager flow closeout task_003_fix_docs --validation \"pytest passed\" --index --lint --audit",
|
|
@@ -338,6 +344,27 @@ def _build_list_help() -> str:
|
|
|
338
344
|
)
|
|
339
345
|
|
|
340
346
|
|
|
347
|
+
def _build_show_help() -> str:
|
|
348
|
+
return "\n".join(
|
|
349
|
+
[
|
|
350
|
+
"Logics Flow Show",
|
|
351
|
+
"Show a bounded workflow document view.",
|
|
352
|
+
"",
|
|
353
|
+
"Usage:",
|
|
354
|
+
" logics-manager flow show <ref-or-path> [args...]",
|
|
355
|
+
"",
|
|
356
|
+
"Flags:",
|
|
357
|
+
" --max-chars",
|
|
358
|
+
" --section",
|
|
359
|
+
" --format {text,json}",
|
|
360
|
+
"",
|
|
361
|
+
"Examples:",
|
|
362
|
+
" logics-manager flow show req_001_my_request",
|
|
363
|
+
" logics-manager flow show task_003_fix_docs --section Validation",
|
|
364
|
+
]
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
|
|
341
368
|
def _build_companion_help() -> str:
|
|
342
369
|
return "\n".join(
|
|
343
370
|
[
|
|
@@ -2214,6 +2241,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2214
2241
|
list_parser.add_argument("--format", choices=("text", "json"), default="text")
|
|
2215
2242
|
list_parser.set_defaults(func=cmd_list)
|
|
2216
2243
|
|
|
2244
|
+
show_parser = sub.add_parser("show", help="Show a bounded workflow document view.")
|
|
2245
|
+
show_parser.add_argument("source")
|
|
2246
|
+
show_parser.add_argument("--max-chars", type=int, default=4000)
|
|
2247
|
+
show_parser.add_argument("--section", action="append", default=[])
|
|
2248
|
+
show_parser.add_argument("--format", choices=("text", "json"), default="text")
|
|
2249
|
+
show_parser.set_defaults(func=cmd_show)
|
|
2250
|
+
|
|
2217
2251
|
companion_parser = sub.add_parser("companion", help="Create a companion doc from the integrated runtime.")
|
|
2218
2252
|
companion_sub = companion_parser.add_subparsers(dest="kind", required=True)
|
|
2219
2253
|
for kind in ("product", "architecture"):
|
|
@@ -2429,6 +2463,22 @@ def cmd_list(args: argparse.Namespace) -> dict[str, object]:
|
|
|
2429
2463
|
return payload
|
|
2430
2464
|
|
|
2431
2465
|
|
|
2466
|
+
def cmd_show(args: argparse.Namespace) -> dict[str, object]:
|
|
2467
|
+
repo_root = _find_repo_root(Path.cwd())
|
|
2468
|
+
max_chars = args.max_chars if args.max_chars > 0 else 4000
|
|
2469
|
+
payload = read_logics_doc_payload(repo_root, args.source, max_chars=min(max_chars, 12000), sections=args.section or None)
|
|
2470
|
+
if args.format == "json":
|
|
2471
|
+
print_payload({"command": "show", **payload}, args.format)
|
|
2472
|
+
else:
|
|
2473
|
+
print(f"{payload['ref']} ({payload['kind']}): {payload['title']}")
|
|
2474
|
+
print(f"- path: {payload['path']}")
|
|
2475
|
+
print(f"- status: {payload['status']}")
|
|
2476
|
+
print(f"- truncated: {payload['truncated']}")
|
|
2477
|
+
print("")
|
|
2478
|
+
print(str(payload["content"]).rstrip())
|
|
2479
|
+
return payload
|
|
2480
|
+
|
|
2481
|
+
|
|
2432
2482
|
def cmd_companion(args: argparse.Namespace) -> dict[str, object]:
|
|
2433
2483
|
repo_root = _find_repo_root(Path.cwd())
|
|
2434
2484
|
request_ref, backlog_ref, task_ref = _resolve_workflow_refs_for_companion(
|
|
@@ -2793,6 +2843,9 @@ def closeout_payload(
|
|
|
2793
2843
|
finish_payload: dict[str, object] | None = None
|
|
2794
2844
|
if not dry_run:
|
|
2795
2845
|
_close_chain_for_kind(repo_root, task_path, DOC_KINDS["task"], dry_run=False, quiet=True)
|
|
2846
|
+
for ref in mermaid_refs:
|
|
2847
|
+
if ref.startswith(f"{DOC_KINDS['request'].prefix}_"):
|
|
2848
|
+
_maybe_close_request_chain(repo_root, ref, dry_run=False, quiet=True)
|
|
2796
2849
|
finish_issues = _verify_finished_task_chain(repo_root, task_path)
|
|
2797
2850
|
if finish_issues:
|
|
2798
2851
|
raise SystemExit("Finish verification failed:\n" + "\n".join(f"- {issue}" for issue in finish_issues))
|
|
@@ -3244,6 +3297,9 @@ def main(argv: list[str]) -> int:
|
|
|
3244
3297
|
if argv[0] == "list" and _help_requested(argv, 1):
|
|
3245
3298
|
_print_help(_build_list_help())
|
|
3246
3299
|
return 0
|
|
3300
|
+
if argv[0] == "show" and _help_requested(argv, 1):
|
|
3301
|
+
_print_help(_build_show_help())
|
|
3302
|
+
return 0
|
|
3247
3303
|
if argv[0] == "companion" and _help_requested(argv, 1):
|
|
3248
3304
|
_print_help(_build_companion_help())
|
|
3249
3305
|
return 0
|
|
@@ -3289,9 +3345,13 @@ def main(argv: list[str]) -> int:
|
|
|
3289
3345
|
if argv[0] == "finish" and len(argv) > 1 and argv[1] == "task" and _help_requested(argv, 2):
|
|
3290
3346
|
_print_help(_build_finish_kind_help(argv[1]))
|
|
3291
3347
|
return 0
|
|
3348
|
+
valid_commands = {"new", "list", "show", "companion", "deliver", "validate-closeout", "repair", "closeout", "promote", "split", "close", "finish"}
|
|
3349
|
+
if argv[0] not in valid_commands:
|
|
3350
|
+
hint = " Use `logics-manager flow show <ref>` to inspect a workflow doc." if argv[0] in {"read", "view", "cat"} else " Run `logics-manager flow --help` for valid commands."
|
|
3351
|
+
raise SystemExit(f"Unsupported flow subcommand: {argv[0]}.{hint}")
|
|
3292
3352
|
parser = build_parser()
|
|
3293
3353
|
args = parser.parse_args(argv)
|
|
3294
|
-
if args.command not in
|
|
3354
|
+
if args.command not in valid_commands:
|
|
3295
3355
|
raise SystemExit("Unsupported flow subcommand for the native CLI slice.")
|
|
3296
3356
|
payload = args.func(args)
|
|
3297
3357
|
if args.command == "validate-closeout" and isinstance(payload, dict) and not payload.get("ok", False):
|
package/logics_manager/sync.py
CHANGED
|
@@ -259,11 +259,23 @@ def _build_context_pack(
|
|
|
259
259
|
profile: str,
|
|
260
260
|
config: dict[str, object] | None = None,
|
|
261
261
|
) -> dict[str, object]:
|
|
262
|
+
seed_refs = [ref for ref in seed_ref.split(",") if ref]
|
|
262
263
|
docs = _load_workflow_docs(repo_root)
|
|
263
|
-
|
|
264
|
-
if
|
|
265
|
-
|
|
266
|
-
|
|
264
|
+
seeds = [docs.get(ref) for ref in seed_refs]
|
|
265
|
+
missing = [ref for ref, doc in zip(seed_refs, seeds) if doc is None]
|
|
266
|
+
if missing:
|
|
267
|
+
raise SystemExit(f"Unknown workflow ref(s): {', '.join(f'`{ref}`' for ref in missing)}.")
|
|
268
|
+
ordered: list[WorkflowDocModel] = []
|
|
269
|
+
seen: set[str] = set()
|
|
270
|
+
per_seed_limit = _context_profile_limit(profile)
|
|
271
|
+
for seed in seeds:
|
|
272
|
+
if seed is None:
|
|
273
|
+
continue
|
|
274
|
+
for doc in _workflow_neighborhood(seed, docs)[:per_seed_limit]:
|
|
275
|
+
if doc.ref in seen:
|
|
276
|
+
continue
|
|
277
|
+
ordered.append(doc)
|
|
278
|
+
seen.add(doc.ref)
|
|
267
279
|
changed_paths = _git_changed_paths(repo_root) if mode == "diff-first" else []
|
|
268
280
|
cache_key = _context_pack_cache_key(
|
|
269
281
|
repo_root,
|
|
@@ -281,7 +293,8 @@ def _build_context_pack(
|
|
|
281
293
|
"ref": seed_ref,
|
|
282
294
|
"mode": mode,
|
|
283
295
|
"profile": profile,
|
|
284
|
-
"
|
|
296
|
+
"refs": seed_refs,
|
|
297
|
+
"budgets": {"max_docs": per_seed_limit * max(1, len(seed_refs)), "max_docs_per_ref": per_seed_limit},
|
|
285
298
|
"changed_paths": changed_paths,
|
|
286
299
|
"docs": pack_docs,
|
|
287
300
|
"estimates": {
|
|
@@ -725,6 +738,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
725
738
|
close_eligible.set_defaults(func=cmd_close_eligible_requests)
|
|
726
739
|
|
|
727
740
|
refresh_mermaid = sub.add_parser("refresh-mermaid-signatures", help="Refresh stale workflow Mermaid signatures without rewriting the full diagram body.")
|
|
741
|
+
refresh_mermaid.add_argument("sources", nargs="*", help="Optional workflow refs or paths to scope the refresh.")
|
|
742
|
+
refresh_mermaid.add_argument("--changed-only", action="store_true", help="Refresh only changed workflow docs.")
|
|
728
743
|
refresh_mermaid.add_argument("--format", choices=("text", "json"), default="text")
|
|
729
744
|
refresh_mermaid.add_argument("--dry-run", action="store_true")
|
|
730
745
|
refresh_mermaid.set_defaults(func=cmd_refresh_mermaid_signatures)
|
|
@@ -779,7 +794,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
779
794
|
append_note.set_defaults(func=cmd_append_note)
|
|
780
795
|
|
|
781
796
|
context_pack = sub.add_parser("context-pack", help="Build a compact context pack from workflow docs.")
|
|
782
|
-
context_pack.add_argument("
|
|
797
|
+
context_pack.add_argument("refs", nargs="+", help="Seed workflow ref(s) for the context pack.")
|
|
783
798
|
context_pack.add_argument("--mode", choices=("summary-only", "diff-first", "full"), default="summary-only")
|
|
784
799
|
context_pack.add_argument("--profile", choices=("tiny", "normal", "deep"), default="normal")
|
|
785
800
|
context_pack.add_argument("--out", help="Write the JSON artifact to this relative path.")
|
|
@@ -812,13 +827,14 @@ def _build_help() -> str:
|
|
|
812
827
|
"",
|
|
813
828
|
" refresh-mermaid-signatures",
|
|
814
829
|
" Refresh stale Mermaid signatures without rewriting diagram bodies.",
|
|
815
|
-
"
|
|
830
|
+
" Args: [refs-or-paths...]",
|
|
831
|
+
" Flags: --changed-only, --format {text,json}, --dry-run",
|
|
816
832
|
"",
|
|
817
833
|
" schema-status [sources...]",
|
|
818
834
|
" Report schema-version coverage for selected workflow docs.",
|
|
819
835
|
" Flags: --format {text,json}",
|
|
820
836
|
"",
|
|
821
|
-
" context-pack <
|
|
837
|
+
" context-pack <refs...>",
|
|
822
838
|
" Build a compact JSON context pack from workflow docs.",
|
|
823
839
|
" Flags: --mode {summary-only,diff-first,full}, --profile {tiny,normal,deep}, --out, --format {text,json}, --dry-run",
|
|
824
840
|
"",
|
|
@@ -848,7 +864,7 @@ def _build_help() -> str:
|
|
|
848
864
|
"",
|
|
849
865
|
"Examples:",
|
|
850
866
|
" logics-manager sync schema-status",
|
|
851
|
-
" logics-manager sync context-pack req_001_my_request --out logics/context-pack.json",
|
|
867
|
+
" logics-manager sync context-pack req_001_my_request task_002_fix_bug --out logics/context-pack.json",
|
|
852
868
|
" logics-manager sync export-graph --format json",
|
|
853
869
|
]
|
|
854
870
|
)
|
|
@@ -879,9 +895,10 @@ def _build_subcommand_help(command: str) -> str:
|
|
|
879
895
|
"Refresh stale workflow Mermaid signatures without rewriting diagram bodies.",
|
|
880
896
|
"",
|
|
881
897
|
"Usage:",
|
|
882
|
-
" logics-manager sync refresh-mermaid-signatures [args...]",
|
|
898
|
+
" logics-manager sync refresh-mermaid-signatures [refs-or-paths...] [args...]",
|
|
883
899
|
"",
|
|
884
900
|
"Flags:",
|
|
901
|
+
" --changed-only",
|
|
885
902
|
" --format {text,json}",
|
|
886
903
|
" --dry-run",
|
|
887
904
|
]
|
|
@@ -909,7 +926,7 @@ def _build_subcommand_help(command: str) -> str:
|
|
|
909
926
|
"Build a compact JSON context pack from workflow docs.",
|
|
910
927
|
"",
|
|
911
928
|
"Usage:",
|
|
912
|
-
" logics-manager sync context-pack <
|
|
929
|
+
" logics-manager sync context-pack <refs...> [args...]",
|
|
913
930
|
"",
|
|
914
931
|
"Flags:",
|
|
915
932
|
" --mode {summary-only,diff-first,full}",
|
|
@@ -919,7 +936,7 @@ def _build_subcommand_help(command: str) -> str:
|
|
|
919
936
|
" --dry-run",
|
|
920
937
|
"",
|
|
921
938
|
"Example:",
|
|
922
|
-
" logics-manager sync context-pack req_001_my_request --out logics/context-pack.json",
|
|
939
|
+
" logics-manager sync context-pack req_001_my_request task_002_fix_bug --out logics/context-pack.json",
|
|
923
940
|
]
|
|
924
941
|
)
|
|
925
942
|
if command == "read-doc":
|
|
@@ -1053,17 +1070,26 @@ def cmd_close_eligible_requests(args: argparse.Namespace) -> dict[str, object]:
|
|
|
1053
1070
|
def cmd_refresh_mermaid_signatures(args: argparse.Namespace) -> dict[str, object]:
|
|
1054
1071
|
repo_root = _find_repo_root(Path.cwd())
|
|
1055
1072
|
modified: list[str] = []
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1073
|
+
if args.changed_only:
|
|
1074
|
+
changed_sources = [
|
|
1075
|
+
path
|
|
1076
|
+
for path in _git_changed_paths(repo_root)
|
|
1077
|
+
if path.startswith(("logics/request/", "logics/backlog/", "logics/tasks/")) and path.endswith(".md")
|
|
1078
|
+
]
|
|
1079
|
+
targets = _resolve_target_docs(repo_root, changed_sources)
|
|
1080
|
+
else:
|
|
1081
|
+
targets = _resolve_target_docs(repo_root, args.sources)
|
|
1082
|
+
for kind, path in targets:
|
|
1083
|
+
if refresh_workflow_mermaid_signature_file(path, kind, args.dry_run, repo_root=repo_root):
|
|
1084
|
+
modified.append(path.relative_to(repo_root).as_posix())
|
|
1061
1085
|
|
|
1062
1086
|
payload = {
|
|
1063
1087
|
"command": "sync",
|
|
1064
1088
|
"kind": "refresh-mermaid-signatures",
|
|
1065
1089
|
"repo_root": repo_root.as_posix(),
|
|
1066
1090
|
"modified_files": modified,
|
|
1091
|
+
"scanned_files": [path.relative_to(repo_root).as_posix() for _kind, path in targets],
|
|
1092
|
+
"changed_only": args.changed_only,
|
|
1067
1093
|
"dry_run": args.dry_run,
|
|
1068
1094
|
}
|
|
1069
1095
|
if args.format == "json":
|
|
@@ -1106,6 +1132,8 @@ def cmd_read_doc(args: argparse.Namespace) -> dict[str, object]:
|
|
|
1106
1132
|
print(f"- path: {payload['path']}")
|
|
1107
1133
|
print(f"- status: {payload['status']}")
|
|
1108
1134
|
print(f"- truncated: {payload['truncated']}")
|
|
1135
|
+
print("")
|
|
1136
|
+
print(str(payload["content"]).rstrip())
|
|
1109
1137
|
return {"command": "sync", "kind": "read-doc", "repo_root": repo_root.as_posix(), **payload}
|
|
1110
1138
|
|
|
1111
1139
|
|
|
@@ -1172,7 +1200,7 @@ def cmd_append_note(args: argparse.Namespace) -> dict[str, object]:
|
|
|
1172
1200
|
|
|
1173
1201
|
def cmd_context_pack(args: argparse.Namespace) -> dict[str, object]:
|
|
1174
1202
|
repo_root = _find_repo_root(Path.cwd())
|
|
1175
|
-
payload = _build_context_pack(repo_root, args.
|
|
1203
|
+
payload = _build_context_pack(repo_root, ",".join(args.refs), mode=args.mode, profile=args.profile, config=None)
|
|
1176
1204
|
if args.out:
|
|
1177
1205
|
out_path, output_path = resolve_repo_output_path(repo_root, args.out)
|
|
1178
1206
|
serialized = json.dumps(payload, indent=2, sort_keys=True) + "\n"
|
|
@@ -1188,7 +1216,7 @@ def cmd_context_pack(args: argparse.Namespace) -> dict[str, object]:
|
|
|
1188
1216
|
if args.format == "json":
|
|
1189
1217
|
print(json.dumps(payload, indent=2, sort_keys=True))
|
|
1190
1218
|
else:
|
|
1191
|
-
print(f"Context pack: {
|
|
1219
|
+
print(f"Context pack: {', '.join(args.refs)} ({payload['mode']}, {payload['profile']})")
|
|
1192
1220
|
print(f"- docs: {payload['estimates']['doc_count']}")
|
|
1193
1221
|
return {"command": "sync", "kind": "context-pack", "repo_root": repo_root.as_posix(), **payload}
|
|
1194
1222
|
|