Skip to content

Commit d1c68e8

Browse files
committed
feat: add multi-panel dashboard with commitments, goals, and status bar
- Add dashboard.py with Table.grid() + Panel pattern for auto-alignment - Implement format_progress_bar() with colored progress indicators - Add format_commitments_panel() showing status icons and due dates - Add format_goals_panel() with progress bars and review warnings - Add format_status_bar() with integrity, streak, and triage counts - Add DisplayLevel enum for adaptive display (MINIMAL/COMPACT/STANDARD/FULL) - Add get_dashboard_commitments() and get_dashboard_goals() queries - Extend Session with dashboard cache fields and DashboardCacheUpdate - Replace single-line summary with full dashboard in REPL loop - Add 28 unit tests for dashboard module - Update README with dashboard visual and new features All 1598 tests passing.
1 parent 2ffe066 commit d1c68e8

File tree

32 files changed

+3552
-88
lines changed

32 files changed

+3552
-88
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ When a feature spec is archived, it should be added here and removed from `ROADM
2020
- **add-ai-driven-uat** (2025-12-19): AI-driven UAT framework with scenario definitions and mock agent testing
2121

2222
#### AI Integration
23+
- **improve-ai-credential-usage**: Explicit API credential passing to PydanticAI providers, credential validation, structured logging, CLI auth commands (`jdo auth set`, `jdo auth status`), improved error messages with recovery hints
2324
- **wire-ai-to-chat** (2025-12-19): Connected PydanticAI agent to ChatScreen with streaming responses and tool invocation
2425
- **persist-handler-results** (2025-12-19): Wired command handlers to database persistence with confirmation flow
2526
- **add-ai-coaching-time-management** (2025-12-19): AI time coaching with /hours command and task duration tracking
@@ -28,6 +29,9 @@ When a feature spec is archived, it should be added here and removed from `ROADM
2829
- **add-commitment-guardrails** (2025-12-19): Validation guardrails for commitment creation with stakeholder and timeline requirements
2930
- **add-reliability-compliance** (2025-12-19): Command timeout handling, retry logic, and reliability patterns
3031

32+
#### Authentication & CLI
33+
- **improve-ai-credential-usage**: Added `jdo auth set` and `jdo auth status` CLI commands, credential storage in auth.json with 0600 permissions, explicit provider initialization with `OpenRouterProvider`/`OpenAIProvider`, comprehensive credential error handling with `MissingCredentialsError`, `InvalidCredentialsError`, `UnsupportedProviderError`
34+
3135
## [0.1.0] - 2025-12-17
3236

3337
Initial development release with core functionality.

README.md

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@ Tell it in plain language. The AI extracts goals, milestones, commitments, and t
5555
|---|---|
5656
| **Conversational Interface** | Chat naturally—the AI structures your goals for you |
5757
| **Flexible Hierarchy** | Visions, Goals, Milestones, Commitments, Tasks |
58+
| **Live Dashboard** | Multi-panel display with commitments, goals, progress bars, and status |
5859
| **Streaming AI Responses** | Responses appear token-by-token with markdown rendering |
5960
| **Hybrid Input** | Natural language primary, slash commands for power users |
60-
| **Visual Polish** | Animated spinner, tab completion, status bar, rounded tables |
61+
| **Visual Polish** | Animated spinner, tab completion, status bar, rounded panels |
6162
| **Recurring Commitments** | Set it once, generate instances automatically |
62-
| **Multiple AI Providers** | OpenAI or OpenRouter—your choice |
63+
| **Multiple AI Providers** | OpenAI, OpenRouter, Anthropic, or Google—your choice |
6364
| **Local-First Storage** | Your data stays on your machine, always |
6465

6566
---
@@ -99,8 +100,16 @@ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | ie
99100

100101
**1. Configure your AI provider**
101102

103+
Using environment variables:
102104
```bash
103-
export OPENAI_API_KEY="sk-..."
105+
export OPENROUTER_API_KEY="sk-or-..."
106+
export JDO_AI_PROVIDER="openrouter"
107+
export JDO_AI_MODEL="gpt-5.1-mini"
108+
```
109+
110+
Or using the interactive auth command:
111+
```bash
112+
jdo auth set openrouter
104113
```
105114

