# Command Restructure: Implementation Plan

**Reference:** `command-restructure/CLI_AUDIT.md`
**Scope:** 10 proposals, 3 implementation phases, estimated ~15 files touched

---

## Phase 1: Zero-Risk Quick Wins (1 commit)

These four changes are purely additive -- no behavior changes, no renames, no removed commands.

### P1: Help Grouping

**Goal:** Group the 29 visible commands into 5 semantic clusters in `--help` output.

**File:** `src/cli/mod.rs` (lines 117-399, the `Commands` enum)

**Changes:** Add `#[command(help_heading = "...")]` to each variant:

```rust
#[derive(Subcommand)]
#[allow(clippy::large_enum_variant)]
pub enum Commands {
    // ── Query ──────────────────────────────────────────────
    /// List or show issues
    #[command(visible_alias = "issue", help_heading = "Query")]
    Issues(IssuesArgs),

    /// List or show merge requests
    #[command(visible_alias = "mr", alias = "merge-requests", alias = "merge-request", help_heading = "Query")]
    Mrs(MrsArgs),

    /// List notes from discussions
    #[command(visible_alias = "note", help_heading = "Query")]
    Notes(NotesArgs),

    /// Search indexed documents
    #[command(visible_alias = "find", alias = "query", help_heading = "Query")]
    Search(SearchArgs),

    /// Count entities in local database
    #[command(help_heading = "Query")]
    Count(CountArgs),

    // ── Intelligence ───────────────────────────────────────
    /// Show a chronological timeline of events matching a query
    #[command(help_heading = "Intelligence")]
    Timeline(TimelineArgs),

    /// People intelligence: experts, workload, active discussions, overlap
    #[command(help_heading = "Intelligence")]
    Who(WhoArgs),

    /// Personal work dashboard: open issues, authored/reviewing MRs, activity
    #[command(help_heading = "Intelligence")]
    Me(MeArgs),

    // ── File Analysis ──────────────────────────────────────
    /// Trace why code was introduced: file -> MR -> issue -> discussion
    #[command(help_heading = "File Analysis")]
    Trace(TraceArgs),

    /// Show MRs that touched a file, with linked discussions
    #[command(name = "file-history", help_heading = "File Analysis")]
    FileHistory(FileHistoryArgs),

    /// Find semantically related entities via vector search
    #[command(help_heading = "File Analysis", ...)]
    Related { ... },

    /// Detect discussion divergence from original intent
    #[command(help_heading = "File Analysis", ...)]
    Drift { ... },

    // ── Data Pipeline ──────────────────────────────────────
    /// Run full sync pipeline: ingest -> generate-docs -> embed
    #[command(help_heading = "Data Pipeline")]
    Sync(SyncArgs),

    /// Ingest data from GitLab
    #[command(help_heading = "Data Pipeline")]
    Ingest(IngestArgs),

    /// Generate searchable documents from ingested data
    #[command(name = "generate-docs", help_heading = "Data Pipeline")]
    GenerateDocs(GenerateDocsArgs),

    /// Generate vector embeddings for documents via Ollama
    #[command(help_heading = "Data Pipeline")]
    Embed(EmbedArgs),

    // ── System ─────────────────────────────────────────────
    // (init, status, health, doctor, stats, auth, token, migrate, cron,
    //  completions, robot-docs, version -- all get help_heading = "System")
}
```

**Verification:**
- `lore --help` shows grouped output
- All existing commands still work identically
- `lore robot-docs` output unchanged (robot-docs is hand-crafted, not derived from clap)

**Files touched:** `src/cli/mod.rs` only

---

### P3: Singular/Plural Entity Type Fix

**Goal:** Accept both `issue`/`issues`, `mr`/`mrs` everywhere entity types are value-parsed.

**File:** `src/cli/args.rs`

**Change 1 -- `CountArgs.entity` (line 819):**
```rust
// BEFORE:
#[arg(value_parser = ["issues", "mrs", "discussions", "notes", "events"])]
pub entity: String,

// AFTER:
#[arg(value_parser = ["issue", "issues", "mr", "mrs", "discussion", "discussions", "note", "notes", "event", "events"])]
pub entity: String,
```

**File:** `src/cli/args.rs`

