---
phase: 26-stash-and-checkout-switch-restore-flags
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - cmd_stash.py
  - cmd_switch.py
  - cmd_restore.py
  - cmd_checkout.py
autonomous: true

must_haves:
  truths:
    - "User can stash untracked files with -u flag"
    - "User can stash with a message using -m flag"
    - "User can view stash diff statistics with show --stat"
    - "User can reference stash by stash@{n} syntax"
    - "User can create branch from stash with stash branch"
    - "User can switch with force-create using -C flag"
    - "User can restore from specific commit with -s/--source"
    - "User receives warning for staging area flags"
  artifacts:
    - path: "cmd_stash.py"
      provides: "Stash flag handling (STSH-01 through STSH-10)"
      contains: "_handle_show"
    - path: "cmd_switch.py"
      provides: "Switch flag handling (CHKT-01, CHKT-02, CHKT-07 through CHKT-09)"
      contains: "force_create"
    - path: "cmd_restore.py"
      provides: "Restore flag handling (CHKT-03, CHKT-04, CHKT-10, CHKT-11)"
      contains: "--source"
    - path: "cmd_checkout.py"
      provides: "Checkout flag handling (CHKT-05, CHKT-06)"
      contains: "--detach"
  key_links:
    - from: "cmd_stash.py"
      to: "sl shelve"
      via: "_handle_show function"
      pattern: "shelve.*--stat"
    - from: "cmd_switch.py"
      to: "sl goto"
      via: "flag translation"
      pattern: "goto.*-C"
    - from: "cmd_restore.py"
      to: "sl revert"
      via: "source flag"
      pattern: "revert.*-r"
---

<objective>
Extend stash, switch, restore, and checkout handlers to support the full range of working tree management flags.

Purpose: Users can use the complete set of flags for stash, checkout, switch, and restore commands with proper git-to-sapling translation.

Output: Four updated command handlers with comprehensive flag support for 21 requirements (STSH-01 through STSH-10, CHKT-01 through CHKT-11).
</objective>

<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/26-stash-and-checkout-switch-restore-flags/26-RESEARCH.md
</context>

<tasks>

<task type="auto">
  <name>Task 1: Extend cmd_stash.py with flag support (STSH-01 through STSH-10)</name>
  <files>cmd_stash.py</files>
  <action>
Extend cmd_stash.py to support all stash flags. Add import `re` at top.

**1. Add stash@{n} translation function:**
```python
def _translate_stash_ref(ref: str) -> Optional[str]:
    """Convert git stash@{n} to sl shelve name."""
    match = re.match(r'stash@\{(\d+)\}', ref)
    if not match:
        return ref  # Not stash syntax, pass through

    index = int(match.group(1))
    shelves = _get_all_shelve_names()

    if index < len(shelves):
        return shelves[index]

    print(f"error: stash@{{{index}}} does not exist", file=sys.stderr)
    return None

def _get_all_shelve_names() -> list:
    """Get list of shelve names in order (most recent first)."""
    result = subprocess.run(
        ["sl", "shelve", "--list"],
        capture_output=True, text=True
    )
    if result.returncode != 0 or not result.stdout.strip():
        return []

    names = []
    for line in result.stdout.strip().split('\n'):
        if line.strip():
            names.append(line.split()[0])
    return names
```

**2. Add _handle_show function:**
```python
def _handle_show(args: list) -> int:
    """Handle git stash show [--stat] [-p] [stash@{n}]."""
    show_stat = '--stat' in args
    show_patch = '-p' in args or '--patch' in args
    stash_ref = None

    for arg in args:
        if arg.startswith('stash@{'):
            stash_ref = _translate_stash_ref(arg)
            if stash_ref is None:
                return 1

    if stash_ref is None:
        stash_ref = _get_most_recent_shelve()

    if stash_ref is None:
        print("No stash entries found.", file=sys.stderr)
        return 1

    if show_patch:
        return run_sl(['shelve', '-p', stash_ref])
    elif show_stat:
        return run_sl(['shelve', '--stat', stash_ref])
    else:
        # Default: show stat (git's default)
        return run_sl(['shelve', '--stat', stash_ref])
```

**3. Add _handle_branch function (STSH-10):**
```python
def _handle_branch(args: list) -> int:
    """Handle git stash branch <name> [stash@{n}]."""
    if not args:
        print("error: stash branch requires a branch name", file=sys.stderr)
        return 1

    branch_name = args[0]
    stash_ref = args[1] if len(args) > 1 else None

    # Translate stash reference if provided
    shelve_name = None
    if stash_ref:
        shelve_name = _translate_stash_ref(stash_ref)
        if shelve_name is None:
            return 1
    else:
        shelve_name = _get_most_recent_shelve()
        if shelve_name is None:
            print("No stash entries found.", file=sys.stderr)
            return 1

    # Create bookmark at current commit
    result = run_sl(['bookmark', branch_name])
    if result != 0:
        return result

    # Unshelve (apply and delete)
    return run_sl(['unshelve', shelve_name])
```