106115
<details>
@@ -111,6 +120,12 @@ export OPENROUTER_API_KEY="sk-or-..."
111120
export JDO_AI_PROVIDER="openrouter"
112121
```
113122

123+
Or interactively:
124+
```bash
125+
jdo auth set openrouter
126+
jdo set JDO_AI_PROVIDER=openrouter
127+
```
128+
114129
</details>
115130

116131
**2. Launch**
@@ -123,6 +138,33 @@ uv run jdo
123138

124139
---
125140

141+
## The Dashboard
142+
143+
When you launch jdo, you see a live dashboard showing your current state at a glance:
144+
145+
```
146+
╭─ 📋 Commitments (3 active, 1 at-risk) ───────────────────────────────────────────────────────────╮
147+
│ │
148+
│ ● Send invoice to Client Client OVERDUE (2 days) │
149+
│ ● Submit Q1 report Finance Today 5:00pm │
150+
│ ○ Review PR #234 Team Tomorrow │
151+
│ │
152+
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
153+
╭─ 🎯 Goals (2 active) ────────────────────────────────────────────────────────────────────────────╮
154+
│ │
155+
│ Launch MVP ████████████████░░░░ 80% 4/5 done │
156+
│ Health & Fitness ████████████░░░░░░░░ 60% review ⚠ │
157+
│ │
158+
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
159+
╭──────────────────────────────────────────────────────────────────────────────────────────────────╮
160+
│ 📊 Integrity: A- (91%) ↑ 🔥 Streak: 3 weeks 📥 Triage: 5 │
161+
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
162+
```
163+
164+
The dashboard adapts to your data—showing only what's relevant, from a minimal status bar to a full three-panel view.
165+
166+
---
167+
126168
## Example Conversation
127169

128170
```
@@ -169,6 +211,7 @@ For power users who prefer instant, deterministic actions:
169211
| `/list goals` | List all goals |
170212
| `/list visions` | List all visions |
171213
| `/commit "..."` | Create a new commitment |
214+
| `/complete <id>` | Mark a commitment as complete |
172215
| `/review` | Review visions due for quarterly review |
173216

174217
Or just type naturally—the AI understands plain English.
@@ -181,6 +224,8 @@ Or just type naturally—the AI understands plain English.
181224
|---------|-------------|
182225
| `jdo` | Launch the conversational REPL |
183226
| `jdo capture "text"` | Quick capture for later triage |
227+
| `jdo auth status` | Show credential status for all providers |
228+
| `jdo auth set <provider>` | Set API key for an AI provider |
184229
| `jdo db status` | Show database migration status |
185230
| `jdo db upgrade` | Apply pending migrations |
186231

@@ -212,8 +257,8 @@ Vision ─────────────── "Become financially indepen
212257

213258
| Variable | Default | Description |
214259
|:---------|:--------|:------------|
215-
| `JDO_AI_PROVIDER` | `openai` | Provider: `openai`, `openrouter` |
216-
| `JDO_AI_MODEL` | `gpt-4o` | Model identifier |
260+
| `JDO_AI_PROVIDER` | `openrouter` | Provider: `openai`, `openrouter` |
261+
| `JDO_AI_MODEL` | `gpt-5.1-mini` | Model identifier (OpenRouter format for best pricing) |
217262
| `JDO_TIMEZONE` | `America/New_York` | Your local timezone |
218263
| `JDO_DATABASE_PATH` | *(platform default)* | Custom database location |
219264

@@ -231,6 +276,38 @@ All data is stored locally using platform-appropriate directories:
231276
- `jdo.db` — SQLite database containing all your data
232277
- `auth.json` — API credentials storage
233278

279+
### Credential Management
280+
281+
JDO supports storing API credentials securely in a local credentials file. This is the recommended approach over environment variables.
282+
283+
**Interactive setup:**
284+
```bash
285+
jdo auth set openai # Configure OpenAI
286+
jdo auth set openrouter # Configure OpenRouter
287+
jdo auth set anthropic # Configure Anthropic
288+
jdo auth set google # Configure Google AI
289+
```
290+
291+
**Check status:**
292+
```bash
293+
jdo auth status # Show credential status for all providers
294+
```
295+
296+
**Credentials are stored in:**
297+
- Linux: `~/.local/share/jdo/auth.json`
298+
- macOS: `~/Library/Application Support/jdo/auth.json`
299+
- Windows: `%LOCALAPPDATA%\jdo\auth.json`
300+
301+
Alternatively, you can use environment variables:
302+
```bash
303+
export OPENAI_API_KEY="sk-..."
304+
export OPENROUTER_API_KEY="sk-or-..."
305+
export ANTHROPIC_API_KEY="sk-ant-..."
306+
export GOOGLE_API_KEY="your-google-key"
307+
export JDO_AI_PROVIDER="openrouter"
308+
export JDO_AI_MODEL="gpt-5.1-mini"
309+
```
310+
234311
---
235312

