Skip to content

feat: interactive and non-interactive operation modes (Task 4) - #8

Merged
onsails merged 6 commits into
masterfrom
feat/task-4-interactive-mode
Oct 28, 2025
Merged

feat: interactive and non-interactive operation modes (Task 4)#8
onsails merged 6 commits into
masterfrom
feat/task-4-interactive-mode

Conversation

@onsails

@onsails onsails commented Oct 28, 2025

Copy link
Copy Markdown
Owner

Summary

Implements Task 4: Interactive and Non-Interactive Operation Modes

This PR adds interactive prompts with single-key shortcuts, session state management, and graceful exit handling. Interactive mode is now the default behavior, with --yes-all flag for automation.

Features

Interactive Mode (Default)

  • Prompts user for each sync action with instant single-key response
  • No Enter key required - press y/n/a/s/d/q for immediate action
  • Clear keyboard hints: [y/n/a/s/d/q] (yes/no/all/skip-all/diff/quit)
  • Session state persistence: "all" and "skip-all" apply to remaining files

Keyboard Shortcuts

  • y / yes - Approve this file
  • n / no - Skip this file (default on Enter)
  • a / all - Approve all remaining files
  • s / skip-all - Skip all remaining files
  • d / diff - Show file content or diff
  • q / quit - Exit cleanly

Diff Display

  • New files: Show full content as green additions (+ prefix)
  • Conflicts: Show colored unified diff between source and dest
  • Error fallback: Display file paths if diff generation fails

Non-Interactive Modes

  • --yes-all flag auto-approves all operations (previous default behavior)
  • --dry-run continues to skip prompts (preview only)
  • Removed redundant --non-interactive flag

Signal Handling

  • Graceful Ctrl+C handling with exit code 130 (SIGINT)
  • User quit exits cleanly with code 0 (not treated as error)

Implementation

New Module: crates/ccsync-cli/src/interactive.rs

  • InteractivePrompter with session state management
  • Single-character terminal input via dialoguer's console
  • Color-coded action descriptions with emoji indicators

Sync Engine Integration

  • Added ApprovalCallback type for flexible approval logic
  • SyncEngine::sync_with_approver() accepts optional callback
  • Skip actions bypass approval (automatic decisions)
  • Backward compatible: original sync() method unchanged

Dependencies Added

  • dialoguer ~0.11.0 - Terminal user interaction
  • ctrlc ~3.4.7 - Signal handling

Behavior Examples

# Interactive mode (default):
ccsync to-local
→ Prompts for each file with single-key shortcuts

# Non-interactive (automation):
ccsync to-local --yes-all
→ Auto-approves all, no prompts

# Preview only:
ccsync to-local --dry-run
→ Shows what would happen, no prompts, no changes

Test Results

  • ✅ 109/109 tests passing (87 lib + 3 interactive + 19 CLI)
  • ✅ Zero clippy warnings with -D warnings
  • ✅ All Task 4 subtasks complete

Subtasks Complete

  • ✅ 4.1: Interactive prompt logic with dialoguer
  • ✅ 4.2: Non-interactive automation (--yes-all)
  • ✅ 4.3: Dry-run simulation
  • ✅ 4.4: Session state management (all/skip-all)
  • ✅ 4.5: Graceful signal and exit code handling

Code Quality

  • Comprehensive error handling with fallbacks
  • Clean separation: UI logic in CLI, sync logic in library
  • Well-documented with doc comments
  • Follows Rust idioms and best practices

Closes #4

Implement Task 4: Interactive and Non-Interactive Operation Modes

Core Features:
- Interactive mode is now the default behavior
- Prompts user for each sync action: yes/no/all/none/diff/quit
- Session state management for 'all' and 'none' choices
- Colored action descriptions with emoji indicators
- Inline diff viewing for conflicts
- Graceful Ctrl+C handling with exit code 130
- --yes-all flag to skip all prompts (previous default behavior)
- --dry-run continues to skip prompts (preview only)

