feat: add ClawdHub skill registry as import source#183
Conversation
…ation - Add clawdhub-helper.sh: Playwright-based SKILL.md fetcher for ClawdHub SPA - Add clawdhub: prefix and clawdhub.com URL detection to add-skill-helper.sh - Import caldav-calendar skill (v1.0.1) from ClawdHub to tools/productivity/ - Import proxmox-full skill (v1.0.0) from ClawdHub to services/hosting/ - Add productivity and virtualization category detection - Update add-skill documentation with ClawdHub examples and architecture
Summary of ChangesHello @marcusquinn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
WalkthroughThis PR integrates ClawdHub as a skill source into the aidevops framework alongside GitHub. It introduces a new helper script for fetching skill content via ClawdHub APIs and Playwright automation, extends the add-skill workflow to recognize and process ClawdHub imports, registers two new skills in the registry, and adds corresponding documentation. Changes
Sequence DiagramsequenceDiagram
participant User
participant AddSkill as add-skill-helper.sh
participant ClawdHub as clawdhub-helper.sh
participant API as ClawdHub API
participant Playwright
participant Registry as skill-sources.json
User->>AddSkill: add-skill clawdhub:slug
AddSkill->>AddSkill: parse_url (detect ClawdHub)
AddSkill->>ClawdHub: fetch slug --output /tmp
ClawdHub->>API: resolve owner/slug
API-->>ClawdHub: owner info
ClawdHub->>Playwright: launch temp project + script
Playwright->>Playwright: navigate to skill page
Playwright->>Playwright: extract HTML → markdown
Playwright-->>ClawdHub: SKILL.md content
ClawdHub-->>AddSkill: SKILL.md file
AddSkill->>AddSkill: convert to aidevops format
AddSkill->>AddSkill: check for conflicts
AddSkill->>Registry: register_skill (upstream: clawdhub)
Registry-->>AddSkill: ack
AddSkill-->>User: import complete
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Multiple new scripts with substantial logic density, API integration patterns, Playwright automation setup, bash error handling edge cases, and integration points across skill import workflow require careful review of security posture, CLI argument parsing, and shell script best practices. Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
🔍 Code Quality Report�[0;35m[MONITOR]�[0m Code Review Monitoring Report �[0;34m[INFO]�[0m Latest Quality Status: �[0;34m[INFO]�[0m Recent monitoring activity: 📈 Current Quality Metrics
Generated on: Sat Jan 24 22:28:36 UTC 2026 Generated by AI DevOps Framework Code Review Monitoring |
There was a problem hiding this comment.
Code Review
This pull request introduces ClawdHub as a new skill import source, which is a great addition. The implementation includes a new helper script, clawdhub-helper.sh, that cleverly uses Playwright for web scraping due to the lack of a public API for skill content. The main add-skill-helper.sh script is updated to integrate this new source. The changes are well-structured and the documentation has been updated accordingly. My review focuses on improving the robustness of the new helper script by making error handling more transparent and promoting consistency by using jq for JSON processing, which is already a project dependency.
|
|
||
| # Install playwright and run the fetch script | ||
| log_info "Installing Playwright (temporary)..." | ||
| if (cd "$pw_dir" && npm install --silent 2>/dev/null && npx playwright install chromium --with-deps 2>/dev/null); then |
There was a problem hiding this comment.
Suppressing all error output from npm install and npx playwright install with 2>/dev/null can make debugging very difficult if the installation fails. It's better to let error messages be displayed so that the user can understand and fix any issues with their Node.js environment, network, or permissions.
| if (cd "$pw_dir" && npm install --silent 2>/dev/null && npx playwright install chromium --with-deps 2>/dev/null); then | |
| if (cd "$pw_dir" && npm install --silent && npx playwright install chromium --with-deps); then |
| display_name=$(echo "$api_response" | python3 -c "import sys,json; print(json.load(sys.stdin).get('skill',{}).get('displayName',''))" 2>/dev/null) | ||
| summary=$(echo "$api_response" | python3 -c "import sys,json; print(json.load(sys.stdin).get('skill',{}).get('summary',''))" 2>/dev/null) | ||
| owner_handle=$(echo "$api_response" | python3 -c "import sys,json; print(json.load(sys.stdin).get('owner',{}).get('handle',''))" 2>/dev/null) | ||
| version=$(echo "$api_response" | python3 -c "import sys,json; print(json.load(sys.stdin).get('latestVersion',{}).get('version',''))" 2>/dev/null) |
There was a problem hiding this comment.
These repeated calls to python3 to parse the JSON response are inefficient as each call starts a new Python interpreter. Since jq is a dependency of this project, you can use it to extract all required values. This is more performant and consistent with other parts of the codebase.
| display_name=$(echo "$api_response" | python3 -c "import sys,json; print(json.load(sys.stdin).get('skill',{}).get('displayName',''))" 2>/dev/null) | |
| summary=$(echo "$api_response" | python3 -c "import sys,json; print(json.load(sys.stdin).get('skill',{}).get('summary',''))" 2>/dev/null) | |
| owner_handle=$(echo "$api_response" | python3 -c "import sys,json; print(json.load(sys.stdin).get('owner',{}).get('handle',''))" 2>/dev/null) | |
| version=$(echo "$api_response" | python3 -c "import sys,json; print(json.load(sys.stdin).get('latestVersion',{}).get('version',''))" 2>/dev/null) | |
| display_name=$(echo "$api_response" | jq -r '.skill.displayName // ""') | |
| summary=$(echo "$api_response" | jq -r '.skill.summary // ""') | |
| owner_handle=$(echo "$api_response" | jq -r '.owner.handle // ""') | |
| version=$(echo "$api_response" | jq -r '.latestVersion.version // ""') |
| local response | ||
| response=$(curl -s --connect-timeout 10 --max-time 30 "${CLAWDHUB_API}/skills/${slug}") | ||
|
|
||
| if echo "$response" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then |
There was a problem hiding this comment.
You're using python3 to validate the JSON response. Since jq is a project dependency, it would be more consistent and potentially more performant to use jq for this check. The -e flag in jq sets the exit code based on the result of the last filter, which is perfect for checks in if statements.
| if echo "$response" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then | |
| if echo "$response" | jq -e . >/dev/null 2>&1; then |
| info=$(fetch_skill_info "$slug") || return 1 | ||
|
|
||
| local owner | ||
| owner=$(echo "$info" | python3 -c "import sys,json; print(json.load(sys.stdin).get('owner',{}).get('handle',''))" 2>/dev/null) |
There was a problem hiding this comment.
For consistency and performance, you can use jq here instead of python3 to parse the JSON and extract the owner's handle. This avoids spawning a separate Python process.
| owner=$(echo "$info" | python3 -c "import sys,json; print(json.load(sys.stdin).get('owner',{}).get('handle',''))" 2>/dev/null) | |
| owner=$(echo "$info" | jq -r '.owner.handle // ""') |
| echo "$response" | python3 -c " | ||
| import sys, json | ||
| data = json.load(sys.stdin) | ||
| skill = data.get('skill', {}) | ||
| owner = data.get('owner', {}) | ||
| version = data.get('latestVersion', {}) | ||
| stats = skill.get('stats', {}) | ||
|
|
||
| print(f' Name: {skill.get(\"displayName\", \"?\")}') | ||
| print(f' Slug: {skill.get(\"slug\", \"?\")}') | ||
| print(f' Owner: @{owner.get(\"handle\", \"?\")}') | ||
| print(f' Version: {version.get(\"version\", \"?\")}') | ||
| print(f' Summary: {skill.get(\"summary\", \"\")}') | ||
| print(f' Stars: {stats.get(\"stars\", 0)}') | ||
| print(f' Downloads: {stats.get(\"downloads\", 0)}') | ||
| print(f' Installs: {stats.get(\"installsCurrent\", 0)}') | ||
| print() | ||
| " |
There was a problem hiding this comment.
This Python script for formatting the output can be replaced with a jq command. This would make the script more consistent by relying on a single tool for JSON processing and avoid the overhead of starting a Python interpreter.
| echo "$response" | python3 -c " | |
| import sys, json | |
| data = json.load(sys.stdin) | |
| skill = data.get('skill', {}) | |
| owner = data.get('owner', {}) | |
| version = data.get('latestVersion', {}) | |
| stats = skill.get('stats', {}) | |
| print(f' Name: {skill.get(\"displayName\", \"?\")}') | |
| print(f' Slug: {skill.get(\"slug\", \"?\")}') | |
| print(f' Owner: @{owner.get(\"handle\", \"?\")}') | |
| print(f' Version: {version.get(\"version\", \"?\")}') | |
| print(f' Summary: {skill.get(\"summary\", \"\")}') | |
| print(f' Stars: {stats.get(\"stars\", 0)}') | |
| print(f' Downloads: {stats.get(\"downloads\", 0)}') | |
| print(f' Installs: {stats.get(\"installsCurrent\", 0)}') | |
| print() | |
| " | |
| echo "$response" | jq -r ' | |
| . as $data | | |
| " Name: \($data.skill.displayName // "?")", | |
| " Slug: \($data.skill.slug // "?")", | |
| " Owner: @\($data.owner.handle // "?")", | |
| " Version: \($data.latestVersion.version // "?")", | |
| " Summary: \($data.skill.summary // "")", | |
| " Stars: \($data.skill.stats.stars // 0)", | |
| " Downloads: \($data.skill.stats.downloads // 0)", | |
| " Installs: \($data.skill.stats.installsCurrent // 0)", | |
| "" | |
| ' |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
.agent/scripts/add-skill-helper.sh (2)
265-299: ClawdHub imports can land in the wrong category.
determine_target_pathonly scans file content, but in the ClawdHub flow it runs before fetching, socontentis empty and keyword matching (calendar/proxmox/etc.) never triggers. Consider incorporating the summary/description into the scan.♻️ Proposed fix
determine_target_path() { local skill_name="$1" local description="$2" local source_dir="$3" @@ - local content="" + local content="${description}" if [[ -f "$source_dir/SKILL.md" ]]; then - content=$(cat "$source_dir/SKILL.md") + content="${content}"$'\n'"$(cat "$source_dir/SKILL.md")" elif [[ -f "$source_dir/AGENTS.md" ]]; then - content=$(cat "$source_dir/AGENTS.md") + content="${content}"$'\n'"$(cat "$source_dir/AGENTS.md")" fiAlso applies to: 807-810
969-979: Skip ClawdHub URLs in update checks to prevent noisy warnings.The
cmd_check_updatesfunction callsparse_github_urlon all skills, but ClawdHub URLs stored ashttps://clawdhub.com/{owner}/{slug}fail parsing and trigger a warning for every ClawdHub-sourced skill during update checks. Add a check to skip ClawdHub entries cleanly, deferring version checking support until it's implemented.Suggested fix
while IFS='|' read -r name url commit; do + if [[ "$url" == *clawdhub.com/* ]]; then + log_info "Skipping ClawdHub skill ($name) — update checks not yet supported" + continue + fi # Extract owner/repo from URL local parsed
🤖 Fix all issues with AI agents
In @.agent/scripts/add-skill-helper.sh:
- Around line 774-784: The ClawdHub `slug` is used to build filesystem paths
(e.g., `fetch_dir`) and must be strictly validated and sanitized before any path
operations or `rm -rf`; update the code that reads `slug` to (1) strip any URL
query/fragment and whitespace, (2) reject or normalize values containing path
separators or traversal tokens (like `../`, `/`, `\`, or leading `-`), and (3)
allow only a safe whitelist of characters (e.g., lowercase letters, digits,
hyphen, underscore, dot). Apply this validation where `slug` is used
(referencing the `slug` variable and the `fetch_dir` usage and the removal block
around lines 851-856) and fail early with an error if validation fails to
prevent directory escape.
In @.agent/scripts/clawdhub-helper.sh:
- Around line 335-346: The --output option parsing in the while loop can access
an unbound $2 (with set -u) when the argument is missing; update the case for
--output to validate that a next argument exists and is not another option
(e.g., check [[ -n "${2-}" && "${2:0:1}" != "-" ]]) before assigning to
output_dir, and if the check fails call log_error with a clear message and
return 1 instead of blindly using $2; adjust shifts only on success.
- Around line 379-381: Replace the current interpolation of $query into the
Python one-liner by passing the query as an argv parameter to Python to avoid
quoting issues: call python3 -c that uses sys.argv[1] and urllib.parse.quote to
produce the encoded value, and pass "$query" as the argument (preserving the
stderr redirect and the fallback to echo "$query"); update the assignment to
encoded_query so it uses this argv-based Python invocation and still falls back
to echo "$query" on error.
- Around line 304-317: The current fallback using find/head to locate SKILL.md
is non-deterministic; instead construct the deterministic path
"$output_dir/skills/$slug/SKILL.md" and check that file first (use that as
installed_skill), and only if that path doesn't exist fall back to the existing
find "$output_dir" -name "SKILL.md" -type f logic; update the block that sets
installed_skill (and the subsequent existence check that compares to
$output_file) to prefer the deterministic path and then fallback to the find
result so the script reliably copies the correct SKILL.md for the given $slug.
🧹 Nitpick comments (2)
.agent/scripts/clawdhub-helper.sh (2)
121-135: Surface HTTP/network failures before JSON validation.
curl -scan mask transport/HTTP errors; using-fSwith explicit handling keeps A‑grade reliability.As per coding guidelines, improve error feedback for automation scripts.♻️ Suggested change
- response=$(curl -s --connect-timeout 10 --max-time 30 "${CLAWDHUB_API}/skills/${slug}") + response=$(curl -fsS --connect-timeout 10 --max-time 30 "${CLAWDHUB_API}/skills/${slug}") || { + log_error "Failed to fetch skill info (HTTP/network) for: $slug" + return 1 + }
162-168: Add a trap to guarantee temp directory cleanup.
Prevents orphaned temp dirs on early exits.♻️ Suggested change
pw_dir=$(mktemp -d "${TMPDIR:-/tmp}/clawdhub-pw-XXXXXX") + trap 'rm -rf "$pw_dir"' EXIT
| if [[ -z "$slug" ]]; then | ||
| log_error "ClawdHub slug required" | ||
| return 1 | ||
| fi | ||
|
|
||
| log_info "Importing from ClawdHub: $slug" | ||
|
|
||
| # Get skill metadata from API | ||
| local api_response | ||
| api_response=$(curl -s --connect-timeout 10 --max-time 30 "${CLAWDHUB_API:-https://clawdhub.com/api/v1}/skills/${slug}" 2>/dev/null) | ||
|
|
There was a problem hiding this comment.
Validate ClawdHub slugs before using them in filesystem paths.
slug is user-controlled and is embedded in fetch_dir that gets removed with rm -rf. A crafted value like ../ can escape the temp directory. Add strict validation (and strip query/fragment) before any path usage to keep the automation A‑grade secure.
🔒 Proposed fix
cmd_add_clawdhub() {
local slug="$1"
@@
if [[ -z "$slug" ]]; then
log_error "ClawdHub slug required"
return 1
fi
+ # Strip query/fragment and validate to prevent path traversal
+ slug="${slug%%\?*}"
+ slug="${slug%%#*}"
+ if [[ ! "$slug" =~ ^[A-Za-z0-9][A-Za-z0-9-]{0,63}$ ]]; then
+ log_error "Invalid ClawdHub slug: $slug"
+ return 1
+ fiAlso applies to: 851-856
🤖 Prompt for AI Agents
In @.agent/scripts/add-skill-helper.sh around lines 774 - 784, The ClawdHub
`slug` is used to build filesystem paths (e.g., `fetch_dir`) and must be
strictly validated and sanitized before any path operations or `rm -rf`; update
the code that reads `slug` to (1) strip any URL query/fragment and whitespace,
(2) reject or normalize values containing path separators or traversal tokens
(like `../`, `/`, `\`, or leading `-`), and (3) allow only a safe whitelist of
characters (e.g., lowercase letters, digits, hyphen, underscore, dot). Apply
this validation where `slug` is used (referencing the `slug` variable and the
`fetch_dir` usage and the removal block around lines 851-856) and fail early
with an error if validation fails to prevent directory escape.
| # Fallback: try clawdhub CLI | ||
| if command -v npx &>/dev/null; then | ||
| log_info "Trying: npx clawdhub install $slug" | ||
| if (cd "$output_dir" && npx --yes clawdhub@latest install "$slug" --force 2>/dev/null); then | ||
| # clawdhub installs to ./skills/<slug>/SKILL.md | ||
| local installed_skill | ||
| installed_skill=$(find "$output_dir" -name "SKILL.md" -type f 2>/dev/null | head -1) | ||
| if [[ -n "$installed_skill" && -f "$installed_skill" ]]; then | ||
| if [[ "$installed_skill" != "$output_file" ]]; then | ||
| cp "$installed_skill" "$output_file" | ||
| fi | ||
| log_success "Fetched via clawdhub CLI" | ||
| return 0 | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the actual file and its context
cat -n .agent/scripts/clawdhub-helper.sh | sed -n '295,325p'Repository: marcusquinn/aidevops
Length of output: 1435
🏁 Script executed:
# Search for any documentation or comments about clawdhub output structure
rg -i "clawdhub" --type sh -A 5 -B 5 .agent/scripts/Repository: marcusquinn/aidevops
Length of output: 27391
🌐 Web query:
clawdhub CLI install output directory structure where does npx clawdhub install write SKILL.md
💡 Result:
npx clawdhub install <skill-slug> installs a skill as a folder containing SKILL.md at:
- Default (no flags):
./skills/<skill-slug>/SKILL.md(relative to your current working directory) [1] - If a Clawdbot workspace is configured: it may instead use the workspace root as the workdir, installing to
<workspace>/skills/<skill-slug>/SKILL.md(unless you override) [1][2]
You can change where it writes by setting:
--workdir <dir>(orCLAWDHUB_WORKDIR) to change the base directory--dir <dir>to change the skills subdirectory name (default isskills) [1]
Sources: [1] [2]
Use deterministic path when clawdhub CLI copies SKILL.md.
When npx clawdhub install is run in $output_dir, it writes to ./skills/<slug>/SKILL.md relative to the working directory. The current find ... | head -1 approach is non-deterministic and will grab any SKILL.md in the output directory if multiple skills exist, potentially copying the wrong file. Use the expected path directly with a fallback:
Suggested fix
- installed_skill=$(find "$output_dir" -name "SKILL.md" -type f 2>/dev/null | head -1)
+ installed_skill="${output_dir}/skills/${slug}/SKILL.md"
+ if [[ ! -f "$installed_skill" ]]; then
+ installed_skill=$(find "$output_dir" -name "SKILL.md" -type f -path "*/${slug}/*" 2>/dev/null | head -1)
+ fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Fallback: try clawdhub CLI | |
| if command -v npx &>/dev/null; then | |
| log_info "Trying: npx clawdhub install $slug" | |
| if (cd "$output_dir" && npx --yes clawdhub@latest install "$slug" --force 2>/dev/null); then | |
| # clawdhub installs to ./skills/<slug>/SKILL.md | |
| local installed_skill | |
| installed_skill=$(find "$output_dir" -name "SKILL.md" -type f 2>/dev/null | head -1) | |
| if [[ -n "$installed_skill" && -f "$installed_skill" ]]; then | |
| if [[ "$installed_skill" != "$output_file" ]]; then | |
| cp "$installed_skill" "$output_file" | |
| fi | |
| log_success "Fetched via clawdhub CLI" | |
| return 0 | |
| fi | |
| # Fallback: try clawdhub CLI | |
| if command -v npx &>/dev/null; then | |
| log_info "Trying: npx clawdhub install $slug" | |
| if (cd "$output_dir" && npx --yes clawdhub@latest install "$slug" --force 2>/dev/null); then | |
| # clawdhub installs to ./skills/<slug>/SKILL.md | |
| local installed_skill | |
| installed_skill="${output_dir}/skills/${slug}/SKILL.md" | |
| if [[ ! -f "$installed_skill" ]]; then | |
| installed_skill=$(find "$output_dir" -name "SKILL.md" -type f -path "*/${slug}/*" 2>/dev/null | head -1) | |
| fi | |
| if [[ -n "$installed_skill" && -f "$installed_skill" ]]; then | |
| if [[ "$installed_skill" != "$output_file" ]]; then | |
| cp "$installed_skill" "$output_file" | |
| fi | |
| log_success "Fetched via clawdhub CLI" | |
| return 0 | |
| fi |
🤖 Prompt for AI Agents
In @.agent/scripts/clawdhub-helper.sh around lines 304 - 317, The current
fallback using find/head to locate SKILL.md is non-deterministic; instead
construct the deterministic path "$output_dir/skills/$slug/SKILL.md" and check
that file first (use that as installed_skill), and only if that path doesn't
exist fall back to the existing find "$output_dir" -name "SKILL.md" -type f
logic; update the block that sets installed_skill (and the subsequent existence
check that compares to $output_file) to prefer the deterministic path and then
fallback to the find result so the script reliably copies the correct SKILL.md
for the given $slug.
| # Parse options | ||
| while [[ $# -gt 0 ]]; do | ||
| case "$1" in | ||
| --output) | ||
| output_dir="$2" | ||
| shift 2 | ||
| ;; | ||
| *) | ||
| log_error "Unknown option: $1" | ||
| return 1 | ||
| ;; | ||
| esac |
There was a problem hiding this comment.
Guard --output against a missing argument.
With set -u, an empty value triggers an unbound variable error.
🐛 Suggested fix
--output)
- output_dir="$2"
- shift 2
+ if [[ $# -lt 2 || -z "${2:-}" ]]; then
+ log_error "--output requires a directory path"
+ return 1
+ fi
+ output_dir="$2"
+ shift 2
;;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Parse options | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| --output) | |
| output_dir="$2" | |
| shift 2 | |
| ;; | |
| *) | |
| log_error "Unknown option: $1" | |
| return 1 | |
| ;; | |
| esac | |
| # Parse options | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| --output) | |
| if [[ $# -lt 2 || -z "${2:-}" ]]; then | |
| log_error "--output requires a directory path" | |
| return 1 | |
| fi | |
| output_dir="$2" | |
| shift 2 | |
| ;; | |
| *) | |
| log_error "Unknown option: $1" | |
| return 1 | |
| ;; | |
| esac |
🤖 Prompt for AI Agents
In @.agent/scripts/clawdhub-helper.sh around lines 335 - 346, The --output
option parsing in the while loop can access an unbound $2 (with set -u) when the
argument is missing; update the case for --output to validate that a next
argument exists and is not another option (e.g., check [[ -n "${2-}" &&
"${2:0:1}" != "-" ]]) before assigning to output_dir, and if the check fails
call log_error with a clear message and return 1 instead of blindly using $2;
adjust shifts only on success.
| local encoded_query | ||
| encoded_query=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$query'))" 2>/dev/null || echo "$query") | ||
|
|
There was a problem hiding this comment.
Avoid interpolating the search query into Python code.
Single quotes break the command; argv-based encoding is safer.
🐛 Suggested fix
- encoded_query=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$query'))" 2>/dev/null || echo "$query")
+ encoded_query=$(python3 -c 'import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))' "$query" 2>/dev/null || echo "$query")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| local encoded_query | |
| encoded_query=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$query'))" 2>/dev/null || echo "$query") | |
| local encoded_query | |
| encoded_query=$(python3 -c 'import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))' "$query" 2>/dev/null || echo "$query") |
🤖 Prompt for AI Agents
In @.agent/scripts/clawdhub-helper.sh around lines 379 - 381, Replace the
current interpolation of $query into the Python one-liner by passing the query
as an argv parameter to Python to avoid quoting issues: call python3 -c that
uses sys.argv[1] and urllib.parse.quote to produce the encoded value, and pass
"$query" as the argument (preserving the stderr redirect and the fallback to
echo "$query"); update the assignment to encoded_query so it uses this
argv-based Python invocation and still falls back to echo "$query" on error.



Summary
Changes
New Files
.agent/scripts/clawdhub-helper.sh— Playwright-based SKILL.md fetcher with API metadata, search, and CLI fallback.agent/tools/productivity/caldav-calendar-skill.md— CalDAV calendar sync via vdirsyncer + khal (ClawdHub v1.0.1).agent/services/hosting/proxmox-full-skill.md— Complete Proxmox VE management via REST API (ClawdHub v1.0.0)Modified Files
.agent/scripts/add-skill-helper.sh— Addedclawdhub:prefix and clawdhub.com URL detection, newcmd_add_clawdhubfunction, expanded category detection (proxmox, calendar).agent/configs/skill-sources.json— Registered both new skills withformat_detected: "clawdhub".agent/scripts/commands/add-skill.md— Updated slash command docs with ClawdHub examples.agent/tools/build-agent/add-skill.md— Updated architecture docs, category table, popular skills sectionUsage
Quality
-skill.mdsuffix, category detection,imported_from: clawdhubfrontmatterSummary by CodeRabbit
Release Notes
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.