236313
## Development
@@ -265,10 +342,10 @@ src/jdo/
265342
├── auth/ API key management
266343
├── commands/ Command parsing and handlers
267344
├── config/ Settings, paths
268-
├── db/ SQLite engine, migrations
345+
├── db/ SQLite engine, migrations, queries
269346
├── models/ SQLModel entities
270-
├── output/ Rich formatters for CLI output
271-
├── repl/ REPL loop and session management
347+
├── output/ Rich formatters, dashboard panels
348+
├── repl/ REPL loop, session, dashboard display
272349
└── cli.py CLI entry point
273350
```
274351

coverage.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

migrations/env.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
# Import all models to ensure they're registered with SQLModel.metadata
1919
# This is required for autogenerate to detect model changes
20+
# Get settings for database path
21+
from jdo.config.settings import get_settings # noqa: E402
2022
from jdo.models import ( # noqa: E402, F401
2123
CleanupPlan,
2224
Commitment,
@@ -29,9 +31,6 @@
2931
Vision,
3032
)
3133

32-
# Get settings for database path
33-
from jdo.config.settings import get_settings # noqa: E402
34-
3534
# Alembic Config object for access to .ini file values
3635
config = context.config
3736

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Design: Commitment Summary Panel
2+
3+
## Context
4+
5+
Users lose context of their active commitments while using the REPL. This design adds a compact visual summary that appears before each prompt.
6+
7+
## Goals / Non-Goals
8+
9+
**Goals:**
10+
- Keep users informed of commitment status without explicit queries
11+
- Minimal visual noise - compact, single-line where possible
12+
- Consistent with existing Rich styling patterns
13+
- No interruption to conversational flow
14+
15+
**Non-Goals:**
16+
- Full-screen dashboard (use `/list` for detailed view)
17+
- Real-time updates during AI streaming
18+
- Persistent display that doesn't scroll away
19+
20+
## Research Findings
21+
22+
### Rich Panel vs Layout/Live
23+
24+
**Decision:** Use `Panel.fit()` with `console.print()` - NOT Layout/Live
25+
26+
**Rationale:**
27+
- Rich Layout is explicitly for "full-screen applications" per Rich docs
28+
- Rich Live is for animated/updating displays, not static output
29+
- Panel with sequential print is the correct pattern for REPLs
30+
- Already proven by 70+ existing `console.print()` calls in codebase
31+
32+
**Sources:**
33+
- Rich Panel docs: https://rich.readthedocs.io/en/stable/panel.html
34+
- Rich Live docs: https://rich.readthedocs.io/en/stable/live.html
35+
- Rich Layout docs: https://rich.readthedocs.io/en/stable/layout.html
36+
37+
### Box Style Selection
38+
39+
**Decision:** Use `box.ROUNDED` for rounded corners (╭─╮╰─╯)
40+
41+
**Rationale:**
42+
- `box.SIMPLE` has NO visible borders - only horizontal separator lines
43+
- `box.ROUNDED` produces the rounded corner aesthetic from the visual concept
44+
- Consistent with existing tables in codebase that use `box.ROUNDED`
45+
46+
**Available box styles:**
47+
| Style | Appearance | Best For |
48+
|-------|------------|----------|
49+
| `ROUNDED` | ╭─╮╰─╯ | Modern, friendly UI ✓ |
50+
| `SQUARE` | ┌─┐└─┘ | Classic, structured |
51+
| `DOUBLE` | ╔═╗╚═╝ | Emphasis |
52+
| `HEAVY` | ┏━┓┗━┛ | Strong emphasis |
53+
| `MINIMAL` | Light lines | Clean, minimal |
54+
| `SIMPLE` | Horizontal only | NO visible border |
55+
56+
**Source:** Rich box.py source code
57+
58+
### Relative Date Formatting
59+
60+
**Decision:** Custom ~15-line implementation, not external library
61+
62+
**Rationale:**
63+
- `humanize.naturalday()` returns "Jun 05" format, not weekday names
64+
- `arrow.humanize()` outputs "in 3 days" but not "Fri" for same-week dates
65+
- Exact format needed: "Today", "Tomorrow", "Fri", "in X days"
66+
- Adding 132KB dependency for single function is overkill
67+
- Custom implementation is easy to test and maintain
68+
69+
**Implementation:**
70+
```python
71+
def format_relative_date(d: date, today: date | None = None) -> str:
72+
today = today or date.today()
73+
delta = (d - today).days
74+
if delta == 0:
75+
return "Today"
76+
if delta == 1:
77+
return "Tomorrow"
78+
if 2 <= delta <= 6:
79+
return d.strftime("%a") # Mon, Tue, etc.
80+
if delta > 6:
81+
return f"in {delta} days"
82+
return d.strftime("%Y-%m-%d") # Past dates: fallback
83+
```
84+
85+
**Source:** PyPI humanize, arrow, pendulum docs
86+
87+
### Session Caching Strategy
88+
89+
**Decision:** Keep existing caching pattern with rate-limited updates
90+
91+
**Rationale:**
92+
- SQLite queries are fast (~0.01ms) but toolbar callback runs every keystroke
93+
- Existing pattern caches `commitment_count` and `triage_count`
94+
- Extend to cache `at_risk_count` and `next_due` info
95+
- Update cache only when commitments change (create/complete)
96+
97+
**Trade-off accepted:**
98+
- Cache may be stale if DB modified externally
99+
- Acceptable for single-user CLI app
100+
- Simplifies implementation vs rate-limited live queries
101+
102+
**Source:** prompt_toolkit bottom_toolbar docs, SQLite isolation docs
103+
104+
## Decisions
105+
106+
### Panel Content Structure
107+
108+
Using `Text.assemble()` for inline styled segments:
109+
110+
```python
111+
content = Text.assemble(
112+
("📋 ", ""), # Clipboard emoji
113+
("3", "bold"), # Count in bold
114+
(" active ", "dim"), # Label dimmed
115+
("(", "dim"),
116+
("1", "bold yellow"), # At-risk count in yellow
117+
(" ⚠️)", ""), # Warning emoji
118+
("", "dim"), # Separator
119+
("Next: ", "dim"), # Label dimmed
120+
("Report", "cyan"), # Deliverable in cyan
121+
("", "dim"), # Arrow separator
122+
("Fri", "bold cyan"), # Date in bold cyan
123+
)
124+
```
125+
126+
### Color Semantics
127+
128+
Consistent with existing codebase patterns:
129+
130+
| Status | Color | Emoji |
131+
|--------|-------|-------|
132+
| Active/Normal | `cyan` / `bold` | 📋 |
133+
| At-risk | `bold yellow` | ⚠️ 🟡 |
134+
| Overdue | `bold red` | ❗ 🔴 |
135+
| Labels | `dim` | - |
136+
| Success | `green` | ✅ 🟢 |
137+
138+
### Panel Styling
139+
140+
```python
141+
Panel.fit(
142+
content,
143+
box=box.ROUNDED, # Rounded corners
144+
border_style="dim", # Subtle border
145+
padding=(0, 1), # Minimal padding
146+
)
147+
```
148+
149+
## Risks / Trade-offs
150+
151+
| Risk | Mitigation |
152+
|------|------------|
153+
| Panel adds visual noise | Use `border_style="dim"` for subtle appearance |
154+
| Cache staleness from external changes | Acceptable for single-user CLI; `/list` shows live data |
155+
| Emoji rendering on older terminals | Emoji are informational only; text is primary |
156+
157+
## Open Questions
158+
159+
1. **Should panel appear after EVERY command or only commitment-related ones?**
160+
- Current decision: After every command for consistent positioning
161+
- Can revisit if users find it noisy
162+
163+
2. **What if terminal is narrow?**
164+
- `Panel.fit()` will wrap content
165+
- Could add `max_width` parameter if needed

0 commit comments

Comments
 (0)