sidekick-agent-hub 0.12.10 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,7 +27,7 @@ Requires **Node.js 20+**.
27
27
 
28
28
  ```bash
29
29
  sidekick dashboard [options]
30
- sidekick tasks|decisions|notes|stats|handoff|search|context [options]
30
+ sidekick tasks|decisions|notes|stats|quota|handoff|search|context [options]
31
31
  ```
32
32
 
33
33
  The standalone commands open the dashboard directly to a specific panel or run a one-shot query. All accept `--project` and `--provider` flags.
@@ -77,6 +77,23 @@ Global flags `--project` and `--provider` also apply.
77
77
 
78
78
  You can also press `r` in the TUI dashboard to generate a report for the current session.
79
79
 
80
+ ## Subscription Quota
81
+
82
+ ```bash
83
+ sidekick quota
84
+ ```
85
+
86
+ Check Claude Max / Claude Code subscription quota utilization. Shows 5-hour and 7-day windows with color-coded progress bars and reset countdowns.
87
+
88
+ ```
89
+ Subscription Quota
90
+ ──────────────────────────────────────────────────
91
+ 5-Hour ████████████░░░░░░░░░░░░░░░░░░ 40% resets in 2h 15m
92
+ 7-Day ██████████████████████░░░░░░░░ 72% resets in 4d 6h
93
+ ```
94
+
95
+ Use `--json` for machine-readable output. Requires Claude Code credentials (`~/.claude/.credentials.json`).
96
+
80
97
  ## Dashboard Panels
81
98
 
82
99
  The dashboard is a two-pane terminal UI. The left side shows a navigable list, the right side shows details for the selected item.
@@ -8251,8 +8251,6 @@ var require_codexDatabase = __commonJS({
8251
8251
  }
8252
8252
  isAvailable() {
8253
8253
  try {
8254
- if (!fs10.existsSync(this.dbPath))
8255
- return false;
8256
8254
  return fs10.statSync(this.dbPath).size > 0;
8257
8255
  } catch {
8258
8256
  return false;
@@ -8275,15 +8273,19 @@ var require_codexDatabase = __commonJS({
8275
8273
  query(sql, params = []) {
8276
8274
  if (!this.sqlite3Available)
8277
8275
  return [];
8278
- let query = sql;
8279
- for (const param of params) {
8280
- if (typeof param === "number")
8281
- query = query.replace("?", String(param));
8282
- else {
8283
- const escaped = String(param).replace(/'/g, "''");
8284
- query = query.replace("?", `'${escaped}'`);
8285
- }
8286
- }
8276
+ let paramIndex = 0;
8277
+ const query = sql.replace(/\?/g, () => {
8278
+ if (paramIndex >= params.length)
8279
+ return "?";
8280
+ const param = params[paramIndex++];
8281
+ if (typeof param === "number") {
8282
+ if (!Number.isFinite(param))
8283
+ return "0";
8284
+ return String(param);
8285
+ }
8286
+ const escaped = String(param).replace(/'/g, "''");
8287
+ return `'${escaped}'`;
8288
+ });
8287
8289
  try {
8288
8290
  const result = (0, child_process_1.execFileSync)("sqlite3", ["-json", "-readonly", this.dbPath, query], {
8289
8291
  encoding: "utf-8",
@@ -18746,19 +18748,18 @@ var init_StaticDataLoader = __esm({
18746
18748
  import * as fs2 from "fs";
18747
18749
  import * as path from "path";
18748
18750
  import * as os from "os";
18749
- var REFRESH_MS, USAGE_URL, BETA_HEADER, MAX_HISTORY, QuotaService;
18751
+ var REFRESH_MS, USAGE_URL, BETA_HEADER, FIVE_HOUR_MS, SEVEN_DAY_MS, QuotaService;
18750
18752
  var init_QuotaService = __esm({
18751
18753
  "src/dashboard/QuotaService.ts"() {
18752
18754
  "use strict";
18753
18755
  REFRESH_MS = 3e4;
18754
18756
  USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
18755
18757
  BETA_HEADER = "oauth-2025-04-20";
18756
- MAX_HISTORY = 10;
18758
+ FIVE_HOUR_MS = 5 * 36e5;
18759
+ SEVEN_DAY_MS = 7 * 864e5;
18757
18760
  QuotaService = class {
18758
18761
  _interval = null;
18759
18762
  _cached = null;
18760
- _fiveHourHistory = [];
18761
- _sevenDayHistory = [];
18762
18763
  _callback = null;
18763
18764
  /** Register a callback for quota updates. */
18764
18765
  onUpdate(cb) {
@@ -18781,6 +18782,41 @@ var init_QuotaService = __esm({
18781
18782
  getCached() {
18782
18783
  return this._cached;
18783
18784
  }
18785
+ /** Single fetch — no polling, includes elapsed-time projections. */
18786
+ async fetchOnce() {
18787
+ const token = await this.readToken();
18788
+ if (!token) {
18789
+ return { fiveHour: { utilization: 0, resetsAt: "" }, sevenDay: { utilization: 0, resetsAt: "" }, available: false, error: "no-credentials" };
18790
+ }
18791
+ try {
18792
+ const res = await fetch(USAGE_URL, {
18793
+ method: "GET",
18794
+ headers: {
18795
+ "Authorization": `Bearer ${token}`,
18796
+ "anthropic-beta": BETA_HEADER,
18797
+ "Content-Type": "application/json"
18798
+ }
18799
+ });
18800
+ if (!res.ok) {
18801
+ const error = res.status === 401 ? "auth-failed" : `API error: ${res.status}`;
18802
+ return { fiveHour: { utilization: 0, resetsAt: "" }, sevenDay: { utilization: 0, resetsAt: "" }, available: false, error };
18803
+ }
18804
+ const data = await res.json();
18805
+ const fiveUtil = data.five_hour?.utilization ?? 0;
18806
+ const sevenUtil = data.seven_day?.utilization ?? 0;
18807
+ const fiveResetsAt = data.five_hour?.resets_at ?? "";
18808
+ const sevenResetsAt = data.seven_day?.resets_at ?? "";
18809
+ return {
18810
+ fiveHour: { utilization: fiveUtil, resetsAt: fiveResetsAt },
18811
+ sevenDay: { utilization: sevenUtil, resetsAt: sevenResetsAt },
18812
+ available: true,
18813
+ projectedFiveHour: this.projectFromElapsed(fiveUtil, fiveResetsAt, FIVE_HOUR_MS),
18814
+ projectedSevenDay: this.projectFromElapsed(sevenUtil, sevenResetsAt, SEVEN_DAY_MS)
18815
+ };
18816
+ } catch {
18817
+ return { fiveHour: { utilization: 0, resetsAt: "" }, sevenDay: { utilization: 0, resetsAt: "" }, available: false, error: "network-error" };
18818
+ }
18819
+ }
18784
18820
  async fetchQuota() {
18785
18821
  const token = await this.readToken();
18786
18822
  if (!token) {
@@ -18809,16 +18845,14 @@ var init_QuotaService = __esm({
18809
18845
  const data = await res.json();
18810
18846
  const fiveUtil = data.five_hour?.utilization ?? 0;
18811
18847
  const sevenUtil = data.seven_day?.utilization ?? 0;
18812
- this.addHistory(this._fiveHourHistory, fiveUtil);
18813
- this.addHistory(this._sevenDayHistory, sevenUtil);
18814
18848
  const fiveResetsAt = data.five_hour?.resets_at ?? "";
18815
18849
  const sevenResetsAt = data.seven_day?.resets_at ?? "";
18816
18850
  this.emit({
18817
18851
  fiveHour: { utilization: fiveUtil, resetsAt: fiveResetsAt },
18818
18852
  sevenDay: { utilization: sevenUtil, resetsAt: sevenResetsAt },
18819
18853
  available: true,
18820
- projectedFiveHour: this.project(fiveUtil, fiveResetsAt, this.rate(this._fiveHourHistory)),
18821
- projectedSevenDay: this.project(sevenUtil, sevenResetsAt, this.rate(this._sevenDayHistory))
18854
+ projectedFiveHour: this.projectFromElapsed(fiveUtil, fiveResetsAt, FIVE_HOUR_MS),
18855
+ projectedSevenDay: this.projectFromElapsed(sevenUtil, sevenResetsAt, SEVEN_DAY_MS)
18822
18856
  });
18823
18857
  } catch {
18824
18858
  if (this._cached?.available) return;
@@ -18843,25 +18877,13 @@ var init_QuotaService = __esm({
18843
18877
  return null;
18844
18878
  }
18845
18879
  }
18846
- addHistory(history, utilization) {
18847
- history.push({ utilization, timestamp: Date.now() });
18848
- while (history.length > MAX_HISTORY) history.shift();
18849
- }
18850
- rate(history) {
18851
- if (history.length < 2) return null;
18852
- const oldest = history[0];
18853
- const newest = history[history.length - 1];
18854
- const diffMs = newest.timestamp - oldest.timestamp;
18855
- if (diffMs < 3e4) return null;
18856
- const diffUtil = newest.utilization - oldest.utilization;
18857
- if (diffUtil <= 0) return 0;
18858
- return diffUtil / (diffMs / 6e4);
18859
- }
18860
- project(current, resetsAt, rate) {
18861
- if (rate === null || rate <= 0 || !resetsAt) return void 0;
18862
- const timeToResetMs = new Date(resetsAt).getTime() - Date.now();
18863
- if (timeToResetMs <= 0) return void 0;
18864
- return Math.min(current + rate * (timeToResetMs / 6e4), 200);
18880
+ projectFromElapsed(utilization, resetsAt, windowMs) {
18881
+ if (!resetsAt || utilization <= 0) return void 0;
18882
+ const resetTime = new Date(resetsAt).getTime();
18883
+ const now = Date.now();
18884
+ const elapsed = windowMs - (resetTime - now);
18885
+ if (elapsed <= 0) return void 0;
18886
+ return Math.min(Math.round(utilization * (windowMs / elapsed)), 200);
18865
18887
  }
18866
18888
  };
18867
18889
  }
@@ -18898,7 +18920,7 @@ var init_UpdateCheckService = __esm({
18898
18920
  /** Run the update check (one-shot). */
18899
18921
  async check() {
18900
18922
  try {
18901
- const current = "0.12.10";
18923
+ const current = "0.13.1";
18902
18924
  const cached = this.readCache();
18903
18925
  let latest;
18904
18926
  if (cached && Date.now() - cached.checkedAt < CACHE_TTL_MS) {
@@ -20469,10 +20491,18 @@ ${hint}{/grey-fg}`;
20469
20491
  lines.push("", sectionHeader("Quota", w2));
20470
20492
  const fiveColor = getUtilizationColor(q.fiveHour.utilization);
20471
20493
  const fiveBar = makeColorBar(q.fiveHour.utilization, 18, fiveColor);
20472
- lines.push(` {grey-fg}5h{/grey-fg} ${fiveBar} {bold}${q.fiveHour.utilization.toFixed(0)}%{/bold}`);
20494
+ const fiveProj = q.projectedFiveHour != null ? (() => {
20495
+ const pc = getUtilizationColor(q.projectedFiveHour);
20496
+ return ` {grey-fg}\u2192{/grey-fg} {${pc}-fg}${q.projectedFiveHour.toFixed(0)}%{/${pc}-fg}`;
20497
+ })() : "";
20498
+ lines.push(` {grey-fg}5h{/grey-fg} ${fiveBar} {bold}${q.fiveHour.utilization.toFixed(0)}%{/bold}${fiveProj}`);
20473
20499
  const sevenColor = getUtilizationColor(q.sevenDay.utilization);
20474
20500
  const sevenBar = makeColorBar(q.sevenDay.utilization, 18, sevenColor);
20475
- lines.push(` {grey-fg}7d{/grey-fg} ${sevenBar} {bold}${q.sevenDay.utilization.toFixed(0)}%{/bold}`);
20501
+ const sevenProj = q.projectedSevenDay != null ? (() => {
20502
+ const pc = getUtilizationColor(q.projectedSevenDay);
20503
+ return ` {grey-fg}\u2192{/grey-fg} {${pc}-fg}${q.projectedSevenDay.toFixed(0)}%{/${pc}-fg}`;
20504
+ })() : "";
20505
+ lines.push(` {grey-fg}7d{/grey-fg} ${sevenBar} {bold}${q.sevenDay.utilization.toFixed(0)}%{/bold}${sevenProj}`);
20476
20506
  }
20477
20507
  const attrLines = renderContextAttribution(m.contextAttribution);
20478
20508
  if (attrLines.length > 0) {
@@ -59446,7 +59476,7 @@ function StatusBar({
59446
59476
  ] }),
59447
59477
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { dimColor: true, children: [
59448
59478
  " v",
59449
- "0.12.10"
59479
+ "0.13.1"
59450
59480
  ] }),
59451
59481
  updateInfo && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { color: "yellow", children: [
59452
59482
  " (v",
@@ -59829,7 +59859,7 @@ function ChangelogOverlay({ entries, scrollOffset }) {
59829
59859
  " ",
59830
59860
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { bold: true, color: "cyan", children: [
59831
59861
  "Terminal Dashboard v",
59832
- "0.12.10"
59862
+ "0.13.1"
59833
59863
  ] }),
59834
59864
  latestDate ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { color: "gray", children: [
59835
59865
  " \u2014 ",
@@ -60184,7 +60214,7 @@ var init_mouse = __esm({
60184
60214
  var CHANGELOG_default;
60185
60215
  var init_CHANGELOG = __esm({
60186
60216
  "CHANGELOG.md"() {
60187
- CHANGELOG_default = '# Changelog\n\nAll notable changes to the Sidekick Agent Hub CLI will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [0.12.10] - 2026-03-01\n\n### Added\n\n- **Events Panel** (key 7): Scrollable live event stream with colored type badges (`[USR]`, `[AST]`, `[TOOL]`, `[RES]`), timestamps, and keyword-highlighted summaries; detail tabs for full event JSON and surrounding context\n- **Charts Panel** (key 8): Tool frequency horizontal bars, event type distribution, 60-minute activity heatmap using `\u2591\u2592\u2593\u2588` intensity characters, and pattern analysis with frequency bars and template text\n- **Multi-Mode Filter**: `/` filter overlay now supports four modes \u2014 substring, fuzzy, regex, and date range \u2014 Tab cycles modes, regex mode shows red validation errors\n- **Search Term Highlighting**: Active filter terms highlighted in blue within side list items\n- **Timeline Keyword Coloring**: Event summaries in the Sessions panel Timeline tab now use semantic keyword coloring \u2014 errors red, success green, tool names cyan, file paths magenta\n\n### Removed\n\n- **Search Panel**: Removed redundant Search panel (previously key 7) \u2014 the `/` filter with multi-mode support serves the same purpose\n\n## [0.12.9] - 2026-02-28\n\n### Added\n\n- **Standalone Data Commands**: `sidekick tasks`, `sidekick decisions`, `sidekick notes`, `sidekick stats`, `sidekick handoff` for accessing project data without launching the TUI\n- **`sidekick search <query>`**: Cross-session full-text search from the terminal\n- **`sidekick context`**: Composite output of tasks, decisions, notes, and handoff for piping into other tools\n- **`--list` flag on `sidekick dump`**: Discover available session IDs before requiring `--session <id>`\n- **Search Panel**: Search panel (panel 7) wired into the TUI dashboard\n\n### Changed\n\n- **`taskMerger` utility**: Duplicate `mergeTasks` logic extracted into shared `taskMerger` utility\n- **Model constants**: Hardcoded model IDs extracted to named constants\n\n### Fixed\n\n- **`convention` icon**: Notes panel icon replaced with valid `tip` type\n- **Linux clipboard**: Now supports Wayland (`wl-copy`) and `xsel` fallbacks, with error messages instead of silent failure\n- **`provider.dispose()`**: Added to `dump` and `report` commands (prevents SQLite connection leaks)\n\n## [0.12.8] - 2026-02-28\n\n### Changed\n\n- **Dashboard UI/UX Polish**: Visual overhaul for better hierarchy, consistency, and readability\n - Splash screen and help overlay now display the robot ASCII logo\n - Toast notifications show severity icons (\u2718 error, \u26A0 warning, \u25CF info) with inner padding\n - Focused pane uses double-border for clear focus indication\n - Section dividers (`\u2500\u2500 Title \u2500\u2500\u2500\u2500`) replace bare bold headers in summary, agents, and context attribution\n - Tab bar: active tab underlined in magenta, inactive tabs dimmed, bracket syntax removed\n - Status bar: segmented layout with `\u2502` separators; keys bold, labels dim\n - Summary metrics condensed: elapsed/events/compactions on one line, tokens on one line with cache rate and cost\n - Sparklines display peak metadata annotations\n - Progress bars use blessed color tags for consistent coloring\n - Help overlay uses dot-leader alignment for all keybinding rows\n - Empty state hints per panel (e.g. "Tasks appear as your agent works.")\n - Session picker groups sessions by provider with section headers when multiple providers are present\n\n## [0.12.7] - 2026-02-27\n\n### Added\n\n- **HTML Session Report**: `sidekick report` command generates a self-contained HTML report and opens it in the default browser\n - Options: `--session`, `--output`, `--theme` (dark/light), `--no-open`, `--no-thinking`\n - TUI Dashboard: press `r` to generate and open an HTML report for the current session\n\n## [0.12.6] - 2026-02-26\n\n### Added\n\n- **Session Dump Command**: `sidekick dump` exports session data in text, markdown, or JSON format with `--format`, `--width`, and `--expand` options\n- **Plans Panel Re-enabled**: Plans panel restored in CLI dashboard with plan file discovery from `~/.claude/plans/`\n- **Enhanced Status Bar**: Session info display improved with richer metadata\n\n### Fixed\n\n- **Old snapshot format migration**: Restoring pre-0.12.3 session snapshots no longer shows empty timeline entries\n\n### Changed\n\n- **Phrase library moved to shared**: CLI-specific phrase formatting kept local, all phrase content now from `sidekick-shared`\n\n## [0.12.5] - 2026-02-24\n\n### Fixed\n\n- **Update check too slow to notice new versions**: Reduced npm registry cache TTL from 24 hours to 4 hours so upgrade notices appear sooner after a new release\n\n## [0.12.4] - 2026-02-24\n\n### Fixed\n\n- **Session crash on upgrade**: Fixed `d.timestamp.getTime is not a function` error when restoring tool call data from session snapshots \u2014 `Date` objects were serialized to strings by JSON but not rehydrated on restore, causing the session monitor to crash on first run after upgrading from 0.12.2 to 0.12.3\n\n## [0.12.3] - 2026-02-24\n\n### Added\n\n- **Latest-node indicator**: The most recently added node in tree and boxed mind map views is now marked with a yellow indicator\n- **Plan analytics in mind map**: Tree and boxed views now display plan progress and per-step metrics\n - Tree view: plan header shows completion stats; steps show complexity, duration, tokens, tool calls, and errors in metadata brackets\n - Box view: progress bar with completion percentage; steps show right-aligned metrics; subtitle shows step count and total duration\n- **Cross-provider plan extraction**: Shared `PlanExtractor` now handles Claude Code (EnterPlanMode/ExitPlanMode) and OpenCode (`<proposed_plan>` XML) plans \u2014 previously only Codex plans were shown\n- **Enriched plan data model**: Plan steps include duration, token count, tool call count, and error messages\n- **Phase-grouped plan display**: When a plan has phase structure, tree and boxed views group steps under phase headers with context lines from the original plan markdown\n- **Node type filter**: Press `f` on the Mind Map tab to cycle through node type filters (file, tool, task, subagent, command, plan, knowledge-note) \u2014 non-matching sections render dimmed in grey\n\n### Fixed\n\n- **Kanban board regression**: Subagent and plan-step tasks now correctly appear in the kanban board\n\n### Changed\n\n- **Plans panel temporarily disabled**: The Plans panel in the CLI dashboard is disabled until plan-mode event capture is reliably working end-to-end. Plan nodes in the mind map remain active.\n- `DashboardState` now delegates to shared `EventAggregator` instead of maintaining its own aggregation logic\n\n## [0.12.2] - 2026-02-23\n\n### Added\n\n- **Update notifications**: The dashboard now checks the npm registry for newer versions on startup and shows a yellow banner in the status bar when an update is available (e.g., `v0.13.0 available \u2014 npm i -g sidekick-agent-hub`). Results are cached for 24 hours to avoid repeated network requests.\n\n## [0.12.1] - 2026-02-23\n\n### Fixed\n\n- **VS Code integration**: Fixed exit code 127 when the extension launches the CLI dashboard on systems using nvm or volta (node binary not found when shell init is bypassed)\n\n## [0.12.0] - 2026-02-22\n\n### Added\n\n- **"Open CLI Dashboard" VS Code Integration**: New VS Code command `Sidekick: Open CLI Dashboard` launches the TUI dashboard in an integrated terminal\n - Install the CLI with `npm install -g sidekick-agent-hub`\n\n## [0.11.0] - 2026-02-19\n\n### Added\n\n- **Initial Release**: Full-screen TUI dashboard for monitoring agent sessions from the terminal\n - Ink-based terminal UI with panels for sessions, tasks, kanban, mind map, notes, decisions, search, files, and git diff\n - Multi-provider support: auto-detects Claude Code, OpenCode, and Codex sessions\n - Reads from `~/.config/sidekick/` \u2014 the same data files the VS Code extension writes\n - Usage: `sidekick dashboard [--project <path>] [--provider <id>]`\n';
60217
+ CHANGELOG_default = '# Changelog\n\nAll notable changes to the Sidekick Agent Hub CLI will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [0.13.1] - 2026-03-04\n\n### Added\n\n- **`sidekick quota` Command**: One-shot subscription quota check showing 5-hour and 7-day utilization with color-coded progress bars and reset countdowns \u2014 supports `--json` for machine-readable output\n- **Quota Projections**: Elapsed-time projections shown in `sidekick quota` output and TUI dashboard quota section \u2014 displays projected end-of-window utilization next to current value (e.g., `40% \u2192 100%`), included in `--json` output as `projectedFiveHour` / `projectedSevenDay`\n\n## [0.13.0] - 2026-03-03\n\n_No CLI-specific changes in this release._\n\n## [0.12.10] - 2026-03-01\n\n### Added\n\n- **Events Panel** (key 7): Scrollable live event stream with colored type badges (`[USR]`, `[AST]`, `[TOOL]`, `[RES]`), timestamps, and keyword-highlighted summaries; detail tabs for full event JSON and surrounding context\n- **Charts Panel** (key 8): Tool frequency horizontal bars, event type distribution, 60-minute activity heatmap using `\u2591\u2592\u2593\u2588` intensity characters, and pattern analysis with frequency bars and template text\n- **Multi-Mode Filter**: `/` filter overlay now supports four modes \u2014 substring, fuzzy, regex, and date range \u2014 Tab cycles modes, regex mode shows red validation errors\n- **Search Term Highlighting**: Active filter terms highlighted in blue within side list items\n- **Timeline Keyword Coloring**: Event summaries in the Sessions panel Timeline tab now use semantic keyword coloring \u2014 errors red, success green, tool names cyan, file paths magenta\n\n### Removed\n\n- **Search Panel**: Removed redundant Search panel (previously key 7) \u2014 the `/` filter with multi-mode support serves the same purpose\n\n## [0.12.9] - 2026-02-28\n\n### Added\n\n- **Standalone Data Commands**: `sidekick tasks`, `sidekick decisions`, `sidekick notes`, `sidekick stats`, `sidekick handoff` for accessing project data without launching the TUI\n- **`sidekick search <query>`**: Cross-session full-text search from the terminal\n- **`sidekick context`**: Composite output of tasks, decisions, notes, and handoff for piping into other tools\n- **`--list` flag on `sidekick dump`**: Discover available session IDs before requiring `--session <id>`\n- **Search Panel**: Search panel (panel 7) wired into the TUI dashboard\n\n### Changed\n\n- **`taskMerger` utility**: Duplicate `mergeTasks` logic extracted into shared `taskMerger` utility\n- **Model constants**: Hardcoded model IDs extracted to named constants\n\n### Fixed\n\n- **`convention` icon**: Notes panel icon replaced with valid `tip` type\n- **Linux clipboard**: Now supports Wayland (`wl-copy`) and `xsel` fallbacks, with error messages instead of silent failure\n- **`provider.dispose()`**: Added to `dump` and `report` commands (prevents SQLite connection leaks)\n\n## [0.12.8] - 2026-02-28\n\n### Changed\n\n- **Dashboard UI/UX Polish**: Visual overhaul for better hierarchy, consistency, and readability\n - Splash screen and help overlay now display the robot ASCII logo\n - Toast notifications show severity icons (\u2718 error, \u26A0 warning, \u25CF info) with inner padding\n - Focused pane uses double-border for clear focus indication\n - Section dividers (`\u2500\u2500 Title \u2500\u2500\u2500\u2500`) replace bare bold headers in summary, agents, and context attribution\n - Tab bar: active tab underlined in magenta, inactive tabs dimmed, bracket syntax removed\n - Status bar: segmented layout with `\u2502` separators; keys bold, labels dim\n - Summary metrics condensed: elapsed/events/compactions on one line, tokens on one line with cache rate and cost\n - Sparklines display peak metadata annotations\n - Progress bars use blessed color tags for consistent coloring\n - Help overlay uses dot-leader alignment for all keybinding rows\n - Empty state hints per panel (e.g. "Tasks appear as your agent works.")\n - Session picker groups sessions by provider with section headers when multiple providers are present\n\n## [0.12.7] - 2026-02-27\n\n### Added\n\n- **HTML Session Report**: `sidekick report` command generates a self-contained HTML report and opens it in the default browser\n - Options: `--session`, `--output`, `--theme` (dark/light), `--no-open`, `--no-thinking`\n - TUI Dashboard: press `r` to generate and open an HTML report for the current session\n\n## [0.12.6] - 2026-02-26\n\n### Added\n\n- **Session Dump Command**: `sidekick dump` exports session data in text, markdown, or JSON format with `--format`, `--width`, and `--expand` options\n- **Plans Panel Re-enabled**: Plans panel restored in CLI dashboard with plan file discovery from `~/.claude/plans/`\n- **Enhanced Status Bar**: Session info display improved with richer metadata\n\n### Fixed\n\n- **Old snapshot format migration**: Restoring pre-0.12.3 session snapshots no longer shows empty timeline entries\n\n### Changed\n\n- **Phrase library moved to shared**: CLI-specific phrase formatting kept local, all phrase content now from `sidekick-shared`\n\n## [0.12.5] - 2026-02-24\n\n### Fixed\n\n- **Update check too slow to notice new versions**: Reduced npm registry cache TTL from 24 hours to 4 hours so upgrade notices appear sooner after a new release\n\n## [0.12.4] - 2026-02-24\n\n### Fixed\n\n- **Session crash on upgrade**: Fixed `d.timestamp.getTime is not a function` error when restoring tool call data from session snapshots \u2014 `Date` objects were serialized to strings by JSON but not rehydrated on restore, causing the session monitor to crash on first run after upgrading from 0.12.2 to 0.12.3\n\n## [0.12.3] - 2026-02-24\n\n### Added\n\n- **Latest-node indicator**: The most recently added node in tree and boxed mind map views is now marked with a yellow indicator\n- **Plan analytics in mind map**: Tree and boxed views now display plan progress and per-step metrics\n - Tree view: plan header shows completion stats; steps show complexity, duration, tokens, tool calls, and errors in metadata brackets\n - Box view: progress bar with completion percentage; steps show right-aligned metrics; subtitle shows step count and total duration\n- **Cross-provider plan extraction**: Shared `PlanExtractor` now handles Claude Code (EnterPlanMode/ExitPlanMode) and OpenCode (`<proposed_plan>` XML) plans \u2014 previously only Codex plans were shown\n- **Enriched plan data model**: Plan steps include duration, token count, tool call count, and error messages\n- **Phase-grouped plan display**: When a plan has phase structure, tree and boxed views group steps under phase headers with context lines from the original plan markdown\n- **Node type filter**: Press `f` on the Mind Map tab to cycle through node type filters (file, tool, task, subagent, command, plan, knowledge-note) \u2014 non-matching sections render dimmed in grey\n\n### Fixed\n\n- **Kanban board regression**: Subagent and plan-step tasks now correctly appear in the kanban board\n\n### Changed\n\n- **Plans panel temporarily disabled**: The Plans panel in the CLI dashboard is disabled until plan-mode event capture is reliably working end-to-end. Plan nodes in the mind map remain active.\n- `DashboardState` now delegates to shared `EventAggregator` instead of maintaining its own aggregation logic\n\n## [0.12.2] - 2026-02-23\n\n### Added\n\n- **Update notifications**: The dashboard now checks the npm registry for newer versions on startup and shows a yellow banner in the status bar when an update is available (e.g., `v0.13.0 available \u2014 npm i -g sidekick-agent-hub`). Results are cached for 24 hours to avoid repeated network requests.\n\n## [0.12.1] - 2026-02-23\n\n### Fixed\n\n- **VS Code integration**: Fixed exit code 127 when the extension launches the CLI dashboard on systems using nvm or volta (node binary not found when shell init is bypassed)\n\n## [0.12.0] - 2026-02-22\n\n### Added\n\n- **"Open CLI Dashboard" VS Code Integration**: New VS Code command `Sidekick: Open CLI Dashboard` launches the TUI dashboard in an integrated terminal\n - Install the CLI with `npm install -g sidekick-agent-hub`\n\n## [0.11.0] - 2026-02-19\n\n### Added\n\n- **Initial Release**: Full-screen TUI dashboard for monitoring agent sessions from the terminal\n - Ink-based terminal UI with panels for sessions, tasks, kanban, mind map, notes, decisions, search, files, and git diff\n - Multi-provider support: auto-detects Claude Code, OpenCode, and Codex sessions\n - Reads from `~/.config/sidekick/` \u2014 the same data files the VS Code extension writes\n - Usage: `sidekick dashboard [--project <path>] [--provider <id>]`\n';
60188
60218
  }
60189
60219
  });
60190
60220
 
@@ -62183,6 +62213,92 @@ var init_stats = __esm({
62183
62213
  }
62184
62214
  });
62185
62215
 
62216
+ // src/commands/quota.ts
62217
+ var quota_exports = {};
62218
+ __export(quota_exports, {
62219
+ formatTimeUntil: () => formatTimeUntil,
62220
+ getUtilizationColor: () => getUtilizationColor2,
62221
+ makeChalkBar: () => makeChalkBar,
62222
+ quotaAction: () => quotaAction
62223
+ });
62224
+ function getUtilizationColor2(percent) {
62225
+ if (percent < 60) return source_default.green;
62226
+ if (percent < 80) return source_default.yellow;
62227
+ return source_default.red;
62228
+ }
62229
+ function makeChalkBar(percent, width) {
62230
+ const filled = Math.round(percent / 100 * width);
62231
+ const empty = width - filled;
62232
+ const color = getUtilizationColor2(percent);
62233
+ return color("\u2588".repeat(filled)) + source_default.dim("\u2591".repeat(empty));
62234
+ }
62235
+ function formatTimeUntil(isoString) {
62236
+ if (!isoString) return "";
62237
+ const ms = new Date(isoString).getTime() - Date.now();
62238
+ if (ms <= 0) return "now";
62239
+ const totalMinutes = Math.floor(ms / 6e4);
62240
+ const days = Math.floor(totalMinutes / 1440);
62241
+ const hours = Math.floor(totalMinutes % 1440 / 60);
62242
+ const minutes = totalMinutes % 60;
62243
+ const parts = [];
62244
+ if (days > 0) parts.push(`${days}d`);
62245
+ if (hours > 0) parts.push(`${hours}h`);
62246
+ if (minutes > 0 && days === 0) parts.push(`${minutes}m`);
62247
+ return "in " + (parts.join(" ") || "0m");
62248
+ }
62249
+ async function quotaAction(_opts, cmd) {
62250
+ const globalOpts = cmd.parent.opts();
62251
+ const jsonOutput = !!globalOpts.json;
62252
+ const service = new QuotaService();
62253
+ const quota = await service.fetchOnce();
62254
+ if (!quota.available) {
62255
+ if (jsonOutput) {
62256
+ process.stdout.write(JSON.stringify(quota, null, 2) + "\n");
62257
+ return;
62258
+ }
62259
+ let msg;
62260
+ switch (quota.error) {
62261
+ case "no-credentials":
62262
+ msg = "No Claude Code credentials found. Sign in with `claude` first.";
62263
+ break;
62264
+ case "auth-failed":
62265
+ msg = "Authentication failed. Try signing in to Claude Code again.";
62266
+ break;
62267
+ case "network-error":
62268
+ msg = "Could not reach the Anthropic API. Check your connection.";
62269
+ break;
62270
+ default:
62271
+ msg = quota.error ?? "Unknown error fetching quota.";
62272
+ }
62273
+ process.stderr.write(source_default.red(msg) + "\n");
62274
+ process.exit(1);
62275
+ }
62276
+ if (jsonOutput) {
62277
+ process.stdout.write(JSON.stringify(quota, null, 2) + "\n");
62278
+ return;
62279
+ }
62280
+ const barWidth = 30;
62281
+ const fivePct = Math.round(quota.fiveHour.utilization);
62282
+ const sevenPct = Math.round(quota.sevenDay.utilization);
62283
+ const fiveReset = formatTimeUntil(quota.fiveHour.resetsAt);
62284
+ const sevenReset = formatTimeUntil(quota.sevenDay.resetsAt);
62285
+ const fiveProj = quota.projectedFiveHour != null ? ` ${source_default.dim("\u2192")} ${getUtilizationColor2(quota.projectedFiveHour)(String(Math.round(quota.projectedFiveHour)).padStart(3) + "%")}` : "";
62286
+ const sevenProj = quota.projectedSevenDay != null ? ` ${source_default.dim("\u2192")} ${getUtilizationColor2(quota.projectedSevenDay)(String(Math.round(quota.projectedSevenDay)).padStart(3) + "%")}` : "";
62287
+ process.stdout.write(source_default.bold("Subscription Quota\n"));
62288
+ process.stdout.write(source_default.dim("\u2500".repeat(50) + "\n"));
62289
+ process.stdout.write(` ${source_default.dim("5-Hour")} ${makeChalkBar(fivePct, barWidth)} ${String(fivePct).padStart(3)}%${fiveProj} ${source_default.dim("resets " + fiveReset)}
62290
+ `);
62291
+ process.stdout.write(` ${source_default.dim("7-Day")} ${makeChalkBar(sevenPct, barWidth)} ${String(sevenPct).padStart(3)}%${sevenProj} ${source_default.dim("resets " + sevenReset)}
62292
+ `);
62293
+ }
62294
+ var init_quota = __esm({
62295
+ "src/commands/quota.ts"() {
62296
+ "use strict";
62297
+ init_source();
62298
+ init_QuotaService();
62299
+ }
62300
+ });
62301
+
62186
62302
  // src/commands/handoff.ts
62187
62303
  var handoff_exports = {};
62188
62304
  __export(handoff_exports, {
@@ -62246,14 +62362,14 @@ function resolveProvider(opts) {
62246
62362
  return new import_sidekick_shared23.ClaudeCodeProvider();
62247
62363
  }
62248
62364
  }
62249
- var import_sidekick_shared22, import_sidekick_shared23, program2, dashCmd, dumpCmd, ctxCmd, reportCmd, searchCmd, tasksCmd, decisionsCmd, notesCmd, statsCmd, handoffCmd;
62365
+ var import_sidekick_shared22, import_sidekick_shared23, program2, dashCmd, dumpCmd, ctxCmd, reportCmd, searchCmd, tasksCmd, decisionsCmd, notesCmd, statsCmd, quotaCmd, handoffCmd;
62250
62366
  var init_cli = __esm({
62251
62367
  "src/cli.ts"() {
62252
62368
  init_esm();
62253
62369
  import_sidekick_shared22 = __toESM(require_dist(), 1);
62254
62370
  import_sidekick_shared23 = __toESM(require_dist(), 1);
62255
62371
  program2 = new Command();
62256
- program2.name("sidekick").description("Query Sidekick project intelligence from the command line").version("0.12.10").option("--json", "Output as JSON").option("--project <path>", "Override project path (default: cwd)").option("--provider <id>", "Provider: claude-code, opencode, codex, auto (default: auto)");
62372
+ program2.name("sidekick").description("Query Sidekick project intelligence from the command line").version("0.13.1").option("--json", "Output as JSON").option("--project <path>", "Override project path (default: cwd)").option("--provider <id>", "Provider: claude-code, opencode, codex, auto (default: auto)");
62257
62373
  dashCmd = new Command("dashboard").description("Full-screen TUI dashboard with live session metrics").option("--session <id>", "Follow a specific session (default: most recent)").option("--replay", "Replay existing events before streaming new ones").action(async (_opts, cmd) => {
62258
62374
  const { dashboardAction: dashboardAction2 } = await init_dashboard().then(() => dashboard_exports);
62259
62375
  return dashboardAction2(_opts, cmd);
@@ -62300,6 +62416,11 @@ var init_cli = __esm({
62300
62416
  return statsAction2(_opts, cmd);
62301
62417
  });
62302
62418
  program2.addCommand(statsCmd);
62419
+ quotaCmd = new Command("quota").description("Show subscription quota utilization (Claude Max / Claude Code)").action(async (_opts, cmd) => {
62420
+ const { quotaAction: quotaAction2 } = await Promise.resolve().then(() => (init_quota(), quota_exports));
62421
+ return quotaAction2(_opts, cmd);
62422
+ });
62423
+ program2.addCommand(quotaCmd);
62303
62424
  handoffCmd = new Command("handoff").description("Show the latest session handoff document for the current project").action(async (_opts, cmd) => {
62304
62425
  const { handoffAction: handoffAction2 } = await Promise.resolve().then(() => (init_handoff(), handoff_exports));
62305
62426
  return handoffAction2(_opts, cmd);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidekick-agent-hub",
3
- "version": "0.12.10",
3
+ "version": "0.13.1",
4
4
  "description": "Terminal dashboard for monitoring AI coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {