@grifhinz/logics-manager 2.0.2 → 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.
- package/README.md +6 -3
- package/VERSION +1 -1
- package/logics_manager/assist.py +386 -12
- package/logics_manager/bootstrap.py +28 -21
- package/logics_manager/cli.py +146 -37
- package/logics_manager/flow.py +570 -1
- package/logics_manager/sync.py +138 -0
- package/logics_manager/termstyle.py +75 -0
- package/package.json +2 -1
- package/pyproject.toml +1 -1
package/logics_manager/cli.py
CHANGED
|
@@ -2,9 +2,10 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
from importlib import metadata
|
|
5
|
+
import subprocess
|
|
5
6
|
import sys
|
|
7
|
+
from shutil import which
|
|
6
8
|
from pathlib import Path
|
|
7
|
-
from textwrap import dedent
|
|
8
9
|
|
|
9
10
|
from .bootstrap import bootstrap_payload, render_bootstrap
|
|
10
11
|
from .assist import main as assist_main
|
|
@@ -14,6 +15,91 @@ from .config import ConfigError, find_repo_root, render_config_show
|
|
|
14
15
|
from .index import index_payload, render_index
|
|
15
16
|
from .lint import lint_payload, render_lint
|
|
16
17
|
from .doctor import render_doctor
|
|
18
|
+
from .termstyle import colorize_help
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
DEFAULT_SELF_UPDATE_PY_PACKAGE = "logics-manager"
|
|
22
|
+
DEFAULT_SELF_UPDATE_PACKAGE = "@grifhinz/logics-manager"
|
|
23
|
+
HELP_ARGV = (["-h"], ["--help"])
|
|
24
|
+
ROOT_COMMANDS = (
|
|
25
|
+
"bootstrap",
|
|
26
|
+
"flow",
|
|
27
|
+
"sync",
|
|
28
|
+
"assist",
|
|
29
|
+
"audit",
|
|
30
|
+
"index",
|
|
31
|
+
"lint",
|
|
32
|
+
"config",
|
|
33
|
+
"doctor",
|
|
34
|
+
"self-update",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _build_root_help() -> str:
|
|
39
|
+
sections = [
|
|
40
|
+
"Logics Manager CLI",
|
|
41
|
+
"Canonical CLI for workflow, validation, and runtime ops.",
|
|
42
|
+
"",
|
|
43
|
+
"Usage:",
|
|
44
|
+
" logics-manager <command> [args...]",
|
|
45
|
+
" logics-manager config show [options]",
|
|
46
|
+
"",
|
|
47
|
+
"Top-level options:",
|
|
48
|
+
" -h, --help Show this help message and exit.",
|
|
49
|
+
" --version Print the installed version.",
|
|
50
|
+
"",
|
|
51
|
+
"Commands:",
|
|
52
|
+
" bootstrap",
|
|
53
|
+
" Prepare or check the workflow tree and generated instructions.",
|
|
54
|
+
" Options: --check, --format {text,json}",
|
|
55
|
+
"",
|
|
56
|
+
" flow",
|
|
57
|
+
" Create and manage workflow docs.",
|
|
58
|
+
" Subcommands: new, list, companion, promote, split, close, finish",
|
|
59
|
+
" Key flags: --title, --slug, --from-version, --understanding, --confidence, --status, --complexity, --theme, --progress, --format {text,json}, --dry-run",
|
|
60
|
+
"",
|
|
61
|
+
" sync",
|
|
62
|
+
" Synchronize workflow transitions and exports.",
|
|
63
|
+
" Subcommands: close-eligible-requests, refresh-mermaid-signatures, schema-status, context-pack, export-graph",
|
|
64
|
+
"",
|
|
65
|
+
" assist",
|
|
66
|
+
" Inspect runtime signals and build context bundles.",
|
|
67
|
+
" Subcommands: runtime-status, diff-risk, commit-plan, changed-surface-summary, doc-consistency, review-checklist, validation-checklist, validation-summary, test-impact-summary, roi-report, claude-bridges, context, claude-instructions, next-step, request-draft, spec-first-pass, backlog-groom, closure-summary",
|
|
68
|
+
"",
|
|
69
|
+
" audit",
|
|
70
|
+
" Audit request, backlog, and task consistency.",
|
|
71
|
+
" Options: --stale-days, --skip-ac-traceability, --skip-gates, --legacy-cutoff-version, --format {text,json}, --group-by-doc, --autofix-ac-traceability, --paths, --refs, --since-version, --token-hygiene, --autofix-structure, --governance-profile",
|
|
72
|
+
"",
|
|
73
|
+
" index",
|
|
74
|
+
" Generate `logics/INDEX.md` from the workflow corpus.",
|
|
75
|
+
" Options: --out, --format {text,json}",
|
|
76
|
+
"",
|
|
77
|
+
" lint",
|
|
78
|
+
" Lint workflow documents for filenames, headings, and indicators.",
|
|
79
|
+
" Options: --require-status, --format {text,json}",
|
|
80
|
+
"",
|
|
81
|
+
" config show",
|
|
82
|
+
" Render the merged runtime config.",
|
|
83
|
+
" Options: --format {text,json}",
|
|
84
|
+
"",
|
|
85
|
+
" doctor",
|
|
86
|
+
" Check required workflow directories and schema metadata.",
|
|
87
|
+
" Options: --format {text,json}",
|
|
88
|
+
"",
|
|
89
|
+
" self-update",
|
|
90
|
+
" Update the installed Python or npm package.",
|
|
91
|
+
" Options: --manager {auto,pip,npm}, --package, --python-package, --dry-run",
|
|
92
|
+
"",
|
|
93
|
+
"Examples:",
|
|
94
|
+
' logics-manager flow new request --title "My request"',
|
|
95
|
+
" logics-manager audit",
|
|
96
|
+
" logics-manager config show --format json",
|
|
97
|
+
]
|
|
98
|
+
return "\n".join(sections)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _print_help(text: str) -> None:
|
|
102
|
+
print(colorize_help(text))
|
|
17
103
|
|
|
18
104
|
|
|
19
105
|
def get_cli_version() -> str:
|
|
@@ -35,34 +121,22 @@ def get_cli_version() -> str:
|
|
|
35
121
|
def main(argv: list[str] | None = None) -> int:
|
|
36
122
|
if argv is None:
|
|
37
123
|
argv = sys.argv[1:]
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
description="Canonical Logics CLI for workflow, validation, and runtime operations.",
|
|
41
|
-
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
42
|
-
epilog=dedent(
|
|
43
|
-
"""
|
|
44
|
-
Examples:
|
|
45
|
-
logics-manager flow new request --title "My request"
|
|
46
|
-
logics-manager audit
|
|
47
|
-
logics-manager config show --format json
|
|
48
|
-
"""
|
|
49
|
-
).strip(),
|
|
50
|
-
)
|
|
51
|
-
parser.add_argument("--version", action="version", version=f"logics-manager {get_cli_version()}")
|
|
52
|
-
parser.add_argument(
|
|
53
|
-
"command",
|
|
54
|
-
nargs="?",
|
|
55
|
-
choices=("bootstrap", "flow", "sync", "assist", "audit", "index", "lint", "config", "doctor"),
|
|
56
|
-
)
|
|
57
|
-
parser.add_argument("rest", nargs=argparse.REMAINDER)
|
|
58
|
-
args = parser.parse_args(argv[:1])
|
|
59
|
-
|
|
60
|
-
if args.command is None:
|
|
61
|
-
parser.print_help()
|
|
124
|
+
if not argv:
|
|
125
|
+
_print_help(_build_root_help())
|
|
62
126
|
return 1
|
|
127
|
+
if argv[0] in ("-h", "--help"):
|
|
128
|
+
_print_help(_build_root_help())
|
|
129
|
+
return 0
|
|
130
|
+
if argv[0] == "--version":
|
|
131
|
+
print(f"logics-manager {get_cli_version()}")
|
|
132
|
+
return 0
|
|
133
|
+
|
|
134
|
+
command = argv[0]
|
|
135
|
+
if command not in ROOT_COMMANDS:
|
|
136
|
+
raise SystemExit(f"Unsupported command: {command}")
|
|
63
137
|
|
|
64
138
|
rest = argv[1:]
|
|
65
|
-
if
|
|
139
|
+
if command == "config":
|
|
66
140
|
if not rest or rest[0] != "show":
|
|
67
141
|
raise SystemExit("Usage: logics-manager config show [args...]")
|
|
68
142
|
config_args = rest[1:]
|
|
@@ -76,7 +150,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
76
150
|
raise SystemExit(str(exc)) from exc
|
|
77
151
|
print(output)
|
|
78
152
|
return 0
|
|
79
|
-
if
|
|
153
|
+
if command == "doctor":
|
|
80
154
|
doctor_args = rest
|
|
81
155
|
parser = argparse.ArgumentParser(prog="logics-manager doctor", add_help=False)
|
|
82
156
|
parser.add_argument("--format", choices=("text", "json"), default="text")
|
|
@@ -88,7 +162,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
88
162
|
raise SystemExit(str(exc)) from exc
|
|
89
163
|
print(output)
|
|
90
164
|
return 0
|
|
91
|
-
if
|
|
165
|
+
if command == "bootstrap":
|
|
92
166
|
parser = argparse.ArgumentParser(prog="logics-manager bootstrap", add_help=False)
|
|
93
167
|
parser.add_argument("--check", action="store_true")
|
|
94
168
|
parser.add_argument("--format", choices=("text", "json"), default="text")
|
|
@@ -100,21 +174,56 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
100
174
|
payload = bootstrap_payload(repo_root, check=parsed.check)
|
|
101
175
|
print(render_bootstrap(payload, output_format=parsed.format))
|
|
102
176
|
return 0 if payload["ok"] else 1
|
|
103
|
-
if
|
|
177
|
+
if command == "self-update":
|
|
178
|
+
parser = argparse.ArgumentParser(prog="logics-manager self-update", add_help=False)
|
|
179
|
+
parser.add_argument("--manager", choices=("auto", "pip", "npm"), default="auto")
|
|
180
|
+
parser.add_argument("--package", default=DEFAULT_SELF_UPDATE_PACKAGE)
|
|
181
|
+
parser.add_argument("--python-package", default=DEFAULT_SELF_UPDATE_PY_PACKAGE)
|
|
182
|
+
parser.add_argument("--dry-run", action="store_true")
|
|
183
|
+
parsed, _unknown = parser.parse_known_args(rest)
|
|
184
|
+
|
|
185
|
+
manager = parsed.manager
|
|
186
|
+
if manager == "auto":
|
|
187
|
+
try:
|
|
188
|
+
metadata.version(parsed.python_package)
|
|
189
|
+
except metadata.PackageNotFoundError:
|
|
190
|
+
manager = "npm" if which("npm") else "pip"
|
|
191
|
+
else:
|
|
192
|
+
manager = "pip"
|
|
193
|
+
|
|
194
|
+
if manager == "pip":
|
|
195
|
+
command = [sys.executable, "-m", "pip", "install", "--upgrade", parsed.python_package]
|
|
196
|
+
else:
|
|
197
|
+
npm = which("npm")
|
|
198
|
+
if not npm:
|
|
199
|
+
print("npm was not found on PATH. Install Node.js/npm or update the package manually.")
|
|
200
|
+
return 1
|
|
201
|
+
command = [npm, "install", "-g", f"{parsed.package}@latest"]
|
|
202
|
+
|
|
203
|
+
if parsed.dry_run:
|
|
204
|
+
print("Dry run: " + " ".join(command))
|
|
205
|
+
return 0
|
|
206
|
+
|
|
207
|
+
result = subprocess.run(command, check=False)
|
|
208
|
+
if result.returncode == 0:
|
|
209
|
+
target = parsed.python_package if manager == "pip" else parsed.package
|
|
210
|
+
print(f"Updated {target} via {manager}.")
|
|
211
|
+
return result.returncode
|
|
212
|
+
if command == "flow" and (rest[:1] in (["new"], ["list"], ["companion"], ["promote"], ["split"], ["close"], ["finish"]) or rest[:1] in HELP_ARGV):
|
|
104
213
|
from .flow import main as flow_main
|
|
105
214
|
|
|
106
215
|
return flow_main(rest)
|
|
107
|
-
if
|
|
108
|
-
if rest[:1] not in (["close-eligible-requests"], ["refresh-mermaid-signatures"], ["schema-status"], ["context-pack"], ["export-graph"]):
|
|
216
|
+
if command == "sync":
|
|
217
|
+
if rest[:1] not in (["close-eligible-requests"], ["refresh-mermaid-signatures"], ["schema-status"], ["context-pack"], ["export-graph"]) and rest[:1] not in HELP_ARGV:
|
|
109
218
|
raise SystemExit("Unsupported sync subcommand for the native CLI slice.")
|
|
110
219
|
from .sync import main as sync_main
|
|
111
220
|
|
|
112
221
|
return sync_main(rest)
|
|
113
|
-
if
|
|
114
|
-
if rest[:1] not in (["runtime-status"], ["diff-risk"], ["commit-plan"], ["changed-surface-summary"], ["doc-consistency"], ["review-checklist"], ["validation-checklist"], ["validation-summary"], ["test-impact-summary"], ["roi-report"], ["next-step"], ["claude-bridges"], ["claude-instructions"], ["request-draft"], ["spec-first-pass"], ["backlog-groom"], ["closure-summary"], ["context"]):
|
|
222
|
+
if command == "assist":
|
|
223
|
+
if rest[:1] not in (["runtime-status"], ["diff-risk"], ["commit-plan"], ["changed-surface-summary"], ["doc-consistency"], ["review-checklist"], ["validation-checklist"], ["validation-summary"], ["test-impact-summary"], ["roi-report"], ["next-step"], ["claude-bridges"], ["claude-instructions"], ["request-draft"], ["spec-first-pass"], ["backlog-groom"], ["closure-summary"], ["context"]) and rest[:1] not in HELP_ARGV:
|
|
115
224
|
raise SystemExit("Unsupported assist subcommand for the native CLI slice.")
|
|
116
225
|
return assist_main(rest)
|
|
117
|
-
if
|
|
226
|
+
if command == "audit":
|
|
118
227
|
audit_parser = build_audit_parser()
|
|
119
228
|
parsed, _unknown = audit_parser.parse_known_args(rest)
|
|
120
229
|
repo_root = find_repo_root(Path.cwd())
|
|
@@ -154,7 +263,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
154
263
|
raise SystemExit(str(exc)) from exc
|
|
155
264
|
print(output)
|
|
156
265
|
return 0 if payload["ok"] else 1
|
|
157
|
-
if
|
|
266
|
+
if command == "index":
|
|
158
267
|
parser = argparse.ArgumentParser(prog="logics-manager index", add_help=False)
|
|
159
268
|
parser.add_argument("--out", default="logics/INDEX.md")
|
|
160
269
|
parser.add_argument("--format", choices=("text", "json"), default="text")
|
|
@@ -167,7 +276,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
167
276
|
output = render_index(repo_root, out=parsed.out, output_format=parsed.format) if parsed.format == "json" else f"Wrote {payload['output_path']}"
|
|
168
277
|
print(output)
|
|
169
278
|
return 0 if payload["ok"] else 1
|
|
170
|
-
if
|
|
279
|
+
if command == "lint":
|
|
171
280
|
parser = argparse.ArgumentParser(prog="logics-manager lint", add_help=False)
|
|
172
281
|
parser.add_argument("--require-status", action="store_true")
|
|
173
282
|
parser.add_argument("--format", choices=("text", "json"), default="text")
|
|
@@ -180,4 +289,4 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
180
289
|
raise SystemExit(str(exc)) from exc
|
|
181
290
|
print(output)
|
|
182
291
|
return 0 if payload["ok"] else 1
|
|
183
|
-
raise SystemExit(f"Unsupported command: {
|
|
292
|
+
raise SystemExit(f"Unsupported command: {command}")
|