Implementation:
- Created InteractivePrompter in cli/interactive.rs module
- Added ApprovalCallback type alias to reduce complexity
- Modified SyncEngine::sync_with_approver() to accept approval callback
- Wired up interactive mode in to_local and to_global commands
- Added ctrlc handler in main.rs for graceful interruption
- Removed redundant --non-interactive flag (use --yes-all instead)

Dependencies Added:
- dialoguer 0.11 for terminal prompts
- ctrlc 3.5 for signal handling

Behavior:
- ccsync to-local → Interactive (prompts for each file)
- ccsync to-local --yes-all → Non-interactive (auto-approve all)
- ccsync to-local --dry-run → Preview only (no prompts, no changes)

Session State:
- 'yes/no' → Ask for next file
- 'all' → Auto-approve all remaining files
- 'none' → Auto-skip all remaining files
- 'diff' → Show diff, then re-prompt
- 'quit' → Abort immediately

Test Results:
- ✅ 109/109 tests passing (87 lib + 3 interactive + 19 CLI)
- ✅ Zero clippy warnings with -D warnings

Subtasks Complete:
- ✅ 4.1: Interactive prompt logic with dialoguer
- ✅ 4.2: Non-interactive automation (--yes-all flag)
- ✅ 4.3: Dry-run already working from Task 9
- ✅ 4.4: Session state management (all/none tracking)
- ✅ 4.5: Graceful Ctrl+C handling with exit code

Related: #4
Fixes interactive mode behavior and improves UX based on review feedback:

- Skip actions now bypass approval prompts (automatic decisions)
- Create actions display source path for clarity
- Improved diff error handling with fallback file path display
- Updated dependencies to workspace-managed versions:
  - dialoguer ~0.11.0 (was 0.11)
  - ctrlc ~3.4.7 (was 3.5)
- Applied cargo fmt formatting fixes

All changes maintain 109/109 test pass rate and zero clippy warnings.

Related to PR feedback on feat/task-4-interactive-mode branch.
When user selects 'quit' in interactive mode, the program now:
- Prints: 'Sync cancelled by user.'
- Exits with code 0 (success, not error)

Previously showed confusing error message:
  Error: Failed to execute to-local command
  Caused by: User aborted sync operation

This was technically correct but misleading - user cancellation
is not an error condition. The new behavior treats quit as a
clean, intentional exit.

Exit codes:
- 0: Success or user-initiated cancellation
- 1: Actual errors (I/O failures, conflicts with fail strategy)
- 130: Ctrl+C interrupt (SIGINT)
Improved interactive UX with single-key shortcuts:
- y/yes → approve this file
- n/no → skip this file
- a/all → approve all remaining
- s/skip-all/none → skip all remaining
- d/diff → show diff
- q/quit/exit → abort sync

The prompt now shows clear hints:
  Proceed? [y/n/a/s/d/q] (yes/no/all/skip-all/diff/quit)

Features:
- Single character input for quick decisions
- Full word alternatives for clarity
- Empty input defaults to 'no' (safe default)
- Invalid input shows help message and re-prompts
- Case-insensitive matching

This makes interactive mode much faster to use while
remaining clear for users who prefer full words.
Improved interactive prompts with immediate response:
- Single keypress shortcuts (no Enter required)
- Press y/n/a/s/d/q for instant action
- Clear hints shown: [y/n/a/s/d/q] (yes/no/all/skip-all/diff/quit)
- Case-insensitive (Y and y both work)
- Invalid keys show help and re-prompt

New file diff display:
- When user presses 'd' for a Create action, shows full file content
- Displays as green additions (+ prefix with color)
- Shows file path with +++ header
- Graceful error handling if file can't be read

User experience improvements:
- Faster workflow (no need to type + Enter)
- Immediate visual feedback (key echo)
- Clear instructions in every prompt
- New files are previewable before creation
@onsails
onsails merged commit 8ace69b into master Oct 28, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant