diff --git a/CHANGELOG.md b/CHANGELOG.md index fe14439d..a50e8ffb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,10 +26,10 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit. settings, and permissions from `~/.claude` and the repo's `.claude` (layered), and caches the slash-command list per claude version. Backs the typeahead and command-aware send. -- Per-session status in the bottom status bar (T-145, T-150) — the +- Per-session status in the bottom status bar (T-145, T-150, T-154) — the active Claude pane shows its model · permission mode (accept-edits / - plan / …) · context-token count in a status-bar slot, swapping to the - focused pane on tab switch and clearing on blur. Long status text + plan / …) · context-token count · configured skills count, swapping to + the focused pane on tab switch and clearing on blur. Long status text marquee-scrolls within the slot. - tmux agent teams surface as native teammate tiles (T-139, T-140) — when a Claude team is running, each teammate shows as a live diff --git a/lib/builtin/claude/src/claude_pane.dart b/lib/builtin/claude/src/claude_pane.dart index 97be9074..c014d688 100644 --- a/lib/builtin/claude/src/claude_pane.dart +++ b/lib/builtin/claude/src/claude_pane.dart @@ -72,13 +72,18 @@ class _ClaudePaneState extends State { bool _spawned = false; bool _usingTmux = false; - // The status line surfaced to the bottom status bar via ClidePane — - // null until Claude reports a model/mode/context. ClidePane conveys it - // while this pane is the focused one (T-150). + // The status line surfaced to the bottom status bar via ClidePane — the + // live session fields (model/mode/context, T-150) plus the configured + // skills count from ClaudeConfig (T-154). Null when there's nothing yet. Widget? _statusWidget(SurfaceTokens tokens) { - if (_status.isEmpty) return null; + final skills = formatSkillsLabel(activeClaudeConfig?.skills.length ?? 0); + final parts = [ + if (!_status.isEmpty) formatStatusLine(_status), + if (skills != null) skills, + ]; + if (parts.isEmpty) return null; return ClideText( - formatStatusLine(_status), + parts.join(' · '), fontSize: clideFontSmall, fontFamily: clideMonoFamily, color: tokens.statusBarForeground, @@ -86,6 +91,12 @@ class _ClaudePaneState extends State { ); } + // Rebuild when the Claude environment changes (e.g. skills load or a + // watched .claude file changes) so the status line stays current (T-154). + void _onConfigChanged() { + if (mounted) setState(() {}); + } + @override void didChangeDependencies() { super.didChangeDependencies(); @@ -97,11 +108,14 @@ class _ClaudePaneState extends State { // Warm the slash-command list in the background (lazy, idempotent) so // custom commands are recognised by the time the user types one (T-153). unawaited(activeClaudeConfig?.ensureProbe()); + // Reflect skills/config changes in the status line (T-154). + activeClaudeConfig?.addListener(_onConfigChanged); } } @override void dispose() { + activeClaudeConfig?.removeListener(_onConfigChanged); _conversation?.dispose(); _conversation = null; unawaited(_feed?.dispose()); diff --git a/lib/builtin/claude/src/claude_status.dart b/lib/builtin/claude/src/claude_status.dart index c76b6671..97649399 100644 --- a/lib/builtin/claude/src/claude_status.dart +++ b/lib/builtin/claude/src/claude_status.dart @@ -43,6 +43,11 @@ String permissionModeLabel(String mode) { } } +/// Configured-skills count for the status line (`12 skills`), from +/// ClaudeConfig — the *environment* side alongside the live session fields +/// (T-154). Null when there are none to show. +String? formatSkillsLabel(int count) => count > 0 ? '$count skill${count == 1 ? '' : 's'}' : null; + /// Compact token count: `765k`, `1.2M`, or the raw number under 1k. String formatTokenCount(int n) { if (n >= 1000000) return '${(n / 1000000).toStringAsFixed(1)}M'; diff --git a/test/builtin/claude/claude_status_test.dart b/test/builtin/claude/claude_status_test.dart index fc7b5a2a..70db86e3 100644 --- a/test/builtin/claude/claude_status_test.dart +++ b/test/builtin/claude/claude_status_test.dart @@ -26,6 +26,12 @@ void main() { expect(formatTokenCount(765000), '765k'); expect(formatTokenCount(1200000), '1.2M'); }); + + test('formatSkillsLabel pluralises and hides zero', () { + expect(formatSkillsLabel(0), isNull); + expect(formatSkillsLabel(1), '1 skill'); + expect(formatSkillsLabel(12), '12 skills'); + }); }); group('formatStatusLine', () {