ccsidekick 1.2.0 → 1.4.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.
package/README.md CHANGED
@@ -16,11 +16,11 @@ Remove it with `npx ccsidekick uninstall`.
16
16
 
17
17
  ## Non-interactive setup (for scripts and AI agents)
18
18
 
19
- No TTY required. `ccsidekick setup` configures and wires everything from flags, so an agent can
19
+ No TTY required. `npx ccsidekick setup` configures and wires everything from flags, so an agent can
20
20
  install it in one command:
21
21
 
22
22
  ```bash
23
- ccsidekick setup --character batman --theme houston --mode fixed
23
+ npx ccsidekick setup --character spiderman --theme houston --mode fixed
24
24
  ```
25
25
 
26
26
  Only the flags you pass are applied (a partial patch onto the existing config, or the defaults on a
@@ -44,8 +44,9 @@ fresh install), then it writes `config.toml` and wires `settings.json` exactly l
44
44
  Discover valid values for scripting, and see every flag:
45
45
 
46
46
  ```bash
47
- ccsidekick list characters # also: themes, widgets
48
- ccsidekick setup --help
47
+ npx ccsidekick list characters # also: themes, widgets
48
+ npx ccsidekick setup --help
49
+
49
50
  ```
50
51
 
51
52
  An unknown value (e.g. a misspelled theme) exits non-zero and prints the valid set — it never
@@ -1037,16 +1037,31 @@ function candidateSet(config, installed) {
1037
1037
  return [...installed].sort();
1038
1038
  return ["batman"];
1039
1039
  }
1040
- function derivePersona(config, state, session, installed) {
1040
+ function lastUsedByCharacter(attribution, currentSession) {
1041
+ const out = {};
1042
+ for (const [sessionId, entry] of Object.entries(attribution)) {
1043
+ if (sessionId === currentSession)
1044
+ continue;
1045
+ const ts = entry.updatedMs ?? 0;
1046
+ if (ts > (out[entry.character] ?? 0))
1047
+ out[entry.character] = ts;
1048
+ }
1049
+ return out;
1050
+ }
1051
+ function derivePersona(config, state, session, installed, attribution) {
1041
1052
  const candidates = candidateSet(config, installed);
1042
1053
  const sticky = state.character;
1043
1054
  if (sticky !== undefined && sticky !== "" && stickyValid(config, candidates, sticky))
1044
1055
  return sticky;
1045
1056
  if (config.character.mode === "fixed")
1046
1057
  return config.character.name;
1058
+ const lastUsed = lastUsedByCharacter(attribution, String(session));
1059
+ const recency = (name) => lastUsed[name] ?? 0;
1060
+ const oldest = Math.min(...candidates.map(recency));
1061
+ const tied = candidates.filter((name) => recency(name) === oldest);
1047
1062
  const hash = createHash("sha1").update(session).digest("hex");
1048
- const idx = parseInt(hash.slice(0, 8), 16) % candidates.length;
1049
- return candidates[idx] ?? "batman";
1063
+ const idx = parseInt(hash.slice(0, 8), 16) % tied.length;
1064
+ return tied[idx] ?? "batman";
1050
1065
  }
1051
1066
  function stickyValid(config, candidates, sticky) {
1052
1067
  return config.character.mode === "fixed" ? sticky === config.character.name : candidates.includes(sticky);
@@ -1320,7 +1335,8 @@ function coerceStore(raw) {
1320
1335
  const project = entry["project"];
1321
1336
  const character = entry["character"];
1322
1337
  if (typeof project === "string" && typeof character === "string") {
1323
- out[key] = { project, character };
1338
+ const updatedMs = entry["updatedMs"];
1339
+ out[key] = typeof updatedMs === "number" ? { project, character, updatedMs } : { project, character };
1324
1340
  }
1325
1341
  }
1326
1342
  return out;
@@ -1339,7 +1355,7 @@ function upsertAttribution(root, sessionId, rec) {
1339
1355
  const path = storePath(root);
1340
1356
  withLock(`${path}.lock`, () => {
1341
1357
  const store = coerceStore(readJson(path, undefined));
1342
- store[sessionId] = { project: rec.project, character: rec.character };
1358
+ store[sessionId] = rec.updatedMs !== undefined ? { project: rec.project, character: rec.character, updatedMs: rec.updatedMs } : { project: rec.project, character: rec.character };
1343
1359
  atomicWrite(path, JSON.stringify(store));
1344
1360
  }, () => {});
1345
1361
  }
@@ -2425,7 +2441,7 @@ var DEFAULT_WIDGETS = {
2425
2441
  };
2426
2442
  var DEFAULT_CONFIG = {
2427
2443
  schema_version: 1,
2428
- character: { enabled: true, mode: "random", name: "batman", roster: [] },
2444
+ character: { enabled: true, mode: "random", name: "spiderman", roster: [] },
2429
2445
  theme: { name: "character", banding: "solid", mood_shift: false, icons: {} },
2430
2446
  comments: { character: true, helpful: true, min_severity: "low" },
2431
2447
  network: { fx_refresh: true, usage_fetch: true, balance_path: "" },
@@ -4110,6 +4126,11 @@ function decodeCwd(encoded) {
4110
4126
  function projectKeyForCwd(cwd) {
4111
4127
  return decodeCwd(cwd.replace(/[/.]/g, "-"));
4112
4128
  }
4129
+ function projectKeyForTranscript(transcriptPath) {
4130
+ if (transcriptPath === "")
4131
+ return;
4132
+ return decodeCwd(basename(dirname6(transcriptPath)));
4133
+ }
4113
4134
  var repoRootCache = new Map;
4114
4135
  function* walkTree(dir, encodedDir) {
4115
4136
  let entries;
@@ -4652,7 +4673,7 @@ var THEMES = {
4652
4673
  },
4653
4674
  solarizedDark: {
4654
4675
  displayName: "Solarized Dark",
4655
- hues: [32, 36, 100, 136, 168],
4676
+ hues: [32, 36, 178, 136, 168],
4656
4677
  comment: [32, 36],
4657
4678
  signals: { nominal: 46, caution: 136, critical: 167 },
4658
4679
  separator: 100
@@ -4680,14 +4701,14 @@ var THEMES = {
4680
4701
  },
4681
4702
  materialPalenight: {
4682
4703
  displayName: "Material Palenight",
4683
- hues: [111, 176, 117, 150, 217],
4704
+ hues: [111, 141, 45, 220, 203],
4684
4705
  comment: [60, 111, 176],
4685
4706
  signals: { nominal: 150, caution: 214, critical: 217 },
4686
4707
  separator: 150
4687
4708
  },
4688
4709
  nightOwl: {
4689
4710
  displayName: "Night Owl",
4690
- hues: [111, 176, 116, 149, 209],
4711
+ hues: [111, 122, 222, 149, 203],
4691
4712
  comment: [111, 116],
4692
4713
  signals: { nominal: 149, caution: 214, critical: 209 },
4693
4714
  separator: 66
@@ -4708,9 +4729,9 @@ var THEMES = {
4708
4729
  },
4709
4730
  darcula: {
4710
4731
  displayName: "Darcula",
4711
- hues: [173, 67, 103, 65, 221],
4732
+ hues: [173, 67, 103, 77, 221],
4712
4733
  comment: [173, 103],
4713
- signals: { nominal: 65, caution: 221, critical: 203 },
4734
+ signals: { nominal: 77, caution: 221, critical: 203 },
4714
4735
  separator: 173
4715
4736
  },
4716
4737
  xcodeDark: {
@@ -4757,35 +4778,35 @@ var THEMES = {
4757
4778
  },
4758
4779
  everforest: {
4759
4780
  displayName: "Everforest",
4760
- hues: [144, 180, 109, 108, 175],
4781
+ hues: [149, 222, 115, 173, 174],
4761
4782
  comment: [144, 174],
4762
4783
  signals: { nominal: 108, caution: 180, critical: 167 },
4763
4784
  separator: 144
4764
4785
  },
4765
4786
  rosePine: {
4766
4787
  displayName: "Rosé Pine",
4767
- hues: [181, 66, 152, 182, 216],
4788
+ hues: [181, 74, 152, 182, 216],
4768
4789
  comment: [60, 181, 182],
4769
4790
  signals: { nominal: 46, caution: 214, critical: 181 },
4770
4791
  separator: 66
4771
4792
  },
4772
4793
  rosePineMoon: {
4773
4794
  displayName: "Rosé Pine Moon",
4774
- hues: [174, 67, 152, 182, 216],
4795
+ hues: [217, 75, 122, 183, 223],
4775
4796
  comment: [60, 174, 182],
4776
4797
  signals: { nominal: 46, caution: 214, critical: 174 },
4777
4798
  separator: 174
4778
4799
  },
4779
4800
  kanagawa: {
4780
4801
  displayName: "Kanagawa",
4781
- hues: [110, 103, 66, 107, 180],
4802
+ hues: [110, 103, 79, 107, 180],
4782
4803
  comment: [110, 103],
4783
4804
  signals: { nominal: 107, caution: 180, critical: 203 },
4784
4805
  separator: 107
4785
4806
  },
4786
4807
  tomorrowNight: {
4787
4808
  displayName: "Tomorrow Night",
4788
- hues: [109, 139, 143, 222],
4809
+ hues: [110, 182, 149, 222, 167],
4789
4810
  comment: [109, 139],
4790
4811
  signals: { nominal: 46, caution: 214, critical: 203 },
4791
4812
  separator: 143
@@ -4799,7 +4820,7 @@ var THEMES = {
4799
4820
  },
4800
4821
  iceberg: {
4801
4822
  displayName: "Iceberg",
4802
- hues: [110, 140, 109, 144, 180],
4823
+ hues: [110, 140, 149, 180, 174],
4803
4824
  comment: [60, 110, 140],
4804
4825
  signals: { nominal: 46, caution: 214, critical: 203 },
4805
4826
  separator: 144
@@ -4841,7 +4862,7 @@ var THEMES = {
4841
4862
  },
4842
4863
  moonlight: {
4843
4864
  displayName: "Moonlight",
4844
- hues: [111, 141, 117, 150, 222],
4865
+ hues: [111, 177, 123, 211, 155],
4845
4866
  comment: [104, 111, 141],
4846
4867
  signals: { nominal: 150, caution: 222, critical: 203 },
4847
4868
  separator: 150
@@ -4862,14 +4883,14 @@ var THEMES = {
4862
4883
  },
4863
4884
  spacegray: {
4864
4885
  displayName: "Spacegray",
4865
- hues: [103, 139, 109, 144, 186],
4886
+ hues: [111, 182, 114, 180, 174],
4866
4887
  comment: [109, 139],
4867
4888
  signals: { nominal: 46, caution: 214, critical: 203 },
4868
4889
  separator: 144
4869
4890
  },
4870
4891
  palenight: {
4871
4892
  displayName: "Palenight",
4872
- hues: [176, 111, 117, 150, 209],
4893
+ hues: [177, 117, 215, 150, 204],
4873
4894
  comment: [60, 176, 111],
4874
4895
  signals: { nominal: 150, caution: 214, critical: 209 },
4875
4896
  separator: 209
@@ -4890,7 +4911,7 @@ var THEMES = {
4890
4911
  },
4891
4912
  githubDarkDimmed: {
4892
4913
  displayName: "GitHub Dark Dimmed",
4893
- hues: [75, 183, 114, 215, 203],
4914
+ hues: [75, 176, 78, 179, 174],
4894
4915
  comment: [75, 183],
4895
4916
  signals: { nominal: 114, caution: 215, critical: 203 },
4896
4917
  separator: 215
@@ -4930,107 +4951,16 @@ var THEMES = {
4930
4951
  signals: { nominal: 107, caution: 179, critical: 167 },
4931
4952
  separator: 179
4932
4953
  },
4933
- githubLight: {
4934
- displayName: "GitHub Light",
4935
- hues: [26, 98, 29, 130, 160],
4936
- comment: [26, 98],
4937
- signals: { nominal: 29, caution: 130, critical: 160 },
4938
- separator: 130
4939
- },
4940
- solarizedLight: {
4941
- displayName: "Solarized Light",
4942
- hues: [32, 36, 100, 136, 168],
4943
- comment: [32, 168],
4944
- signals: { nominal: 46, caution: 136, critical: 167 },
4945
- separator: 100
4946
- },
4947
- catppuccinLatte: {
4948
- displayName: "Catppuccin Latte",
4949
- hues: [99, 176, 27, 70, 202],
4950
- comment: [99, 176],
4951
- signals: { nominal: 70, caution: 214, critical: 203 },
4952
- separator: 70
4953
- },
4954
- gruvboxLight: {
4955
- displayName: "Gruvbox Light",
4956
- hues: [136, 130, 100, 24, 95],
4957
- comment: [136, 130],
4958
- signals: { nominal: 46, caution: 136, critical: 95 },
4959
- separator: 187
4960
- },
4961
- ayuLight: {
4962
- displayName: "Ayu Light",
4963
- hues: [209, 74, 106, 140, 204],
4964
- comment: [209, 74],
4965
- signals: { nominal: 46, caution: 214, critical: 209 },
4966
- separator: 106
4967
- },
4968
- oneLight: {
4969
- displayName: "One Light",
4970
- hues: [69, 71, 127, 31, 136],
4971
- comment: [69, 127],
4972
- signals: { nominal: 71, caution: 136, critical: 203 },
4973
- separator: 136
4974
- },
4975
- vscodeLightPlus: {
4976
- displayName: "VS Code Light+",
4977
- hues: [21, 128, 30, 29, 124],
4978
- comment: [28, 21, 30],
4979
- signals: { nominal: 29, caution: 214, critical: 124 },
4980
- separator: 124
4981
- },
4982
- xcodeLight: {
4983
- displayName: "Xcode Light",
4984
- hues: [133, 24, 20, 160],
4985
- comment: [66, 133, 20],
4986
- signals: { nominal: 46, caution: 214, critical: 160 },
4987
- separator: 160
4988
- },
4989
- quietLight: {
4990
- displayName: "Quiet Light",
4991
- hues: [62, 131, 64, 97, 136],
4992
- comment: [62, 64],
4993
- signals: { nominal: 64, caution: 136, critical: 131 },
4994
- separator: 64
4995
- },
4996
- tomorrowLight: {
4997
- displayName: "Tomorrow Light",
4998
- hues: [61, 97, 67, 64, 208],
4999
- comment: [61, 97],
5000
- signals: { nominal: 64, caution: 208, critical: 203 },
5001
- separator: 64
5002
- },
5003
- blulocoLight: {
5004
- displayName: "Bluloco Light",
5005
- hues: [26, 99, 32, 29, 166],
5006
- comment: [26, 32],
5007
- signals: { nominal: 29, caution: 166, critical: 203 },
5008
- separator: 166
5009
- },
5010
- rosePineDawn: {
5011
- displayName: "Rosé Pine Dawn",
5012
- hues: [174, 24, 67, 103, 179],
5013
- comment: [174, 103],
5014
- signals: { nominal: 46, caution: 179, critical: 174 },
5015
- separator: 179
5016
- },
5017
4954
  githubHCDark: {
5018
4955
  displayName: "GitHub HC Dark",
5019
- hues: [75, 183, 77, 215, 210],
4956
+ hues: [45, 135, 47, 220, 196],
5020
4957
  comment: [75, 183],
5021
4958
  signals: { nominal: 77, caution: 215, critical: 210 },
5022
4959
  separator: 77
5023
4960
  },
5024
- githubHCLight: {
5025
- displayName: "GitHub HC Light",
5026
- hues: [25, 55, 22, 52, 124],
5027
- comment: [25, 55],
5028
- signals: { nominal: 22, caution: 214, critical: 52 },
5029
- separator: 22
5030
- },
5031
4961
  vscodeHighContrast: {
5032
4962
  displayName: "VS Code HC",
5033
- hues: [74, 175, 79, 187, 174],
4963
+ hues: [45, 129, 118, 226, 203],
5034
4964
  comment: [107, 74],
5035
4965
  signals: { nominal: 79, caution: 214, critical: 203 },
5036
4966
  separator: 74
@@ -6973,14 +6903,22 @@ function loadPack(name, resolver = defaultResolver) {
6973
6903
  var PACKS = [
6974
6904
  "barbie",
6975
6905
  "batman",
6906
+ "ben10",
6907
+ "darth-vader",
6976
6908
  "deadpool",
6909
+ "gandalf",
6977
6910
  "harry-potter",
6978
6911
  "hello-kitty",
6912
+ "iron-man",
6979
6913
  "james-bond",
6980
6914
  "joker",
6915
+ "naruto",
6981
6916
  "pikachu",
6982
6917
  "sherlock-holmes",
6983
- "spiderman"
6918
+ "shinchan",
6919
+ "spiderman",
6920
+ "superman",
6921
+ "yoda"
6984
6922
  ];
6985
6923
  // src/cli/gc.ts
6986
6924
  import { existsSync as existsSync2, readdirSync as readdirSync3, rmSync, statSync as statSync4 } from "node:fs";
@@ -7137,11 +7075,11 @@ function build(stdin, env, term, clock, overrides) {
7137
7075
  const costCache = readCostCache(root);
7138
7076
  const fxTable = readFxCached(root);
7139
7077
  const { creds, usage, balance, helpfulEnv } = readGated(overrides, envInputs, config, root, dir, markers, clock);
7140
- const persona = derivePersona(config, sessionState, session, PACKS);
7078
+ const persona = derivePersona(config, sessionState, session, PACKS, attribution);
7141
7079
  const loaded = loadPack(persona);
7142
7080
  const pack = loaded.ok ? loaded.pack : null;
7143
7081
  const project = deriveProject(git, payload);
7144
- const projectKey = projectKeyForCwd(payload.workspace.current_dir ?? payload.cwd ?? "");
7082
+ const projectKey = projectKeyForTranscript(payload.transcript_path ?? "") ?? projectKeyForCwd(payload.workspace.current_dir ?? payload.cwd ?? "");
7145
7083
  const resolveProject = (sess, decodedCwd) => sess === String(session) ? String(project) : decodedCwd;
7146
7084
  const scan = scanTranscript(payload.transcript_path ?? "", clock, price);
7147
7085
  const scannedCache = scanCostTree(projectsRoot, costCache, clock, price, resolveProject);
@@ -7246,7 +7184,8 @@ function build(stdin, env, term, clock, overrides) {
7246
7184
  swallow(() => {
7247
7185
  upsertAttribution(root, String(session), {
7248
7186
  project: String(project),
7249
- character: persona
7187
+ character: persona,
7188
+ updatedMs: clock.now()
7250
7189
  });
7251
7190
  });
7252
7191
  if (!isDefault) {
@@ -479,7 +479,7 @@ var init_themes = __esm(() => {
479
479
  },
480
480
  solarizedDark: {
481
481
  displayName: "Solarized Dark",
482
- hues: [32, 36, 100, 136, 168],
482
+ hues: [32, 36, 178, 136, 168],
483
483
  comment: [32, 36],
484
484
  signals: { nominal: 46, caution: 136, critical: 167 },
485
485
  separator: 100
@@ -507,14 +507,14 @@ var init_themes = __esm(() => {
507
507
  },
508
508
  materialPalenight: {
509
509
  displayName: "Material Palenight",
510
- hues: [111, 176, 117, 150, 217],
510
+ hues: [111, 141, 45, 220, 203],
511
511
  comment: [60, 111, 176],
512
512
  signals: { nominal: 150, caution: 214, critical: 217 },
513
513
  separator: 150
514
514
  },
515
515
  nightOwl: {
516
516
  displayName: "Night Owl",
517
- hues: [111, 176, 116, 149, 209],
517
+ hues: [111, 122, 222, 149, 203],
518
518
  comment: [111, 116],
519
519
  signals: { nominal: 149, caution: 214, critical: 209 },
520
520
  separator: 66
@@ -535,9 +535,9 @@ var init_themes = __esm(() => {
535
535
  },
536
536
  darcula: {
537
537
  displayName: "Darcula",
538
- hues: [173, 67, 103, 65, 221],
538
+ hues: [173, 67, 103, 77, 221],
539
539
  comment: [173, 103],
540
- signals: { nominal: 65, caution: 221, critical: 203 },
540
+ signals: { nominal: 77, caution: 221, critical: 203 },
541
541
  separator: 173
542
542
  },
543
543
  xcodeDark: {
@@ -584,35 +584,35 @@ var init_themes = __esm(() => {
584
584
  },
585
585
  everforest: {
586
586
  displayName: "Everforest",
587
- hues: [144, 180, 109, 108, 175],
587
+ hues: [149, 222, 115, 173, 174],
588
588
  comment: [144, 174],
589
589
  signals: { nominal: 108, caution: 180, critical: 167 },
590
590
  separator: 144
591
591
  },
592
592
  rosePine: {
593
593
  displayName: "Rosé Pine",
594
- hues: [181, 66, 152, 182, 216],
594
+ hues: [181, 74, 152, 182, 216],
595
595
  comment: [60, 181, 182],
596
596
  signals: { nominal: 46, caution: 214, critical: 181 },
597
597
  separator: 66
598
598
  },
599
599
  rosePineMoon: {
600
600
  displayName: "Rosé Pine Moon",
601
- hues: [174, 67, 152, 182, 216],
601
+ hues: [217, 75, 122, 183, 223],
602
602
  comment: [60, 174, 182],
603
603
  signals: { nominal: 46, caution: 214, critical: 174 },
604
604
  separator: 174
605
605
  },
606
606
  kanagawa: {
607
607
  displayName: "Kanagawa",
608
- hues: [110, 103, 66, 107, 180],
608
+ hues: [110, 103, 79, 107, 180],
609
609
  comment: [110, 103],
610
610
  signals: { nominal: 107, caution: 180, critical: 203 },
611
611
  separator: 107
612
612
  },
613
613
  tomorrowNight: {
614
614
  displayName: "Tomorrow Night",
615
- hues: [109, 139, 143, 222],
615
+ hues: [110, 182, 149, 222, 167],
616
616
  comment: [109, 139],
617
617
  signals: { nominal: 46, caution: 214, critical: 203 },
618
618
  separator: 143
@@ -626,7 +626,7 @@ var init_themes = __esm(() => {
626
626
  },
627
627
  iceberg: {
628
628
  displayName: "Iceberg",
629
- hues: [110, 140, 109, 144, 180],
629
+ hues: [110, 140, 149, 180, 174],
630
630
  comment: [60, 110, 140],
631
631
  signals: { nominal: 46, caution: 214, critical: 203 },
632
632
  separator: 144
@@ -668,7 +668,7 @@ var init_themes = __esm(() => {
668
668
  },
669
669
  moonlight: {
670
670
  displayName: "Moonlight",
671
- hues: [111, 141, 117, 150, 222],
671
+ hues: [111, 177, 123, 211, 155],
672
672
  comment: [104, 111, 141],
673
673
  signals: { nominal: 150, caution: 222, critical: 203 },
674
674
  separator: 150
@@ -689,14 +689,14 @@ var init_themes = __esm(() => {
689
689
  },
690
690
  spacegray: {
691
691
  displayName: "Spacegray",
692
- hues: [103, 139, 109, 144, 186],
692
+ hues: [111, 182, 114, 180, 174],
693
693
  comment: [109, 139],
694
694
  signals: { nominal: 46, caution: 214, critical: 203 },
695
695
  separator: 144
696
696
  },
697
697
  palenight: {
698
698
  displayName: "Palenight",
699
- hues: [176, 111, 117, 150, 209],
699
+ hues: [177, 117, 215, 150, 204],
700
700
  comment: [60, 176, 111],
701
701
  signals: { nominal: 150, caution: 214, critical: 209 },
702
702
  separator: 209
@@ -717,7 +717,7 @@ var init_themes = __esm(() => {
717
717
  },
718
718
  githubDarkDimmed: {
719
719
  displayName: "GitHub Dark Dimmed",
720
- hues: [75, 183, 114, 215, 203],
720
+ hues: [75, 176, 78, 179, 174],
721
721
  comment: [75, 183],
722
722
  signals: { nominal: 114, caution: 215, critical: 203 },
723
723
  separator: 215
@@ -757,107 +757,16 @@ var init_themes = __esm(() => {
757
757
  signals: { nominal: 107, caution: 179, critical: 167 },
758
758
  separator: 179
759
759
  },
760
- githubLight: {
761
- displayName: "GitHub Light",
762
- hues: [26, 98, 29, 130, 160],
763
- comment: [26, 98],
764
- signals: { nominal: 29, caution: 130, critical: 160 },
765
- separator: 130
766
- },
767
- solarizedLight: {
768
- displayName: "Solarized Light",
769
- hues: [32, 36, 100, 136, 168],
770
- comment: [32, 168],
771
- signals: { nominal: 46, caution: 136, critical: 167 },
772
- separator: 100
773
- },
774
- catppuccinLatte: {
775
- displayName: "Catppuccin Latte",
776
- hues: [99, 176, 27, 70, 202],
777
- comment: [99, 176],
778
- signals: { nominal: 70, caution: 214, critical: 203 },
779
- separator: 70
780
- },
781
- gruvboxLight: {
782
- displayName: "Gruvbox Light",
783
- hues: [136, 130, 100, 24, 95],
784
- comment: [136, 130],
785
- signals: { nominal: 46, caution: 136, critical: 95 },
786
- separator: 187
787
- },
788
- ayuLight: {
789
- displayName: "Ayu Light",
790
- hues: [209, 74, 106, 140, 204],
791
- comment: [209, 74],
792
- signals: { nominal: 46, caution: 214, critical: 209 },
793
- separator: 106
794
- },
795
- oneLight: {
796
- displayName: "One Light",
797
- hues: [69, 71, 127, 31, 136],
798
- comment: [69, 127],
799
- signals: { nominal: 71, caution: 136, critical: 203 },
800
- separator: 136
801
- },
802
- vscodeLightPlus: {
803
- displayName: "VS Code Light+",
804
- hues: [21, 128, 30, 29, 124],
805
- comment: [28, 21, 30],
806
- signals: { nominal: 29, caution: 214, critical: 124 },
807
- separator: 124
808
- },
809
- xcodeLight: {
810
- displayName: "Xcode Light",
811
- hues: [133, 24, 20, 160],
812
- comment: [66, 133, 20],
813
- signals: { nominal: 46, caution: 214, critical: 160 },
814
- separator: 160
815
- },
816
- quietLight: {
817
- displayName: "Quiet Light",
818
- hues: [62, 131, 64, 97, 136],
819
- comment: [62, 64],
820
- signals: { nominal: 64, caution: 136, critical: 131 },
821
- separator: 64
822
- },
823
- tomorrowLight: {
824
- displayName: "Tomorrow Light",
825
- hues: [61, 97, 67, 64, 208],
826
- comment: [61, 97],
827
- signals: { nominal: 64, caution: 208, critical: 203 },
828
- separator: 64
829
- },
830
- blulocoLight: {
831
- displayName: "Bluloco Light",
832
- hues: [26, 99, 32, 29, 166],
833
- comment: [26, 32],
834
- signals: { nominal: 29, caution: 166, critical: 203 },
835
- separator: 166
836
- },
837
- rosePineDawn: {
838
- displayName: "Rosé Pine Dawn",
839
- hues: [174, 24, 67, 103, 179],
840
- comment: [174, 103],
841
- signals: { nominal: 46, caution: 179, critical: 174 },
842
- separator: 179
843
- },
844
760
  githubHCDark: {
845
761
  displayName: "GitHub HC Dark",
846
- hues: [75, 183, 77, 215, 210],
762
+ hues: [45, 135, 47, 220, 196],
847
763
  comment: [75, 183],
848
764
  signals: { nominal: 77, caution: 215, critical: 210 },
849
765
  separator: 77
850
766
  },
851
- githubHCLight: {
852
- displayName: "GitHub HC Light",
853
- hues: [25, 55, 22, 52, 124],
854
- comment: [25, 55],
855
- signals: { nominal: 22, caution: 214, critical: 52 },
856
- separator: 22
857
- },
858
767
  vscodeHighContrast: {
859
768
  displayName: "VS Code HC",
860
- hues: [74, 175, 79, 187, 174],
769
+ hues: [45, 129, 118, 226, 203],
861
770
  comment: [107, 74],
862
771
  signals: { nominal: 79, caution: 214, critical: 203 },
863
772
  separator: 74
@@ -3575,16 +3484,31 @@ function candidateSet(config, installed) {
3575
3484
  return [...installed].sort();
3576
3485
  return ["batman"];
3577
3486
  }
3578
- function derivePersona(config, state, session, installed) {
3487
+ function lastUsedByCharacter(attribution, currentSession) {
3488
+ const out = {};
3489
+ for (const [sessionId, entry] of Object.entries(attribution)) {
3490
+ if (sessionId === currentSession)
3491
+ continue;
3492
+ const ts = entry.updatedMs ?? 0;
3493
+ if (ts > (out[entry.character] ?? 0))
3494
+ out[entry.character] = ts;
3495
+ }
3496
+ return out;
3497
+ }
3498
+ function derivePersona(config, state, session, installed, attribution) {
3579
3499
  const candidates = candidateSet(config, installed);
3580
3500
  const sticky = state.character;
3581
3501
  if (sticky !== undefined && sticky !== "" && stickyValid(config, candidates, sticky))
3582
3502
  return sticky;
3583
3503
  if (config.character.mode === "fixed")
3584
3504
  return config.character.name;
3505
+ const lastUsed = lastUsedByCharacter(attribution, String(session));
3506
+ const recency = (name) => lastUsed[name] ?? 0;
3507
+ const oldest = Math.min(...candidates.map(recency));
3508
+ const tied = candidates.filter((name) => recency(name) === oldest);
3585
3509
  const hash = createHash2("sha1").update(session).digest("hex");
3586
- const idx = parseInt(hash.slice(0, 8), 16) % candidates.length;
3587
- return candidates[idx] ?? "batman";
3510
+ const idx = parseInt(hash.slice(0, 8), 16) % tied.length;
3511
+ return tied[idx] ?? "batman";
3588
3512
  }
3589
3513
  function stickyValid(config, candidates, sticky) {
3590
3514
  return config.character.mode === "fixed" ? sticky === config.character.name : candidates.includes(sticky);
@@ -4028,14 +3952,22 @@ var init_registry = __esm(() => {
4028
3952
  PACKS = [
4029
3953
  "barbie",
4030
3954
  "batman",
3955
+ "ben10",
3956
+ "darth-vader",
4031
3957
  "deadpool",
3958
+ "gandalf",
4032
3959
  "harry-potter",
4033
3960
  "hello-kitty",
3961
+ "iron-man",
4034
3962
  "james-bond",
4035
3963
  "joker",
3964
+ "naruto",
4036
3965
  "pikachu",
4037
3966
  "sherlock-holmes",
4038
- "spiderman"
3967
+ "shinchan",
3968
+ "spiderman",
3969
+ "superman",
3970
+ "yoda"
4039
3971
  ];
4040
3972
  });
4041
3973
 
@@ -4157,7 +4089,8 @@ function coerceStore(raw) {
4157
4089
  const project = entry["project"];
4158
4090
  const character = entry["character"];
4159
4091
  if (typeof project === "string" && typeof character === "string") {
4160
- out[key] = { project, character };
4092
+ const updatedMs = entry["updatedMs"];
4093
+ out[key] = typeof updatedMs === "number" ? { project, character, updatedMs } : { project, character };
4161
4094
  }
4162
4095
  }
4163
4096
  return out;
@@ -4176,7 +4109,7 @@ function upsertAttribution(root, sessionId, rec) {
4176
4109
  const path = storePath(root);
4177
4110
  withLock(`${path}.lock`, () => {
4178
4111
  const store = coerceStore(readJson(path, undefined));
4179
- store[sessionId] = { project: rec.project, character: rec.character };
4112
+ store[sessionId] = rec.updatedMs !== undefined ? { project: rec.project, character: rec.character, updatedMs: rec.updatedMs } : { project: rec.project, character: rec.character };
4180
4113
  atomicWrite(path, JSON.stringify(store));
4181
4114
  }, () => {});
4182
4115
  }
@@ -5550,7 +5483,7 @@ var init_config = __esm(() => {
5550
5483
  };
5551
5484
  DEFAULT_CONFIG = {
5552
5485
  schema_version: 1,
5553
- character: { enabled: true, mode: "random", name: "batman", roster: [] },
5486
+ character: { enabled: true, mode: "random", name: "spiderman", roster: [] },
5554
5487
  theme: { name: "character", banding: "solid", mood_shift: false, icons: {} },
5555
5488
  comments: { character: true, helpful: true, min_severity: "low" },
5556
5489
  network: { fx_refresh: true, usage_fetch: true, balance_path: "" },
@@ -7160,6 +7093,11 @@ function decodeCwd(encoded) {
7160
7093
  function projectKeyForCwd(cwd) {
7161
7094
  return decodeCwd(cwd.replace(/[/.]/g, "-"));
7162
7095
  }
7096
+ function projectKeyForTranscript(transcriptPath) {
7097
+ if (transcriptPath === "")
7098
+ return;
7099
+ return decodeCwd(basename2(dirname5(transcriptPath)));
7100
+ }
7163
7101
  function repoRootForCwd(cwd) {
7164
7102
  const cached = repoRootCache.get(cwd);
7165
7103
  if (cached !== undefined)
@@ -7527,11 +7465,11 @@ function build(stdin, env, term, clock, overrides) {
7527
7465
  const costCache = readCostCache(root);
7528
7466
  const fxTable = readFxCached(root);
7529
7467
  const { creds, usage, balance, helpfulEnv } = readGated(overrides, envInputs, config, root, dir, markers, clock);
7530
- const persona = derivePersona(config, sessionState, session, PACKS);
7468
+ const persona = derivePersona(config, sessionState, session, PACKS, attribution);
7531
7469
  const loaded = loadPack(persona);
7532
7470
  const pack = loaded.ok ? loaded.pack : null;
7533
7471
  const project = deriveProject(git, payload);
7534
- const projectKey = projectKeyForCwd(payload.workspace.current_dir ?? payload.cwd ?? "");
7472
+ const projectKey = projectKeyForTranscript(payload.transcript_path ?? "") ?? projectKeyForCwd(payload.workspace.current_dir ?? payload.cwd ?? "");
7535
7473
  const resolveProject = (sess, decodedCwd) => sess === String(session) ? String(project) : decodedCwd;
7536
7474
  const scan = scanTranscript(payload.transcript_path ?? "", clock, price);
7537
7475
  const scannedCache = scanCostTree(projectsRoot, costCache, clock, price, resolveProject);
@@ -7636,7 +7574,8 @@ function build(stdin, env, term, clock, overrides) {
7636
7574
  swallow(() => {
7637
7575
  upsertAttribution(root, String(session), {
7638
7576
  project: String(project),
7639
- character: persona
7577
+ character: persona,
7578
+ updatedMs: clock.now()
7640
7579
  });
7641
7580
  });
7642
7581
  if (!isDefault) {
@@ -10966,10 +10905,11 @@ import { execFileSync } from "node:child_process";
10966
10905
  import { existsSync as existsSync6, mkdirSync as mkdirSync5, mkdtempSync, realpathSync, writeFileSync as writeFileSync4 } from "node:fs";
10967
10906
  import { tmpdir } from "node:os";
10968
10907
  import { join as join20 } from "node:path";
10969
- function seedTranscriptFixture(workdir) {
10970
- const path = join20(workdir, "preview-transcript.jsonl");
10908
+ function seedTranscriptFixture(workdir, root) {
10909
+ const dir = root !== undefined ? join20(root, "projects", workdir.replace(/[/.]/g, "-")) : workdir;
10910
+ const path = join20(dir, "preview-transcript.jsonl");
10971
10911
  try {
10972
- mkdirSync5(workdir, { recursive: true });
10912
+ mkdirSync5(dir, { recursive: true });
10973
10913
  if (!existsSync6(path)) {
10974
10914
  const lines = [
10975
10915
  JSON.stringify({ type: "system", subtype: "compact_boundary" }),
@@ -11000,13 +10940,13 @@ function seedTranscriptFixture(workdir) {
11000
10940
  } catch {}
11001
10941
  return path;
11002
10942
  }
11003
- function basePayload(workdir, over = {}) {
10943
+ function basePayload(workdir, over = {}, root) {
11004
10944
  const cost = { total_cost_usd: 0.42, total_duration_ms: 3600000, ...over.cost };
11005
10945
  const cwd = over.cwd ?? workdir;
11006
10946
  const base = {
11007
10947
  session_id: "preview",
11008
10948
  session_name: "preview",
11009
- transcript_path: seedTranscriptFixture(workdir),
10949
+ transcript_path: seedTranscriptFixture(workdir, root),
11010
10950
  cwd,
11011
10951
  workspace: { current_dir: cwd },
11012
10952
  model: { id: "claude-opus-4-1", display_name: "Opus 4.1" },
@@ -11112,8 +11052,8 @@ var sharedScratch;
11112
11052
  var init_fixture = () => {};
11113
11053
 
11114
11054
  // src/tui/preview/scenarios.ts
11115
- function scenarioPayloadJson(scenario, workdir) {
11116
- return JSON.stringify(basePayload(workdir, scenario.payload ?? {}));
11055
+ function scenarioPayloadJson(scenario, workdir, root) {
11056
+ return JSON.stringify(basePayload(workdir, scenario.payload ?? {}, root));
11117
11057
  }
11118
11058
  var creds = (subscriptionType) => ({
11119
11059
  creds: { present: true, subscriptionType }
@@ -11189,7 +11129,7 @@ function renderScenario(scenario, draft, opts) {
11189
11129
  setupGitFixture(workdir);
11190
11130
  seedCostFixture(root, workdir);
11191
11131
  const term = { columns: opts.columns, noColor: opts.noColor, isTTY: true };
11192
- return runRender(scenarioPayloadJson(scenario, workdir), env2, term, clock, scenario.overrides).line;
11132
+ return runRender(scenarioPayloadJson(scenario, workdir, root), env2, term, clock, scenario.overrides).line;
11193
11133
  } catch (e) {
11194
11134
  return `preview unavailable: ${e instanceof Error ? e.message : String(e)}`;
11195
11135
  }
@@ -13214,7 +13154,7 @@ function Dashboard(props) {
13214
13154
  const activeIds = draft.character.mode === "fixed" ? [draft.character.name] : draft.character.roster.length > 0 ? draft.character.roster : packs;
13215
13155
  const savePreviewChars = savePreviewSet(draft, installed, packs);
13216
13156
  const saveCharIdxClamped = Math.min(saveCharIdx, savePreviewChars.length - 1);
13217
- const saveCharName = savePreviewChars[saveCharIdxClamped] ?? "batman";
13157
+ const saveCharName = savePreviewChars[saveCharIdxClamped] ?? "spiderman";
13218
13158
  const selectCharacter = (id) => {
13219
13159
  const ch = draft.character;
13220
13160
  if (ch.mode === "fixed")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccsidekick",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "exports": {
@@ -20,14 +20,22 @@
20
20
  "dependencies": {
21
21
  "@ccsidekick/pack-barbie": "1.0.0",
22
22
  "@ccsidekick/pack-batman": "1.0.0",
23
+ "@ccsidekick/pack-ben10": "1.0.0",
24
+ "@ccsidekick/pack-darth-vader": "1.0.0",
23
25
  "@ccsidekick/pack-deadpool": "1.0.0",
26
+ "@ccsidekick/pack-gandalf": "1.0.0",
24
27
  "@ccsidekick/pack-harry-potter": "1.0.0",
25
28
  "@ccsidekick/pack-hello-kitty": "1.0.0",
26
29
  "@ccsidekick/pack-james-bond": "1.0.0",
27
- "@ccsidekick/pack-joker": "1.0.0",
28
- "@ccsidekick/pack-pikachu": "1.0.0",
29
- "@ccsidekick/pack-sherlock-holmes": "1.0.0",
30
+ "@ccsidekick/pack-joker": "1.0.1",
31
+ "@ccsidekick/pack-naruto": "1.0.0",
32
+ "@ccsidekick/pack-pikachu": "1.0.1",
33
+ "@ccsidekick/pack-sherlock-holmes": "1.0.1",
34
+ "@ccsidekick/pack-shinchan": "1.0.0",
30
35
  "@ccsidekick/pack-spiderman": "1.0.0",
36
+ "@ccsidekick/pack-superman": "1.0.0",
37
+ "@ccsidekick/pack-iron-man": "1.0.0",
38
+ "@ccsidekick/pack-yoda": "1.0.0",
31
39
  "@inkjs/ui": "2.0.0",
32
40
  "ink": "^5.2.0",
33
41
  "react": "^18.3.1",
@@ -46,6 +54,7 @@
46
54
  "anthropic",
47
55
  "statusline",
48
56
  "status-line",
57
+ "claude-code-statusline",
49
58
  "cli",
50
59
  "ascii-art",
51
60
  "tui",
@@ -54,18 +63,19 @@
54
63
  "git",
55
64
  "cost-tracking",
56
65
  "token-usage",
66
+ "usage-tracking",
57
67
  "developer-tools",
58
68
  "coding-companion",
59
69
  "character"
60
70
  ],
61
71
  "license": "MIT",
62
- "description": "A customizable Claude Code status line with a reactive ASCII character and a full widget layer.",
72
+ "description": "Free Claude Code status line with a reactive character plus cost, git, and usage widgets. Zero token spend, local-first, MIT.",
63
73
  "repository": {
64
74
  "type": "git",
65
75
  "url": "git+https://github.com/krayong/ccsidekick.git",
66
76
  "directory": "packages/core"
67
77
  },
68
- "homepage": "https://github.com/krayong/ccsidekick#readme",
78
+ "homepage": "https://ccsidekick.krayong.com",
69
79
  "bugs": {
70
80
  "url": "https://github.com/krayong/ccsidekick/issues"
71
81
  },