imgstat 3.0.2 → 3.1.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.
- package/README.md +20 -0
- package/bin/imgstat +24 -15
- package/lib/analyze.sh +18 -18
- package/lib/detect-ide.sh +77 -0
- package/lib/inspect.sh +23 -6
- package/lib/remote.sh +27 -9
- package/lib/rename.sh +9 -9
- package/lib/ui.sh +5 -0
- package/lib/utils.sh +26 -0
- package/llms.txt +45 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,6 +14,26 @@ can actually read — without guessing, without vision tokens, without touching
|
|
|
14
14
|
npm install -g imgstat
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
## usage
|
|
18
|
+
|
|
19
|
+
imgstat infers your intent automatically based on the target you provide.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Print dimensions of images from a web URL
|
|
23
|
+
imgstat https://example.com/logo.png
|
|
24
|
+
|
|
25
|
+
# Inspect a local directory
|
|
26
|
+
imgstat ./public/images
|
|
27
|
+
|
|
28
|
+
# Output strict, flat JSON (ideal for AI parsing)
|
|
29
|
+
imgstat https://example.com/images --json
|
|
30
|
+
|
|
31
|
+
# Rename local images to append dimensions (e.g., image-800x600.jpg)
|
|
32
|
+
imgstat rename ./public/images -y
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
When run in non-interactive environments or with flags, imgstat routes all progress logs to standard error, ensuring `stdout` remains clean for pipeline parsing and LLM usage.
|
|
36
|
+
|
|
17
37
|
## Contribution Rules
|
|
18
38
|
|
|
19
39
|
Keep the tool small. If you are considering adding a feature, ask: **does this help AI understand images faster?** If the answer is not clearly yes, it probably does not belong here.
|
package/bin/imgstat
CHANGED
|
@@ -45,30 +45,36 @@ while [[ $# -gt 0 ]]; do
|
|
|
45
45
|
--help|-h)
|
|
46
46
|
echo "imgstat — Embed image dimensions directly into filenames."
|
|
47
47
|
echo "Usage: imgstat [mode] [target] [options]"
|
|
48
|
+
echo " imgstat <url> [--json] (Auto-infers remote mode)"
|
|
49
|
+
echo " imgstat <path> [--json] (Auto-infers inspect mode)"
|
|
48
50
|
echo ""
|
|
49
51
|
echo "Modes:"
|
|
50
|
-
echo " (none) Interactive menu"
|
|
52
|
+
echo " (none) Interactive menu (Not recommended for AI agents)"
|
|
51
53
|
echo " inspect Print dimensions of local directory images"
|
|
52
54
|
echo " rename Rename local files (e.g. image-800x600.jpg)"
|
|
53
55
|
echo " remote Print dimensions of images from a URL"
|
|
54
56
|
echo " analyze Scan codebase for URLs, write dimension report for your IDE"
|
|
55
57
|
echo ""
|
|
56
|
-
echo "Options:"
|
|
57
|
-
echo " --
|
|
58
|
-
echo " --
|
|
59
|
-
echo " --
|
|
60
|
-
echo " --help
|
|
58
|
+
echo "Options for AI Agents:"
|
|
59
|
+
echo " --json Output strictly flat JSON to stdout (Recommended)"
|
|
60
|
+
echo " --yes, -y Skip all confirmation prompts"
|
|
61
|
+
echo " --dry-run Safe preview of changes"
|
|
62
|
+
echo " --help Show this help message"
|
|
61
63
|
exit 0
|
|
62
64
|
;;
|
|
63
65
|
--format)
|
|
64
66
|
shift
|
|
65
67
|
FORMAT="$1"
|
|
66
68
|
if [[ "$FORMAT" != "json" ]]; then
|
|
67
|
-
echo "Error: unsupported format '$FORMAT'. Only 'json' is supported."
|
|
69
|
+
echo "Error: unsupported format '$FORMAT'. Only 'json' is supported." >&2
|
|
68
70
|
exit 1
|
|
69
71
|
fi
|
|
70
72
|
shift
|
|
71
73
|
;;
|
|
74
|
+
--json)
|
|
75
|
+
FORMAT="json"
|
|
76
|
+
shift
|
|
77
|
+
;;
|
|
72
78
|
-*)
|
|
73
79
|
echo "Unknown flag: $1"
|
|
74
80
|
exit 1
|
|
@@ -87,13 +93,16 @@ done
|
|
|
87
93
|
|
|
88
94
|
if [[ -z "$MODE" ]]; then
|
|
89
95
|
if [[ -n "$TARGET" ]]; then
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
if [[ "$TARGET" =~ ^https?:// ]]; then
|
|
97
|
+
MODE="remote"
|
|
98
|
+
else
|
|
99
|
+
MODE="inspect"
|
|
100
|
+
fi
|
|
101
|
+
else
|
|
102
|
+
source "$LIB_DIR/ui.sh"
|
|
103
|
+
cmd_ui
|
|
104
|
+
exit 0
|
|
93
105
|
fi
|
|
94
|
-
source "$LIB_DIR/ui.sh"
|
|
95
|
-
cmd_ui
|
|
96
|
-
exit 0
|
|
97
106
|
fi
|
|
98
107
|
|
|
99
108
|
# Fallback target if none provided
|
|
@@ -104,7 +113,7 @@ fi
|
|
|
104
113
|
case "$MODE" in
|
|
105
114
|
inspect)
|
|
106
115
|
source "$LIB_DIR/inspect.sh"
|
|
107
|
-
cmd_inspect "$TARGET"
|
|
116
|
+
cmd_inspect "$TARGET" "$FORMAT"
|
|
108
117
|
;;
|
|
109
118
|
rename)
|
|
110
119
|
source "$LIB_DIR/rename.sh"
|
|
@@ -116,7 +125,7 @@ case "$MODE" in
|
|
|
116
125
|
exit 1
|
|
117
126
|
fi
|
|
118
127
|
source "$LIB_DIR/remote.sh"
|
|
119
|
-
cmd_remote "$TARGET" "$DRY_RUN"
|
|
128
|
+
cmd_remote "$TARGET" "$DRY_RUN" "$FORMAT"
|
|
120
129
|
;;
|
|
121
130
|
analyze)
|
|
122
131
|
source "$LIB_DIR/analyze.sh"
|
package/lib/analyze.sh
CHANGED
|
@@ -6,7 +6,7 @@ cmd_analyze() {
|
|
|
6
6
|
local dir="$1"
|
|
7
7
|
local target_option="${2:-1}"
|
|
8
8
|
local json_format="$3"
|
|
9
|
-
echo "Analyzing codebase for remote image references in $dir..."
|
|
9
|
+
echo "Analyzing codebase for remote image references in $dir..." >&2
|
|
10
10
|
|
|
11
11
|
# ── Step 1: Extract all http/https URLs from common code files ──────────────
|
|
12
12
|
local all_urls=()
|
|
@@ -18,12 +18,12 @@ cmd_analyze() {
|
|
|
18
18
|
-exec grep -hoE "https?://[^\"')[:space:]]+" {} + 2>/dev/null | sort -u)
|
|
19
19
|
|
|
20
20
|
if [[ ${#all_urls[@]} -eq 0 ]]; then
|
|
21
|
-
echo "No remote URLs found in code files."
|
|
21
|
+
echo "No remote URLs found in code files." >&2
|
|
22
22
|
return 0
|
|
23
23
|
fi
|
|
24
24
|
|
|
25
|
-
echo "Found ${#all_urls[@]} unique URL(s). Classifying..."
|
|
26
|
-
echo ""
|
|
25
|
+
echo "Found ${#all_urls[@]} unique URL(s). Classifying..." >&2
|
|
26
|
+
echo "" >&2
|
|
27
27
|
|
|
28
28
|
# ── Step 2: Classify each URL via 3-tier pipeline ──────────────────────────
|
|
29
29
|
local image_extensions=("jpg" "jpeg" "png" "gif" "webp" "avif" "svg" "bmp" "tiff" "tif" "ico")
|
|
@@ -39,7 +39,7 @@ cmd_analyze() {
|
|
|
39
39
|
|
|
40
40
|
# ── Tier 1: Obvious non-image check (fast, no network) ──────────────────
|
|
41
41
|
if is_obvious_non_image "$url"; then
|
|
42
|
-
printf " \033[2m⏭ Skip (pattern match) : %s\033[0m\n" "$url"
|
|
42
|
+
printf " \033[2m⏭ Skip (pattern match) : %s\033[0m\n" "$url" >&2
|
|
43
43
|
continue
|
|
44
44
|
fi
|
|
45
45
|
|
|
@@ -55,30 +55,30 @@ cmd_analyze() {
|
|
|
55
55
|
done
|
|
56
56
|
|
|
57
57
|
if [[ "$is_image_ext" == "true" ]]; then
|
|
58
|
-
printf " \033[1;32m✓ Queue (extension) : %s\033[0m\n" "$url"
|
|
58
|
+
printf " \033[1;32m✓ Queue (extension) : %s\033[0m\n" "$url" >&2
|
|
59
59
|
queued_urls+=("$url")
|
|
60
60
|
continue
|
|
61
61
|
fi
|
|
62
62
|
|
|
63
63
|
# ── Tier 3: HTTP HEAD Content-Type check (network, no body download) ─────
|
|
64
|
-
printf " \033[33m? Check (HEAD request) : %s\033[0m" "$url"
|
|
64
|
+
printf " \033[33m? Check (HEAD request) : %s\033[0m" "$url" >&2
|
|
65
65
|
if check_content_type_is_image "$url"; then
|
|
66
|
-
printf "\r \033[1;32m✓ Queue (content-type) : %s\033[0m\n" "$url"
|
|
66
|
+
printf "\r \033[1;32m✓ Queue (content-type) : %s\033[0m\n" "$url" >&2
|
|
67
67
|
queued_urls+=("$url")
|
|
68
68
|
else
|
|
69
|
-
printf "\r \033[2m⏭ Skip (not image CT) : %s\033[0m\n" "$url"
|
|
69
|
+
printf "\r \033[2m⏭ Skip (not image CT) : %s\033[0m\n" "$url" >&2
|
|
70
70
|
fi
|
|
71
71
|
done
|
|
72
72
|
|
|
73
|
-
echo ""
|
|
73
|
+
echo "" >&2
|
|
74
74
|
|
|
75
75
|
if [[ ${#queued_urls[@]} -eq 0 ]]; then
|
|
76
|
-
echo "No image URLs found after classification."
|
|
76
|
+
echo "No image URLs found after classification." >&2
|
|
77
77
|
return 0
|
|
78
78
|
fi
|
|
79
79
|
|
|
80
|
-
echo "Fetching dimensions for ${#queued_urls[@]} image URL(s)..."
|
|
81
|
-
echo ""
|
|
80
|
+
echo "Fetching dimensions for ${#queued_urls[@]} image URL(s)..." >&2
|
|
81
|
+
echo "" >&2
|
|
82
82
|
|
|
83
83
|
# ── Step 3: Download & measure queued image URLs ───────────────────────────
|
|
84
84
|
local TEMP_DIR
|
|
@@ -119,7 +119,7 @@ cmd_analyze() {
|
|
|
119
119
|
local w="${dim% *}"
|
|
120
120
|
local h="${dim#* }"
|
|
121
121
|
echo "${url}|${w}|${h}" >> "$TEMP_RESULTS"
|
|
122
|
-
printf " \033[1;32m📐 %s → %sx%s\033[0m\n" "$url" "$w" "$h"
|
|
122
|
+
printf " \033[1;32m📐 %s → %sx%s\033[0m\n" "$url" "$w" "$h" >&2
|
|
123
123
|
processed=$((processed + 1))
|
|
124
124
|
fi
|
|
125
125
|
fi
|
|
@@ -128,11 +128,11 @@ cmd_analyze() {
|
|
|
128
128
|
rm -rf "${TEMP_DIR:?}"/*
|
|
129
129
|
done
|
|
130
130
|
|
|
131
|
-
echo ""
|
|
132
|
-
echo "Analysis complete!"
|
|
131
|
+
echo "" >&2
|
|
132
|
+
echo "Analysis complete!" >&2
|
|
133
133
|
|
|
134
134
|
if [[ $processed -eq 0 ]]; then
|
|
135
|
-
echo "No matching images with dimension info could be parsed."
|
|
135
|
+
echo "No matching images with dimension info could be parsed." >&2
|
|
136
136
|
return 0
|
|
137
137
|
fi
|
|
138
138
|
|
|
@@ -172,5 +172,5 @@ cmd_analyze() {
|
|
|
172
172
|
;;
|
|
173
173
|
esac
|
|
174
174
|
|
|
175
|
-
echo "This file can now be read by autonomous coding agents."
|
|
175
|
+
echo "This file can now be read by autonomous coding agents." >&2
|
|
176
176
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# IDE Detection Script for imgstat
|
|
4
|
+
# Identifies the environment and exports the correct routes for rule generation
|
|
5
|
+
|
|
6
|
+
# 1. Initialize variables
|
|
7
|
+
IDE_NAME="Generic"
|
|
8
|
+
RULE_PATH="AGENTS.md"
|
|
9
|
+
SUPPORTS_IMAGES=false
|
|
10
|
+
|
|
11
|
+
# 2. Check for Cursor specifically (it often sets CURSOR_CLI)
|
|
12
|
+
if [[ "$TERM_PROGRAM" == "cursor" ]] || [[ -n "$CURSOR_CLI" ]]; then
|
|
13
|
+
IDE_NAME="Cursor"
|
|
14
|
+
RULE_PATH=".cursor/rules/"
|
|
15
|
+
SUPPORTS_IMAGES=false
|
|
16
|
+
|
|
17
|
+
# 3. Check for Windsurf
|
|
18
|
+
elif [[ "$TERM_PROGRAM" == "windsurf" ]] || [[ -n "$WINDSURF_CLI" ]]; then
|
|
19
|
+
IDE_NAME="Windsurf"
|
|
20
|
+
RULE_PATH=".windsurf/rules/"
|
|
21
|
+
SUPPORTS_IMAGES=false
|
|
22
|
+
|
|
23
|
+
# 4. Check for VS Code / GitHub Copilot
|
|
24
|
+
elif [[ "$TERM_PROGRAM" == "vscode" ]]; then
|
|
25
|
+
IDE_NAME="VS Code"
|
|
26
|
+
RULE_PATH=".github/copilot-instructions.md"
|
|
27
|
+
SUPPORTS_IMAGES=false
|
|
28
|
+
|
|
29
|
+
# 5. Check for Kiro
|
|
30
|
+
elif [[ -n "$KIRO_SESSION_ID" ]] || [[ "$TERM_PROGRAM" == "kiro" ]]; then
|
|
31
|
+
IDE_NAME="Kiro"
|
|
32
|
+
RULE_PATH=".kiro/steering/"
|
|
33
|
+
SUPPORTS_IMAGES=false
|
|
34
|
+
|
|
35
|
+
# 6. Check for iTerm2 (Good for imgstat image rendering)
|
|
36
|
+
elif [[ "$TERM_PROGRAM" == "iTerm.app" ]] || [[ -n "$ITERM_SESSION_ID" ]]; then
|
|
37
|
+
IDE_NAME="iTerm2"
|
|
38
|
+
RULE_PATH=".agents/rules/"
|
|
39
|
+
SUPPORTS_IMAGES=true
|
|
40
|
+
|
|
41
|
+
# 7. Check for Apple Terminal
|
|
42
|
+
elif [[ "$TERM_PROGRAM" == "Apple_Terminal" ]]; then
|
|
43
|
+
IDE_NAME="Apple Terminal"
|
|
44
|
+
RULE_PATH="AGENTS.md"
|
|
45
|
+
SUPPORTS_IMAGES=false
|
|
46
|
+
|
|
47
|
+
# 8. Windows-specific checks (WSL/Git Bash)
|
|
48
|
+
elif [[ -n "$WSLENV" ]] || [[ "$(uname -r)" == *"microsoft"* ]]; then
|
|
49
|
+
# Check for Cursor on Windows
|
|
50
|
+
if command -v tasklist.exe &> /dev/null && tasklist.exe 2>/dev/null | grep -qi "Cursor.exe"; then
|
|
51
|
+
IDE_NAME="Cursor (Windows)"
|
|
52
|
+
RULE_PATH=".cursor/rules/"
|
|
53
|
+
SUPPORTS_IMAGES=false
|
|
54
|
+
# Check for VS Code on Windows
|
|
55
|
+
elif command -v tasklist.exe &> /dev/null && tasklist.exe 2>/dev/null | grep -qi "Code.exe"; then
|
|
56
|
+
IDE_NAME="VS Code (Windows)"
|
|
57
|
+
RULE_PATH=".github/copilot-instructions.md"
|
|
58
|
+
SUPPORTS_IMAGES=false
|
|
59
|
+
else
|
|
60
|
+
IDE_NAME="WSL ($SHELL)"
|
|
61
|
+
RULE_PATH="AGENTS.md"
|
|
62
|
+
SUPPORTS_IMAGES=false
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# 9. Fallback/Standard Check
|
|
66
|
+
else
|
|
67
|
+
IDE_NAME="Generic Shell ($SHELL)"
|
|
68
|
+
RULE_PATH="AGENTS.md"
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# Export the results for use in other scripts
|
|
72
|
+
export IMGSTAT_IDE="$IDE_NAME"
|
|
73
|
+
export IMGSTAT_PATH="$RULE_PATH"
|
|
74
|
+
export IMGSTAT_SUPPORTS_IMAGES="$SUPPORTS_IMAGES"
|
|
75
|
+
|
|
76
|
+
# Return values (for sourcing)
|
|
77
|
+
echo "$IDE_NAME|$RULE_PATH|$SUPPORTS_IMAGES"
|
package/lib/inspect.sh
CHANGED
|
@@ -5,19 +5,36 @@ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scan.sh"
|
|
|
5
5
|
|
|
6
6
|
cmd_inspect() {
|
|
7
7
|
local dir="$1"
|
|
8
|
-
|
|
8
|
+
local format="$2"
|
|
9
|
+
echo "Inspecting directory: $dir" >&2
|
|
9
10
|
|
|
10
11
|
local files
|
|
11
12
|
mapfile -t files < <(scan_images "$dir")
|
|
12
13
|
|
|
13
14
|
if [[ ${#files[@]} -eq 0 ]]; then
|
|
14
|
-
echo "No images found in $dir."
|
|
15
|
+
echo "No images found in $dir." >&2
|
|
15
16
|
return 0
|
|
16
17
|
fi
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
if [[ "$format" == "json" ]]; then
|
|
20
|
+
local temp_res=$(mktemp)
|
|
21
|
+
for file in "${files[@]}"; do
|
|
22
|
+
local dim
|
|
23
|
+
dim=$(get_dimensions "$file" || true)
|
|
24
|
+
if [[ -n "$dim" ]]; then
|
|
25
|
+
local w="${dim% *}"
|
|
26
|
+
local h="${dim#* }"
|
|
27
|
+
echo "${file}|${w}|${h}" >> "$temp_res"
|
|
28
|
+
fi
|
|
29
|
+
done
|
|
30
|
+
bash "$(dirname "${BASH_SOURCE[0]}")/format-json.sh" "." "$temp_res" "0"
|
|
31
|
+
rm -f "$temp_res"
|
|
32
|
+
return 0
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
printf "%-50s | %-15s\n" "Filename" "Dimensions" >&2
|
|
36
|
+
printf "%.s-" {1..70} >&2
|
|
37
|
+
echo >&2
|
|
21
38
|
|
|
22
39
|
for file in "${files[@]}"; do
|
|
23
40
|
local dim
|
|
@@ -27,7 +44,7 @@ cmd_inspect() {
|
|
|
27
44
|
local h="${dim#* }"
|
|
28
45
|
printf "%-50s | %s x %s\n" "$(basename "$file")" "$w" "$h"
|
|
29
46
|
else
|
|
30
|
-
printf "%-50s | %-15s\n" "$(basename "$file")" "Error reading"
|
|
47
|
+
printf "%-50s | %-15s\n" "$(basename "$file")" "Error reading" >&2
|
|
31
48
|
fi
|
|
32
49
|
done
|
|
33
50
|
}
|
package/lib/remote.sh
CHANGED
|
@@ -5,23 +5,24 @@ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utils.sh"
|
|
|
5
5
|
cmd_remote() {
|
|
6
6
|
local url="$1"
|
|
7
7
|
local dry_run="$2"
|
|
8
|
+
local format="$3"
|
|
8
9
|
|
|
9
10
|
if [[ "$dry_run" == "true" ]]; then
|
|
10
|
-
echo "[Dry Run] Would fetch $url to extract dimensions. Nothing will be downloaded."
|
|
11
|
+
echo "[Dry Run] Would fetch $url to extract dimensions. Nothing will be downloaded." >&2
|
|
11
12
|
return 0
|
|
12
13
|
fi
|
|
13
14
|
|
|
14
|
-
echo "Fetching images from $url..."
|
|
15
|
+
echo "Fetching images from $url..." >&2
|
|
15
16
|
|
|
16
17
|
local TEMP_DIR
|
|
17
18
|
TEMP_DIR=$(mktemp -d)
|
|
18
19
|
trap 'rm -rf "$TEMP_DIR"' EXIT
|
|
19
20
|
|
|
20
21
|
# 1. Fetch direct URL (handles direct images without extensions like Cloudinary)
|
|
21
|
-
wget -q --content-disposition -P "$TEMP_DIR" "$url" || true
|
|
22
|
+
wget -q --timeout=10 --tries=2 --content-disposition -P "$TEMP_DIR" "$url" || true
|
|
22
23
|
|
|
23
24
|
# 2. Shallow scrape for linked images (handles HTML pages)
|
|
24
|
-
wget -q -nd -r -l 1 -A jpeg,jpg,bmp,gif,png,webp,avif -P "$TEMP_DIR" "$url" || true
|
|
25
|
+
wget -q --timeout=10 --tries=2 -nd -r -l 1 -A jpeg,jpg,bmp,gif,png,webp,avif -P "$TEMP_DIR" "$url" || true
|
|
25
26
|
|
|
26
27
|
local all_files=( "$TEMP_DIR"/* )
|
|
27
28
|
local found_images=()
|
|
@@ -37,13 +38,30 @@ cmd_remote() {
|
|
|
37
38
|
done
|
|
38
39
|
|
|
39
40
|
if [[ ${#found_images[@]} -eq 0 ]]; then
|
|
40
|
-
echo "No images found at $url."
|
|
41
|
+
echo "No images found at $url." >&2
|
|
41
42
|
return 0
|
|
42
43
|
fi
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
if [[ "$format" == "json" ]]; then
|
|
46
|
+
local temp_res=$(mktemp)
|
|
47
|
+
for file in "${found_images[@]}"; do
|
|
48
|
+
local dim
|
|
49
|
+
dim=$(get_dimensions "$file" || true)
|
|
50
|
+
if [[ -n "$dim" ]]; then
|
|
51
|
+
local w="${dim% *}"
|
|
52
|
+
local h="${dim#* }"
|
|
53
|
+
# Provide base filename since it matches expected JSON output
|
|
54
|
+
echo "$(basename "$file")|${w}|${h}" >> "$temp_res"
|
|
55
|
+
fi
|
|
56
|
+
done
|
|
57
|
+
bash "$(dirname "${BASH_SOURCE[0]}")/format-json.sh" "." "$temp_res" "0"
|
|
58
|
+
rm -f "$temp_res"
|
|
59
|
+
return 0
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
printf "%-50s | %-15s\n" "Filename" "Dimensions" >&2
|
|
63
|
+
printf "%.s-" {1..70} >&2
|
|
64
|
+
echo >&2
|
|
47
65
|
|
|
48
66
|
for file in "${found_images[@]}"; do
|
|
49
67
|
local dim
|
|
@@ -53,7 +71,7 @@ cmd_remote() {
|
|
|
53
71
|
local h="${dim#* }"
|
|
54
72
|
printf "%-50s | %s x %s\n" "$(basename "$file")" "$w" "$h"
|
|
55
73
|
else
|
|
56
|
-
printf "%-50s | %-15s\n" "$(basename "$file")" "Error reading"
|
|
74
|
+
printf "%-50s | %-15s\n" "$(basename "$file")" "Error reading" >&2
|
|
57
75
|
fi
|
|
58
76
|
done
|
|
59
77
|
}
|
package/lib/rename.sh
CHANGED
|
@@ -12,7 +12,7 @@ cmd_rename() {
|
|
|
12
12
|
mapfile -t files < <(scan_images "$dir")
|
|
13
13
|
|
|
14
14
|
if [[ ${#files[@]} -eq 0 ]]; then
|
|
15
|
-
echo "No images found in $dir."
|
|
15
|
+
echo "No images found in $dir." >&2
|
|
16
16
|
return 0
|
|
17
17
|
fi
|
|
18
18
|
|
|
@@ -38,26 +38,26 @@ cmd_rename() {
|
|
|
38
38
|
done
|
|
39
39
|
|
|
40
40
|
if [[ ${#to_rename[@]} -eq 0 ]]; then
|
|
41
|
-
echo "All images already have dimensions in their filenames. Nothing to do."
|
|
41
|
+
echo "All images already have dimensions in their filenames. Nothing to do." >&2
|
|
42
42
|
return 0
|
|
43
43
|
fi
|
|
44
44
|
|
|
45
|
-
echo "Found ${#to_rename[@]} file(s) to rename:"
|
|
45
|
+
echo "Found ${#to_rename[@]} file(s) to rename:" >&2
|
|
46
46
|
for i in "${!to_rename[@]}"; do
|
|
47
|
-
echo " $(basename "${to_rename[$i]}") -> $(basename "${new_names[$i]}")"
|
|
47
|
+
echo " $(basename "${to_rename[$i]}") -> $(basename "${new_names[$i]}")" >&2
|
|
48
48
|
done
|
|
49
49
|
|
|
50
50
|
if [[ "$dry_run" == "true" ]]; then
|
|
51
|
-
echo
|
|
52
|
-
echo "[Dry Run] No files were changed."
|
|
51
|
+
echo >&2
|
|
52
|
+
echo "[Dry Run] No files were changed." >&2
|
|
53
53
|
return 0
|
|
54
54
|
fi
|
|
55
55
|
|
|
56
56
|
if [[ "$auto_yes" != "true" ]]; then
|
|
57
|
-
echo
|
|
57
|
+
echo >&2
|
|
58
58
|
read -r -p "Proceed with renaming? [y/N] " response
|
|
59
59
|
if [[ ! "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
|
|
60
|
-
echo "Aborted."
|
|
60
|
+
echo "Aborted." >&2
|
|
61
61
|
return 1
|
|
62
62
|
fi
|
|
63
63
|
fi
|
|
@@ -66,5 +66,5 @@ cmd_rename() {
|
|
|
66
66
|
mv "${to_rename[$i]}" "${new_names[$i]}"
|
|
67
67
|
done
|
|
68
68
|
|
|
69
|
-
echo "Renaming complete."
|
|
69
|
+
echo "Renaming complete." >&2
|
|
70
70
|
}
|
package/lib/ui.sh
CHANGED
|
@@ -6,6 +6,11 @@ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/remote.sh"
|
|
|
6
6
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/analyze.sh"
|
|
7
7
|
|
|
8
8
|
cmd_ui() {
|
|
9
|
+
if [ ! -t 0 ]; then
|
|
10
|
+
echo "Error: Non-interactive terminal detected. Interactive menus are not supported here." >&2
|
|
11
|
+
echo "Please provide arguments instead. Run 'imgstat --help' for usage." >&2
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
9
14
|
local options=("Inspect local directory" "Rename local files" "Analyze remote URL" "Analyze codebase (.agent/rules)" "Quit")
|
|
10
15
|
local selected=0
|
|
11
16
|
|
package/lib/utils.sh
CHANGED
|
@@ -168,3 +168,29 @@ check_content_type_is_image() {
|
|
|
168
168
|
fi
|
|
169
169
|
return 1
|
|
170
170
|
}
|
|
171
|
+
|
|
172
|
+
# Detect the current IDE/environment and return detection info
|
|
173
|
+
# Usage: detect_ide
|
|
174
|
+
# Returns: IDE_NAME|RULE_PATH|SUPPORTS_IMAGES
|
|
175
|
+
# Also exports: IMGSTAT_IDE, IMGSTAT_PATH, IMGSTAT_SUPPORTS_IMAGES
|
|
176
|
+
detect_ide() {
|
|
177
|
+
local script_dir
|
|
178
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
179
|
+
source "$script_dir/detect-ide.sh"
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# Get the IDE-specific rule path for writing output
|
|
183
|
+
# Usage: get_rule_path
|
|
184
|
+
# Returns: The path where rules/context should be written
|
|
185
|
+
get_rule_path() {
|
|
186
|
+
detect_ide > /dev/null
|
|
187
|
+
echo "$IMGSTAT_PATH"
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
# Check if the current environment supports image rendering
|
|
191
|
+
# Usage: supports_image_rendering
|
|
192
|
+
# Returns: 0 (true) if images can be rendered, 1 (false) otherwise
|
|
193
|
+
supports_image_rendering() {
|
|
194
|
+
detect_ide > /dev/null
|
|
195
|
+
[[ "$IMGSTAT_SUPPORTS_IMAGES" == "true" ]]
|
|
196
|
+
}
|
package/llms.txt
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# imgstat CLI (Agent Reference)
|
|
2
|
+
|
|
3
|
+
`imgstat` is a specialized tool that extracts dimensions (width and height) from local images or remote URLs to provide context.
|
|
4
|
+
|
|
5
|
+
> **CRITICAL RULE FOR AI AGENTS:** Always use the `--json` flag to receive structured output. Never run this tool without flags, as the interactive fallback will block execution. Always pass `-y` to skip user prompts on state-mutating commands.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Recommended one-shot remote inspection
|
|
11
|
+
npx imgstat https://example.com/image.jpg --json
|
|
12
|
+
|
|
13
|
+
# Recommended one-shot local inspection
|
|
14
|
+
npx imgstat ./public/images --json
|
|
15
|
+
|
|
16
|
+
# Rename all local images to append their dimensions (e.g. image-800x600.jpg)
|
|
17
|
+
npx imgstat rename ./public/images -y
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Available Modes
|
|
21
|
+
If you just pass a single argument path or URL, imgstat will automatically infer `inspect` or `remote` respectively.
|
|
22
|
+
|
|
23
|
+
- `remote`: Downloads a URL (or shallow scrapes an HTML page) and measures any embedded images.
|
|
24
|
+
- `inspect`: Scans a specific local directory for images and prints their dimensions.
|
|
25
|
+
- `rename`: Actually modifies files on the file system to append dimensions. (Requires `-y` to bypass confirmation).
|
|
26
|
+
- `analyze`: Scrapes an entire codebase for external URL references and builds a report.
|
|
27
|
+
|
|
28
|
+
## Required Flags for Autonomous Agents
|
|
29
|
+
- `--json`: (Highly Recommended). Formats output as flat JSON. All logs, spinners, and debug text are routed to `stderr`, leaving only the pure JSON on `stdout`.
|
|
30
|
+
- `-y` or `--yes`: Skip confirmation prompts (vital for `rename`).
|
|
31
|
+
- `--dry-run`: Performs read operations and displays actions it *will* perform without writing.
|
|
32
|
+
|
|
33
|
+
## Output Schema (When using --json)
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"generated_at": "2026-04-17T12:00:00Z",
|
|
37
|
+
"images": [
|
|
38
|
+
{
|
|
39
|
+
"path": "hero-bg.jpg",
|
|
40
|
+
"width": 1920,
|
|
41
|
+
"height": 1080
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
```
|