**Change 2 -- `SearchArgs.source_type` (line 369):**
```rust
// BEFORE:
#[arg(long = "type", value_parser = ["issue", "mr", "discussion", "note"], ...)]
pub source_type: Option<String>,

// AFTER:
#[arg(long = "type", value_parser = ["issue", "issues", "mr", "mrs", "discussion", "discussions", "note", "notes"], ...)]
pub source_type: Option<String>,
```

**File:** `src/cli/mod.rs`

**Change 3 -- `Drift.entity_type` (line 287):**
```rust
// BEFORE:
#[arg(value_parser = ["issues"])]
pub entity_type: String,

// AFTER:
#[arg(value_parser = ["issue", "issues"])]
pub entity_type: String,
```

**Normalization layer:** In the handlers that consume these values, normalize to the canonical form (plural for entity names, singular for source_type) so downstream code doesn't need changes:

**File:** `src/app/handlers.rs`

In `handle_count` (~line 409): Normalize entity string before passing to `run_count`:
```rust
let entity = match args.entity.as_str() {
    "issue" => "issues",
    "mr" => "mrs",
    "discussion" => "discussions",
    "note" => "notes",
    "event" => "events",
    other => other,
};
```

In `handle_search` (search handler): Normalize source_type:
```rust
let source_type = args.source_type.as_deref().map(|t| match t {
    "issues" => "issue",
    "mrs" => "mr",
    "discussions" => "discussion",
    "notes" => "note",
    other => other,
});
```

In `handle_drift` (~line 225): Normalize entity_type:
```rust
let entity_type = if entity_type == "issue" { "issues" } else { &entity_type };
```

**Verification:**
- `lore count issue` works (same as `lore count issues`)
- `lore search --type issues 'foo'` works (same as `--type issue`)
- `lore drift issue 42` works (same as `drift issues 42`)
- All existing invocations unchanged

**Files touched:** `src/cli/args.rs`, `src/cli/mod.rs`, `src/app/handlers.rs`

---

### P5: Fix `-f` Short Flag Collision

**Goal:** Remove `-f` shorthand from `count --for` so `-f` consistently means `--force` across the CLI.

**File:** `src/cli/args.rs` (line 823)

```rust
// BEFORE:
#[arg(short = 'f', long = "for", value_parser = ["issue", "mr"])]
pub for_entity: Option<String>,

// AFTER:
#[arg(long = "for", value_parser = ["issue", "mr"])]
pub for_entity: Option<String>,
```

