Update name to gitlore instead of gitlab-inbox

This commit is contained in:
teernisse
2026-01-28 15:49:10 -05:00
parent 9a6357c353
commit 55b895a2eb
31 changed files with 1046 additions and 373 deletions

2
.gitignore vendored
View File

@@ -30,7 +30,7 @@ yarn-debug.log*
yarn-error.log* yarn-error.log*
# Local config files # Local config files
gi.config.json lore.config.json
# beads # beads
.bv/ .bv/

View File

@@ -25,46 +25,46 @@ If you aren't 100% sure how to use a third-party library, **SEARCH ONLINE** to f
--- ---
## GitLab Inbox Robot Mode ## Gitlore Robot Mode
The `gi` CLI has a robot mode optimized for AI agent consumption with structured JSON output, meaningful exit codes, and TTY auto-detection. The `lore` CLI has a robot mode optimized for AI agent consumption with structured JSON output, meaningful exit codes, and TTY auto-detection.
### Activation ### Activation
```bash ```bash
# Explicit flag # Explicit flag
gi --robot list issues lore --robot list issues
# Auto-detection (when stdout is not a TTY) # Auto-detection (when stdout is not a TTY)
gi list issues | jq . lore list issues | jq .
# Environment variable # Environment variable
GI_ROBOT=1 gi list issues LORE_ROBOT=true lore list issues
``` ```
### Robot Mode Commands ### Robot Mode Commands
```bash ```bash
# List issues/MRs with JSON output # List issues/MRs with JSON output
gi --robot list issues --limit=10 lore --robot list issues --limit=10
gi --robot list mrs --state=opened lore --robot list mrs --state=opened
# Count entities # Count entities
gi --robot count issues lore --robot count issues
gi --robot count discussions --type=mr lore --robot count discussions --type=mr
# Show detailed entity info # Show detailed entity info
gi --robot show issue 123 lore --robot show issue 123
gi --robot show mr 456 --project=group/repo lore --robot show mr 456 --project=group/repo
# Check sync status # Check sync status
gi --robot sync-status lore --robot sync-status
# Run ingestion (quiet, JSON summary) # Run ingestion (quiet, JSON summary)
gi --robot ingest --type=issues lore --robot ingest --type=issues
# Check environment health # Check environment health
gi --robot doctor lore --robot doctor
``` ```
### Response Format ### Response Format
@@ -78,7 +78,7 @@ All commands return consistent JSON:
Errors return structured JSON to stderr: Errors return structured JSON to stderr:
```json ```json
{"error":{"code":"CONFIG_NOT_FOUND","message":"...","suggestion":"Run 'gi init'"}} {"error":{"code":"CONFIG_NOT_FOUND","message":"...","suggestion":"Run 'lore init'"}}
``` ```
### Exit Codes ### Exit Codes
@@ -102,7 +102,7 @@ Errors return structured JSON to stderr:
### Best Practices ### Best Practices
- Use `gi --robot` for all agent interactions - Use `lore --robot` for all agent interactions
- Check exit codes for error handling - Check exit codes for error handling
- Parse JSON errors from stderr - Parse JSON errors from stderr
- Use `--limit` to control response size - Use `--limit` to control response size

66
Cargo.lock generated
View File

@@ -630,39 +630,6 @@ dependencies = [
"wasip2", "wasip2",
] ]
[[package]]
name = "gi"
version = "0.1.0"
dependencies = [
"async-stream",
"chrono",
"clap",
"comfy-table",
"console",
"dialoguer",
"dirs",
"flate2",
"futures",
"indicatif",
"open",
"reqwest",
"rusqlite",
"serde",
"serde_json",
"sha2",
"sqlite-vec",
"tempfile",
"thiserror",
"tokio",
"tracing",
"tracing-indicatif",
"tracing-subscriber",
"url",
"urlencoding",
"uuid",
"wiremock",
]
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.4.13" version = "0.4.13"
@@ -1111,6 +1078,39 @@ version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "lore"
version = "0.1.0"
dependencies = [
"async-stream",
"chrono",
"clap",
"comfy-table",
"console",
"dialoguer",
"dirs",
"flate2",
"futures",
"indicatif",
"open",
"reqwest",
"rusqlite",
"serde",
"serde_json",
"sha2",
"sqlite-vec",
"tempfile",
"thiserror",
"tokio",
"tracing",
"tracing-indicatif",
"tracing-subscriber",
"url",
"urlencoding",
"uuid",
"wiremock",
]
[[package]] [[package]]
name = "matchers" name = "matchers"
version = "0.2.0" version = "0.2.0"

View File

@@ -1,13 +1,13 @@
[package] [package]
name = "gi" name = "lore"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
description = "GitLab Knowledge Engine - semantic search for GitLab issues, MRs, and discussions" description = "Gitlore - Local GitLab data management with semantic search"
authors = ["Taylor Eernisse"] authors = ["Taylor Eernisse"]
license = "MIT" license = "MIT"
[[bin]] [[bin]]
name = "gi" name = "lore"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]

6
PRD.md
View File

@@ -1,8 +1,10 @@
# GitLab Inbox - Product Requirements Document # Gitlore - Product Requirements Document
> **Note:** The project was renamed from "gitlab-inbox" to "gitlore" and the CLI from "gi" to "lore".
## Overview ## Overview
**Product Name**: GitLab Inbox **Product Name**: Gitlore (formerly GitLab Inbox)
**Version**: 1.0 **Version**: 1.0
**Author**: Taylor Eernisse **Author**: Taylor Eernisse
**Date**: January 16, 2026 **Date**: January 16, 2026

208
README.md
View File

@@ -1,6 +1,6 @@
# gi - GitLab Inbox # Gitlore
A command-line tool for managing GitLab issues and merge requests locally. Syncs issues, MRs, discussions, and notes from GitLab to a local SQLite database for fast, offline-capable querying and filtering. Local GitLab data management with semantic search. Syncs issues, MRs, discussions, and notes from GitLab to a local SQLite database for fast, offline-capable querying and filtering.
## Features ## Features
@@ -22,40 +22,40 @@ Or build from source:
```bash ```bash
cargo build --release cargo build --release
./target/release/gi --help ./target/release/lore --help
``` ```
## Quick Start ## Quick Start
```bash ```bash
# Initialize configuration (interactive) # Initialize configuration (interactive)
gi init lore init
# Verify authentication # Verify authentication
gi auth-test lore auth-test
# Sync issues from GitLab # Sync issues from GitLab
gi ingest --type issues lore ingest --type issues
# Sync merge requests from GitLab # Sync merge requests from GitLab
gi ingest --type mrs lore ingest --type mrs
# List recent issues # List recent issues
gi list issues --limit 10 lore list issues --limit 10
# List open merge requests # List open merge requests
gi list mrs --state opened lore list mrs --state opened
# Show issue details # Show issue details
gi show issue 123 --project group/repo lore show issue 123 --project group/repo
# Show MR details with discussions # Show MR details with discussions
gi show mr 456 --project group/repo lore show mr 456 --project group/repo
``` ```
## Configuration ## Configuration
Configuration is stored in `~/.config/gi/config.json` (or `$XDG_CONFIG_HOME/gi/config.json`). Configuration is stored in `~/.config/lore/config.json` (or `$XDG_CONFIG_HOME/lore/config.json`).
### Example Configuration ### Example Configuration
@@ -96,8 +96,8 @@ Configuration is stored in `~/.config/gi/config.json` (or `$XDG_CONFIG_HOME/gi/c
| `sync` | `cursorRewindSeconds` | `2` | Seconds to rewind cursor for overlap safety | | `sync` | `cursorRewindSeconds` | `2` | Seconds to rewind cursor for overlap safety |
| `sync` | `primaryConcurrency` | `4` | Concurrent GitLab requests for primary resources | | `sync` | `primaryConcurrency` | `4` | Concurrent GitLab requests for primary resources |
| `sync` | `dependentConcurrency` | `2` | Concurrent requests for dependent resources | | `sync` | `dependentConcurrency` | `2` | Concurrent requests for dependent resources |
| `storage` | `dbPath` | `~/.local/share/gi/gi.db` | Database file path | | `storage` | `dbPath` | `~/.local/share/lore/lore.db` | Database file path |
| `storage` | `backupDir` | `~/.local/share/gi/backups` | Backup directory | | `storage` | `backupDir` | `~/.local/share/lore/backups` | Backup directory |
| `storage` | `compressRawPayloads` | `true` | Compress stored API responses with gzip | | `storage` | `compressRawPayloads` | `true` | Compress stored API responses with gzip |
| `embedding` | `provider` | `ollama` | Embedding provider | | `embedding` | `provider` | `ollama` | Embedding provider |
| `embedding` | `model` | `nomic-embed-text` | Model name for embeddings | | `embedding` | `model` | `nomic-embed-text` | Model name for embeddings |
@@ -108,9 +108,9 @@ Configuration is stored in `~/.config/gi/config.json` (or `$XDG_CONFIG_HOME/gi/c
The config file is resolved in this order: The config file is resolved in this order:
1. `--config` CLI flag 1. `--config` CLI flag
2. `GI_CONFIG_PATH` environment variable 2. `LORE_CONFIG_PATH` environment variable
3. `~/.config/gi/config.json` (XDG default) 3. `~/.config/lore/config.json` (XDG default)
4. `./gi.config.json` (local fallback for development) 4. `./lore.config.json` (local fallback for development)
### GitLab Token ### GitLab Token
@@ -125,40 +125,40 @@ Create a personal access token with `read_api` scope:
| Variable | Purpose | Required | | Variable | Purpose | Required |
|----------|---------|----------| |----------|---------|----------|
| `GITLAB_TOKEN` | GitLab API authentication token (name configurable via `gitlab.tokenEnvVar`) | Yes | | `GITLAB_TOKEN` | GitLab API authentication token (name configurable via `gitlab.tokenEnvVar`) | Yes |
| `GI_CONFIG_PATH` | Override config file location | No | | `LORE_CONFIG_PATH` | Override config file location | No |
| `XDG_CONFIG_HOME` | XDG Base Directory for config (fallback: `~/.config`) | No | | `XDG_CONFIG_HOME` | XDG Base Directory for config (fallback: `~/.config`) | No |
| `XDG_DATA_HOME` | XDG Base Directory for data (fallback: `~/.local/share`) | No | | `XDG_DATA_HOME` | XDG Base Directory for data (fallback: `~/.local/share`) | No |
| `RUST_LOG` | Logging level filter (e.g., `gi=debug`) | No | | `RUST_LOG` | Logging level filter (e.g., `lore=debug`) | No |
## Commands ## Commands
### `gi init` ### `lore init`
Initialize configuration and database interactively. Initialize configuration and database interactively.
```bash ```bash
gi init # Interactive setup lore init # Interactive setup
gi init --force # Overwrite existing config lore init --force # Overwrite existing config
gi init --non-interactive # Fail if prompts needed lore init --non-interactive # Fail if prompts needed
``` ```
### `gi auth-test` ### `lore auth-test`
Verify GitLab authentication is working. Verify GitLab authentication is working.
```bash ```bash
gi auth-test lore auth-test
# Authenticated as @username (Full Name) # Authenticated as @username (Full Name)
# GitLab: https://gitlab.com # GitLab: https://gitlab.com
``` ```
### `gi doctor` ### `lore doctor`
Check environment health and configuration. Check environment health and configuration.
```bash ```bash
gi doctor # Human-readable output lore doctor # Human-readable output
gi doctor --json # JSON output for scripting lore doctor --json # JSON output for scripting
``` ```
Checks performed: Checks performed:
@@ -168,21 +168,21 @@ Checks performed:
- Project accessibility - Project accessibility
- Ollama connectivity (optional) - Ollama connectivity (optional)
### `gi ingest` ### `lore ingest`
Sync data from GitLab to local database. Sync data from GitLab to local database.
```bash ```bash
# Issues # Issues
gi ingest --type issues # Sync all projects lore ingest --type issues # Sync all projects
gi ingest --type issues --project group/repo # Single project lore ingest --type issues --project group/repo # Single project
gi ingest --type issues --force # Override stale lock lore ingest --type issues --force # Override stale lock
gi ingest --type issues --full # Full re-sync (reset cursors) lore ingest --type issues --full # Full re-sync (reset cursors)
# Merge Requests # Merge Requests
gi ingest --type mrs # Sync all projects lore ingest --type mrs # Sync all projects
gi ingest --type mrs --project group/repo # Single project lore ingest --type mrs --project group/repo # Single project
gi ingest --type mrs --full # Full re-sync (reset cursors) lore ingest --type mrs --full # Full re-sync (reset cursors)
``` ```
The `--full` flag resets sync cursors and discussion watermarks, then fetches all data from scratch. Useful when: The `--full` flag resets sync cursors and discussion watermarks, then fetches all data from scratch. Useful when:
@@ -190,103 +190,103 @@ The `--full` flag resets sync cursors and discussion watermarks, then fetches al
- You want to ensure complete data after schema changes - You want to ensure complete data after schema changes
- Troubleshooting sync issues - Troubleshooting sync issues
### `gi list issues` ### `lore list issues`
Query issues from local database. Query issues from local database.
```bash ```bash
gi list issues # Recent issues (default 50) lore list issues # Recent issues (default 50)
gi list issues --limit 100 # More results lore list issues --limit 100 # More results
gi list issues --state opened # Only open issues lore list issues --state opened # Only open issues
gi list issues --state closed # Only closed issues lore list issues --state closed # Only closed issues
gi list issues --author username # By author (@ prefix optional) lore list issues --author username # By author (@ prefix optional)
gi list issues --assignee username # By assignee (@ prefix optional) lore list issues --assignee username # By assignee (@ prefix optional)
gi list issues --label bug # By label (AND logic) lore list issues --label bug # By label (AND logic)
gi list issues --label bug --label urgent # Multiple labels lore list issues --label bug --label urgent # Multiple labels
gi list issues --milestone "v1.0" # By milestone title lore list issues --milestone "v1.0" # By milestone title
gi list issues --since 7d # Updated in last 7 days lore list issues --since 7d # Updated in last 7 days
gi list issues --since 2w # Updated in last 2 weeks lore list issues --since 2w # Updated in last 2 weeks
gi list issues --since 2024-01-01 # Updated since date lore list issues --since 2024-01-01 # Updated since date
gi list issues --due-before 2024-12-31 # Due before date lore list issues --due-before 2024-12-31 # Due before date
gi list issues --has-due-date # Only issues with due dates lore list issues --has-due-date # Only issues with due dates
gi list issues --project group/repo # Filter by project lore list issues --project group/repo # Filter by project
gi list issues --sort created --order asc # Sort options lore list issues --sort created --order asc # Sort options
gi list issues --open # Open first result in browser lore list issues --open # Open first result in browser
gi list issues --json # JSON output lore list issues --json # JSON output
``` ```
Output includes: IID, title, state, author, assignee, labels, and update time. Output includes: IID, title, state, author, assignee, labels, and update time.
### `gi list mrs` ### `lore list mrs`
Query merge requests from local database. Query merge requests from local database.
```bash ```bash
gi list mrs # Recent MRs (default 50) lore list mrs # Recent MRs (default 50)
gi list mrs --limit 100 # More results lore list mrs --limit 100 # More results
gi list mrs --state opened # Only open MRs lore list mrs --state opened # Only open MRs
gi list mrs --state merged # Only merged MRs lore list mrs --state merged # Only merged MRs
gi list mrs --state closed # Only closed MRs lore list mrs --state closed # Only closed MRs
gi list mrs --state locked # Only locked MRs lore list mrs --state locked # Only locked MRs
gi list mrs --state all # All states lore list mrs --state all # All states
gi list mrs --author username # By author (@ prefix optional) lore list mrs --author username # By author (@ prefix optional)
gi list mrs --assignee username # By assignee (@ prefix optional) lore list mrs --assignee username # By assignee (@ prefix optional)
gi list mrs --reviewer username # By reviewer (@ prefix optional) lore list mrs --reviewer username # By reviewer (@ prefix optional)
gi list mrs --draft # Only draft/WIP MRs lore list mrs --draft # Only draft/WIP MRs
gi list mrs --no-draft # Exclude draft MRs lore list mrs --no-draft # Exclude draft MRs
gi list mrs --target-branch main # By target branch lore list mrs --target-branch main # By target branch
gi list mrs --source-branch feature/foo # By source branch lore list mrs --source-branch feature/foo # By source branch
gi list mrs --label needs-review # By label (AND logic) lore list mrs --label needs-review # By label (AND logic)
gi list mrs --since 7d # Updated in last 7 days lore list mrs --since 7d # Updated in last 7 days
gi list mrs --project group/repo # Filter by project lore list mrs --project group/repo # Filter by project
gi list mrs --sort created --order asc # Sort options lore list mrs --sort created --order asc # Sort options
gi list mrs --open # Open first result in browser lore list mrs --open # Open first result in browser
gi list mrs --json # JSON output lore list mrs --json # JSON output
``` ```
Output includes: IID, title (with [DRAFT] prefix if applicable), state, author, assignee, labels, and update time. Output includes: IID, title (with [DRAFT] prefix if applicable), state, author, assignee, labels, and update time.
### `gi show issue` ### `lore show issue`
Display detailed issue information. Display detailed issue information.
```bash ```bash
gi show issue 123 # Show issue #123 lore show issue 123 # Show issue #123
gi show issue 123 --project group/repo # Disambiguate if needed lore show issue 123 --project group/repo # Disambiguate if needed
``` ```
Shows: title, description, state, author, assignees, labels, milestone, due date, web URL, and threaded discussions. Shows: title, description, state, author, assignees, labels, milestone, due date, web URL, and threaded discussions.
### `gi show mr` ### `lore show mr`
Display detailed merge request information. Display detailed merge request information.
```bash ```bash
gi show mr 456 # Show MR !456 lore show mr 456 # Show MR !456
gi show mr 456 --project group/repo # Disambiguate if needed lore show mr 456 --project group/repo # Disambiguate if needed
``` ```
Shows: title, description, state, draft status, author, assignees, reviewers, labels, source/target branches, merge status, web URL, and threaded discussions. Inline code review comments (DiffNotes) display file context in the format `[src/file.ts:45]`. Shows: title, description, state, draft status, author, assignees, reviewers, labels, source/target branches, merge status, web URL, and threaded discussions. Inline code review comments (DiffNotes) display file context in the format `[src/file.ts:45]`.
### `gi count` ### `lore count`
Count entities in local database. Count entities in local database.
```bash ```bash
gi count issues # Total issues lore count issues # Total issues
gi count mrs # Total MRs (with state breakdown) lore count mrs # Total MRs (with state breakdown)
gi count discussions # Total discussions lore count discussions # Total discussions
gi count discussions --type issue # Issue discussions only lore count discussions --type issue # Issue discussions only
gi count discussions --type mr # MR discussions only lore count discussions --type mr # MR discussions only
gi count notes # Total notes (shows system vs user breakdown) lore count notes # Total notes (shows system vs user breakdown)
``` ```
### `gi sync-status` ### `lore sync-status`
Show current sync state and watermarks. Show current sync state and watermarks.
```bash ```bash
gi sync-status lore sync-status
``` ```
Displays: Displays:
@@ -294,40 +294,40 @@ Displays:
- Cursor positions per project and resource type (issues and MRs) - Cursor positions per project and resource type (issues and MRs)
- Data summary counts - Data summary counts
### `gi migrate` ### `lore migrate`
Run pending database migrations. Run pending database migrations.
```bash ```bash
gi migrate lore migrate
``` ```
Shows current schema version and applies any pending migrations. Shows current schema version and applies any pending migrations.
### `gi version` ### `lore version`
Show version information. Show version information.
```bash ```bash
gi version lore version
``` ```
### `gi backup` ### `lore backup`
Create timestamped database backup. Create timestamped database backup.
```bash ```bash
gi backup lore backup
``` ```
*Note: Not yet implemented.* *Note: Not yet implemented.*
### `gi reset` ### `lore reset`
Delete database and reset all state. Delete database and reset all state.
```bash ```bash
gi reset --confirm lore reset --confirm
``` ```
*Note: Not yet implemented.* *Note: Not yet implemented.*
@@ -356,12 +356,12 @@ Data is stored in SQLite with WAL mode and foreign keys enabled. Main tables:
| `raw_payloads` | Compressed original API responses | | `raw_payloads` | Compressed original API responses |
| `schema_version` | Migration version tracking | | `schema_version` | Migration version tracking |
The database is stored at `~/.local/share/gi/gi.db` by default (XDG compliant). The database is stored at `~/.local/share/lore/lore.db` by default (XDG compliant).
## Global Options ## Global Options
```bash ```bash
gi --config /path/to/config.json <command> # Use alternate config lore --config /path/to/config.json <command> # Use alternate config
``` ```
## Development ## Development
@@ -371,10 +371,10 @@ gi --config /path/to/config.json <command> # Use alternate config
cargo test cargo test
# Run with debug logging # Run with debug logging
RUST_LOG=gi=debug gi list issues RUST_LOG=lore=debug lore list issues
# Run with trace logging # Run with trace logging
RUST_LOG=gi=trace gi ingest --type issues RUST_LOG=lore=trace lore ingest --type issues
# Check formatting # Check formatting
cargo fmt --check cargo fmt --check
@@ -396,7 +396,7 @@ cargo clippy
## Current Status ## Current Status
This is Checkpoint 2 (CP2) of the GitLab Knowledge Engine project. Currently implemented: This is Checkpoint 2 (CP2) of the Gitlore project. Currently implemented:
- Issue ingestion with cursor-based incremental sync - Issue ingestion with cursor-based incremental sync
- Merge request ingestion with cursor-based incremental sync - Merge request ingestion with cursor-based incremental sync

View File

@@ -1,5 +1,7 @@
# SPEC.md Revision Document - Round 2 # SPEC.md Revision Document - Round 2
> **Note:** The project was renamed from "gitlab-inbox" to "gitlore" and the CLI from "gi" to "lore". References to "gi" in this document should be read as "lore".
This document provides git-diff style changes for the second round of improvements from ChatGPT's review. These are primarily correctness fixes and optimizations. This document provides git-diff style changes for the second round of improvements from ChatGPT's review. These are primarily correctness fixes and optimizations.
--- ---

View File

@@ -1,5 +1,7 @@
# SPEC.md Revisions - First-Time User Experience # SPEC.md Revisions - First-Time User Experience
> **Note:** The project was renamed from "gitlab-inbox" to "gitlore" and the CLI from "gi" to "lore". References to "gi" in this document should be read as "lore".
**Date:** 2026-01-21 **Date:** 2026-01-21
**Purpose:** Document all changes adding installation, setup, and user flow documentation to SPEC.md **Purpose:** Document all changes adding installation, setup, and user flow documentation to SPEC.md

View File

@@ -1,5 +1,7 @@
# SPEC.md Revision Document # SPEC.md Revision Document
> **Note:** The project was renamed from "gitlab-inbox" to "gitlore" and the CLI from "gi" to "lore". References to "gi" in this document should be read as "lore".
This document provides git-diff style changes to integrate improvements from ChatGPT's review into the original SPEC.md. The goal is a "best of all worlds" hybrid that maintains the original architecture while adding production-grade hardening. This document provides git-diff style changes to integrate improvements from ChatGPT's review into the original SPEC.md. The goal is a "best of all worlds" hybrid that maintains the original architecture while adding production-grade hardening.
--- ---

View File

@@ -1,6 +1,6 @@
# GitLab Knowledge Engine - Spec Document # Gitlore - Spec Document
> **Note:** This is a historical planning document. The actual implementation uses Rust instead of TypeScript/Node.js. See [README.md](README.md) for current documentation. > **Note:** This is a historical planning document. The actual implementation uses Rust instead of TypeScript/Node.js. See [README.md](README.md) for current documentation. The project was renamed from "gitlab-inbox" to "gitlore" and the CLI from "gi" to "lore".
## Executive Summary ## Executive Summary

View File

@@ -1,5 +1,7 @@
# Checkpoint 0: Project Setup - PRD # Checkpoint 0: Project Setup - PRD
> **Note:** The project was renamed from "gitlab-inbox" to "gitlore" and the CLI from "gi" to "lore". References to "gi" in this document should be read as "lore".
**Version:** 1.0 **Version:** 1.0
**Status:** Ready for Implementation **Status:** Ready for Implementation
**Depends On:** None (first checkpoint) **Depends On:** None (first checkpoint)

View File

@@ -1,5 +1,7 @@
# Checkpoint 1: Issue Ingestion - PRD # Checkpoint 1: Issue Ingestion - PRD
> **Note:** The project was renamed from "gitlab-inbox" to "gitlore" and the CLI from "gi" to "lore". References to "gi" in this document should be read as "lore".
**Version:** 2.0 **Version:** 2.0
**Status:** Ready for Implementation **Status:** Ready for Implementation
**Depends On:** Checkpoint 0 (Project Setup) **Depends On:** Checkpoint 0 (Project Setup)

View File

@@ -1,5 +1,7 @@
# Checkpoint 2: MR Ingestion - PRD # Checkpoint 2: MR Ingestion - PRD
> **Note:** The project was renamed from "gitlab-inbox" to "gitlore" and the CLI from "gi" to "lore". References to "gi" in this document should be read as "lore".
**Version:** 1.3 **Version:** 1.3
**Status:** Ready for Implementation **Status:** Ready for Implementation
**Depends On:** Checkpoint 1 (Issue Ingestion) **Depends On:** Checkpoint 1 (Issue Ingestion)

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,7 @@
# CP1 ↔ CP2 Alignment Audit # CP1 ↔ CP2 Alignment Audit
> **Note:** The project was renamed from "gitlab-inbox" to "gitlore" and the CLI from "gi" to "lore". References to "gi" in this document should be read as "lore".
**Created:** 2026-01-26 **Created:** 2026-01-26
**Purpose:** Document deviations between CP1 (Issue Ingestion) and CP2 (MR Ingestion) PRDs that could cause implementation drift. Use this checklist to verify alignment before CP2 implementation. **Purpose:** Document deviations between CP1 (Issue Ingestion) and CP2 (MR Ingestion) PRDs that could cause implementation drift. Use this checklist to verify alignment before CP2 implementation.

View File

@@ -2,19 +2,19 @@
## Overview ## Overview
Robot mode optimizes the `gi` CLI for AI agent consumption with structured JSON output, meaningful exit codes, and token-efficient responses. Robot mode optimizes the `lore` CLI for AI agent consumption with structured JSON output, meaningful exit codes, and token-efficient responses.
## Activation ## Activation
```bash ```bash
# Explicit flag # Explicit flag
gi --robot list issues lore --robot list issues
# Auto-detection (when stdout is not a TTY) # Auto-detection (when stdout is not a TTY)
gi list issues | jq . lore list issues | jq .
# Environment variable # Environment variable
GI_ROBOT=1 gi list issues LORE_ROBOT=true lore list issues
``` ```
## Global Flags ## Global Flags
@@ -51,8 +51,8 @@ When `--robot` is active, errors are JSON on stderr:
{ {
"error": { "error": {
"code": "CONFIG_NOT_FOUND", "code": "CONFIG_NOT_FOUND",
"message": "Config file not found at ~/.config/gi/config.toml", "message": "Config file not found at ~/.config/lore/config.toml",
"suggestion": "Run 'gi init' to create configuration" "suggestion": "Run 'lore init' to create configuration"
} }
} }
``` ```
@@ -75,7 +75,7 @@ All commands return consistent JSON structure:
## Command-Specific Output ## Command-Specific Output
### gi list issues --robot ### lore list issues --robot
```json ```json
{ {
@@ -100,7 +100,7 @@ All commands return consistent JSON structure:
} }
``` ```
### gi show issue 123 --robot ### lore show issue 123 --robot
```json ```json
{ {
@@ -134,7 +134,7 @@ All commands return consistent JSON structure:
} }
``` ```
### gi ingest --type issues --robot ### lore ingest --type issues --robot
```json ```json
{ {
@@ -157,7 +157,7 @@ All commands return consistent JSON structure:
} }
``` ```
### gi count issues --robot ### lore count issues --robot
```json ```json
{ {
@@ -173,7 +173,7 @@ All commands return consistent JSON structure:
} }
``` ```
### gi doctor --robot ### lore doctor --robot
```json ```json
{ {
@@ -181,7 +181,7 @@ All commands return consistent JSON structure:
"data": { "data": {
"success": true, "success": true,
"checks": { "checks": {
"config": { "status": "ok", "path": "~/.config/gi/config.toml" }, "config": { "status": "ok", "path": "~/.config/lore/config.toml" },
"database": { "status": "ok", "version": 6 }, "database": { "status": "ok", "version": 6 },
"gitlab": { "status": "ok", "user": "username" }, "gitlab": { "status": "ok", "user": "username" },
"projects": [ "projects": [
@@ -192,7 +192,7 @@ All commands return consistent JSON structure:
} }
``` ```
### gi sync-status --robot ### lore sync-status --robot
```json ```json
{ {

View File

@@ -178,7 +178,7 @@ fn check_database(config: Option<&Config>) -> DatabaseCheck {
return DatabaseCheck { return DatabaseCheck {
result: CheckResult { result: CheckResult {
status: CheckStatus::Error, status: CheckStatus::Error,
message: Some("Database file not found. Run \"gi init\" first.".to_string()), message: Some("Database file not found. Run \"lore init\" first.".to_string()),
}, },
path: Some(db_path.display().to_string()), path: Some(db_path.display().to_string()),
schema_version: None, schema_version: None,
@@ -302,7 +302,7 @@ fn check_projects(config: Option<&Config>) -> ProjectsCheck {
return ProjectsCheck { return ProjectsCheck {
result: CheckResult { result: CheckResult {
status: CheckStatus::Error, status: CheckStatus::Error,
message: Some("Database not found. Run \"gi init\" first.".to_string()), message: Some("Database not found. Run \"lore init\" first.".to_string()),
}, },
configured: Some(configured), configured: Some(configured),
resolved: Some(0), resolved: Some(0),
@@ -320,7 +320,7 @@ fn check_projects(config: Option<&Config>) -> ProjectsCheck {
result: CheckResult { result: CheckResult {
status: CheckStatus::Error, status: CheckStatus::Error,
message: Some(format!( message: Some(format!(
"{configured} configured, 0 resolved. Run \"gi init\" to resolve projects." "{configured} configured, 0 resolved. Run \"lore init\" to resolve projects."
)), )),
}, },
configured: Some(configured), configured: Some(configured),
@@ -459,7 +459,7 @@ async fn check_ollama(config: Option<&Config>) -> OllamaCheck {
/// Format and print doctor results to console. /// Format and print doctor results to console.
pub fn print_doctor_results(result: &DoctorResult) { pub fn print_doctor_results(result: &DoctorResult) {
println!("\ngi doctor\n"); println!("\nlore doctor\n");
print_check("Config", &result.checks.config.result); print_check("Config", &result.checks.config.result);
print_check("Database", &result.checks.database.result); print_check("Database", &result.checks.database.result);

View File

@@ -124,7 +124,7 @@ pub async fn run_ingest(
))); )));
} }
return Err(GiError::Other( return Err(GiError::Other(
"No projects configured. Run 'gi init' first.".to_string(), "No projects configured. Run 'lore init' first.".to_string(),
)); ));
} }

View File

@@ -143,7 +143,7 @@ pub async fn run_init(inputs: InitInputs, options: InitOptions) -> Result<InitRe
// 7. Create data directory and initialize database // 7. Create data directory and initialize database
fs::create_dir_all(&data_dir)?; fs::create_dir_all(&data_dir)?;
let db_path = data_dir.join("gi.db"); let db_path = data_dir.join("lore.db");
let conn = create_connection(&db_path)?; let conn = create_connection(&db_path)?;
// Run embedded migrations // Run embedded migrations

View File

@@ -298,7 +298,7 @@ pub fn print_sync_status(result: &SyncStatusResult) {
println!(" {}", style("No sync runs recorded yet.").dim()); println!(" {}", style("No sync runs recorded yet.").dim());
println!( println!(
" {}", " {}",
style("Run 'gi ingest --type=issues' to start.").dim() style("Run 'lore ingest --type=issues' to start.").dim()
); );
} }
} }

View File

@@ -5,9 +5,9 @@ pub mod commands;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use std::io::IsTerminal; use std::io::IsTerminal;
/// GitLab Inbox - Unified notification management /// Gitlore - Local GitLab data management with semantic search
#[derive(Parser)] #[derive(Parser)]
#[command(name = "gi")] #[command(name = "lore")]
#[command(version, about, long_about = None)] #[command(version, about, long_about = None)]
pub struct Cli { pub struct Cli {
/// Path to config file /// Path to config file
@@ -15,7 +15,7 @@ pub struct Cli {
pub config: Option<String>, pub config: Option<String>,
/// Machine-readable JSON output (auto-enabled when piped) /// Machine-readable JSON output (auto-enabled when piped)
#[arg(long, global = true, env = "GI_ROBOT")] #[arg(long, global = true, env = "LORE_ROBOT")]
pub robot: bool, pub robot: bool,
#[command(subcommand)] #[command(subcommand)]

View File

@@ -159,7 +159,7 @@ pub fn run_migrations_from_dir(conn: &Connection, migrations_dir: &Path) -> Resu
} }
/// Verify database pragmas are set correctly. /// Verify database pragmas are set correctly.
/// Used by gi doctor command. /// Used by lore doctor command.
pub fn verify_pragmas(conn: &Connection) -> (bool, Vec<String>) { pub fn verify_pragmas(conn: &Connection) -> (bool, Vec<String>) {
let mut issues = Vec::new(); let mut issues = Vec::new();

View File

@@ -1,4 +1,4 @@
//! Custom error types for gitlab-inbox. //! Custom error types for gitlore.
//! //!
//! Uses thiserror for ergonomic error definitions with structured error codes. //! Uses thiserror for ergonomic error definitions with structured error codes.
@@ -65,10 +65,10 @@ impl ErrorCode {
} }
} }
/// Main error type for gitlab-inbox. /// Main error type for gitlore.
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum GiError { pub enum GiError {
#[error("Config file not found at {path}. Run \"gi init\" first.")] #[error("Config file not found at {path}. Run \"lore init\" first.")]
ConfigNotFound { path: String }, ConfigNotFound { path: String },
#[error("Invalid config: {details}")] #[error("Invalid config: {details}")]
@@ -158,18 +158,18 @@ impl GiError {
/// Get a suggestion for how to fix this error. /// Get a suggestion for how to fix this error.
pub fn suggestion(&self) -> Option<&'static str> { pub fn suggestion(&self) -> Option<&'static str> {
match self { match self {
Self::ConfigNotFound { .. } => Some("Run 'gi init' to create configuration"), Self::ConfigNotFound { .. } => Some("Run 'lore init' to create configuration"),
Self::ConfigInvalid { .. } => Some("Check config file syntax or run 'gi init' to recreate"), Self::ConfigInvalid { .. } => Some("Check config file syntax or run 'lore init' to recreate"),
Self::GitLabAuthFailed => Some("Verify token has read_api scope and is not expired"), Self::GitLabAuthFailed => Some("Verify token has read_api scope and is not expired"),
Self::GitLabNotFound { .. } => Some("Check the resource path exists and you have access"), Self::GitLabNotFound { .. } => Some("Check the resource path exists and you have access"),
Self::GitLabRateLimited { .. } => Some("Wait and retry, or reduce request frequency"), Self::GitLabRateLimited { .. } => Some("Wait and retry, or reduce request frequency"),
Self::GitLabNetworkError { .. } => Some("Check network connection and GitLab URL"), Self::GitLabNetworkError { .. } => Some("Check network connection and GitLab URL"),
Self::DatabaseLocked { .. } => Some("Wait for other sync to complete or use --force"), Self::DatabaseLocked { .. } => Some("Wait for other sync to complete or use --force"),
Self::MigrationFailed { .. } => Some("Check database file permissions or reset with 'gi reset'"), Self::MigrationFailed { .. } => Some("Check database file permissions or reset with 'lore reset'"),
Self::TokenNotSet { .. } => Some("Export the token environment variable"), Self::TokenNotSet { .. } => Some("Export the token environment variable"),
Self::Database(_) => Some("Check database file permissions or reset with 'gi reset'"), Self::Database(_) => Some("Check database file permissions or reset with 'lore reset'"),
Self::Http(_) => Some("Check network connection"), Self::Http(_) => Some("Check network connection"),
Self::NotFound(_) => Some("Verify the entity exists using 'gi list'"), Self::NotFound(_) => Some("Verify the entity exists using 'lore list'"),
Self::Ambiguous(_) => Some("Use --project flag to disambiguate"), Self::Ambiguous(_) => Some("Use --project flag to disambiguate"),
_ => None, _ => None,
} }

View File

@@ -6,9 +6,9 @@ use std::path::PathBuf;
/// ///
/// Resolution order: /// Resolution order:
/// 1. CLI flag override (if provided) /// 1. CLI flag override (if provided)
/// 2. GI_CONFIG_PATH environment variable /// 2. LORE_CONFIG_PATH environment variable
/// 3. XDG default (~/.config/gi/config.json) /// 3. XDG default (~/.config/lore/config.json)
/// 4. Local fallback (./gi.config.json) if exists /// 4. Local fallback (./lore.config.json) if exists
/// 5. Returns XDG default even if not exists /// 5. Returns XDG default even if not exists
pub fn get_config_path(cli_override: Option<&str>) -> PathBuf { pub fn get_config_path(cli_override: Option<&str>) -> PathBuf {
// 1. CLI flag override // 1. CLI flag override
@@ -17,18 +17,18 @@ pub fn get_config_path(cli_override: Option<&str>) -> PathBuf {
} }
// 2. Environment variable // 2. Environment variable
if let Ok(path) = std::env::var("GI_CONFIG_PATH") { if let Ok(path) = std::env::var("LORE_CONFIG_PATH") {
return PathBuf::from(path); return PathBuf::from(path);
} }
// 3. XDG default // 3. XDG default
let xdg_path = get_xdg_config_dir().join("gi").join("config.json"); let xdg_path = get_xdg_config_dir().join("lore").join("config.json");
if xdg_path.exists() { if xdg_path.exists() {
return xdg_path; return xdg_path;
} }
// 4. Local fallback (for development) // 4. Local fallback (for development)
let local_path = PathBuf::from("gi.config.json"); let local_path = PathBuf::from("lore.config.json");
if local_path.exists() { if local_path.exists() {
return local_path; return local_path;
} }
@@ -38,9 +38,9 @@ pub fn get_config_path(cli_override: Option<&str>) -> PathBuf {
} }
/// Get the data directory path. /// Get the data directory path.
/// Uses XDG_DATA_HOME or defaults to ~/.local/share/gi /// Uses XDG_DATA_HOME or defaults to ~/.local/share/lore
pub fn get_data_dir() -> PathBuf { pub fn get_data_dir() -> PathBuf {
get_xdg_data_dir().join("gi") get_xdg_data_dir().join("lore")
} }
/// Get the database file path. /// Get the database file path.
@@ -49,7 +49,7 @@ pub fn get_db_path(config_override: Option<&str>) -> PathBuf {
if let Some(path) = config_override { if let Some(path) = config_override {
return PathBuf::from(path); return PathBuf::from(path);
} }
get_data_dir().join("gi.db") get_data_dir().join("lore.db")
} }
/// Get the backup directory path. /// Get the backup directory path.

View File

@@ -1,7 +1,7 @@
//! GitLab Inbox - Semantic search for GitLab issues, MRs, and discussions. //! Gitlore - Semantic search for GitLab issues, MRs, and discussions.
//! //!
//! A self-hosted CLI tool that consolidates GitLab notifications into a unified inbox //! A self-hosted CLI tool that syncs GitLab data to a local SQLite database
//! with semantic search capabilities. //! with fast querying and semantic search capabilities.
pub mod cli; pub mod cli;
pub mod core; pub mod core;

View File

@@ -1,4 +1,4 @@
//! GitLab Inbox CLI entry point. //! Gitlore CLI entry point.
use clap::Parser; use clap::Parser;
use console::style; use console::style;
@@ -8,8 +8,8 @@ use tracing_subscriber::EnvFilter;
use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt; use tracing_subscriber::util::SubscriberInitExt;
use gi::Config; use lore::Config;
use gi::cli::commands::{ use lore::cli::commands::{
InitInputs, InitOptions, ListFilters, MrListFilters, open_issue_in_browser, open_mr_in_browser, InitInputs, InitOptions, ListFilters, MrListFilters, open_issue_in_browser, open_mr_in_browser,
print_count, print_count_json, print_doctor_results, print_ingest_summary, print_count, print_count_json, print_doctor_results, print_ingest_summary,
print_ingest_summary_json, print_list_issues, print_list_issues_json, print_list_mrs, print_ingest_summary_json, print_list_issues, print_list_issues_json, print_list_mrs,
@@ -18,11 +18,11 @@ use gi::cli::commands::{
run_doctor, run_ingest, run_init, run_list_issues, run_list_mrs, run_show_issue, run_show_mr, run_doctor, run_ingest, run_init, run_list_issues, run_list_mrs, run_show_issue, run_show_mr,
run_sync_status, run_sync_status,
}; };
use gi::cli::{Cli, Commands}; use lore::cli::{Cli, Commands};
use gi::core::db::{create_connection, get_schema_version, run_migrations}; use lore::core::db::{create_connection, get_schema_version, run_migrations};
use gi::core::error::{GiError, RobotErrorOutput}; use lore::core::error::{GiError, RobotErrorOutput};
use gi::core::paths::get_config_path; use lore::core::paths::get_config_path;
use gi::core::paths::get_db_path; use lore::core::paths::get_db_path;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
@@ -37,7 +37,7 @@ async fn main() {
) )
.with( .with(
EnvFilter::from_default_env() EnvFilter::from_default_env()
.add_directive("gi=info".parse().unwrap()) .add_directive("lore=info".parse().unwrap())
.add_directive("warn".parse().unwrap()), .add_directive("warn".parse().unwrap()),
) )
.with(indicatif_layer) .with(indicatif_layer)
@@ -326,7 +326,7 @@ async fn handle_init(
); );
println!( println!(
"{}", "{}",
style("\nSetup complete! Run 'gi doctor' to verify.").blue() style("\nSetup complete! Run 'lore doctor' to verify.").blue()
); );
Ok(()) Ok(())
@@ -612,7 +612,7 @@ fn handle_version(robot_mode: bool) -> Result<(), Box<dyn std::error::Error>> {
}; };
println!("{}", serde_json::to_string(&output)?); println!("{}", serde_json::to_string(&output)?);
} else { } else {
println!("gi version {}", version); println!("lore version {}", version);
} }
Ok(()) Ok(())
} }
@@ -641,7 +641,7 @@ fn handle_backup(robot_mode: bool) -> Result<(), Box<dyn std::error::Error>> {
}; };
println!("{}", serde_json::to_string(&output)?); println!("{}", serde_json::to_string(&output)?);
} else { } else {
println!("gi backup - not yet implemented"); println!("lore backup - not yet implemented");
} }
Ok(()) Ok(())
} }
@@ -657,7 +657,7 @@ fn handle_reset(robot_mode: bool) -> Result<(), Box<dyn std::error::Error>> {
}; };
println!("{}", serde_json::to_string(&output)?); println!("{}", serde_json::to_string(&output)?);
} else { } else {
println!("gi reset - not yet implemented"); println!("lore reset - not yet implemented");
} }
Ok(()) Ok(())
} }
@@ -702,7 +702,7 @@ async fn handle_migrate(
error: RobotErrorSuggestionData { error: RobotErrorSuggestionData {
code: "DB_ERROR".to_string(), code: "DB_ERROR".to_string(),
message: format!("Database not found at {}", db_path.display()), message: format!("Database not found at {}", db_path.display()),
suggestion: "Run 'gi init' first".to_string(), suggestion: "Run 'lore init' first".to_string(),
}, },
}; };
eprintln!("{}", serde_json::to_string(&output)?); eprintln!("{}", serde_json::to_string(&output)?);
@@ -713,7 +713,7 @@ async fn handle_migrate(
); );
eprintln!( eprintln!(
"{}", "{}",
style("Run 'gi init' first to create the database.").yellow() style("Run 'lore init' first to create the database.").yellow()
); );
} }
std::process::exit(10); // DB_ERROR exit code std::process::exit(10); // DB_ERROR exit code

