fix(cli): audit-driven improvements to flags, help, exit codes, and deprecation

Addresses findings from a comprehensive CLI readiness audit:

Flag design (I2):
- Add hidden --no-verbose flag with overrides_with semantics, matching
  the --no-quiet pattern already established for all other boolean flags.

Help text (I3):
- Add after_help examples to issues, mrs, search, sync, and timeline
  subcommands. Each shows 3-4 concrete, runnable commands with comments.

Help headings (I4/P5):
- Move --mode and --fts-mode from "Output" heading to "Mode" heading
  in the search subcommand. These control search strategy, not output
  format — "Output" is reserved for --limit, --explain, --fields.

Exit codes (I5):
- Health check failure now exits 19 (was 1). Exit code 1 is reserved
  for internal errors only. robot-docs updated to document code 19.

Deprecation visibility (P4):
- Deprecated commands (list, show, auth-test, sync-status) now emit
  structured JSON warnings to stderr in robot mode:
  {"warning":{"type":"DEPRECATED","message":"...","successor":"..."}}
  Previously these were silently swallowed in robot mode.

Version string (P1):
- Cli struct uses env!("LORE_VERSION") from build.rs so --version shows
  git hash (see previous commit).

Fields flag (P3):
- --fields help text updated to document the "minimal" preset.

Robot-docs (parallel work):
- response_schema added for every command, documenting the JSON shape
  agents will receive. Agents can now introspect expected fields before
  calling a command.
- error_format documents the new "actions" array.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Taylor Eernisse
2026-02-06 23:47:04 -05:00
parent cf6d27435a
commit b5f78e31a8
2 changed files with 248 additions and 62 deletions

View File

@@ -1,12 +1,13 @@
pub mod commands;
pub mod progress;
pub mod robot;
use clap::{Parser, Subcommand};
use std::io::IsTerminal;
#[derive(Parser)]
#[command(name = "lore")]
#[command(version, about = "Local GitLab data management with semantic search", long_about = None)]
#[command(version = env!("LORE_VERSION"), about = "Local GitLab data management with semantic search", long_about = None)]
#[command(subcommand_required = false)]
pub struct Cli {
/// Path to config file
@@ -54,9 +55,17 @@ pub struct Cli {
pub no_quiet: bool,
/// Increase log verbosity (-v, -vv, -vvv)
#[arg(short = 'v', long = "verbose", action = clap::ArgAction::Count, global = true, help = "Increase log verbosity (-v, -vv, -vvv)")]
#[arg(short = 'v', long = "verbose", action = clap::ArgAction::Count, global = true, help = "Increase log verbosity (-v, -vv, -vvv)", overrides_with = "no_verbose")]
pub verbose: u8,
#[arg(
long = "no-verbose",
global = true,
hide = true,
overrides_with = "verbose"
)]
pub no_verbose: bool,
/// Log format for stderr output: text (default) or json
#[arg(long = "log-format", global = true, value_parser = ["text", "json"], default_value = "text", help = "Log format for stderr output: text (default) or json")]
pub log_format: String,
@@ -246,6 +255,11 @@ pub enum Commands {
}
#[derive(Parser)]
#[command(after_help = "\x1b[1mExamples:\x1b[0m
lore issues -n 10 # List 10 most recently updated issues
lore issues -s opened -l bug # Open issues labeled 'bug'
lore issues 42 -p group/repo # Show issue #42 in a specific project
lore issues --since 7d -a jsmith # Issues updated in last 7 days by jsmith")]
pub struct IssuesArgs {
/// Issue IID (omit to list, provide to show details)
pub iid: Option<i64>,
@@ -259,7 +273,7 @@ pub struct IssuesArgs {
)]
pub limit: usize,
/// Select output fields (comma-separated: iid,title,state,author,labels,updated)
/// Select output fields (comma-separated, or 'minimal' preset: iid,title,state,updated_at_iso)
#[arg(long, help_heading = "Output", value_delimiter = ',')]
pub fields: Option<Vec<String>>,
@@ -331,6 +345,11 @@ pub struct IssuesArgs {
}
#[derive(Parser)]
#[command(after_help = "\x1b[1mExamples:\x1b[0m
lore mrs -s opened # List open merge requests
lore mrs -s merged --since 2w # MRs merged in the last 2 weeks
lore mrs 99 -p group/repo # Show MR !99 in a specific project
lore mrs -D --reviewer jsmith # Non-draft MRs reviewed by jsmith")]
pub struct MrsArgs {
/// MR IID (omit to list, provide to show details)
pub iid: Option<i64>,
@@ -344,7 +363,7 @@ pub struct MrsArgs {
)]
pub limit: usize,
/// Select output fields (comma-separated: iid,title,state,author,labels,updated)
/// Select output fields (comma-separated, or 'minimal' preset: iid,title,state,updated_at_iso)
#[arg(long, help_heading = "Output", value_delimiter = ',')]
pub fields: Option<Vec<String>>,
@@ -480,12 +499,17 @@ pub struct StatsArgs {
}
#[derive(Parser)]
#[command(after_help = "\x1b[1mExamples:\x1b[0m
lore search 'authentication bug' # Hybrid search (default)
lore search 'deploy' --mode lexical --type mr # Lexical search, MRs only
lore search 'API rate limit' --since 30d # Recent results only
lore search 'config' -p group/repo --explain # With ranking explanation")]
pub struct SearchArgs {
/// Search query string
pub query: String,
/// Search mode (lexical, hybrid, semantic)
#[arg(long, default_value = "hybrid", value_parser = ["lexical", "hybrid", "semantic"], help_heading = "Output")]
#[arg(long, default_value = "hybrid", value_parser = ["lexical", "hybrid", "semantic"], help_heading = "Mode")]
pub mode: String,
/// Filter by source type (issue, mr, discussion)
@@ -533,7 +557,7 @@ pub struct SearchArgs {
pub no_explain: bool,
/// FTS query mode: safe (default) or raw
#[arg(long = "fts-mode", default_value = "safe", value_parser = ["safe", "raw"], help_heading = "Output")]
#[arg(long = "fts-mode", default_value = "safe", value_parser = ["safe", "raw"], help_heading = "Mode")]
pub fts_mode: String,
}
@@ -549,6 +573,11 @@ pub struct GenerateDocsArgs {
}
#[derive(Parser)]
#[command(after_help = "\x1b[1mExamples:\x1b[0m
lore sync # Full pipeline: ingest + docs + embed
lore sync --no-embed # Skip embedding step
lore sync --full --force # Full re-sync, override stale lock
lore sync --dry-run # Preview what would change")]
pub struct SyncArgs {
/// Reset cursors, fetch everything
#[arg(long, overrides_with = "no_full")]
@@ -602,6 +631,10 @@ pub struct EmbedArgs {
}
#[derive(Parser)]
#[command(after_help = "\x1b[1mExamples:\x1b[0m
lore timeline 'deployment' # Events related to deployments
lore timeline 'auth' --since 30d -p group/repo # Scoped to project and time
lore timeline 'migration' --depth 2 --expand-mentions # Deep cross-reference expansion")]
pub struct TimelineArgs {
/// Search query (keywords to find in issues, MRs, and discussions)
pub query: String,