Files
gitlore/command-restructure/IMPLEMENTATION_PLAN.md
teernisse 06852e90a6 docs(cli): add command restructure audit and implementation plan
CLI audit scoring the current command surface across human ergonomics,
robot/agent ergonomics, documentation quality, and flag design. Paired
with a detailed implementation plan for restructuring commands into a
more consistent, discoverable hierarchy.
2026-03-10 11:06:53 -04:00

32 KiB

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:

#[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):

// 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):

// 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):

// 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:

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:

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:

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)

// 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):

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

And normalize in handle_count:

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:

// 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)

// 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:

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

// 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)

// 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)

// 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):

// Replace "stats" with "index" in the canonical list
// Add ("stats", "index") and ("stat", "index") to SUBCOMMAND_ALIASES

Update COMMAND_FLAGS (line 166-area):

// 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

// 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

// 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:

"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:

"pre_flight": [
    "lore --robot doctor --quick"
],

Add to aliases.deprecated_commands:

"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:

#[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:

// 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

// 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:

/// 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:

"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:

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:

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:

/// 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:

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:

"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:

// 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:

// 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:

/// 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


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

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

/// 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:

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)