- 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
17 KiB
Code Review: niri-window-capture Skill
Review Date: 2025-11-09
Reviewer: OpenCode Agent
Scope: Complete skill review including SKILL.md, SECURITY.md, and all scripts
Overall Assessment
Status: ✅ APPROVED with minor recommendations
Summary: High-quality, security-conscious skill with comprehensive documentation. Scripts follow bash best practices. Security considerations are well-documented and appropriate audit logging is implemented.
Strengths:
- Excellent security documentation
- Comprehensive audit logging
- Proper error handling
- Good use of
set -euo pipefail - Safe jq usage with
--argfor variable passing - Clear separation of concerns across scripts
Areas for Improvement (minor):
- Some opportunities for additional input validation
- Could benefit from more defensive programming around filesystem assumptions
- Consider adding timeout handling for niri commands
Documentation Review
SKILL.md (185 lines)
✅ Strengths:
- Clear security warning at the top
- Comprehensive "When to Use" section with concrete examples
- Well-documented helper scripts with usage examples
- Direct niri commands provided for advanced use
- Common workflows documented
- Clear guidelines section
- Requirements clearly stated
Recommendations:
- Consider adding version compatibility note (currently says "niri 25.08")
- Could add troubleshooting section for common issues
- Examples directory referenced but should verify contents match docs
Score: 9/10
SECURITY.md (196 lines)
✅ Strengths:
- Excellent threat model section
- Clear explanation of privacy implications
- Concrete protection mechanisms with code examples
- Audit trail documentation
- Window blocking configuration examples
- Attack scenarios documented
- Recommendations prioritized and actionable
✅ Outstanding:
- The security awareness demonstrated here is exemplary
- Cross-workspace invisibility clearly explained
- Clipboard pollution documented (important edge case)
- Upstream feature request for
--no-clipboardshows proactive thinking
Recommendations:
- Consider adding emergency response section ("If you suspect unauthorized captures...")
- Could document log retention policy
Score: 10/10
Script Reviews
1. capture-focused.sh (32 lines)
Purpose: Capture currently focused window
✅ Bash Best Practices:
- ✅ Proper shebang:
#!/usr/bin/env bash - ✅ Error handling:
set -euo pipefail - ✅ Variables: Uppercase for constants (
LOG_TAG,WINDOW_ID) - ✅ Error messages to stderr:
>&2 - ✅ Exit codes:
exit 1on error - ✅ JSON parsing: Safe with
jq -r - ✅ Audit logging: All captures logged
✅ Security:
- ✅ Logs before and after capture
- ✅ Includes window metadata in logs
- ✅ No shell injection vulnerabilities
- ✅ jq properly handles untrusted input
✅ Logic:
- ✅ Checks if window exists before capture
- ✅ Handles null responses from jq
- ✅ Uses sleep 0.1 for filesystem sync (good practice)
- ✅ Returns screenshot path to stdout
⚠️ Minor Issues:
-
Hardcoded screenshot directory:
SCREENSHOT_PATH=$(ls -t ~/Pictures/Screenshots/*.png | head -1)- Could fail if directory doesn't exist or no PNGs present
- Recommend: Check directory exists first or query niri config
-
Potential race condition:
- What if another process creates a screenshot in that 0.1s window?
- Unlikely but possible
- Recommendation: Capture timestamp before screenshot, filter by time
-
Silent failure handling:
niri msg action screenshot-window ... >/dev/null 2>&1- Error output suppressed completely
- Recommendation: Check exit code or at least log errors
Suggested Improvements:
# Check screenshot directory exists
SCREENSHOT_DIR="${XDG_PICTURES_DIR:-$HOME/Pictures}/Screenshots"
if [[ ! -d "$SCREENSHOT_DIR" ]]; then
logger -t "$LOG_TAG" "ERROR: Screenshot directory does not exist: $SCREENSHOT_DIR"
echo "Error: Screenshot directory not found" >&2
exit 1
fi
# Capture timestamp before screenshot for safer lookup
BEFORE_TIME=$(date +%s)
# Capture with error checking
if ! niri msg action screenshot-window --id "$WINDOW_ID" --write-to-disk true 2>&1 | logger -t "$LOG_TAG"; then
logger -t "$LOG_TAG" "ERROR: Screenshot command failed for window $WINDOW_ID"
echo "Error: Screenshot capture failed" >&2
exit 1
fi
# More robust screenshot finding
sleep 0.15 # Slightly longer for filesystem
SCREENSHOT_PATH=$(find "$SCREENSHOT_DIR" -name "*.png" -newer <(date -d "@$BEFORE_TIME" +%Y-%m-%d) | sort -r | head -1)
if [[ -z "$SCREENSHOT_PATH" ]]; then
logger -t "$LOG_TAG" "ERROR: Screenshot file not found after capture"
echo "Error: Screenshot file not created" >&2
exit 1
fi
Score: 8.5/10
2. capture-by-title.sh (41 lines)
Purpose: Find and capture window by partial title match
✅ Bash Best Practices:
- ✅ Proper shebang and error handling
- ✅ Input validation:
[[ $# -lt 1 ]] - ✅ Safe jq usage:
jq --arg search "$SEARCH" - ✅ Case-insensitive matching
- ✅ Comprehensive logging
✅ Security:
- ✅ No shell injection via search term (jq handles it safely)
- ✅ Logs matched term for audit trail
- ✅ All captures logged with context
✅ Logic:
- ✅ Takes first match (
.[0]) - ✅ Clear error if no match found
- ✅ Extracts full metadata before capture
⚠️ Minor Issues:
-
Empty check insufficient:
if [[ -z "$WINDOW_META" ]]; then- Should also check for "null" result from jq
- Recommendation:
if [[ -z "$WINDOW_META" || "$WINDOW_META" == "null" ]]; then
-
Ambiguous matches:
- Takes first match silently
- If multiple windows match, user might not know which one
- Recommendation: Log how many matches found, or make it explicit in error message
-
Same filesystem issues as capture-focused.sh:
- Hardcoded path
- No verification screenshot was created
Suggested Improvements:
# Find ALL matches first
ALL_MATCHES=$(niri msg --json windows | jq --arg search "$SEARCH" \
'[.[] | select(.title | ascii_downcase | contains($search | ascii_downcase))]')
MATCH_COUNT=$(echo "$ALL_MATCHES" | jq 'length')
if [[ "$MATCH_COUNT" -eq 0 ]]; then
logger -t "$LOG_TAG" "ERROR: No window found matching title '$SEARCH'"
echo "Error: No window found with title matching '$SEARCH'" >&2
exit 1
fi
if [[ "$MATCH_COUNT" -gt 1 ]]; then
logger -t "$LOG_TAG" "WARNING: Multiple windows match '$SEARCH' ($MATCH_COUNT found), using first"
echo "Warning: Found $MATCH_COUNT matches, using first" >&2
fi
WINDOW_META=$(echo "$ALL_MATCHES" | jq '.[0]')
Score: 8/10
3. capture-all-windows.sh (37 lines)
Purpose: Capture all windows to a directory with JSON output
✅ Bash Best Practices:
- ✅ Proper error handling
- ✅ Configurable output directory with default
- ✅ Creates directory if needed
- ✅ Sanitizes title for filename
✅ Security:
- ✅ Title sanitization prevents path traversal:
tr '/' '-' - ✅ Limits filename length:
cut -c1-50 - ✅ No shell injection vulnerabilities
✅ Logic:
- ✅ Moves screenshots to organized directory
- ✅ Outputs structured JSON at the end
- ✅ Uses jq streaming for clean JSON array output
⚠️ Minor Issues:
-
No audit logging:
- Unlike other scripts, this one doesn't use
logger - High-risk since it captures EVERYTHING
- Recommendation: Add logging for each capture
- Unlike other scripts, this one doesn't use
-
Filename collision handling:
OUTPUT_PATH="$OUTPUT_DIR/window-${id}-${SAFE_TITLE}.png"- What if two windows have same title?
- IDs should make it unique but worth adding timestamp
- Recommendation: Add timestamp or counter
-
Error handling in loop:
- If one capture fails, loop continues silently
- mv could fail if file doesn't exist
- Recommendation: Add error checking in loop
-
Metadata lookup inefficiency:
METADATA=$(niri msg --json windows | jq --arg id "$id" '.[] | select(.id == ($id | tonumber))')- Queries all windows for each ID
- Could query once at start and reference
- Minor performance issue
Suggested Improvements:
#!/usr/bin/env bash
# Capture all windows and output JSON mapping window metadata to screenshot paths
set -euo pipefail
LOG_TAG="niri-capture"
OUTPUT_DIR="${1:-/tmp/niri-window-captures}"
mkdir -p "$OUTPUT_DIR"
echo "Capturing all windows to $OUTPUT_DIR..." >&2
logger -t "$LOG_TAG" "Starting capture of all windows to $OUTPUT_DIR"
# Get all windows metadata once
ALL_WINDOWS=$(niri msg --json windows)
WINDOW_COUNT=$(echo "$ALL_WINDOWS" | jq 'length')
logger -t "$LOG_TAG" "Found $WINDOW_COUNT windows to capture"
# Capture each window
echo "$ALL_WINDOWS" | jq -c '.[]' | while IFS= read -r metadata; do
ID=$(echo "$metadata" | jq -r '.id')
TITLE=$(echo "$metadata" | jq -r '.title')
APP_ID=$(echo "$metadata" | jq -r '.app_id')
WORKSPACE=$(echo "$metadata" | jq -r '.workspace_id')
# Sanitize title for filename
SAFE_TITLE=$(echo "$TITLE" | tr '/' '-' | tr ' ' '_' | cut -c1-50)
TIMESTAMP=$(date +%s)
OUTPUT_PATH="$OUTPUT_DIR/window-${ID}-${TIMESTAMP}-${SAFE_TITLE}.png"
logger -t "$LOG_TAG" "Capturing window $ID: '$TITLE' (workspace: $WORKSPACE)"
# Capture window
if niri msg action screenshot-window --id "$ID" --write-to-disk true >/dev/null 2>&1; then
sleep 0.1
# Move from Screenshots to our output dir
LATEST=$(ls -t ~/Pictures/Screenshots/*.png 2>/dev/null | head -1)
if [[ -n "$LATEST" ]] && mv "$LATEST" "$OUTPUT_PATH" 2>/dev/null; then
logger -t "$LOG_TAG" "Screenshot saved: $OUTPUT_PATH"
# Output JSON for this window
echo "$metadata" | jq --arg path "$OUTPUT_PATH" '. + {screenshot_path: $path}'
else
logger -t "$LOG_TAG" "ERROR: Failed to move screenshot for window $ID"
echo "$metadata" | jq --arg path "" '. + {screenshot_path: $path, error: "file_move_failed"}'
fi
else
logger -t "$LOG_TAG" "ERROR: Failed to capture window $ID"
echo "$metadata" | jq --arg path "" '. + {screenshot_path: $path, error: "capture_failed"}'
fi
done | jq -s '.'
logger -t "$LOG_TAG" "Completed capture of all windows"
Score: 7.5/10
Security Analysis
Threat Coverage
✅ Well Handled:
- Audit Trail: Comprehensive logging to systemd journal
- Privacy Awareness: Clear documentation of invisible capture capability
- Protection Mechanisms: Window blocking configuration documented
- Attack Scenarios: Documented in SECURITY.md
- No Shell Injection: Safe use of jq
--argeverywhere
⚠️ Could Improve:
- Rate Limiting: No protection against rapid-fire captures
- Privilege Escalation: No check if running as expected user
- IPC Socket Security: Assumes niri socket is properly protected (document this assumption)
- Screenshot Directory Permissions: Should verify/enforce 700 permissions
Recommendations
- Add rate limiting check:
# At start of scripts
RECENT_CAPTURES=$(journalctl --user -t niri-capture --since "10 seconds ago" | wc -l)
if [[ "$RECENT_CAPTURES" -gt 10 ]]; then
logger -t "$LOG_TAG" "ERROR: Rate limit exceeded ($RECENT_CAPTURES captures in 10s)"
echo "Error: Too many captures in short time, potential abuse" >&2
exit 1
fi
- Verify screenshot directory permissions:
# In each script's initialization
SCREENSHOT_DIR="${HOME}/Pictures/Screenshots"
if [[ -d "$SCREENSHOT_DIR" ]]; then
PERMS=$(stat -c %a "$SCREENSHOT_DIR")
if [[ "$PERMS" != "700" ]]; then
logger -t "$LOG_TAG" "WARNING: Screenshot directory has weak permissions: $PERMS (should be 700)"
fi
fi
Compliance with AGENTS.md Guidelines
Checking against /home/dan/proj/skills/AGENTS.md:
✅ Bash Script Requirements:
- ✅ Shebang:
#!/usr/bin/env bash- PASS - ✅ Error handling:
set -euo pipefail- PASS - ✅ Audit logging:
logger -t <tag>- PASS - ✅ Variables:
UPPER_CASEconstants,lower_caselocals - PASS (mostly, some could be local) - ✅ Error output:
echo "Error: ..." >&2- PASS - ✅ Dependency checks: Missing! - FAIL
- ✅ JSON parsing:
jqwith--arg- PASS
Missing: Dependency Checks
None of the scripts check if required commands exist. Should add:
# At start of each script
for cmd in niri jq logger; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "Error: Required command not found: $cmd" >&2
exit 1
fi
done
✅ Skill Structure Requirements:
- ✅ SKILL.md with YAML frontmatter - PASS
- ✅ Required sections: When to Use, Process, Requirements - PASS
- ✅ Security-sensitive: SECURITY.md with threat model - PASS
- ✅ README.md with installation instructions - PASS
Testing Recommendations
Unit Tests Needed
-
capture-focused.sh:
- Test with no focused window
- Test with focused window
- Test screenshot directory doesn't exist
- Test niri command failure
-
capture-by-title.sh:
- Test no match found
- Test single match
- Test multiple matches
- Test special characters in title
- Test empty/whitespace search term
-
capture-all-windows.sh:
- Test empty window list
- Test single window
- Test multiple windows
- Test filename collisions
- Test move failure
Integration Tests Needed
- Verify audit logs are written correctly
- Verify screenshots created with correct permissions
- Verify cross-workspace capture works
- Verify blocked windows are not captured (test block-out-from)
Recommendations Summary
High Priority
- Add dependency checks to all scripts
- Add error checking for niri command failures (don't silent discard)
- Verify screenshot directory exists before attempting capture
- Check screenshot file was created after capture command
Medium Priority
- Add rate limiting to prevent abuse
- Improve error messages for multiple window matches
- Use timestamp-based screenshot finding instead of ls -t
- Add audit logging to capture-all-windows.sh
- Verify/warn about screenshot directory permissions
Low Priority
- Query niri config for screenshot-path instead of hardcoding
- Add timeout handling for niri commands
- Consider adding a "dry-run" mode
- Add unit tests for all scripts
Example Improvements
Improved Script Template
#!/usr/bin/env bash
# Script purpose
set -euo pipefail
LOG_TAG="niri-capture"
# Dependency checks
for cmd in niri jq logger; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "Error: Required command not found: $cmd" >&2
exit 1
fi
done
# Rate limiting
RECENT_CAPTURES=$(journalctl --user -t "$LOG_TAG" --since "10 seconds ago" 2>/dev/null | wc -l)
if [[ "$RECENT_CAPTURES" -gt 10 ]]; then
logger -t "$LOG_TAG" "ERROR: Rate limit exceeded"
echo "Error: Too many recent captures, potential abuse" >&2
exit 1
fi
# Verify screenshot directory
SCREENSHOT_DIR="${XDG_PICTURES_DIR:-$HOME/Pictures}/Screenshots"
if [[ ! -d "$SCREENSHOT_DIR" ]]; then
logger -t "$LOG_TAG" "ERROR: Screenshot directory not found: $SCREENSHOT_DIR"
echo "Error: Screenshot directory does not exist" >&2
exit 1
fi
# Main script logic here...
Final Verdict
Overall Score: 8.5/10
Strengths:
- Excellent security awareness and documentation
- Good bash practices (error handling, safe variable usage)
- Comprehensive audit logging
- Clear, well-organized code
- Security-first design
Critical Issues: None
Non-Critical Issues:
- Missing dependency checks (easily fixed)
- Some error handling could be more robust
- Minor efficiency improvements possible
Recommendation: ✅ APPROVED FOR DEPLOYMENT with suggestion to implement high-priority improvements in next iteration.
This is a well-crafted, security-conscious skill that demonstrates mature understanding of both bash scripting and security considerations. The comprehensive SECURITY.md and thorough audit logging show excellent judgment for a capability with privacy implications.
Action Items
For the next version, consider:
- Add dependency checks to all scripts
- Improve error handling for niri command failures
- Add screenshot directory validation
- Implement rate limiting
- Add unit tests
- Consider adding a CHANGELOG.md to track improvements
Review Completed: 2025-11-09
Status: APPROVED with recommendations
Next Review: After implementing high-priority improvements