@biggora/claude-plugins 1.1.1 → 1.2.0

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.
Files changed (117) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/README.md +13 -13
  3. package/codex-cli-workspace/iteration-1/benchmark.json +122 -0
  4. package/codex-cli-workspace/iteration-1/eval-1-ci-integration/eval_metadata.json +13 -0
  5. package/codex-cli-workspace/iteration-1/eval-1-ci-integration/with_skill/grading.json +52 -0
  6. package/codex-cli-workspace/iteration-1/eval-1-ci-integration/with_skill/outputs/response.md +163 -0
  7. package/codex-cli-workspace/iteration-1/eval-1-ci-integration/with_skill/timing.json +5 -0
  8. package/codex-cli-workspace/iteration-1/eval-1-ci-integration/without_skill/grading.json +58 -0
  9. package/codex-cli-workspace/iteration-1/eval-1-ci-integration/without_skill/outputs/response.md +151 -0
  10. package/codex-cli-workspace/iteration-1/eval-1-ci-integration/without_skill/timing.json +5 -0
  11. package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/eval_metadata.json +13 -0
  12. package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/grading.json +52 -0
  13. package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/outputs/response.md +86 -0
  14. package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/timing.json +5 -0
  15. package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/grading.json +58 -0
  16. package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/outputs/response.md +164 -0
  17. package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/timing.json +5 -0
  18. package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/eval_metadata.json +13 -0
  19. package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/with_skill/grading.json +52 -0
  20. package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/with_skill/outputs/response.md +130 -0
  21. package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/with_skill/timing.json +5 -0
  22. package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/without_skill/grading.json +64 -0
  23. package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/without_skill/outputs/response.md +209 -0
  24. package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/without_skill/timing.json +5 -0
  25. package/codex-cli-workspace/iteration-1/review.html +1325 -0
  26. package/gemini-cli-workspace/iteration-1/benchmark.json +86 -0
  27. package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/eval_metadata.json +37 -0
  28. package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/with_skill/grading.json +37 -0
  29. package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/with_skill/outputs/response.md +401 -0
  30. package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/with_skill/timing.json +5 -0
  31. package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/without_skill/grading.json +37 -0
  32. package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/without_skill/outputs/response.md +405 -0
  33. package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/without_skill/timing.json +5 -0
  34. package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/eval_metadata.json +37 -0
  35. package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/grading.json +37 -0
  36. package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/outputs/response.md +212 -0
  37. package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/timing.json +5 -0
  38. package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/grading.json +37 -0
  39. package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/outputs/response.md +427 -0
  40. package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/timing.json +5 -0
  41. package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/eval_metadata.json +32 -0
  42. package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/with_skill/grading.json +32 -0
  43. package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/with_skill/outputs/response.md +171 -0
  44. package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/with_skill/timing.json +5 -0
  45. package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/without_skill/grading.json +32 -0
  46. package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/without_skill/outputs/response.md +199 -0
  47. package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/without_skill/timing.json +5 -0
  48. package/gemini-cli-workspace/iteration-1/review.html +1325 -0
  49. package/gemini-cli-workspace/iteration-2/benchmark.json +173 -0
  50. package/gemini-cli-workspace/iteration-2/benchmark.md +28 -0
  51. package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/eval_metadata.json +37 -0
  52. package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/with_skill/grading.json +37 -0
  53. package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/with_skill/outputs/response.md +195 -0
  54. package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/with_skill/timing.json +5 -0
  55. package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/without_skill/grading.json +37 -0
  56. package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/without_skill/outputs/response.md +377 -0
  57. package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/without_skill/timing.json +5 -0
  58. package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/eval_metadata.json +37 -0
  59. package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/with_skill/grading.json +37 -0
  60. package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/with_skill/outputs/response.md +127 -0
  61. package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/with_skill/timing.json +5 -0
  62. package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/without_skill/grading.json +37 -0
  63. package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/without_skill/outputs/response.md +164 -0
  64. package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/without_skill/timing.json +5 -0
  65. package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/eval_metadata.json +32 -0
  66. package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/with_skill/grading.json +32 -0
  67. package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/with_skill/outputs/response.md +91 -0
  68. package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/with_skill/timing.json +5 -0
  69. package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/without_skill/grading.json +32 -0
  70. package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/without_skill/outputs/response.md +112 -0
  71. package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/without_skill/timing.json +5 -0
  72. package/gemini-cli-workspace/iteration-2/eval-viewer.html +1325 -0
  73. package/package.json +1 -1
  74. package/screen-recording-workspace/evals.json +41 -0
  75. package/screen-recording-workspace/iteration-1/benchmark.json +102 -0
  76. package/screen-recording-workspace/iteration-1/eval-0-fullscreen/eval_metadata.json +31 -0
  77. package/screen-recording-workspace/iteration-1/eval-0-fullscreen/with_skill/grading.json +11 -0
  78. package/screen-recording-workspace/iteration-1/eval-0-fullscreen/with_skill/outputs/demo.mp4 +0 -0
  79. package/screen-recording-workspace/iteration-1/eval-0-fullscreen/with_skill/timing.json +5 -0
  80. package/screen-recording-workspace/iteration-1/eval-0-fullscreen/without_skill/grading.json +11 -0
  81. package/screen-recording-workspace/iteration-1/eval-0-fullscreen/without_skill/outputs/demo.mp4 +0 -0
  82. package/screen-recording-workspace/iteration-1/eval-0-fullscreen/without_skill/timing.json +5 -0
  83. package/screen-recording-workspace/iteration-1/eval-1-region-audio/eval_metadata.json +31 -0
  84. package/screen-recording-workspace/iteration-1/eval-1-region-audio/with_skill/grading.json +11 -0
  85. package/screen-recording-workspace/iteration-1/eval-1-region-audio/with_skill/outputs/region_capture.mp4 +0 -0
  86. package/screen-recording-workspace/iteration-1/eval-1-region-audio/with_skill/timing.json +5 -0
  87. package/screen-recording-workspace/iteration-1/eval-1-region-audio/without_skill/grading.json +11 -0
  88. package/screen-recording-workspace/iteration-1/eval-1-region-audio/without_skill/outputs/region_capture.mp4 +0 -0
  89. package/screen-recording-workspace/iteration-1/eval-1-region-audio/without_skill/timing.json +5 -0
  90. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/eval_metadata.json +31 -0
  91. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/with_skill/grading.json +11 -0
  92. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/with_skill/outputs/fallback_recording.mp4 +0 -0
  93. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/with_skill/timing.json +5 -0
  94. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/without_skill/grading.json +11 -0
  95. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/without_skill/outputs/fallback_recording.mp4 +0 -0
  96. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/without_skill/outputs/record_screen.py +67 -0
  97. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/without_skill/timing.json +5 -0
  98. package/screen-recording-workspace/iteration-1/review.html +1325 -0
  99. package/src/skills/codex-cli/SKILL.md +21 -11
  100. package/src/skills/codex-cli/evals/evals.json +47 -0
  101. package/src/skills/gemini-cli/SKILL.md +27 -13
  102. package/src/skills/gemini-cli/evals/evals.json +46 -0
  103. package/src/skills/gemini-cli/references/commands.md +21 -14
  104. package/src/skills/gemini-cli/references/configuration.md +23 -18
  105. package/src/skills/gemini-cli/references/headless-and-scripting.md +7 -17
  106. package/src/skills/gemini-cli/references/mcp-and-extensions.md +12 -6
  107. package/src/skills/notebook-lm/SKILL.md +1 -1
  108. package/src/skills/screen-recording/SKILL.md +243 -213
  109. package/src/skills/screen-recording/references/design-patterns.md +4 -2
  110. package/src/skills/screen-recording/references/ffmpeg-recording.md +473 -0
  111. package/src/skills/screen-recording/references/{approach1-programmatic.md → programmatic-generation.md} +45 -22
  112. package/src/skills/screen-recording/references/python-fallback.md +222 -0
  113. package/src/skills/tm-search/SKILL.md +242 -106
  114. package/src/skills/tm-search/evals/evals.json +23 -0
  115. package/src/skills/tm-search/references/scraping-fallback.md +60 -95
  116. package/src/skills/tm-search/scripts/tm_search.py +453 -375
  117. package/src/skills/screen-recording/references/approach2-xvfb.md +0 -232
