feat(me): add lore me personal work dashboard command
Implement a personal work dashboard that shows everything relevant to the
configured GitLab user: open issues assigned to them, MRs they authored,
MRs they are reviewing, and a chronological activity feed.
Design decisions:
- Attention state computed from GitLab interaction data (comments, reviews)
with no local state tracking -- purely derived from existing synced data
- Username resolution: --user flag > config.gitlab.username > actionable error
- Project scoping: --project (fuzzy) | --all | default_project | all
- Section filtering: --issues, --mrs, --activity (combinable, default = all)
- Activity feed controlled by --since (default 30d); work item sections
always show all open items regardless of --since
Architecture (src/cli/commands/me/):
- types.rs: MeDashboard, MeSummary, AttentionState data types
- queries.rs: 4 SQL queries (open_issues, authored_mrs, reviewing_mrs,
activity) using existing issue_assignees, mr_reviewers, notes tables
- render_human.rs: colored terminal output with attention state indicators
- render_robot.rs: {ok, data, meta} JSON envelope with field selection
- mod.rs: orchestration (resolve_username, resolve_project_scope, run_me)
- me_tests.rs: comprehensive unit tests covering all query paths
Config additions:
- New optional gitlab.username field in config.json
- Tests for config with/without username
- Existing test configs updated with username: None
CLI wiring:
- MeArgs struct with section filter, since, project, all, user, fields flags
- Autocorrect support for me command flags
- LoreRenderer::try_get() for safe renderer access in me module
- Robot mode field selection presets (me_items, me_activity)
- handle_me() in main.rs command dispatch
Also fixes duplicate assertions in surgical sync tests (removed 6
duplicate assert! lines that were copy-paste artifacts).
Spec: docs/lore-me-spec.md
This commit is contained in:
@@ -261,6 +261,9 @@ pub enum Commands {
|
||||
/// People intelligence: experts, workload, active discussions, overlap
|
||||
Who(WhoArgs),
|
||||
|
||||
/// Personal work dashboard: open issues, authored/reviewing MRs, activity
|
||||
Me(MeArgs),
|
||||
|
||||
/// Show MRs that touched a file, with linked discussions
|
||||
#[command(name = "file-history")]
|
||||
FileHistory(FileHistoryArgs),
|
||||
@@ -1050,6 +1053,57 @@ pub struct WhoArgs {
|
||||
pub all_history: bool,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(after_help = "\x1b[1mExamples:\x1b[0m
|
||||
lore me # Full dashboard (default project or all)
|
||||
lore me --issues # Issues section only
|
||||
lore me --mrs # MRs section only
|
||||
lore me --activity # Activity feed only
|
||||
lore me --all # All synced projects
|
||||
lore me --since 2d # Activity window (default: 30d)
|
||||
lore me --project group/repo # Scope to one project
|
||||
lore me --user jdoe # Override configured username")]
|
||||
pub struct MeArgs {
|
||||
/// Show open issues section
|
||||
#[arg(long, help_heading = "Sections")]
|
||||
pub issues: bool,
|
||||
|
||||
/// Show authored + reviewing MRs section
|
||||
#[arg(long, help_heading = "Sections")]
|
||||
pub mrs: bool,
|
||||
|
||||
/// Show activity feed section
|
||||
#[arg(long, help_heading = "Sections")]
|
||||
pub activity: bool,
|
||||
|
||||
/// Activity window (e.g. 7d, 2w, 30d). Default: 30d. Only affects activity section.
|
||||
#[arg(long, help_heading = "Filters")]
|
||||
pub since: Option<String>,
|
||||
|
||||
/// Scope to a project (supports fuzzy matching)
|
||||
#[arg(short = 'p', long, help_heading = "Filters", conflicts_with = "all")]
|
||||
pub project: Option<String>,
|
||||
|
||||
/// Show all synced projects (overrides default_project)
|
||||
#[arg(long, help_heading = "Filters", conflicts_with = "project")]
|
||||
pub all: bool,
|
||||
|
||||
/// Override configured username
|
||||
#[arg(long = "user", help_heading = "Filters")]
|
||||
pub user: Option<String>,
|
||||
|
||||
/// Select output fields (comma-separated, or 'minimal' preset)
|
||||
#[arg(long, help_heading = "Output", value_delimiter = ',')]
|
||||
pub fields: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl MeArgs {
|
||||
/// Returns true if no section flags were passed (show all sections).
|
||||
pub fn show_all_sections(&self) -> bool {
|
||||
!self.issues && !self.mrs && !self.activity
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(after_help = "\x1b[1mExamples:\x1b[0m
|
||||
lore file-history src/main.rs # MRs that touched this file
|
||||
|
||||
Reference in New Issue
Block a user