skills/specs/001-screenshot-analysis/contracts/script-interface.md
dan 5fea49b7c0 feat(tufte-press): evolve skill to complete workflow with JSON generation and build automation
- Transform tufte-press from reference guide to conversation-aware generator
- Add JSON generation from conversation context following strict schema
- Create build automation scripts with Nix environment handling
- Integrate CUPS printing with duplex support
- Add comprehensive workflow documentation

Scripts added:
- skills/tufte-press/scripts/generate-and-build.sh (242 lines)
- skills/tufte-press/scripts/build-card.sh (23 lines)

Documentation:
- Updated SKILL.md with complete workflow instructions (370 lines)
- Updated README.md with usage examples (340 lines)
- Created SKILL-DEVELOPMENT-STRATEGY-tufte-press.md (450 lines)
- Added worklog: 2025-11-10-tufte-press-skill-evolution.org

Features:
- Agent generates valid JSON from conversation
- Schema validation before build (catches errors early)
- Automatic Nix shell entry for dependencies
- PDF build via tufte-press toolchain
- Optional print with duplex support
- Self-contained margin notes enforced
- Complete end-to-end testing

Workflow: Conversation → JSON → Validate → Build → Print

Related: niri-window-capture, screenshot-latest, worklog skills
2025-11-10 15:03:44 -08:00

9.8 KiB

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:

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:

# 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:

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:

# 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:

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:

# 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:

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:

# 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:

    set -euo pipefail
    
  2. Provide help flag:

    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:

    chmod +x scripts/*.sh
    
  6. Include shebang:

    #!/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:

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

    SCREENSHOT_PATH=$(~/.claude/skills/screenshot-analysis/scripts/find-latest-screenshot.sh "$SCREENSHOT_DIR")
    
  2. 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)