sidekick-agent-hub 0.17.4 → 0.17.6

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
@@ -95,6 +95,16 @@ sidekick status
95
95
 
96
96
  Check API health for both Claude (status.claude.com) and OpenAI (status.openai.com). Shows indicators with color coding (green/yellow/red), affected components, and active incident details. Use `--json` for machine-readable output. In the dashboard, provider-status surfaces are scoped to the monitored provider: Claude for Claude Code sessions, OpenAI for Codex sessions, and hidden for OpenCode.
97
97
 
98
+ When the active provider is `claude-code`, the output also includes a **Claude Peak Hours** block (see below).
99
+
100
+ ## Peak Hours
101
+
102
+ ```bash
103
+ sidekick peak
104
+ ```
105
+
106
+ Show whether Claude is currently in peak hours (weekdays 13:00–19:00 UTC — when session limits drain faster on Free/Pro/Max/Team subscriptions). Data comes from the public `promoclock.co/api/status` endpoint (third-party, unaffiliated with Anthropic). Use `--json` for machine-readable output. The peak-hours summary also appears under the bars in `sidekick quota` for Claude subscriptions.
107
+
98
108
  ## Quota & Rate Limits
99
109
 
100
110
  ```bash
@@ -103,7 +113,7 @@ sidekick quota
103
113
 
104
114
  Provider-aware quota and rate-limit display. The command auto-detects the active provider:
105
115
 
106
- - **Claude Code**: Shows Claude Max subscription quota — 5-hour and 7-day windows with color-coded progress bars, projections, and reset countdowns.
116
+ - **Claude Code**: Shows Claude Max subscription quota — 5-hour and 7-day windows with color-coded progress bars, projections, and reset countdowns. Includes a peak-hours summary line.
107
117
  - **Codex**: Shows rate limits from the latest session's event stream — primary and secondary windows with progress bars and reset countdowns.
108
118
  - **OpenCode**: Prints an informational message (no rate-limit data available).
109
119
 
@@ -128,6 +138,8 @@ sidekick account [options]
128
138
 
129
139
  Manage accounts across providers — save, list, switch, and remove without manual login/logout cycles. Supports Claude Code and Codex profiles. Account data is stored in `~/.config/sidekick/accounts/` with strict file permissions and atomic writes with rollback on failure.
130
140
 
141
+ On first CLI startup, Sidekick auto-registers the active system Claude Code and Codex credentials as a **"Default"** account (when no saved account exists for that provider yet). Existing manually saved accounts are never overwritten — the flags below are only needed to add additional accounts or switch between them.
142
+
131
143
  | Flag | Description |
132
144
  |------|-------------|
133
145
  | `--provider <id>` | Provider: `claude-code` (default) or `codex` |
@@ -18022,6 +18022,112 @@ var require_accounts = __commonJS({
18022
18022
  }
18023
18023
  });
18024
18024
 
18025
+ // ../sidekick-shared/dist/ensureDefaultAccounts.js
18026
+ var require_ensureDefaultAccounts = __commonJS({
18027
+ "../sidekick-shared/dist/ensureDefaultAccounts.js"(exports) {
18028
+ "use strict";
18029
+ var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
18030
+ if (k2 === void 0) k2 = k;
18031
+ var desc = Object.getOwnPropertyDescriptor(m, k);
18032
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18033
+ desc = { enumerable: true, get: function() {
18034
+ return m[k];
18035
+ } };
18036
+ }
18037
+ Object.defineProperty(o, k2, desc);
18038
+ } : function(o, m, k, k2) {
18039
+ if (k2 === void 0) k2 = k;
18040
+ o[k2] = m[k];
18041
+ });
18042
+ var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
18043
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18044
+ } : function(o, v) {
18045
+ o["default"] = v;
18046
+ });
18047
+ var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
18048
+ var ownKeys = function(o) {
18049
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
18050
+ var ar = [];
18051
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
18052
+ return ar;
18053
+ };
18054
+ return ownKeys(o);
18055
+ };
18056
+ return function(mod) {
18057
+ if (mod && mod.__esModule) return mod;
18058
+ var result = {};
18059
+ if (mod != null) {
18060
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
18061
+ }
18062
+ __setModuleDefault(result, mod);
18063
+ return result;
18064
+ };
18065
+ }();
18066
+ Object.defineProperty(exports, "__esModule", { value: true });
18067
+ exports.ensureDefaultAccounts = ensureDefaultAccounts2;
18068
+ var fs9 = __importStar(__require("fs"));
18069
+ var path8 = __importStar(__require("path"));
18070
+ var accounts_1 = require_accounts();
18071
+ var accountRegistry_1 = require_accountRegistry();
18072
+ var codexProfiles_1 = require_codexProfiles();
18073
+ var credentials_1 = require_credentials();
18074
+ function logFailure(options, message, error) {
18075
+ try {
18076
+ options?.logger?.(message, error);
18077
+ } catch {
18078
+ }
18079
+ }
18080
+ async function ensureDefaultClaudeAccount(options) {
18081
+ try {
18082
+ if ((0, accountRegistry_1.getActiveSavedAccount)("claude-code"))
18083
+ return "skipped";
18084
+ const active = (0, accounts_1.readActiveClaudeAccount)();
18085
+ if (!active)
18086
+ return "skipped";
18087
+ const credentials = await (0, credentials_1.readClaudeMaxCredentials)();
18088
+ if (!credentials)
18089
+ return "skipped";
18090
+ const result = (0, accounts_1.addCurrentAccount)("Default");
18091
+ if (result.success)
18092
+ return "registered";
18093
+ logFailure(options, "Claude default account registration failed.", result.error ?? "unknown error");
18094
+ return "error";
18095
+ } catch (error) {
18096
+ logFailure(options, "Claude default account registration failed.", error);
18097
+ return "error";
18098
+ }
18099
+ }
18100
+ function cleanupPendingCodexProfile(profileId) {
18101
+ fs9.rmSync(path8.join((0, codexProfiles_1.getCodexProfilesDir)(), profileId), { recursive: true, force: true });
18102
+ }
18103
+ function ensureDefaultCodexAccount(options) {
18104
+ try {
18105
+ if ((0, codexProfiles_1.getActiveCodexAccount)())
18106
+ return "skipped";
18107
+ const systemAuthPath = path8.join((0, codexProfiles_1.getSystemCodexHome)(), "auth.json");
18108
+ if (!fs9.existsSync(systemAuthPath))
18109
+ return "skipped";
18110
+ const result = (0, codexProfiles_1.prepareCodexAccount)("Default");
18111
+ if (result.success && !result.needsLogin)
18112
+ return "registered";
18113
+ if (result.profileId) {
18114
+ cleanupPendingCodexProfile(result.profileId);
18115
+ }
18116
+ logFailure(options, "Codex default account registration failed.", result.error ?? "Codex auth could not be finalized.");
18117
+ return "error";
18118
+ } catch (error) {
18119
+ logFailure(options, "Codex default account registration failed.", error);
18120
+ return "error";
18121
+ }
18122
+ }
18123
+ async function ensureDefaultAccounts2(options) {
18124
+ const claude = await ensureDefaultClaudeAccount(options);
18125
+ const codex = ensureDefaultCodexAccount(options);
18126
+ return { claude, codex };
18127
+ }
18128
+ }
18129
+ });
18130
+
18025
18131
  // ../sidekick-shared/dist/quota.js
18026
18132
  var require_quota = __commonJS({
18027
18133
  "../sidekick-shared/dist/quota.js"(exports) {
@@ -34823,6 +34929,65 @@ var require_providerStatus = __commonJS({
34823
34929
  }
34824
34930
  });
34825
34931
 
34932
+ // ../sidekick-shared/dist/peakHours.js
34933
+ var require_peakHours = __commonJS({
34934
+ "../sidekick-shared/dist/peakHours.js"(exports) {
34935
+ "use strict";
34936
+ Object.defineProperty(exports, "__esModule", { value: true });
34937
+ exports.fetchPeakHoursStatus = fetchPeakHoursStatus4;
34938
+ var PROMOCLOCK_ENDPOINT = "https://promoclock.co/api/status";
34939
+ function unavailableState() {
34940
+ return {
34941
+ status: "unknown",
34942
+ isPeak: false,
34943
+ sessionLimitSpeed: "unknown",
34944
+ label: "Peak-hours status unavailable",
34945
+ peakHoursDescription: "",
34946
+ nextChange: null,
34947
+ minutesUntilChange: null,
34948
+ note: "",
34949
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
34950
+ unavailable: true
34951
+ };
34952
+ }
34953
+ function normalizeStatus(raw) {
34954
+ if (raw === "peak" || raw === "off_peak")
34955
+ return raw;
34956
+ return "unknown";
34957
+ }
34958
+ function normalizeSpeed(raw) {
34959
+ if (raw === "normal" || raw === "faster")
34960
+ return raw;
34961
+ return "unknown";
34962
+ }
34963
+ async function fetchPeakHoursStatus4() {
34964
+ try {
34965
+ const res = await fetch(PROMOCLOCK_ENDPOINT);
34966
+ if (!res.ok)
34967
+ return unavailableState();
34968
+ const data = await res.json();
34969
+ const status = normalizeStatus(data.status);
34970
+ const sessionLimitSpeed = normalizeSpeed(data.sessionLimitSpeed);
34971
+ const isPeak = typeof data.isPeak === "boolean" ? data.isPeak : status === "peak";
34972
+ return {
34973
+ status,
34974
+ isPeak,
34975
+ sessionLimitSpeed,
34976
+ label: data.label ?? (isPeak ? "Peak Hours" : "Off-Peak"),
34977
+ peakHoursDescription: data.peakHours ?? "",
34978
+ nextChange: data.nextChange ?? null,
34979
+ minutesUntilChange: typeof data.minutesUntilChange === "number" ? data.minutesUntilChange : null,
34980
+ note: data.note ?? "",
34981
+ updatedAt: data.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
34982
+ unavailable: false
34983
+ };
34984
+ } catch {
34985
+ return unavailableState();
34986
+ }
34987
+ }
34988
+ }
34989
+ });
34990
+
34826
34991
  // ../sidekick-shared/dist/index.js
34827
34992
  var require_dist = __commonJS({
34828
34993
  "../sidekick-shared/dist/index.js"(exports) {
@@ -34830,8 +34995,8 @@ var require_dist = __commonJS({
34830
34995
  Object.defineProperty(exports, "__esModule", { value: true });
34831
34996
  exports.findActiveClaudeSession = exports.discoverSessionDirectory = exports.getClaudeSessionDirectory = exports.encodeClaudeWorkspacePath = exports.detectSessionActivity = exports.extractTaskInfo = exports.scanSubagentDir = exports.normalizeCodexToolInput = exports.normalizeCodexToolName = exports.extractPatchFilePaths = exports.CodexRolloutParser = exports.parseDbPartData = exports.parseDbMessageData = exports.convertOpenCodeMessage = exports.detectPlanModeFromText = exports.normalizeToolInput = exports.normalizeToolName = exports.TRUNCATION_PATTERNS = exports.JsonlParser = exports.CodexProvider = exports.OpenCodeProvider = exports.ClaudeCodeProvider = exports.getAllDetectedProviders = exports.detectProvider = exports.readClaudeCodePlanFiles = exports.getPlanAnalytics = exports.writePlans = exports.getLatestPlan = exports.readPlans = exports.readLatestHandoff = exports.readHistory = exports.readNotes = exports.readDecisions = exports.readTasks = exports.getProjectSlugRaw = exports.getProjectSlug = exports.encodeWorkspacePath = exports.getGlobalDataPath = exports.getProjectDataPath = exports.getConfigDir = exports.MAX_PLANS_PER_PROJECT = exports.PLAN_SCHEMA_VERSION = exports.createEmptyTokenTotals = exports.HISTORICAL_DATA_SCHEMA_VERSION = exports.STALENESS_THRESHOLDS = exports.IMPORTANCE_DECAY_FACTORS = exports.KNOWLEDGE_NOTE_SCHEMA_VERSION = exports.DECISION_LOG_SCHEMA_VERSION = exports.normalizeTaskStatus = exports.TASK_PERSISTENCE_SCHEMA_VERSION = void 0;
34832
34997
  exports.HeatmapTracker = exports.FrequencyTracker = exports.getSnapshotPath = exports.isSnapshotValid = exports.deleteSnapshot = exports.loadSnapshot = exports.saveSnapshot = exports.parseTodoDependencies = exports.EventAggregator = exports.getRandomPhrase = exports.ALL_PHRASES = exports.HIGHLIGHT_CSS = exports.clearHighlightCache = exports.highlightEvent = exports.formatSessionJson = exports.formatSessionMarkdown = exports.formatSessionText = exports.classifyNoise = exports.shouldMergeWithPrevious = exports.classifyFollowEvent = exports.classifyMessage = exports.getSoftNoiseReason = exports.isHardNoiseFollowEvent = exports.isHardNoise = exports.formatToolSummary = exports.toFollowEvents = exports.createWatcher = exports.parseChangelog = exports.extractProposedPlanShared = exports.parsePlanMarkdownShared = exports.PlanExtractor = exports.composeContext = exports.FilterEngine = exports.searchSessions = exports.CodexDatabase = exports.OpenCodeDatabase = exports.discoverDebugLogs = exports.collapseDuplicates = exports.filterByLevel = exports.parseDebugLog = exports.scanSubagentTraces = exports.findAllSessionsWithWorktrees = exports.discoverWorktreeSiblings = exports.resolveWorktreeMainRepo = exports.getAllClaudeProjectFolders = exports.decodeEncodedPath = exports.getMostRecentlyActiveSessionDir = exports.findSubdirectorySessionDirs = exports.findSessionsInDirectory = exports.findAllClaudeSessions = void 0;
34833
- exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = exports.quotaFromCodexRateLimits = exports.writeQuotaSnapshot = exports.readQuotaSnapshot = exports.QuotaPoller = exports.describeQuotaFailure = exports.fetchQuota = exports.removeCodexAccount = exports.switchToCodexAccount = exports.finalizeCodexAccount = exports.prepareCodexAccount = exports.getCodexExecutionEnv = exports.resolveSidekickCodexHome = exports.getActiveCodexAccount = exports.listCodexAccounts = exports.getSystemCodexHome = exports.getCodexMonitoringHomes = exports.getCodexProfileHome = exports.getCodexProfilesDir = exports.removeSavedAccountProfile = exports.replaceSavedAccountProfiles = exports.setActiveSavedAccount = exports.upsertSavedAccountProfile = exports.getActiveSavedAccount = exports.listSavedAccountProfiles = exports.writeSavedAccountRegistry = exports.readSavedAccountRegistry = exports.getAccountsDir = exports.isMultiAccountEnabled = exports.getActiveAccount = exports.listAccounts = exports.removeAccount = exports.switchToAccount = exports.addCurrentAccount = exports.readActiveClaudeAccount = exports.writeAccountRegistry = exports.readAccountRegistry = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.writeActiveCredentials = exports.readActiveCredentials = exports.openInBrowser = exports.parseTranscript = exports.generateHtmlReport = exports.PatternExtractor = void 0;
34834
- exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = exports.messageUsageSchema = exports.extractToolCalls = exports.extractTokenUsage = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = exports.formatCost = exports.calculateCostWithPricing = void 0;
34998
+ exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = exports.quotaFromCodexRateLimits = exports.writeQuotaSnapshot = exports.readQuotaSnapshot = exports.QuotaPoller = exports.describeQuotaFailure = exports.fetchQuota = exports.removeCodexAccount = exports.switchToCodexAccount = exports.finalizeCodexAccount = exports.prepareCodexAccount = exports.getCodexExecutionEnv = exports.resolveSidekickCodexHome = exports.getActiveCodexAccount = exports.listCodexAccounts = exports.getSystemCodexHome = exports.getCodexMonitoringHomes = exports.getCodexProfileHome = exports.getCodexProfilesDir = exports.removeSavedAccountProfile = exports.replaceSavedAccountProfiles = exports.setActiveSavedAccount = exports.upsertSavedAccountProfile = exports.getActiveSavedAccount = exports.listSavedAccountProfiles = exports.writeSavedAccountRegistry = exports.readSavedAccountRegistry = exports.getAccountsDir = exports.isMultiAccountEnabled = exports.getActiveAccount = exports.listAccounts = exports.removeAccount = exports.switchToAccount = exports.addCurrentAccount = exports.readActiveClaudeAccount = exports.writeAccountRegistry = exports.readAccountRegistry = exports.ensureDefaultAccounts = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.writeActiveCredentials = exports.readActiveCredentials = exports.openInBrowser = exports.parseTranscript = exports.generateHtmlReport = exports.PatternExtractor = void 0;
34999
+ exports.fetchPeakHoursStatus = exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = exports.messageUsageSchema = exports.extractToolCalls = exports.extractTokenUsage = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = exports.formatCost = exports.calculateCostWithPricing = exports.calculateCost = void 0;
34835
35000
  var taskPersistence_1 = require_taskPersistence();
34836
35001
  Object.defineProperty(exports, "TASK_PERSISTENCE_SCHEMA_VERSION", { enumerable: true, get: function() {
34837
35002
  return taskPersistence_1.TASK_PERSISTENCE_SCHEMA_VERSION;
@@ -35202,6 +35367,10 @@ var require_dist = __commonJS({
35202
35367
  Object.defineProperty(exports, "readClaudeMaxAccessTokenSync", { enumerable: true, get: function() {
35203
35368
  return credentials_1.readClaudeMaxAccessTokenSync;
35204
35369
  } });
35370
+ var ensureDefaultAccounts_1 = require_ensureDefaultAccounts();
35371
+ Object.defineProperty(exports, "ensureDefaultAccounts", { enumerable: true, get: function() {
35372
+ return ensureDefaultAccounts_1.ensureDefaultAccounts;
35373
+ } });
35205
35374
  var accounts_1 = require_accounts();
35206
35375
  Object.defineProperty(exports, "readAccountRegistry", { enumerable: true, get: function() {
35207
35376
  return accounts_1.readAccountRegistry;
@@ -35382,6 +35551,10 @@ var require_dist = __commonJS({
35382
35551
  Object.defineProperty(exports, "fetchOpenAIStatus", { enumerable: true, get: function() {
35383
35552
  return providerStatus_1.fetchOpenAIStatus;
35384
35553
  } });
35554
+ var peakHours_1 = require_peakHours();
35555
+ Object.defineProperty(exports, "fetchPeakHoursStatus", { enumerable: true, get: function() {
35556
+ return peakHours_1.fetchPeakHoursStatus;
35557
+ } });
35385
35558
  }
35386
35559
  });
35387
35560
 
@@ -37617,7 +37790,7 @@ var init_UpdateCheckService = __esm({
37617
37790
  /** Run the update check (one-shot). */
37618
37791
  async check() {
37619
37792
  try {
37620
- const current = "0.17.4";
37793
+ const current = "0.17.6";
37621
37794
  const cached = this.readCache();
37622
37795
  let latest;
37623
37796
  if (cached && Date.now() - cached.checkedAt < CACHE_TTL_MS) {
@@ -78235,7 +78408,7 @@ function StatusBar({
78235
78408
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: parseBlessedTags(BRAND_INLINE) }),
78236
78409
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { dimColor: true, children: [
78237
78410
  " v",
78238
- "0.17.4"
78411
+ "0.17.6"
78239
78412
  ] }),
78240
78413
  updateInfo && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { color: "yellow", children: [
78241
78414
  " (v",
@@ -78625,7 +78798,7 @@ function ChangelogOverlay({ entries, scrollOffset }) {
78625
78798
  " ",
78626
78799
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { bold: true, color: "cyan", children: [
78627
78800
  "Terminal Dashboard v",
78628
- "0.17.4"
78801
+ "0.17.6"
78629
78802
  ] }),
78630
78803
  latestDate ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { color: "gray", children: [
78631
78804
  " \u2014 ",
@@ -78947,7 +79120,7 @@ var init_mouse = __esm({
78947
79120
  var CHANGELOG_default;
78948
79121
  var init_CHANGELOG = __esm({
78949
79122
  "CHANGELOG.md"() {
78950
- 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.17.4] - 2026-04-17\n\n### Changed\n\n- **Pricing hydration import migrated to `sidekick-shared/node`**: `cli.ts` now imports `hydratePricingCatalog` from the new Node-only subpath and keeps `detectProvider` on the package root. Runtime behavior is unchanged; the split makes the CLI\'s import surface self-documenting (hydration is explicitly a Node API) and aligns the CLI with the shared library\'s new versioned public API contract\n\n## [0.17.3] - 2026-04-17\n\n### Changed\n\n- **Version sync with the VS Code extension**: Republished to keep CLI, extension, and shared-library versions aligned after a cosmetic changelog fix in 0.17.3. No CLI code changes \u2014 functionally identical to 0.17.2\n\n## [0.17.2] - 2026-04-17\n\n### Added\n\n- **LiteLLM pricing hydration on startup**: The CLI now fetches the LiteLLM pricing catalog on startup and caches to `~/.config/sidekick/pricing-catalog.json` with a 24-hour TTL, 3s timeout, and stale-cache fallback \u2014 new model prices are picked up without a CLI upgrade\n- **Expanded pricing coverage**: GPT-4o, GPT-4.1, GPT-5.x, o1, o3, and o3-mini families are now priced alongside the existing Claude entries\n- **Real-dollar Codex / Claude Code costs**: `EventAggregator` computes cost from the pricing table when the session provider doesn\'t report one, so `sidekick` live dashboards now show actual dollars for Codex and Claude Code sessions\n- **`stats` footer lists unpriced models**: `sidekick stats` prints any models encountered with no pricing entry so missing coverage is visible\n\n### Fixed\n\n- **Context-gauge % wrong for Opus 4.7 (1M) and other new models**: The dashboard\'s context gauge was dividing by 200K for Claude Opus 4.7 (native 1M), inflating the displayed %. The shared model \u2192 context-window map now includes Opus/Sonnet 4.7 (1M), GPT-5.4 (1.05M), GPT-5.3-Codex (400K), and GPT-5.3-Codex-Spark (128K). Claude Code\'s `[1m]` suffix is now also honored as an explicit 1M marker\n- **Silent Sonnet-priced fallback for unknown models**: Codex, GPT-5.x, and o-series rows were being rendered at Sonnet rates. Unknown-model rows now render as `\u2014` in yellow instead of inventing a dollar figure\n\n### Changed\n\n- **`historical-data.json` schema v2**: reads `priced` flag and `unpricedModelIds` from records written by the latest VS Code extension; v1 records still read correctly\n\n## [0.17.1] - 2026-04-13\n\n### Fixed\n\n- **Codex multi-home session discovery**: Provider detection now scans all candidate Codex home directories, fixing missed sessions when the managed profile home is empty but the system `~/.codex/` has activity\n\n## [0.17.0] - 2026-04-13\n\n### Added\n\n- **Multi-provider account management**: `sidekick account` now supports `--provider codex` for Codex profile management alongside Claude Code accounts\n- **Codex account lifecycle**: `--add` prepares a profile and spawns `codex login`; `--switch-to` and `--remove` accept email, label, or profile ID\n- **Quota snapshot fallback**: `sidekick quota` for Codex shows cached rate-limit snapshots when no active session exists, with "cached from" timestamp\n\n### Fixed\n\n- **Email normalization**: Claude account lookup normalizes email case for reliable matching\n\n## [0.16.1] - 2026-03-27\n\n### Fixed\n\n- **Dashboard provider status scoping**: The TUI now shows degraded-service notices only for the monitored provider \u2014 Claude for Claude Code sessions, OpenAI for Codex sessions, and no status banner for OpenCode\n\n## [0.16.0] - 2026-03-23\n\n### Changed\n\n- **Consistent cost formatting**: All cost displays (`stats`, `context`, Sessions panel, narrative prompt) now use shared `formatCost()` with intelligent decimal precision (4 places for < $0.01, 2 otherwise)\n- **QuotaService**: Rewritten to wrap shared `QuotaPoller` with exponential backoff instead of manual polling loop\n- **modelContext**: Now re-exports `getModelInfo` from shared library alongside `getContextWindowSize`\n\n## [0.15.2] - 2026-03-18\n\n### Fixed\n\n- **CLI help descriptions**: Updated `quota` and `status` command descriptions to reflect provider-aware behavior\n- **`sidekick quota --provider`**: Added local `--provider` option so `sidekick quota --provider codex` works naturally\n\n## [0.15.0] - 2026-03-18\n\n### Added\n\n- **OpenAI status page monitoring**: CLI dashboard now shows OpenAI API status alongside Claude API status\n- **Codex rate limits in dashboard**: Sessions panel displays Codex rate-limit data with "Rate Limits" header instead of "Quota"\n- **Provider-aware `sidekick quota` command**: Detects active provider and shows Codex rate limits, Claude subscription quota, or an informational message for OpenCode\n\n### Fixed\n\n- **QuotaService polling for Codex**: Dashboard no longer starts Claude OAuth quota polling when the active provider is Codex\n\n## [0.14.2] - 2026-03-16\n\n### Fixed\n\n- **Quota polling interval**: Reduced quota refresh from every 30 seconds to every 5 minutes to avoid unnecessary API calls\n- **SessionsPanel `detailWidth()` call**: Removed unused parameter from `detailWidth()` in the Sessions panel quota rendering\n\n## [0.14.1] - 2026-03-14\n\n### Fixed\n\n- **Per-model context window sizes**: Dashboard context gauge now shows correct utilization for Claude Opus 4.6 (1M context) and other models with non-200K windows\n\n### Changed\n\n- **Shared model context lookup**: CLI dashboard now uses the centralized `getModelContextWindowSize()` from `sidekick-shared` instead of a local duplicate map\n\n## [0.14.0] - 2026-03-12\n\n### Added\n\n- **`sidekick account` Command**: Manage Claude Code accounts from the terminal \u2014 list saved accounts, add the current account with an optional label, switch to the next or a specific account, and remove accounts. Supports `--json` output for scripting\n- **Quota Account Label**: `sidekick quota` now shows the active account email and label above the quota bars when multi-account is enabled\n- **macOS Keychain Support**: `sidekick account` and `sidekick quota` now read and write credentials via the system Keychain on macOS, fixing account switching and quota checks on Mac\n\n## [0.13.8] - 2026-03-12\n\n### Changed\n\n- **Structured quota failure output**: `sidekick quota` now renders consistent auth, rate-limit, server, network, and unexpected-failure copy from shared quota failure descriptors while preserving `--json` machine-readable output\n- **Dashboard unavailable quota rendering**: The Sessions panel now shows Claude Code quota failures inline instead of hiding the quota section whenever subscription data is unavailable\n- **Quota transition toasts**: The Ink dashboard now fires low-noise toast notifications only when Claude Code quota failure state changes, avoiding repeated alerts every polling interval\n\n## [0.13.7] - 2026-03-11\n\n### Changed\n\n- **npm README sync**: Updated the published CLI package README to reflect current OpenCode monitoring behavior, platform-specific data directories, and the `sqlite3` runtime requirement\n- **README badge cleanup**: Removed the Ask DeepWiki badge from the published CLI package README; the repo root README still keeps it\n\n## [0.13.6] - 2026-03-11\n\n### Changed\n\n- **Refreshed CLI Dashboard Wordmark**: Updated the dashboard wordmark/header styling for a cleaner splash and dashboard identity\n\n### Fixed\n\n- **OpenCode dashboard startup**: OpenCode DB-backed session discovery now resolves projects by worktree, sandboxes, and session directory instead of quietly behaving like no session exists\n- **OpenCode runtime notices**: The CLI now prints an OpenCode-only actionable notice when `opencode.db` exists but `sqlite3` is missing, blocked, or otherwise unusable in the current shell environment\n\n## [0.13.5] - 2026-03-10\n\n### Added\n\n- **`sidekick status` Command**: One-shot Claude API status check with color-coded text output and `--json` mode\n- **Dashboard Status Banner**: Status bar shows a colored `\u25CF API minor/major/critical` indicator when Claude is degraded; Sessions panel Summary tab shows an "API Status" section with affected components and active incident details. Polls every 60s\n\n## [0.13.4] - 2026-03-08\n\n### Fixed\n\n- **Onboarding Phrase Spam**: Splash screen and detail pane motivational phrases memoized \u2014 no longer flicker every render tick (fixes [#13](https://github.com/cesarandreslopez/sidekick-agent-hub/issues/13))\n\n### Changed\n\n- **Simplified Logo**: Replaced 6-line ASCII robot art with compact text header in splash, help, and changelog overlays\n- **Removed Dead Code**: Removed unused `getSplashContent()` and `HELP_HEADER` exports from branding module\n\n## [0.13.3] - 2026-03-04\n\n_No CLI-specific changes in this release._\n\n## [0.13.2] - 2026-03-04\n\n_No CLI-specific changes in this release._\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';
79123
+ 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.17.6] - 2026-04-19\n\n### Added\n\n- **`sidekick peak` command**: One-shot check for Claude\'s current peak-hours state \u2014 weekdays 13:00\u201319:00 UTC, when session limits drain faster on Free/Pro/Max/Team subscriptions. Prints a color-coded status block with a countdown to the next transition. Data comes from the public `promoclock.co/api/status` endpoint (third-party, unaffiliated with Anthropic) with a graceful fallback when unreachable. `--json` emits the full raw state\n- **Peak-hours block in `sidekick status`**: When the active provider is `claude-code`, the Claude + OpenAI health blocks are now followed by a **Claude Peak Hours** block (off-peak or in-peak, with countdown). Gated on the provider so OpenCode / Codex users don\'t trigger an unnecessary third-party fetch. `--json` output includes the new `peak` field\n- **Peak-hours summary in `sidekick quota`**: Claude subscription quota output now shows a **Peak** line under the 5-hour / 7-day bars \u2014 green dot off-peak, orange dot during an active peak, with a countdown to the next transition. `--json` output includes the new `peak` field\n\n## [0.17.5] - 2026-04-18\n\n### Added\n\n- **Default account bootstrap at CLI startup**: The CLI now calls `ensureDefaultAccounts()` from `sidekick-shared` at module load and awaits the result inside a Commander `preAction` hook, so the first real subcommand blocks briefly on the bootstrap while `--version` and `--help` stay instant. When a system Claude Code or Codex credential exists and no saved account is active for that provider yet, the CLI registers it as "Default" \u2014 `sidekick quota`, `sidekick account`, and `sidekick stats` now reflect the active account on first run without requiring an explicit `sidekick account --add` first. Idempotent, never overwrites manually saved accounts, and all errors are swallowed so startup is never blocked\n\nThanks to [@B33pBeeps](https://github.com/B33pBeeps) (Juan Fourie) for contributing this feature in [#16](https://github.com/cesarandreslopez/sidekick-agent-hub/pull/16).\n\n## [0.17.4] - 2026-04-17\n\n### Changed\n\n- **Pricing hydration import migrated to `sidekick-shared/node`**: `cli.ts` now imports `hydratePricingCatalog` from the new Node-only subpath and keeps `detectProvider` on the package root. Runtime behavior is unchanged; the split makes the CLI\'s import surface self-documenting (hydration is explicitly a Node API) and aligns the CLI with the shared library\'s new versioned public API contract\n\n## [0.17.3] - 2026-04-17\n\n### Changed\n\n- **Version sync with the VS Code extension**: Republished to keep CLI, extension, and shared-library versions aligned after a cosmetic changelog fix in 0.17.3. No CLI code changes \u2014 functionally identical to 0.17.2\n\n## [0.17.2] - 2026-04-17\n\n### Added\n\n- **LiteLLM pricing hydration on startup**: The CLI now fetches the LiteLLM pricing catalog on startup and caches to `~/.config/sidekick/pricing-catalog.json` with a 24-hour TTL, 3s timeout, and stale-cache fallback \u2014 new model prices are picked up without a CLI upgrade\n- **Expanded pricing coverage**: GPT-4o, GPT-4.1, GPT-5.x, o1, o3, and o3-mini families are now priced alongside the existing Claude entries\n- **Real-dollar Codex / Claude Code costs**: `EventAggregator` computes cost from the pricing table when the session provider doesn\'t report one, so `sidekick` live dashboards now show actual dollars for Codex and Claude Code sessions\n- **`stats` footer lists unpriced models**: `sidekick stats` prints any models encountered with no pricing entry so missing coverage is visible\n\n### Fixed\n\n- **Context-gauge % wrong for Opus 4.7 (1M) and other new models**: The dashboard\'s context gauge was dividing by 200K for Claude Opus 4.7 (native 1M), inflating the displayed %. The shared model \u2192 context-window map now includes Opus/Sonnet 4.7 (1M), GPT-5.4 (1.05M), GPT-5.3-Codex (400K), and GPT-5.3-Codex-Spark (128K). Claude Code\'s `[1m]` suffix is now also honored as an explicit 1M marker\n- **Silent Sonnet-priced fallback for unknown models**: Codex, GPT-5.x, and o-series rows were being rendered at Sonnet rates. Unknown-model rows now render as `\u2014` in yellow instead of inventing a dollar figure\n\n### Changed\n\n- **`historical-data.json` schema v2**: reads `priced` flag and `unpricedModelIds` from records written by the latest VS Code extension; v1 records still read correctly\n\n## [0.17.1] - 2026-04-13\n\n### Fixed\n\n- **Codex multi-home session discovery**: Provider detection now scans all candidate Codex home directories, fixing missed sessions when the managed profile home is empty but the system `~/.codex/` has activity\n\n## [0.17.0] - 2026-04-13\n\n### Added\n\n- **Multi-provider account management**: `sidekick account` now supports `--provider codex` for Codex profile management alongside Claude Code accounts\n- **Codex account lifecycle**: `--add` prepares a profile and spawns `codex login`; `--switch-to` and `--remove` accept email, label, or profile ID\n- **Quota snapshot fallback**: `sidekick quota` for Codex shows cached rate-limit snapshots when no active session exists, with "cached from" timestamp\n\n### Fixed\n\n- **Email normalization**: Claude account lookup normalizes email case for reliable matching\n\n## [0.16.1] - 2026-03-27\n\n### Fixed\n\n- **Dashboard provider status scoping**: The TUI now shows degraded-service notices only for the monitored provider \u2014 Claude for Claude Code sessions, OpenAI for Codex sessions, and no status banner for OpenCode\n\n## [0.16.0] - 2026-03-23\n\n### Changed\n\n- **Consistent cost formatting**: All cost displays (`stats`, `context`, Sessions panel, narrative prompt) now use shared `formatCost()` with intelligent decimal precision (4 places for < $0.01, 2 otherwise)\n- **QuotaService**: Rewritten to wrap shared `QuotaPoller` with exponential backoff instead of manual polling loop\n- **modelContext**: Now re-exports `getModelInfo` from shared library alongside `getContextWindowSize`\n\n## [0.15.2] - 2026-03-18\n\n### Fixed\n\n- **CLI help descriptions**: Updated `quota` and `status` command descriptions to reflect provider-aware behavior\n- **`sidekick quota --provider`**: Added local `--provider` option so `sidekick quota --provider codex` works naturally\n\n## [0.15.0] - 2026-03-18\n\n### Added\n\n- **OpenAI status page monitoring**: CLI dashboard now shows OpenAI API status alongside Claude API status\n- **Codex rate limits in dashboard**: Sessions panel displays Codex rate-limit data with "Rate Limits" header instead of "Quota"\n- **Provider-aware `sidekick quota` command**: Detects active provider and shows Codex rate limits, Claude subscription quota, or an informational message for OpenCode\n\n### Fixed\n\n- **QuotaService polling for Codex**: Dashboard no longer starts Claude OAuth quota polling when the active provider is Codex\n\n## [0.14.2] - 2026-03-16\n\n### Fixed\n\n- **Quota polling interval**: Reduced quota refresh from every 30 seconds to every 5 minutes to avoid unnecessary API calls\n- **SessionsPanel `detailWidth()` call**: Removed unused parameter from `detailWidth()` in the Sessions panel quota rendering\n\n## [0.14.1] - 2026-03-14\n\n### Fixed\n\n- **Per-model context window sizes**: Dashboard context gauge now shows correct utilization for Claude Opus 4.6 (1M context) and other models with non-200K windows\n\n### Changed\n\n- **Shared model context lookup**: CLI dashboard now uses the centralized `getModelContextWindowSize()` from `sidekick-shared` instead of a local duplicate map\n\n## [0.14.0] - 2026-03-12\n\n### Added\n\n- **`sidekick account` Command**: Manage Claude Code accounts from the terminal \u2014 list saved accounts, add the current account with an optional label, switch to the next or a specific account, and remove accounts. Supports `--json` output for scripting\n- **Quota Account Label**: `sidekick quota` now shows the active account email and label above the quota bars when multi-account is enabled\n- **macOS Keychain Support**: `sidekick account` and `sidekick quota` now read and write credentials via the system Keychain on macOS, fixing account switching and quota checks on Mac\n\n## [0.13.8] - 2026-03-12\n\n### Changed\n\n- **Structured quota failure output**: `sidekick quota` now renders consistent auth, rate-limit, server, network, and unexpected-failure copy from shared quota failure descriptors while preserving `--json` machine-readable output\n- **Dashboard unavailable quota rendering**: The Sessions panel now shows Claude Code quota failures inline instead of hiding the quota section whenever subscription data is unavailable\n- **Quota transition toasts**: The Ink dashboard now fires low-noise toast notifications only when Claude Code quota failure state changes, avoiding repeated alerts every polling interval\n\n## [0.13.7] - 2026-03-11\n\n### Changed\n\n- **npm README sync**: Updated the published CLI package README to reflect current OpenCode monitoring behavior, platform-specific data directories, and the `sqlite3` runtime requirement\n- **README badge cleanup**: Removed the Ask DeepWiki badge from the published CLI package README; the repo root README still keeps it\n\n## [0.13.6] - 2026-03-11\n\n### Changed\n\n- **Refreshed CLI Dashboard Wordmark**: Updated the dashboard wordmark/header styling for a cleaner splash and dashboard identity\n\n### Fixed\n\n- **OpenCode dashboard startup**: OpenCode DB-backed session discovery now resolves projects by worktree, sandboxes, and session directory instead of quietly behaving like no session exists\n- **OpenCode runtime notices**: The CLI now prints an OpenCode-only actionable notice when `opencode.db` exists but `sqlite3` is missing, blocked, or otherwise unusable in the current shell environment\n\n## [0.13.5] - 2026-03-10\n\n### Added\n\n- **`sidekick status` Command**: One-shot Claude API status check with color-coded text output and `--json` mode\n- **Dashboard Status Banner**: Status bar shows a colored `\u25CF API minor/major/critical` indicator when Claude is degraded; Sessions panel Summary tab shows an "API Status" section with affected components and active incident details. Polls every 60s\n\n## [0.13.4] - 2026-03-08\n\n### Fixed\n\n- **Onboarding Phrase Spam**: Splash screen and detail pane motivational phrases memoized \u2014 no longer flicker every render tick (fixes [#13](https://github.com/cesarandreslopez/sidekick-agent-hub/issues/13))\n\n### Changed\n\n- **Simplified Logo**: Replaced 6-line ASCII robot art with compact text header in splash, help, and changelog overlays\n- **Removed Dead Code**: Removed unused `getSplashContent()` and `HELP_HEADER` exports from branding module\n\n## [0.13.3] - 2026-03-04\n\n_No CLI-specific changes in this release._\n\n## [0.13.2] - 2026-03-04\n\n_No CLI-specific changes in this release._\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';
78951
79124
  }
78952
79125
  });
78953
79126
 
@@ -81026,6 +81199,57 @@ var init_stats = __esm({
81026
81199
  }
81027
81200
  });
81028
81201
 
81202
+ // src/commands/peakHoursRender.ts
81203
+ function formatCountdown(minutes) {
81204
+ if (typeof minutes !== "number" || minutes <= 0) return "";
81205
+ const h = Math.floor(minutes / 60);
81206
+ const m = minutes % 60;
81207
+ return h > 0 ? `${h}h ${m}m` : `${m}m`;
81208
+ }
81209
+ function printPeakHoursBlock(state) {
81210
+ process.stdout.write(source_default.bold("Claude Peak Hours\n"));
81211
+ process.stdout.write(source_default.dim("\u2500".repeat(50) + "\n"));
81212
+ if (state.unavailable) {
81213
+ process.stdout.write(source_default.dim(" Peak-hours status unavailable (promoclock.co unreachable).\n"));
81214
+ return;
81215
+ }
81216
+ const color = state.isPeak ? source_default.hex("#E59C4F") : source_default.green;
81217
+ const dot = "\u25CF";
81218
+ process.stdout.write(` ${color(dot)} ${color(state.label || (state.isPeak ? "Peak" : "Off-Peak"))}
81219
+ `);
81220
+ const countdown = formatCountdown(state.minutesUntilChange);
81221
+ if (countdown) {
81222
+ const label = state.isPeak ? "Off-peak in" : "Next peak in";
81223
+ process.stdout.write(source_default.dim(` ${label} ${countdown}
81224
+ `));
81225
+ }
81226
+ if (state.peakHoursDescription) {
81227
+ process.stdout.write(source_default.dim(` ${state.peakHoursDescription}
81228
+ `));
81229
+ }
81230
+ if (state.note) {
81231
+ process.stdout.write(source_default.dim(` ${state.note}
81232
+ `));
81233
+ }
81234
+ process.stdout.write(source_default.dim(" Source: promoclock.co (third-party, unaffiliated with Anthropic)\n"));
81235
+ }
81236
+ function formatPeakHoursLine(state) {
81237
+ if (state.unavailable) return "";
81238
+ const countdown = formatCountdown(state.minutesUntilChange);
81239
+ if (state.isPeak) {
81240
+ const suffix2 = countdown ? ` ${source_default.dim("(off-peak in " + countdown + ")")}` : "";
81241
+ return `${source_default.hex("#E59C4F")("\u25CF")} ${source_default.hex("#E59C4F")(state.label || "Peak Hours \u2014 Faster Drain")}${suffix2}`;
81242
+ }
81243
+ const suffix = countdown ? ` ${source_default.dim("(peak in " + countdown + ")")}` : "";
81244
+ return `${source_default.green("\u25CF")} ${source_default.green(state.label || "Off-Peak \u2014 Normal Speed")}${suffix}`;
81245
+ }
81246
+ var init_peakHoursRender = __esm({
81247
+ "src/commands/peakHoursRender.ts"() {
81248
+ "use strict";
81249
+ init_source();
81250
+ }
81251
+ });
81252
+
81029
81253
  // src/commands/quota.ts
81030
81254
  var quota_exports = {};
81031
81255
  __export(quota_exports, {
@@ -81088,10 +81312,13 @@ async function quotaAction(_opts, cmd) {
81088
81312
  }
81089
81313
  async function claudeQuotaAction(jsonOutput) {
81090
81314
  const service = new QuotaService();
81091
- const quota = await service.fetchOnce();
81315
+ const [quota, peak] = await Promise.all([
81316
+ service.fetchOnce(),
81317
+ (0, import_sidekick_shared28.fetchPeakHoursStatus)()
81318
+ ]);
81092
81319
  if (!quota.available) {
81093
81320
  if (jsonOutput) {
81094
- process.stdout.write(JSON.stringify(quota, null, 2) + "\n");
81321
+ process.stdout.write(JSON.stringify({ ...quota, peak }, null, 2) + "\n");
81095
81322
  return;
81096
81323
  }
81097
81324
  const descriptor = (0, import_sidekick_shared28.describeQuotaFailure)(quota);
@@ -81119,7 +81346,7 @@ async function claudeQuotaAction(jsonOutput) {
81119
81346
  process.exit(1);
81120
81347
  }
81121
81348
  if (jsonOutput) {
81122
- process.stdout.write(JSON.stringify(quota, null, 2) + "\n");
81349
+ process.stdout.write(JSON.stringify({ ...quota, peak }, null, 2) + "\n");
81123
81350
  return;
81124
81351
  }
81125
81352
  const barWidth = 30;
@@ -81139,6 +81366,13 @@ async function claudeQuotaAction(jsonOutput) {
81139
81366
  process.stdout.write(` ${source_default.dim("5-Hour")} ${makeChalkBar(fivePct, barWidth)} ${String(fivePct).padStart(3)}%${fiveProj} ${source_default.dim("resets " + fiveReset)}
81140
81367
  `);
81141
81368
  process.stdout.write(` ${source_default.dim("7-Day")} ${makeChalkBar(sevenPct, barWidth)} ${String(sevenPct).padStart(3)}%${sevenProj} ${source_default.dim("resets " + sevenReset)}
81369
+ `);
81370
+ printPeakHoursSummary(peak);
81371
+ }
81372
+ function printPeakHoursSummary(peak) {
81373
+ const line = formatPeakHoursLine(peak);
81374
+ if (!line) return;
81375
+ process.stdout.write(` ${source_default.dim("Peak")} ${line}
81142
81376
  `);
81143
81377
  }
81144
81378
  async function codexQuotaAction(provider, globalOpts, jsonOutput) {
@@ -81233,6 +81467,7 @@ var init_quota = __esm({
81233
81467
  import_sidekick_shared28 = __toESM(require_dist(), 1);
81234
81468
  init_cli();
81235
81469
  init_QuotaService();
81470
+ init_peakHoursRender();
81236
81471
  }
81237
81472
  });
81238
81473
 
@@ -81279,17 +81514,24 @@ function printStatus(label, statusPageHost, status) {
81279
81514
  async function statusAction(_opts, cmd) {
81280
81515
  const globalOpts = cmd.parent.opts();
81281
81516
  const jsonOutput = !!globalOpts.json;
81282
- const [claude, openai] = await Promise.all([
81517
+ const providerId = resolveProviderId(globalOpts);
81518
+ const wantsPeak = providerId === "claude-code";
81519
+ const [claude, openai, peak] = await Promise.all([
81283
81520
  (0, import_sidekick_shared29.fetchProviderStatus)(),
81284
- (0, import_sidekick_shared29.fetchOpenAIStatus)()
81521
+ (0, import_sidekick_shared29.fetchOpenAIStatus)(),
81522
+ wantsPeak ? (0, import_sidekick_shared29.fetchPeakHoursStatus)() : Promise.resolve(null)
81285
81523
  ]);
81286
81524
  if (jsonOutput) {
81287
- process.stdout.write(JSON.stringify({ claude, openai }, null, 2) + "\n");
81525
+ process.stdout.write(JSON.stringify({ claude, openai, peak }, null, 2) + "\n");
81288
81526
  return;
81289
81527
  }
81290
81528
  printStatus("Claude API Status", "status.claude.com", claude);
81291
81529
  process.stdout.write("\n");
81292
81530
  printStatus("OpenAI API Status", "status.openai.com", openai);
81531
+ if (peak) {
81532
+ process.stdout.write("\n");
81533
+ printPeakHoursBlock(peak);
81534
+ }
81293
81535
  }
81294
81536
  var import_sidekick_shared29;
81295
81537
  var init_status = __esm({
@@ -81297,6 +81539,32 @@ var init_status = __esm({
81297
81539
  "use strict";
81298
81540
  init_source();
81299
81541
  import_sidekick_shared29 = __toESM(require_dist(), 1);
81542
+ init_peakHoursRender();
81543
+ init_cli();
81544
+ }
81545
+ });
81546
+
81547
+ // src/commands/peak.ts
81548
+ var peak_exports = {};
81549
+ __export(peak_exports, {
81550
+ peakAction: () => peakAction
81551
+ });
81552
+ async function peakAction(_opts, cmd) {
81553
+ const globalOpts = cmd.parent.opts();
81554
+ const jsonOutput = !!globalOpts.json;
81555
+ const state = await (0, import_sidekick_shared30.fetchPeakHoursStatus)();
81556
+ if (jsonOutput) {
81557
+ process.stdout.write(JSON.stringify(state, null, 2) + "\n");
81558
+ return;
81559
+ }
81560
+ printPeakHoursBlock(state);
81561
+ }
81562
+ var import_sidekick_shared30;
81563
+ var init_peak = __esm({
81564
+ "src/commands/peak.ts"() {
81565
+ "use strict";
81566
+ import_sidekick_shared30 = __toESM(require_dist(), 1);
81567
+ init_peakHoursRender();
81300
81568
  }
81301
81569
  });
81302
81570
 
@@ -81327,13 +81595,13 @@ async function accountAction(_opts, cmd) {
81327
81595
  }
81328
81596
  function claudeAccountAction(opts, jsonOutput) {
81329
81597
  if (opts.add) {
81330
- const result = (0, import_sidekick_shared30.addCurrentAccount)(opts.label);
81598
+ const result = (0, import_sidekick_shared31.addCurrentAccount)(opts.label);
81331
81599
  if (!result.success) {
81332
81600
  process.stderr.write(source_default.red(result.error ?? "Failed to save account.") + "\n");
81333
81601
  process.exit(1);
81334
81602
  return;
81335
81603
  }
81336
- const active2 = (0, import_sidekick_shared30.readActiveClaudeAccount)();
81604
+ const active2 = (0, import_sidekick_shared31.readActiveClaudeAccount)();
81337
81605
  if (jsonOutput) {
81338
81606
  process.stdout.write(JSON.stringify({ action: "added", provider: "claude-code", email: active2?.email, label: opts.label }) + "\n");
81339
81607
  } else {
@@ -81342,14 +81610,14 @@ function claudeAccountAction(opts, jsonOutput) {
81342
81610
  return;
81343
81611
  }
81344
81612
  if (opts.remove) {
81345
- const accounts2 = (0, import_sidekick_shared30.listAccounts)();
81613
+ const accounts2 = (0, import_sidekick_shared31.listAccounts)();
81346
81614
  const target = findClaudeAccount(opts.remove, accounts2);
81347
81615
  if (!target) {
81348
81616
  process.stderr.write(source_default.red(`Account "${opts.remove}" not found.`) + "\n");
81349
81617
  process.exit(1);
81350
81618
  return;
81351
81619
  }
81352
- const result = (0, import_sidekick_shared30.removeAccount)(target.uuid);
81620
+ const result = (0, import_sidekick_shared31.removeAccount)(target.uuid);
81353
81621
  if (!result.success) {
81354
81622
  process.stderr.write(source_default.red(result.error ?? "Failed to remove account.") + "\n");
81355
81623
  process.exit(1);
@@ -81363,14 +81631,14 @@ function claudeAccountAction(opts, jsonOutput) {
81363
81631
  return;
81364
81632
  }
81365
81633
  if (opts.switchTo) {
81366
- const accounts2 = (0, import_sidekick_shared30.listAccounts)();
81634
+ const accounts2 = (0, import_sidekick_shared31.listAccounts)();
81367
81635
  const target = findClaudeAccount(opts.switchTo, accounts2);
81368
81636
  if (!target) {
81369
81637
  process.stderr.write(source_default.red(`Account "${opts.switchTo}" not found.`) + "\n");
81370
81638
  process.exit(1);
81371
81639
  return;
81372
81640
  }
81373
- const result = (0, import_sidekick_shared30.switchToAccount)(target.uuid);
81641
+ const result = (0, import_sidekick_shared31.switchToAccount)(target.uuid);
81374
81642
  if (!result.success) {
81375
81643
  process.stderr.write(source_default.red(result.error ?? "Failed to switch.") + "\n");
81376
81644
  process.exit(1);
@@ -81384,17 +81652,17 @@ function claudeAccountAction(opts, jsonOutput) {
81384
81652
  return;
81385
81653
  }
81386
81654
  if (opts.switch) {
81387
- const accounts2 = (0, import_sidekick_shared30.listAccounts)();
81655
+ const accounts2 = (0, import_sidekick_shared31.listAccounts)();
81388
81656
  if (accounts2.length < 2) {
81389
81657
  process.stderr.write(source_default.yellow("Need at least 2 saved accounts to switch.") + "\n");
81390
81658
  process.exit(1);
81391
81659
  return;
81392
81660
  }
81393
- const active2 = (0, import_sidekick_shared30.getActiveAccount)();
81661
+ const active2 = (0, import_sidekick_shared31.getActiveAccount)();
81394
81662
  const currentIdx = active2 ? accounts2.findIndex((a) => a.uuid === active2.uuid) : -1;
81395
81663
  const nextIdx = (currentIdx + 1) % accounts2.length;
81396
81664
  const target = accounts2[nextIdx];
81397
- const result = (0, import_sidekick_shared30.switchToAccount)(target.uuid);
81665
+ const result = (0, import_sidekick_shared31.switchToAccount)(target.uuid);
81398
81666
  if (!result.success) {
81399
81667
  process.stderr.write(source_default.red(result.error ?? "Failed to switch.") + "\n");
81400
81668
  process.exit(1);
@@ -81407,9 +81675,9 @@ function claudeAccountAction(opts, jsonOutput) {
81407
81675
  }
81408
81676
  return;
81409
81677
  }
81410
- const accounts = (0, import_sidekick_shared30.listAccounts)();
81678
+ const accounts = (0, import_sidekick_shared31.listAccounts)();
81411
81679
  if (accounts.length === 0) {
81412
- const current = (0, import_sidekick_shared30.readActiveClaudeAccount)();
81680
+ const current = (0, import_sidekick_shared31.readActiveClaudeAccount)();
81413
81681
  if (jsonOutput) {
81414
81682
  process.stdout.write(JSON.stringify({ provider: "claude-code", accounts: [], current: current ?? null }) + "\n");
81415
81683
  } else if (current) {
@@ -81421,14 +81689,14 @@ function claudeAccountAction(opts, jsonOutput) {
81421
81689
  return;
81422
81690
  }
81423
81691
  if (jsonOutput) {
81424
- const active2 = (0, import_sidekick_shared30.getActiveAccount)();
81692
+ const active2 = (0, import_sidekick_shared31.getActiveAccount)();
81425
81693
  process.stdout.write(JSON.stringify({
81426
81694
  provider: "claude-code",
81427
81695
  accounts: accounts.map((a) => ({ ...a, active: a.uuid === active2?.uuid }))
81428
81696
  }, null, 2) + "\n");
81429
81697
  return;
81430
81698
  }
81431
- const active = (0, import_sidekick_shared30.getActiveAccount)();
81699
+ const active = (0, import_sidekick_shared31.getActiveAccount)();
81432
81700
  process.stdout.write(source_default.bold("Claude Accounts\n"));
81433
81701
  process.stdout.write(source_default.dim("\u2500".repeat(50) + "\n"));
81434
81702
  for (const account of accounts) {
@@ -81475,7 +81743,7 @@ function codexAccountAction(opts, jsonOutput) {
81475
81743
  process.exit(1);
81476
81744
  return;
81477
81745
  }
81478
- const prepared = (0, import_sidekick_shared30.prepareCodexAccount)(opts.label);
81746
+ const prepared = (0, import_sidekick_shared31.prepareCodexAccount)(opts.label);
81479
81747
  if (!prepared.success) {
81480
81748
  process.stderr.write(source_default.red(prepared.error ?? "Failed to prepare Codex account.") + "\n");
81481
81749
  process.exit(1);
@@ -81493,14 +81761,14 @@ function codexAccountAction(opts, jsonOutput) {
81493
81761
  process.exit(1);
81494
81762
  return;
81495
81763
  }
81496
- const finalized = (0, import_sidekick_shared30.finalizeCodexAccount)(prepared.profileId);
81764
+ const finalized = (0, import_sidekick_shared31.finalizeCodexAccount)(prepared.profileId);
81497
81765
  if (!finalized.success) {
81498
81766
  process.stderr.write(source_default.red(finalized.error ?? "Failed to finalize Codex account.") + "\n");
81499
81767
  process.exit(1);
81500
81768
  return;
81501
81769
  }
81502
81770
  }
81503
- const active2 = (0, import_sidekick_shared30.getActiveCodexAccount)();
81771
+ const active2 = (0, import_sidekick_shared31.getActiveCodexAccount)();
81504
81772
  if (jsonOutput) {
81505
81773
  process.stdout.write(JSON.stringify({
81506
81774
  action: "added",
@@ -81516,14 +81784,14 @@ function codexAccountAction(opts, jsonOutput) {
81516
81784
  return;
81517
81785
  }
81518
81786
  if (opts.remove) {
81519
- const accounts2 = (0, import_sidekick_shared30.listCodexAccounts)();
81787
+ const accounts2 = (0, import_sidekick_shared31.listCodexAccounts)();
81520
81788
  const target = findCodexAccount(opts.remove, accounts2);
81521
81789
  if (!target) {
81522
81790
  process.stderr.write(source_default.red(`Codex account "${opts.remove}" not found.`) + "\n");
81523
81791
  process.exit(1);
81524
81792
  return;
81525
81793
  }
81526
- const result = (0, import_sidekick_shared30.removeCodexAccount)(target.id);
81794
+ const result = (0, import_sidekick_shared31.removeCodexAccount)(target.id);
81527
81795
  if (!result.success) {
81528
81796
  process.stderr.write(source_default.red(result.error ?? "Failed to remove Codex account.") + "\n");
81529
81797
  process.exit(1);
@@ -81537,14 +81805,14 @@ function codexAccountAction(opts, jsonOutput) {
81537
81805
  return;
81538
81806
  }
81539
81807
  if (opts.switchTo) {
81540
- const accounts2 = (0, import_sidekick_shared30.listCodexAccounts)();
81808
+ const accounts2 = (0, import_sidekick_shared31.listCodexAccounts)();
81541
81809
  const target = findCodexAccount(opts.switchTo, accounts2);
81542
81810
  if (!target) {
81543
81811
  process.stderr.write(source_default.red(`Codex account "${opts.switchTo}" not found.`) + "\n");
81544
81812
  process.exit(1);
81545
81813
  return;
81546
81814
  }
81547
- const result = (0, import_sidekick_shared30.switchToCodexAccount)(target.id);
81815
+ const result = (0, import_sidekick_shared31.switchToCodexAccount)(target.id);
81548
81816
  if (!result.success) {
81549
81817
  process.stderr.write(source_default.red(result.error ?? "Failed to switch Codex account.") + "\n");
81550
81818
  process.exit(1);
@@ -81558,17 +81826,17 @@ function codexAccountAction(opts, jsonOutput) {
81558
81826
  return;
81559
81827
  }
81560
81828
  if (opts.switch) {
81561
- const accounts2 = (0, import_sidekick_shared30.listCodexAccounts)();
81829
+ const accounts2 = (0, import_sidekick_shared31.listCodexAccounts)();
81562
81830
  if (accounts2.length < 2) {
81563
81831
  process.stderr.write(source_default.yellow("Need at least 2 saved Codex accounts to switch.") + "\n");
81564
81832
  process.exit(1);
81565
81833
  return;
81566
81834
  }
81567
- const active2 = (0, import_sidekick_shared30.getActiveCodexAccount)();
81835
+ const active2 = (0, import_sidekick_shared31.getActiveCodexAccount)();
81568
81836
  const currentIdx = active2 ? accounts2.findIndex((account) => account.id === active2.id) : -1;
81569
81837
  const nextIdx = (currentIdx + 1) % accounts2.length;
81570
81838
  const target = accounts2[nextIdx];
81571
- const result = (0, import_sidekick_shared30.switchToCodexAccount)(target.id);
81839
+ const result = (0, import_sidekick_shared31.switchToCodexAccount)(target.id);
81572
81840
  if (!result.success) {
81573
81841
  process.stderr.write(source_default.red(result.error ?? "Failed to switch Codex account.") + "\n");
81574
81842
  process.exit(1);
@@ -81581,7 +81849,7 @@ function codexAccountAction(opts, jsonOutput) {
81581
81849
  }
81582
81850
  return;
81583
81851
  }
81584
- const accounts = (0, import_sidekick_shared30.listCodexAccounts)();
81852
+ const accounts = (0, import_sidekick_shared31.listCodexAccounts)();
81585
81853
  if (accounts.length === 0) {
81586
81854
  if (jsonOutput) {
81587
81855
  process.stdout.write(JSON.stringify({ provider: "codex", accounts: [], current: null }) + "\n");
@@ -81591,7 +81859,7 @@ function codexAccountAction(opts, jsonOutput) {
81591
81859
  return;
81592
81860
  }
81593
81861
  if (jsonOutput) {
81594
- const active2 = (0, import_sidekick_shared30.getActiveCodexAccount)();
81862
+ const active2 = (0, import_sidekick_shared31.getActiveCodexAccount)();
81595
81863
  process.stdout.write(JSON.stringify({
81596
81864
  provider: "codex",
81597
81865
  accounts: accounts.map((account) => ({
@@ -81601,7 +81869,7 @@ function codexAccountAction(opts, jsonOutput) {
81601
81869
  }, null, 2) + "\n");
81602
81870
  return;
81603
81871
  }
81604
- const active = (0, import_sidekick_shared30.getActiveCodexAccount)();
81872
+ const active = (0, import_sidekick_shared31.getActiveCodexAccount)();
81605
81873
  process.stdout.write(source_default.bold("Codex Accounts\n"));
81606
81874
  process.stdout.write(source_default.dim("\u2500".repeat(50) + "\n"));
81607
81875
  for (const account of accounts) {
@@ -81612,12 +81880,12 @@ function codexAccountAction(opts, jsonOutput) {
81612
81880
  `);