@@ -0,0 +1,222 @@
1
+ # Python Fallback — Screen Recording without FFmpeg
2
+
3
+ Use this approach when FFmpeg is not installed. Captures screenshots in a loop using `mss` and assembles them into MP4 with OpenCV.
4
+
5
+ > **Limitations**: No audio capture, no cursor capture, lower FPS than FFmpeg, larger CPU usage.
6
+
7
+ ## Dependencies
8
+
9
+ ```bash
10
+ pip install mss opencv-python numpy
11
+ ```
12
+
13
+ ## Full Working Template
14
+
15
+ ```python
16
+ #!/usr/bin/env python3
17
+ """
18
+ Screen recorder using Python only (mss + OpenCV).
19
+ No FFmpeg required. Cross-platform: Windows, macOS, Linux.
20
+
21
+ Limitations:
22
+ - No audio capture
23
+ - No cursor capture
24
+ - Lower FPS (~15-25 depending on resolution and hardware)
25
+ """
26
+
27
+ import time, os, sys
28
+ import numpy as np
29
+ import cv2
30
+ import mss
31
+
32
+ def record_screen(output="recording.mp4", duration=30, fps=20,
33
+ region=None, monitor=1):
34
+ """
35
+ Record the screen using mss + OpenCV.
36
+
37
+ Args:
38
+ output: Output file path (.mp4)
39
+ duration: Recording duration in seconds
40
+ fps: Target frames per second (actual may be lower)
41
+ region: Tuple (x, y, w, h) for specific region, or None for full screen
42
+ monitor: Monitor number (1 = primary, 2 = secondary, etc.)
43
+ """
44
+ with mss.mss() as sct:
45
+ # Define capture area
46
+ if region:
47
+ x, y, w, h = region
48
+ capture_area = {"left": x, "top": y, "width": w, "height": h}
49
+ else:
50
+ mon = sct.monitors[monitor] # monitors[0] is "all monitors"
51
+ capture_area = {
52
+ "left": mon["left"],
53
+ "top": mon["top"],
54
+ "width": mon["width"],
55
+ "height": mon["height"],
56
+ }
57
+
58
+ width = capture_area["width"]
59
+ height = capture_area["height"]
60
+
61
+ # Setup video writer
62
+ # Try H264 first, fall back to mp4v
63
+ fourcc = cv2.VideoWriter_fourcc(*"mp4v")
64
+ writer = cv2.VideoWriter(output, fourcc, fps, (width, height))
65
+
66
+ if not writer.isOpened():
67
+ raise RuntimeError(f"Failed to open VideoWriter for {output}")
68
+
69
+ print(f"Recording {width}x{height} @ {fps}fps → {output}")
70
+ print(f"Duration: {duration}s (Ctrl+C to stop early)")
71
+
72
+ frame_count = 0
73
+ frame_interval = 1.0 / fps
74
+ start_time = time.time()
75
+
76
+ try:
77
+ while True:
78
+ frame_start = time.time()
79
+ elapsed = frame_start - start_time
80
+
81
+ if elapsed >= duration:
82
+ break
83
+
84
+ # Capture screenshot
85
+ screenshot = sct.grab(capture_area)
86
+ # Convert BGRA → BGR (OpenCV format)
87
+ frame = np.array(screenshot)[:, :, :3]
88
+ # mss returns BGRA, but numpy slice gives BGR which is what OpenCV expects
89
+
90
+ writer.write(frame)
91
+ frame_count += 1
92
+
93
+ # Frame rate control
94
+ frame_time = time.time() - frame_start
95
+ sleep_time = frame_interval - frame_time
96
+ if sleep_time > 0:
97
+ time.sleep(sleep_time)
98
+
99
+ except KeyboardInterrupt:
100
+ print("\nStopped by user.")
101
+
102
+ writer.release()
103
+ actual_duration = time.time() - start_time
104
+ actual_fps = frame_count / actual_duration if actual_duration > 0 else 0
105
+ size = os.path.getsize(output) if os.path.exists(output) else 0
106
+
107
+ print(f"Saved: {output}")
108
+ print(f" Frames: {frame_count}")
109
+ print(f" Duration: {actual_duration:.1f}s")
110
+ print(f" Actual FPS: {actual_fps:.1f}")
111
+ print(f" Size: {size:,} bytes ({size // 1024} KB)")
112
+
113
+ return output
114
+
115
+
116
+ def list_monitors():
117
+ """List available monitors and their dimensions."""
118
+ with mss.mss() as sct:
119
+ for i, mon in enumerate(sct.monitors):
120
+ if i == 0:
121
+ print(f" [0] All monitors combined: {mon['width']}x{mon['height']}")
122
+ else:
123
+ print(f" [{i}] Monitor {i}: {mon['width']}x{mon['height']} at ({mon['left']}, {mon['top']})")
124
+
125
+
126
+ if __name__ == "__main__":
127
+ import argparse
128
+ parser = argparse.ArgumentParser(description="Record screen (Python only)")
129
+ parser.add_argument("output", nargs="?", default="recording.mp4")
130
+ parser.add_argument("--duration", "-t", type=int, default=30)
131
+ parser.add_argument("--fps", type=int, default=20)
132
+ parser.add_argument("--region", "-r", type=str, default=None,
133
+ help="x,y,w,h (e.g. 100,200,800,600)")
134
+ parser.add_argument("--monitor", "-m", type=int, default=1,
135
+ help="Monitor number (default: 1 = primary)")
136
+ parser.add_argument("--list-monitors", action="store_true",
137
+ help="List available monitors and exit")
138
+ args = parser.parse_args()
139
+
140
+ if args.list_monitors:
141
+ list_monitors()
142
+ sys.exit(0)
143
+
144
+ region = tuple(map(int, args.region.split(","))) if args.region else None
145
+ record_screen(args.output, args.duration, args.fps, region, args.monitor)
146
+ ```
147
+
148
+ ## Multi-Monitor Support
149
+
150
+ ```python
151
+ with mss.mss() as sct:
152
+ # sct.monitors[0] = virtual screen (all monitors combined)
153
+ # sct.monitors[1] = primary monitor
154
+ # sct.monitors[2] = secondary monitor, etc.
155
+
156
+ # Record specific monitor
157
+ mon = sct.monitors[2] # secondary monitor
158
+ screenshot = sct.grab(mon)
159
+
160
+ # Record all monitors as one image
161
+ screenshot = sct.grab(sct.monitors[0])
162
+ ```
163
+
164
+ ## Performance Tips
165
+
166
+ ### Reduce resolution for higher FPS
167
+ ```python
168
+ # Capture at half resolution, then resize for writing
169
+ frame = np.array(sct.grab(capture_area))[:, :, :3]
170
+ small = cv2.resize(frame, (width // 2, height // 2))
171
+ writer.write(small)
172
+ ```
173
+
174
+ ### Use JPEG compression for intermediate frames
175
+ ```python
176
+ # For very long recordings, write frames as JPEG to reduce memory/disk
177
+ _, buf = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 85])
178
+ # Later decode for video assembly
179
+ ```
180
+
181
+ ### Codec alternatives
182
+ ```python
183
+ # mp4v — most compatible, larger files
184
+ fourcc = cv2.VideoWriter_fourcc(*"mp4v")
185
+
186
+ # XVID — better compression (requires ffmpeg/xvid codec)
187
+ fourcc = cv2.VideoWriter_fourcc(*"XVID")
188
+ # Output file should be .avi for XVID
189
+
190
+ # H264 — best compression (may not be available everywhere)
191
+ fourcc = cv2.VideoWriter_fourcc(*"avc1") # macOS
192
+ fourcc = cv2.VideoWriter_fourcc(*"H264") # Linux
193
+ ```
194
+
195
+ ## Adding Audio After Recording
196
+
197
+ If you need audio, record video with this script, then merge audio separately:
198
+
199
+ ```python
200
+ import subprocess
201
+
202
+ def add_audio_to_video(video_path, audio_path, output_path):
203
+ """Merge separately-recorded audio into the video."""
204
+ subprocess.run([
205
+ "ffmpeg", "-i", video_path, "-i", audio_path,
206
+ "-c:v", "copy", "-c:a", "aac", "-b:a", "128k",
207
+ "-shortest", output_path, "-y", "-loglevel", "quiet"
208
+ ], check=True)
209
+ ```
210
+
211
+ ## Comparison: Python Fallback vs FFmpeg
212
+
213
+ | Feature | Python (mss+cv2) | FFmpeg Native |
214
+ |---------|-------------------|---------------|
215
+ | **FPS** | 15-25 (resolution-dependent) | 30-60 |
216
+ | **Audio** | No | Yes |
217
+ | **Cursor** | No | Yes |
218
+ | **CPU Usage** | Higher | Lower |
219
+ | **Dependencies** | pip only | System FFmpeg |
220
+ | **File Size** | Larger (mp4v codec) | Smaller (H.264) |
221
+ | **Setup** | Easier | Requires FFmpeg install |
222
+ | **Reliability** | Good | Excellent |
@@ -16,123 +16,283 @@ description: >
16
16
 
