From e54629bf17eb7013ce61bae2a9006e3d8041fab1 Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 19:37:08 +0800 Subject: [PATCH 01/23] fix(send-file): add required --tag/--category/--project flags and isolate HTML in temp dir Fixes two issues with send-file.sh: 1. Pass --tag, --category, --project to sth send (now required by CLI) 2. Copy HTML to a temp directory before sending to avoid packaging the entire project directory into the upload ZIP --- skills/sth/SKILL.md | 4 +- skills/sth/scripts/send-file.sh | 79 +++++++++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/skills/sth/SKILL.md b/skills/sth/SKILL.md index 9cee3d1..4096d17 100644 --- a/skills/sth/SKILL.md +++ b/skills/sth/SKILL.md @@ -33,10 +33,10 @@ Defaults: 3. Register an HTML file: ```bash -bash scripts/send-file.sh /absolute/or/relative/file.html [server_url] +bash scripts/send-file.sh /absolute/or/relative/file.html --tag TAG --category CAT --project PROJ [--server URL] ``` -The command uploads the HTML file and sibling assets from the same directory, then prints a session URL like `http://192.168.2.14:3939/s//`. +The command copies the HTML file to an isolated temporary directory (avoiding packaging unrelated files), uploads it, and prints a session URL like `http://192.168.2.14:3939/s//`. When calling `sth send` directly, `--tag` accepts multiple comma-separated values: diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index d329598..b04eee3 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -2,15 +2,86 @@ set -euo pipefail +usage() { + cat >&2 < [options] + +Options: + --tag TAG Tag(s) for the session (required) + --category CAT Category for the session (required) + --project PROJ Project for the session (required) + --server URL Server URL (default: \$STATIC_HTML_SERVER_URL or http://127.0.0.1:3939) +EOF + exit 1 +} + if [ "$#" -lt 1 ]; then - echo "Usage: send-file.sh [server_url]" >&2 + usage +fi + +target_file="" +server_url="${STATIC_HTML_SERVER_URL:-http://127.0.0.1:3939}" +tag="" +category="" +project="" + +while [ "$#" -gt 0 ]; do + case "$1" in + --tag) + tag="$2"; shift 2 ;; + --category) + category="$2"; shift 2 ;; + --project) + project="$2"; shift 2 ;; + --server) + server_url="$2"; shift 2 ;; + -*) + echo "Unknown option: $1" >&2; usage ;; + *) + if [ -z "$target_file" ]; then + target_file="$1"; shift + else + echo "Unexpected argument: $1" >&2; usage + fi ;; + esac +done + +if [ -z "$target_file" ]; then + echo "Error: is required" >&2 + usage +fi +if [ -z "$tag" ]; then + echo "Error: --tag is required" >&2 + usage +fi +if [ -z "$category" ]; then + echo "Error: --category is required" >&2 + usage +fi +if [ -z "$project" ]; then + echo "Error: --project is required" >&2 + usage +fi + +target_file="$(cd "$(dirname "$target_file")" && pwd)/$(basename "$target_file")" +if [ ! -f "$target_file" ]; then + echo "Error: file not found: $target_file" >&2 exit 1 fi +tmpdir="$(mktemp -d)" +cleanup() { rm -rf "$tmpdir"; } +trap cleanup EXIT + +cp "$target_file" "$tmpdir/" +filename="$(basename "$target_file")" + script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" repo_dir="$("$script_dir/bootstrap-repo.sh")" -target_file="$1" -server_url="${2:-${STATIC_HTML_SERVER_URL:-http://127.0.0.1:3939}}" cd "$repo_dir" -exec "$repo_dir/dist/sth" send "$target_file" --server "$server_url" +exec "$repo_dir/dist/sth" send "$tmpdir/$filename" \ + --server "$server_url" \ + --tag "$tag" \ + --category "$category" \ + --project "$project" From ed9f0b74be3ed6ce12d30913c14f0074744aa7be Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 19:39:11 +0800 Subject: [PATCH 02/23] fix(send-file): address review feedback - guard missing values, support --flag=VALUE syntax, directory check --- skills/sth/scripts/send-file.sh | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index b04eee3..fb06223 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -11,6 +11,9 @@ Options: --category CAT Category for the session (required) --project PROJ Project for the session (required) --server URL Server URL (default: \$STATIC_HTML_SERVER_URL or http://127.0.0.1:3939) + +Note: Only the specified HTML file is uploaded. If you need to include sibling +resources (CSS/JS/images), use 'sth send' directly with the appropriate directory. EOF exit 1 } @@ -28,13 +31,25 @@ project="" while [ "$#" -gt 0 ]; do case "$1" in --tag) + [ -z "${2:-}" ] && { echo "Error: --tag requires a value" >&2; usage; } tag="$2"; shift 2 ;; + --tag=*) + tag="${1#--tag=}"; shift ;; --category) + [ -z "${2:-}" ] && { echo "Error: --category requires a value" >&2; usage; } category="$2"; shift 2 ;; + --category=*) + category="${1#--category=}"; shift ;; --project) + [ -z "${2:-}" ] && { echo "Error: --project requires a value" >&2; usage; } project="$2"; shift 2 ;; + --project=*) + project="${1#--project=}"; shift ;; --server) + [ -z "${2:-}" ] && { echo "Error: --server requires a value" >&2; usage; } server_url="$2"; shift 2 ;; + --server=*) + server_url="${1#--server=}"; shift ;; -*) echo "Unknown option: $1" >&2; usage ;; *) @@ -63,7 +78,12 @@ if [ -z "$project" ]; then usage fi -target_file="$(cd "$(dirname "$target_file")" && pwd)/$(basename "$target_file")" +dir="$(dirname "$target_file")" +if [ ! -d "$dir" ]; then + echo "Error: directory not found: $dir" >&2 + exit 1 +fi +target_file="$(cd "$dir" && pwd)/$(basename "$target_file")" if [ ! -f "$target_file" ]; then echo "Error: file not found: $target_file" >&2 exit 1 From 6353325d3526081268dc6b96c0b640e66f4b15fc Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 19:41:35 +0800 Subject: [PATCH 03/23] fix(send-file): remove exec to allow trap cleanup, add empty-value guards for --flag= syntax - Replace exec with direct invocation so trap cleanup EXIT fires and temp directory is properly removed after upload - Add empty-value validation for --tag=, --category=, --project= forms - Simplify redundant directory existence check before file check --- skills/sth/scripts/send-file.sh | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index fb06223..3f1d82f 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -34,17 +34,17 @@ while [ "$#" -gt 0 ]; do [ -z "${2:-}" ] && { echo "Error: --tag requires a value" >&2; usage; } tag="$2"; shift 2 ;; --tag=*) - tag="${1#--tag=}"; shift ;; + tag="${1#--tag=}"; [ -z "$tag" ] && { echo "Error: --tag value cannot be empty" >&2; usage; }; shift ;; --category) [ -z "${2:-}" ] && { echo "Error: --category requires a value" >&2; usage; } category="$2"; shift 2 ;; --category=*) - category="${1#--category=}"; shift ;; + category="${1#--category=}"; [ -z "$category" ] && { echo "Error: --category value cannot be empty" >&2; usage; }; shift ;; --project) [ -z "${2:-}" ] && { echo "Error: --project requires a value" >&2; usage; } project="$2"; shift 2 ;; --project=*) - project="${1#--project=}"; shift ;; + project="${1#--project=}"; [ -z "$project" ] && { echo "Error: --project value cannot be empty" >&2; usage; }; shift ;; --server) [ -z "${2:-}" ] && { echo "Error: --server requires a value" >&2; usage; } server_url="$2"; shift 2 ;; @@ -78,12 +78,7 @@ if [ -z "$project" ]; then usage fi -dir="$(dirname "$target_file")" -if [ ! -d "$dir" ]; then - echo "Error: directory not found: $dir" >&2 - exit 1 -fi -target_file="$(cd "$dir" && pwd)/$(basename "$target_file")" +target_file="$(cd "$(dirname "$target_file")" && pwd)/$(basename "$target_file")" if [ ! -f "$target_file" ]; then echo "Error: file not found: $target_file" >&2 exit 1 @@ -100,7 +95,7 @@ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" repo_dir="$("$script_dir/bootstrap-repo.sh")" cd "$repo_dir" -exec "$repo_dir/dist/sth" send "$tmpdir/$filename" \ +"$repo_dir/dist/sth" send "$tmpdir/$filename" \ --server "$server_url" \ --tag "$tag" \ --category "$category" \ From 1e73263aea09c35a192d735619b9f86b486ccdbc Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 19:43:04 +0800 Subject: [PATCH 04/23] fix(send-file): add empty-value guard for --server= flag Review feedback: --server=* branch was missing the same empty-value validation that --tag=*, --category=*, and --project=* already had. --- skills/sth/scripts/send-file.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index 3f1d82f..c561572 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -49,7 +49,7 @@ while [ "$#" -gt 0 ]; do [ -z "${2:-}" ] && { echo "Error: --server requires a value" >&2; usage; } server_url="$2"; shift 2 ;; --server=*) - server_url="${1#--server=}"; shift ;; + server_url="${1#--server=}"; [ -z "$server_url" ] && { echo "Error: --server value cannot be empty" >&2; usage; }; shift ;; -*) echo "Unknown option: $1" >&2; usage ;; *) From b86f0fcec40e1f9f6a59c21e0be5564ebd1a0726 Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 19:45:09 +0800 Subject: [PATCH 05/23] fix(send-file): address review feedback - directory check and exec comment --- skills/sth/scripts/send-file.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index c561572..7109086 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -78,7 +78,12 @@ if [ -z "$project" ]; then usage fi -target_file="$(cd "$(dirname "$target_file")" && pwd)/$(basename "$target_file")" +dir="$(dirname "$target_file")" +if [ ! -d "$dir" ]; then + echo "Error: directory not found: $dir" >&2 + exit 1 +fi +target_file="$(cd "$dir" && pwd)/$(basename "$target_file")" if [ ! -f "$target_file" ]; then echo "Error: file not found: $target_file" >&2 exit 1 @@ -95,6 +100,7 @@ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" repo_dir="$("$script_dir/bootstrap-repo.sh")" cd "$repo_dir" +# Do NOT use exec here; the shell must remain alive so the EXIT trap can clean up $tmpdir. "$repo_dir/dist/sth" send "$tmpdir/$filename" \ --server "$server_url" \ --tag "$tag" \ From 056972ac1e3b234c125931dcf60b3a6700c16143 Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 19:47:17 +0800 Subject: [PATCH 06/23] refactor(send-file): use realpath for cleaner path resolution Replace cd+pwd+basename with realpath(1) for resolving the target file path, as suggested by review. This handles symlinks correctly and is more semantically clear. --- skills/sth/scripts/send-file.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index 7109086..38cbe50 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -78,12 +78,10 @@ if [ -z "$project" ]; then usage fi -dir="$(dirname "$target_file")" -if [ ! -d "$dir" ]; then - echo "Error: directory not found: $dir" >&2 +target_file="$(realpath -- "$target_file" 2>/dev/null)" || { + echo "Error: file not found or inaccessible: $1" >&2 exit 1 -fi -target_file="$(cd "$dir" && pwd)/$(basename "$target_file")" +} if [ ! -f "$target_file" ]; then echo "Error: file not found: $target_file" >&2 exit 1 From 9cce285ab07334d6283aceebe108748965ff1315 Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 19:49:05 +0800 Subject: [PATCH 07/23] fix(send-file): use saved filename in realpath error message After the argument parsing loop, $1 is empty because all positional parameters have been consumed. Save the original filename before calling realpath so the error message is actually helpful. --- skills/sth/scripts/send-file.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index 38cbe50..254d780 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -78,8 +78,9 @@ if [ -z "$project" ]; then usage fi +original_file="$target_file" target_file="$(realpath -- "$target_file" 2>/dev/null)" || { - echo "Error: file not found or inaccessible: $1" >&2 + echo "Error: file not found or inaccessible: $original_file" >&2 exit 1 } if [ ! -f "$target_file" ]; then From a2597ac4f9cd4182d0926f4a7373c99b4cf27067 Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 19:51:41 +0800 Subject: [PATCH 08/23] fix(send-file): address review feedback - unify errors, cross-platform realpath, signal traps - Unify --flag= empty value error messages to match --flag format - Add SIGINT/SIGTERM traps alongside EXIT for tmpdir cleanup - Add macOS-compatible realpath fallback using dirname/pwd - Document sibling resource workflow in SKILL.md --- skills/sth/SKILL.md | 2 ++ skills/sth/scripts/send-file.sh | 26 +++++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/skills/sth/SKILL.md b/skills/sth/SKILL.md index 4096d17..6e2ae54 100644 --- a/skills/sth/SKILL.md +++ b/skills/sth/SKILL.md @@ -38,6 +38,8 @@ bash scripts/send-file.sh /absolute/or/relative/file.html --tag TAG --category C The command copies the HTML file to an isolated temporary directory (avoiding packaging unrelated files), uploads it, and prints a session URL like `http://192.168.2.14:3939/s//`. +If you need to include sibling resources (CSS, JS, images) alongside the HTML file, use `sth send` directly with the directory containing those resources instead of `send-file.sh`. + When calling `sth send` directly, `--tag` accepts multiple comma-separated values: ```bash diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index 254d780..3aa8612 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -34,22 +34,22 @@ while [ "$#" -gt 0 ]; do [ -z "${2:-}" ] && { echo "Error: --tag requires a value" >&2; usage; } tag="$2"; shift 2 ;; --tag=*) - tag="${1#--tag=}"; [ -z "$tag" ] && { echo "Error: --tag value cannot be empty" >&2; usage; }; shift ;; + tag="${1#--tag=}"; [ -z "$tag" ] && { echo "Error: --tag requires a value" >&2; usage; }; shift ;; --category) [ -z "${2:-}" ] && { echo "Error: --category requires a value" >&2; usage; } category="$2"; shift 2 ;; --category=*) - category="${1#--category=}"; [ -z "$category" ] && { echo "Error: --category value cannot be empty" >&2; usage; }; shift ;; + category="${1#--category=}"; [ -z "$category" ] && { echo "Error: --category requires a value" >&2; usage; }; shift ;; --project) [ -z "${2:-}" ] && { echo "Error: --project requires a value" >&2; usage; } project="$2"; shift 2 ;; --project=*) - project="${1#--project=}"; [ -z "$project" ] && { echo "Error: --project value cannot be empty" >&2; usage; }; shift ;; + project="${1#--project=}"; [ -z "$project" ] && { echo "Error: --project requires a value" >&2; usage; }; shift ;; --server) [ -z "${2:-}" ] && { echo "Error: --server requires a value" >&2; usage; } server_url="$2"; shift 2 ;; --server=*) - server_url="${1#--server=}"; [ -z "$server_url" ] && { echo "Error: --server value cannot be empty" >&2; usage; }; shift ;; + server_url="${1#--server=}"; [ -z "$server_url" ] && { echo "Error: --server requires a value" >&2; usage; }; shift ;; -*) echo "Unknown option: $1" >&2; usage ;; *) @@ -79,10 +79,18 @@ if [ -z "$project" ]; then fi original_file="$target_file" -target_file="$(realpath -- "$target_file" 2>/dev/null)" || { - echo "Error: file not found or inaccessible: $original_file" >&2 - exit 1 -} +if command -v realpath >/dev/null 2>&1; then + target_file="$(realpath -- "$target_file" 2>/dev/null)" || { + echo "Error: file not found or inaccessible: $original_file" >&2 + exit 1 + } +else + target_file="$(cd "$(dirname "$target_file")" && pwd)/$(basename "$target_file")" + if [ ! -e "$target_file" ]; then + echo "Error: file not found or inaccessible: $original_file" >&2 + exit 1 + fi +fi if [ ! -f "$target_file" ]; then echo "Error: file not found: $target_file" >&2 exit 1 @@ -90,7 +98,7 @@ fi tmpdir="$(mktemp -d)" cleanup() { rm -rf "$tmpdir"; } -trap cleanup EXIT +trap cleanup EXIT SIGINT SIGTERM cp "$target_file" "$tmpdir/" filename="$(basename "$target_file")" From e89c2f95c8193f5b7a3314abbe20ef1bf91038ba Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 19:54:57 +0800 Subject: [PATCH 09/23] fix(send-file): address review feedback - idempotent cleanup, cp --, realpath fallback --- skills/sth/scripts/send-file.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index 3aa8612..8cd6f4e 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -85,7 +85,10 @@ if command -v realpath >/dev/null 2>&1; then exit 1 } else - target_file="$(cd "$(dirname "$target_file")" && pwd)/$(basename "$target_file")" + target_file="$(cd "$(dirname "$target_file")" 2>/dev/null && pwd)/$(basename "$target_file")" || { + echo "Error: cannot resolve path: $original_file" >&2 + exit 1 + } if [ ! -e "$target_file" ]; then echo "Error: file not found or inaccessible: $original_file" >&2 exit 1 @@ -97,10 +100,10 @@ if [ ! -f "$target_file" ]; then fi tmpdir="$(mktemp -d)" -cleanup() { rm -rf "$tmpdir"; } -trap cleanup EXIT SIGINT SIGTERM +cleanup() { [ -d "$tmpdir" ] && rm -rf "$tmpdir"; } +trap cleanup EXIT -cp "$target_file" "$tmpdir/" +cp -- "$target_file" "$tmpdir/" filename="$(basename "$target_file")" script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" From 1fd2b696d7df3c2d963874147ab30f24d06c85b5 Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 19:59:15 +0800 Subject: [PATCH 10/23] fix: initialize tmpdir and use defensive access in cleanup Address review feedback: initialize tmpdir="" before mktemp and use ${tmpdir:-} in cleanup to be robust under set -u. --- skills/sth/scripts/send-file.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index 8cd6f4e..2405ddf 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -99,8 +99,9 @@ if [ ! -f "$target_file" ]; then exit 1 fi +tmpdir="" tmpdir="$(mktemp -d)" -cleanup() { [ -d "$tmpdir" ] && rm -rf "$tmpdir"; } +cleanup() { [ -d "${tmpdir:-}" ] && rm -rf "$tmpdir"; } trap cleanup EXIT cp -- "$target_file" "$tmpdir/" From 37c81f2661b6ec510a6b621f547989e84f50bdba Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 20:00:58 +0800 Subject: [PATCH 11/23] fix(send-file): add readlink -f fallback and simplify tmpdir init --- skills/sth/scripts/send-file.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index 2405ddf..cb4aeab 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -84,6 +84,11 @@ if command -v realpath >/dev/null 2>&1; then echo "Error: file not found or inaccessible: $original_file" >&2 exit 1 } +elif command -v readlink >/dev/null 2>&1; then + target_file="$(readlink -f -- "$target_file" 2>/dev/null)" || { + echo "Error: file not found or inaccessible: $original_file" >&2 + exit 1 + } else target_file="$(cd "$(dirname "$target_file")" 2>/dev/null && pwd)/$(basename "$target_file")" || { echo "Error: cannot resolve path: $original_file" >&2 @@ -99,7 +104,6 @@ if [ ! -f "$target_file" ]; then exit 1 fi -tmpdir="" tmpdir="$(mktemp -d)" cleanup() { [ -d "${tmpdir:-}" ] && rm -rf "$tmpdir"; } trap cleanup EXIT From 16d635dc57954be0de2b9a4ea70c074852c84198 Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 21:18:52 +0800 Subject: [PATCH 12/23] fix(send-file): address review feedback - URL validation, trim whitespace, secure tmpdir --- skills/sth/scripts/send-file.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index cb4aeab..7d745af 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -78,6 +78,16 @@ if [ -z "$project" ]; then usage fi +if [[ ! "$server_url" =~ ^https?:// ]]; then + echo "Error: server URL must start with http:// or https://" >&2 + exit 1 +fi + +trim() { local v="$1"; v="${v#"${v%%[![:space:]]*}"}"; v="${v%"${v##*[![:space:]]}"}"; printf '%s' "$v"; } +tag="$(trim "$tag")" +category="$(trim "$category")" +project="$(trim "$project")" + original_file="$target_file" if command -v realpath >/dev/null 2>&1; then target_file="$(realpath -- "$target_file" 2>/dev/null)" || { @@ -105,6 +115,7 @@ if [ ! -f "$target_file" ]; then fi tmpdir="$(mktemp -d)" +chmod 700 "$tmpdir" cleanup() { [ -d "${tmpdir:-}" ] && rm -rf "$tmpdir"; } trap cleanup EXIT From ca29f2962c51953ee0ff5b047c76152b3ea916dc Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 21:23:44 +0800 Subject: [PATCH 13/23] fix(send-file): trim params before required validation, add HTML extension check Move trim operation before required parameter checks so whitespace-only values (e.g. --tag ' ') are caught. Add .html/.htm extension validation to prevent accidental non-HTML uploads. --- skills/sth/scripts/send-file.sh | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index 7d745af..5b712c7 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -65,6 +65,17 @@ if [ -z "$target_file" ]; then echo "Error: is required" >&2 usage fi + +if [[ ! "$server_url" =~ ^https?:// ]]; then + echo "Error: server URL must start with http:// or https://" >&2 + exit 1 +fi + +trim() { local v="$1"; v="${v#"${v%%[![:space:]]*}"}"; v="${v%"${v##*[![:space:]]}"}"; printf '%s' "$v"; } +tag="$(trim "$tag")" +category="$(trim "$category")" +project="$(trim "$project")" + if [ -z "$tag" ]; then echo "Error: --tag is required" >&2 usage @@ -78,16 +89,6 @@ if [ -z "$project" ]; then usage fi -if [[ ! "$server_url" =~ ^https?:// ]]; then - echo "Error: server URL must start with http:// or https://" >&2 - exit 1 -fi - -trim() { local v="$1"; v="${v#"${v%%[![:space:]]*}"}"; v="${v%"${v##*[![:space:]]}"}"; printf '%s' "$v"; } -tag="$(trim "$tag")" -category="$(trim "$category")" -project="$(trim "$project")" - original_file="$target_file" if command -v realpath >/dev/null 2>&1; then target_file="$(realpath -- "$target_file" 2>/dev/null)" || { @@ -113,6 +114,11 @@ if [ ! -f "$target_file" ]; then echo "Error: file not found: $target_file" >&2 exit 1 fi +filename="$(basename "$target_file")" +if [[ "$filename" != *.html && "$filename" != *.htm ]]; then + echo "Error: file must have .html or .htm extension: $filename" >&2 + exit 1 +fi tmpdir="$(mktemp -d)" chmod 700 "$tmpdir" From 70f26e3a0bf1b5e66497c94b3ba6212b72ce393c Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 21:26:45 +0800 Subject: [PATCH 14/23] fix(send-file): address review - remove duplicate assignment, case-insensitive extension check --- skills/sth/scripts/send-file.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index 5b712c7..9fe8c4c 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -115,7 +115,8 @@ if [ ! -f "$target_file" ]; then exit 1 fi filename="$(basename "$target_file")" -if [[ "$filename" != *.html && "$filename" != *.htm ]]; then +lowername="${filename,,}" +if [[ "$lowername" != *.html && "$lowername" != *.htm ]]; then echo "Error: file must have .html or .htm extension: $filename" >&2 exit 1 fi @@ -126,7 +127,6 @@ cleanup() { [ -d "${tmpdir:-}" ] && rm -rf "$tmpdir"; } trap cleanup EXIT cp -- "$target_file" "$tmpdir/" -filename="$(basename "$target_file")" script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" repo_dir="$("$script_dir/bootstrap-repo.sh")" From 15e8d260da72e2febc0b34ae02aff07fbbdb4ba1 Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 21:31:45 +0800 Subject: [PATCH 15/23] fix(send-file): address review - Bash 3.2 compat, move trim(), safer path fallback - Replace ${filename,,} with tr for macOS Bash 3.2 compatibility - Move trim() function definition to top with other helpers - Pre-extract basename before cd in fallback path resolution --- skills/sth/scripts/send-file.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index 9fe8c4c..b29e51f 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -18,6 +18,8 @@ EOF exit 1 } +trim() { local v="$1"; v="${v#"${v%%[![:space:]]*}"}"; v="${v%"${v##*[![:space:]]}"}"; printf '%s' "$v"; } + if [ "$#" -lt 1 ]; then usage fi @@ -71,7 +73,6 @@ if [[ ! "$server_url" =~ ^https?:// ]]; then exit 1 fi -trim() { local v="$1"; v="${v#"${v%%[![:space:]]*}"}"; v="${v%"${v##*[![:space:]]}"}"; printf '%s' "$v"; } tag="$(trim "$tag")" category="$(trim "$category")" project="$(trim "$project")" @@ -101,7 +102,8 @@ elif command -v readlink >/dev/null 2>&1; then exit 1 } else - target_file="$(cd "$(dirname "$target_file")" 2>/dev/null && pwd)/$(basename "$target_file")" || { + _base="$(basename "$target_file")" + target_file="$(cd "$(dirname "$target_file")" 2>/dev/null && pwd)/$_base" || { echo "Error: cannot resolve path: $original_file" >&2 exit 1 } @@ -115,7 +117,7 @@ if [ ! -f "$target_file" ]; then exit 1 fi filename="$(basename "$target_file")" -lowername="${filename,,}" +lowername="$(echo "$filename" | tr '[:upper:]' '[:lower:]')" if [[ "$lowername" != *.html && "$lowername" != *.htm ]]; then echo "Error: file must have .html or .htm extension: $filename" >&2 exit 1 From 9ffd1bc529c2957e36d76e09d4afd988c1713fe0 Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 21:35:27 +0800 Subject: [PATCH 16/23] fix(send-file): address review - reject flag-like option values, add comments --- skills/sth/scripts/send-file.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index b29e51f..39c6333 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -18,6 +18,7 @@ EOF exit 1 } +# Remove leading and trailing whitespace from a string trim() { local v="$1"; v="${v#"${v%%[![:space:]]*}"}"; v="${v%"${v##*[![:space:]]}"}"; printf '%s' "$v"; } if [ "$#" -lt 1 ]; then @@ -33,22 +34,22 @@ project="" while [ "$#" -gt 0 ]; do case "$1" in --tag) - [ -z "${2:-}" ] && { echo "Error: --tag requires a value" >&2; usage; } + [ -z "${2:-}" ] || [[ "${2:-}" == -* ]] && { echo "Error: --tag requires a value" >&2; usage; } tag="$2"; shift 2 ;; --tag=*) tag="${1#--tag=}"; [ -z "$tag" ] && { echo "Error: --tag requires a value" >&2; usage; }; shift ;; --category) - [ -z "${2:-}" ] && { echo "Error: --category requires a value" >&2; usage; } + [ -z "${2:-}" ] || [[ "${2:-}" == -* ]] && { echo "Error: --category requires a value" >&2; usage; } category="$2"; shift 2 ;; --category=*) category="${1#--category=}"; [ -z "$category" ] && { echo "Error: --category requires a value" >&2; usage; }; shift ;; --project) - [ -z "${2:-}" ] && { echo "Error: --project requires a value" >&2; usage; } + [ -z "${2:-}" ] || [[ "${2:-}" == -* ]] && { echo "Error: --project requires a value" >&2; usage; } project="$2"; shift 2 ;; --project=*) project="${1#--project=}"; [ -z "$project" ] && { echo "Error: --project requires a value" >&2; usage; }; shift ;; --server) - [ -z "${2:-}" ] && { echo "Error: --server requires a value" >&2; usage; } + [ -z "${2:-}" ] || [[ "${2:-}" == -* ]] && { echo "Error: --server requires a value" >&2; usage; } server_url="$2"; shift 2 ;; --server=*) server_url="${1#--server=}"; [ -z "$server_url" ] && { echo "Error: --server requires a value" >&2; usage; }; shift ;; @@ -102,6 +103,7 @@ elif command -v readlink >/dev/null 2>&1; then exit 1 } else + # Best-effort path resolution via cd+pwd; correctness is guaranteed by the -f check below. _base="$(basename "$target_file")" target_file="$(cd "$(dirname "$target_file")" 2>/dev/null && pwd)/$_base" || { echo "Error: cannot resolve path: $original_file" >&2 From 0af4461e8b2fd93a9cbdee9f6db44477210c131d Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 21:38:51 +0800 Subject: [PATCH 17/23] refactor(send-file): address review - extract require_value helper, simplify path fallback - Extract require_value() helper to reduce duplicated option validation - Accept values starting with '-' (remove false-positive rejection) - Make cd+pwd path fallback error handling more explicit --- skills/sth/scripts/send-file.sh | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index 39c6333..a5714da 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -31,28 +31,32 @@ tag="" category="" project="" +require_value() { + local opt="$1" val="${2:-}" + if [ -z "$val" ]; then + echo "Error: $opt requires a value" >&2 + usage + fi +} + while [ "$#" -gt 0 ]; do case "$1" in --tag) - [ -z "${2:-}" ] || [[ "${2:-}" == -* ]] && { echo "Error: --tag requires a value" >&2; usage; } - tag="$2"; shift 2 ;; + require_value "--tag" "${2:-}"; tag="$2"; shift 2 ;; --tag=*) - tag="${1#--tag=}"; [ -z "$tag" ] && { echo "Error: --tag requires a value" >&2; usage; }; shift ;; + tag="${1#--tag=}"; require_value "--tag" "$tag"; shift ;; --category) - [ -z "${2:-}" ] || [[ "${2:-}" == -* ]] && { echo "Error: --category requires a value" >&2; usage; } - category="$2"; shift 2 ;; + require_value "--category" "${2:-}"; category="$2"; shift 2 ;; --category=*) - category="${1#--category=}"; [ -z "$category" ] && { echo "Error: --category requires a value" >&2; usage; }; shift ;; + category="${1#--category=}"; require_value "--category" "$category"; shift ;; --project) - [ -z "${2:-}" ] || [[ "${2:-}" == -* ]] && { echo "Error: --project requires a value" >&2; usage; } - project="$2"; shift 2 ;; + require_value "--project" "${2:-}"; project="$2"; shift 2 ;; --project=*) - project="${1#--project=}"; [ -z "$project" ] && { echo "Error: --project requires a value" >&2; usage; }; shift ;; + project="${1#--project=}"; require_value "--project" "$project"; shift ;; --server) - [ -z "${2:-}" ] || [[ "${2:-}" == -* ]] && { echo "Error: --server requires a value" >&2; usage; } - server_url="$2"; shift 2 ;; + require_value "--server" "${2:-}"; server_url="$2"; shift 2 ;; --server=*) - server_url="${1#--server=}"; [ -z "$server_url" ] && { echo "Error: --server requires a value" >&2; usage; }; shift ;; + server_url="${1#--server=}"; require_value "--server" "$server_url"; shift ;; -*) echo "Unknown option: $1" >&2; usage ;; *) @@ -105,10 +109,11 @@ elif command -v readlink >/dev/null 2>&1; then else # Best-effort path resolution via cd+pwd; correctness is guaranteed by the -f check below. _base="$(basename "$target_file")" - target_file="$(cd "$(dirname "$target_file")" 2>/dev/null && pwd)/$_base" || { + _dir="$(cd "$(dirname "$target_file")" 2>/dev/null && pwd)" || { echo "Error: cannot resolve path: $original_file" >&2 exit 1 } + target_file="$_dir/$_base" if [ ! -e "$target_file" ]; then echo "Error: file not found or inaccessible: $original_file" >&2 exit 1 From 02185ddd889c2bac324b497ae494f096e49b17b9 Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 21:42:09 +0800 Subject: [PATCH 18/23] refactor(send-file): address review - inline trim during parsing, add sth send example to docs --- skills/sth/SKILL.md | 6 +++++- skills/sth/scripts/send-file.sh | 28 ++++++++-------------------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/skills/sth/SKILL.md b/skills/sth/SKILL.md index 6e2ae54..c09ff6a 100644 --- a/skills/sth/SKILL.md +++ b/skills/sth/SKILL.md @@ -38,7 +38,11 @@ bash scripts/send-file.sh /absolute/or/relative/file.html --tag TAG --category C The command copies the HTML file to an isolated temporary directory (avoiding packaging unrelated files), uploads it, and prints a session URL like `http://192.168.2.14:3939/s//`. -If you need to include sibling resources (CSS, JS, images) alongside the HTML file, use `sth send` directly with the directory containing those resources instead of `send-file.sh`. +If you need to include sibling resources (CSS, JS, images) alongside the HTML file, use `sth send` directly with the directory containing those resources instead of `send-file.sh`: + +```bash +sth send ./my-project-dir --tag demo --category preview --project myproj [--server URL] +``` When calling `sth send` directly, `--tag` accepts multiple comma-separated values: diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index a5714da..e0b72cb 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -31,32 +31,24 @@ tag="" category="" project="" -require_value() { - local opt="$1" val="${2:-}" - if [ -z "$val" ]; then - echo "Error: $opt requires a value" >&2 - usage - fi -} - while [ "$#" -gt 0 ]; do case "$1" in --tag) - require_value "--tag" "${2:-}"; tag="$2"; shift 2 ;; + [ -z "${2:-}" ] && { echo "Error: --tag requires a value" >&2; usage; }; tag="$(trim "$2")"; shift 2 ;; --tag=*) - tag="${1#--tag=}"; require_value "--tag" "$tag"; shift ;; + tag="$(trim "${1#--tag=}")"; [ -z "$tag" ] && { echo "Error: --tag requires a value" >&2; usage; }; shift ;; --category) - require_value "--category" "${2:-}"; category="$2"; shift 2 ;; + [ -z "${2:-}" ] && { echo "Error: --category requires a value" >&2; usage; }; category="$(trim "$2")"; shift 2 ;; --category=*) - category="${1#--category=}"; require_value "--category" "$category"; shift ;; + category="$(trim "${1#--category=}")"; [ -z "$category" ] && { echo "Error: --category requires a value" >&2; usage; }; shift ;; --project) - require_value "--project" "${2:-}"; project="$2"; shift 2 ;; + [ -z "${2:-}" ] && { echo "Error: --project requires a value" >&2; usage; }; project="$(trim "$2")"; shift 2 ;; --project=*) - project="${1#--project=}"; require_value "--project" "$project"; shift ;; + project="$(trim "${1#--project=}")"; [ -z "$project" ] && { echo "Error: --project requires a value" >&2; usage; }; shift ;; --server) - require_value "--server" "${2:-}"; server_url="$2"; shift 2 ;; + [ -z "${2:-}" ] && { echo "Error: --server requires a value" >&2; usage; }; server_url="$(trim "$2")"; shift 2 ;; --server=*) - server_url="${1#--server=}"; require_value "--server" "$server_url"; shift ;; + server_url="$(trim "${1#--server=}")"; [ -z "$server_url" ] && { echo "Error: --server requires a value" >&2; usage; }; shift ;; -*) echo "Unknown option: $1" >&2; usage ;; *) @@ -78,10 +70,6 @@ if [[ ! "$server_url" =~ ^https?:// ]]; then exit 1 fi -tag="$(trim "$tag")" -category="$(trim "$category")" -project="$(trim "$project")" - if [ -z "$tag" ]; then echo "Error: --tag is required" >&2 usage From b0e7a94b917e3fa3163c0edff0602ed01553cfbc Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 21:45:36 +0800 Subject: [PATCH 19/23] refactor(send-file): address review - multi-line case branches, --help, trim newlines --- skills/sth/scripts/send-file.sh | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index e0b72cb..1b7f1c5 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -18,10 +18,15 @@ EOF exit 1 } -# Remove leading and trailing whitespace from a string -trim() { local v="$1"; v="${v#"${v%%[![:space:]]*}"}"; v="${v%"${v##*[![:space:]]}"}"; printf '%s' "$v"; } +trim() { + local v="$1" + v="${v#"${v%%[![:space:]]*}"}" + v="${v%"${v##*[![:space:]]}"}" + v="${v//$'\n'/ }" + printf '%s' "$v" +} -if [ "$#" -lt 1 ]; then +if [ "$#" -lt 1 ] || [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then usage fi @@ -33,20 +38,26 @@ project="" while [ "$#" -gt 0 ]; do case "$1" in + --help|-h) + usage ;; --tag) - [ -z "${2:-}" ] && { echo "Error: --tag requires a value" >&2; usage; }; tag="$(trim "$2")"; shift 2 ;; + if [ -z "${2:-}" ]; then echo "Error: --tag requires a value" >&2; usage; fi + tag="$(trim "$2")"; shift 2 ;; --tag=*) tag="$(trim "${1#--tag=}")"; [ -z "$tag" ] && { echo "Error: --tag requires a value" >&2; usage; }; shift ;; --category) - [ -z "${2:-}" ] && { echo "Error: --category requires a value" >&2; usage; }; category="$(trim "$2")"; shift 2 ;; + if [ -z "${2:-}" ]; then echo "Error: --category requires a value" >&2; usage; fi + category="$(trim "$2")"; shift 2 ;; --category=*) category="$(trim "${1#--category=}")"; [ -z "$category" ] && { echo "Error: --category requires a value" >&2; usage; }; shift ;; --project) - [ -z "${2:-}" ] && { echo "Error: --project requires a value" >&2; usage; }; project="$(trim "$2")"; shift 2 ;; + if [ -z "${2:-}" ]; then echo "Error: --project requires a value" >&2; usage; fi + project="$(trim "$2")"; shift 2 ;; --project=*) project="$(trim "${1#--project=}")"; [ -z "$project" ] && { echo "Error: --project requires a value" >&2; usage; }; shift ;; --server) - [ -z "${2:-}" ] && { echo "Error: --server requires a value" >&2; usage; }; server_url="$(trim "$2")"; shift 2 ;; + if [ -z "${2:-}" ]; then echo "Error: --server requires a value" >&2; usage; fi + server_url="$(trim "$2")"; shift 2 ;; --server=*) server_url="$(trim "${1#--server=}")"; [ -z "$server_url" ] && { echo "Error: --server requires a value" >&2; usage; }; shift ;; -*) @@ -96,6 +107,7 @@ elif command -v readlink >/dev/null 2>&1; then } else # Best-effort path resolution via cd+pwd; correctness is guaranteed by the -f check below. + # Note: this branch does not resolve symlinks; use realpath or readlink if available. _base="$(basename "$target_file")" _dir="$(cd "$(dirname "$target_file")" 2>/dev/null && pwd)" || { echo "Error: cannot resolve path: $original_file" >&2 From 84de2acc047ebe0206dad73f29e2d82116ff5fd5 Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 21:48:28 +0800 Subject: [PATCH 20/23] fix(send-file): address review - document trim(), extract allowed_exts, clarify symlink fallback --- skills/sth/scripts/send-file.sh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index 1b7f1c5..34e03eb 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -18,6 +18,7 @@ EOF exit 1 } +# Strips leading/trailing whitespace and collapses internal newlines to spaces. trim() { local v="$1" v="${v#"${v%%[![:space:]]*}"}" @@ -107,7 +108,8 @@ elif command -v readlink >/dev/null 2>&1; then } else # Best-effort path resolution via cd+pwd; correctness is guaranteed by the -f check below. - # Note: this branch does not resolve symlinks; use realpath or readlink if available. + # Limitations compared to realpath/readlink: symlinks are not resolved, and a dangling + # symlink will use the link name as basename rather than the target name. _base="$(basename "$target_file")" _dir="$(cd "$(dirname "$target_file")" 2>/dev/null && pwd)" || { echo "Error: cannot resolve path: $original_file" >&2 @@ -123,10 +125,15 @@ if [ ! -f "$target_file" ]; then echo "Error: file not found: $target_file" >&2 exit 1 fi +allowed_exts="html htm" filename="$(basename "$target_file")" lowername="$(echo "$filename" | tr '[:upper:]' '[:lower:]')" -if [[ "$lowername" != *.html && "$lowername" != *.htm ]]; then - echo "Error: file must have .html or .htm extension: $filename" >&2 +_ext_match=false +for _ext in $allowed_exts; do + [[ "$lowername" == *".$_ext" ]] && { _ext_match=true; break; } +done +if [ "$_ext_match" = false ]; then + echo "Error: file must have .${allowed_exts// / or .} extension: $filename" >&2 exit 1 fi From eecbdebb0eba9428a195af7e998d71375ba7f211 Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 21:51:48 +0800 Subject: [PATCH 21/23] fix(send-file): address review - case-based ext check, test readlink -f compat - Probe readlink -f with a known path (/) before using it, so macOS BSD readlink falls through to the cd+pwd fallback instead of emitting a misleading 'file not found' error. - Replace allowed_exts loop with a case statement for extension matching, improving readability. --- skills/sth/scripts/send-file.sh | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index 34e03eb..ed906f3 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -101,7 +101,7 @@ if command -v realpath >/dev/null 2>&1; then echo "Error: file not found or inaccessible: $original_file" >&2 exit 1 } -elif command -v readlink >/dev/null 2>&1; then +elif command -v readlink >/dev/null 2>&1 && readlink -f / >/dev/null 2>&1; then target_file="$(readlink -f -- "$target_file" 2>/dev/null)" || { echo "Error: file not found or inaccessible: $original_file" >&2 exit 1 @@ -125,17 +125,12 @@ if [ ! -f "$target_file" ]; then echo "Error: file not found: $target_file" >&2 exit 1 fi -allowed_exts="html htm" filename="$(basename "$target_file")" lowername="$(echo "$filename" | tr '[:upper:]' '[:lower:]')" -_ext_match=false -for _ext in $allowed_exts; do - [[ "$lowername" == *".$_ext" ]] && { _ext_match=true; break; } -done -if [ "$_ext_match" = false ]; then - echo "Error: file must have .${allowed_exts// / or .} extension: $filename" >&2 - exit 1 -fi +case "$lowername" in + *.html|*.htm) ;; + *) echo "Error: file must have .html or .htm extension: $filename" >&2; exit 1 ;; +esac tmpdir="$(mktemp -d)" chmod 700 "$tmpdir" From 3a41045de300cf29a903750ec253d125737763ba Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 22:40:41 +0800 Subject: [PATCH 22/23] fix(send-file.sh): address review feedback - usage() now accepts exit code parameter; --help/-h exit 0, errors exit 1 - Replace echo with printf for filename to avoid dash-prefixed filename issues - Add directory validation for bootstrap-repo.sh output --- skills/sth/scripts/send-file.sh | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index ed906f3..1c5aa75 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -15,7 +15,7 @@ Options: Note: Only the specified HTML file is uploaded. If you need to include sibling resources (CSS/JS/images), use 'sth send' directly with the appropriate directory. EOF - exit 1 + exit "${1:-1}" } # Strips leading/trailing whitespace and collapses internal newlines to spaces. @@ -27,8 +27,11 @@ trim() { printf '%s' "$v" } -if [ "$#" -lt 1 ] || [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then - usage +if [ "$#" -lt 1 ]; then + usage 1 +fi +if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then + usage 0 fi target_file="" @@ -40,7 +43,7 @@ project="" while [ "$#" -gt 0 ]; do case "$1" in --help|-h) - usage ;; + usage 0 ;; --tag) if [ -z "${2:-}" ]; then echo "Error: --tag requires a value" >&2; usage; fi tag="$(trim "$2")"; shift 2 ;; @@ -126,7 +129,7 @@ if [ ! -f "$target_file" ]; then exit 1 fi filename="$(basename "$target_file")" -lowername="$(echo "$filename" | tr '[:upper:]' '[:lower:]')" +lowername="$(printf '%s' "$filename" | tr '[:upper:]' '[:lower:]')" case "$lowername" in *.html|*.htm) ;; *) echo "Error: file must have .html or .htm extension: $filename" >&2; exit 1 ;; @@ -141,6 +144,10 @@ cp -- "$target_file" "$tmpdir/" script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" repo_dir="$("$script_dir/bootstrap-repo.sh")" +if [ ! -d "$repo_dir" ]; then + echo "Error: bootstrap-repo.sh did not return a valid directory: $repo_dir" >&2 + exit 1 +fi cd "$repo_dir" # Do NOT use exec here; the shell must remain alive so the EXIT trap can clean up $tmpdir. From 58cc252325e63508339b4f69751f156a5fbb8cb9 Mon Sep 17 00:00:00 2001 From: Agent Date: Thu, 14 May 2026 22:44:58 +0800 Subject: [PATCH 23/23] fix(send-file.sh): support POSIX end-of-options separator (--) Add a --) case before the catch-all -* so that -- is properly recognized as the POSIX end-of-options marker, allowing filenames starting with -. --- skills/sth/scripts/send-file.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/skills/sth/scripts/send-file.sh b/skills/sth/scripts/send-file.sh index 1c5aa75..a049430 100755 --- a/skills/sth/scripts/send-file.sh +++ b/skills/sth/scripts/send-file.sh @@ -64,6 +64,8 @@ while [ "$#" -gt 0 ]; do server_url="$(trim "$2")"; shift 2 ;; --server=*) server_url="$(trim "${1#--server=}")"; [ -z "$server_url" ] && { echo "Error: --server requires a value" >&2; usage; }; shift ;; + --) + shift; break ;; -*) echo "Unknown option: $1" >&2; usage ;; *)