---
phase: 24-status-and-add-flags
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - cmd_status.py
  - cmd_add.py
autonomous: true

must_haves:
  truths:
    - "User running `git status --ignored` sees ignored files in output"
    - "User running `git status -b` sees current branch name in status output"
    - "User running `git status -uno` suppresses untracked file listing"
    - "User running `git add -n <file>` sees what would be added without adding"
    - "User running `git add -f <ignored-file>` gets a warning about limitation"
    - "User running `git add -v <file>` sees files being added"
  artifacts:
    - path: "cmd_status.py"
      provides: "Status flag handling (--ignored, -b, -v, -u modes)"
      contains: "show_ignored"
    - path: "cmd_add.py"
      provides: "Add flag handling (--dry-run, --force, --verbose)"
      contains: "dry_run"
  key_links:
    - from: "cmd_status.py"
      to: "sl status -i"
      via: "--ignored flag translation"
      pattern: "sl_args.append.*-i"
    - from: "cmd_add.py"
      to: "sl add -n"
      via: "--dry-run flag translation"
      pattern: "sl_args.append.*-n"
---

<objective>
Extend cmd_status.py and cmd_add.py with additional flag support for STAT-01 through STAT-05 and ADD-03 through ADD-05.

Purpose: Users can customize status and add behavior with standard git flags for filtering, verbosity, and preview mode.

Output: Updated cmd_status.py and cmd_add.py with comprehensive flag translation following the established pattern from cmd_diff.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/24-status-and-add-flags/24-RESEARCH.md

# Existing implementations to extend
@cmd_status.py
@cmd_add.py

# Pattern reference
@cmd_diff.py
</context>

<tasks>

<task type="auto">
  <name>Task 1: Extend cmd_status.py with new flag support</name>
  <files>cmd_status.py</files>
  <action>
Extend the `handle()` function in cmd_status.py to support these flags:

**STAT-01: --ignored**
- Add `--ignored` flag detection
- Translate to `sl status -i` to include ignored files
- Ensure ignored files show as `!!` in porcelain output (already mapped in SL_TO_GIT_STATUS as 'I': '!!')

**STAT-02: -b/--branch**
- Add `-b` and `--branch` flag detection
- Create helper function `get_branch_header()` that runs `sl log -r . --template "{activebookmark}"` to get current bookmark
- When -b is present AND (--short or --porcelain or -s), prepend `## {branch}` to output
- When -b is present with normal status, note: sl status does not natively show branch, so prepend branch line

**STAT-03: -v/--verbose**
- Add `-v` and `--verbose` flag detection
- Note: git -v shows staged changes diff. Since Sapling has no staging, -v could show all uncommitted changes
- For now, pass `-v` through to sl status (which shows repo state info) and print a note to stderr explaining the difference

**STAT-05: -u/--untracked-files[=<mode>]**
- Support these formats: `-u`, `-uno`, `-unormal`, `-uall`, `-u no`, `-u normal`, `-u all`, `--untracked-files`, `--untracked-files=no`, `--untracked-files=normal`, `--untracked-files=all`
- Mode "no": Use `sl status -mard` (modified, added, removed, deleted only - no untracked)
- Mode "normal"/"all": Default sl behavior (show untracked). Note: sl treats these the same as git does for directories

**Implementation pattern (follow cmd_diff.py):**
```python
def handle(parsed: ParsedCommand) -> int:
    sl_args = []
    needs_transform = False
    show_ignored = False
    show_branch = False
    verbose = False
    untracked_mode = 'normal'  # Default

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

        # Existing porcelain/short detection
        if arg in ('--porcelain', '--short', '-s'):
            needs_transform = True

        # STAT-01: --ignored
        elif arg == '--ignored':
            show_ignored = True

        # STAT-02: -b/--branch
        elif arg in ('-b', '--branch'):
            show_branch = True

        # STAT-03: -v/--verbose
        elif arg in ('-v', '--verbose'):
            verbose = True

        # STAT-05: -u modes (multiple formats)
        elif arg.startswith('--untracked-files'):
            if '=' in arg:
                untracked_mode = arg.split('=', 1)[1]
            else:
                untracked_mode = 'all'  # git default when no value
        elif arg == '-u':
            if i + 1 < len(parsed.args) and parsed.args[i + 1] in ('no', 'normal', 'all'):
                i += 1
                untracked_mode = parsed.args[i]
            else:
                untracked_mode = 'all'
        elif arg.startswith('-u') and len(arg) > 2:
            untracked_mode = arg[2:]  # -uno, -unormal, -uall

        else:
            sl_args.append(arg)

        i += 1

    # Build final sl command
    if show_ignored:
        sl_args.append('-i')

    if untracked_mode == 'no':
        sl_args.extend(['-mard'])  # Only tracked file changes

    # ... continue with transform or passthrough
```

Add helper function for branch info:
```python
def get_branch_header() -> str:
    """Get git-style branch header for status -b output."""
    import subprocess
    result = subprocess.run(
        ['sl', 'log', '-r', '.', '--template', '{activebookmark}'],
        capture_output=True,
        text=True
    )
    branch = result.stdout.strip() or '(detached)'
    return f"## {branch}"
```
  </action>
  <verify>
Run manual tests:
```bash
cd /path/to/sl-repo
python gitsl.py status --ignored  # Should show ignored files with !!
python gitsl.py status -b --short  # Should show ## branch at top
python gitsl.py status -uno --porcelain  # Should NOT show ?? entries
```
  </verify>
  <done>
cmd_status.py handles --ignored, -b/--branch, -v/--verbose, and -u/--untracked-files modes with correct sl translations and porcelain output formatting.
  </done>