17
17
  This skill enables an agent to search, validate, and analyze US trademarks via USPTO APIs.
18
18
 
19
- ## API Overview
19
+ ## Important: API Access Reality
20
+
21
+ The USPTO trademark search system (`tmsearch.uspto.gov`) uses **AWS WAF bot protection** on its
22
+ Elasticsearch search backend. Direct keyword search via HTTP is **not possible** without browser
23
+ automation. However, the **TSDR details API** (case lookup by serial number) works without
24
+ authentication and returns JSON.
25
+
26
+ For **keyword search**, use one of these approaches:
27
+ 1. **Playwright/browser automation** — drive the tmsearch.uspto.gov web UI (most reliable)
28
+ 2. **Third-party API** — RapidAPI USPTO wrapper (requires API key, simpler to use)
20
29
 
21
- There are **two main API approaches** choose based on needs:
30
+ For **case status lookup by serial number**, the built-in TSDR API works directly.
22
31
 
23
- | Use Case | API |
32
+ ## API Overview
33
+
34
+ | Use Case | Approach |
24
35
  |---|---|
25
- | Keyword/name text search, availability check | **tmsearch.uspto.gov** (web backend) |
26
- | Status lookup by serial/registration number | **tsdrapi.uspto.gov** (official TSDR API) |
27
- | Batch/programmatic word validation | **tmsearch.uspto.gov** or RapidAPI wrapper |
36
+ | Keyword/name text search | Browser automation on tmsearch.uspto.gov **or** RapidAPI |
37
+ | Case status by serial number | TSDR Details API (no auth, JSON) |
38
+ | Batch keyword validation | Browser automation + delays **or** RapidAPI |
39
+ | Case documents / images | TSDR API (requires API key since Oct 2024) |
28
40
 