81613
81881
  }
81614
81882
  }
81615
- var import_sidekick_shared30;
81883
+ var import_sidekick_shared31;
81616
81884
  var init_account = __esm({
81617
81885
  "src/commands/account.ts"() {
81618
81886
  "use strict";
81619
81887
  init_source();
81620
- import_sidekick_shared30 = __toESM(require_dist(), 1);
81888
+ import_sidekick_shared31 = __toESM(require_dist(), 1);
81621
81889
  init_cli();
81622
81890
  }
81623
81891
  });
@@ -81632,12 +81900,12 @@ async function handoffAction(_opts, cmd) {
81632
81900
  const workspacePath = globalOpts.project || process.cwd();
81633
81901
  const jsonOutput = !!globalOpts.json;
81634
81902
  try {
81635
- const rawSlug = (0, import_sidekick_shared31.getProjectSlugRaw)(workspacePath);
81636
- const resolvedSlug = (0, import_sidekick_shared31.getProjectSlug)(workspacePath);
81903
+ const rawSlug = (0, import_sidekick_shared32.getProjectSlugRaw)(workspacePath);
81904
+ const resolvedSlug = (0, import_sidekick_shared32.getProjectSlug)(workspacePath);
81637
81905
  const slugs = rawSlug !== resolvedSlug ? [rawSlug, resolvedSlug] : [rawSlug];
81638
81906
  let content = null;
81639
81907
  for (const slug of slugs) {
81640
- content = await (0, import_sidekick_shared31.readLatestHandoff)(slug);
81908
+ content = await (0, import_sidekick_shared32.readLatestHandoff)(slug);
81641
81909
  if (content) break;
81642
81910
  }
81643
81911
  if (!content) {
@@ -81662,12 +81930,12 @@ async function handoffAction(_opts, cmd) {
81662
81930
  process.exit(1);
81663
81931
  }
81664
81932
  }
81665
- var import_sidekick_shared31;
81933
+ var import_sidekick_shared32;
81666
81934
  var init_handoff = __esm({
81667
81935
  "src/commands/handoff.ts"() {
81668
81936
  "use strict";
81669
81937
  init_source();
81670
- import_sidekick_shared31 = __toESM(require_dist(), 1);
81938
+ import_sidekick_shared32 = __toESM(require_dist(), 1);
81671
81939
  }
81672
81940
  });
81673
81941
 
@@ -81681,33 +81949,38 @@ function resolveProviderId(opts, defaultProvider = "auto") {
81681
81949
  if (defaultProvider !== "auto") {
81682
81950
  return defaultProvider;
81683
81951
  }
81684
- return (0, import_sidekick_shared32.detectProvider)();
81952
+ return (0, import_sidekick_shared33.detectProvider)();
81685
81953
  }
81686
81954
  function resolveProvider(opts) {
81687
81955
  const id = resolveProviderId(opts);
81688
81956
  switch (id) {
81689
81957
  case "opencode":
81690
- return new import_sidekick_shared33.OpenCodeProvider();
81958
+ return new import_sidekick_shared34.OpenCodeProvider();
81691
81959
  case "codex":
81692
- return new import_sidekick_shared33.CodexProvider();
81960
+ return new import_sidekick_shared34.CodexProvider();
81693
81961
  case "claude-code":
81694
81962
  default:
81695
- return new import_sidekick_shared33.ClaudeCodeProvider();
81963
+ return new import_sidekick_shared34.ClaudeCodeProvider();
81696
81964
  }
81697
81965
  }
81698
- var import_sidekick_shared32, import_node, import_sidekick_shared33, program2, dashCmd, dumpCmd, ctxCmd, reportCmd, searchCmd, tasksCmd, decisionsCmd, notesCmd, statsCmd, quotaCmd, statusCmd, accountCmd, handoffCmd;
81966
+ var import_sidekick_shared33, import_node, import_sidekick_shared34, defaultAccountsReady, program2, dashCmd, dumpCmd, ctxCmd, reportCmd, searchCmd, tasksCmd, decisionsCmd, notesCmd, statsCmd, quotaCmd, statusCmd, peakCmd, accountCmd, handoffCmd;
81699
81967
  var init_cli = __esm({
81700
81968
  "src/cli.ts"() {
81701
81969
  init_esm();
81702
- import_sidekick_shared32 = __toESM(require_dist(), 1);
81703
- import_node = __toESM(require_node(), 1);
81704
81970
  import_sidekick_shared33 = __toESM(require_dist(), 1);
81971
+ import_node = __toESM(require_node(), 1);
81972
+ import_sidekick_shared34 = __toESM(require_dist(), 1);
81705
81973
  (0, import_node.hydratePricingCatalog)({
81706
81974
  cacheDir: path7.join(os5.homedir(), ".config", "sidekick")
81707
81975
  }).catch(() => {
81708
81976
  });
81977
+ defaultAccountsReady = (0, import_sidekick_shared33.ensureDefaultAccounts)().catch(() => {
81978
+ });
81709
81979
  program2 = new Command();
81710
- program2.name("sidekick").description("Query Sidekick project intelligence from the command line").version("0.17.4").option("--json", "Output as JSON").option("--project <path>", "Override project path (default: cwd)").option("--provider <id>", "Provider: claude-code, opencode, codex, auto (default: auto)");
81980
+ program2.name("sidekick").description("Query Sidekick project intelligence from the command line").version("0.17.6").option("--json", "Output as JSON").option("--project <path>", "Override project path (default: cwd)").option("--provider <id>", "Provider: claude-code, opencode, codex, auto (default: auto)");
81981
+ program2.hook("preAction", async () => {
81982
+ await defaultAccountsReady;
81983
+ });
81711
81984
  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) => {
81712
81985
  const { dashboardAction: dashboardAction2 } = await init_dashboard().then(() => dashboard_exports);
81713
81986
  return dashboardAction2(_opts, cmd);
@@ -81764,6 +82037,11 @@ var init_cli = __esm({
81764
82037
  return statusAction2(_opts, cmd);
81765
82038
  });
81766
82039
  program2.addCommand(statusCmd);
82040
+ peakCmd = new Command("peak").description("Show whether Claude is currently in peak hours (faster session-limit drain)").action(async (_opts, cmd) => {
82041
+ const { peakAction: peakAction2 } = await Promise.resolve().then(() => (init_peak(), peak_exports));
82042
+ return peakAction2(_opts, cmd);
82043
+ });
82044
+ program2.addCommand(peakCmd);
81767
82045
  accountCmd = new Command("account").description("Manage saved accounts (list, add, switch, remove)").option("--provider <id>", "Provider: claude-code, codex, auto (default: claude-code)").option("--add", "Save the currently signed-in account").option("--label <name>", "Label for the account (required for Codex, optional for Claude)").option("--switch", "Switch to the next saved account").option("--switch-to <identifier>", "Switch to a specific account by email, label, or id").option("--remove <identifier>", "Remove a saved account by email, label, or id").action(async (_opts, cmd) => {
81768
82046
  const { accountAction: accountAction2 } = await Promise.resolve().then(() => (init_account(), account_exports));
81769
82047
  return accountAction2(_opts, cmd);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidekick-agent-hub",
3
- "version": "0.17.4",
3
+ "version": "0.17.6",
4
4
  "description": "Terminal dashboard for monitoring AI coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {