Skip to content

Jackknife is a command-line utility that allows you to run various Python tool scripts, each within its own isolated virtual environment managed by uv. ๐Ÿ› ๏ธ

License

Notifications You must be signed in to change notification settings

AlanSynn/jackknife

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

24 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Jackknife

Jackknife Logo

Python 3.8+ License: MIT Test Coverage pre-commit Ruff Code style: black

โš ๏ธ WORK IN PROGRESS This project is still in early development. Features may change, break, or be completely removed.

Jackknife is a command-line utility that allows you to run various Python tool scripts, each within its own isolated virtual environment managed by uv.

Features

  • Isolated Environments: Each tool runs in its own dedicated virtual environment
  • Fast Setup: Uses uv for lightning-fast dependency installation
  • Modular Design: Add new tools without modifying the core code
  • Easy Updates: Update tool dependencies without affecting other tools
  • Zero Global Pollution: No global package installations required
  • No-Boilerplate Tools: Create tools without writing repetitive argparse code
  • Cascading Execution: Run multiple tools in sequence with a single command
  • Environment Optimization: Reuse compatible environments when possible to save space

Prerequisites

  • Python: Version 3.8 or higher

  • uv: The extremely fast Python package installer and resolver

    # Linux/macOS
    curl -LsSf https://astral.sh/uv/install.sh | sh
    
    # Windows
    powershell -c "irm https://astral.sh/uv/install.ps1 | iex"

    Verify installation: uv --version

Installation

  1. Clone the repository:

    git clone https://github.com/alansynn/jackknife.git
    cd jackknife
  2. Install Jackknife:

    # Regular installation
    pip install .
    
    # Development mode (for making changes)
    pip install -e .
    
    # Development mode with testing dependencies
    pip install -e ".[dev]"

Usage

Jackknife now uses subcommands to organize its functionality:

Running Tools (run command)

To run a single tool or a chain of tools, use the run subcommand:

jackknife run <tool_name | tool_chain> [tool_arguments...]
  • <tool_name>: The name of the tool script (without the .py extension) in the tools/ directory.
  • <tool_chain>: A comma-separated list of tool names, optionally with arguments in brackets (see below).
  • [tool_arguments...]: Any arguments you want to pass directly to the tool script (only applies when running a single tool).

Example (Single Tool)

Using the included giftomp4 tool:

# Show help for the giftomp4 tool
jackknife run giftomp4 --help

# Convert a GIF to MP4
jackknife run giftomp4 my_animation.gif -o my_video.mp4 --fps 24 --verbose

Example (Tool Chain)

You can run multiple tools in sequence:

# Run multiple tools in sequence
jackknife run tool1,tool2,tool3

# Stop on first error (default behavior)
jackknife run tool1,tool2,tool3

# Continue even if a tool fails
jackknife run tool1,tool2,tool3 --continue-on-error
Tool-Specific Arguments in Chains

You can provide arguments for each tool in a chain using square brackets:

# Run tools with their own arguments
jackknife run "tool1[--option value],tool2[arg1 arg2],tool3[--flag]"

Note: When using tool-specific arguments in chains, you often need to quote the entire tool chain string to prevent the brackets from being interpreted by your shell.

Environment Sharing

Environment sharing options are part of the run command:

# Run with environment sharing (default)
jackknife run mytool

# Run without environment sharing
jackknife run --no-share-environments mytool

You can also control this behavior globally with an environment variable:

# Disable environment sharing globally
export JACKKNIFE_SHARE_ENVIRONMENTS=false
jackknife run mytool

# Enable environment sharing globally
export JACKKNIFE_SHARE_ENVIRONMENTS=true
jackknife run mytool

Activating a Tool's Environment (activate command)

Sometimes, you might want to manually work within a tool's isolated environment (e.g., to use its specific Python interpreter or installed packages directly). The activate command helps with this:

jackknife activate <tool_name>

This command prints the necessary shell command to activate the tool's virtual environment. It does not activate the environment itself.

Example (macOS/Linux - bash/zsh)

# Print the activation command for the 'cinit' tool's environment
jackknife activate cinit

# To actually activate it, use eval:
eval $(jackknife activate cinit)

# Now your shell is using the cinit environment
(cinit) $ python --version
(cinit) $ which python
# .../.jackknife_envs/cinit/bin/python

# Deactivate when done
(cinit) $ deactivate
$

Example (Windows - Command Prompt)

:: Print the activation command path
jackknife activate cinit

:: You would typically run the printed .bat script directly
C:\Users\You\.jackknife_envs\cinit\Scripts\activate.bat

(cinit) C:\Your\Project> REM Now in the tool's environment

Note: The activate command only works if the tool has been run at least once with jackknife run <tool_name> to create its environment.

Included Tools Examples

Note: The first time you run a specific tool, Jackknife needs to create its isolated environment and install dependencies, which might take a few extra seconds. Subsequent runs will be much faster.

Jackknife comes with several useful tools:

  • giftomp4: Converts animated GIFs to MP4 videos using ffmpeg (requires ffmpeg to be installed separately).
    jackknife run giftomp4 input.gif -o output.mp4 --fps 30
  • cinit: Manages Cursor rules (link, add, edit) by interacting with ~/.cursor/rules/ and the current directory's ./.cursor/rules/.
    # Interactively link rules from ~/.cursor/rules to ./.cursor/rules
    jackknife run cinit link
    
    # Add a new rule file to ~/.cursor/rules and optionally link it
    jackknife run cinit add path/to/my_rule.mdc
    
    # Select a rule from ~/.cursor/rules to open in your editor
    jackknife run cinit edit
  • mcpm: Manages GitMCP server entries in ~/.cursor/mcp.json.
    jackknife run mcpm list # List configured servers
    jackknife run mcpm add github.com/owner/repo --name my-repo # Add a server
    jackknife run mcpm remove my-repo # Remove a server
  • piazzad: Downloads resources from a Piazza course page (requires manual login via Selenium).
    # Example: jackknife run piazzad <piazza_resources_url> <network_id> -o <output_dir>
    jackknife run piazzad https://piazza.com/class/abc123xyz/resources abc123xyz -o ./course_downloads
    # Requires you to log in via the opened browser window.

How It Works

  1. When you run jackknife run giftomp4 ..., the script finds tools/giftomp4.py.
  2. It checks for a corresponding virtual environment in ~/.jackknife_envs/giftomp4.
  3. If needed (and not using a shared environment), it creates the environment and installs dependencies from tools/giftomp4.requirements.txt using uv.
  4. It executes the tool script with your arguments using the isolated Python interpreter from the environment.

Adding New Tools

Adding new tools to Jackknife is straightforward. You can either write a standard Python script or use the provided helper decorators for easier argument parsing.

For detailed instructions and examples, please see the dedicated guide:

โžก๏ธ docs/creating-tools.md

Here's a quick summary of the two approaches:

1. Using the Tool Decorator (Recommended)

Leverage @tool and @argument from jackknife.tool_helpers for automatic argument parsing. See tools/example_decorated.py and the guide for details.

2. Traditional Script Approach

Write a standard script using argparse, typer, or similar. Ensure it has an if __name__ == "__main__": block and uses sys.exit(). See tools/example_traditional.py and the guide for details.

Key Steps:

  1. Place your Python script (e.g., mytool.py) inside the tools/ directory.
  2. If your tool has dependencies, list them in mytool.requirements.txt in the same directory.
  3. Run it: jackknife run mytool [arguments...]

Environment Location

By default, environments are stored in ~/.jackknife_envs/. You can change this location:

export JACKKNIFE_ENVS_DIR=/path/to/your/envs
jackknife run giftomp4 input.gif

Jackknife will create separate environments for each tool, or reuse compatible environments if sharing is enabled. Use jackknife activate <tool_name> to get the command for entering a specific tool's environment manually.

Code Quality

Jackknife uses several tools to maintain high code quality:

Ruff for Linting and Formatting

Ruff is an extremely fast Python linter and formatter written in Rust. It replaces many traditional Python tools (Flake8, Black, isort, etc.) with a single, much faster alternative.

To run Ruff linting:

ruff check .

To format the code with Ruff:

ruff format .

Pre-commit Hooks

We use pre-commit to manage our pre-commit hooks. After cloning the repository, install the hooks with:

pip install pre-commit
pre-commit install

This will automatically check the code quality before each commit.

Testing

Jackknife includes a comprehensive test suite using pytest and coverage reporting.

Running Tests

# Install development dependencies first
pip install -e ".[dev]"

# Run tests
pytest

# Run tests with verbose output
pytest -v

# Run a specific test
pytest tests/test_cli.py::TestMain::test_successful_tool_execution

Coverage Reports

The test suite includes coverage reporting to ensure code quality:

# Run tests with coverage (automatically includes --cov options)
pytest

# Generate HTML coverage report only
pytest --cov-report=html --cov-report=term-missing

# View coverage report
open htmlcov/index.html

Current test coverage: 91%

Test Structure

  • Unit Tests: Test individual functions in isolation
  • Integration Tests: Test how components work together
  • Functional Tests: End-to-end tests using real subprocess calls

Todo

  • Add boilerplate-free tool development with decorators (@tool, @argument)
  • Add Ruff for linting and formatting
  • Add pre-commit hooks
  • Add cascading tool execution (run tool1,tool2,...)
  • Add environment sharing for compatible tools (--no-share-environments)
  • Add activate command to get venv source string
  • Add cinit tool for Cursor rule management
  • Add mcpm tool for MCP server management
  • Add piazzad tool for Piazza resource downloading
  • Add tool discovery plugins (e.g., load tools from other installed packages)
  • Add caching for environment checks/dependency resolution to speed up startup
  • Implement tool update command (e.g., jackknife update mytool to reinstall dependencies)
  • Add tool versioning and dependency locking (e.g., using uv lock)
  • Improve error handling and reporting for tool execution failures

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Make sure to run tests and pre-commit hooks before submitting your PR.

License

MIT License - See LICENSE file for details

About

Jackknife is a command-line utility that allows you to run various Python tool scripts, each within its own isolated virtual environment managed by uv. ๐Ÿ› ๏ธ

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published