29
41
  ---
30
42
 
31
- ## API 1: tmsearch.uspto.gov (New Trademark SearchNo API key required)
43
+ ## API 1: TSDR Details API (No auth requiredJSON)
32
44
 
33
- The USPTO replaced TESS in November 2023 with a new search system at `https://tmsearch.uspto.gov`.
34
- This is a **web application** but its backend can be called directly.
45
+ Look up trademark case details **by serial number**. This endpoint is hosted on the tmsearch
46
+ site and returns JSON without authentication.
35
47
 
36
- ### Search Endpoint
48
+ ### Endpoint
37
49
 
38
50
  ```
39
- POST https://tmsearch.uspto.gov/search/keyword
40
- Content-Type: application/json
51
+ GET https://tmsearch.uspto.gov/tsdr-api-v1-0-0/tsdr-api?serialNumber={SERIAL_NUMBER}
41
52
  ```
42
53
 
43
- **Request body:**
44
- ```json
45
- {
46
- "keyword": "APPLE",
47
- "searchType": "1",
48
- "statusType": "A",
49
- "pluralVariants": false,
50
- "start": 0,
51
- "rows": 25
52
- }
53
- ```
54
-
55
- **Parameters:**
56
- - `keyword` — the word/phrase to search (required)
57
- - `searchType` — `"1"` = basic keyword, `"2"` = design code, `"3"` = owner name
58
- - `statusType` — `"A"` = active/live only, `"D"` = dead only, `""` = all
59
- - `pluralVariants` — `true` to include plural variations
60
- - `start` — pagination offset (0-indexed)
61
- - `rows` — results per page (max 500)
62
-
63
- ### Alternative: GET search
54
+ Serial numbers are 8 digits (no dashes). Example:
64
55
 
65
- ```
66
- GET https://tmsearch.uspto.gov/search/keyword?keyword=APPLE&statusType=A&rows=25
56
+ ```bash
57
+ curl -s "https://tmsearch.uspto.gov/tsdr-api-v1-0-0/tsdr-api?serialNumber=78787878" \
58
+ -H "Accept: application/json"
67
59
  ```
68
60
 
69
- ### Response Fields
61
+ ### Response Structure
70
62
 
71
63
  ```json
72
64
  {
73
- "totalFound": 1247,
74
- "trademarks": [
65
+ "metadata": {
66
+ "caseStatus": "Abandoned because the applicant failed to respond...",
67
+ "statusDate": "2007-09-25",
68
+ "owners": [
69
+ {
70
+ "ipInfo": {
71
+ "name": "OWNER NAME",
72
+ "legalEntity": "3",
73
+ "contactAddress": {
74
+ "mailingAddresses": [
75
+ {
76
+ "cityName": "City",
77
+ "geographicRegionName": "STATE",
78
+ "postalCode": "12345",
79
+ "countryName": "UNITED STATES OF AMERICA"
80
+ }
81
+ ]
82
+ },
83
+ "citizenship": {
84
+ "countryName": "UNITED STATES OF AMERICA",
85
+ "geographicRegionName": "STATE"
86
+ }
87
+ }
88
+ }
89
+ ],
90
+ "attorney": {
91
+ "ipInfo": { "name": "Attorney Name" }
92
+ },
93
+ "correspondent": {
94
+ "ipInfo": {
95
+ "name": "Correspondent Name",
96
+ "contactAddress": { "mailingAddresses": [...], "electronicAddresses": [...] }
97
+ }
98
+ },
99
+ "markDetails": {
100
+ "isStandardCharClaimed": false
101
+ },
102
+ "tm5Status": {
103
+ "tm5StatusDescription": "...",
104
+ "tm5StatusCode": "10",
105
+ "tm5StatusDescriptor": "DEAD/APPLICATION/Refused/Dismissed or Invalidated",
106
+ "tm5LiveDead": "dead"
107
+ },
108
+ "classes": [
109
+ { "classNumber": "009", "firstUseAnywhereDate": null, "firstUseInCommerceDate": null }
110
+ ],
111
+ "docketNumber": "..."
112
+ },
113
+ "maintenance": {},
114
+ "prosecutionHistory": [
75
115
  {
76
- "serialNumber": "75123456",
77
- "registrationNumber": "2345678",
78
- "wordMark": "APPLE",
79
- "status": "Live/Registered",
80
- "statusCode": "A",
81
- "filingDate": "1997-03-15",
82
- "registrationDate": "1999-08-17",
83
- "owner": "Apple Inc.",
84
- "ownerAddress": "Cupertino, CALIFORNIA, UNITED STATES",
85
- "internationalClassification": ["009", "042"],
86
- "goodsServices": "Computers, computer software...",
87
- "attorney": "...",
88
- "markDrawingCode": "4"
116
+ "historyDate": "2026-03-06",
117
+ "historyDescription": "Amended Drawing",
118
+ "documentId": "https://tsdr.uspto.gov/documentviewer?caseId=sn78787878&docId=..."
89
119
  }
90
- ]
120
+ ],
121
+ "assignments": [...],
122
+ "proceedings": [...],
123
+ "international": {}
91
124
  }
