devready-cli 0.2.0__py3-none-any.whl → 1.0.0__py3-none-any.whl

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.
devready/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.2.0"
1
+ __version__ = "1.0.0"
@@ -0,0 +1,108 @@
1
+ """OS-specific pain points, each checked only when the project makes it relevant.
2
+
3
+ Windows: long paths, PowerShell execution policy (blocks npm.ps1), git line endings.
4
+ macOS/Linux: a compiler toolchain when the project looks like it builds native code.
5
+ Read-only; fixed argv only.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import shutil
11
+ import sys
12
+ from collections.abc import Callable
13
+ from pathlib import Path
14
+
15
+ from devready.checks.extras import _run
16
+ from devready.models import Result, Status
17
+
18
+ NATIVE_HINTS = ("binding.gyp", "setup.py", "Gemfile", "Cargo.toml")
19
+ BLOCKING_POLICIES = {"Restricted", "AllSigned"}
20
+ KNOWN_POLICIES = BLOCKING_POLICIES | {"RemoteSigned", "Unrestricted", "Bypass", "Undefined"}
21
+
22
+
23
+ def _long_paths_enabled() -> bool | None:
24
+ try:
25
+ import winreg
26
+
27
+ with winreg.OpenKey(
28
+ winreg.HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\FileSystem"
29
+ ) as key:
30
+ return bool(winreg.QueryValueEx(key, "LongPathsEnabled")[0])
31
+ except (ImportError, OSError):
32
+ return None
33
+
34
+
35
+ def _windows(root: Path, run: Callable, which: Callable, long_paths: Callable) -> list[Result]:
36
+ out: list[Result] = []
37
+ if (root / "package.json").is_file():
38
+ if long_paths() is False:
39
+ out.append(
40
+ Result(
41
+ "platform.long_paths",
42
+ "Long paths",
43
+ Status.WARN,
44
+ "Windows long path support is off; deep node_modules trees can fail",
45
+ "Enable LongPathsEnabled (administrator) and git's core.longpaths.",
46
+ )
47
+ )
48
+ ps = which("powershell")
49
+ p = (
50
+ run([ps, "-NoProfile", "-NonInteractive", "-Command", "Get-ExecutionPolicy"])
51
+ if ps
52
+ else None
53
+ )
54
+ policy = p.stdout.strip() if p is not None and p.returncode == 0 else ""
55
+ if policy in BLOCKING_POLICIES:
56
+ out.append(
57
+ Result(
58
+ "platform.exec_policy",
59
+ "PowerShell policy",
60
+ Status.WARN,
61
+ f"PowerShell policy is {policy}; npm/pnpm .ps1 scripts will be blocked",
62
+ "Use cmd, or allow local scripts for your user: "
63
+ "Set-ExecutionPolicy -Scope CurrentUser RemoteSigned",
64
+ )
65
+ )
66
+ git = which("git")
67
+ if git and (root / ".git").exists() and not (root / ".gitattributes").is_file():
68
+ p = run([git, "-C", str(root), "config", "--get", "core.autocrlf"])
69
+ if p is not None and not p.stdout.strip():
70
+ out.append(
71
+ Result(
72
+ "platform.autocrlf",
73
+ "Line endings",
74
+ Status.WARN,
75
+ "core.autocrlf is unset and the repo has no .gitattributes",
76
+ "Add a .gitattributes with line-ending rules, or set core.autocrlf.",
77
+ )
78
+ )
79
+ return out
80
+
81
+
82
+ def _build_tools(root: Path, plat: str, run: Callable, which: Callable) -> list[Result]:
83
+ if not any((root / n).is_file() for n in NATIVE_HINTS):
84
+ return []
85
+ cid, title = "platform.build_tools", "Build tools"
86
+ if plat == "darwin":
87
+ xs = which("xcode-select")
88
+ p = run([xs, "-p"]) if xs else None
89
+ ok = p is not None and p.returncode == 0
90
+ fix = "Install the Xcode Command Line Tools: xcode-select --install"
91
+ else:
92
+ ok = any(which(c) for c in ("cc", "gcc", "clang")) and bool(which("make"))
93
+ fix = "Install a C compiler and make (for example your distro's build-essential)."
94
+ if ok:
95
+ return [Result(cid, title, Status.PASS, "compiler toolchain found")]
96
+ return [Result(cid, title, Status.WARN, "no compiler toolchain found for native builds", fix)]
97
+
98
+
99
+ def check_platform(
100
+ root: Path,
101
+ plat: str = sys.platform,
102
+ run: Callable = _run,
103
+ which: Callable = shutil.which,
104
+ long_paths: Callable = _long_paths_enabled,
105
+ ) -> list[Result]:
106
+ if plat == "win32":
107
+ return _windows(root, run, which, long_paths)
108
+ return _build_tools(root, plat, run, which)
@@ -11,7 +11,7 @@ import re
11
11
  from pathlib import Path
12
12
 
13
13
  from devready.models import Result, Status
14
- from devready.versions import SAFE_SPEC, Version, parse_version, satisfies
14
+ from devready.versions import Version, parse_version, satisfies
15
15
 
16
16
  MAX_BYTES = 1_000_000
17
17
 
@@ -25,7 +25,8 @@ def _read(path: Path) -> str | None:
25
25
  def _compare(
26
26
  check_id: str, title: str, source: str, spec: str, installed: Version | None, miss: Status
27
27
  ) -> Result:
28
- shown = spec if SAFE_SPEC.match(spec) else "(unrecognized)"
28
+ # Echo only specs the matcher fully parses: plain words can pass SAFE_SPEC but are not versions.
29
+ shown = spec if satisfies((0, 0, 0), spec) is not None else "(unrecognized)"
29
30
  if installed is None:
30
31
  return Result(check_id, title, Status.SKIP, f"{source} wants {shown}; tool not found")
31
32
  ok = satisfies(installed, spec)
devready/cli.py CHANGED
@@ -16,7 +16,10 @@ EXIT_OK, EXIT_FAILED, EXIT_USAGE, EXIT_INTERNAL = 0, 1, 2, 3
16
16
 
17
17
  def _text(results: list[Result]) -> str:
18
18
  lines = ["DevReady - Environment Check", ""]
19
- lines += [f"[{r.status.value.upper():<5}] {r.title:<12} {r.summary}" for r in results]
19
+ for r in results:
20
+ lines.append(f"[{r.status.value.upper():<5}] {r.title:<12} {r.summary}")
21
+ if r.status is not Status.PASS and r.suggestion:
22
+ lines.append(f" {r.suggestion}")
20
23
  lines += ["", "No changes were made to your system."]
21
24
  return "\n".join(lines)
22
25
 
devready/explain.py CHANGED
@@ -47,4 +47,8 @@ EXPLANATIONS = {
47
47
  "project.node.config": "require.node in .devready.json is a hard requirement (fails when unmet).",
48
48
  "project.python.config": "require.python in .devready.json is a hard requirement (fails when unmet).",
49
49
  "project.env.required": "Variable NAMES listed under env in .devready.json must be set in the environment. Values are never read.",
50
+ "platform.long_paths": "Windows only, when package.json exists. Without LongPathsEnabled, deeply nested node_modules paths can exceed 260 characters and installs fail.",
51
+ "platform.exec_policy": "Windows only, when package.json exists. A Restricted or AllSigned PowerShell policy blocks npm.ps1 ('running scripts is disabled'). Use cmd or relax the policy for your user.",
52
+ "platform.autocrlf": "Windows only, in a git repo without .gitattributes. Unset core.autocrlf can flip line endings between Windows and Linux/macOS contributors.",
53
+ "platform.build_tools": "macOS/Linux, when the project has binding.gyp, setup.py, Gemfile or Cargo.toml. Native dependencies need a compiler (Xcode CLT on macOS, cc/gcc/clang and make on Linux).",
50
54
  }
devready/runner.py CHANGED
@@ -6,6 +6,7 @@ from pathlib import Path
6
6
  from devready.checks.config import check_config, load_config
7
7
  from devready.checks.extras import check_disk, check_docker, check_git_identity
8
8
  from devready.checks.health import check_health
9
+ from devready.checks.platform_checks import check_platform
9
10
  from devready.checks.project import check_project
10
11
  from devready.checks.python_check import check_python
11
12
  from devready.checks.runtimes import check_runtimes
@@ -27,7 +28,7 @@ def run_checks(root: Path) -> list[Result]:
27
28
  results += check_git_identity()
28
29
  results += check_project(root, versions["node"], platform.python_version())
29
30
  results += check_runtimes(root) + check_health(root)
30
- results += check_docker(root) + check_disk(root)
31
+ results += check_docker(root) + check_platform(root) + check_disk(root)
31
32
  results += (
32
33
  [cfg_error] if cfg_error else check_config(cfg, versions["node"], platform.python_version())
33
34
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: devready-cli
3
- Version: 0.2.0
3
+ Version: 1.0.0
4
4
  Summary: A local-first, read-only CLI that checks a project's environment requirements against what is installed.
5
5
  Project-URL: Homepage, https://devready-ten.vercel.app/
6
6
  Project-URL: Repository, https://github.com/Evalogical-Inc/devReady
@@ -9,7 +9,7 @@ Author: Evalogical Inc
9
9
  License-Expression: MIT
10
10
  License-File: LICENSE
11
11
  Keywords: ai-agents,claude-code,cli,codex,developer-tools,doctor,environment,mcp,nvmrc,onboarding,preflight
12
- Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Development Status :: 5 - Production/Stable
13
13
  Classifier: Environment :: Console
14
14
  Classifier: Intended Audience :: Developers
15
15
  Classifier: Operating System :: OS Independent
@@ -37,7 +37,7 @@ Built for humans, CI, and AI coding agents (Claude Code, Codex, any MCP client).
37
37
  [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
38
38
  ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)
39
39
  ![Dependencies: 0](https://img.shields.io/badge/dependencies-0-brightgreen)
40
- ![Status: alpha](https://img.shields.io/badge/status-alpha-orange)
40
+ ![Status: stable](https://img.shields.io/badge/status-stable-green)
41
41
 
42
42
  </div>
43
43
 
@@ -193,9 +193,9 @@ args = ["--from", "devready-cli", "devready", "mcp"]
193
193
  - Environment variable values are never read into output. Nothing leaves your machine.
194
194
  - A passing report does **not** guarantee your project builds.
195
195
 
196
- See [SECURITY.md](SECURITY.md) to report a vulnerability.
196
+ See [docs/security.md](docs/security.md) for the model, [docs/versioning.md](docs/versioning.md) for what stays stable, [docs/checks.md](docs/checks.md) for every check, and [SECURITY.md](SECURITY.md) to report a vulnerability.
197
197
 
198
- ## Limitations (alpha)
198
+ ## Limitations
199
199
 
200
200
  - Checks today: Python, Git (+ identity set), Node.js, npm, project pins (`.nvmrc`, `.node-version`, `.python-version`, `.tool-versions`, `package.json` engines, `pyproject.toml` requires-python), `.env.example` vs `.env` key names, Docker CLI + daemon (only when the project has a Dockerfile/compose file), free disk space, and (only when the project declares them) Go, Ruby, Java, Rust, .NET, pnpm, Yarn and Bun pins, `.env` not git-ignored, `node_modules` vs lockfile, and declared ports already in use.
201
201
  - The version-range matcher covers common npm/PEP 440 syntax; pre-releases and exotic ranges report `skip` rather than guess.
@@ -1,23 +1,24 @@
1
- devready/__init__.py,sha256=Zn1KFblwuFHiDRdRAiRnDBRkbPttWh44jKa5zG2ov0E,22
1
+ devready/__init__.py,sha256=J-j-u0itpEFT6irdmWmixQqYMadNl1X91TxUmoiLHMI,22
2
2
  devready/__main__.py,sha256=c-4qpm2SKZs1nENGzSnPnChNTo8iMwjY0XaPyAApn4I,60
3
3
  devready/baseline.py,sha256=pfVzZ695bLEiSWKUBK0PNDp1S2UWTWj9lfUCDAsVS4g,1524
4
- devready/cli.py,sha256=RPijit2MT_ouZgD2TOUu1x-6YJevRIkh_a1wTWbMeuY,4619
5
- devready/explain.py,sha256=nYzzwtZGpn-FQQR8_KxC61_FLf7jCuQEv4iX-Fvqqr0,5140
4
+ devready/cli.py,sha256=oetC_jJlXk4SlyB_oKqi5XYwDouFBjeT5Ya9vUmSfXc,4736
5
+ devready/explain.py,sha256=zVRUNzqtVdO-EcC6rUCLPuwswbMlo59xzncCBiSvi1M,5889
6
6
  devready/formats.py,sha256=akN62ouKLeZQB6POcXNzINYKHwpUd4im0n2-9rtB8eE,3129
7
7
  devready/mcp_server.py,sha256=zVm89kZL_-pZwTSWkt6rxKonytNklUSa0RCQkX0lzFk,3177
8
8
  devready/models.py,sha256=KyGp3ie2LVQmuSfzLV8tbEH_hJyU5-Vi11d0JcQPAuE,646
9
- devready/runner.py,sha256=MVQh-dlsXnihWSbvbF4XKuFxgIyoywtL-9I-y0XrPpc,1456
9
+ devready/runner.py,sha256=yjd7cdH0G-ksUD9viJ6coEB0iug7CazqWBQPlIW2lLU,1538
10
10
  devready/versions.py,sha256=dXKIl1-qvcfadRNaCURMoftwx7YtadDZ-YfUp-85XZc,2112
11
11
  devready/checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  devready/checks/config.py,sha256=SGyIot_QXmZtPLIybtxnyJEFL2aV1NemSqX_fXbFI6A,3471
13
13
  devready/checks/extras.py,sha256=pDJbFaAwjkLc92tcFagOmj-5c_vMx32_MtYa_DBfCz8,3428
14
14
  devready/checks/health.py,sha256=woaaUL2TJ_biRuiStXqJtnvPua_y69bW2quNPlwd7tQ,4235
15
- devready/checks/project.py,sha256=UnnjuZmG2aXXBAY45wTEP-bj4CWODMv3DPfEi1V_QDE,5983
15
+ devready/checks/platform_checks.py,sha256=_ayTrJalFf4k5As0jq-t8GDh4IOMmgZJJdhKU4rIujQ,4113
16
+ devready/checks/project.py,sha256=JTeSmkuz4Z7Jy5aQnP4o4TcdhQYRZfGaGhj6f0NslXw,6090
16
17
  devready/checks/python_check.py,sha256=uQCd-N0LEwt9R5gwZm5mlYiPMST1JXVDNQOdZ5xpako,328
17
18
  devready/checks/runtimes.py,sha256=OiC5LEhmfkiMseP2PbQS6Dc6TC2JhYNICU6LmdFFvqk,4446
18
19
  devready/checks/tools.py,sha256=AiL9TdIREmY7tEkkpt62DELKuJ2lhu3XcA2yGak9QkA,1616
19
- devready_cli-0.2.0.dist-info/METADATA,sha256=b76wbK0qZnoG8aNy66wxg8QmFDPENvL3GhzAwa2-RMQ,10533
20
- devready_cli-0.2.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
21
- devready_cli-0.2.0.dist-info/entry_points.txt,sha256=7oAecSWtsIt7qLOP8PipOeDqhXCkEJat0oxhD4T1RpY,47
22
- devready_cli-0.2.0.dist-info/licenses/LICENSE,sha256=S_AQsEw289FIsOgT-OPWL5JZeV8nn5kicwEq0r9qu7s,1071
23
- devready_cli-0.2.0.dist-info/RECORD,,
20
+ devready_cli-1.0.0.dist-info/METADATA,sha256=Bl4J-zxCaKppIWKFH-iFOxw0WNCOxXMYM_LgYuUhJVk,10708
21
+ devready_cli-1.0.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
22
+ devready_cli-1.0.0.dist-info/entry_points.txt,sha256=7oAecSWtsIt7qLOP8PipOeDqhXCkEJat0oxhD4T1RpY,47
23
+ devready_cli-1.0.0.dist-info/licenses/LICENSE,sha256=S_AQsEw289FIsOgT-OPWL5JZeV8nn5kicwEq0r9qu7s,1071
24
+ devready_cli-1.0.0.dist-info/RECORD,,