---
phase: 19-checkout-command
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - cmd_checkout.py
  - gitsl.py
autonomous: true

must_haves:
  truths:
    - "User can switch to an existing branch with git checkout <branch>"
    - "User can switch to a commit with git checkout <commit>"
    - "User can restore a file with git checkout <file> or git checkout -- <file>"
    - "User can create and switch to a new branch with git checkout -b <name>"
    - "Ambiguous arguments error clearly instead of wrong action"
  artifacts:
    - path: "cmd_checkout.py"
      provides: "Checkout command handler with disambiguation"
      min_lines: 80
      exports: ["handle"]
    - path: "gitsl.py"
      provides: "Dispatch routing for checkout"
      contains: "cmd_checkout"
  key_links:
    - from: "gitsl.py"
      to: "cmd_checkout.py"
      via: "import and dispatch"
      pattern: "import cmd_checkout"
    - from: "cmd_checkout.py"
      to: "common.py"
      via: "ParsedCommand, run_sl"
      pattern: "from common import"
---

<objective>
Create the checkout command handler with full disambiguation logic

Purpose: Enable git checkout to work correctly against Sapling repos, handling the overloaded nature of checkout (switch branches, restore files, create branches) with proper disambiguation
Output: Working cmd_checkout.py with routing integrated in gitsl.py
</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/19-checkout-command/19-RESEARCH.md

# Patterns from prior phases
@cmd_switch.py - Flag detection pattern for -c
@cmd_restore.py - Simple passthrough to revert
@cmd_stash.py - Output capture with subprocess.run
@gitsl.py - Dispatch routing pattern
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create checkout command handler with disambiguation</name>
  <files>cmd_checkout.py</files>
  <action>
Create cmd_checkout.py with:

1. **_is_valid_revision(arg, cwd=None)** helper:
   - Use subprocess.run with ["sl", "log", "-r", arg, "-T", "{node}", "-l", "1"]
   - Return True if returncode == 0
   - Pattern from cmd_stash.py _get_most_recent_shelve()

2. **_split_at_separator(args)** helper:
   - Find "--" in args
   - Return (before, after) tuple
   - If no "--", return (args, [])

3. **_handle_create_branch(args)** helper:
   - Extract branch name after -b/-B flag
   - Extract optional start-point (arg after branch name)
   - If start-point: run_sl(["goto", start_point]) first
   - Then run_sl(["bookmark", branch_name])
   - Then run_sl(["goto", branch_name]) to activate
   - Handle missing branch name: print error, return 128

4. **handle(parsed)** main function:
   - If no args: print usage error, return 1
   - If "-b" or "-B" in args: delegate to _handle_create_branch (CHECKOUT-05)
   - Split at "--" separator using _split_at_separator
   - If after_sep (files after --):
     - If before_sep has valid revision: run_sl(["revert", "-r", revision] + files) (CHECKOUT-04 with commit)
     - Else: run_sl(["revert"] + files) (CHECKOUT-04)
   - If no "--", disambiguate single argument:
     - Check _is_valid_revision(target)
     - Check os.path.exists(target)
     - If BOTH: print ambiguity error with hint to use --, return 1 (CHECKOUT-06)
     - If revision only: run_sl(["goto"] + args) (CHECKOUT-01, CHECKOUT-02)
     - If file only: run_sl(["revert"] + args) (CHECKOUT-03)
     - If neither: run_sl(["goto"] + args) to get sl's error message

Imports needed: os, subprocess, sys, typing (List, Optional, Tuple), common (ParsedCommand, run_sl)

Error message for ambiguity (match git style):
```
error: '{target}' could be both a ref and a file.
Use -- to separate paths from revisions:
  git checkout -- {target}
```
  </action>
  <verify>python -c "import cmd_checkout; print('OK')" exits 0</verify>
  <done>cmd_checkout.py exists with handle(), _is_valid_revision(), _split_at_separator(), _handle_create_branch() functions</done>
</task>

<task type="auto">
  <name>Task 2: Update gitsl.py dispatch routing</name>
  <files>gitsl.py</files>
  <action>
1. Add import at top with other cmd_* imports:
   import cmd_checkout

2. Add dispatch case after the stash handler (before unsupported command handling):
   if parsed.command == "checkout":
       return cmd_checkout.handle(parsed)

Follow existing pattern from other command handlers.
  </action>
  <verify>python -c "import gitsl; print('OK')" exits 0 and grep -q "cmd_checkout" gitsl.py</verify>
  <done>gitsl.py imports cmd_checkout and dispatches checkout command to handler</done>
</task>

</tasks>

<verification>
1. Import check: `python -c "from cmd_checkout import handle; print('OK')"`
2. Dispatch check: `grep "checkout" gitsl.py` shows import and dispatch
3. Manual smoke test in sl repo:
   - `python gitsl.py checkout -b test-branch` creates bookmark
   - `python gitsl.py checkout -- README.md` runs revert
</verification>

<success_criteria>
- cmd_checkout.py exists with all required functions
- gitsl.py imports and dispatches to cmd_checkout
- No import errors when loading gitsl.py
- All 6 requirements implemented:
  - CHECKOUT-01: checkout <commit> -> goto <commit>
  - CHECKOUT-02: checkout <branch> -> goto <bookmark>
  - CHECKOUT-03: checkout <file> -> revert <file>
  - CHECKOUT-04: checkout -- <file> -> revert <file>
  - CHECKOUT-05: checkout -b <name> -> bookmark + goto
  - CHECKOUT-06: disambiguation logic with clear error
</success_criteria>

<output>
After completion, create `.planning/phases/19-checkout-command/19-01-SUMMARY.md`
</output>