92
125
  ```
93
126
 
94
- > **Note:** The tmsearch.uspto.gov backend API is not officially documented. If the above endpoint
95
- > returns errors, fall back to scraping `https://tmsearch.uspto.gov/search/search-information` or
96
- > use the TSDR API below. See `references/scraping-fallback.md` for the web scraping approach.
127
+ ### Key Response Fields
97
128
 
98
- ---
129
+ | Path | Description |
130
+ |---|---|
131
+ | `metadata.caseStatus` | Human-readable current status |
132
+ | `metadata.statusDate` | Date of last status change |
133
+ | `metadata.owners[].ipInfo.name` | Owner/applicant name |
134
+ | `metadata.owners[].ipInfo.contactAddress` | Owner address |
135
+ | `metadata.attorney.ipInfo.name` | Attorney of record |
136
+ | `metadata.tm5Status.tm5LiveDead` | `"live"` or `"dead"` |
137
+ | `metadata.tm5Status.tm5StatusDescriptor` | Structured status (e.g., `"LIVE/REGISTRATION/Registered"`) |
138
+ | `metadata.classes[].classNumber` | Nice Classification codes |
139
+ | `metadata.markDetails.isStandardCharClaimed` | Whether it's a standard character mark |
140
+ | `prosecutionHistory[]` | Timeline of case events with document links |
141
+
142
+ > **Note:** This API does NOT return the word mark text itself. To get the mark text, you need
143
+ > the keyword search approach (browser automation or RapidAPI).
99
144
 
100
- ## API 2: TSDR API (Official — requires API key for bulk use)
145
+ ---
101
146
 
102
- Looks up trademark **by serial number or registration number**. Returns full case status, documents.
147
+ ## API 2: TSDR Bulk API (Requires API key XML)
103
148
 
104
- **Base URL:** `https://tsdrapi.uspto.gov/ts/cd/`
149
+ The original TSDR API at `tsdrapi.uspto.gov` now **requires an API key for all requests** (changed
150
+ October 2024). Register at https://account.uspto.gov/api-manager/.
105
151
 
106
- ### Key Endpoints
152
+ ### Endpoints
107
153
 
108
154
  ```bash
109
- # Case status by serial number (HTML)
110
- GET https://tsdrapi.uspto.gov/ts/cd/casestatus/sn{SERIAL_NUMBER}/content.html
111
-
112
- # Case status as XML (machine-readable)
155
+ # Case status as XML (requires API key)
113
156
  GET https://tsdrapi.uspto.gov/ts/cd/casestatus/sn{SERIAL_NUMBER}/info.xml
157
+ Header: USPTO-API-KEY: YOUR_KEY
158
+
159
+ # Case status as HTML
160
+ GET https://tsdrapi.uspto.gov/ts/cd/casestatus/sn{SERIAL_NUMBER}/content.html
161
+ Header: USPTO-API-KEY: YOUR_KEY
114
162
 
115
- # Case status by registration number
163
+ # By registration number
116
164
  GET https://tsdrapi.uspto.gov/ts/cd/casestatus/rn{REG_NUMBER}/info.xml
117
165
 
118
- # Case documents as PDF bundle
166
+ # Case documents PDF bundle
119
167
  GET https://tsdrapi.uspto.gov/ts/cd/casedocs/bundle.pdf?sn={SERIAL_NUMBER}
120
168
 
121
169
  # Raw trademark image
122
170
  GET https://tsdrapi.uspto.gov/ts/cd/rawImage/{SERIAL_NUMBER}
123
171
  ```
124
172
 
