A lightweight, extensible micro‑framework for fast, friendly automation scripts.
Giorgio helps you scaffold, run, and compose automation scripts—from one‑off utilities to ambitious prototypes—without the ceremony. Think of it as a courteous digital butler: it asks the right questions, remembers your preferences, and carries out your instructions with minimal fuss.
- Giorgio - Automation Framework
Script files are quick to write—until they aren’t. Once you add parameters, validation, prompts, environment variables, and a touch of UX, the "quick script" becomes an accidental framework. Giorgio gives that structure to you from the start: a clean scaffold, a type‑safe parameter system, an interactive runner, and an optional AI‑powered script generator that turns plain‑language ideas into working code.
Use Giorgio when you want:
- Rapid local automations with a consistent shape.
- Interactive runs that ask for what’s missing, or non‑interactive runs suitable for Cron.
- A clear migration path from small scripts to reusable building blocks.
- To generate boilerplate (or whole scripts) from a description using any OpenAI‑compatible API.
- Instant scaffolding with a best‑practice directory layout.
- Script generator from templates or via vibe‑coding using an OpenAI‑compatible API.
- Interactive CLI with dynamic prompts and live output, or fully automated runs.
- Type‑safe parameters with custom types, choices, and validation.
- Environment variable placeholders and
.envloading. - Composable automation: call other scripts from your script.
- Pluggable UI renderers for tailored interactive experiences.
- Minimal setup, maximum extensibility: configure only what you need.
Giorgio supports Python 3.10+. Installing with pipx keeps your CLI isolated and on your PATH:
pipx install giorgioAlternatively, use pip in a virtual environment:
python -m venv .venv
source .venv/bin/activate # Windows: .venv\\Scripts\\activate
pip install giorgioNote For AI‑powered generation you’ll need an OpenAI‑compatible endpoint and API key (see Generate a script with AI).
giorgio init my_project
cd my_projectCreates a clean structure with dedicated folders for scripts, shared modules, and configuration.
giorgio new cleanup_logsThis creates scripts/cleanup_logs/script.py with a ready‑to‑edit skeleton (CONFIG, PARAMS, and run(context)).
Note Scripts can also be created in subdirectories, e.g.:
giorgio new cleansing/cleanup_logsThis will create
scripts/cleansing/cleanup_logs/script.py.
giorgio new greet_user --ai-prompt "Create a script that prints a text provided as a parameter"When using --ai-prompt, Giorgio may also ask to include project modules or existing scripts as context to improve generation. The result follows Giorgio’s conventions and includes inline documentation.
During script generation Giorgio automatically shares additional context with the AI:
requirements.txtcontents if the file exists, so dependencies stay aligned.- The list of variable names defined in the project
.env, helping the AI reference available configuration knobs without exposing secrets.
Environment (typical):
You can set environment variables directly in your shell, or place them in your project’s .env file (recommended for local development):
export AI_API_KEY=...
export AI_BASE_URL=https://my-endpoint.example/v1
export AI_API_MODEL=my-favorite-model
export AI_TEMPERATURE=0.2 # (optional) Controls randomness, default 0.0
export AI_MAX_TOKENS=2048 # (optional) Max output tokens, default unlimitedOr in .env:
AI_API_KEY=...
AI_BASE_URL=https://my-endpoint.example/v1
AI_API_MODEL=my-favorite-model
AI_TEMPERATURE=0.2
AI_MAX_TOKENS=2048
Interactive mode: Prompts you to select a script, then asks for parameters.
giorgio startAutomated (non-interactive) mode: Runs the specified script directly. You must provide all required parameters (or ensure they have defaults).
giorgio run cleanup_logs --param days=30 --param log_dir=/var/logsUse interactive mode for guided runs and parameter prompts; use non-interactive mode for automation and scripting (e.g., in Cron jobs).
After giorgio init my_project:
my_project/
├─ .giorgio/config.json
├─ modules/
│ └─ __init__.py
├─ scripts/
└─ .env
Every Giorgio script follows a standard structure: CONFIG, PARAMS, and a run(context) function.
CONFIG is optional metadata shown in interactive mode.
CONFIG = {
"name": "Cleanup Logs",
"description": "Remove old log files from a target directory."
}PARAMS declares all inputs the script needs. Giorgio validates types, applies defaults, and—if running interactively—prompts for anything missing.
Supported attributes:
type(required) — validates and converts the value (e.g.str,int,bool,Path, or custom classes).default(optional) — a fallback value; supports${VAR_NAME}placeholders for environment variables.description(optional) — a short help text.choices(optional) — restricts input to a predefined list of values.multiple(optional) — allow selection of multiple values (withchoices).required(optional) — mark the parameter as mandatory.validation(optional) — a function that returnsTrueor an error message.
Example:
from pathlib import Path
PARAMS = {
"confirm": {
"type": bool,
"default": False,
"description": "Whether to confirm the action.",
},
"days": {
"type": int,
"default": 30,
"description": "Delete files older than N days.",
"validation": lambda x: x > 0 or "Days must be a positive integer."
},
"log_dir": {
"type": Path,
"description": "Directory containing log files.",
"required": True
},
"level": {
"type": str,
"choices": ["debug", "info", "warning", "error"],
"description": "Logging verbosity.",
},
"options": {
"type": str,
"choices": ["dry_run", "force"],
"multiple": True,
"description": "Optional flags."
},
"environment_var": {
"type": str,
"default": "${MY_ENV_VAR}",
"description": "Value read from env if set."
}
}run(context) is the script’s entry point. It receives a Context object providing everything needed at runtime.
Performance tip: When Giorgio inspects a script it still imports the module, even if it only reads
CONFIG. Keep top-levelCONFIGandPARAMSdefinitions lightweight and move heavyweight imports or setup intorun(context)(or helpers it calls) so script discovery stays fast.
from pathlib import Path
from giorgio.execution_engine import Context, GiorgioCancellationError
def run(context: Context):
try:
# Lazily import heavier dependencies
from big_data_toolkit import RetentionPlanner
log_dir: Path = context.params["log_dir"]
days: int = context.params["days"]
print(f"Cleaning logs older than {days} days in {log_dir}…")
planner = RetentionPlanner(base_dir=log_dir, retention_days=days)
stale_files = planner.compute_candidates()
if not stale_files:
print("No files exceeded the retention policy.")
return
# Example of dynamic prompting (interactive mode only)
context.add_params({
"confirm": {
"type": bool,
"description": "Are you sure you want to delete these files?",
"default": False,
}
})
if not context.params["confirm"]:
print("Operation cancelled.")
return
# Compose with another Giorgio script, passing the computed file list.
context.call_script(
"delete_files",
{
"target_dir": log_dir,
"files": stale_files,
}
)
print("Cleanup completed.")
except GiorgioCancellationError:
print("Script execution cancelled by the user.")The Context object exposes:
- Validated parameters —
context.params["key"]. - Environment variables —
context.env["VAR"](loads from.envand the system). - Dynamic prompting —
context.add_params({...})to ask for more inputs at runtime (interactive mode only). - Composition —
context.call_script(name, params)to run other Giorgio scripts programmatically. - Logging —
context.loggerexposes standard logging methods (debug/info/warning/error); the engine replaces it with a script-specific child loggergiorgio.scripts.<script_path>when a script runs. - Output — standard
print()and other output streams are supported; UI renderers may capture and display these outputs in interactive mode.
Scripts are building blocks. You can invoke one script from another to create small, readable steps that add up to robust flows.
def run(context):
# Normalize inputs in one script…
context.call_script("normalize_dataset", {"src": "./raw.csv"})
# …then pass results into the next one.
context.call_script("train_model", {"epochs": 10, "lr": 0.001})Composition keeps each script focused and testable while enabling richer automations.
Giorgio reads environment variables from the OS and, if present, from a local .env file at project root. Within PARAMS, you may reference variables using ${VAR_NAME} in default values; these expand at runtime.
Supported AI environment variables:
AI_API_KEY— API key for your OpenAI-compatible endpoint (required)AI_BASE_URL— Base URL for the API (required)AI_MODELorAI_API_MODEL— Model name (required)AI_TEMPERATURE— (optional) Controls randomness (float, default: 0.0)AI_MAX_TOKENS— (optional) Maximum output tokens (int, default: unlimited)
Example .env:
DATA_DIR=./data
MY_ENV_VAR=hello
Example PARAMS default using an env placeholder:
"data_dir": {
"type": Path,
"default": "${DATA_DIR}",
"description": "Where datasets are stored."
}giorgio init <project_name>
Create a new Giorgio project (folders for scripts, modules, config).
giorgio new <script_name> [--ai-prompt "…"]
Scaffold a new script. With --ai-prompt, generate code via an OpenAI‑compatible API.
giorgio start
Launch interactive mode. Select a script; Giorgio prompts for missing params.
giorgio run <script_name> --param key=value [--param key=value ...]
Run non‑interactively (suitable for Cron). Provide all required params.
giorgio upgrade [--force]
Update the project's pinned Giorgio version in .giorgio/config.json. Use --force to skip script validation.
giorgio validate
Run static validation on all scripts without executing them.
giorgio --help
Show general help and options.
Tip Use
--paramrepeatedly to pass multiple key/value pairs.
Heads-up If the project configuration references a different Giorgio version than the currently installed CLI, commands such as
run,start, andnewdisplay a warning suggestinggiorgio upgradeso you can realign the versions.
Cron (Linux/macOS):
# Run every night at 02:30
30 2 * * * cd /path/to/my_project && /usr/bin/env giorgio run cleanup_logs \
--param days=30 --param log_dir=/var/logs >> cron.log 2>&1
Windows Task Scheduler: create a task that executes:
C:\\Path\\To\\Python\\Scripts\\giorgio.exe run cleanup_logs --param days=30 --param log_dir=C:\\Logs
- Keep scripts small and focused. Compose multiple scripts for larger automation flows to maximize clarity and reusability.
- Declare all inputs in
PARAMS. Avoid using implicit globals; explicit parameters improve validation and documentation. - Leverage
choicesandvalidation. Define allowed values and validation logic to catch errors early and guide users. - Encapsulate reusable logic in
modules/. Place shared functions and utilities in themodules/directory and import them into your scripts. - Use
context.add_params()for dynamic prompting. Prefer Giorgio’s built-in parameter prompting over other package methods or native Python functions likeinput(). This ensures consistent UX, validation, and compatibility with both interactive and automated runs. - Reserve AI generation for boilerplate or drafts. Use AI-powered script generation to accelerate prototyping, but always review and refine generated code before use.
- Handle secrets securely. Store API keys and sensitive data in your
.envfile (excluded from version control) or your OS keychain—never hard-code them. - Review AI-generated code before production use. Always inspect and test code generated by AI before deploying in critical environments.
- Avoid logging secrets. Redact or exclude sensitive information from logs to prevent accidental exposure.
Community contributions are welcome and encouraged. To maintain a smooth and manageable workflow, please adhere to the following guidelines.
Development follows a trunk-based workflow using short-lived branches created from main. When your changes are complete and stable, open a pull request (PR) targeting main. This approach ensures the codebase remains up-to-date and avoids long-lived branches.
Please write commit messages according to the Conventional Commits specification. To make this easier, use the commitizen tool, which helps you generate properly formatted commit messages. You can run cz commit instead of git commit to be guided through the process.
All pull requests against main trigger the CI workflow, which runs linting, tests across Python 3.10–3.12 on Linux, macOS, and Windows, and enforces the coverage threshold. After a push to the main branch, the version bump workflow runs to bump the version with Commitizen and update CHANGELOG.md, pushing the new v* tag. Pushing that tag then triggers the publish workflow to run final tests & coverage upload, build the distribution, and publish the package to PyPI.
Please ensure that every contribution includes appropriate tests. Adding or updating tests helps maintain code quality and reliability for all users.
Thank you for contributing to Giorgio!
This project is licensed under the terms of the MIT License. Refer to the LICENSE file for full license text.
Happy automating with Giorgio!