diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 3dfa0fe..0fb63f4 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,7 +6,7 @@ }, "metadata": { "description": "Agent skill and Claude Code plugin for the remove-ai-watermarks CLI.", - "version": "1.0.5" + "version": "1.0.6" }, "plugins": [ { diff --git a/skills/.claude-plugin/plugin.json b/skills/.claude-plugin/plugin.json index 374d525..e634531 100644 --- a/skills/.claude-plugin/plugin.json +++ b/skills/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "remove-ai-watermarks", "displayName": "Remove AI Watermarks", - "version": "1.0.5", + "version": "1.0.6", "description": "Identify and remove AI provenance marks from images and video the user generated or edited, using the remove-ai-watermarks CLI.", "author": { "name": "wiltodelta", diff --git a/skills/remove-ai-watermarks/SKILL.md b/skills/remove-ai-watermarks/SKILL.md index af2034a..a068324 100644 --- a/skills/remove-ai-watermarks/SKILL.md +++ b/skills/remove-ai-watermarks/SKILL.md @@ -5,7 +5,7 @@ license: Apache-2.0 compatibility: Python 3.11-3.14. Invisible image removal needs NVIDIA CUDA. Some video writes need ffmpeg. Installer may be uv, pipx, or pip. metadata: author: wiltodelta - version: "1.0.5" + version: "1.0.6" homepage: https://raiw.cc repository: https://github.com/wiltodelta/remove-ai-watermarks --- diff --git a/skills/remove-ai-watermarks/references/commands.md b/skills/remove-ai-watermarks/references/commands.md index 96da78a..d389b89 100644 --- a/skills/remove-ai-watermarks/references/commands.md +++ b/skills/remove-ai-watermarks/references/commands.md @@ -26,7 +26,8 @@ Image mark keys: `gemini`, `doubao`, `jimeng`, `qwen`, `kling`, `yuanbao`, `--mark auto` removes every selected match. `microsoft` is the top-right AI badge, separate from the invisible InvisMark watermark on the same vendor's images. `jimeng_pill` is a weak detector and needs corroboration. The compact -`liblib_pill` likewise requires LiblibAI metadata or the bottom-center wordmark. +`liblib_pill` likewise requires LiblibAI metadata or the bottom-center wordmark +(since CLI 0.39.0). ```bash remove-ai-watermarks erase image.png --region 1640,1930,400,100 -o clean.png @@ -88,8 +89,8 @@ run on. Pipeline profiles: `qwen-zimage` (the default), `sdxl-zimage`, `chroma-zimage`, `auto`. All are CUDA-only and all install from the same `qwen-zimage` extra. `auto` picks the engine from the file's own provenance -- chroma-zimage for -Microsoft, qwen-zimage for OpenAI, Google, Meta and unknown -- so prefer it -over naming an engine when the user has no reason to care. `sdxl-zimage` is the +Microsoft, qwen-zimage for OpenAI (since CLI 0.39.0), Google, Meta and unknown +-- so prefer it over naming an engine when the user has no reason to care. `sdxl-zimage` is the heavier alternative and `chroma-zimage` is the Apache-2.0 global stage. ```bash diff --git a/skills/remove-ai-watermarks/scripts/probe.py b/skills/remove-ai-watermarks/scripts/probe.py index db7c69e..5ddd25f 100644 --- a/skills/remove-ai-watermarks/scripts/probe.py +++ b/skills/remove-ai-watermarks/scripts/probe.py @@ -24,13 +24,14 @@ from typing import Any _GPU_MARKERS = ("GPU ", "gpu:") # Below this, the CLI predates flags and behavior this skill documents. Raise it in # the same change as any reference that names a flag the previous release lacked: -# 0.37.0 is here because `classify` arrived with it (`--pipeline auto` needed -# 0.36.0; chroma-zimage and the doubao video mark 0.35.0; -# the `microsoft` mark 0.34.0; `--vendor` 0.32.0), and a stale floor told an agent -# its CLI was current right before it typed a value that build rejects. -# tests/test_agent_skill.py pins it against the package version, so it can never -# advertise a release that does not exist. -MIN_CLI_VERSION = (0, 38, 0) +# 0.39.0 is here because the `liblib_pill` mark and `auto` routing OpenAI +# provenance to qwen-zimage arrived with it; 0.38.0 added the receipt gate; +# `classify` needed 0.37.0 (`--pipeline auto` 0.36.0; chroma-zimage and the +# doubao video mark 0.35.0; the `microsoft` mark 0.34.0; `--vendor` 0.32.0), +# and a stale floor told an agent its CLI was current right before it typed a +# value that build rejects. tests/test_agent_skill.py pins it against the +# package version and against every "(since CLI X.Y.Z)" annotation in the skill. +MIN_CLI_VERSION = (0, 39, 0) # Markers a build without the pixel stack prints. The first is the current CLI's own # install hint; the rest are what older builds emit before the guard existed, and the # Homebrew formula ships exactly such a build. diff --git a/tests/test_agent_skill.py b/tests/test_agent_skill.py index 0c2bfeb..5a5e473 100644 --- a/tests/test_agent_skill.py +++ b/tests/test_agent_skill.py @@ -104,6 +104,35 @@ def test_probe_min_cli_version_never_exceeds_the_package_version() -> None: sys.modules.pop("probe", None) +def test_probe_floor_covers_every_since_annotation() -> None: + """MIN_CLI_VERSION must be at least the newest named "(since CLI X.Y.Z)" value. + + A reference that names a value the previous release lacked has to raise the + floor in the same change; this reads the annotations back out of the skill + tree so a stale floor fails the suite instead of an agent's CLI. + """ + + sys.path.insert(0, str(ROOT / "skills" / "remove-ai-watermarks" / "scripts")) + try: + import probe + + annotated = [] + for path in (ROOT / "skills" / "remove-ai-watermarks").rglob("*"): + if path.suffix not in {".md", ".py"} or not path.is_file(): + continue + for match in re.finditer(r"\(since CLI (\d+)\.(\d+)\.(\d+)\)", path.read_text(encoding="utf-8")): + annotated.append(tuple(int(part) for part in match.groups())) + assert annotated, "no (since CLI X.Y.Z) annotations found; the floor guard lost its source" + highest = max(annotated) + assert highest <= probe.MIN_CLI_VERSION, ( + f"probe.py MIN_CLI_VERSION {probe.MIN_CLI_VERSION} is below {highest}, the newest " + "(since CLI X.Y.Z) annotation in the skill; a build that old rejects a named value" + ) + finally: + sys.path.remove(str(ROOT / "skills" / "remove-ai-watermarks" / "scripts")) + sys.modules.pop("probe", None) + + @pytest.mark.parametrize( "relative", [