@grifhinz/logics-manager 2.0.0 → 2.0.4
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 +5 -2
- package/VERSION +1 -1
- package/logics_manager/assist.py +20 -12
- package/logics_manager/bootstrap.py +28 -21
- package/logics_manager/cli.py +42 -1
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/npm/logics-manager.mjs +0 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/AlexAgo83/logics-manager/actions/workflows/ci.yml)
|
|
4
4
|
[](LICENSE)
|
|
5
|
-

|
|
6
6
|

|
|
7
7
|

|
|
8
8
|

|
|
@@ -35,6 +35,9 @@ Install the npm package with:
|
|
|
35
35
|
npm install -g @grifhinz/logics-manager
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
To update that CLI later, run `logics-manager self-update`.
|
|
39
|
+
The command uses `pip` when the Python package is installed and falls back to `npm` for the global npm package.
|
|
40
|
+
|
|
38
41
|
For the editor client, build and install the VSIX:
|
|
39
42
|
|
|
40
43
|
```bash
|
|
@@ -267,7 +270,7 @@ Contract:
|
|
|
267
270
|
- `Environment` can also surface direct remediation actions when the plugin detects a stale runtime, an incomplete bootstrap, a missing global publication, or missing environment placeholders.
|
|
268
271
|
- `Environment` now uses a clearer hierarchy with summary, recommended actions, current status, and technical details, plus hybrid assist runtime state, backend availability, degraded reasons, Claude-bridge presence, and the shared Windows-safe runtime entrypoint.
|
|
269
272
|
- `Check Environment` can be promoted into `Recommended` when the current repo state actually warrants operator attention.
|
|
270
|
-
- repo-local refresh now watches `logics/**/*`, `logics.yaml`, and
|
|
273
|
+
- repo-local refresh now watches `logics/**/*`, `logics.yaml`, and `.git/HEAD`; external global runtime state still requires an explicit refresh because it lives outside the workspace.
|
|
271
274
|
- `Launch Codex` starts Codex using the globally published Logics runtime when the shared runtime is healthy.
|
|
272
275
|
- `AI Runtime Status` probes the shared `logics.py flow assist runtime-status` surface and reports ready providers, flagged providers, cooldown or credential issues, and bounded backend provenance.
|
|
273
276
|
- `AI Provider Insights` opens a dedicated plugin panel backed by `logics.py flow assist roi-report`, with provider mix, execution-path breakdowns, derived rates, estimated ROI proxies, and recent audit drill-down over the shared runtime output.
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.0.
|
|
1
|
+
2.0.4
|
package/logics_manager/assist.py
CHANGED
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
import json
|
|
5
|
+
import os
|
|
5
6
|
from collections import Counter
|
|
6
7
|
from datetime import datetime, timedelta, timezone
|
|
7
8
|
import re
|
|
@@ -1283,7 +1284,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1283
1284
|
|
|
1284
1285
|
claude_bridges = sub.add_parser(
|
|
1285
1286
|
"claude-bridges",
|
|
1286
|
-
help="Render the canonical Claude
|
|
1287
|
+
help="Render the canonical Claude runtime publication manifest and prompts derived from the integrated runtime.",
|
|
1287
1288
|
)
|
|
1288
1289
|
claude_bridges.add_argument("--format", choices=("text", "json"), default="text")
|
|
1289
1290
|
claude_bridges.add_argument("--dry-run", action="store_true")
|
|
@@ -1346,10 +1347,17 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1346
1347
|
return parser
|
|
1347
1348
|
|
|
1348
1349
|
|
|
1349
|
-
def
|
|
1350
|
+
def _get_global_claude_home() -> Path:
|
|
1351
|
+
return Path(os.environ.get("LOGICS_CLAUDE_GLOBAL_HOME") or (Path.home() / ".claude")).resolve()
|
|
1352
|
+
|
|
1353
|
+
|
|
1354
|
+
def _claude_bridge_status(_repo_root: Path) -> dict[str, object]:
|
|
1355
|
+
global_home = _get_global_claude_home()
|
|
1350
1356
|
detected_variants: list[str] = []
|
|
1351
1357
|
for variant in CLAUDE_BRIDGE_VARIANTS:
|
|
1352
|
-
|
|
1358
|
+
command_path = global_home / str(variant["command_path"]).replace(".claude/", "")
|
|
1359
|
+
agent_path = global_home / str(variant["agent_path"]).replace(".claude/", "")
|
|
1360
|
+
if command_path.is_file() and agent_path.is_file():
|
|
1353
1361
|
detected_variants.append(variant["id"])
|
|
1354
1362
|
return {
|
|
1355
1363
|
"available": bool(detected_variants),
|
|
@@ -1368,7 +1376,7 @@ def _render_claude_bridge_lines(variant: dict[str, object], prompt: str) -> tupl
|
|
|
1368
1376
|
command_lines = [
|
|
1369
1377
|
f"# {title}",
|
|
1370
1378
|
"",
|
|
1371
|
-
f"Use the
|
|
1379
|
+
f"Use the published global {title.lower()} bridge for this project.",
|
|
1372
1380
|
"",
|
|
1373
1381
|
"Primary prompt:",
|
|
1374
1382
|
prompt,
|
|
@@ -1377,7 +1385,7 @@ def _render_claude_bridge_lines(variant: dict[str, object], prompt: str) -> tupl
|
|
|
1377
1385
|
agent_lines = [
|
|
1378
1386
|
f"# {title} Agent",
|
|
1379
1387
|
"",
|
|
1380
|
-
f"Use the
|
|
1388
|
+
f"Use the published global {title.lower()} agent for this project.",
|
|
1381
1389
|
"",
|
|
1382
1390
|
"Default prompt:",
|
|
1383
1391
|
prompt,
|
|
@@ -1433,8 +1441,8 @@ def _build_claude_instructions(repo_root: Path) -> dict[str, object]:
|
|
|
1433
1441
|
"- `python3 -m logics_manager lint --require-status`",
|
|
1434
1442
|
"- `python3 -m logics_manager audit --legacy-cutoff-version 1.1.0 --group-by-doc`",
|
|
1435
1443
|
"",
|
|
1436
|
-
"
|
|
1437
|
-
"Do not edit
|
|
1444
|
+
"Claude runtime artifacts are generated outside the repository from the integrated runtime.",
|
|
1445
|
+
"Do not edit generated runtime artifacts by hand unless you are deliberately repairing a generated artifact.",
|
|
1438
1446
|
"",
|
|
1439
1447
|
"Do not edit indicator lines or workflow links by hand.",
|
|
1440
1448
|
"",
|
|
@@ -1454,8 +1462,8 @@ def _select_backend(requested_backend: str | None, bridge_status: dict[str, obje
|
|
|
1454
1462
|
if requested_backend and requested_backend != "auto":
|
|
1455
1463
|
return requested_backend, []
|
|
1456
1464
|
if bridge_status.get("available"):
|
|
1457
|
-
return "codex", ["
|
|
1458
|
-
return "deterministic", ["no
|
|
1465
|
+
return "codex", ["global Claude runtime published"]
|
|
1466
|
+
return "deterministic", ["no global Claude runtime published"]
|
|
1459
1467
|
|
|
1460
1468
|
|
|
1461
1469
|
def cmd_claude_bridges(args: argparse.Namespace) -> dict[str, object]:
|
|
@@ -1953,9 +1961,9 @@ def cmd_runtime_status(args: argparse.Namespace) -> dict[str, object]:
|
|
|
1953
1961
|
print(f"- selected backend: {selected_backend}")
|
|
1954
1962
|
print(f"- model profile: {default_profile}")
|
|
1955
1963
|
print(f"- model: {resolved_model}")
|
|
1956
|
-
print(f"-
|
|
1964
|
+
print(f"- global Claude runtime available: {'yes' if bridge_status['available'] else 'no'}")
|
|
1957
1965
|
if bridge_status["preferred_variant"]:
|
|
1958
|
-
print(f"-
|
|
1966
|
+
print(f"- runtime variant: {bridge_status['preferred_variant']}")
|
|
1959
1967
|
return payload
|
|
1960
1968
|
|
|
1961
1969
|
|
|
@@ -2200,7 +2208,7 @@ def cmd_context(args: argparse.Namespace) -> dict[str, object]:
|
|
|
2200
2208
|
print(f"- ref: {args.ref or '<flow-default>'}")
|
|
2201
2209
|
print(f"- mode: {context_mode}")
|
|
2202
2210
|
print(f"- profile: {profile}")
|
|
2203
|
-
print(f"-
|
|
2211
|
+
print(f"- global Claude runtime available: {'yes' if bridge_status['available'] else 'no'}")
|
|
2204
2212
|
return payload
|
|
2205
2213
|
|
|
2206
2214
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import shutil
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
|
-
from .assist import
|
|
7
|
+
from .assist import _build_claude_instructions
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
WORKFLOW_DIRS: tuple[str, ...] = ("request", "backlog", "tasks", "specs", "product", "architecture", "external", ".cache")
|
|
@@ -13,14 +14,34 @@ def _workflow_directories(repo_root: Path) -> list[Path]:
|
|
|
13
14
|
return [repo_root / "logics" / name for name in WORKFLOW_DIRS]
|
|
14
15
|
|
|
15
16
|
|
|
17
|
+
def _legacy_runtime_paths(repo_root: Path) -> list[Path]:
|
|
18
|
+
return [repo_root / ".claude", repo_root / "logics" / "skills"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _remove_legacy_runtime_paths(repo_root: Path, *, check: bool) -> list[str]:
|
|
22
|
+
removed_paths: list[str] = []
|
|
23
|
+
for target in _legacy_runtime_paths(repo_root):
|
|
24
|
+
if not target.exists():
|
|
25
|
+
continue
|
|
26
|
+
removed_paths.append(target.relative_to(repo_root).as_posix() + ("/" if target.is_dir() else ""))
|
|
27
|
+
if not check:
|
|
28
|
+
if target.is_dir():
|
|
29
|
+
shutil.rmtree(target)
|
|
30
|
+
else:
|
|
31
|
+
target.unlink()
|
|
32
|
+
return removed_paths
|
|
33
|
+
|
|
34
|
+
|
|
16
35
|
def bootstrap_payload(repo_root: Path, *, check: bool) -> dict[str, object]:
|
|
17
36
|
logics_root = repo_root / "logics"
|
|
18
|
-
bridge_manifest = _build_claude_bridge_manifest(repo_root)
|
|
19
37
|
instructions_manifest = _build_claude_instructions(repo_root)
|
|
20
38
|
directory_actions: list[dict[str, object]] = []
|
|
21
39
|
created_paths: list[str] = []
|
|
40
|
+
removed_paths: list[str] = []
|
|
22
41
|
missing_paths: list[str] = []
|
|
23
42
|
|
|
43
|
+
removed_paths.extend(_remove_legacy_runtime_paths(repo_root, check=check))
|
|
44
|
+
|
|
24
45
|
if not logics_root.exists():
|
|
25
46
|
missing_paths.append("logics/")
|
|
26
47
|
elif not logics_root.is_dir():
|
|
@@ -71,24 +92,6 @@ def bootstrap_payload(repo_root: Path, *, check: bool) -> dict[str, object]:
|
|
|
71
92
|
instructions_path.write_text(instructions_content, encoding="utf-8")
|
|
72
93
|
created_paths.append("logics/instructions.md")
|
|
73
94
|
|
|
74
|
-
for bridge in bridge_manifest["bridges"]:
|
|
75
|
-
for rel_path, content in (
|
|
76
|
-
(str(bridge["command_path"]), str(bridge["command_content"])),
|
|
77
|
-
(str(bridge["agent_path"]), str(bridge["agent_content"])),
|
|
78
|
-
):
|
|
79
|
-
bridge_path = repo_root / rel_path
|
|
80
|
-
if bridge_path.exists():
|
|
81
|
-
try:
|
|
82
|
-
if bridge_path.read_text(encoding="utf-8") == content:
|
|
83
|
-
continue
|
|
84
|
-
except Exception:
|
|
85
|
-
pass
|
|
86
|
-
missing_paths.append(rel_path)
|
|
87
|
-
if not check:
|
|
88
|
-
bridge_path.parent.mkdir(parents=True, exist_ok=True)
|
|
89
|
-
bridge_path.write_text(content, encoding="utf-8")
|
|
90
|
-
created_paths.append(rel_path)
|
|
91
|
-
|
|
92
95
|
ok = not missing_paths if check else True
|
|
93
96
|
return {
|
|
94
97
|
"command": "bootstrap",
|
|
@@ -97,8 +100,8 @@ def bootstrap_payload(repo_root: Path, *, check: bool) -> dict[str, object]:
|
|
|
97
100
|
"ok": ok,
|
|
98
101
|
"missing_paths": missing_paths,
|
|
99
102
|
"created_paths": created_paths,
|
|
103
|
+
"removed_paths": removed_paths,
|
|
100
104
|
"directory_actions": directory_actions,
|
|
101
|
-
"claude_bridge_count": bridge_manifest["bridge_count"],
|
|
102
105
|
"claude_instruction_line_count": instructions_manifest["line_count"],
|
|
103
106
|
}
|
|
104
107
|
|
|
@@ -114,6 +117,10 @@ def render_bootstrap(payload: dict[str, object], *, output_format: str) -> str:
|
|
|
114
117
|
lines.append(f"- missing: {path}")
|
|
115
118
|
return "\n".join(lines)
|
|
116
119
|
lines = ["Bootstrap: OK"]
|
|
120
|
+
if payload.get("removed_paths"):
|
|
121
|
+
lines.append("- removed:")
|
|
122
|
+
for path in payload["removed_paths"]:
|
|
123
|
+
lines.append(f" - {path}")
|
|
117
124
|
if payload["created_paths"]:
|
|
118
125
|
lines.append("- created:")
|
|
119
126
|
for path in payload["created_paths"]:
|
package/logics_manager/cli.py
CHANGED
|
@@ -2,7 +2,9 @@ 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
9
|
from textwrap import dedent
|
|
8
10
|
|
|
@@ -16,6 +18,10 @@ from .lint import lint_payload, render_lint
|
|
|
16
18
|
from .doctor import render_doctor
|
|
17
19
|
|
|
18
20
|
|
|
21
|
+
DEFAULT_SELF_UPDATE_PY_PACKAGE = "logics-manager"
|
|
22
|
+
DEFAULT_SELF_UPDATE_PACKAGE = "@grifhinz/logics-manager"
|
|
23
|
+
|
|
24
|
+
|
|
19
25
|
def get_cli_version() -> str:
|
|
20
26
|
version_file = Path(__file__).resolve().parents[1] / "VERSION"
|
|
21
27
|
try:
|
|
@@ -52,7 +58,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
52
58
|
parser.add_argument(
|
|
53
59
|
"command",
|
|
54
60
|
nargs="?",
|
|
55
|
-
choices=("bootstrap", "flow", "sync", "assist", "audit", "index", "lint", "config", "doctor"),
|
|
61
|
+
choices=("bootstrap", "flow", "sync", "assist", "audit", "index", "lint", "config", "doctor", "self-update"),
|
|
56
62
|
)
|
|
57
63
|
parser.add_argument("rest", nargs=argparse.REMAINDER)
|
|
58
64
|
args = parser.parse_args(argv[:1])
|
|
@@ -100,6 +106,41 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
100
106
|
payload = bootstrap_payload(repo_root, check=parsed.check)
|
|
101
107
|
print(render_bootstrap(payload, output_format=parsed.format))
|
|
102
108
|
return 0 if payload["ok"] else 1
|
|
109
|
+
if args.command == "self-update":
|
|
110
|
+
parser = argparse.ArgumentParser(prog="logics-manager self-update", add_help=False)
|
|
111
|
+
parser.add_argument("--manager", choices=("auto", "pip", "npm"), default="auto")
|
|
112
|
+
parser.add_argument("--package", default=DEFAULT_SELF_UPDATE_PACKAGE)
|
|
113
|
+
parser.add_argument("--python-package", default=DEFAULT_SELF_UPDATE_PY_PACKAGE)
|
|
114
|
+
parser.add_argument("--dry-run", action="store_true")
|
|
115
|
+
parsed, _unknown = parser.parse_known_args(rest)
|
|
116
|
+
|
|
117
|
+
manager = parsed.manager
|
|
118
|
+
if manager == "auto":
|
|
119
|
+
try:
|
|
120
|
+
metadata.version(parsed.python_package)
|
|
121
|
+
except metadata.PackageNotFoundError:
|
|
122
|
+
manager = "npm" if which("npm") else "pip"
|
|
123
|
+
else:
|
|
124
|
+
manager = "pip"
|
|
125
|
+
|
|
126
|
+
if manager == "pip":
|
|
127
|
+
command = [sys.executable, "-m", "pip", "install", "--upgrade", parsed.python_package]
|
|
128
|
+
else:
|
|
129
|
+
npm = which("npm")
|
|
130
|
+
if not npm:
|
|
131
|
+
print("npm was not found on PATH. Install Node.js/npm or update the package manually.")
|
|
132
|
+
return 1
|
|
133
|
+
command = [npm, "install", "-g", f"{parsed.package}@latest"]
|
|
134
|
+
|
|
135
|
+
if parsed.dry_run:
|
|
136
|
+
print("Dry run: " + " ".join(command))
|
|
137
|
+
return 0
|
|
138
|
+
|
|
139
|
+
result = subprocess.run(command, check=False)
|
|
140
|
+
if result.returncode == 0:
|
|
141
|
+
target = parsed.python_package if manager == "pip" else parsed.package
|
|
142
|
+
print(f"Updated {target} via {manager}.")
|
|
143
|
+
return result.returncode
|
|
103
144
|
if args.command == "flow" and rest[:1] in (["new"], ["companion"], ["promote"], ["split"], ["close"], ["finish"]):
|
|
104
145
|
from .flow import main as flow_main
|
|
105
146
|
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@grifhinz/logics-manager",
|
|
3
3
|
"displayName": "Logics Orchestrator",
|
|
4
4
|
"description": "Visual orchestration for Logics workflows inside VS Code.",
|
|
5
|
-
"version": "2.0.
|
|
5
|
+
"version": "2.0.4",
|
|
6
6
|
"publisher": "cdx-logics",
|
|
7
7
|
"icon": "media/icon.png",
|
|
8
8
|
"repository": {
|
package/pyproject.toml
CHANGED
|
File without changes
|