**4. Extend _handle_push to support new flags:**
```python
def _handle_push(args: list) -> int:
    """Handle git stash push with flags."""
    sl_args = ['shelve']
    remaining = []
    quiet = False
    keep_index = False
    all_files = False

    i = 0
    while i < len(args):
        arg = args[i]

        if arg in ('-u', '--include-untracked'):
            sl_args.append('-u')  # STSH-01
            i += 1
            continue

        if arg in ('-m', '--message'):
            if i + 1 < len(args):
                sl_args.extend(['-m', args[i + 1]])  # STSH-02
                i += 2
                continue

        if arg in ('-p', '--patch'):
            sl_args.append('-i')  # STSH-05: translate to interactive
            i += 1
            continue

        if arg in ('-k', '--keep-index'):
            keep_index = True  # STSH-06
            i += 1
            continue

        if arg in ('-a', '--all'):
            all_files = True  # STSH-07
            sl_args.append('-u')  # At minimum include untracked
            i += 1
            continue

        if arg in ('-q', '--quiet'):
            quiet = True  # STSH-08
            i += 1
            continue

        # STSH-09: pathspec support - pass through
        remaining.append(arg)
        i += 1

    # Warnings
    if keep_index:
        print("Warning: -k/--keep-index has no effect. "
              "Sapling has no staging area; all changes are shelved.",
              file=sys.stderr)

    if all_files:
        print("Note: -a/--all includes untracked files. "
              "Ignored files may not be included.",
              file=sys.stderr)

    sl_args.extend(remaining)

    if quiet:
        result = subprocess.run(
            ['sl'] + sl_args,
            capture_output=True
        )
        return result.returncode

    return run_sl(sl_args)
```