View File

@@ -1,7 +1,7 @@
//! Tests for DiffNote position extraction in note transformer. //! Tests for DiffNote position extraction in note transformer.
use gi::gitlab::transformers::discussion::transform_notes_with_diff_position; use lore::gitlab::transformers::discussion::transform_notes_with_diff_position;
use gi::gitlab::types::{ use lore::gitlab::types::{
GitLabAuthor, GitLabDiscussion, GitLabLineRange, GitLabLineRangePoint, GitLabNote, GitLabAuthor, GitLabDiscussion, GitLabLineRange, GitLabLineRangePoint, GitLabNote,
GitLabNotePosition, GitLabNotePosition,
}; };

View File

@@ -1,6 +1,6 @@
//! Tests for test fixtures - verifies they deserialize correctly. //! Tests for test fixtures - verifies they deserialize correctly.
use gi::gitlab::types::{GitLabDiscussion, GitLabIssue}; use lore::gitlab::types::{GitLabDiscussion, GitLabIssue};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use std::path::PathBuf; use std::path::PathBuf;

View File

@@ -1,6 +1,6 @@
//! Tests for GitLab API response type deserialization. //! Tests for GitLab API response type deserialization.
use gi::gitlab::types::{ use lore::gitlab::types::{
GitLabAuthor, GitLabDiscussion, GitLabIssue, GitLabMergeRequest, GitLabMilestone, GitLabNote, GitLabAuthor, GitLabDiscussion, GitLabIssue, GitLabMergeRequest, GitLabMilestone, GitLabNote,
GitLabNotePosition, GitLabReferences, GitLabReviewer, GitLabNotePosition, GitLabReferences, GitLabReviewer,
}; };

View File

@@ -1,7 +1,7 @@
//! Tests for MR discussion transformer. //! Tests for MR discussion transformer.
use gi::gitlab::transformers::discussion::transform_mr_discussion; use lore::gitlab::transformers::discussion::transform_mr_discussion;
use gi::gitlab::types::{GitLabAuthor, GitLabDiscussion, GitLabNote}; use lore::gitlab::types::{GitLabAuthor, GitLabDiscussion, GitLabNote};
fn make_author() -> GitLabAuthor { fn make_author() -> GitLabAuthor {
GitLabAuthor { GitLabAuthor {

View File

@@ -1,7 +1,7 @@
//! Tests for MR transformer module. //! Tests for MR transformer module.
use gi::gitlab::transformers::merge_request::transform_merge_request; use lore::gitlab::transformers::merge_request::transform_merge_request;
use gi::gitlab::types::{GitLabAuthor, GitLabMergeRequest, GitLabReferences, GitLabReviewer}; use lore::gitlab::types::{GitLabAuthor, GitLabMergeRequest, GitLabReferences, GitLabReviewer};
fn make_test_mr() -> GitLabMergeRequest { fn make_test_mr() -> GitLabMergeRequest {
GitLabMergeRequest { GitLabMergeRequest {