diff --git a/CHANGELOG.md b/CHANGELOG.md index bdaa4e9..5d125f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [0.3.0] - 2026-05-26 + +### Changed +- Project renamed from **promptvero** to **graver** and transferred to the Practical Mind organization +- Originally created by Muhammed Şen as [promptvero](https://github.com/MuhammedSenn/promptvero) +- Python package renamed: `promptvero` → `graver` +- CLI command renamed: `pv` → `gr` +- Default storage directory renamed: `.promptvero/` → `.graver/` +- Base exception class renamed: `PromptVeroError` → `GraverError` + ## [0.2.0] - 2026-04-14 ### Added diff --git a/LICENSE b/LICENSE index 4c24e5d..d6b2367 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2026 Muhammed Şen +Copyright (c) 2026 Practical Mind Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index e3f4cf4..b939af4 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -# promptvero +# Graver Git-like version control for your LLM prompts. Save prompt versions, view history, compare diffs, and roll back — all from Python, with zero external dependencies. -## Why promptvero? +## Why graver? LLM prompts change constantly. Tracking what changed, when, and which version performed better quickly becomes a mess. -promptvero solves this: +graver solves this: - Every change is automatically versioned — v1, v2, v3... - Roll back to any version instantly - Pin a specific version as your main (canonical) prompt @@ -18,13 +18,13 @@ promptvero solves this: ## Installation ```bash -pip install promptvero +pip install graver ``` ## Quick Start ```python -from promptvero import Prompt +from graver import Prompt p = Prompt("system") p.save("You are a helpful assistant") @@ -45,34 +45,34 @@ print(p.changes("v1", "v2")) ## CLI -The `pv` command lets you use promptvero directly from the terminal — no Python script needed. Useful for quickly saving files, inspecting version history, or integrating into shell scripts and CI/CD pipelines. +The `gr` command lets you use graver directly from the terminal — no Python script needed. Useful for quickly saving files, inspecting version history, or integrating into shell scripts and CI/CD pipelines. ```bash -pv list # list all saved prompts -pv save # save a file as a new version -pv log # show version history -pv show [version] # show prompt content -pv changes [v1] [v2] # show what changed between versions -pv set-main # mark a version as main -pv get-main # print the main version content -pv delete # delete a specific version -pv delete-prompt # delete a prompt and all its versions +gr list # list all saved prompts +gr save # save a file as a new version +gr log # show version history +gr show [version] # show prompt content +gr changes [v1] [v2] # show what changed between versions +gr set-main # mark a version as main +gr get-main # print the main version content +gr delete # delete a specific version +gr delete-prompt # delete a prompt and all its versions ``` Use `--base-dir` to specify a custom storage directory: ```bash -pv --base-dir ./prompts log my-prompt +gr --base-dir ./prompts log my-prompt ``` ## API Reference -### `Prompt(name, base_dir=".promptvero")` +### `Prompt(name, base_dir=".graver")` | Parameter | Type | Description | |-----------|------|-------------| | `name` | `str` | Unique identifier for this prompt. | -| `base_dir` | `str` | Root directory for storage. Defaults to `.promptvero`. | +| `base_dir` | `str` | Root directory for storage. Defaults to `.graver`. | --- @@ -194,7 +194,7 @@ Returns the full content of a version with a formatted header. Displays a `[main --- -### `Prompt.list_all(base_dir=".promptvero") -> list[str]` +### `Prompt.list_all(base_dir=".graver") -> list[str]` Returns the names of all saved prompts in the given base directory. @@ -204,10 +204,10 @@ Returns the names of all saved prompts in the given base directory. ## How It Works -Every `Prompt` writes to a `.promptvero/` folder in your working directory. +Every `Prompt` writes to a `.graver/` folder in your working directory. ``` -.promptvero/ +.graver/ system/ v1.txt v2.txt diff --git a/promptvero/__init__.py b/graver/__init__.py similarity index 51% rename from promptvero/__init__.py rename to graver/__init__.py index 5d42036..02b7309 100644 --- a/promptvero/__init__.py +++ b/graver/__init__.py @@ -1,31 +1,31 @@ -"""promptvero — Git-like version control for LLM prompts. +"""graver — Git-like version control for LLM prompts. Save, diff, and roll back prompt versions with a simple Python API. -All data is stored as plain text files under a ``.promptvero/`` directory. +All data is stored as plain text files under a ``.graver/`` directory. Example:: - from promptvero import Prompt + from graver import Prompt p = Prompt("system") p.save("You are a helpful assistant") print(p.get()) """ -from promptvero.core import Prompt -from promptvero.exceptions import ( +from graver.core import Prompt +from graver.exceptions import ( + GraverError, PromptNotFoundError, - PromptVeroError, StorageError, VersionNotFoundError, ) -__version__ = "0.2.0" -__author__ = "Muhammed Sen" +__version__ = "0.3.0" +__author__ = "Practical Mind" __all__ = [ "Prompt", - "PromptVeroError", + "GraverError", "PromptNotFoundError", "VersionNotFoundError", "StorageError", diff --git a/promptvero/cli.py b/graver/cli.py similarity index 93% rename from promptvero/cli.py rename to graver/cli.py index 0e78bcb..53f71c0 100644 --- a/promptvero/cli.py +++ b/graver/cli.py @@ -1,10 +1,10 @@ -"""Command-line interface for promptvero.""" +"""Command-line interface for graver.""" import argparse import sys -from promptvero.core import Prompt -from promptvero.exceptions import PromptVeroError +from graver.core import Prompt +from graver.exceptions import GraverError def cmd_list(args: argparse.Namespace) -> None: @@ -68,14 +68,14 @@ def cmd_delete_prompt(args: argparse.Namespace) -> None: def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( - prog="pv", + prog="gr", description="Git-like version control for LLM prompts.", ) parser.add_argument( "--base-dir", - default=".promptvero", + default=".graver", metavar="DIR", - help="Root directory for prompt storage (default: .promptvero)", + help="Root directory for prompt storage (default: .graver)", ) sub = parser.add_subparsers(dest="command", metavar="COMMAND") @@ -144,6 +144,6 @@ def main() -> None: args = parser.parse_args() try: COMMANDS[args.command](args) - except PromptVeroError as exc: + except GraverError as exc: print(f"Error: {exc}", file=sys.stderr) sys.exit(1) diff --git a/promptvero/core.py b/graver/core.py similarity index 95% rename from promptvero/core.py rename to graver/core.py index 167bb8e..e061faf 100644 --- a/promptvero/core.py +++ b/graver/core.py @@ -1,9 +1,9 @@ -"""Public-facing API for promptvero.""" +"""Public-facing API for graver.""" from pathlib import Path -from promptvero.exceptions import PromptNotFoundError -from promptvero.storage import Storage +from graver.exceptions import PromptNotFoundError +from graver.storage import Storage class Prompt: @@ -11,15 +11,15 @@ class Prompt: Args: name: Unique identifier for this prompt. - base_dir: Root directory for all prompt storage. Defaults to ".promptvero". + base_dir: Root directory for all prompt storage. Defaults to ".graver". """ - def __init__(self, name: str, base_dir: str = ".promptvero") -> None: + def __init__(self, name: str, base_dir: str = ".graver") -> None: self.name = name self._storage = Storage(base_dir=base_dir) @staticmethod - def list_all(base_dir: str = ".promptvero") -> list[str]: + def list_all(base_dir: str = ".graver") -> list[str]: """Return the names of all saved prompts in the given base directory. Args: diff --git a/graver/exceptions.py b/graver/exceptions.py new file mode 100644 index 0000000..9551f1c --- /dev/null +++ b/graver/exceptions.py @@ -0,0 +1,17 @@ +"""Custom exceptions for the graver library.""" + + +class GraverError(Exception): + """Base class for all graver errors.""" + + +class PromptNotFoundError(GraverError): + """Raised when a prompt name does not exist in storage.""" + + +class VersionNotFoundError(GraverError): + """Raised when a requested version string does not exist.""" + + +class StorageError(GraverError): + """Raised when a file system operation fails.""" diff --git a/promptvero/storage.py b/graver/storage.py similarity index 97% rename from promptvero/storage.py rename to graver/storage.py index 1d84188..aaf0a22 100644 --- a/promptvero/storage.py +++ b/graver/storage.py @@ -1,11 +1,11 @@ -"""File-system storage backend for promptvero.""" +"""File-system storage backend for graver.""" import difflib import json from datetime import datetime from pathlib import Path -from promptvero.exceptions import ( +from graver.exceptions import ( PromptNotFoundError, StorageError, VersionNotFoundError, @@ -13,13 +13,13 @@ class Storage: - """Handles all file system operations for promptvero. + """Handles all file system operations for graver. Args: base_dir: Root directory for all prompt storage. """ - def __init__(self, base_dir: str = ".promptvero") -> None: + def __init__(self, base_dir: str = ".graver") -> None: self._base = Path(base_dir).resolve() self._base.mkdir(parents=True, exist_ok=True) diff --git a/promptvero/exceptions.py b/promptvero/exceptions.py deleted file mode 100644 index 021d0a0..0000000 --- a/promptvero/exceptions.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Custom exceptions for the promptvero library.""" - - -class PromptVeroError(Exception): - """Base class for all promptvero errors.""" - - -class PromptNotFoundError(PromptVeroError): - """Raised when a prompt name does not exist in storage.""" - - -class VersionNotFoundError(PromptVeroError): - """Raised when a requested version string does not exist.""" - - -class StorageError(PromptVeroError): - """Raised when a file system operation fails.""" diff --git a/pyproject.toml b/pyproject.toml index 13fe48f..44651d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,14 +3,14 @@ requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "promptvero" -version = "0.2.0" +name = "graver" +version = "0.3.0" description = "Git-like version control for LLM prompts" readme = "README.md" license = {file = "LICENSE"} requires-python = ">=3.10" authors = [ - {name = "Muhammed Sen"} + {name = "Practical Mind"} ] keywords = ["prompt", "versioning", "llm", "ai", "agents", "version-control"] classifiers = [ @@ -28,7 +28,7 @@ classifiers = [ dependencies = [] [project.scripts] -pv = "promptvero.cli:main" +gr = "graver.cli:main" [project.optional-dependencies] dev = [ @@ -38,9 +38,9 @@ dev = [ ] [project.urls] -Homepage = "https://github.com/MuhammedSenn/promptvero" -Repository = "https://github.com/MuhammedSenn/promptvero" -Issues = "https://github.com/MuhammedSenn/promptvero/issues" +Homepage = "https://github.com/PracticalMind/graver" +Repository = "https://github.com/PracticalMind/graver" +Issues = "https://github.com/PracticalMind/graver/issues" [tool.black] line-length = 88 @@ -53,7 +53,7 @@ line-length = 88 select = ["E", "F", "I"] [tool.hatch.build.targets.wheel] -packages = ["promptvero"] +packages = ["graver"] exclude = ["tests/"] [tool.hatch.build.targets.sdist] diff --git a/tests/test_cli.py b/tests/test_cli.py index 349b84b..9ca6176 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,16 +1,16 @@ -"""Tests for promptvero.cli.""" +"""Tests for graver.cli.""" import sys import pytest -from promptvero.cli import main -from promptvero.core import Prompt +from graver.cli import main +from graver.core import Prompt def run_cli(args: list[str], base_dir: str) -> tuple[str, str, int]: """Run the CLI with given args and return (stdout, stderr, exit_code).""" - argv = ["pv", "--base-dir", base_dir] + args + argv = ["gr", "--base-dir", base_dir] + args with pytest.MonkeyPatch().context() as mp: mp.setattr(sys, "argv", argv) try: diff --git a/tests/test_core.py b/tests/test_core.py index 6efa680..03ea9e4 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1,9 +1,9 @@ -"""Tests for promptvero.core.Prompt.""" +"""Tests for graver.core.Prompt.""" import pytest -from promptvero.core import Prompt -from promptvero.exceptions import PromptNotFoundError +from graver.core import Prompt +from graver.exceptions import PromptNotFoundError def test_save_and_get(tmp_path): diff --git a/tests/test_storage.py b/tests/test_storage.py index 8d6e330..35e93f2 100644 --- a/tests/test_storage.py +++ b/tests/test_storage.py @@ -1,9 +1,9 @@ -"""Tests for promptvero.storage.Storage.""" +"""Tests for graver.storage.Storage.""" import pytest -from promptvero.exceptions import PromptNotFoundError, VersionNotFoundError -from promptvero.storage import Storage +from graver.exceptions import PromptNotFoundError, VersionNotFoundError +from graver.storage import Storage def test_save_creates_file(tmp_path):