makememe 0.1.0__tar.gz → 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ name: publish
2
+
3
+ # Publishes to PyPI automatically whenever you create a GitHub Release.
4
+ # Uses PyPI Trusted Publishing (OIDC) — no API token or password needed.
5
+ on:
6
+ release:
7
+ types: [published]
8
+
9
+ jobs:
10
+ pypi:
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ id-token: write # required for trusted publishing
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: astral-sh/setup-uv@v6
17
+ - run: uv build
18
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,14 @@
1
+ name: tests
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v4
10
+ - uses: actions/setup-python@v5
11
+ with:
12
+ python-version: "3.12"
13
+ - run: pip install .
14
+ - run: python -m unittest discover -s tests
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: makememe
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: A tiny zero-dependency CLI for generating memes via the free memegen.link API. Agent-friendly.
5
5
  Project-URL: Homepage, https://pypi.org/project/makememe/
6
6
  Project-URL: memegen.link API, https://api.memegen.link
@@ -32,20 +32,33 @@ meme drake "not reading docs" "reading docs" -o out.png
32
32
 
33
33
  ## Install
34
34
 
35
- Requires Python 3.8+.
35
+ Requires Python 3.8+. Easiest is [uv](https://docs.astral.sh/uv/) (installs
36
+ the `meme` command in its own isolated environment):
36
37
 
37
38
  ```bash
38
- pip install makememe
39
- # or, as an isolated tool:
40
- uv tool install makememe # or: pipx install makememe
39
+ uv tool install makememe # or: pipx install makememe
41
40
  ```
42
41
 
43
- Run once without installing:
42
+ Update later with:
43
+
44
+ ```bash
45
+ uv tool upgrade makememe
46
+ ```
47
+
48
+ Run once without installing anything:
44
49
 
45
50
  ```bash
46
51
  uvx --from makememe meme drake "a" "b"
47
52
  ```
48
53
 
54
+ (If you specifically want pip: `python3 -m pip install makememe`.)
55
+
56
+ Check the version anytime:
57
+
58
+ ```bash
59
+ meme --version
60
+ ```
61
+
49
62
  ## Usage
50
63
 
51
64
  ```bash
@@ -109,21 +122,19 @@ meme drake "old way" "new way" --json # generate, capture the path
109
122
 
110
123
  ### Claude Code skill
111
124
 
112
- A ready-made skill lives in [`skill/meme/`](skill/meme/SKILL.md). Install it so
113
- Claude Code auto-discovers the tool:
125
+ The package bundles a Claude Code skill. After installing, run one command to
126
+ make Claude Code auto-discover the tool:
114
127
 
115
128
  ```bash
116
- # user-level (all projects)
117
- mkdir -p ~/.claude/skills
118
- cp -r skill/meme ~/.claude/skills/meme
119
-
120
- # or project-level
121
- mkdir -p .claude/skills
122
- cp -r skill/meme .claude/skills/meme
129
+ meme --install-skill # installs into ~/.claude/skills/meme/ (all projects)
130
+ meme --install-skill --project # or into ./.claude/skills/meme/ (this repo only)
123
131
  ```
124
132
 
125
- Then just ask Claude Code things like *"make a drake meme about writing tests"*
126
- and it will call `meme` for you.
133
+ Restart Claude Code, then just ask things like *"make a drake meme about
134
+ writing tests"* and it will call `meme` for you.
135
+
136
+ (Other agents like Codex don't use this skill format — they discover everything
137
+ through `meme --help` and `meme --list`, which already works out of the box.)
127
138
 
128
139
  ## Robustness
129
140
 
@@ -13,20 +13,33 @@ meme drake "not reading docs" "reading docs" -o out.png
13
13
 
14
14
  ## Install
15
15
 
16
- Requires Python 3.8+.
16
+ Requires Python 3.8+. Easiest is [uv](https://docs.astral.sh/uv/) (installs
17
+ the `meme` command in its own isolated environment):
17
18
 
18
19
  ```bash
