mirror of
https://github.com/guillaumemeyer/watermarks-remover.git
synced 2026-08-22 13:11:57 +02:00
feat: add a lightweight Cursor text skill (#35)
* feat: add lightweight Cursor and Codex text skill Package the text-only workflow with safe cross-platform installation, optional persistent instructions, and focused tests so users can adopt it without the media tooling. Co-authored-by: Cursor <cursoragent@cursor.com> * test: keep the lightweight skill independently reviewable Avoid coupling the packaging PR to the separate Unicode safety change so either pull request can merge on its own. Co-authored-by: Cursor <cursoragent@cursor.com> * test: force UTF-8 for subprocess stdin in Windows CI --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Guillaume Meyer (The Opinionated Man) <1385518+guillaumemeyer@users.noreply.github.com>
This commit is contained in:
co-authored by
Cursor
Guillaume Meyer
parent
c7200d20c2
commit
8d79155ad2
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Install the lightweight text skill for Cursor."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
SKILL_NAME = "clean-user-facing-text"
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
SOURCE = ROOT / "skills" / SKILL_NAME
|
||||
|
||||
|
||||
def _present(path: Path) -> bool:
|
||||
return path.exists() or path.is_symlink()
|
||||
|
||||
|
||||
def _remove_path(path: Path) -> None:
|
||||
if path.is_symlink() or path.is_file():
|
||||
path.unlink()
|
||||
elif path.exists():
|
||||
shutil.rmtree(path)
|
||||
|
||||
|
||||
def _stage(skills_dir: Path) -> tuple[Path, Path]:
|
||||
skills_dir.mkdir(parents=True, exist_ok=True)
|
||||
staging_root = Path(
|
||||
tempfile.mkdtemp(prefix=f".{SKILL_NAME}.staging.", dir=skills_dir)
|
||||
)
|
||||
staged_skill = staging_root / SKILL_NAME
|
||||
try:
|
||||
shutil.copytree(SOURCE, staged_skill)
|
||||
if not (staged_skill / "SKILL.md").is_file():
|
||||
raise RuntimeError("staged skill is missing SKILL.md")
|
||||
except BaseException:
|
||||
shutil.rmtree(staging_root, ignore_errors=True)
|
||||
raise
|
||||
return staging_root, staged_skill
|
||||
|
||||
|
||||
def _install(destination: Path, force: bool) -> tuple[bool, Path | None]:
|
||||
if _present(destination) and not force:
|
||||
print(f"already exists: {destination}", file=sys.stderr)
|
||||
print(
|
||||
"No changes made. Re-run with --force to back up and replace.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return False, None
|
||||
|
||||
staging_root, staged_skill = _stage(destination.parent)
|
||||
backup: Path | None = None
|
||||
try:
|
||||
if _present(destination):
|
||||
backup = destination.with_name(
|
||||
f"{destination.name}.backup.{uuid.uuid4().hex[:12]}"
|
||||
)
|
||||
os.replace(destination, backup)
|
||||
try:
|
||||
os.replace(staged_skill, destination)
|
||||
except BaseException:
|
||||
if backup is not None and not _present(destination):
|
||||
os.replace(backup, destination)
|
||||
raise
|
||||
finally:
|
||||
shutil.rmtree(staging_root, ignore_errors=True)
|
||||
|
||||
return True, backup
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"--force",
|
||||
action="store_true",
|
||||
help="Back up and replace an existing Cursor installation",
|
||||
)
|
||||
parser.add_argument("--cursor-home", help="Override Cursor home (default: ~/.cursor)")
|
||||
args = parser.parse_args()
|
||||
|
||||
cursor_home = Path(
|
||||
args.cursor_home
|
||||
or os.environ.get("CURSOR_HOME", Path.home() / ".cursor")
|
||||
).expanduser()
|
||||
destination = cursor_home / "skills" / SKILL_NAME
|
||||
|
||||
installed, backup = _install(destination, args.force)
|
||||
if not installed:
|
||||
return 1
|
||||
if backup is not None:
|
||||
print(f"Cursor: backed up existing skill to {backup}")
|
||||
print(f"Cursor: installed {destination}")
|
||||
print("Start a new Cursor session if the skill does not appear automatically.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user