docs: rewrite robot-mode-design to reflect implemented features
Comprehensive update to the robot mode design document bringing it in sync with the actual implementation after the elapsed_ms, --fields, and error actions features landed. Major additions: - Response envelope section documenting compact JSON with elapsed_ms timing - Error actions table mapping each error code to executable recovery commands - Field selection section with presets (minimal) and per-entity available fields - Expanded exit codes table (14-20) covering Ollama, embedding, ambiguity errors - Updated command examples to use current CLI syntax (lore issues vs lore list issues) - Added -J shorthand and --fields to global flags table - Best practices section with --fields minimal for token efficiency (~60% reduction) Removed outdated sections that no longer match the implementation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,19 +2,22 @@
|
||||
|
||||
## Overview
|
||||
|
||||
Robot mode optimizes the `lore` 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 compact JSON output, structured errors with machine-actionable recovery steps, meaningful exit codes, response timing metadata, field selection for token efficiency, and TTY auto-detection.
|
||||
|
||||
## Activation
|
||||
|
||||
```bash
|
||||
# Explicit flag
|
||||
lore --robot list issues
|
||||
lore --robot issues -n 5
|
||||
|
||||
# Auto-detection (when stdout is not a TTY)
|
||||
lore list issues | jq .
|
||||
# JSON shorthand
|
||||
lore -J issues -n 5
|
||||
|
||||
# Environment variable
|
||||
LORE_ROBOT=true lore list issues
|
||||
LORE_ROBOT=1 lore issues
|
||||
|
||||
# Auto-detection (when stdout is not a TTY)
|
||||
lore issues | jq .
|
||||
```
|
||||
|
||||
## Global Flags
|
||||
@@ -22,218 +25,160 @@ LORE_ROBOT=true lore list issues
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--robot` | Force JSON output, structured errors |
|
||||
| `--quiet` | Suppress progress/spinners (implied by --robot) |
|
||||
| `-J` / `--json` | Shorthand for `--robot` |
|
||||
| `--quiet` | Suppress progress/spinners (implied by `--robot`) |
|
||||
| `--fields <list>` | Select output fields for list commands |
|
||||
|
||||
## Exit Codes
|
||||
## Response Envelope
|
||||
|
||||
| Code | ErrorCode | Meaning |
|
||||
|------|-----------|---------|
|
||||
| 0 | - | Success |
|
||||
| 1 | INTERNAL_ERROR | Unknown/internal error |
|
||||
| 2 | CONFIG_NOT_FOUND | Config file missing |
|
||||
| 3 | CONFIG_INVALID | Config file malformed |
|
||||
| 4 | TOKEN_NOT_SET | GitLab token not configured |
|
||||
| 5 | GITLAB_AUTH_FAILED | Authentication failed |
|
||||
| 6 | GITLAB_NOT_FOUND | Resource not found |
|
||||
| 7 | GITLAB_RATE_LIMITED | Rate limited |
|
||||
| 8 | GITLAB_NETWORK_ERROR | Network/connection error |
|
||||
| 9 | DB_LOCKED | Database locked by another process |
|
||||
| 10 | DB_ERROR | Database error |
|
||||
| 11 | MIGRATION_FAILED | Migration failed |
|
||||
| 12 | IO_ERROR | File I/O error |
|
||||
| 13 | TRANSFORM_ERROR | Data transformation error |
|
||||
All commands return a consistent JSON envelope to stdout:
|
||||
|
||||
```json
|
||||
{"ok":true,"data":{...},"meta":{"elapsed_ms":42}}
|
||||
```
|
||||
|
||||
Key properties:
|
||||
- **Compact JSON**: Single-line output (no pretty-printing) for efficient parsing
|
||||
- **Uniform envelope**: Every command wraps its data in `{"ok":true,"data":{...},"meta":{...}}`
|
||||
- **Timing metadata**: `meta.elapsed_ms` is present on every response (wall-clock milliseconds)
|
||||
|
||||
## Error Output Format
|
||||
|
||||
When `--robot` is active, errors are JSON on stderr:
|
||||
Errors are JSON on stderr with structured fields for programmatic handling:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "CONFIG_NOT_FOUND",
|
||||
"message": "Config file not found at ~/.config/lore/config.toml",
|
||||
"suggestion": "Run 'lore init' to create configuration"
|
||||
"message": "Config file not found at ~/.config/lore/config.json. Run \"lore init\" first.",
|
||||
"suggestion": "Run 'lore init' to set up your GitLab connection.",
|
||||
"actions": ["lore init"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Success Output Format
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `code` | string | Machine-readable error code (e.g., `CONFIG_NOT_FOUND`) |
|
||||
| `message` | string | Human-readable error description |
|
||||
| `suggestion` | string? | Recovery guidance (omitted when not applicable) |
|
||||
| `actions` | string[]? | Executable shell commands for recovery (omitted when empty) |
|
||||
|
||||
All commands return consistent JSON structure:
|
||||
### Error Actions by Code
|
||||
|
||||
| Error Code | Actions |
|
||||
|------------|---------|
|
||||
| `CONFIG_NOT_FOUND` | `["lore init"]` |
|
||||
| `CONFIG_INVALID` | `["lore init --force"]` |
|
||||
| `GITLAB_AUTH_FAILED` | `["export GITLAB_TOKEN=glpat-xxx", "lore auth"]` |
|
||||
| `TOKEN_NOT_SET` | `["export GITLAB_TOKEN=glpat-xxx"]` |
|
||||
| `OLLAMA_UNAVAILABLE` | `["ollama serve"]` |
|
||||
| `OLLAMA_MODEL_NOT_FOUND` | `["ollama pull nomic-embed-text"]` |
|
||||
| `DB_LOCKED` | `["lore ingest --force"]` |
|
||||
| `EMBEDDING_FAILED` | `["lore embed --retry-failed"]` |
|
||||
| `MIGRATION_FAILED` | `["lore migrate"]` |
|
||||
| `GITLAB_NETWORK_ERROR` | `["lore doctor"]` |
|
||||
|
||||
## Exit Codes
|
||||
|
||||
| Code | ErrorCode | Meaning |
|
||||
|------|-----------|---------|
|
||||
| 0 | -- | Success |
|
||||
| 1 | `INTERNAL_ERROR` | Unknown/internal error |
|
||||
| 2 | -- | Usage error (invalid flags or arguments) |
|
||||
| 3 | `CONFIG_INVALID` | Config file malformed |
|
||||
| 4 | `TOKEN_NOT_SET` | GitLab token not configured |
|
||||
| 5 | `GITLAB_AUTH_FAILED` | Authentication failed |
|
||||
| 6 | `GITLAB_NOT_FOUND` | Resource not found |
|
||||
| 7 | `GITLAB_RATE_LIMITED` | Rate limited |
|
||||
| 8 | `GITLAB_NETWORK_ERROR` | Network/connection error |
|
||||
| 9 | `DB_LOCKED` | Database locked by another process |
|
||||
| 10 | `DB_ERROR` | Database error |
|
||||
| 11 | `MIGRATION_FAILED` | Migration failed |
|
||||
| 12 | `IO_ERROR` | File I/O error |
|
||||
| 13 | `TRANSFORM_ERROR` | Data transformation error |
|
||||
| 14 | `OLLAMA_UNAVAILABLE` | Ollama not running |
|
||||
| 15 | `OLLAMA_MODEL_NOT_FOUND` | Ollama model not installed |
|
||||
| 16 | `EMBEDDING_FAILED` | Embedding generation failed |
|
||||
| 17 | `NOT_FOUND` | Entity does not exist locally |
|
||||
| 18 | `AMBIGUOUS` | Multiple projects match (use `-p`) |
|
||||
| 19 | -- | Health check failed |
|
||||
| 20 | `CONFIG_NOT_FOUND` | Config file missing |
|
||||
|
||||
## Field Selection
|
||||
|
||||
The `--fields` flag on `issues` and `mrs` list commands controls which fields appear in each item of the response array:
|
||||
|
||||
```bash
|
||||
# Preset: ~60% fewer tokens
|
||||
lore -J issues --fields minimal
|
||||
|
||||
# Custom field list
|
||||
lore -J mrs --fields iid,title,state,draft,target_branch
|
||||
```
|
||||
|
||||
### Presets
|
||||
|
||||
| Preset | Expands to |
|
||||
|--------|------------|
|
||||
| `minimal` | `iid`, `title`, `state`, `updated_at_iso` |
|
||||
|
||||
### Available Fields
|
||||
|
||||
**Issues**: `iid`, `title`, `state`, `author_username`, `labels`, `assignees`, `discussion_count`, `unresolved_count`, `created_at_iso`, `updated_at_iso`, `web_url`, `project_path`
|
||||
|
||||
**MRs**: `iid`, `title`, `state`, `author_username`, `labels`, `draft`, `target_branch`, `source_branch`, `discussion_count`, `unresolved_count`, `created_at_iso`, `updated_at_iso`, `web_url`, `project_path`, `reviewers`
|
||||
|
||||
Field selection applies only to list output, not to show (single-entity) output which returns full detail.
|
||||
|
||||
## Command Response Schemas
|
||||
|
||||
Every command in `lore robot-docs` includes a `response_schema` field describing the shape of its JSON response. This enables agents to understand response structures without trial-and-error.
|
||||
|
||||
```bash
|
||||
# Get schema for a specific command
|
||||
lore robot-docs | jq '.data.commands.issues.response_schema'
|
||||
|
||||
# Get all schemas
|
||||
lore robot-docs | jq '[.data.commands | to_entries[] | select(.value.response_schema) | {(.key): .value.response_schema}] | add'
|
||||
```
|
||||
|
||||
## Clap Error Handling
|
||||
|
||||
Parse errors from the argument parser emit structured JSON to stderr with semantic error codes:
|
||||
|
||||
| Code | Meaning |
|
||||
|------|---------|
|
||||
| `UNKNOWN_COMMAND` | Unrecognized subcommand (includes fuzzy suggestion) |
|
||||
| `UNKNOWN_FLAG` | Unrecognized command-line flag |
|
||||
| `MISSING_REQUIRED` | Required argument not provided |
|
||||
| `INVALID_VALUE` | Invalid value for argument |
|
||||
| `TOO_MANY_VALUES` | Too many values provided |
|
||||
| `TOO_FEW_VALUES` | Too few values provided |
|
||||
| `ARGUMENT_CONFLICT` | Conflicting arguments |
|
||||
| `MISSING_COMMAND` | No subcommand provided |
|
||||
| `HELP_REQUESTED` | Help or version flag used |
|
||||
| `PARSE_ERROR` | General parse error |
|
||||
|
||||
Unknown commands include a fuzzy suggestion when a close match exists:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"data": { ... },
|
||||
"meta": {
|
||||
"count": 50,
|
||||
"total": 1234,
|
||||
"elapsed_ms": 45
|
||||
}
|
||||
}
|
||||
{"error":{"code":"UNKNOWN_COMMAND","message":"...","suggestion":"Did you mean 'lore issues'? Run 'lore robot-docs' for all commands"}}
|
||||
```
|
||||
|
||||
## Command-Specific Output
|
||||
## Agent Self-Discovery
|
||||
|
||||
### lore list issues --robot
|
||||
`lore robot-docs` provides a complete manifest for agent bootstrapping:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"data": {
|
||||
"issues": [
|
||||
{
|
||||
"iid": 123,
|
||||
"project": "group/project",
|
||||
"title": "Bug in login",
|
||||
"state": "opened",
|
||||
"author": "username",
|
||||
"assignees": ["user1"],
|
||||
"labels": ["bug", "priority::high"],
|
||||
"discussions": { "total": 5, "unresolved": 2 },
|
||||
"updated_at": "2024-01-15T10:30:00Z",
|
||||
"web_url": "https://..."
|
||||
}
|
||||
]
|
||||
},
|
||||
"meta": { "showing": 50, "total": 234 }
|
||||
}
|
||||
```bash
|
||||
lore robot-docs # Pretty-printed (human-readable)
|
||||
lore --robot robot-docs # Compact (for parsing)
|
||||
```
|
||||
|
||||
### lore show issue 123 --robot
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"data": {
|
||||
"issue": {
|
||||
"iid": 123,
|
||||
"project": "group/project",
|
||||
"title": "Bug in login",
|
||||
"description": "Full markdown...",
|
||||
"state": "opened",
|
||||
"author": "username",
|
||||
"created_at": "2024-01-10T08:00:00Z",
|
||||
"updated_at": "2024-01-15T10:30:00Z",
|
||||
"discussions": [
|
||||
{
|
||||
"id": "abc123",
|
||||
"resolved": false,
|
||||
"notes": [
|
||||
{
|
||||
"author": "user1",
|
||||
"body": "Comment text...",
|
||||
"created_at": "2024-01-11T09:00:00Z",
|
||||
"system": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### lore ingest --type issues --robot
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"data": {
|
||||
"resource_type": "issues",
|
||||
"projects": [
|
||||
{
|
||||
"path": "group/project",
|
||||
"issues_synced": 45,
|
||||
"discussions_synced": 123
|
||||
}
|
||||
],
|
||||
"totals": {
|
||||
"issues": 45,
|
||||
"discussions": 123
|
||||
}
|
||||
},
|
||||
"meta": { "elapsed_ms": 3400 }
|
||||
}
|
||||
```
|
||||
|
||||
### lore count issues --robot
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"data": {
|
||||
"entity": "issues",
|
||||
"count": 1234,
|
||||
"breakdown": {
|
||||
"opened": 456,
|
||||
"closed": 778
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### lore doctor --robot
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"data": {
|
||||
"success": true,
|
||||
"checks": {
|
||||
"config": { "status": "ok", "path": "~/.config/lore/config.toml" },
|
||||
"database": { "status": "ok", "version": 6 },
|
||||
"gitlab": { "status": "ok", "user": "username" },
|
||||
"projects": [
|
||||
{ "path": "group/project", "status": "ok" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### lore sync-status --robot
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"data": {
|
||||
"last_sync": {
|
||||
"status": "completed",
|
||||
"resource_type": "issues",
|
||||
"started_at": "2024-01-15T10:00:00Z",
|
||||
"completed_at": "2024-01-15T10:00:45Z",
|
||||
"duration_ms": 45000
|
||||
},
|
||||
"cursors": [
|
||||
{
|
||||
"project": "group/project",
|
||||
"resource_type": "issues",
|
||||
"cursor": "2024-01-15T10:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Core Infrastructure
|
||||
1. Add `--robot` global flag to Cli struct
|
||||
2. Create `RobotOutput` trait for consistent JSON serialization
|
||||
3. Add exit code mapping from ErrorCode
|
||||
4. Implement TTY detection with `atty` crate
|
||||
|
||||
### Phase 2: Command Updates
|
||||
1. Update all commands to check robot mode
|
||||
2. Add JSON output variants for commands missing them (count, ingest, sync-status)
|
||||
3. Suppress progress bars in robot mode
|
||||
|
||||
### Phase 3: Error Handling
|
||||
1. Update main.rs error handler for robot mode
|
||||
2. Add suggestion field to GiError variants
|
||||
3. Emit structured JSON errors to stderr
|
||||
|
||||
### Phase 4: Documentation
|
||||
1. Update AGENTS.md with robot mode commands
|
||||
2. Add --robot examples to help text
|
||||
The manifest includes:
|
||||
- All commands with flags, examples, and response schemas
|
||||
- Deprecated command aliases (e.g., `list issues` -> `issues`)
|
||||
- Exit codes with meanings
|
||||
- Clap error codes
|
||||
- Suggested workflows (first setup, daily sync, search, pre-flight)
|
||||
- Activation methods (flags, env vars, TTY auto-detection)
|
||||
|
||||
Reference in New Issue
Block a user