**Also update the value_parser to accept both forms** (while we're here):
```rust
#[arg(long = "for", value_parser = ["issue", "issues", "mr", "mrs"])]
pub for_entity: Option<String>,
```

And normalize in `handle_count`:
```rust
let for_entity = args.for_entity.as_deref().map(|f| match f {
    "issues" => "issue",
    "mrs" => "mr",
    other => other,
});
```

**File:** `src/app/robot_docs.rs` (line 173) -- update the robot-docs entry:
```rust
// BEFORE:
"flags": ["<entity: issues|mrs|discussions|notes|events>", "-f/--for <issue|mr>"],

// AFTER:
"flags": ["<entity: issues|mrs|discussions|notes|events>", "--for <issue|mr>"],
```

**Verification:**
- `lore count notes --for mr` still works
- `lore count notes -f mr` now fails with a clear error (unknown flag `-f`)
- `lore ingest -f` still works (means `--force`)

**Files touched:** `src/cli/args.rs`, `src/app/robot_docs.rs`

---

### P9: Consistent `--open` Short Flag on `notes`

**Goal:** Add `-o` shorthand to `notes --open`, matching `issues` and `mrs`.

**File:** `src/cli/args.rs` (line 292)

```rust
// BEFORE:
#[arg(long, help_heading = "Actions")]
pub open: bool,

// AFTER:
#[arg(short = 'o', long, help_heading = "Actions", overrides_with = "no_open")]
pub open: bool,

#[arg(long = "no-open", hide = true, overrides_with = "open")]
pub no_open: bool,
```

**Verification:**
- `lore notes -o` opens first result in browser
- Matches behavior of `lore issues -o` and `lore mrs -o`

**Files touched:** `src/cli/args.rs`

---

### Phase 1 Commit Summary

**Files modified:**
1. `src/cli/mod.rs` -- help_heading on all Commands variants + drift value_parser
2. `src/cli/args.rs` -- singular/plural value_parsers, remove `-f` from count, add `-o` to notes
3. `src/app/handlers.rs` -- normalization of entity/source_type strings
4. `src/app/robot_docs.rs` -- update count flags documentation

**Test plan:**
```bash
cargo check --all-targets
cargo clippy --all-targets -- -D warnings
cargo fmt --check
cargo test
lore --help                    # Verify grouped output
lore count issue               # Verify singular accepted
lore search --type issues 'x'  # Verify plural accepted
lore drift issue 42            # Verify singular accepted
lore notes -o                  # Verify short flag works
```

---

## Phase 2: Renames and Merges (2-3 commits)

These changes rename commands and merge overlapping ones. Hidden aliases preserve backward compatibility.

### P2: Rename `stats` -> `index`

**Goal:** Eliminate `status`/`stats`/`stat` confusion. `stats` becomes `index`.

**File:** `src/cli/mod.rs`

```rust
// BEFORE:
/// Show document and index statistics
#[command(visible_alias = "stat", help_heading = "System")]
Stats(StatsArgs),

// AFTER:
/// Show document and index statistics
#[command(visible_alias = "idx", alias = "stats", alias = "stat", help_heading = "System")]
Index(StatsArgs),
```

Note: `alias = "stats"` and `alias = "stat"` are hidden aliases (not `visible_alias`) -- old invocations still work, but `--help` shows `index`.

**File:** `src/main.rs` (line 257)

```rust
// BEFORE:
Some(Commands::Stats(args)) => handle_stats(cli.config.as_deref(), args, robot_mode).await,

// AFTER:
Some(Commands::Index(args)) => handle_stats(cli.config.as_deref(), args, robot_mode).await,
```

**File:** `src/app/robot_docs.rs` (line 181)

```rust
// BEFORE:
"stats": {
    "description": "Show document and index statistics",
    ...

// AFTER:
"index": {
    "description": "Show document and index statistics (formerly 'stats')",
    ...
```

Also update references in:
- `robot_docs.rs` quick_start.lore_exclusive array (line 415): `"stats: Database statistics..."` -> `"index: Database statistics..."`
- `robot_docs.rs` aliases.deprecated_commands: add `"stats": "index"`, `"stat": "index"`

**File:** `src/cli/autocorrect.rs`

Update `CANONICAL_SUBCOMMANDS` (line 366-area):
```rust
// Replace "stats" with "index" in the canonical list
// Add ("stats", "index") and ("stat", "index") to SUBCOMMAND_ALIASES
```

Update `COMMAND_FLAGS` (line 166-area):
```rust
// BEFORE:
("stats", &["--check", ...]),

// AFTER:
("index", &["--check", ...]),
```

**File:** `src/cli/robot.rs` -- update `expand_fields_preset` if any preset key is `"stats"` (currently no stats preset, so no change needed).

**Verification:**
- `lore index` works (shows document/index stats)
- `lore stats` still works (hidden alias)
- `lore stat` still works (hidden alias)
- `lore index --check` works
- `lore --help` shows `index` in System group, not `stats`
- `lore robot-docs` shows `index` key in commands map

**Files touched:** `src/cli/mod.rs`, `src/main.rs`, `src/app/robot_docs.rs`, `src/cli/autocorrect.rs`

---

### P4: Merge `health` into `doctor`

**Goal:** One diagnostic command (`doctor`) with a `--quick` flag for the pre-flight check that `health` currently provides.

**File:** `src/cli/mod.rs`

```rust
// BEFORE:
/// Quick health check: config, database, schema version
#[command(after_help = "...")]
Health,

/// Check environment health
#[command(after_help = "...")]
Doctor,

// AFTER:
// Remove Health variant entirely. Add hidden alias:
/// Check environment health (--quick for fast pre-flight)
#[command(
    after_help = "...",
    alias = "health",  // hidden backward compat
    help_heading = "System"
)]
Doctor {
    /// Fast pre-flight check only (config, DB, schema). Exit 0 = healthy.
    #[arg(long)]
    quick: bool,
},
```

**File:** `src/main.rs`

```rust
// BEFORE:
Some(Commands::Doctor) => handle_doctor(cli.config.as_deref(), robot_mode).await,
...
Some(Commands::Health) => handle_health(cli.config.as_deref(), robot_mode).await,

// AFTER:
Some(Commands::Doctor { quick }) => {
    if quick {
        handle_health(cli.config.as_deref(), robot_mode).await
    } else {
        handle_doctor(cli.config.as_deref(), robot_mode).await
    }
}
// Health variant removed from enum, so no separate match arm
```

**File:** `src/app/robot_docs.rs`

Merge the `health` and `doctor` entries:
```rust
"doctor": {
    "description": "Environment health check. Use --quick for fast pre-flight (exit 0 = healthy, 19 = unhealthy).",
    "flags": ["--quick"],
    "example": "lore --robot doctor",
    "notes": {
        "quick_mode": "lore --robot doctor --quick — fast pre-flight check (formerly 'lore health'). Only checks config, DB, schema version. Returns exit 19 on failure.",
        "full_mode": "lore --robot doctor — full diagnostic: config, auth, database, Ollama"
    },
    "response_schema": {
        "full": { ... },  // current doctor schema
        "quick": { ... }  // current health schema
    }
}
```

Remove the standalone `health` entry from the commands map.

**File:** `src/cli/autocorrect.rs`

- Remove `"health"` from `CANONICAL_SUBCOMMANDS` (clap's `alias` handles it)
- Or keep it -- since clap treats aliases as valid subcommands, the autocorrect system will still resolve typos like `"helth"` to `"health"` which clap then maps to `doctor`. Either way works.

**File:** `src/app/robot_docs.rs` -- update `workflows.pre_flight`:
```rust
"pre_flight": [
    "lore --robot doctor --quick"
],
```

Add to aliases.deprecated_commands:
```rust
"health": "doctor --quick"
```

**Verification:**
- `lore doctor` runs full diagnostic (unchanged behavior)
- `lore doctor --quick` runs fast pre-flight (exit 0/19)
- `lore health` still works (hidden alias, runs `doctor --quick`)
- `lore --help` shows only `doctor` in System group
- `lore robot-docs` shows merged entry

**Files touched:** `src/cli/mod.rs`, `src/main.rs`, `src/app/robot_docs.rs`, `src/cli/autocorrect.rs`

**Important edge case:** `lore health` via the hidden alias will invoke `Doctor { quick: false }` unless we handle it specially. Two options:

**Option A (simpler):** Instead of making `health` an alias of `doctor`, keep both variants but hide `Health`:
```rust
#[command(hide = true, help_heading = "System")]
Health,
```
Then in `main.rs`, `Commands::Health` maps to `handle_health()` as before. This is less clean but zero-risk.

**Option B (cleaner):** In the autocorrect layer, rewrite `health` -> `doctor --quick` before clap parsing:
```rust
// In SUBCOMMAND_ALIASES or a new pre-clap rewrite:
("health", "doctor"),  // plus inject "--quick" flag
```
This requires a small enhancement to autocorrect to support flag injection during alias resolution.

**Recommendation:** Use Option A for initial implementation. It's one line (`hide = true`) and achieves the goal of removing `health` from `--help` while preserving full backward compatibility. The `doctor --quick` flag is additive.

---

### P7: Hide Pipeline Sub-stages

**Goal:** Remove `ingest`, `generate-docs`, `embed` from `--help` while keeping them fully functional.

**File:** `src/cli/mod.rs`

```rust
// Add hide = true to each:

/// Ingest data from GitLab
#[command(hide = true)]
Ingest(IngestArgs),

/// Generate searchable documents from ingested data
#[command(name = "generate-docs", hide = true)]
GenerateDocs(GenerateDocsArgs),

/// Generate vector embeddings for documents via Ollama
#[command(hide = true)]
Embed(EmbedArgs),
```

**File:** `src/cli/mod.rs` -- Update `Sync` help text to mention the individual stage commands:

```rust
/// Run full sync pipeline: ingest -> generate-docs -> embed
#[command(after_help = "\x1b[1mExamples:\x1b[0m
  lore sync                             # Full pipeline: ingest + docs + embed
  lore sync --no-embed                  # Skip embedding step
  ...

\x1b[1mIndividual stages:\x1b[0m
  lore ingest                           # Fetch from GitLab only
  lore generate-docs                    # Rebuild documents only
  lore embed                            # Re-embed only",
    help_heading = "Data Pipeline"
)]
Sync(SyncArgs),
```

**File:** `src/app/robot_docs.rs` -- Add a `"hidden": true` field to the ingest/generate-docs/embed entries so agents know these are secondary:
```rust
"ingest": {
    "hidden": true,
    "description": "Sync data from GitLab (prefer 'sync' for full pipeline)",
    ...
```

**Verification:**
- `lore --help` no longer shows ingest, generate-docs, embed
- `lore ingest`, `lore generate-docs`, `lore embed` all still work
- `lore sync --help` mentions individual stage commands
- `lore robot-docs` still includes all three (with `hidden: true`)

**Files touched:** `src/cli/mod.rs`, `src/app/robot_docs.rs`

---

### Phase 2 Commit Summary

**Commit A: Rename `stats` -> `index`**
- `src/cli/mod.rs`, `src/main.rs`, `src/app/robot_docs.rs`, `src/cli/autocorrect.rs`

**Commit B: Merge `health` into `doctor`, hide pipeline stages**
- `src/cli/mod.rs`, `src/main.rs`, `src/app/robot_docs.rs`, `src/cli/autocorrect.rs`

**Test plan:**
```bash
cargo check --all-targets
cargo clippy --all-targets -- -D warnings
cargo fmt --check
cargo test

# Rename verification
lore index                     # Works (new name)
lore stats                     # Works (hidden alias)
lore index --check             # Works

# Doctor merge verification
lore doctor                    # Full diagnostic
lore doctor --quick            # Fast pre-flight
lore health                    # Still works (hidden)

# Hidden stages verification
lore --help                    # ingest/generate-docs/embed gone
lore ingest                    # Still works
lore sync --help               # Mentions individual stages
```

---

## Phase 3: Structural Consolidation (requires careful design)

These changes merge or absorb commands. More effort, more testing, but the biggest UX wins.

### P6: Consolidate `file-history` into `trace`

**Goal:** `trace` absorbs `file-history`. One command for file-centric intelligence.

**Approach:** Add `--mrs-only` flag to `trace`. When set, output matches `file-history` format (flat MR list, no issue/discussion linking). `file-history` becomes a hidden alias.

**File:** `src/cli/args.rs` -- Add flag to `TraceArgs`:

```rust
pub struct TraceArgs {
    pub path: String,

    #[arg(short = 'p', long, help_heading = "Filters")]
    pub project: Option<String>,

    #[arg(long, help_heading = "Output")]
    pub discussions: bool,

    #[arg(long = "no-follow-renames", help_heading = "Filters")]
    pub no_follow_renames: bool,

    #[arg(short = 'n', long = "limit", default_value = "20", help_heading = "Output")]
    pub limit: usize,

    // NEW: absorb file-history behavior
    /// Show only MR list without issue/discussion linking (file-history mode)
    #[arg(long = "mrs-only", help_heading = "Output")]
    pub mrs_only: bool,

    /// Only show merged MRs (file-history mode)
    #[arg(long, help_heading = "Filters")]
    pub merged: bool,
}
```

**File:** `src/cli/mod.rs` -- Hide `FileHistory`:

```rust
/// Show MRs that touched a file, with linked discussions
#[command(name = "file-history", hide = true, help_heading = "File Analysis")]
FileHistory(FileHistoryArgs),
```

**File:** `src/app/handlers.rs` -- Route `trace --mrs-only` to the file-history handler:

```rust
fn handle_trace(
    config_override: Option<&str>,
    args: TraceArgs,
    robot_mode: bool,
) -> Result<(), Box<dyn std::error::Error>> {
    if args.mrs_only {
        // Delegate to file-history handler
        let fh_args = FileHistoryArgs {
            path: args.path,
            project: args.project,
            discussions: args.discussions,
            no_follow_renames: args.no_follow_renames,
            merged: args.merged,
            limit: args.limit,
        };
        return handle_file_history(config_override, fh_args, robot_mode);
    }
    // ... existing trace logic ...
}
```

**File:** `src/app/robot_docs.rs` -- Update trace entry, mark file-history as deprecated:

```rust
"trace": {
    "description": "Trace why code was introduced: file -> MR -> issue -> discussion. Use --mrs-only for flat MR listing.",
    "flags": ["<path>", "-p/--project", "--discussions", "--no-follow-renames", "-n/--limit", "--mrs-only", "--merged"],
    ...
},
"file-history": {
    "hidden": true,
    "deprecated": "Use 'trace --mrs-only' instead",
    ...
}
```

**Verification:**
- `lore trace src/main.rs` works unchanged
- `lore trace src/main.rs --mrs-only` produces file-history output
- `lore trace src/main.rs --mrs-only --merged` filters to merged MRs
- `lore file-history src/main.rs` still works (hidden command)
- `lore --help` shows only `trace` in File Analysis group

**Files touched:** `src/cli/args.rs`, `src/cli/mod.rs`, `src/app/handlers.rs`, `src/app/robot_docs.rs`

---

### P8: Make `count` a Flag on Entity Commands

**Goal:** `lore issues --count` replaces `lore count issues`. Standalone `count` becomes hidden.

**File:** `src/cli/args.rs` -- Add `--count` to `IssuesArgs`, `MrsArgs`, `NotesArgs`:

```rust
// In IssuesArgs:
/// Show count only (no listing)
#[arg(long, help_heading = "Output", conflicts_with_all = ["iid", "open"])]
pub count: bool,

// In MrsArgs:
/// Show count only (no listing)
#[arg(long, help_heading = "Output", conflicts_with_all = ["iid", "open"])]
pub count: bool,

// In NotesArgs:
/// Show count only (no listing)
#[arg(long, help_heading = "Output", conflicts_with = "open")]
pub count: bool,
```

**File:** `src/app/handlers.rs` -- In `handle_issues`, `handle_mrs`, `handle_notes`, check the count flag early:

```rust
// In handle_issues (pseudocode):
if args.count {
    let count_args = CountArgs { entity: "issues".to_string(), for_entity: None };
    return handle_count(config_override, count_args, robot_mode).await;
}
```

**File:** `src/cli/mod.rs` -- Hide `Count`:

```rust
/// Count entities in local database
#[command(hide = true, help_heading = "Query")]
Count(CountArgs),
```

**File:** `src/app/robot_docs.rs` -- Mark count as hidden, add `--count` documentation to issues/mrs/notes entries.

**Verification:**
- `lore issues --count` returns issue count
- `lore mrs --count` returns MR count
- `lore notes --count` returns note count
- `lore count issues` still works (hidden)
- `lore count discussions --for mr` still works (no equivalent in the new pattern -- discussions/events/references still need the standalone `count` command)

**Important note:** `count` supports entity types that don't have their own command (discussions, events, references). The standalone `count` must remain functional (just hidden). The `--count` flag on `issues`/`mrs`/`notes` handles the common cases only.

**Files touched:** `src/cli/args.rs`, `src/cli/mod.rs`, `src/app/handlers.rs`, `src/app/robot_docs.rs`

---

### P10: Add `--sort` to `search`

**Goal:** Allow sorting search results by score, created date, or updated date.

**File:** `src/cli/args.rs` -- Add to `SearchArgs`:

```rust
/// Sort results by field (score is default for ranked search)
#[arg(long, value_parser = ["score", "created", "updated"], default_value = "score", help_heading = "Sorting")]
pub sort: String,

/// Sort ascending (default: descending)
#[arg(long, help_heading = "Sorting", overrides_with = "no_asc")]
pub asc: bool,

#[arg(long = "no-asc", hide = true, overrides_with = "asc")]
pub no_asc: bool,
```

**File:** `src/cli/commands/search.rs` -- Thread the sort parameter through to the search query.

The search function currently returns results sorted by score. When `--sort created` or `--sort updated` is specified, apply an `ORDER BY` clause to the final result set.

**File:** `src/app/robot_docs.rs` -- Add `--sort` and `--asc` to the search command's flags list.

**Verification:**
- `lore search 'auth' --sort score` (default, unchanged)
- `lore search 'auth' --sort created --asc` (oldest first)
- `lore search 'auth' --sort updated` (most recently updated first)

**Files touched:** `src/cli/args.rs`, `src/cli/commands/search.rs`, `src/app/robot_docs.rs`

---

### Phase 3 Commit Summary

**Commit C: Consolidate file-history into trace**
- `src/cli/args.rs`, `src/cli/mod.rs`, `src/app/handlers.rs`, `src/app/robot_docs.rs`

**Commit D: Add `--count` flag to entity commands**
- `src/cli/args.rs`, `src/cli/mod.rs`, `src/app/handlers.rs`, `src/app/robot_docs.rs`

**Commit E: Add `--sort` to search**
- `src/cli/args.rs`, `src/cli/commands/search.rs`, `src/app/robot_docs.rs`

**Test plan:**
```bash
cargo check --all-targets
cargo clippy --all-targets -- -D warnings
cargo fmt --check
cargo test

# trace consolidation
lore trace src/main.rs --mrs-only
lore trace src/main.rs --mrs-only --merged --discussions
lore file-history src/main.rs     # backward compat

# count flag
lore issues --count
lore mrs --count -s opened
lore notes --count --for-issue 42
lore count discussions --for mr   # still works

# search sort
lore search 'auth' --sort created --asc
```

---

## Documentation Updates

After all implementation is complete:

### CLAUDE.md / AGENTS.md

Update the robot mode command reference to reflect:
- `stats` -> `index` (with note that `stats` is a hidden alias)
- `health` -> `doctor --quick` (with note that `health` is a hidden alias)
- Remove `ingest`, `generate-docs`, `embed` from the primary command table (mention as "hidden, use `sync`")
- Remove `file-history` from primary table (mention as "hidden, use `trace --mrs-only`")
- Add `--count` flag to issues/mrs/notes documentation
- Add `--sort` flag to search documentation
- Add `--mrs-only` and `--merged` flags to trace documentation

### robot-docs Self-Discovery

The `robot_docs.rs` changes above handle this. Key points:
- New `"hidden": true` field on deprecated/hidden commands
- Updated descriptions mentioning canonical alternatives
- Updated flags lists
- Updated workflows section

---

## File Impact Summary

| File | Phase 1 | Phase 2 | Phase 3 | Total Changes |
|------|---------|---------|---------|---------------|
| `src/cli/mod.rs` | help_heading, drift value_parser | stats->index rename, hide health, hide pipeline stages | hide file-history, hide count | 4 passes |
| `src/cli/args.rs` | singular/plural, remove `-f`, add `-o` | — | `--mrs-only`/`--merged` on trace, `--count` on entities, `--sort` on search | 2 passes |
| `src/app/handlers.rs` | normalize entity strings | route doctor --quick | trace mrs-only delegation, count flag routing | 3 passes |
| `src/app/robot_docs.rs` | update count flags | rename stats->index, merge health+doctor, add hidden field | update trace, file-history, count, search entries | 3 passes |
| `src/cli/autocorrect.rs` | — | update CANONICAL_SUBCOMMANDS, SUBCOMMAND_ALIASES, COMMAND_FLAGS | — | 1 pass |
| `src/main.rs` | — | stats->index variant rename, doctor variant change | — | 1 pass |
| `src/cli/commands/search.rs` | — | — | sort parameter threading | 1 pass |

---

## Before / After Summary

### Command Count

| Metric | Before | After | Delta |
|--------|--------|-------|-------|
| Visible top-level commands | 29 | 21 | -8 (-28%) |
| Hidden commands (functional) | 4 | 12 | +8 (absorbed) |
| Stub/unimplemented commands | 2 | 2 | 0 |
| Total functional commands | 33 | 33 | 0 (nothing lost) |

### `lore --help` Output

**Before (29 commands, flat list, ~50 lines of commands):**
```
Commands:
  issues         List or show issues [aliases: issue]
  mrs            List or show merge requests [aliases: mr]
  notes          List notes from discussions [aliases: note]
  ingest         Ingest data from GitLab
  count          Count entities in local database
  status         Show sync state [aliases: st]
  auth           Verify GitLab authentication
  doctor         Check environment health
  version        Show version information
  init           Initialize configuration and database
  search         Search indexed documents [aliases: find]
  stats          Show document and index statistics [aliases: stat]
  generate-docs  Generate searchable documents from ingested data
  embed          Generate vector embeddings for documents via Ollama
  sync           Run full sync pipeline: ingest -> generate-docs -> embed
  migrate        Run pending database migrations
  health         Quick health check: config, database, schema version
  robot-docs     Machine-readable command manifest for agent self-discovery
  completions    Generate shell completions
  timeline       Show a chronological timeline of events matching a query
  who            People intelligence: experts, workload, active discussions, overlap
  me             Personal work dashboard: open issues, authored/reviewing MRs, activity
  file-history   Show MRs that touched a file, with linked discussions
  trace          Trace why code was introduced: file -> MR -> issue -> discussion
  drift          Detect discussion divergence from original intent
  related        Find semantically related entities via vector search
  cron           Manage cron-based automatic syncing
  token          Manage stored GitLab token
  help           Print this message or the help of the given subcommand(s)
```

**After (21 commands, grouped, ~35 lines of commands):**
```
Query:
  issues      List or show issues [aliases: issue]
  mrs         List or show merge requests [aliases: mr]
  notes       List notes from discussions [aliases: note]
  search      Search indexed documents [aliases: find]

Intelligence:
  timeline    Chronological timeline of events
  who         People intelligence: experts, workload, overlap
  me          Personal work dashboard

File Analysis:
  trace       Trace code provenance / file history
  related     Find semantically related entities
  drift       Detect discussion divergence

Data Pipeline:
  sync        Run full sync pipeline

System:
  init        Initialize configuration and database
  status      Show sync state [aliases: st]
  doctor      Check environment health (--quick for pre-flight)
  index       Document and index statistics [aliases: idx]
  auth        Verify GitLab authentication
  token       Manage stored GitLab token
  migrate     Run pending database migrations
  cron        Manage automatic syncing
  robot-docs  Agent self-discovery manifest
  completions Generate shell completions
  version     Show version information
```

### Flag Consistency

| Issue | Before | After |
|-------|--------|-------|
| `-f` collision (force vs for) | `ingest -f`=force, `count -f`=for | `-f` removed from count; `-f` = force everywhere |
| Singular/plural entity types | `count issues` but `search --type issue` | Both forms accepted everywhere |
| `notes --open` missing `-o` | `notes --open` (no shorthand) | `notes -o` works (matches issues/mrs) |
| `search` missing `--sort` | No sort override | `--sort score\|created\|updated` + `--asc` |

### Naming Confusion

| Before | After | Resolution |
|--------|-------|------------|
| `status` vs `stats` vs `stat` (3 names, 2 commands) | `status` + `index` (2 names, 2 commands) | Eliminated near-homonym collision |
| `health` vs `doctor` (2 commands, overlapping scope) | `doctor` + `doctor --quick` (1 command) | Progressive disclosure |
| `trace` vs `file-history` (2 commands, overlapping function) | `trace` + `trace --mrs-only` (1 command) | Superset absorbs subset |

### Robot Ergonomics

| Metric | Before | After |
|--------|--------|-------|
| Commands in robot-docs manifest | 29 | 21 visible + hidden section |
| Agent decision space for "system check" | 4 commands | 2 commands (status, doctor) |
| Agent decision space for "file query" | 3 commands + 2 who modes | 1 command (trace) + 2 who modes |
| Entity type parse errors from singular/plural | Common | Eliminated |
| Estimated token cost of robot-docs | Baseline | ~15% reduction (fewer entries, hidden flagged) |

### What Stays Exactly The Same

- All 33 functional commands remain callable (nothing is removed)
- All existing flags and their behavior are preserved
- All response schemas are unchanged
- All exit codes are unchanged
- The autocorrect system continues to work
- All hidden/deprecated commands emit their existing warnings

### What Breaks (Intentional)

- `lore count -f mr` (the `-f` shorthand) -- must use `--for` instead
- `lore --help` layout changes (commands are grouped, 8 commands hidden)
- `lore robot-docs` output changes (new `hidden` field, renamed keys)
- Any scripts parsing `--help` text (but `robot-docs` is the stable contract)
