# Script Interface Contract: Screenshot Analysis Skill

**Feature**: 001-screenshot-analysis  
**Date**: 2025-11-08  
**Type**: Bash Script CLI Interface

## Overview

This document defines the interface contract for bash helper scripts used by the screenshot analysis skill. These scripts are invoked by the SKILL.md instructions and must adhere to the following specifications.

---

## Script 1: find-latest-screenshot.sh

**Purpose**: Locate the most recent screenshot file in a directory

**Location**: `skills/screenshot-analysis/scripts/find-latest-screenshot.sh`

**Interface**:
```bash
find-latest-screenshot.sh [DIRECTORY]
```

**Arguments**:
- `DIRECTORY` (optional): Path to screenshot directory
  - Default: Value from config or `~/Pictures/Screenshots`
  - Type: String (absolute or tilde-expanded path)

**Output** (stdout):
- Success with file found: Absolute path to most recent screenshot file
  - Example: `/home/user/Pictures/Screenshots/screenshot-2025-11-08.png`
- Success with no files: Empty string (or optional message)
  - Example: (empty) or `No screenshots found in /home/user/Pictures/Screenshots`

**Errors** (stderr):
- Directory not found: `Error: Directory not found: {path}`
- Permission denied: `Error: Directory not readable (permission denied): {path}`
- Invalid argument: `Error: Invalid directory path: {path}`

**Exit Codes**:
- `0`: Success (file found or directory empty)
- `1`: Error (directory not found, permission denied, invalid argument)

**Behavior**:
- Scans for files matching extensions: `.png`, `.jpg`, `.jpeg` (case-insensitive)
- Excludes symlinks (only regular files via `find -type f`)
- Sorts by modification time (newest first)
- Applies lexicographic tiebreaker for identical timestamps
- Returns first result after sorting

**Example Usage**:
```bash
# Use default directory
$ ./find-latest-screenshot.sh
/home/user/Pictures/Screenshots/screenshot-2025-11-08-143022.png

# Use custom directory
$ ./find-latest-screenshot.sh ~/Documents/Screenshots
/home/user/Documents/Screenshots/image-20251108.png

# Empty directory
$ ./find-latest-screenshot.sh /tmp/empty

# Directory doesn't exist
$ ./find-latest-screenshot.sh /nonexistent
Error: Directory not found: /nonexistent
```

---

## Script 2: find-nth-screenshot.sh

**Purpose**: Locate the Nth most recent screenshot file

**Location**: `skills/screenshot-analysis/scripts/find-nth-screenshot.sh`

**Interface**:
```bash
find-nth-screenshot.sh N [DIRECTORY]
```

**Arguments**:
- `N` (required): Screenshot index (1-based, 1 = most recent)
  - Type: Positive integer
  - Example: `2` for "previous screenshot", `3` for "third-most-recent"
- `DIRECTORY` (optional): Path to screenshot directory
  - Default: Value from config or `~/Pictures/Screenshots`

**Output** (stdout):
- Success with file found: Absolute path to Nth most recent screenshot
- Success but N exceeds count: Empty string

**Errors** (stderr):
- Invalid N: `Error: N must be a positive integer, got: {N}`
- N exceeds available files: `Error: Only {count} screenshot(s) available, cannot retrieve #{N}`
- Directory errors: Same as find-latest-screenshot.sh

**Exit Codes**:
- `0`: Success (file found)
- `1`: Error (invalid N, directory error, N exceeds count)

**Behavior**:
- Same filtering/sorting logic as find-latest-screenshot.sh
- Selects Nth result from sorted list

**Example Usage**:
```bash
# Get previous screenshot (2nd most recent)
$ ./find-nth-screenshot.sh 2
/home/user/Pictures/Screenshots/screenshot-2025-11-08-120000.png

# Only 1 screenshot available
$ ./find-nth-screenshot.sh 2
Error: Only 1 screenshot(s) available, cannot retrieve #2

# Invalid N
$ ./find-nth-screenshot.sh 0
Error: N must be a positive integer, got: 0
```

---

## Script 3: filter-by-time.sh

**Purpose**: Find screenshots within a time range

**Location**: `skills/screenshot-analysis/scripts/filter-by-time.sh`

**Interface**:
```bash
filter-by-time.sh TIME_SPEC [DIRECTORY]
```

**Arguments**:
- `TIME_SPEC` (required): Time filter specification
  - `today`: Screenshots from today (00:00:00 to now)
  - `{N}m`: Last N minutes (e.g., `5m` for last 5 minutes)
  - `{N}h`: Last N hours (e.g., `2h` for last 2 hours)
  - `{N}d`: Last N days (e.g., `7d` for last week)
- `DIRECTORY` (optional): Path to screenshot directory

**Output** (stdout):
- Success: Newline-separated list of matching screenshot paths (sorted newest first)
- No matches: Empty string

**Errors** (stderr):
- Invalid time spec: `Error: Invalid time specification: {TIME_SPEC}`
- Directory errors: Same as find-latest-screenshot.sh

**Exit Codes**:
- `0`: Success (files found or no matches)
- `1`: Error (invalid time spec, directory error)

**Example Usage**:
```bash
# Screenshots from today
$ ./filter-by-time.sh today
/home/user/Pictures/Screenshots/screenshot-2025-11-08-143022.png
/home/user/Pictures/Screenshots/screenshot-2025-11-08-120000.png

# Last 5 minutes
$ ./filter-by-time.sh 5m
/home/user/Pictures/Screenshots/screenshot-2025-11-08-143022.png

# Last 2 hours (none found)
$ ./filter-by-time.sh 2h

```

---

## Script 4: load-config.sh

**Purpose**: Load configuration and return screenshot directory path

**Location**: `skills/screenshot-analysis/scripts/load-config.sh`

**Interface**:
```bash
load-config.sh
```

**Arguments**: None

**Output** (stdout):
- Absolute path to screenshot directory (default or from config)

**Errors** (stderr):
- Malformed config JSON: `Warning: Failed to parse config.json, using default directory`
- Missing jq: `Warning: jq not found, using default directory`
- Invalid screenshot_dir: `Error: Configured screenshot_dir is not a valid directory: {path}`

**Exit Codes**:
- `0`: Success (config loaded or default used)
- `1`: Error (configured directory invalid)

**Behavior**:
- Checks for config file in order:
  1. `~/.config/opencode/skills/screenshot-analysis/config.json`
  2. `~/.claude/skills/screenshot-analysis/config.json`
- Parses JSON using `jq -r '.screenshot_dir // empty'`
- Expands tilde in paths
- Falls back to `~/Pictures/Screenshots` if config missing/malformed
- Validates returned directory exists and is readable

**Example Usage**:
```bash
# No config file (use default)
$ ./load-config.sh
/home/user/Pictures/Screenshots

# Valid config with custom directory
$ ./load-config.sh
/home/user/Documents/Screenshots

# Malformed config JSON
$ ./load-config.sh
Warning: Failed to parse config.json, using default directory
/home/user/Pictures/Screenshots
```

---

## Common Contract Elements

### All Scripts Must:

1. **Set strict error handling**:
   ```bash
   set -euo pipefail
   ```

2. **Provide help flag**:
   ```bash
   if [[ "${1:-}" == "-h" ]] || [[ "${1:-}" == "--help" ]]; then
       show_help
       exit 0
   fi
   ```

3. **Use absolute paths** in output (no relative paths)

4. **Handle missing dependencies** gracefully:
   - Check for required commands (`jq`, `find`, `stat`)
   - Provide actionable error messages

5. **Be executable**:
   ```bash
   chmod +x scripts/*.sh
   ```

6. **Include shebang**:
   ```bash
   #!/usr/bin/env bash
   ```

7. **Support testing** with fixtures:
   - Accept directory argument for test isolation
   - Don't hard-code paths

---

## Integration Contract

### SKILL.md → Scripts

**How SKILL.md invokes scripts**:

```markdown
1. Run helper script to load config:
   ```bash
   SCREENSHOT_DIR=$(~/.claude/skills/screenshot-analysis/scripts/load-config.sh)
   ```

2. Find latest screenshot:
   ```bash
   SCREENSHOT_PATH=$(~/.claude/skills/screenshot-analysis/scripts/find-latest-screenshot.sh "$SCREENSHOT_DIR")
   ```

3. Pass path to agent's image analysis capability
```

**Contract Requirements**:
- Scripts output ONLY the requested data to stdout (no logging/diagnostics)
- Errors/warnings go to stderr
- Exit codes indicate success/failure clearly
- Scripts are idempotent (multiple calls produce same result)

---

## Version Compatibility

**Bash Version**: 4.0+ required for:
- `[[ ]]` conditional expressions
- `$(...)` command substitution
- Arrays and associative arrays (future use)

**GNU Coreutils**: Standard versions (no special features)
- `find`: POSIX-compliant options
- `stat`: GNU stat format strings (`-c`)
- `sort`: Numeric sorting (`-rn`)

**External Dependencies**:
- `jq` 1.5+ (JSON parsing)
- All dependencies checked at runtime with fallback behavior

---

## Testing Contract

Each script must have corresponding bats tests:

**Required Test Coverage**:
1. Happy path (valid input, expected output)
2. Empty directory (no screenshots)
3. Invalid directory (not found, permission denied)
4. Symlink filtering (symlinks present, excluded correctly)
5. Timestamp tiebreaker (multiple files, same mtime)
6. Edge case: Very large directory (performance test)

**Test Location**: `tests/skills/screenshot-analysis/unit/test-{script-name}.bats`

**Example Test Structure**:
```bash
#!/usr/bin/env bats

setup() {
    TEST_DIR="$(mktemp -d)"
    export TEST_DIR
}

teardown() {
    rm -rf "$TEST_DIR"
}

@test "script-name: happy path" {
    # ... test code
}
```

---

## Error Message Standards

**Format**: `{Level}: {Description}: {Context}`

**Examples**:
- `Error: Directory not found: /home/user/nonexistent`
- `Warning: jq not found, using default directory`
- `Error: Only 5 screenshot(s) available, cannot retrieve #10`

**Guidelines**:
- Be specific (include paths, values)
- Be actionable (user knows how to fix)
- Use consistent terminology (directory, screenshot, file)
- No technical jargon (avoid "errno", "ENOENT")

---

## Performance Contracts

| Script | Max Execution Time | Conditions |
|--------|--------------------|------------|
| find-latest-screenshot.sh | <1s | Up to 1000 files |
| find-nth-screenshot.sh | <1s | Up to 1000 files |
| filter-by-time.sh | <1s | Up to 1000 files |
| load-config.sh | <10ms | Config file <1KB |

**Measurement**: Time from script invocation to stdout output complete

**Degradation**: Graceful performance degradation up to 10,000 files (<5s acceptable)
