imgstat 1.0.4 → 2.0.1

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/bin/imgstat ADDED
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ LIB_DIR="$DIR/../lib"
7
+
8
+ source "$LIB_DIR/utils.sh"
9
+
10
+ # Ensure identify is available
11
+ require_command identify
12
+
13
+ # Parse global flags and modes
14
+ MODE=""
15
+ TARGET=""
16
+ DRY_RUN="false"
17
+ YES_FLAG="false"
18
+
19
+ # Simple parser
20
+ while [[ $# -gt 0 ]]; do
21
+ case $1 in
22
+ inspect|rename|remote|analyze)
23
+ if [[ -n "$MODE" ]]; then
24
+ echo "Error: Multiple modes specified ($MODE and $1)."
25
+ exit 1
26
+ fi
27
+ MODE="$1"
28
+ shift
29
+ ;;
30
+ --dry-run)
31
+ DRY_RUN="true"
32
+ shift
33
+ ;;
34
+ --yes|-y)
35
+ YES_FLAG="true"
36
+ shift
37
+ ;;
38
+ --help|-h)
39
+ echo "imgstat — Embed image dimensions directly into filenames."
40
+ echo "Usage: imgstat [mode] [target] [options]"
41
+ echo ""
42
+ echo "Modes:"
43
+ echo " (none) Interactive menu"
44
+ echo " inspect Print dimensions of local directory images"
45
+ echo " rename Rename local files (e.g. image-800x600.jpg)"
46
+ echo " remote Print dimensions of images from a URL"
47
+ echo " analyze Scan codebase for URLs, write to .agent/rules/image_dimensions.md"
48
+ echo ""
49
+ echo "Options:"
50
+ echo " --dry-run Show what would happen without making changes"
51
+ echo " --yes, -y Skip confirmation prompts (for rename)"
52
+ echo " --help, -h Show this help message"
53
+ exit 0
54
+ ;;
55
+ -*)
56
+ echo "Unknown flag: $1"
57
+ exit 1
58
+ ;;
59
+ *)
60
+ if [[ -z "$TARGET" ]]; then
61
+ TARGET="$1"
62
+ else
63
+ echo "Error: Unexpected argument: $1"
64
+ exit 1
65
+ fi
66
+ shift
67
+ ;;
68
+ esac
69
+ done
70
+
71
+ if [[ -z "$MODE" ]]; then
72
+ if [[ -n "$TARGET" ]]; then
73
+ echo "Error: A target was provided without a mode."
74
+ echo "Usage: imgstat [mode] [target] [--dry-run] [--yes]"
75
+ exit 1
76
+ fi
77
+ source "$LIB_DIR/ui.sh"
78
+ cmd_ui
79
+ exit 0
80
+ fi
81
+
82
+ # Fallback target if none provided
83
+ if [[ "$MODE" == "inspect" || "$MODE" == "rename" || "$MODE" == "analyze" ]]; then
84
+ TARGET="${TARGET:-./}"
85
+ fi
86
+
87
+ case "$MODE" in
88
+ inspect)
89
+ source "$LIB_DIR/inspect.sh"
90
+ cmd_inspect "$TARGET"
91
+ ;;
92
+ rename)
93
+ source "$LIB_DIR/rename.sh"
94
+ cmd_rename "$TARGET" "$DRY_RUN" "$YES_FLAG"
95
+ ;;
96
+ remote)
97
+ if [[ -z "$TARGET" ]]; then
98
+ echo "Error: URL target required for remote mode."
99
+ exit 1
100
+ fi
101
+ source "$LIB_DIR/remote.sh"
102
+ cmd_remote "$TARGET" "$DRY_RUN"
103
+ ;;
104
+ analyze)
105
+ source "$LIB_DIR/analyze.sh"
106
+ cmd_analyze "$TARGET"
107
+ ;;
108
+ esac
package/build_deb.sh ADDED
@@ -0,0 +1,48 @@
1
+ #!/bin/bash
2
+ PACKAGE_NAME="imagestat"
3
+ VERSION="1.0"
4
+ ARCH="all"
5
+ DEB_DIR="${PACKAGE_NAME}_${VERSION}_${ARCH}"
6
+
7
+ echo "Building .deb package for $PACKAGE_NAME..."
8
+
9
+ # Create directory structure
10
+ mkdir -p "$DEB_DIR/usr/local/bin"
11
+ mkdir -p "$DEB_DIR/DEBIAN"
12
+
13
+ # Determine source file (handle rename)
14
+ if [ -f "imagestat" ]; then
15
+ cp imagestat "$DEB_DIR/usr/local/bin/$PACKAGE_NAME"
16
+ elif [ -f "imgstat.sh" ]; then
17
+ cp imgstat.sh "$DEB_DIR/usr/local/bin/$PACKAGE_NAME"
18
+ else
19
+ echo "Error: Source script not found (looked for 'imagestat' and 'imgstat.sh')."
20
+ exit 1
21
+ fi
22
+
23
+ chmod 755 "$DEB_DIR/usr/local/bin/$PACKAGE_NAME"
24
+
25
+ # Create control file
26
+ cat > "$DEB_DIR/DEBIAN/control" <<EOF
27
+ Package: $PACKAGE_NAME
28
+ Version: $VERSION
29
+ Section: utils
30
+ Priority: optional
31
+ Architecture: $ARCH
32
+ Allowed-Architectures: all
33
+ Maintainer: User <user@example.com>
34
+ Depends: imagemagick, wget
35
+ Description: Image dimensions scanner and renamer
36
+ Recursively scans directories for images and renames them to include their
37
+ dimensions (e.g., image-800x600.jpg). Ignores heavy directories like node_modules.
38
+ EOF
39
+
40
+ # Build package
41
+ dpkg-deb --build "$DEB_DIR"
42
+
43
+ # Cleanup
44
+ mv "${DEB_DIR}.deb" "${PACKAGE_NAME}.deb" 2>/dev/null || true
45
+ rm -rf "$DEB_DIR"
46
+
47
+ echo "Build complete: ${PACKAGE_NAME}.deb"
48
+ echo "Install with: sudo apt install ./$PACKAGE_NAME.deb"
package/install.sh ADDED
@@ -0,0 +1,68 @@
1
+ #!/bin/bash
2
+
3
+ APP_NAME="imagestat"
4
+ INSTALL_DIR="/usr/local/bin"
5
+ TARGET_PATH="$INSTALL_DIR/$APP_NAME"
6
+ SOURCE_FILE=""
7
+
8
+ # Colors
9
+ GREEN='\033[0;32m'
10
+ RED='\033[0;31m'
11
+ YELLOW='\033[1;33m'
12
+ NC='\033[0m' # No Color
13
+
14
+ echo -e "${GREEN}Installing $APP_NAME...${NC}"
15
+
16
+ # Check sudo
17
+ if [ "$EUID" -ne 0 ]; then
18
+ echo -e "${YELLOW}Please run as root (sudo) to install globally.${NC}"
19
+ echo "Try: sudo ./install.sh"
20
+ exit 1
21
+ fi
22
+
23
+ # Locate source file
24
+ if [ -f "imagestat" ]; then
25
+ SOURCE_FILE="imagestat"
26
+ elif [ -f "imgstat.sh" ]; then
27
+ SOURCE_FILE="imgstat.sh"
28
+ else
29
+ echo "Local script not found. Downloading from GitHub..."
30
+ REMOTE_URL="https://raw.githubusercontent.com/isaac0yen/imgstat/main/imagestat"
31
+ if command -v curl &> /dev/null; then
32
+ curl -fsSL "$REMOTE_URL" -o imagestat
33
+ elif command -v wget &> /dev/null; then
34
+ wget -q "$REMOTE_URL" -O imagestat
35
+ else
36
+ echo -e "${RED}Error: Neither curl nor wget found. Cannot download script.${NC}"
37
+ exit 1
38
+ fi
39
+
40
+ if [ ! -f "imagestat" ]; then
41
+ echo -e "${RED}Error: Failed to download script.${NC}"
42
+ exit 1
43
+ fi
44
+ SOURCE_FILE="imagestat"
45
+ fi
46
+
47
+ # Check dependencies
48
+ MISSING_DEPS=0
49
+ if ! command -v identify &> /dev/null; then
50
+ echo -e "${RED}Error: 'identify' (ImageMagick) is not installed.${NC}"
51
+ MISSING_DEPS=1
52
+ fi
53
+ if ! command -v wget &> /dev/null; then
54
+ echo -e "${RED}Error: 'wget' is not installed.${NC}"
55
+ MISSING_DEPS=1
56
+ fi
57
+
58
+ if [ $MISSING_DEPS -eq 1 ]; then
59
+ echo -e "${YELLOW}Please install missing dependencies first.${NC}"
60
+ exit 1
61
+ fi
62
+
63
+ echo "Installing $SOURCE_FILE to $TARGET_PATH..."
64
+ cp "$SOURCE_FILE" "$TARGET_PATH"
65
+ chmod +x "$TARGET_PATH"
66
+
67
+ echo -e "${GREEN}Success! $APP_NAME has been installed.${NC}"
68
+ echo -e "Usage: ${GREEN}$APP_NAME [-u|--url <url>] [directory]${NC}"
package/lib/analyze.sh ADDED
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utils.sh"
4
+
5
+ cmd_analyze() {
6
+ local dir="$1"
7
+ echo "Analyzing codebase for remote image references in $dir..."
8
+
9
+ # Scan common code files for URLs
10
+ # We extract http/https links specifically in src, href, url() contexts
11
+ local urls=()
12
+
13
+ # Read all matching files, use grep to find URLs, and sort uniquely
14
+ # This uses a basic grep regex to find common image URL patterns.
15
+ mapfile -t urls < <(find "$dir" \
16
+ -type d \( -name "node_modules" -o -name ".git" -o -name "dist" -o -name "build" \) -prune -o \
17
+ -type f \( -name "*.html" -o -name "*.jsx" -o -name "*.tsx" -o -name "*.js" -o -name "*.ts" -o -name "*.vue" -o -name "*.css" -o -name "*.scss" \) \
18
+ -exec grep -oE "https?://[^\"')[:space:]]+" {} + 2>/dev/null | sort -u)
19
+
20
+ if [[ ${#urls[@]} -eq 0 ]]; then
21
+ echo "No remote URLs found in code files."
22
+ return 0
23
+ fi
24
+
25
+ echo "Found ${#urls[@]} unique URL(s). Fetching dimensions..."
26
+
27
+ # Prepare secure fetch directory
28
+ local TEMP_DIR
29
+ TEMP_DIR=$(mktemp -d)
30
+ trap 'rm -rf "$TEMP_DIR"' EXIT
31
+
32
+ # Output file setup
33
+ local rules_dir="$dir/.agent/rules"
34
+ local rules_file="$rules_dir/image_dimensions.md"
35
+
36
+ mkdir -p "$rules_dir"
37
+
38
+ # Write header for the agent
39
+ cat << 'EOF' > "$rules_file"
40
+ # Codebase Remote Images
41
+
42
+ > [!NOTE]
43
+ > This file is strictly auto-generated by \`imgstat\`.
44
+ > It maps remote image URLs found in the codebase to their exact physical dimensions.
45
+ > AI assistants should use this dictionary when asked about layout constraints, native sizes, or aspect ratios of referenced images.
46
+
47
+ | Documented URL | Detected Size (W x H) |
48
+ |---|---|
49
+ EOF
50
+
51
+ local processed=0
52
+
53
+ for url in "${urls[@]}"; do
54
+ # Skip obvious non-images if possible, but Cloudinary etc don't have extensions
55
+ # so we attempt a shallow fetch for all to be safe.
56
+
57
+ # 1. Fetch direct URL
58
+ wget -q --content-disposition -P "$TEMP_DIR" "$url" || true
59
+
60
+ local all_files=( "$TEMP_DIR"/* )
61
+ local found_images=()
62
+
63
+ for file in "${all_files[@]}"; do
64
+ if [[ ! -f "$file" ]]; then continue; fi
65
+ local mimetype
66
+ mimetype=$(file -b --mime-type "$file" 2>/dev/null || true)
67
+ if [[ "$mimetype" == image/* ]]; then
68
+ found_images+=("$file")
69
+ fi
70
+ done
71
+
72
+ # Process only the first valid image found for the URL
73
+ if [[ ${#found_images[@]} -gt 0 ]]; then
74
+ local file="${found_images[0]}"
75
+ local dim
76
+ dim=$(get_dimensions "$file" || true)
77
+
78
+ if [[ -n "$dim" ]]; then
79
+ local w="${dim% *}"
80
+ local h="${dim#* }"
81
+ echo "| \`$url\` | **${w}x${h}** |" >> "$rules_file"
82
+ processed=$((processed + 1))
83
+ fi
84
+ fi
85
+
86
+ # Clean temp dir contents for the next URL to avoid collision
87
+ rm -rf "${TEMP_DIR:?}"/*
88
+ done
89
+
90
+ echo "Analysis complete!"
91
+ echo "Documented $processed valid image reference(s) into: $rules_file"
92
+ echo "This file can now be read by autonomous coding agents."
93
+ }
package/lib/inspect.sh ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utils.sh"
4
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scan.sh"
5
+
6
+ cmd_inspect() {
7
+ local dir="$1"
8
+ echo "Inspecting directory: $dir"
9
+
10
+ local files
11
+ mapfile -t files < <(scan_images "$dir")
12
+
13
+ if [[ ${#files[@]} -eq 0 ]]; then
14
+ echo "No images found in $dir."
15
+ return 0
16
+ fi
17
+
18
+ printf "%-50s | %-15s\n" "Filename" "Dimensions"
19
+ printf "%.s-" {1..70}
20
+ echo
21
+
22
+ for file in "${files[@]}"; do
23
+ local dim
24
+ dim=$(get_dimensions "$file" || true)
25
+ if [[ -n "$dim" ]]; then
26
+ local w="${dim% *}"
27
+ local h="${dim#* }"
28
+ printf "%-50s | %s x %s\n" "$(basename "$file")" "$w" "$h"
29
+ else
30
+ printf "%-50s | %-15s\n" "$(basename "$file")" "Error reading"
31
+ fi
32
+ done
33
+ }
package/lib/remote.sh ADDED
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utils.sh"
4
+
5
+ cmd_remote() {
6
+ local url="$1"
7
+ local dry_run="$2"
8
+
9
+ if [[ "$dry_run" == "true" ]]; then
10
+ echo "[Dry Run] Would fetch $url to extract dimensions. Nothing will be downloaded."
11
+ return 0
12
+ fi
13
+
14
+ echo "Fetching images from $url..."
15
+
16
+ local TEMP_DIR
17
+ TEMP_DIR=$(mktemp -d)
18
+ trap 'rm -rf "$TEMP_DIR"' EXIT
19
+
20
+ # 1. Fetch direct URL (handles direct images without extensions like Cloudinary)
21
+ wget -q --content-disposition -P "$TEMP_DIR" "$url" || true
22
+
23
+ # 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
+
26
+ local all_files=( "$TEMP_DIR"/* )
27
+ local found_images=()
28
+
29
+ # Filter only actual images
30
+ for file in "${all_files[@]}"; do
31
+ if [[ ! -f "$file" ]]; then continue; fi
32
+ local mimetype
33
+ mimetype=$(file -b --mime-type "$file" 2>/dev/null || true)
34
+ if [[ "$mimetype" == image/* ]]; then
35
+ found_images+=("$file")
36
+ fi
37
+ done
38
+
39
+ if [[ ${#found_images[@]} -eq 0 ]]; then
40
+ echo "No images found at $url."
41
+ return 0
42
+ fi
43
+
44
+ printf "%-50s | %-15s\n" "Filename" "Dimensions"
45
+ printf "%.s-" {1..70}
46
+ echo
47
+
48
+ for file in "${found_images[@]}"; do
49
+ local dim
50
+ dim=$(get_dimensions "$file" || true)
51
+ if [[ -n "$dim" ]]; then
52
+ local w="${dim% *}"
53
+ local h="${dim#* }"
54
+ printf "%-50s | %s x %s\n" "$(basename "$file")" "$w" "$h"
55
+ else
56
+ printf "%-50s | %-15s\n" "$(basename "$file")" "Error reading"
57
+ fi
58
+ done
59
+ }
package/lib/rename.sh ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/utils.sh"
4
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/scan.sh"
5
+
6
+ cmd_rename() {
7
+ local dir="$1"
8
+ local dry_run="$2" # true or false
9
+ local auto_yes="$3" # true or false
10
+
11
+ local files
12
+ mapfile -t files < <(scan_images "$dir")
13
+
14
+ if [[ ${#files[@]} -eq 0 ]]; then
15
+ echo "No images found in $dir."
16
+ return 0
17
+ fi
18
+
19
+ local to_rename=()
20
+ local new_names=()
21
+
22
+ for file in "${files[@]}"; do
23
+ local dim
24
+ dim=$(get_dimensions "$file" || true)
25
+ if [[ -z "$dim" ]]; then
26
+ continue
27
+ fi
28
+ local w="${dim% *}"
29
+ local h="${dim#* }"
30
+
31
+ local new_filename
32
+ new_filename=$(format_filename "$file" "$w" "$h")
33
+
34
+ if [[ -n "$new_filename" ]]; then
35
+ to_rename+=("$file")
36
+ new_names+=("$new_filename")
37
+ fi
38
+ done
39
+
40
+ if [[ ${#to_rename[@]} -eq 0 ]]; then
41
+ echo "All images already have dimensions in their filenames. Nothing to do."
42
+ return 0
43
+ fi
44
+
45
+ echo "Found ${#to_rename[@]} file(s) to rename:"
46
+ for i in "${!to_rename[@]}"; do
47
+ echo " $(basename "${to_rename[$i]}") -> $(basename "${new_names[$i]}")"
48
+ done
49
+
50
+ if [[ "$dry_run" == "true" ]]; then
51
+ echo
52
+ echo "[Dry Run] No files were changed."
53
+ return 0
54
+ fi
55
+
56
+ if [[ "$auto_yes" != "true" ]]; then
57
+ echo
58
+ read -r -p "Proceed with renaming? [y/N] " response
59
+ if [[ ! "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
60
+ echo "Aborted."
61
+ return 1
62
+ fi
63
+ fi
64
+
65
+ for i in "${!to_rename[@]}"; do
66
+ mv "${to_rename[$i]}" "${new_names[$i]}"
67
+ done
68
+
69
+ echo "Renaming complete."
70
+ }
package/lib/scan.sh ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Recursively find images, ignoring node_modules and .git
4
+ # Usage: scan_images "directory"
5
+ scan_images() {
6
+ local dir="$1"
7
+ if [[ ! -d "$dir" ]]; then
8
+ echo "Error: Directory '$dir' does not exist." >&2
9
+ exit 1
10
+ fi
11
+
12
+ # GNU find syntax for ignoring dirs and matching extensions case-insensitively
13
+ find "$dir" -type d \( -name "node_modules" -o -name ".git" \) -prune \
14
+ -o -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.avif" \) -print
15
+ }
package/lib/ui.sh ADDED
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/inspect.sh"
4
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/rename.sh"
5
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/remote.sh"
6
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/analyze.sh"
7
+
8
+ cmd_ui() {
9
+ local options=("Inspect local directory" "Rename local files" "Analyze remote URL" "Analyze codebase (.agent/rules)" "Quit")
10
+ local selected=0
11
+
12
+ # Hide cursor
13
+ tput civis
14
+
15
+ while true; do
16
+ echo -ne "\033[1;36mimgstat\033[0m: The tool for embedding image dimensions in filenames.\n"
17
+ echo "Select an operation mode (Use Up/Down arrows and Enter):"
18
+
19
+ for i in "${!options[@]}"; do
20
+ if [[ $i -eq $selected ]]; then
21
+ echo -e "\033[1;32m> ${options[$i]}\033[0m"
22
+ else
23
+ echo " ${options[$i]}"
24
+ fi
25
+ done
26
+
27
+ read -rsn1 key || true
28
+ if [[ $key == $'\x1b' ]]; then
29
+ read -rsn2 -t 0.1 seq || true
30
+ case "$seq" in
31
+ "[A") # Up arrow
32
+ ((selected--)) || true
33
+ if [[ $selected -lt 0 ]]; then selected=$((${#options[@]} - 1)); fi
34
+ ;;
35
+ "[B") # Down arrow
36
+ ((selected++)) || true
37
+ if [[ $selected -ge ${#options[@]} ]]; then selected=0; fi
38
+ ;;
39
+ esac
40
+ elif [[ $key == "" ]]; then # Enter key
41
+ # Clear menu lines before proceeding
42
+ tput cuu $((${#options[@]} + 2))
43
+ tput ed
44
+ break
45
+ fi
46
+
47
+ # Go up and clear to redraw
48
+ tput cuu $((${#options[@]} + 2))
49
+ tput ed
50
+ done
51
+
52
+ # Restore cursor
53
+ tput cnorm
54
+
55
+ case "$selected" in
56
+ 0)
57
+ read -e -p "Enter directory to inspect [./]: " dir
58
+ dir="${dir:-./}"
59
+ cmd_inspect "$dir"
60
+ ;;
61
+ 1)
62
+ read -e -p "Enter directory to rename [./]: " dir
63
+ dir="${dir:-./}"
64
+ cmd_rename "$dir" "false" "false"
65
+ ;;
66
+ 2)
67
+ read -e -p "Enter URL to analyze: " url
68
+ if [[ -z "$url" ]]; then
69
+ echo "URL cannot be empty."
70
+ else
71
+ cmd_remote "$url" "false"
72
+ fi
73
+ ;;
74
+ 3)
75
+ read -e -p "Enter codebase directory to analyze [./]: " dir
76
+ dir="${dir:-./}"
77
+ cmd_analyze "$dir"
78
+ ;;
79
+ 4)
80
+ echo "Exiting."
81
+ ;;
82
+ esac
83
+ }
package/lib/utils.sh ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Check if required command is available
4
+ require_command() {
5
+ local cmd="$1"
6
+ if ! command -v "$cmd" &> /dev/null; then
7
+ echo "Error: Required command '$cmd' is not installed." >&2
8
+ exit 1
9
+ fi
10
+ }
11
+
12
+ # Get Image Dimensions using ImageMagick 'identify'
13
+ # Usage: get_dimensions "file.jpg"
14
+ # Outputs: width height
15
+ get_dimensions() {
16
+ local file="$1"
17
+ # -ping is faster because it doesn't read the whole image data if possible
18
+ if ! dim=$(identify -ping -format "%w %h" "$file" 2>/dev/null); then
19
+ return 1
20
+ fi
21
+ echo "$dim"
22
+ }
23
+
24
+ # Generate the target filename if it doesn't already have the dimensions
25
+ # Usage: format_filename "file.jpg" "800" "600"
26
+ # Outputs: target_file.jpg (or nothing if it shouldn't be renamed)
27
+ format_filename() {
28
+ local file="$1"
29
+ local w="$2"
30
+ local h="$3"
31
+
32
+ local filename
33
+ filename=$(basename -- "$file")
34
+ local ext="${filename##*.}"
35
+ local name="${filename%.*}"
36
+
37
+ # If file has no extension
38
+ if [[ "$name" == "$filename" ]]; then
39
+ ext=""
40
+ fi
41
+
42
+ local dim_suffix="${w}x${h}"
43
+
44
+ # Check if already ends with -WxH
45
+ if [[ "$name" == *-*x* ]]; then
46
+ local current_suffix="${name##*-}"
47
+ if [[ "$current_suffix" == "$dim_suffix" ]]; then
48
+ # Already correct
49
+ return 0
50
+ fi
51
+ fi
52
+
53
+ local new_name
54
+ if [[ -n "$ext" ]]; then
55
+ new_name="${name}-${dim_suffix}.${ext}"
56
+ else
57
+ new_name="${name}-${dim_suffix}"
58
+ fi
59
+
60
+ local dirname
61
+ dirname=$(dirname -- "$file")
62
+
63
+ echo "$dirname/$new_name"
64
+ }
package/package.json CHANGED
@@ -1,30 +1,18 @@
1
1
  {
2
2
  "name": "imgstat",
3
- "version": "1.0.4",
4
- "description": "Recursively rename images with dimensions so LLMs can see them.",
5
- "bin": {
6
- "imgstat": "./imagestat",
7
- "imagestat": "./imagestat"
3
+ "version": "2.0.1",
4
+ "description": "Embeds image dimensions directly into filenames for natural AI context.",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1"
8
7
  },
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/isaac0yen/imgstat.git"
8
+ "bin": {
9
+ "imgstat": "./bin/imgstat"
12
10
  },
11
+ "license": "MIT",
13
12
  "keywords": [
13
+ "cli",
14
14
  "image",
15
- "resize",
16
- "rename",
17
15
  "dimensions",
18
- "cli",
19
- "bash"
20
- ],
21
- "author": "Isaac Oyeniyi",
22
- "license": "MIT",
23
- "bugs": {
24
- "url": "https://github.com/isaac0yen/imgstat/issues"
25
- },
26
- "homepage": "https://github.com/isaac0yen/imgstat#readme",
27
- "files": [
28
- "imagestat"
16
+ "ai"
29
17
  ]
30
- }
18
+ }
package/readme.md CHANGED
@@ -4,27 +4,28 @@
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
6
  **Give AI context about your images.**
7
+ imgstat is a CLI tool that embeds image dimensions directly into filenames, or analyzes remote imagery, to give AI context without needing external parsers.
7
8
 
8
- LLMs can't see image dimensions. `imgstat` recursively scans directories and renames images to include their size (e.g., `photo.jpg` → `photo-1920x1080.jpg`), so your AI coding assistant knows exactly what it's working with.
9
+ ## Features
9
10
 
10
- **Features:**
11
- - **AI-Ready Context**: Embeds dimensions directly in filenames.
12
- - **Idempotent**: Smartly skips images that are already renamed.
13
- - **Recursive**: Handles deep directory structures.
11
+ imgstat handles renaming smoothly and idempotently—it will never re-append dimensions to a file that already has them. When dealing with remote imagery from URLs or scanning your codebase, it securely generates dimension reports without leaving permanent downloads on your machine. For AI integration, the `analyze` mode seamlessly builds an `.agent/rules/image_dimensions.md` file, giving your local language models instant, zero-config context about the images used in your project.
14
12
 
15
- ## Installation & Usage
13
+ ## Usage
16
14
 
17
- **Install:**
18
- ```bash
19
- npm install -g imgstat
20
- ```
15
+ Run `imgstat` with no arguments to get an interactive menu. You will be prompted to select the mode you want to use.
21
16
 
22
- **Run:**
23
17
  ```bash
24
- imgstat [directory]
18
+ imgstat
25
19
  ```
26
20
 
27
- **Download & Process:**
28
- ```bash
29
- imgstat -u https://example.com/images
30
- ```
21
+ ## Contribution Rules
22
+
23
+ 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.
24
+
25
+ **Every file in `lib/` must have one clear responsibility.** If you find yourself writing image discovery logic inside `rename.sh`, stop and move it to `scan.sh`.
26
+
27
+ **No feature should require memorizing new flags.** If it can be handled by a mode or an interactive prompt, prefer that.
28
+
29
+ **Remote mode must never leave files on disk.** The `trap` cleanup is non-negotiable.
30
+
31
+ **Dry-run must work for any operation that touches files.** This is a safety contract with users.