19
- pip install makememe
20
- # or, as an isolated tool:
21
- uv tool install makememe # or: pipx install makememe
20
+ uv tool install makememe # or: pipx install makememe
22
21
  ```
23
22
 
24
- Run once without installing:
23
+ Update later with:
24
+
25
+ ```bash
26
+ uv tool upgrade makememe
27
+ ```
28
+
29
+ Run once without installing anything:
25
30
 
26
31
  ```bash
27
32
  uvx --from makememe meme drake "a" "b"
28
33
  ```
29
34
 
35
+ (If you specifically want pip: `python3 -m pip install makememe`.)
36
+
37
+ Check the version anytime:
38
+
39
+ ```bash
40
+ meme --version
41
+ ```
42
+
30
43
  ## Usage
31
44
 
32
45
  ```bash
@@ -90,21 +103,19 @@ meme drake "old way" "new way" --json # generate, capture the path
90
103
 
91
104
  ### Claude Code skill
92
105
 
93
- A ready-made skill lives in [`skill/meme/`](skill/meme/SKILL.md). Install it so
94
- Claude Code auto-discovers the tool:
106
+ The package bundles a Claude Code skill. After installing, run one command to
107
+ make Claude Code auto-discover the tool:
95
108
 
96
109
  ```bash
97
- # user-level (all projects)
98
- mkdir -p ~/.claude/skills
99
- cp -r skill/meme ~/.claude/skills/meme
100
-
101
- # or project-level
102
- mkdir -p .claude/skills
103
- cp -r skill/meme .claude/skills/meme
110
+ meme --install-skill # installs into ~/.claude/skills/meme/ (all projects)
111
+ meme --install-skill --project # or into ./.claude/skills/meme/ (this repo only)
104
112
  ```
105
113
 
106
- Then just ask Claude Code things like *"make a drake meme about writing tests"*
107
- and it will call `meme` for you.
114
+ Restart Claude Code, then just ask things like *"make a drake meme about
115
+ writing tests"* and it will call `meme` for you.
116
+
117
+ (Other agents like Codex don't use this skill format — they discover everything
118
+ through `meme --help` and `meme --list`, which already works out of the box.)
108
119
 
109
120
  ## Robustness
110
121
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "makememe"
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  description = "A tiny zero-dependency CLI for generating memes via the free memegen.link API. Agent-friendly."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -14,8 +14,8 @@ The `meme` command must be installed. Check with `meme --list`. If it's missing,
14
14
  install it:
15
15
 
16
16
  ```bash
17
- pip install makememe
18
- # or: uv tool install makememe
17
+ uv tool install makememe
18
+ # or: pipx install makememe
19
19
  ```
20
20
 
21
21
  ## Workflow
@@ -1,6 +1,6 @@
1
1
  """meme - a tiny zero-dependency CLI over the free memegen.link API."""
2
2
 
3
- from .cli import build_url, download, escape, get_templates, main
3
+ from .cli import build_url, download, escape, get_templates, get_version, main
4
4
 
5
- __version__ = "0.1.0"
5
+ __version__ = get_version()
6
6
  __all__ = ["main", "build_url", "escape", "download", "get_templates", "__version__"]
@@ -29,6 +29,35 @@ import urllib.request
29
29
  API = "https://api.memegen.link"
30
30
 
31
31
 
32
+ def get_version():
33
+ """Read the installed package version; fall back gracefully from source."""
34
+ try:
35
+ from importlib.metadata import version
36
+ return version("makememe")
37
+ except Exception:
38
+ return "0.0.0+source"
39
+
40
+
41
+ def install_skill(project=False):
42
+ """Copy the bundled Claude Code skill into ~/.claude/skills/meme/ (or ./.claude
43
+ with project=True). Returns the path written."""
44
+ try:
45
+ from importlib.resources import files
46
+ content = files("memecli").joinpath("SKILL.md").read_text(encoding="utf-8")
47
+ except Exception:
48
+ # running from a source checkout without installed metadata
49
+ here = os.path.dirname(os.path.abspath(__file__))
50
+ with open(os.path.join(here, "SKILL.md"), encoding="utf-8") as f:
51
+ content = f.read()
52
+ base = os.getcwd() if project else os.path.expanduser("~")
53
+ dest_dir = os.path.join(base, ".claude", "skills", "meme")
54
+ os.makedirs(dest_dir, exist_ok=True)
55
+ dest = os.path.join(dest_dir, "SKILL.md")
56
+ with open(dest, "w", encoding="utf-8") as f:
57
+ f.write(content)
58
+ return dest
59
+
60
+
32
61
  # memegen path-segment escaping. Order matters: escape the escape chars first.
33
62
  # Confirmed from the API: space->_, _->__, -->--, ?->~q, newline->~n, "->''
34
63
  # Others use memegen's documented tilde codes; verify with --print-url if unsure.
@@ -96,6 +125,12 @@ def build_parser():
96
125
  "before your lines so it isn't read as a flag:\n"
97
126
  " meme regret --json -- \"-26%\" \"WHY\"",
98
127
  formatter_class=argparse.RawDescriptionHelpFormatter)
128
+ ap.add_argument("--version", action="version",
129
+ version=f"%(prog)s {get_version()}")
130
+ ap.add_argument("--install-skill", action="store_true",
131
+ help="install the Claude Code skill into ~/.claude/skills/meme/ and exit")
132
+ ap.add_argument("--project", action="store_true",
133
+ help="with --install-skill: install into ./.claude/skills/ instead of your home dir")
99
134
  ap.add_argument("template", nargs="?",
100
135
  help="template id (see --list), or any id when using --bg")
101
136
  ap.add_argument("lines", nargs="*", help="text lines, in order")
@@ -119,6 +154,15 @@ def _run(argv=None):
119
154
  ap = build_parser()
120
155
  args = ap.parse_args(argv)
121
156
 
157
+ if args.install_skill:
158
+ try:
159
+ dest = install_skill(project=args.project)
160
+ except Exception as e:
161
+ sys.exit(f"could not install skill: {e}")
162
+ print(dest)
163
+ print("Skill installed. Restart Claude Code to pick it up.", file=sys.stderr)
164
+ return
165
+
122
166
  if args.list:
123
167
  try:
124
168
  list_templates(as_json=args.json)
@@ -5,6 +5,7 @@ No network access required (network paths are covered by --print-url style asser
5
5
  """
6
6
 
7
7
  import io
8
+ import os
8
9
  import unittest
9
10
  from contextlib import redirect_stderr, redirect_stdout
10
11
 
@@ -102,5 +103,29 @@ class TestCrashSafety(unittest.TestCase):
102
103
  cli.download = orig
103
104
 
104
105
 
106
+ class TestNewCommands(unittest.TestCase):
107
+ def test_version_flag_prints_and_exits(self):
108
+ buf = io.StringIO()
109
+ with self.assertRaises(SystemExit) as ctx, redirect_stdout(buf):
110
+ cli.main(["--version"])
111
+ self.assertEqual(ctx.exception.code, 0)
112
+ self.assertIn("meme", buf.getvalue())
113
+
114
+ def test_install_skill_writes_file(self):
115
+ import tempfile
116
+ with tempfile.TemporaryDirectory() as d:
117
+ old = os.environ.get("HOME")
118
+ os.environ["HOME"] = d
119
+ try:
120
+ with redirect_stdout(io.StringIO()), redirect_stderr(io.StringIO()):
121
+ cli.main(["--install-skill"])
122
+ dest = os.path.join(d, ".claude", "skills", "meme", "SKILL.md")
123
+ self.assertTrue(os.path.exists(dest))
124
+ self.assertIn("name: meme", open(dest).read())
125
+ finally:
126
+ if old is not None:
127
+ os.environ["HOME"] = old
128
+
129
+
105
130
  if __name__ == "__main__":
106
131
  unittest.main()
@@ -1,9 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(cp -r /Users/dhruvmehra/Desktop/Claude/Life/tools/meme/skill/meme ~/.claude/skills/meme)",
5
- "Read(//Users/dhruvmehra/.claude/skills/**)",
6
- "Read(//Users/dhruvmehra/.claude/skills/meme/**)"
7
- ]
8
- }
9
- }
File without changes
File without changes