125
- ### API Key (required for bulk/automated use)
173
+ Rate limit: **60 requests/minute per API key**.
174
+
175
+ ---
176
+
177
+ ## Keyword Search via Browser Automation (Recommended)
178
+
179
+ Since the tmsearch.uspto.gov search backend is protected by AWS WAF, use Playwright to drive
180
+ the web UI and intercept API responses.
181
+
182
+ **Important:** The AWS WAF bot detection is non-deterministic — headless browsers are blocked
183
+ intermittently. Use anti-detection settings and retries. Even with these, keyword search may
184
+ fail occasionally. The RapidAPI wrapper (below) is more reliable if you need consistent results.
185
+
186
+ ```python
187
+ from playwright.sync_api import sync_playwright
188
+
189
+ def search_trademark(keyword: str, max_results: int = 25) -> dict:
190
+ """Search USPTO trademarks by keyword using browser automation."""
191
+ results = {"totalFound": 0, "trademarks": []}
192
+
193
+ with sync_playwright() as p:
194
+ # Anti-detection settings to bypass AWS WAF
195
+ browser = p.chromium.launch(
196
+ headless=True,
197
+ args=["--disable-blink-features=AutomationControlled"],
198
+ )
199
+ context = browser.new_context(
200
+ user_agent=(
201
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
202
+ "AppleWebKit/537.36 (KHTML, like Gecko) "
203
+ "Chrome/120.0.0.0 Safari/537.36"
204
+ ),
205
+ viewport={"width": 1920, "height": 1080},
206
+ )
207
+ page = context.new_page()
208
+ page.add_init_script(
209
+ 'Object.defineProperty(navigator, "webdriver", {get: () => undefined})'
210
+ )
211
+
212
+ # Intercept the Elasticsearch API responses
213
+ def handle_response(response):
214
+ if "prod-stage" in response.url and response.status == 200:
215
+ try:
216
+ data = response.json()
217
+ if isinstance(data, dict) and "hits" in data:
218
+ hits = data.get("hits", {})
219
+ total = hits.get("total", {})
220
+ results["totalFound"] = total.get("value", 0) if isinstance(total, dict) else total
221
+ for hit in hits.get("hits", [])[:max_results]:
222
+ results["trademarks"].append(hit.get("_source", {}))
223
+ except Exception:
224
+ pass
225
+
226
+ page.on("response", handle_response)
227
+ page.goto("https://tmsearch.uspto.gov/search/search-information", timeout=30000)
228
+ page.wait_for_load_state("networkidle")
229
+ page.wait_for_timeout(2000) # Wait for WAF challenge to resolve
230
+
231
+ # Type keyword and submit
232
+ search_input = page.locator('input[type="text"]').first
233
+ search_input.fill(keyword.upper(), timeout=10000)
234
+ search_input.press("Enter")
235
+
236
+ # Wait for results (longer wait needed for WAF + async results)
237
+ page.wait_for_timeout(6000)
238
+
239
+ browser.close()
240
+
241
+ return results
242
+ ```
243
+
244
+ ### Install Playwright
245
+
246
+ ```bash
247
+ pip install playwright
248
+ playwright install chromium
249
+ ```
250
+
251
+ ---
252
+
253
+ ## Keyword Search via RapidAPI (Alternative — requires API key)
254
+
255
+ If browser automation is not practical, the RapidAPI unofficial USPTO wrapper provides reliable
256
+ keyword search.
126
257
 
127
- - Register at: https://account.uspto.gov/api-manager/
128
- - Add key as header: `USPTO-API-KEY: YOUR_KEY_HERE`
129
- - Rate limit: **60 requests/minute per API key**
258
+ **Sign up:** https://rapidapi.com/pentium10/api/uspto-trademark
130
259
 
131
260
  ```bash
132
- curl -H "USPTO-API-KEY: YOUR_KEY" \
133
- "https://tsdrapi.uspto.gov/ts/cd/casestatus/sn78787878/info.xml"
261
+ # Keyword search (active marks)
262
+ curl "https://uspto-trademark.p.rapidapi.com/v1/trademarkSearch/APPLE/active" \
263
+ -H "x-rapidapi-host: uspto-trademark.p.rapidapi.com" \
264
+ -H "x-rapidapi-key: YOUR_RAPIDAPI_KEY"
265
+
266
+ # Availability check
267
+ curl "https://uspto-trademark.p.rapidapi.com/v1/trademarkAvailable/CLOUDPEAK" \
268
+ -H "x-rapidapi-host: uspto-trademark.p.rapidapi.com" \
269
+ -H "x-rapidapi-key: YOUR_RAPIDAPI_KEY"
270
+
271
+ # Search by owner
272
+ curl "https://uspto-trademark.p.rapidapi.com/v1/ownerSearch/Apple%20Inc/" \
273
+ -H "x-rapidapi-host: uspto-trademark.p.rapidapi.com" \
274
+ -H "x-rapidapi-key: YOUR_RAPIDAPI_KEY"
275
+
276
+ # Lookup by serial number
277
+ curl "https://uspto-trademark.p.rapidapi.com/v1/serialSearch/78787878" \
278
+ -H "x-rapidapi-host: uspto-trademark.p.rapidapi.com" \
279
+ -H "x-rapidapi-key: YOUR_RAPIDAPI_KEY"
280
+
281
+ # Batch search (POST)
282
+ curl -X POST "https://uspto-trademark.p.rapidapi.com/v1/batchTrademarkSearch" \
283
+ -H "x-rapidapi-host: uspto-trademark.p.rapidapi.com" \
284
+ -H "x-rapidapi-key: YOUR_RAPIDAPI_KEY" \
285
+ -H "Content-Type: application/json" \
286
+ -d '{"keywords": ["CLOUDPEAK", "SKYBRIDGE", "NEONPULSE"]}'
134
287
  ```
135
288
 
289
+ RapidAPI endpoints:
290
+ - `/v1/trademarkSearch/{keyword}/{status}` — keyword search (`active`, `dead`, or `all`)
291
+ - `/v1/trademarkAvailable/{keyword}` — simple yes/no availability
292
+ - `/v1/ownerSearch/{owner_name}/{postcode}` — search by owner
293
+ - `/v1/serialSearch/{serial_number}` — lookup by serial number
294
+ - `/v1/batchTrademarkSearch` — multiple keywords (POST)
295
+
136
296
  ---
