feat(cli): Implement complete command-line interface

Provides a user-friendly CLI for all GitLab Inbox operations.

src/cli/mod.rs - Clap command definitions:
- Global --config flag for alternate config path
- Subcommands: init, auth-test, doctor, version, backup, reset,
  migrate, sync-status, ingest, list, count, show
- Ingest supports --type (issues/merge_requests), --project filter,
  --force lock override, --full resync
- List supports rich filtering: --state, --author, --assignee,
  --label, --milestone, --since, --due-before, --has-due-date
- List supports --sort (updated/created/iid), --order (asc/desc)
- List supports --open to launch browser, --json for scripting

src/cli/commands/ - Command implementations:

init.rs: Interactive configuration wizard
- Prompts for GitLab URL, token env var, projects to track
- Creates config file and initializes database
- Supports --force overwrite and --non-interactive mode

auth_test.rs: Verify GitLab authentication
- Calls /api/v4/user to validate token
- Displays username and GitLab instance URL

doctor.rs: Environment health check
- Validates config file exists and parses correctly
- Checks database connectivity and migration state
- Verifies GitLab authentication
- Reports token environment variable status
- Supports --json output for CI integration

ingest.rs: Data synchronization from GitLab
- Acquires sync lock with stale detection
- Shows progress bars for issues and discussions
- Reports sync statistics on completion
- Supports --full flag to reset cursors and refetch all data

list.rs: Query local database
- Formatted table output with comfy-table
- Filters build dynamic SQL with parameterized queries
- Username filters normalize @ prefix automatically
- --open flag uses 'open' crate for cross-platform browser launch
- --json outputs array of issue objects

show.rs: Detailed entity view
- Displays issue metadata in structured format
- Shows full description with markdown
- Lists labels, assignees, milestone
- Shows discussion threads with notes

count.rs: Entity statistics
- Counts issues, discussions, or notes
- Supports --type filter for discussions/notes

sync_status.rs: Display sync watermarks
- Shows last sync time per project
- Displays cursor positions for debugging

src/main.rs - Application entry point:
- Initializes tracing subscriber with env-filter
- Parses CLI arguments via clap
- Dispatches to appropriate command handler
- Consistent error formatting for all failure modes

src/lib.rs - Library entry point:
- Exports cli, core, gitlab, ingestion modules
- Re-exports Config, GiError, Result for convenience

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Taylor Eernisse
2026-01-26 11:28:52 -05:00
parent cd60350c6d
commit 8fb890c528
12 changed files with 3034 additions and 0 deletions

168
src/cli/mod.rs Normal file
View File

@@ -0,0 +1,168 @@
//! CLI module with clap command definitions.
pub mod commands;
use clap::{Parser, Subcommand};
/// GitLab Inbox - Unified notification management
#[derive(Parser)]
#[command(name = "gi")]
#[command(version, about, long_about = None)]
pub struct Cli {
/// Path to config file
#[arg(short, long, global = true)]
pub config: Option<String>,
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
/// Initialize configuration and database
Init {
/// Skip overwrite confirmation
#[arg(long)]
force: bool,
/// Fail if prompts would be shown
#[arg(long)]
non_interactive: bool,
},
/// Verify GitLab authentication
AuthTest,
/// Check environment health
Doctor {
/// Output as JSON
#[arg(long)]
json: bool,
},
/// Show version information
Version,
/// Create timestamped database backup
Backup,
/// Delete database and reset all state
Reset {
/// Skip confirmation prompt
#[arg(long)]
confirm: bool,
},
/// Run pending database migrations
Migrate,
/// Show sync state
SyncStatus,
/// Ingest data from GitLab
Ingest {
/// Resource type to ingest
#[arg(long, value_parser = ["issues", "merge_requests"])]
r#type: String,
/// Filter to single project
#[arg(long)]
project: Option<String>,
/// Override stale sync lock
#[arg(long)]
force: bool,
/// Full re-sync: reset cursors and fetch all data from scratch
#[arg(long)]
full: bool,
},
/// List issues or MRs from local database
List {
/// Entity type to list
#[arg(value_parser = ["issues", "mrs"])]
entity: String,
/// Maximum results
#[arg(long, default_value = "50")]
limit: usize,
/// Filter by project path
#[arg(long)]
project: Option<String>,
/// Filter by state
#[arg(long, value_parser = ["opened", "closed", "all"])]
state: Option<String>,
/// Filter by author username
#[arg(long)]
author: Option<String>,
/// Filter by assignee username
#[arg(long)]
assignee: Option<String>,
/// Filter by label (repeatable, AND logic)
#[arg(long)]
label: Option<Vec<String>>,
/// Filter by milestone title
#[arg(long)]
milestone: Option<String>,
/// Filter by time (7d, 2w, 1m, or YYYY-MM-DD)
#[arg(long)]
since: Option<String>,
/// Filter by due date (before this date, YYYY-MM-DD)
#[arg(long)]
due_before: Option<String>,
/// Show only issues with a due date
#[arg(long)]
has_due_date: bool,
/// Sort field
#[arg(long, value_parser = ["updated", "created", "iid"], default_value = "updated")]
sort: String,
/// Sort order
#[arg(long, value_parser = ["desc", "asc"], default_value = "desc")]
order: String,
/// Open first matching issue in browser
#[arg(long)]
open: bool,
/// Output as JSON
#[arg(long)]
json: bool,
},
/// Count entities in local database
Count {
/// Entity type to count
#[arg(value_parser = ["issues", "mrs", "discussions", "notes"])]
entity: String,
/// Filter by noteable type (for discussions/notes)
#[arg(long, value_parser = ["issue", "mr"])]
r#type: Option<String>,
},
/// Show detailed entity information
Show {
/// Entity type to show
#[arg(value_parser = ["issue", "mr"])]
entity: String,
/// Entity IID
iid: i64,
/// Filter by project path (required if iid is ambiguous)
#[arg(long)]
project: Option<String>,
},
}