# pyproject.toml — packaging metadata for SmartCLI. # # --------------------------------------------------------------------------- # INSTALL TARGET (read before `pip install .`): # # The distribution installs the typed shared core (`smartcli_core`) and the # drive/MCP command package (`smartcli_drive`). It exposes `smartcli-tui`, # `smartcli-mcp`, and the registry-compatible `smartcli-toolkit` executable. # The visual `cmd-art` and `tui-ui` skills still run in place from a checkout; # they are intentionally not repackaged under an artificial namespace. # # Dependency floors are `>=` known-good baselines. Installed & VERIFIED on the # dev box (Windows 11, CPython 3.14.6): pyte 0.8.2, pywinpty 3.0.5, # pyfiglet 1.0.4, Pillow 12.2.0, wcwidth 0.8.1. # --------------------------------------------------------------------------- [build-system] requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta" [project] name = "smartcli-toolkit" version = "0.3.2" description = "Cross-platform PTY, semantic terminal screen model, CLI and MCP server for driving interactive TUIs." readme = "README.md" license = "MIT" license-files = ["LICENSE"] # The packaged MCP/drive surface uses PEP 604 unions, so 3.10 is the real floor. requires-python = ">=3.10" authors = [{name = "dwgx"}] # Keywords feed PyPI's own search and the third-party indexes that mirror it # (Libraries.io, AI/LLM package indexes). Ordered roughly by how someone would # actually search for this: the MCP/agent terms were missing entirely, so the # package did not surface for "mcp server" or "terminal automation" at all. keywords = [ "mcp", "mcp-server", "ai-agents", "agent-tools", "terminal-automation", "pexpect", "expect", "terminal", "tui", "pty", "ptyprocess", "screen-scraping", "ansi", "cli", "automation", "ascii-art", "terminal-effects", "claude-skill", "pyte", "conpty", ] # Real PyPI trove classifiers. NOTE: no `License ::` classifier is listed on # purpose — with the PEP 639 SPDX `license = "MIT"` field above, setuptools # (77+) treats a license trove classifier as redundant/conflicting and errors # out. The SPDX field is the single source of truth for licensing. classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Topic :: Terminals", "Topic :: Terminals :: Terminal Emulators/X Terminals", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Testing", "Topic :: System :: Shells", "Environment :: Console", "Intended Audience :: System Administrators", # The wheel ships smartcli_core/py.typed (PEP 561); declare it so type # checkers and PyPI's faceted browse both know. "Typing :: Typed", ] # REQUIRED runtime deps (mirror requirements.txt). pyte is a hard top-level # import, mcp powers the installed stdio server, and pywinpty is imported only # on Windows. POSIX uses the stdlib `pty` backend. dependencies = [ "pyte>=0.8.1", # Pinned below 2.0: the MCP Python SDK 2.0 removed `mcp.server.fastmcp` # (and the rest of the v1 FastMCP surface this server imports), so an # unbounded `mcp>=1.0` resolves to 2.x and the server exits with # "the 'mcp' package is required" even though mcp is installed. # Upstream keeps v1.x maintained on its own branch; revisit when the # server is ported to the 2.x API. "mcp>=1.0,<2", "pywinpty>=2.0; platform_system == 'Windows'", ] [project.scripts] # Keep an executable matching the distribution name so MCP Registry clients can # launch the PyPI package with `uvx smartcli-toolkit`. The explicit aliases are # friendlier for humans and scripts. smartcli-toolkit = "smartcli_drive.mcp_server:main" smartcli-mcp = "smartcli_drive.mcp_server:main" smartcli-tui = "smartcli_drive.tui:main" [project.urls] Homepage = "https://github.com/dwgx/SmartCLI" Repository = "https://github.com/dwgx/SmartCLI" Changelog = "https://github.com/dwgx/SmartCLI/blob/main/CHANGELOG.md" Issues = "https://github.com/dwgx/SmartCLI/issues" PyPI = "https://pypi.org/project/smartcli-toolkit/" Documentation = "https://smartcli.readthedocs.io/" Demo = "https://dwgx.github.io/SmartCLI/" [project.optional-dependencies] # All guarded imports with pure-stdlib fallbacks — none required to run. art = ["pyfiglet>=1.0.0"] # real FIGlet fonts (else built-in block font) image = ["Pillow>=10.0.0"] # real raster images (else PNM/procedural demo) width = ["wcwidth>=0.2.0"] # authoritative cell width (else unicodedata) all = ["pyfiglet>=1.0.0", "Pillow>=10.0.0", "wcwidth>=0.2.0"] # Dev-only: multi-process coverage over the script-style suite (see # tools/coverage_run.py + .coveragerc). Not needed to run SmartCLI. dev = ["coverage[toml]>=7.0"] [tool.setuptools] # `smartcli_drive` maps to the existing skill scripts so one implementation # serves source checkouts, standalone skill installs, console scripts and MCP. packages = ["smartcli_core", "smartcli_drive"] [tool.setuptools.package-dir] smartcli_drive = "skills/drive-tui/scripts" [tool.setuptools.package-data] # PEP 561: ship the py.typed marker inside the wheel so downstream type # checkers (mypy/pyright) treat smartcli_core's public API as typed. smartcli_core = ["py.typed"] [tool.ruff] # Lint the shared core strictly; the skills run in place and lean on runtime # discovery, so they're linted more leniently (see per-file ignores). In CI the # correctness subset (ruff E9,F63,F7,F82) and mypy block; the full ruff check # and format check stay advisory. line-length = 100 target-version = "py310" # verification/ holds the evidence tree (receipts, probes, historical harness revisions) # and is excluded from the product gates on purpose: one of those files is a preserved # earlier revision of the mutation harness whose known SyntaxError is part of the record. extend-exclude = ["research", "_refs", "docs/site/assets", "**/_vendor/**", "verification"] [tool.ruff.lint] select = ["E", "F", "W", "I", "UP", "B"] ignore = [ "E501", # line length is handled by the formatter; don't fail on long lines "B008", # function-call default args appear in some effect signatures by design ] [tool.ruff.lint.per-file-ignores] "tests/**" = ["E402", "F401"] # tests do sys.path juggling before imports "skills/**" = ["E402"] # skills insert paths before importing core [tool.mypy] python_version = "3.10" # Only the shared core ships as a typed package; check it, ignore the rest. files = ["smartcli_core"] ignore_missing_imports = true warn_unused_ignores = true