137
297
 
138
298
  ## CLI Tool Implementation (tm-search)
@@ -142,11 +302,11 @@ When building the `tm-search` command-line tool, follow this structure:
142
302
  ### Core Commands
143
303
 
144
304
  ```bash
145
- tm-search keyword <word> # Search by keyword
305
+ tm-search keyword <word> # Search by keyword (uses Playwright)
146
306
  tm-search keyword <word> --status=A # Active trademarks only
147
307
  tm-search keyword <word> --status=D # Dead trademarks only
148
308
  tm-search available <word> # Check if word is available (not registered live)
149
- tm-search status <serial_number> # Lookup by serial number
309
+ tm-search status <serial_number> # Lookup by serial number (uses TSDR API)
150
310
  tm-search batch <word1,word2,...> # Check multiple words
151
311
  tm-search validate <file.txt> # Validate words from a file (one per line)
152
312
  ```
@@ -177,58 +337,34 @@ Top matches:
177
337
  A keyword is considered **AVAILABLE** if there are zero live/active marks with exact OR confusingly
178
338
  similar text. The agent should:
179
339
 
180
- 1. Search exact keyword with `statusType=A`
340
+ 1. Search exact keyword (via Playwright or RapidAPI)
181
341
  2. If results > 0 → **LIKELY REGISTERED** — show matches
182
342
  3. If results == 0 → **LIKELY AVAILABLE** — note this is not legal advice
183
343
  4. Always caveat: suggest professional trademark attorney review before filing
184
344
 
185
- ```python
186
- def check_availability(keyword):
187
- results = search_trademark(keyword, status="A")
188
- if results["totalFound"] == 0:
189
- return "LIKELY AVAILABLE"
190
- else:
191
- return f"LIKELY TAKEN ({results['totalFound']} active marks)"
192
- ```
193
-
194
345
  ---
195
346
 
196
347
  ## Batch Validation
197
348
 
198
- For validating a list of keywords (e.g., from a CSV or text file):
199
-
200
- ```python
201
- words = ["CloudPeak", "SkyBridge", "NeonPulse"]
202
- results = []
203
- for word in words:
204
- r = search_trademark(word.upper(), status="A")
205
- results.append({
206
- "keyword": word,
207
- "status": "AVAILABLE" if r["totalFound"] == 0 else "TAKEN",
208
- "count": r["totalFound"]
209
- })
210
- # Output as table or CSV
211
- ```
212
-
213
- Rate-limit: Add 0.5–1s delay between requests to avoid throttling.
349
+ For validating a list of keywords, add a 2–3 second delay between Playwright searches to avoid
350
+ triggering rate limits. With RapidAPI, 0.5–1s delay is sufficient.
214
351
 
215
352
  ---
216
353
 
217
354
  ## Implementation Notes
218
355
 
219
- - The `tmsearch.uspto.gov` endpoint is a **public government site** — no auth required for search
356
+ - The `tmsearch.uspto.gov` search backend is protected by **AWS WAF** — direct HTTP keyword search does not work
357
+ - The TSDR Details API (`/tsdr-api-v1-0-0/tsdr-api?serialNumber=...`) works without auth and returns JSON
358
+ - The old TSDR XML API (`tsdrapi.uspto.gov`) requires an **API key for all requests** since October 2024
220
359
  - Always **uppercase** keywords before searching (USPTO stores marks in uppercase)
221
- - Include `User-Agent` header to avoid bot detection: `"Mozilla/5.0 (compatible; tm-search/1.0)"`
222
- - For `pluralVariants=true`, USPTO auto-expands COFFEE → COFFEES, etc.
223
360
  - International Classification (Nice Classification) codes define goods/services category
224
361
  - Serial numbers are 8 digits; Registration numbers are 7 digits
225
362
 
226
363
  ## Reference Files
227
364
 
228
365
  - `references/field-guide.md` — Full field descriptions and Nice Classification codes
229
- - `references/scraping-fallback.md` — Web scraping approach if API is unavailable
366
+ - `references/scraping-fallback.md` — Browser automation fallback details
230
367
  - `scripts/tm_search.py` — Ready-to-use Python implementation
231
- - `scripts/tm_validate.py` — Batch validation script
232
368
 
233
369
  ---
234
370
 
@@ -237,4 +373,4 @@ Rate-limit: Add 0.5–1s delay between requests to avoid throttling.
237
373
  Always include when providing availability results:
238
374
  > "This is a preliminary search only. Trademark availability is complex and depends on many
239
375
  > factors including similar marks, geographic use, and goods/services classification. Consult a
240
- > licensed trademark attorney before filing."
376
+ > licensed trademark attorney before filing."