- 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
551 lines
17 KiB
Markdown
551 lines
17 KiB
Markdown
# 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 `--arg` for 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:**
|
|
1. Consider adding version compatibility note (currently says "niri 25.08")
|
|
2. Could add troubleshooting section for common issues
|
|
3. 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-clipboard` shows proactive thinking
|
|
|
|
**Recommendations:**
|
|
1. Consider adding emergency response section ("If you suspect unauthorized captures...")
|
|
2. 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 1` on 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:**
|
|
|
|
1. **Hardcoded screenshot directory:**
|
|
```bash
|
|
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
|
|
|
|
2. **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
|
|
|
|
3. **Silent failure handling:**
|
|
```bash
|
|
niri msg action screenshot-window ... >/dev/null 2>&1
|
|
```
|
|
- Error output suppressed completely
|
|
- Recommendation: Check exit code or at least log errors
|
|
|
|
**Suggested Improvements:**
|
|
```bash
|
|
# 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:**
|
|
|
|
1. **Empty check insufficient:**
|
|
```bash
|
|
if [[ -z "$WINDOW_META" ]]; then
|
|
```
|
|
- Should also check for "null" result from jq
|
|
- Recommendation: `if [[ -z "$WINDOW_META" || "$WINDOW_META" == "null" ]]; then`
|
|
|
|
2. **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
|
|
|
|
3. **Same filesystem issues as capture-focused.sh:**
|
|
- Hardcoded path
|
|
- No verification screenshot was created
|
|
|
|
**Suggested Improvements:**
|
|
```bash
|
|
# 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:**
|
|
|
|
1. **No audit logging:**
|
|
- Unlike other scripts, this one doesn't use `logger`
|
|
- High-risk since it captures EVERYTHING
|
|
- Recommendation: Add logging for each capture
|
|
|
|
2. **Filename collision handling:**
|
|
```bash
|
|
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
|
|
|
|
3. **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
|
|
|
|
4. **Metadata lookup inefficiency:**
|
|
```bash
|
|
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:**
|
|
```bash
|
|
#!/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:**
|
|
1. **Audit Trail**: Comprehensive logging to systemd journal
|
|
2. **Privacy Awareness**: Clear documentation of invisible capture capability
|
|
3. **Protection Mechanisms**: Window blocking configuration documented
|
|
4. **Attack Scenarios**: Documented in SECURITY.md
|
|
5. **No Shell Injection**: Safe use of jq `--arg` everywhere
|
|
|
|
**⚠️ Could Improve:**
|
|
1. **Rate Limiting**: No protection against rapid-fire captures
|
|
2. **Privilege Escalation**: No check if running as expected user
|
|
3. **IPC Socket Security**: Assumes niri socket is properly protected (document this assumption)
|
|
4. **Screenshot Directory Permissions**: Should verify/enforce 700 permissions
|
|
|
|
### Recommendations
|
|
|
|
1. **Add rate limiting check:**
|
|
```bash
|
|
# 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
|
|
```
|
|
|
|
2. **Verify screenshot directory permissions:**
|
|
```bash
|
|
# 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_CASE` constants, `lower_case` locals - **PASS** (mostly, some could be local)
|
|
- ✅ Error output: `echo "Error: ..." >&2` - **PASS**
|
|
- ✅ Dependency checks: Missing! - **FAIL**
|
|
- ✅ JSON parsing: `jq` with `--arg` - **PASS**
|
|
|
|
**Missing: Dependency Checks**
|
|
|
|
None of the scripts check if required commands exist. Should add:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
1. **capture-focused.sh**:
|
|
- Test with no focused window
|
|
- Test with focused window
|
|
- Test screenshot directory doesn't exist
|
|
- Test niri command failure
|
|
|
|
2. **capture-by-title.sh**:
|
|
- Test no match found
|
|
- Test single match
|
|
- Test multiple matches
|
|
- Test special characters in title
|
|
- Test empty/whitespace search term
|
|
|
|
3. **capture-all-windows.sh**:
|
|
- Test empty window list
|
|
- Test single window
|
|
- Test multiple windows
|
|
- Test filename collisions
|
|
- Test move failure
|
|
|
|
### Integration Tests Needed
|
|
|
|
1. Verify audit logs are written correctly
|
|
2. Verify screenshots created with correct permissions
|
|
3. Verify cross-workspace capture works
|
|
4. Verify blocked windows are not captured (test block-out-from)
|
|
|
|
---
|
|
|
|
## Recommendations Summary
|
|
|
|
### High Priority
|
|
|
|
1. **Add dependency checks** to all scripts
|
|
2. **Add error checking** for niri command failures (don't silent discard)
|
|
3. **Verify screenshot directory exists** before attempting capture
|
|
4. **Check screenshot file was created** after capture command
|
|
|
|
### Medium Priority
|
|
|
|
5. **Add rate limiting** to prevent abuse
|
|
6. **Improve error messages** for multiple window matches
|
|
7. **Use timestamp-based screenshot finding** instead of ls -t
|
|
8. **Add audit logging** to capture-all-windows.sh
|
|
9. **Verify/warn about screenshot directory permissions**
|
|
|
|
### Low Priority
|
|
|
|
10. Query niri config for screenshot-path instead of hardcoding
|
|
11. Add timeout handling for niri commands
|
|
12. Consider adding a "dry-run" mode
|
|
13. Add unit tests for all scripts
|
|
|
|
---
|
|
|
|
## Example Improvements
|
|
|
|
### Improved Script Template
|
|
|
|
```bash
|
|
#!/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:
|
|
|
|
1. [ ] Add dependency checks to all scripts
|
|
2. [ ] Improve error handling for niri command failures
|
|
3. [ ] Add screenshot directory validation
|
|
4. [ ] Implement rate limiting
|
|
5. [ ] Add unit tests
|
|
6. [ ] Consider adding a CHANGELOG.md to track improvements
|
|
|
|
---
|
|
|
|
**Review Completed**: 2025-11-09
|
|
**Status**: APPROVED with recommendations
|
|
**Next Review**: After implementing high-priority improvements
|