sidekick-agent-hub 0.18.5 → 0.19.0

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.
Files changed (2) hide show
  1. package/dist/sidekick-cli.mjs +417 -60
  2. package/package.json +1 -1
@@ -4518,6 +4518,7 @@ var require_codexProfiles = __commonJS({
4518
4518
  exports.prepareCodexAccount = prepareCodexAccount2;
4519
4519
  exports.finalizeCodexAccount = finalizeCodexAccount2;
4520
4520
  exports.switchToCodexAccount = switchToCodexAccount2;
4521
+ exports.reconcileCodexAuthState = reconcileCodexAuthState;
4521
4522
  exports.removeCodexAccount = removeCodexAccount2;
4522
4523
  var fs9 = __importStar(__require("fs"));
4523
4524
  var os6 = __importStar(__require("os"));
@@ -4525,6 +4526,7 @@ var require_codexProfiles = __commonJS({
4525
4526
  var child_process_1 = __require("child_process");
4526
4527
  var crypto_1 = __require("crypto");
4527
4528
  var accountRegistry_1 = require_accountRegistry();
4529
+ var STALE_AUTH_THRESHOLD_MS = 8 * 24 * 60 * 60 * 1e3;
4528
4530
  function getDefaultSystemCodexHome() {
4529
4531
  return path8.join(os6.homedir(), ".codex");
4530
4532
  }
@@ -4551,12 +4553,13 @@ var require_codexProfiles = __commonJS({
4551
4553
  const explicitHome = getExplicitCodexHome();
4552
4554
  if (explicitHome)
4553
4555
  return [explicitHome];
4554
- const homes = [];
4555
- const active = getActiveCodexAccount3();
4556
- if (active) {
4557
- homes.push(getCodexProfileHome(active.id));
4556
+ const homes = [getDefaultSystemCodexHome()];
4557
+ for (const profile of listCodexAccounts2()) {
4558
+ const profileHome = getCodexProfileHome(profile.id);
4559
+ if (fs9.existsSync(path8.join(profileHome, "sessions"))) {
4560
+ homes.push(profileHome);
4561
+ }
4558
4562
  }
4559
- homes.push(getDefaultSystemCodexHome());
4560
4563
  return dedupePaths(homes);
4561
4564
  }
4562
4565
  function getCodexProfilesDir() {
@@ -4578,8 +4581,37 @@ var require_codexProfiles = __commonJS({
4578
4581
  const tmp = filePath + ".tmp";
4579
4582
  const json = JSON.stringify(data, null, 2);
4580
4583
  JSON.parse(json);
4581
- fs9.writeFileSync(tmp, json, { encoding: "utf8", mode });
4582
- fs9.renameSync(tmp, filePath);
4584
+ try {
4585
+ fs9.writeFileSync(tmp, json, { encoding: "utf8", mode });
4586
+ fs9.renameSync(tmp, filePath);
4587
+ } catch (err) {
4588
+ try {
4589
+ fs9.unlinkSync(tmp);
4590
+ } catch {
4591
+ }
4592
+ throw err;
4593
+ }
4594
+ }
4595
+ function atomicWriteFile(filePath, content, mode = 384) {
4596
+ fs9.mkdirSync(path8.dirname(filePath), { recursive: true, mode: 448 });
4597
+ const tmp = filePath + ".tmp";
4598
+ try {
4599
+ fs9.writeFileSync(tmp, content, { encoding: "utf8", mode });
4600
+ fs9.renameSync(tmp, filePath);
4601
+ } catch (err) {
4602
+ try {
4603
+ fs9.unlinkSync(tmp);
4604
+ } catch {
4605
+ }
4606
+ throw err;
4607
+ }
4608
+ }
4609
+ function readFileOrNull(filePath) {
4610
+ try {
4611
+ return fs9.readFileSync(filePath, "utf8");
4612
+ } catch {
4613
+ return null;
4614
+ }
4583
4615
  }
4584
4616
  function readPendingProfile(profileId) {
4585
4617
  try {
@@ -4618,30 +4650,55 @@ var require_codexProfiles = __commonJS({
4618
4650
  return null;
4619
4651
  }
4620
4652
  }
4621
- function readMetadataFromAuthJson(codexHome) {
4622
- const authPath = path8.join(codexHome, "auth.json");
4623
- if (!fs9.existsSync(authPath))
4624
- return {};
4653
+ function parseAuthJson(raw) {
4654
+ if (!raw)
4655
+ return null;
4625
4656
  try {
4626
- const parsed = JSON.parse(fs9.readFileSync(authPath, "utf8"));
4627
- const idToken = parsed.tokens?.id_token;
4628
- const claims = idToken ? parseJwtPayload(idToken) : null;
4629
- const profileClaims = claims?.["https://api.openai.com/profile"];
4630
- const authClaims = claims?.["https://api.openai.com/auth"];
4631
- const email = typeof claims?.email === "string" ? claims.email : typeof profileClaims?.email === "string" ? profileClaims.email : void 0;
4632
- const workspaceId = typeof authClaims?.chatgpt_account_id === "string" ? authClaims.chatgpt_account_id : parsed.tokens?.account_id;
4633
- const planType = typeof authClaims?.chatgpt_plan_type === "string" ? authClaims.chatgpt_plan_type : void 0;
4634
- const authMode = parsed.OPENAI_API_KEY || parsed.auth_mode === "api_key" ? "api-key" : "chatgpt";
4635
- return {
4636
- email,
4637
- workspaceId,
4638
- planType,
4639
- authMode
4640
- };
4657
+ return JSON.parse(raw);
4641
4658
  } catch {
4642
- return {};
4659
+ return null;
4643
4660
  }
4644
4661
  }
4662
+ function readAuthIdentityFromRaw(raw) {
4663
+ const parsed = parseAuthJson(raw);
4664
+ if (!parsed)
4665
+ return null;
4666
+ const idToken = parsed.tokens?.id_token;
4667
+ const claims = idToken ? parseJwtPayload(idToken) : null;
4668
+ const profileClaims = claims?.["https://api.openai.com/profile"];
4669
+ const authClaims = claims?.["https://api.openai.com/auth"];
4670
+ const email = typeof claims?.email === "string" ? claims.email : typeof profileClaims?.email === "string" ? profileClaims.email : void 0;
4671
+ const workspaceId = typeof authClaims?.chatgpt_account_id === "string" ? authClaims.chatgpt_account_id : parsed.tokens?.account_id;
4672
+ const planType = typeof authClaims?.chatgpt_plan_type === "string" ? authClaims.chatgpt_plan_type : void 0;
4673
+ const authMode = parsed.OPENAI_API_KEY || parsed.auth_mode === "api_key" ? "api-key" : "chatgpt";
4674
+ return { email, workspaceId, planType, authMode };
4675
+ }
4676
+ function readLastRefresh(raw, fallbackPath) {
4677
+ const parsed = parseAuthJson(raw);
4678
+ if (parsed?.last_refresh) {
4679
+ const ts = Date.parse(parsed.last_refresh);
4680
+ if (!Number.isNaN(ts))
4681
+ return ts;
4682
+ }
4683
+ if (fallbackPath) {
4684
+ try {
4685
+ return fs9.statSync(fallbackPath).mtimeMs;
4686
+ } catch {
4687
+ }
4688
+ }
4689
+ return null;
4690
+ }
4691
+ function readMetadataFromAuthJson(codexHome) {
4692
+ const identity = readAuthIdentityFromRaw(readFileOrNull(path8.join(codexHome, "auth.json")));
4693
+ if (!identity)
4694
+ return {};
4695
+ return {
4696
+ email: identity.email,
4697
+ workspaceId: identity.workspaceId,
4698
+ planType: identity.planType,
4699
+ authMode: identity.authMode
4700
+ };
4701
+ }
4645
4702
  function readMetadataFromLegacyCredentials(codexHome) {
4646
4703
  const legacyPath = path8.join(codexHome, ".credentials.json");
4647
4704
  if (!fs9.existsSync(legacyPath))
@@ -4676,6 +4733,15 @@ var require_codexProfiles = __commonJS({
4676
4733
  }
4677
4734
  return { loggedIn: false };
4678
4735
  }
4736
+ function detectRunningCodexProcess() {
4737
+ if (process.platform === "win32")
4738
+ return false;
4739
+ try {
4740
+ return (0, child_process_1.spawnSync)("pgrep", ["-x", "codex"], { encoding: "utf8" }).status === 0;
4741
+ } catch {
4742
+ return false;
4743
+ }
4744
+ }
4679
4745
  function readCodexAccountMetadata(codexHome) {
4680
4746
  const fromAuth = readMetadataFromAuthJson(codexHome);
4681
4747
  if (fromAuth.email || fromAuth.workspaceId || fromAuth.planType || fromAuth.authMode) {
@@ -4711,15 +4777,6 @@ var require_codexProfiles = __commonJS({
4711
4777
  return (0, accountRegistry_1.getActiveSavedAccount)("codex");
4712
4778
  }
4713
4779
  function resolveSidekickCodexHome() {
4714
- const explicitHome = process.env.CODEX_HOME;
4715
- if (explicitHome)
4716
- return explicitHome;
4717
- const active = getActiveCodexAccount3();
4718
- if (active) {
4719
- const managedHome = getCodexProfileHome(active.id);
4720
- if (fs9.existsSync(managedHome))
4721
- return managedHome;
4722
- }
4723
4780
  return getSystemCodexHome();
4724
4781
  }
4725
4782
  function getCodexExecutionEnv2(baseEnv = process.env) {
@@ -4773,24 +4830,245 @@ var require_codexProfiles = __commonJS({
4773
4830
  return { success: false, error: "Codex profile is not authenticated yet." };
4774
4831
  }
4775
4832
  const metadata = readCodexAccountMetadata(codexHome);
4776
- (0, accountRegistry_1.upsertSavedAccountProfile)({
4833
+ const profile = {
4777
4834
  id: profileId,
4778
4835
  providerId: "codex",
4779
4836
  label: pending.label,
4780
4837
  email: metadata.email,
4781
4838
  addedAt: pending.addedAt,
4782
4839
  metadata
4783
- });
4784
- (0, accountRegistry_1.setActiveSavedAccount)("codex", profileId);
4785
- return { success: true };
4840
+ };
4841
+ (0, accountRegistry_1.upsertSavedAccountProfile)(profile);
4842
+ const hasCredentialFiles = fs9.existsSync(path8.join(codexHome, "auth.json")) || fs9.existsSync(path8.join(codexHome, ".credentials.json"));
4843
+ if (!hasCredentialFiles) {
4844
+ (0, accountRegistry_1.setActiveSavedAccount)("codex", profileId);
4845
+ return {
4846
+ success: true,
4847
+ warning: "Codex stores credentials in the OS keyring; sidekick cannot swap them per account, so `codex` keeps using the keyring credentials."
4848
+ };
4849
+ }
4850
+ return performCodexAuthSwap(profile);
4851
+ }
4852
+ function getCodexStashDir() {
4853
+ return path8.join((0, accountRegistry_1.getAccountsDir)(), "codex", "stash");
4854
+ }
4855
+ function stashLiveCodexAuth(liveAuthRaw, liveLegacyRaw) {
4856
+ try {
4857
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4858
+ let stashPath = null;
4859
+ if (liveAuthRaw) {
4860
+ stashPath = path8.join(getCodexStashDir(), `auth-${stamp}.json`);
4861
+ atomicWriteFile(stashPath, liveAuthRaw);
4862
+ }
4863
+ if (liveLegacyRaw) {
4864
+ const legacyStashPath = path8.join(getCodexStashDir(), `credentials-${stamp}.json`);
4865
+ atomicWriteFile(legacyStashPath, liveLegacyRaw);
4866
+ stashPath = stashPath ?? legacyStashPath;
4867
+ }
4868
+ return stashPath;
4869
+ } catch {
4870
+ return null;
4871
+ }
4872
+ }
4873
+ function findProfileForIdentity(identity) {
4874
+ const profiles = listCodexAccounts2();
4875
+ if (identity?.workspaceId) {
4876
+ const byWorkspace = profiles.find((profile) => profile.metadata?.workspaceId === identity.workspaceId);
4877
+ if (byWorkspace)
4878
+ return byWorkspace;
4879
+ }
4880
+ if (identity?.email) {
4881
+ const byEmail = profiles.find((profile) => (profile.email ?? profile.metadata?.email) === identity.email);
4882
+ if (byEmail)
4883
+ return byEmail;
4884
+ }
4885
+ if (!identity?.workspaceId && !identity?.email) {
4886
+ return getActiveCodexAccount3();
4887
+ }
4888
+ return null;
4889
+ }
4890
+ function syncBackLiveCodexAuth(liveAuthRaw, liveLegacyRaw) {
4891
+ if (!liveAuthRaw && !liveLegacyRaw)
4892
+ return {};
4893
+ try {
4894
+ const identity = readAuthIdentityFromRaw(liveAuthRaw);
4895
+ const profile = findProfileForIdentity(identity);
4896
+ if (!profile) {
4897
+ const stashPath = stashLiveCodexAuth(liveAuthRaw, liveLegacyRaw);
4898
+ return {
4899
+ stashPath: stashPath ?? void 0,
4900
+ warning: stashPath ? `Live Codex credentials did not match any saved account; stashed at ${stashPath}.` : "Live Codex credentials did not match any saved account and could not be stashed."
4901
+ };
4902
+ }
4903
+ const profileHome = getCodexProfileHome(profile.id);
4904
+ if (liveAuthRaw)
4905
+ atomicWriteFile(path8.join(profileHome, "auth.json"), liveAuthRaw);
4906
+ if (liveLegacyRaw)
4907
+ atomicWriteFile(path8.join(profileHome, ".credentials.json"), liveLegacyRaw);
4908
+ try {
4909
+ const metadata = readCodexAccountMetadata(profileHome);
4910
+ (0, accountRegistry_1.upsertSavedAccountProfile)({
4911
+ ...profile,
4912
+ email: metadata.email ?? profile.email,
4913
+ metadata: { ...profile.metadata, ...metadata }
4914
+ });
4915
+ } catch {
4916
+ }
4917
+ return { syncedProfileId: profile.id };
4918
+ } catch (err) {
4919
+ return { warning: `Could not back up live Codex credentials: ${err}` };
4920
+ }
4921
+ }
4922
+ function performCodexAuthSwap(target) {
4923
+ const systemHome = getSystemCodexHome();
4924
+ const liveAuthPath = path8.join(systemHome, "auth.json");
4925
+ const liveLegacyPath = path8.join(systemHome, ".credentials.json");
4926
+ const liveAuthRaw = readFileOrNull(liveAuthPath);
4927
+ const liveLegacyRaw = readFileOrNull(liveLegacyPath);
4928
+ if (!liveAuthRaw && !liveLegacyRaw && getCodexLoginStatus(systemHome).loggedIn) {
4929
+ return {
4930
+ success: false,
4931
+ error: 'Codex stores credentials in the OS keyring; file-based account switching is not supported. Set `cli_auth_credentials_store = "file"` in ~/.codex/config.toml and run `codex login` again.'
4932
+ };
4933
+ }
4934
+ const profileHome = getCodexProfileHome(target.id);
4935
+ const targetAuthPath = path8.join(profileHome, "auth.json");
4936
+ const targetAuthRaw = readFileOrNull(targetAuthPath);
4937
+ const targetLegacyRaw = readFileOrNull(path8.join(profileHome, ".credentials.json"));
4938
+ const targetName = target.label ?? target.email ?? target.id;
4939
+ if (!targetAuthRaw && !targetLegacyRaw) {
4940
+ return { success: false, error: `No stored credentials for "${targetName}". Remove and re-add this account.` };
4941
+ }
4942
+ if (targetAuthRaw && !parseAuthJson(targetAuthRaw)) {
4943
+ return { success: false, error: `Stored credentials for "${targetName}" are corrupted. Remove and re-add this account.` };
4944
+ }
4945
+ const warnings = [];
4946
+ if (detectRunningCodexProcess()) {
4947
+ warnings.push("A codex process appears to be running; restart codex sessions so they pick up the switched account.");
4948
+ }
4949
+ const liveIdentity = readAuthIdentityFromRaw(liveAuthRaw);
4950
+ const targetIdentity = readAuthIdentityFromRaw(targetAuthRaw);
4951
+ const targetWorkspaceId = target.metadata?.workspaceId ?? targetIdentity?.workspaceId;
4952
+ const targetEmail = target.email ?? target.metadata?.email ?? targetIdentity?.email;
4953
+ const liveMatchesTarget = Boolean(liveIdentity?.workspaceId && targetWorkspaceId && liveIdentity.workspaceId === targetWorkspaceId || liveIdentity?.email && targetEmail && liveIdentity.email === targetEmail || liveAuthRaw !== null && liveAuthRaw === targetAuthRaw || !liveAuthRaw && !targetAuthRaw && liveLegacyRaw !== null && liveLegacyRaw === targetLegacyRaw);
4954
+ if (liveMatchesTarget) {
4955
+ try {
4956
+ if (liveAuthRaw)
4957
+ atomicWriteFile(targetAuthPath, liveAuthRaw);
4958
+ if (liveLegacyRaw)
4959
+ atomicWriteFile(path8.join(profileHome, ".credentials.json"), liveLegacyRaw);
4960
+ const metadata = readCodexAccountMetadata(profileHome);
4961
+ (0, accountRegistry_1.upsertSavedAccountProfile)({
4962
+ ...target,
4963
+ email: metadata.email ?? target.email,
4964
+ metadata: { ...target.metadata, ...metadata }
4965
+ });
4966
+ } catch {
4967
+ }
4968
+ (0, accountRegistry_1.setActiveSavedAccount)("codex", target.id);
4969
+ return { success: true, warning: warnings.length ? warnings.join(" ") : void 0 };
4970
+ }
4971
+ const targetLastRefresh = readLastRefresh(targetAuthRaw, targetAuthPath);
4972
+ if (targetLastRefresh !== null && Date.now() - targetLastRefresh > STALE_AUTH_THRESHOLD_MS) {
4973
+ warnings.push(`Stored credentials for "${targetName}" have not been refreshed in over 8 days; codex may ask you to log in again.`);
4974
+ }
4975
+ const syncBack = syncBackLiveCodexAuth(liveAuthRaw, liveLegacyRaw);
4976
+ if (syncBack.warning)
4977
+ warnings.push(syncBack.warning);
4978
+ const restoreLiveFiles = () => {
4979
+ try {
4980
+ if (liveAuthRaw)
4981
+ atomicWriteFile(liveAuthPath, liveAuthRaw);
4982
+ else
4983
+ fs9.rmSync(liveAuthPath, { force: true });
4984
+ if (liveLegacyRaw)
4985
+ atomicWriteFile(liveLegacyPath, liveLegacyRaw);
4986
+ else
4987
+ fs9.rmSync(liveLegacyPath, { force: true });
4988
+ } catch {
4989
+ }
4990
+ };
4991
+ try {
4992
+ if (targetAuthRaw) {
4993
+ atomicWriteFile(liveAuthPath, targetAuthRaw);
4994
+ if (targetLegacyRaw)
4995
+ atomicWriteFile(liveLegacyPath, targetLegacyRaw);
4996
+ else if (liveLegacyRaw)
4997
+ fs9.rmSync(liveLegacyPath, { force: true });
4998
+ } else {
4999
+ atomicWriteFile(liveLegacyPath, targetLegacyRaw);
5000
+ if (liveAuthRaw)
5001
+ fs9.rmSync(liveAuthPath, { force: true });
5002
+ }
5003
+ } catch (err) {
5004
+ restoreLiveFiles();
5005
+ return { success: false, error: `Failed to write Codex credentials: ${err}` };
5006
+ }
5007
+ try {
5008
+ (0, accountRegistry_1.setActiveSavedAccount)("codex", target.id);
5009
+ } catch (err) {
5010
+ restoreLiveFiles();
5011
+ return { success: false, error: `Failed to update account registry: ${err}` };
5012
+ }
5013
+ return { success: true, warning: warnings.length ? warnings.join(" ") : void 0 };
4786
5014
  }
4787
5015
  function switchToCodexAccount2(profileId) {
4788
5016
  const target = listCodexAccounts2().find((account) => account.id === profileId);
4789
5017
  if (!target) {
4790
5018
  return { success: false, error: `Codex account ${profileId} not found.` };
4791
5019
  }
4792
- (0, accountRegistry_1.setActiveSavedAccount)("codex", profileId);
4793
- return { success: true };
5020
+ return performCodexAuthSwap(target);
5021
+ }
5022
+ function reconcileCodexAuthState() {
5023
+ try {
5024
+ const markerPath = path8.join((0, accountRegistry_1.getAccountsDir)(), "codex", ".live-auth-migrated-v1");
5025
+ if (fs9.existsSync(markerPath))
5026
+ return;
5027
+ const writeMarker = () => atomicWriteFile(markerPath, (/* @__PURE__ */ new Date()).toISOString() + "\n");
5028
+ const active = getActiveCodexAccount3();
5029
+ if (!active) {
5030
+ writeMarker();
5031
+ return;
5032
+ }
5033
+ const profileHome = getCodexProfileHome(active.id);
5034
+ const profileAuthPath = path8.join(profileHome, "auth.json");
5035
+ const profileAuthRaw = readFileOrNull(profileAuthPath);
5036
+ if (!profileAuthRaw) {
5037
+ writeMarker();
5038
+ return;
5039
+ }
5040
+ const systemHome = getSystemCodexHome();
5041
+ const liveAuthPath = path8.join(systemHome, "auth.json");
5042
+ const liveAuthRaw = readFileOrNull(liveAuthPath);
5043
+ if (!liveAuthRaw) {
5044
+ if (!getCodexLoginStatus(systemHome).loggedIn) {
5045
+ atomicWriteFile(liveAuthPath, profileAuthRaw);
5046
+ }
5047
+ writeMarker();
5048
+ return;
5049
+ }
5050
+ const liveIdentity = readAuthIdentityFromRaw(liveAuthRaw);
5051
+ const profileIdentity = readAuthIdentityFromRaw(profileAuthRaw);
5052
+ const sameIdentity = Boolean(liveIdentity?.workspaceId && profileIdentity?.workspaceId && liveIdentity.workspaceId === profileIdentity.workspaceId || liveIdentity?.email && liveIdentity.email === profileIdentity?.email);
5053
+ if (sameIdentity) {
5054
+ const liveRefresh = readLastRefresh(liveAuthRaw, liveAuthPath);
5055
+ const profileRefresh = readLastRefresh(profileAuthRaw, profileAuthPath);
5056
+ if (profileRefresh !== null && (liveRefresh === null || profileRefresh > liveRefresh)) {
5057
+ stashLiveCodexAuth(liveAuthRaw, null);
5058
+ atomicWriteFile(liveAuthPath, profileAuthRaw);
5059
+ } else {
5060
+ atomicWriteFile(profileAuthPath, liveAuthRaw);
5061
+ }
5062
+ } else {
5063
+ const matching = findProfileForIdentity(liveIdentity);
5064
+ if (matching && matching.id !== active.id) {
5065
+ atomicWriteFile(path8.join(getCodexProfileHome(matching.id), "auth.json"), liveAuthRaw);
5066
+ (0, accountRegistry_1.setActiveSavedAccount)("codex", matching.id);
5067
+ }
5068
+ }
5069
+ writeMarker();
5070
+ } catch {
5071
+ }
4794
5072
  }
4795
5073
  function removeCodexAccount2(profileId) {
4796
5074
  const removed = (0, accountRegistry_1.removeSavedAccountProfile)("codex", profileId);
@@ -5939,7 +6217,9 @@ var require_modelContext = __commonJS({
5939
6217
  exports.DEFAULT_CONTEXT_WINDOW = void 0;
5940
6218
  exports.getModelContextWindowSize = getModelContextWindowSize2;
5941
6219
  var MODEL_CONTEXT_SIZES = {
5942
- // Claude — native 1M context (Opus 4.6+, Sonnet 4.6+)
6220
+ // Claude — native 1M context (Fable 5, Opus 4.6+, Sonnet 4.6+)
6221
+ "claude-fable-5": 1e6,
6222
+ "claude-opus-4-8": 1e6,
5943
6223
  "claude-opus-4-7": 1e6,
5944
6224
  "claude-opus-4-6": 1e6,
5945
6225
  "claude-sonnet-4-7": 1e6,
@@ -6021,6 +6301,18 @@ var require_modelInfo = __commonJS({
6021
6301
  var modelContext_1 = require_modelContext();
6022
6302
  var PRICING_TABLE = {
6023
6303
  // ── Anthropic: Claude ──
6304
+ "claude-fable-5": {
6305
+ inputCostPerMillion: 10,
6306
+ outputCostPerMillion: 50,
6307
+ cacheWriteCostPerMillion: 12.5,
6308
+ cacheReadCostPerMillion: 1
6309
+ },
6310
+ "claude-haiku-4-5": {
6311
+ inputCostPerMillion: 1,
6312
+ outputCostPerMillion: 5,
6313
+ cacheWriteCostPerMillion: 1.25,
6314
+ cacheReadCostPerMillion: 0.1
6315
+ },
6024
6316
  "claude-haiku-4.5": {
6025
6317
  inputCostPerMillion: 1,
6026
6318
  outputCostPerMillion: 5,
@@ -6033,12 +6325,24 @@ var require_modelInfo = __commonJS({
6033
6325
  cacheWriteCostPerMillion: 1,
6034
6326
  cacheReadCostPerMillion: 0.08
6035
6327
  },
6328
+ "claude-sonnet-4-6": {
6329
+ inputCostPerMillion: 3,
6330
+ outputCostPerMillion: 15,
6331
+ cacheWriteCostPerMillion: 3.75,
6332
+ cacheReadCostPerMillion: 0.3
6333
+ },
6036
6334
  "claude-sonnet-4.6": {
6037
6335
  inputCostPerMillion: 3,
6038
6336
  outputCostPerMillion: 15,
6039
6337
  cacheWriteCostPerMillion: 3.75,
6040
6338
  cacheReadCostPerMillion: 0.3
6041
6339
  },
6340
+ "claude-sonnet-4-5": {
6341
+ inputCostPerMillion: 3,
6342
+ outputCostPerMillion: 15,
6343
+ cacheWriteCostPerMillion: 3.75,
6344
+ cacheReadCostPerMillion: 0.3
6345
+ },
6042
6346
  "claude-sonnet-4.5": {
6043
6347
  inputCostPerMillion: 3,
6044
6348
  outputCostPerMillion: 15,
@@ -6051,11 +6355,41 @@ var require_modelInfo = __commonJS({
6051
6355
  cacheWriteCostPerMillion: 3.75,
6052
6356
  cacheReadCostPerMillion: 0.3
6053
6357
  },
6358
+ "claude-opus-4-8": {
6359
+ inputCostPerMillion: 5,
6360
+ outputCostPerMillion: 25,
6361
+ cacheWriteCostPerMillion: 6.25,
6362
+ cacheReadCostPerMillion: 0.5
6363
+ },
6364
+ "claude-opus-4.8": {
6365
+ inputCostPerMillion: 5,
6366
+ outputCostPerMillion: 25,
6367
+ cacheWriteCostPerMillion: 6.25,
6368
+ cacheReadCostPerMillion: 0.5
6369
+ },
6370
+ "claude-opus-4-7": {
6371
+ inputCostPerMillion: 5,
6372
+ outputCostPerMillion: 25,
6373
+ cacheWriteCostPerMillion: 6.25,
6374
+ cacheReadCostPerMillion: 0.5
6375
+ },
6376
+ "claude-opus-4.7": {
6377
+ inputCostPerMillion: 5,
6378
+ outputCostPerMillion: 25,
6379
+ cacheWriteCostPerMillion: 6.25,
6380
+ cacheReadCostPerMillion: 0.5
6381
+ },
6382
+ "claude-opus-4-6": {
6383
+ inputCostPerMillion: 5,
6384
+ outputCostPerMillion: 25,
6385
+ cacheWriteCostPerMillion: 6.25,
6386
+ cacheReadCostPerMillion: 0.5
6387
+ },
6054
6388
  "claude-opus-4.6": {
6055
- inputCostPerMillion: 15,
6056
- outputCostPerMillion: 75,
6057
- cacheWriteCostPerMillion: 18.75,
6058
- cacheReadCostPerMillion: 1.5
6389
+ inputCostPerMillion: 5,
6390
+ outputCostPerMillion: 25,
6391
+ cacheWriteCostPerMillion: 6.25,
6392
+ cacheReadCostPerMillion: 0.5
6059
6393
  },
6060
6394
  "claude-opus-4.5": {
6061
6395
  inputCostPerMillion: 5,
@@ -6063,6 +6397,7 @@ var require_modelInfo = __commonJS({
6063
6397
  cacheWriteCostPerMillion: 6.25,
6064
6398
  cacheReadCostPerMillion: 0.5
6065
6399
  },
6400
+ // Opus 4.0 / 4.1 — pre-4.5 pricing tier
6066
6401
  "claude-opus-4": {
6067
6402
  inputCostPerMillion: 15,
6068
6403
  outputCostPerMillion: 75,
@@ -6161,7 +6496,7 @@ var require_modelInfo = __commonJS({
6161
6496
  overrideTable = {};
6162
6497
  overrideSortedKeys = [];
6163
6498
  }
6164
- var CLAUDE_RE = /^claude-(haiku|sonnet|opus)-([0-9.]+)/i;
6499
+ var CLAUDE_RE = /^claude-(haiku|sonnet|opus|fable)-([0-9.]+)/i;
6165
6500
  var LEGACY_CLAUDE_RE = /^claude-([0-9]+(?:[-.][0-9]+)?)-(haiku|sonnet|opus)(?:-|$)/i;
6166
6501
  var GPT_RE = /^gpt-([0-9][0-9.A-Za-z-]*)/i;
6167
6502
  var O_SERIES_RE = /^o([0-9]+)(-mini|-pro)?/i;
@@ -6282,9 +6617,10 @@ var require_modelInfo = __commonJS({
6282
6617
  return modelId;
6283
6618
  }
6284
6619
  var CLAUDE_FAMILY_RANK = {
6285
- opus: 0,
6286
- sonnet: 1,
6287
- haiku: 2
6620
+ fable: 0,
6621
+ opus: 1,
6622
+ sonnet: 2,
6623
+ haiku: 3
6288
6624
  };
6289
6625
  function versionRank(version) {
6290
6626
  if (!version)
@@ -19309,6 +19645,11 @@ var require_ensureDefaultAccounts = __commonJS({
19309
19645
  async function ensureDefaultAccounts2(options) {
19310
19646
  const claude = await ensureDefaultClaudeAccount(options);
19311
19647
  const codex = ensureDefaultCodexAccount(options);
19648
+ try {
19649
+ (0, codexProfiles_1.reconcileCodexAuthState)();
19650
+ } catch (error) {
19651
+ logFailure(options, "Codex auth reconciliation failed.", error);
19652
+ }
19312
19653
  return { claude, codex };
19313
19654
  }
19314
19655
  }
@@ -40657,7 +40998,7 @@ var init_UpdateCheckService = __esm({
40657
40998
  /** Run the update check (one-shot). */
40658
40999
  async check() {
40659
41000
  try {
40660
- const current = "0.18.5";
41001
+ const current = "0.19.0";
40661
41002
  const cached = this.readCache();
40662
41003
  let latest;
40663
41004
  if (cached && Date.now() - cached.checkedAt < CACHE_TTL_MS) {
@@ -81274,7 +81615,7 @@ function StatusBar({
81274
81615
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: parseBlessedTags(BRAND_INLINE) }),
81275
81616
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { dimColor: true, children: [
81276
81617
  " v",
81277
- "0.18.5"
81618
+ "0.19.0"
81278
81619
  ] }),
81279
81620
  updateInfo && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { color: "yellow", children: [
81280
81621
  " (v",
@@ -81664,7 +82005,7 @@ function ChangelogOverlay({ entries, scrollOffset }) {
81664
82005
  " ",
81665
82006
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { bold: true, color: "cyan", children: [
81666
82007
  "Terminal Dashboard v",
81667
- "0.18.5"
82008
+ "0.19.0"
81668
82009
  ] }),
81669
82010
  latestDate ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { color: "gray", children: [
81670
82011
  " \u2014 ",
@@ -81986,7 +82327,7 @@ var init_mouse = __esm({
81986
82327
  var CHANGELOG_default;
81987
82328
  var init_CHANGELOG = __esm({
81988
82329
  "CHANGELOG.md"() {
81989
- 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.18.5] - 2026-06-04\n\n### Changed\n\n- **Consistent Codex transcripts**: `sidekick dashboard` and `sidekick report` now parse Codex sessions via `parseTranscriptFromEvents()`, matching the canonical `SessionEvent` pipeline used by the other providers\n- **Bundled `sidekick-shared` 0.18.5**: Picks up the new session context evidence snapshot API (`buildSessionContextSnapshot`, `readSessionContextSnapshot`, `SessionContextSnapshot` and related types) and the Codex session evidence gap closures \u2014 `system` audit events, normalized `token_count` rate limits, per-file `apply_patch` expansion, tool-emission dedupe, MCP server attribution, and the new `ProviderReaderSessionWatcher`\n\n## [0.18.4] - 2026-05-27\n\n### Added\n\n- **`sidekick peak --provider <id>`**: New flag gates peak-hours output on the session provider. When the resolved provider is not `claude-code`, the command prints a "not applicable" message instead of calling the upstream endpoint\n\n### Changed\n\n- **Bundled `sidekick-shared` 0.18.4**: Picks up `scopePeakHoursToSessionProvider()`, `isClaudeCodeSessionProvider()`, `createPeakHoursNotApplicableState()` for peak-hours scoping, the improved Codex quota snapshot selection logic (`isPreferredQuotaHit`, `findAccountRolloutFiles`, `shouldKeepExistingSnapshot`), and the `notApplicable` field on `PeakHoursState`\n\n## [0.18.3] - 2026-05-19\n\n### Added\n\n- **`sidekick quota history`**: New subcommand that renders a 13-week GitHub-contributions-style heatmap of quota utilization for the current workspace. Flags: `--weeks <n>` (1-26, default 13), `--provider claude|codex` (default both), `--workspace <path>` (default cwd). Bucketed glyphs (`\xB7 \u2591 \u2592 \u2593 \u2588`) are color-coded by utilization band (\u22640 / <25 / <50 / <75 / \u226575), with per-provider rows and a peak / avg / unavailable-days / samples footer. Days that hit `available: false` render as a red `\xD7`. With `--json`, emits a `{ workspaceId, weeks, providers: { claude?, codex? }, generatedAt }` payload \u2014 the same shape consumed by the VS Code dashboard\n\n### Changed\n\n- **Bundled `sidekick-shared` 0.18.3**: Picks up the new per-workspace quota history surface (`appendQuotaHistorySample`, `readQuotaHistoryRange`, `readQuotaHistoryDailyBuckets`, `pruneQuotaHistory`, `getWorkspaceIdFromPath`) and the optional `workspaceId` / `appendHistorySample` hooks on `CodexQuotaWatcher`\n\n## [0.18.2] - 2026-05-19\n\n### Added\n\n- **`sidekick quota --refresh`**: New flag on the `quota` command that, for Codex, explicitly refreshes from the ChatGPT usage API before falling back to local rollout data and cached snapshots. Without the flag, the Codex quota path stays fully local and makes no upstream network call\n\n### Changed\n\n- **Codex quota is local-only by default**: `sidekick quota --provider codex` now delegates to the new `resolveCodexQuota` orchestrator in `sidekick-shared`. It checks the current workspace\'s most recent rollout, then recent account-level rollouts under `CODEX_HOME/sessions`, then the active account\'s cached snapshot \u2014 no upstream network call unless `--refresh` is passed. Failure output continues to include structured `failureKind` / `httpStatus` / `retryAfterMs` fields under `--json`\n- **Bundled `sidekick-shared` 0.18.2**: Picks up the new Codex quota orchestrator (`resolveCodexQuota`, `resolveCodexQuotaFromLocalSources`, `readLatestCodexQuotaFromRollouts`, `fetchCodexQuotaFromApi`), the relaxed `CodexRateLimits` shape (nullable `resets_at` / `window_minutes`), the rate-limit-only `token_count` event emission in `JsonlSessionWatcher`, and `state_N.sqlite` discovery in `CodexDatabase` + provider auto-detect\n\n## [0.18.1] - 2026-05-08\n\n### Changed\n\n- **Shared dashboard formatting**: terminal dashboard `fmtNum()` and `formatDuration()` now delegate to `formatTokenCount()` and `formatDurationMs()` from `sidekick-shared`, keeping the existing CLI surface (uppercase `K`/`M` suffix, compact `1m5s` style) while removing forked rounding logic\n\n## [0.18.0] - 2026-05-08\n\n### Changed\n\n- **Bundled `sidekick-shared` 0.18.0**: Picks up the new provider-aware quota orchestration surface \u2014 `MultiProviderQuotaService`, `CodexQuotaWatcher`, `getActiveAccountStatus()`, `extractToolCall()`, cost-provenance helpers (`calculateCostWithProvenance`, `mergeCostSources`), and model display helpers (`shortModelName`, `getModelDisplayInfo`, `compareModelIds`, `sortModelIds`). `parseModelId()` also now recognizes legacy Claude IDs such as `claude-3-opus-20240229` and `claude-3-5-sonnet-20241022`\n- **No CLI runtime changes**: This release ships the shared library upgrade for downstream tooling alignment; `sidekick quota`, `sidekick status`, and the live dashboard keep using the existing polling path. Wiring the new orchestrator into the CLI will land in a follow-up release\n\n## [0.17.7] - 2026-04-28\n\n### Fixed\n\n- **Quota snapshot write race**: Updated the bundled `sidekick-shared` snapshot writer so concurrent `sidekick quota` / Codex session updates no longer collide on `quota-snapshots.json.tmp` or throw `ENOENT`. Failed writes now also clean up their partial temp files instead of leaving orphans in `~/.config/sidekick/`\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';
82330
+ 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.19.0] - 2026-06-09\n\n### Added\n\n- **Claude Opus 4.8 & Fable 5 support**: The dashboard\'s context-window gauge and cost estimates recognize `claude-opus-4-8` and `claude-fable-5` (both 1M-token context; Opus 4.8: $5/$25 per MTok, Fable 5: $10/$50 per MTok) via the shared model catalog\n\n### Changed\n\n- **Codex account switching now swaps `~/.codex/auth.json`**: `sidekick account --provider codex --switch-to <id>` (and `--add`) activates the account by atomically swapping its backed-up credentials into the system `~/.codex/` home, mirroring the Claude switch pattern \u2014 codex terminals outside Sidekick pick up the switch. Profile directories become pure credential backups, with a one-time startup migration for installs created under the old `CODEX_HOME`-redirection model. The command surfaces swap warnings on add, switch, and remove: a running codex process that needs restarting, stale credentials, or OS-keyring credential storage that Sidekick cannot swap\n\n### Fixed\n\n- **Opus 4.6/4.7 cost over-estimation**: Dashed model IDs fell back to the Opus 4.0 pricing tier ($15/$75 instead of $5/$25), inflating estimated costs 3\xD7\n- **Haiku 4.5 unpriced under dashed IDs**: Costs for `claude-haiku-4-5-*` sessions could render as "\u2014" because no dashed static pricing key existed\n\n## [0.18.5] - 2026-06-04\n\n### Changed\n\n- **Consistent Codex transcripts**: `sidekick dashboard` and `sidekick report` now parse Codex sessions via `parseTranscriptFromEvents()`, matching the canonical `SessionEvent` pipeline used by the other providers\n- **Bundled `sidekick-shared` 0.18.5**: Picks up the new session context evidence snapshot API (`buildSessionContextSnapshot`, `readSessionContextSnapshot`, `SessionContextSnapshot` and related types) and the Codex session evidence gap closures \u2014 `system` audit events, normalized `token_count` rate limits, per-file `apply_patch` expansion, tool-emission dedupe, MCP server attribution, and the new `ProviderReaderSessionWatcher`\n\n## [0.18.4] - 2026-05-27\n\n### Added\n\n- **`sidekick peak --provider <id>`**: New flag gates peak-hours output on the session provider. When the resolved provider is not `claude-code`, the command prints a "not applicable" message instead of calling the upstream endpoint\n\n### Changed\n\n- **Bundled `sidekick-shared` 0.18.4**: Picks up `scopePeakHoursToSessionProvider()`, `isClaudeCodeSessionProvider()`, `createPeakHoursNotApplicableState()` for peak-hours scoping, the improved Codex quota snapshot selection logic (`isPreferredQuotaHit`, `findAccountRolloutFiles`, `shouldKeepExistingSnapshot`), and the `notApplicable` field on `PeakHoursState`\n\n## [0.18.3] - 2026-05-19\n\n### Added\n\n- **`sidekick quota history`**: New subcommand that renders a 13-week GitHub-contributions-style heatmap of quota utilization for the current workspace. Flags: `--weeks <n>` (1-26, default 13), `--provider claude|codex` (default both), `--workspace <path>` (default cwd). Bucketed glyphs (`\xB7 \u2591 \u2592 \u2593 \u2588`) are color-coded by utilization band (\u22640 / <25 / <50 / <75 / \u226575), with per-provider rows and a peak / avg / unavailable-days / samples footer. Days that hit `available: false` render as a red `\xD7`. With `--json`, emits a `{ workspaceId, weeks, providers: { claude?, codex? }, generatedAt }` payload \u2014 the same shape consumed by the VS Code dashboard\n\n### Changed\n\n- **Bundled `sidekick-shared` 0.18.3**: Picks up the new per-workspace quota history surface (`appendQuotaHistorySample`, `readQuotaHistoryRange`, `readQuotaHistoryDailyBuckets`, `pruneQuotaHistory`, `getWorkspaceIdFromPath`) and the optional `workspaceId` / `appendHistorySample` hooks on `CodexQuotaWatcher`\n\n## [0.18.2] - 2026-05-19\n\n### Added\n\n- **`sidekick quota --refresh`**: New flag on the `quota` command that, for Codex, explicitly refreshes from the ChatGPT usage API before falling back to local rollout data and cached snapshots. Without the flag, the Codex quota path stays fully local and makes no upstream network call\n\n### Changed\n\n- **Codex quota is local-only by default**: `sidekick quota --provider codex` now delegates to the new `resolveCodexQuota` orchestrator in `sidekick-shared`. It checks the current workspace\'s most recent rollout, then recent account-level rollouts under `CODEX_HOME/sessions`, then the active account\'s cached snapshot \u2014 no upstream network call unless `--refresh` is passed. Failure output continues to include structured `failureKind` / `httpStatus` / `retryAfterMs` fields under `--json`\n- **Bundled `sidekick-shared` 0.18.2**: Picks up the new Codex quota orchestrator (`resolveCodexQuota`, `resolveCodexQuotaFromLocalSources`, `readLatestCodexQuotaFromRollouts`, `fetchCodexQuotaFromApi`), the relaxed `CodexRateLimits` shape (nullable `resets_at` / `window_minutes`), the rate-limit-only `token_count` event emission in `JsonlSessionWatcher`, and `state_N.sqlite` discovery in `CodexDatabase` + provider auto-detect\n\n## [0.18.1] - 2026-05-08\n\n### Changed\n\n- **Shared dashboard formatting**: terminal dashboard `fmtNum()` and `formatDuration()` now delegate to `formatTokenCount()` and `formatDurationMs()` from `sidekick-shared`, keeping the existing CLI surface (uppercase `K`/`M` suffix, compact `1m5s` style) while removing forked rounding logic\n\n## [0.18.0] - 2026-05-08\n\n### Changed\n\n- **Bundled `sidekick-shared` 0.18.0**: Picks up the new provider-aware quota orchestration surface \u2014 `MultiProviderQuotaService`, `CodexQuotaWatcher`, `getActiveAccountStatus()`, `extractToolCall()`, cost-provenance helpers (`calculateCostWithProvenance`, `mergeCostSources`), and model display helpers (`shortModelName`, `getModelDisplayInfo`, `compareModelIds`, `sortModelIds`). `parseModelId()` also now recognizes legacy Claude IDs such as `claude-3-opus-20240229` and `claude-3-5-sonnet-20241022`\n- **No CLI runtime changes**: This release ships the shared library upgrade for downstream tooling alignment; `sidekick quota`, `sidekick status`, and the live dashboard keep using the existing polling path. Wiring the new orchestrator into the CLI will land in a follow-up release\n\n## [0.17.7] - 2026-04-28\n\n### Fixed\n\n- **Quota snapshot write race**: Updated the bundled `sidekick-shared` snapshot writer so concurrent `sidekick quota` / Codex session updates no longer collide on `quota-snapshots.json.tmp` or throw `ENOENT`. Failed writes now also clean up their partial temp files instead of leaving orphans in `~/.config/sidekick/`\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';
81990
82331
  }
81991
82332
  });
81992
82333
 
@@ -84756,6 +85097,11 @@ function runCodexLogin(codexHome) {
84756
85097
  }
84757
85098
  return { success: true };
84758
85099
  }
85100
+ function printCodexWarning(warning, jsonOutput) {
85101
+ if (warning && !jsonOutput) {
85102
+ process.stderr.write(source_default.yellow(warning) + "\n");
85103
+ }
85104
+ }
84759
85105
  function codexAccountAction(opts, jsonOutput) {
84760
85106
  if (opts.add) {
84761
85107
  if (!opts.label?.trim()) {
@@ -84769,6 +85115,7 @@ function codexAccountAction(opts, jsonOutput) {
84769
85115
  process.exit(1);
84770
85116
  return;
84771
85117
  }
85118
+ let warning = prepared.warning;
84772
85119
  if (prepared.needsLogin) {
84773
85120
  if (!prepared.profileId || !prepared.codexHome) {
84774
85121
  process.stderr.write(source_default.red("Prepared Codex profile is missing login context.") + "\n");
@@ -84783,12 +85130,15 @@ function codexAccountAction(opts, jsonOutput) {
84783
85130
  }
84784
85131
  const finalized = (0, import_sidekick_shared33.finalizeCodexAccount)(prepared.profileId);
84785
85132
  if (!finalized.success) {
84786
- process.stderr.write(source_default.red(finalized.error ?? "Failed to finalize Codex account.") + "\n");
85133
+ process.stderr.write(source_default.red("Account saved, but activation failed: " + (finalized.error ?? "unknown error")) + "\n");
85134
+ process.stderr.write(source_default.dim("Use `sidekick account --provider codex --switch-to <label>` to retry activation.\n"));
84787
85135
  process.exit(1);
84788
85136
  return;
84789
85137
  }
85138
+ warning = finalized.warning;
84790
85139
  }
84791
85140
  const active2 = (0, import_sidekick_shared33.getActiveCodexAccount)();
85141
+ printCodexWarning(warning, jsonOutput);
84792
85142
  if (jsonOutput) {
84793
85143
  process.stdout.write(JSON.stringify({
84794
85144
  action: "added",
@@ -84796,7 +85146,8 @@ function codexAccountAction(opts, jsonOutput) {
84796
85146
  id: active2?.id,
84797
85147
  label: active2?.label,
84798
85148
  email: active2?.email ?? null,
84799
- authMode: active2?.metadata?.authMode ?? null
85149
+ authMode: active2?.metadata?.authMode ?? null,
85150
+ warning: warning ?? null
84800
85151
  }) + "\n");
84801
85152
  } else {
84802
85153
  process.stdout.write(source_default.green("Codex account saved: ") + (active2 ? formatCodexAccount(active2) : opts.label) + "\n");
@@ -84811,6 +85162,7 @@ function codexAccountAction(opts, jsonOutput) {
84811
85162
  process.exit(1);
84812
85163
  return;
84813
85164
  }
85165
+ const wasActive = (0, import_sidekick_shared33.getActiveCodexAccount)()?.id === target.id;
84814
85166
  const result = (0, import_sidekick_shared33.removeCodexAccount)(target.id);
84815
85167
  if (!result.success) {
84816
85168
  process.stderr.write(source_default.red(result.error ?? "Failed to remove Codex account.") + "\n");
@@ -84821,6 +85173,9 @@ function codexAccountAction(opts, jsonOutput) {
84821
85173
  process.stdout.write(JSON.stringify({ action: "removed", provider: "codex", id: target.id, label: target.label }) + "\n");
84822
85174
  } else {
84823
85175
  process.stdout.write(source_default.green("Removed: ") + formatCodexAccount(target) + "\n");
85176
+ if (wasActive) {
85177
+ process.stdout.write(source_default.dim("The live ~/.codex credentials are unchanged. Use `--switch-to` to activate another saved account.\n"));
85178
+ }
84824
85179
  }
84825
85180
  return;
84826
85181
  }
@@ -84838,8 +85193,9 @@ function codexAccountAction(opts, jsonOutput) {
84838
85193
  process.exit(1);
84839
85194
  return;
84840
85195
  }
85196
+ printCodexWarning(result.warning, jsonOutput);
84841
85197
  if (jsonOutput) {
84842
- process.stdout.write(JSON.stringify({ action: "switched", provider: "codex", id: target.id, label: target.label }) + "\n");
85198
+ process.stdout.write(JSON.stringify({ action: "switched", provider: "codex", id: target.id, label: target.label, warning: result.warning ?? null }) + "\n");
84843
85199
  } else {
84844
85200
  process.stdout.write(source_default.green("Switched to: ") + formatCodexAccount(target) + "\n");
84845
85201
  }
@@ -84862,8 +85218,9 @@ function codexAccountAction(opts, jsonOutput) {
84862
85218
  process.exit(1);
84863
85219
  return;
84864
85220
  }
85221
+ printCodexWarning(result.warning, jsonOutput);
84865
85222
  if (jsonOutput) {
84866
- process.stdout.write(JSON.stringify({ action: "switched", provider: "codex", id: target.id, label: target.label }) + "\n");
85223
+ process.stdout.write(JSON.stringify({ action: "switched", provider: "codex", id: target.id, label: target.label, warning: result.warning ?? null }) + "\n");
84867
85224
  } else {
84868
85225
  process.stdout.write(source_default.green("Switched to: ") + formatCodexAccount(target) + "\n");
84869
85226
  }
@@ -84997,7 +85354,7 @@ var init_cli = __esm({
84997
85354
  defaultAccountsReady = (0, import_sidekick_shared35.ensureDefaultAccounts)().catch(() => {
84998
85355
  });
84999
85356
  program2 = new Command();
85000
- program2.name("sidekick").description("Query Sidekick project intelligence from the command line").version("0.18.5").option("--json", "Output as JSON").option("--project <path>", "Override project path (default: cwd)").option("--provider <id>", "Provider: claude-code, opencode, codex, auto (default: auto)");
85357
+ program2.name("sidekick").description("Query Sidekick project intelligence from the command line").version("0.19.0").option("--json", "Output as JSON").option("--project <path>", "Override project path (default: cwd)").option("--provider <id>", "Provider: claude-code, opencode, codex, auto (default: auto)");
85001
85358
  program2.hook("preAction", async () => {
85002
85359
  await defaultAccountsReady;
85003
85360
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidekick-agent-hub",
3
- "version": "0.18.5",
3
+ "version": "0.19.0",
4
4
  "description": "Terminal dashboard for monitoring AI coding agent sessions",
5
5
  "type": "module",
6
6
  "bin": {