logics-manager 2.3.0__tar.gz → 2.3.2__tar.gz

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.
Files changed (32) hide show
  1. {logics_manager-2.3.0 → logics_manager-2.3.2}/PKG-INFO +1 -1
  2. {logics_manager-2.3.0 → logics_manager-2.3.2}/README.md +42 -1
  3. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/cli.py +128 -6
  4. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager.egg-info/PKG-INFO +1 -1
  5. {logics_manager-2.3.0 → logics_manager-2.3.2}/pyproject.toml +1 -1
  6. {logics_manager-2.3.0 → logics_manager-2.3.2}/LICENSE +0 -0
  7. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/__init__.py +0 -0
  8. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/__main__.py +0 -0
  9. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/assist.py +0 -0
  10. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/assist_handoff.py +0 -0
  11. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/assist_surface.py +0 -0
  12. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/audit.py +0 -0
  13. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/bootstrap.py +0 -0
  14. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/cli_output.py +0 -0
  15. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/config.py +0 -0
  16. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/doctor.py +0 -0
  17. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/flow.py +0 -0
  18. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/flow_evidence.py +0 -0
  19. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/index.py +0 -0
  20. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/insights.py +0 -0
  21. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/lint.py +0 -0
  22. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/mcp.py +0 -0
  23. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/path_utils.py +0 -0
  24. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/sync.py +0 -0
  25. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/termstyle.py +0 -0
  26. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/update_check.py +0 -0
  27. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager/viewer.py +0 -0
  28. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager.egg-info/SOURCES.txt +0 -0
  29. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager.egg-info/dependency_links.txt +0 -0
  30. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager.egg-info/entry_points.txt +0 -0
  31. {logics_manager-2.3.0 → logics_manager-2.3.2}/logics_manager.egg-info/top_level.txt +0 -0
  32. {logics_manager-2.3.0 → logics_manager-2.3.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: logics-manager
3
- Version: 2.3.0
3
+ Version: 2.3.2
4
4
  Summary: Canonical Logics CLI
5
5
  Requires-Python: >=3.10
6
6
  License-File: LICENSE
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![CI](https://github.com/AlexAgo83/logics-manager/actions/workflows/ci.yml/badge.svg)](https://github.com/AlexAgo83/logics-manager/actions/workflows/ci.yml)
4
4
  [![License](https://img.shields.io/github/license/AlexAgo83/logics-manager)](LICENSE)
5
- ![Version](https://img.shields.io/badge/version-v2.3.0-4C8BF5)
5
+ ![Version](https://img.shields.io/badge/version-v2.3.2-4C8BF5)
6
6
  ![VS Code](https://img.shields.io/badge/VS%20Code-1.86.0-007ACC?logo=visualstudiocode&logoColor=white)
7
7
  ![TypeScript](https://img.shields.io/badge/TypeScript-5.3.3-3178C6?logo=typescript&logoColor=white)
8
8
  ![Vitest](https://img.shields.io/badge/Vitest-2.1.8-6E9F18?logo=vitest&logoColor=white)
@@ -58,6 +58,15 @@ python3.11 -m pip install .
58
58
  logics-manager --help
59
59
  ```
60
60
 
61
+ On Debian, Ubuntu, or WSL environments where Python is externally managed, use `pipx` instead of installing into the system Python:
62
+
63
+ ```bash
64
+ sudo apt update
65
+ sudo apt install pipx python3-venv
66
+ pipx ensurepath
67
+ pipx install logics-manager
68
+ ```
69
+
61
70
  Or install the npm package:
62
71
 
63
72
  ```bash
@@ -197,6 +206,38 @@ To update the installed CLI later:
197
206
  logics-manager self-update
198
207
  ```
199
208
 
209
+ If `self-update` reports an externally managed Python environment, migrate the Python install through `pipx`:
210
+
211
+ ```bash
212
+ sudo apt update
213
+ sudo apt install pipx python3-venv
214
+ pipx ensurepath
215
+ pipx install --force logics-manager
216
+ ```
217
+
218
+ If `logics-manager` is already installed through `pipx`, update that managed environment directly:
219
+
220
+ ```bash
221
+ pipx upgrade logics-manager
222
+ ```
223
+
224
+ For npm installs, update with:
225
+
226
+ ```bash
227
+ npm install -g @grifhinz/logics-manager@latest
228
+ ```
229
+
230
+ If npm reports a successful update but `logics-manager --version` still shows an older version, another installation is earlier on `PATH`. Diagnose it with:
231
+
232
+ ```bash
233
+ command -v -a logics-manager
234
+ npm prefix -g
235
+ npm list -g @grifhinz/logics-manager --depth=0
236
+ "$(npm prefix -g)/bin/logics-manager" --version
237
+ ```
238
+
239
+ If the direct npm binary shows the expected version, remove the older Python install or move the npm global `bin` directory earlier on `PATH`.
240
+
200
241
  ## VS Code Extension
201
242
 
202
243
  The VS Code extension is the human cockpit around the same runtime. It helps you:
@@ -1,9 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import argparse
4
+ import json
5
+ import os
4
6
  from importlib import metadata
5
7
  import subprocess
6
8
  import sys
9
+ import sysconfig
7
10
  from shutil import which
8
11
  from pathlib import Path
9
12
 
@@ -133,6 +136,104 @@ def get_cli_version() -> str:
133
136
  return "0.0.0"
134
137
 
135
138
 
139
+ def _is_running_inside_venv() -> bool:
140
+ return sys.prefix != getattr(sys, "base_prefix", sys.prefix)
141
+
142
+
143
+ def _is_externally_managed_python() -> bool:
144
+ if _is_running_inside_venv():
145
+ return False
146
+ stdlib = sysconfig.get_path("stdlib")
147
+ return bool(stdlib and (Path(stdlib) / "EXTERNALLY-MANAGED").exists())
148
+
149
+
150
+ def _is_running_from_pipx(package_name: str = DEFAULT_SELF_UPDATE_PY_PACKAGE) -> bool:
151
+ expected = package_name.replace("_", "-").lower()
152
+ candidates = [Path(sys.prefix), Path(sys.executable)]
153
+ for candidate in candidates:
154
+ parts = [part.lower() for part in candidate.parts]
155
+ for index, part in enumerate(parts[:-2]):
156
+ if part == "pipx" and parts[index + 1] == "venvs" and parts[index + 2] == expected:
157
+ return True
158
+ return False
159
+
160
+
161
+ def _is_running_from_npm_package() -> bool:
162
+ package_json = Path(__file__).resolve().parents[1] / "package.json"
163
+ try:
164
+ payload = json.loads(package_json.read_text(encoding="utf-8"))
165
+ except (OSError, json.JSONDecodeError):
166
+ return False
167
+ return payload.get("name") == DEFAULT_SELF_UPDATE_PACKAGE
168
+
169
+
170
+ def _find_executable_paths(command: str) -> list[str]:
171
+ paths: list[str] = []
172
+ seen: set[str] = set()
173
+ names = [command]
174
+ if sys.platform == "win32":
175
+ extensions = [suffix.lower() for suffix in os.environ.get("PATHEXT", ".COM;.EXE;.BAT;.CMD").split(";") if suffix]
176
+ if Path(command).suffix.lower() not in extensions:
177
+ names = [command + extension for extension in extensions]
178
+ for directory in os.environ.get("PATH", "").split(os.pathsep):
179
+ if not directory:
180
+ continue
181
+ for name in names:
182
+ candidate = Path(directory) / name
183
+ try:
184
+ resolved = str(candidate.resolve())
185
+ except OSError:
186
+ resolved = str(candidate)
187
+ if resolved in seen:
188
+ continue
189
+ if candidate.exists() and os.access(candidate, os.X_OK):
190
+ seen.add(resolved)
191
+ paths.append(str(candidate))
192
+ return paths
193
+
194
+
195
+ def _print_path_conflict_guidance(paths: list[str]) -> None:
196
+ if len(paths) <= 1:
197
+ return
198
+ print(
199
+ "\n".join(
200
+ [
201
+ "",
202
+ "Multiple logics-manager executables are on PATH. If --version still shows an older release, an earlier install is taking precedence.",
203
+ "Diagnose with:",
204
+ " command -v -a logics-manager",
205
+ " pipx list",
206
+ " npm list -g @grifhinz/logics-manager --depth=0",
207
+ ]
208
+ )
209
+ )
210
+
211
+
212
+ def _print_externally_managed_update_guidance(package_name: str) -> None:
213
+ print(
214
+ "\n".join(
215
+ [
216
+ "This Python installation is externally managed, so pip cannot safely update logics-manager in the system environment.",
217
+ "",
218
+ "If this command was installed with pipx, update it with:",
219
+ f" pipx upgrade {package_name}",
220
+ "",
221
+ "Otherwise migrate the Python install through pipx:",
222
+ " sudo apt update",
223
+ " sudo apt install pipx python3-venv",
224
+ " pipx ensurepath",
225
+ f" pipx install --force {package_name}",
226
+ "",
227
+ "If you installed the npm package instead, run:",
228
+ f" npm install -g {DEFAULT_SELF_UPDATE_PACKAGE}@latest",
229
+ "",
230
+ "Advanced override, at your own risk:",
231
+ f" logics-manager self-update --manager pip --break-system-packages",
232
+ ]
233
+ )
234
+ )
235
+
236
+
136
237
  def _is_json_mode(argv: list[str]) -> bool:
137
238
  return "--json" in argv or any(argv[index] == "--format" and index + 1 < len(argv) and argv[index + 1] == "json" for index in range(len(argv)))
138
239
 
@@ -205,23 +306,40 @@ def main(argv: list[str] | None = None) -> int:
205
306
  return 0 if payload["ok"] else 1
206
307
  if command == "self-update":
207
308
  parser = argparse.ArgumentParser(prog="logics-manager self-update", add_help=False)
208
- parser.add_argument("--manager", choices=("auto", "pip", "npm"), default="auto")
309
+ parser.add_argument("--manager", choices=("auto", "pip", "pipx", "npm"), default="auto")
209
310
  parser.add_argument("--package", default=DEFAULT_SELF_UPDATE_PACKAGE)
210
311
  parser.add_argument("--python-package", default=DEFAULT_SELF_UPDATE_PY_PACKAGE)
312
+ parser.add_argument("--break-system-packages", action="store_true")
211
313
  parser.add_argument("--dry-run", action="store_true")
212
314
  parsed = parser.parse_args(rest)
213
315
 
214
316
  manager = parsed.manager
215
317
  if manager == "auto":
216
- try:
217
- metadata.version(parsed.python_package)
218
- except metadata.PackageNotFoundError:
219
- manager = "npm" if which("npm") else "pip"
318
+ if _is_running_from_npm_package() and which("npm"):
319
+ manager = "npm"
320
+ elif _is_running_from_pipx(parsed.python_package) and which("pipx"):
321
+ manager = "pipx"
220
322
  else:
221
- manager = "pip"
323
+ try:
324
+ metadata.version(parsed.python_package)
325
+ except metadata.PackageNotFoundError:
326
+ manager = "npm" if which("npm") else "pip"
327
+ else:
328
+ manager = "pip"
222
329
 
223
330
  if manager == "pip":
331
+ if _is_externally_managed_python() and not parsed.break_system_packages:
332
+ _print_externally_managed_update_guidance(parsed.python_package)
333
+ return 1
224
334
  command = [sys.executable, "-m", "pip", "install", "--upgrade", parsed.python_package]
335
+ if parsed.break_system_packages:
336
+ command.append("--break-system-packages")
337
+ elif manager == "pipx":
338
+ pipx = which("pipx")
339
+ if not pipx:
340
+ print("pipx was not found on PATH. Install pipx or update with --manager pip/npm.")
341
+ return 1
342
+ command = [pipx, "upgrade", parsed.python_package]
225
343
  else:
226
344
  npm = which("npm")
227
345
  if not npm:
@@ -236,7 +354,11 @@ def main(argv: list[str] | None = None) -> int:
236
354
  result = subprocess.run(command, check=False)
237
355
  if result.returncode == 0:
238
356
  target = parsed.python_package if manager == "pip" else parsed.package
357
+ if manager == "pipx":
358
+ target = parsed.python_package
239
359
  print(f"Updated {target} via {manager}.")
360
+ if manager == "npm":
361
+ _print_path_conflict_guidance(_find_executable_paths("logics-manager"))
240
362
  return result.returncode
241
363
  if command == "flow" and (rest[:1] in (["new"], ["list"], ["companion"], ["deliver"], ["validate-closeout"], ["repair"], ["closeout"], ["promote"], ["split"], ["close"], ["finish"]) or rest[:1] in HELP_ARGV):
242
364
  from .flow import main as flow_main
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: logics-manager
3
- Version: 2.3.0
3
+ Version: 2.3.2
4
4
  Summary: Canonical Logics CLI
5
5
  Requires-Python: >=3.10
6
6
  License-File: LICENSE
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "logics-manager"
7
- version = "2.3.0"
7
+ version = "2.3.2"
8
8
  description = "Canonical Logics CLI"
9
9
  requires-python = ">=3.10"
10
10
 
File without changes
File without changes