feat(cli): add --no-status flag to skip GraphQL status enrichment during sync

This commit is contained in:
teernisse
2026-02-16 09:43:36 -05:00
parent fc0d9cb1d3
commit b9063aa17a
4 changed files with 1222 additions and 8 deletions

99
.claude/plan.md Normal file
View File

@@ -0,0 +1,99 @@
# Plan: Add Colors to Sync Command Output
## Current State
The sync output has three layers, each needing color treatment:
### Layer 1: Stage Lines (during sync)
```
✓ Issues 10 issues from 2 projects 4.2s
✓ Status 3 statuses updated · 5 seen 4.2s
vs/typescript-code 2 issues · 1 statuses updated
✓ MRs 5 merge requests from 2 projects 12.3s
vs/python-code 3 MRs · 10 discussions
✓ Docs 1,200 documents generated 8.1s
✓ Embed 3,400 chunks embedded 45.2s
```
**What's uncolored:** icons, labels, numbers, elapsed times, sub-row project paths, failure counts in parentheses.
### Layer 2: Summary (after sync)
```
Synced 10 issues and 5 MRs in 42.3s
120 discussions · 45 events · 12 diffs · 3 statuses updated
1,200 docs regenerated · 3,400 embedded
```
**What's already colored:** headline ("Synced" = green bold, "Sync completed with issues" = warning bold), issue/MR counts (bold), error line (red). Detail lines are all dim.
### Layer 3: Timing breakdown (`-t` flag)
```
── Timing ──────────────────────
issues .............. 4.2s
merge_requests ...... 12.3s
```
**What's already colored:** dots (dim), time (bold), errors (red), rate limits (warning).
---
## Color Plan
Using only existing `Theme` methods — no new colors needed.
### Stage Lines (`format_stage_line` + callers in sync.rs)
| Element | Current | Proposed | Theme method |
|---------|---------|----------|-------------|
| Icon (✓/⚠) | plain | green for success, yellow for warning | `Theme::success()` / `Theme::warning()` |
| Label ("Issues", "MRs", etc.) | plain | bold | `Theme::bold()` |
| Numbers in summary text | plain | bold | `Theme::bold()` (just the count) |
| Elapsed time | plain | muted gray | `Theme::timing()` |
| Failure text in parens | plain | warning/error color | `Theme::warning()` |
### Sub-rows (project breakdown lines)
| Element | Current | Proposed |
|---------|---------|----------|
| Project path | dim | `Theme::muted()` (slightly brighter than dim) |
| Counts (numbers only) | dim | `Theme::dim()` but numbers in normal weight |
| Error/failure counts | dim | `Theme::warning()` |
| Middle dots | dim | keep dim (they're separators, should recede) |
### Summary (`print_sync`)
| Element | Current | Proposed |
|---------|---------|----------|
| Issue/MR counts in headline | bold only | `Theme::info()` + bold (cyan numbers pop) |
| Time in headline | plain | `Theme::timing()` |
| Detail line numbers | all dim | numbers in `Theme::info()`, rest stays dim |
| Doc line numbers | all dim | numbers in `Theme::info()`, rest stays dim |
| "Already up to date" time | plain | `Theme::timing()` |
---
## Files to Change
1. **`src/cli/progress.rs`** — `format_stage_line()`: apply color to icon, bold to label, `Theme::timing()` to elapsed
2. **`src/cli/commands/sync.rs`** —
- Pass colored icons to `format_stage_line` / `emit_stage_line` / `emit_stage_block`
- Color failure text in `append_failures()`
- Color numbers and time in `print_sync()`
- Color error/failure counts in sub-row functions (`issue_sub_rows`, `mr_sub_rows`, `status_sub_rows`)
## Approach
- `format_stage_line` already receives the icon string — color it before passing
- Add a `color_icon` helper that applies success/warning color to the icon glyph
- Bold the label in `format_stage_line`
- Apply `Theme::timing()` to elapsed in `format_stage_line`
- In `append_failures`, wrap failure text in `Theme::warning()`
- In `print_sync`, wrap count numbers with `Theme::info().bold()`
- In sub-row functions, apply `Theme::warning()` to error/failure parts only (keep rest dim)
## Non-goals
- No changes to robot mode (JSON output)
- No changes to dry-run output (already reasonably colored)
- No new Theme colors — use existing palette
- No changes to timing breakdown (already colored)

File diff suppressed because it is too large Load Diff

View File

@@ -750,6 +750,7 @@ pub struct GenerateDocsArgs {
#[command(after_help = "\x1b[1mExamples:\x1b[0m #[command(after_help = "\x1b[1mExamples:\x1b[0m
lore sync # Full pipeline: ingest + docs + embed lore sync # Full pipeline: ingest + docs + embed
lore sync --no-embed # Skip embedding step lore sync --no-embed # Skip embedding step
lore sync --no-status # Skip work-item status enrichment
lore sync --full --force # Full re-sync, override stale lock lore sync --full --force # Full re-sync, override stale lock
lore sync --dry-run # Preview what would change")] lore sync --dry-run # Preview what would change")]
pub struct SyncArgs { pub struct SyncArgs {
@@ -783,6 +784,10 @@ pub struct SyncArgs {
#[arg(long = "no-file-changes")] #[arg(long = "no-file-changes")]
pub no_file_changes: bool, pub no_file_changes: bool,
/// Skip work-item status enrichment via GraphQL (overrides config)
#[arg(long = "no-status")]
pub no_status: bool,
/// Preview what would be synced without making changes /// Preview what would be synced without making changes
#[arg(long, overrides_with = "no_dry_run")] #[arg(long, overrides_with = "no_dry_run")]
pub dry_run: bool, pub dry_run: bool,

View File

@@ -2046,6 +2046,9 @@ async fn handle_sync_cmd(
if args.no_file_changes { if args.no_file_changes {
config.sync.fetch_mr_file_changes = false; config.sync.fetch_mr_file_changes = false;
} }
if args.no_status {
config.sync.fetch_work_item_status = false;
}
let options = SyncOptions { let options = SyncOptions {
full: args.full && !args.no_full, full: args.full && !args.no_full,
force: args.force && !args.no_force, force: args.force && !args.no_force,
@@ -2337,7 +2340,7 @@ fn handle_robot_docs(robot_mode: bool, brief: bool) -> Result<(), Box<dyn std::e
}, },
"sync": { "sync": {
"description": "Full sync pipeline: ingest -> generate-docs -> embed", "description": "Full sync pipeline: ingest -> generate-docs -> embed",
"flags": ["--full", "--no-full", "--force", "--no-force", "--no-embed", "--no-docs", "--no-events", "--no-file-changes", "--dry-run", "--no-dry-run"], "flags": ["--full", "--no-full", "--force", "--no-force", "--no-embed", "--no-docs", "--no-events", "--no-file-changes", "--no-status", "--dry-run", "--no-dry-run"],
"example": "lore --robot sync", "example": "lore --robot sync",
"response_schema": { "response_schema": {
"ok": "bool", "ok": "bool",
@@ -2478,7 +2481,7 @@ fn handle_robot_docs(robot_mode: bool, brief: bool) -> Result<(), Box<dyn std::e
"description": "Chronological timeline of events matching a keyword query or entity reference", "description": "Chronological timeline of events matching a keyword query or entity reference",
"flags": ["<QUERY>", "-p/--project", "--since <duration>", "--depth <n>", "--no-mentions", "-n/--limit", "--fields <list>", "--max-seeds", "--max-entities", "--max-evidence"], "flags": ["<QUERY>", "-p/--project", "--since <duration>", "--depth <n>", "--no-mentions", "-n/--limit", "--fields <list>", "--max-seeds", "--max-entities", "--max-evidence"],
"query_syntax": { "query_syntax": {
"search": "Any text -> hybrid search seeding (FTS + vector)", "search": "Any text -> hybrid search seeding (FTS5 + vector)",
"entity_direct": "issue:N, i:N, mr:N, m:N -> direct entity seeding (no search, no Ollama)" "entity_direct": "issue:N, i:N, mr:N, m:N -> direct entity seeding (no search, no Ollama)"
}, },
"example": "lore --robot timeline issue:42", "example": "lore --robot timeline issue:42",
@@ -2642,11 +2645,14 @@ fn handle_robot_docs(robot_mode: bool, brief: bool) -> Result<(), Box<dyn std::e
"mr": "mrs", "mr": "mrs",
"merge-requests": "mrs", "merge-requests": "mrs",
"merge-request": "mrs", "merge-request": "mrs",
"note": "notes", "mergerequests": "mrs",
"find": "search", "mergerequest": "mrs",
"query": "search", "generate-docs": "generate-docs",
"stat": "stats", "generatedocs": "generate-docs",
"st": "status" "gendocs": "generate-docs",
"gen-docs": "generate-docs",
"robot-docs": "robot-docs",
"robotdocs": "robot-docs"
}, },
"pre_clap_aliases": { "pre_clap_aliases": {
"note": "Underscore/no-separator forms auto-corrected before parsing", "note": "Underscore/no-separator forms auto-corrected before parsing",
@@ -2658,7 +2664,7 @@ fn handle_robot_docs(robot_mode: bool, brief: bool) -> Result<(), Box<dyn std::e
"generatedocs": "generate-docs", "generatedocs": "generate-docs",
"gendocs": "generate-docs", "gendocs": "generate-docs",
"gen-docs": "generate-docs", "gen-docs": "generate-docs",
"robot_docs": "robot-docs", "robot-docs": "robot-docs",
"robotdocs": "robot-docs" "robotdocs": "robot-docs"
}, },
"prefix_matching": "Enabled via infer_subcommands. Unambiguous prefixes work: 'iss' -> issues, 'time' -> timeline, 'sea' -> search" "prefix_matching": "Enabled via infer_subcommands. Unambiguous prefixes work: 'iss' -> issues, 'time' -> timeline, 'sea' -> search"