CtrlK
BlogDocsLog inGet started
Tessl Logo

need-skills/up-to-date

MANDATORY when ANY of these appear: (1) writing/editing/refactoring code that calls any third-party SDK, API, or library method, (2) debugging ANY issue where an API call succeeds but the expected side-effect doesn't happen, (3) installing or importing external packages, (4) user reports something 'doesn't work' and the code involves an external service. This skill MUST be loaded BEFORE proposing any fix — never diagnose from memory.

92

Quality

92%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Skills
Evals
Files

check_versions.pyscripts/

#!/usr/bin/env python3
"""
Version Check - Compare installed vs latest package versions.

Usage:
    check_versions.py npm <package>
    check_versions.py pip <package>
    check_versions.py npm --all          # Check all in package.json

Outputs: UP_TO_DATE, MINOR_BEHIND, MAJOR_BEHIND, or NOT_INSTALLED.
"""

import subprocess
import sys
import json
import re
from pathlib import Path


def run_cmd(cmd):
    """Run a command and return stdout, or None on failure."""
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
        return result.stdout.strip() if result.returncode == 0 else None
    except (subprocess.TimeoutExpired, FileNotFoundError):
        return None


def parse_semver(version):
    """Parse a semver string into (major, minor, patch) tuple."""
    match = re.match(r'(\d+)\.(\d+)\.(\d+)', version.strip().lstrip('v^~>=<'))
    return tuple(int(x) for x in match.groups()) if match else None


def check_npm(package):
    """Check npm package version."""
    installed = None
    pkg_json = Path("node_modules", package, "package.json")
    if pkg_json.exists():
        try:
            installed = json.loads(pkg_json.read_text()).get("version")
        except (json.JSONDecodeError, IOError):
            pass

    latest = run_cmd(["npm", "info", package, "version"])
    return {"package": package, "manager": "npm", "installed": installed, "latest": latest}


def check_pip(package):
    """Check pip package version."""
    installed = None
    output = run_cmd(["pip", "show", package])
    if output:
        for line in output.split("\n"):
            if line.startswith("Version:"):
                installed = line.split(":", 1)[1].strip()
                break

    latest = None
    output = run_cmd(["pip", "index", "versions", package])
    if output:
        match = re.search(r'\((.+?)\)', output)
        if match:
            latest = match.group(1)

    return {"package": package, "manager": "pip", "installed": installed, "latest": latest}


def check_all_npm():
    """Check all packages in package.json."""
    pkg_path = Path("package.json")
    if not pkg_path.exists():
        print("❌ No package.json found in current directory")
        return []

    data = json.loads(pkg_path.read_text())
    deps = {**data.get("dependencies", {}), **data.get("devDependencies", {})}
    return [check_npm(p) for p in sorted(deps)]


def get_verdict(installed, latest):
    """Determine update verdict."""
    if not installed:
        return "NOT_INSTALLED"
    if not latest:
        return "UNKNOWN"

    inst, lat = parse_semver(installed), parse_semver(latest)
    if not inst or not lat:
        return "UNKNOWN"

    if inst[0] < lat[0]:
        return "MAJOR_BEHIND"
    if inst[1] < lat[1]:
        return "MINOR_BEHIND"
    if inst[2] < lat[2]:
        return "PATCH_BEHIND"
    return "UP_TO_DATE"


ICONS = {
    "UP_TO_DATE": "✅", "PATCH_BEHIND": "✅", "MINOR_BEHIND": "⚠️ ",
    "MAJOR_BEHIND": "🚨", "NOT_INSTALLED": "❌", "UNKNOWN": "❓",
}


def format_result(result):
    """Format a single check result."""
    verdict = get_verdict(result["installed"], result["latest"])
    installed = result["installed"] or "not installed"
    latest = result["latest"] or "unknown"
    line = f"{ICONS.get(verdict, '❓')} {result['package']}: {installed} → {latest} [{verdict}]"
    if verdict == "MAJOR_BEHIND":
        line += "\n   ⚡ MAJOR version behind — check migration guide before updating!"
    return line


CHECKERS = {"npm": check_npm, "pip": check_pip}


def main():
    if len(sys.argv) < 2:
        print(__doc__)
        sys.exit(1)

    manager = sys.argv[1]
    package = sys.argv[2] if len(sys.argv) >= 3 else None

    # npm --all: check everything in package.json
    if manager == "npm" and package == "--all":
        results = check_all_npm()
        if not results:
            sys.exit(1)
        major = []
        for r in results:
            print(format_result(r))
            if get_verdict(r["installed"], r["latest"]) == "MAJOR_BEHIND":
                major.append(r["package"])
        print(f"\n📊 Checked {len(results)} packages")
        if major:
            print(f"🚨 {len(major)} MAJOR behind: {', '.join(major)}")
        sys.exit(0)

    if not package:
        print("Usage: check_versions.py <npm|pip> <package>")
        sys.exit(1)

    checker = CHECKERS.get(manager)
    if not checker:
        print(f"❌ Unknown package manager: {manager}. Supported: npm, pip")
        sys.exit(1)

    result = checker(package)
    print(format_result(result))
    verdict = get_verdict(result["installed"], result["latest"])
    sys.exit(1 if verdict in ("MAJOR_BEHIND", "NOT_INSTALLED") else 0)


if __name__ == "__main__":
    main()

Install with Tessl CLI

npx tessl i need-skills/up-to-date@0.2.0

README.md

SKILL.md

tile.json