</task>

<task type="auto">
  <name>Task 2: Extend cmd_add.py with new flag support</name>
  <files>cmd_add.py</files>
  <action>
Extend the `handle()` function in cmd_add.py to support these flags:

**ADD-03: --dry-run/-n**
- Add `-n` and `--dry-run` flag detection
- Pass through to `sl add -n` for dry-run preview
- Also ensure -n works with -A/--all (translate to `sl addremove -n`)
- Also ensure -n works with -u/--update (show what would be marked for removal without doing it)

**ADD-04: -f/--force**
- Add `-f` and `--force` flag detection
- Sapling's `sl add` has no --force equivalent for adding ignored files
- Print warning to stderr: "Warning: -f/--force not directly supported. Sapling cannot force-add ignored files. Consider updating your .gitignore instead."
- Continue with normal add (skip the flag)

**ADD-05: -v/--verbose**
- Add `-v` and `--verbose` flag detection
- Capture sl add output and format to show "add 'filename'" for each file added
- For dry-run mode, show what would be added

**Implementation approach:**
```python
def handle(parsed: ParsedCommand) -> int:
    # Extract flags
    dry_run = '-n' in parsed.args or '--dry-run' in parsed.args
    force = '-f' in parsed.args or '--force' in parsed.args
    verbose = '-v' in parsed.args or '--verbose' in parsed.args
    use_update = '-u' in parsed.args or '--update' in parsed.args
    use_all = '-A' in parsed.args or '--all' in parsed.args

    # Filter out processed flags
    remaining = [a for a in parsed.args
                 if a not in ('-n', '--dry-run', '-f', '--force', '-v', '--verbose',
                              '-u', '--update', '-A', '--all')]

    # ADD-04: Warn about --force
    if force:
        print("Warning: -f/--force not directly supported. "
              "Sapling cannot force-add ignored files. "
              "Consider updating your .gitignore instead.",
              file=sys.stderr)

    # Handle -u/--update mode
    if use_update:
        return handle_update_with_flags(remaining, dry_run, verbose)

    # Handle -A/--all mode
    if use_all:
        return handle_all_with_flags(remaining, dry_run, verbose)

    # Standard add with new flags
    return handle_add_with_flags(remaining, dry_run, verbose)


def handle_update_with_flags(pathspec: list, dry_run: bool, verbose: bool) -> int:
    """Handle git add -u with optional dry-run and verbose."""
    deleted_files = get_deleted_files(pathspec if pathspec else None)

    if not deleted_files:
        if verbose:
            print("No deleted files to mark for removal.")
        return 0

    if dry_run:
        for f in deleted_files:
            print(f"remove '{f}'")
        return 0

    result = subprocess.run(["sl", "remove", "--mark"] + deleted_files,
                           capture_output=verbose, text=True)
    if verbose and result.returncode == 0:
        for f in deleted_files:
            print(f"remove '{f}'")
    return result.returncode


def handle_all_with_flags(remaining: list, dry_run: bool, verbose: bool) -> int:
    """Handle git add -A with optional dry-run and verbose."""
    cmd = ["addremove"] + remaining
    if dry_run:
        cmd.append("-n")

    if verbose:
        result = subprocess.run(["sl"] + cmd, capture_output=True, text=True)
        if result.stdout:
            for line in result.stdout.strip().splitlines():
                if line:
                    # Format: adding/removing filename
                    print(line)
        return result.returncode

    return run_sl(cmd)


def handle_add_with_flags(remaining: list, dry_run: bool, verbose: bool) -> int:
    """Handle standard git add with optional dry-run and verbose."""
    cmd = ["add"] + remaining
    if dry_run:
        cmd.append("-n")

    if verbose:
        result = subprocess.run(["sl"] + cmd, capture_output=True, text=True)
        if result.stdout:
            for line in result.stdout.strip().splitlines():
                if line:
                    print(f"add '{line}'")
        return result.returncode

    return run_sl(cmd)
```

Also add `import sys` at the top if not present.
  </action>
  <verify>
Run manual tests:
```bash
cd /path/to/sl-repo
echo "test" > newfile.txt
python gitsl.py add -n newfile.txt  # Should show what would be added, file remains untracked
sl status  # Should show ? newfile.txt (still untracked)
python gitsl.py add -v newfile.txt  # Should print "add 'newfile.txt'" and add file
python gitsl.py add -f .ignored_file 2>&1  # Should show warning about --force
```
  </verify>
  <done>
cmd_add.py handles --dry-run/-n, -f/--force (with warning), and -v/--verbose flags correctly, including combinations with -A and -u modes.
  </done>
</task>

</tasks>

<verification>
After completing both tasks:

1. Run existing tests to verify no regressions:
```bash
pytest tests/test_status.py tests/test_add.py -v
```

2. Verify all new flags work in an sl repo:
```bash
# Status flags
python gitsl.py status --ignored
python gitsl.py status -b --short
python gitsl.py status -uno --porcelain

# Add flags
echo "test" > /tmp/testfile.txt
python gitsl.py add -n /tmp/testfile.txt
python gitsl.py add -v /tmp/testfile.txt
```
</verification>

<success_criteria>
1. `git status --ignored` shows ignored files with !! porcelain code
2. `git status -b` shows branch header in short/porcelain output
3. `git status -uno` suppresses untracked files from output
4. `git add -n <file>` previews addition without modifying state
5. `git add -f` prints warning about Sapling limitation
6. `git add -v <file>` shows files being added
7. All existing status/add tests pass
</success_criteria>

<output>
After completion, create `.planning/phases/24-status-and-add-flags/24-01-SUMMARY.md`
</output>