**5. Update _handle_pop, _handle_apply, _handle_drop to use stash ref translation:**
Add stash@{n} translation to these functions:
- In _handle_pop: translate first arg if it starts with stash@{
- In _handle_apply: same translation
- In _handle_drop: same translation

**6. Update main handle function to route to show and branch subcommands:**
Add to the subcommand routing:
```python
if subcommand == "show":
    return _handle_show(subargs)

if subcommand == "branch":
    return _handle_branch(subargs)
```
  </action>
  <verify>
Run: `python -c "import cmd_stash; print('import ok')"` succeeds.
Run existing stash tests: `pytest tests/test_stash.py -v` - all 13 tests pass.
  </verify>
  <done>
cmd_stash.py handles STSH-01 (-u), STSH-02 (-m), STSH-03 (show --stat), STSH-04 (stash@{n}), STSH-05 (-p), STSH-06 (keep-index warning), STSH-07 (-a), STSH-08 (-q), STSH-09 (pathspec), STSH-10 (branch).
  </done>
</task>

<task type="auto">
  <name>Task 2: Extend cmd_switch.py, cmd_restore.py, cmd_checkout.py with flag support</name>
  <files>cmd_switch.py, cmd_restore.py, cmd_checkout.py</files>
  <action>
**1. Extend cmd_switch.py (CHKT-01, CHKT-02, CHKT-07, CHKT-08, CHKT-09):**

Replace the entire handle function with comprehensive flag handling:

```python
"""Handler for 'git switch' command."""

import subprocess
from common import ParsedCommand, run_sl


def handle(parsed: ParsedCommand) -> int:
    """
    Handle 'git switch' command with full flag support.

    Translations:
    - git switch <branch>              -> sl goto <bookmark>
    - git switch -c/--create <name>    -> sl bookmark <name> + sl goto <name>
    - git switch -C/--force-create     -> sl bookmark -f <name> + sl goto <name>
    - git switch -d/--detach           -> sl goto --inactive
    - git switch -f/--force/--discard-changes -> sl goto -C
    - git switch -m/--merge            -> sl goto -m
    """
    args = list(parsed.args)

    # Extract flags
    create = False
    force_create = False
    detach = False
    force = False
    merge = False
    branch_name = None
    remaining = []

    i = 0
    while i < len(args):
        arg = args[i]

        # CHKT-01: -c/--create
        if arg in ('-c', '--create'):
            create = True
            if i + 1 < len(args):
                branch_name = args[i + 1]
                i += 2
                continue
            i += 1
            continue

        # CHKT-02: -C/--force-create
        if arg in ('-C', '--force-create'):
            force_create = True
            if i + 1 < len(args):
                branch_name = args[i + 1]
                i += 2
                continue
            i += 1
            continue

        # CHKT-07: -d/--detach
        if arg in ('-d', '--detach'):
            detach = True
            i += 1
            continue

        # CHKT-08: -f/--force/--discard-changes
        if arg in ('-f', '--force', '--discard-changes'):
            force = True
            i += 1
            continue

        # CHKT-09: -m/--merge
        if arg in ('-m', '--merge'):
            merge = True
            i += 1
            continue

        remaining.append(arg)
        i += 1

    # Handle create modes
    if create and branch_name:
        result = run_sl(['bookmark', branch_name])
        if result != 0:
            return result
        return run_sl(['goto', branch_name])

    if force_create and branch_name:
        # Use -f to force update if bookmark exists
        result = run_sl(['bookmark', '-f', branch_name])
        if result != 0:
            return result
        return run_sl(['goto', branch_name])

    # Build goto command for regular switch
    goto_args = ['goto']

    if force:
        goto_args.append('-C')  # Clean/discard changes
    if merge:
        goto_args.append('-m')
    if detach:
        goto_args.append('--inactive')

    goto_args.extend(remaining)
    return run_sl(goto_args)
```

**2. Extend cmd_restore.py (CHKT-03, CHKT-04, CHKT-10, CHKT-11):**

Replace the entire file with comprehensive flag handling:

```python
"""Handler for 'git restore' command."""

import subprocess
import sys
from common import ParsedCommand, run_sl


def handle(parsed: ParsedCommand) -> int:
    """
    Handle 'git restore' command with full flag support.

    Translations:
    - git restore <file>           -> sl revert <file>
    - git restore -s/--source=REV  -> sl revert -r REV
    - git restore --staged/-S      -> warning (no staging area)
    - git restore -q/--quiet       -> suppress output
    - git restore -W/--worktree    -> default behavior (skip flag)
    """
    args = list(parsed.args)

    # Extract flags
    source = None
    staged = False
    quiet = False
    remaining = []

    i = 0
    while i < len(args):
        arg = args[i]

        # CHKT-03: -s/--source
        if arg in ('-s', '--source'):
            if i + 1 < len(args):
                source = args[i + 1]
                i += 2
                continue
        elif arg.startswith('--source='):
            source = arg.split('=', 1)[1]
            i += 1
            continue

        # CHKT-04: --staged/-S (warning)
        if arg in ('-S', '--staged'):
            staged = True
            i += 1
            continue

        # CHKT-10: -q/--quiet
        if arg in ('-q', '--quiet'):
            quiet = True
            i += 1
            continue

        # CHKT-11: -W/--worktree (default, skip)
        if arg in ('-W', '--worktree'):
            i += 1
            continue

        remaining.append(arg)
        i += 1

    # Warn about staged
    if staged:
        print("Warning: --staged/-S has no effect. "
              "Sapling has no staging area.",
              file=sys.stderr)

    # Build revert command
    revert_args = ['revert']

    if source:
        revert_args.extend(['-r', source])

    revert_args.extend(remaining)

    if quiet:
        # Capture and discard output
        result = subprocess.run(
            ['sl'] + revert_args,
            capture_output=True
        )
        return result.returncode

    return run_sl(revert_args)
```

**3. Extend cmd_checkout.py (CHKT-05, CHKT-06):**

Add detach and track handling. In the handle function, add flag extraction before disambiguation:

Add to imports at top: `import sys`

Before the disambiguation logic (after -b/-B handling), add flag extraction:

```python
# Handle --detach (CHKT-05)
detach = False
track = False
filtered_args = []

for arg in args:
    if arg == '--detach':
        detach = True
    elif arg in ('-t', '--track'):
        track = True
    else:
        filtered_args.append(arg)

args = filtered_args
```

Then when calling goto for a valid revision, add the --inactive flag if detach is set:

```python
# Valid revision - switch to it (CHECKOUT-01, CHECKOUT-02)
if is_revision:
    goto_args = _translate_goto_flags(args)
    if detach:
        goto_args.insert(0, '--inactive')
    return run_sl(['goto'] + goto_args)
```

For track flag (CHKT-06), add a note when used with -b:

```python
if track:
    print("Note: -t/--track is accepted but tracking configuration "
          "is not fully emulated.", file=sys.stderr)
```
  </action>
  <verify>
Run: `python -c "import cmd_switch; import cmd_restore; import cmd_checkout; print('imports ok')"` succeeds.
Run existing tests: `pytest tests/test_switch.py tests/test_restore.py tests/test_checkout.py -v` - all tests pass.
  </verify>
  <done>
cmd_switch.py handles CHKT-01 (-c), CHKT-02 (-C), CHKT-07 (-d), CHKT-08 (-f), CHKT-09 (-m).
cmd_restore.py handles CHKT-03 (-s/--source), CHKT-04 (--staged warning), CHKT-10 (-q), CHKT-11 (-W).
cmd_checkout.py handles CHKT-05 (--detach), CHKT-06 (-t/--track note).
  </done>
</task>

</tasks>

<verification>
1. All imports work without error
2. All existing tests for stash, switch, restore, checkout continue to pass
3. No regressions in other command tests: `pytest tests/ -v --ignore=tests/test_readme.py`
</verification>

<success_criteria>
- STSH-01 through STSH-10: Stash flag handling complete
- CHKT-01 through CHKT-11: Checkout/switch/restore flag handling complete
- All 21 requirements have handler code implemented
- Existing tests pass (no regressions)
</success_criteria>

<output>
After completion, create `.planning/phases/26-stash-and-checkout-switch-restore-flags/26-01-SUMMARY.md`
</output>
