ccsidekick 1.1.0 → 1.3.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.
@@ -1037,15 +1037,34 @@ function candidateSet(config, installed) {
1037
1037
  return [...installed].sort();
1038
1038
  return ["batman"];
1039
1039
  }
1040
- function derivePersona(config, state, session, installed) {
1041
- if (state.character !== undefined && state.character !== "")
1042
- return state.character;
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) {
1052
+ const candidates = candidateSet(config, installed);
1053
+ const sticky = state.character;
1054
+ if (sticky !== undefined && sticky !== "" && stickyValid(config, candidates, sticky))
1055
+ return sticky;
1043
1056
  if (config.character.mode === "fixed")
1044
1057
  return config.character.name;
1045
- const candidates = candidateSet(config, installed);
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);
1046
1062
  const hash = createHash("sha1").update(session).digest("hex");
1047
- const idx = parseInt(hash.slice(0, 8), 16) % candidates.length;
1048
- return candidates[idx] ?? "batman";
1063
+ const idx = parseInt(hash.slice(0, 8), 16) % tied.length;
1064
+ return tied[idx] ?? "batman";
1065
+ }
1066
+ function stickyValid(config, candidates, sticky) {
1067
+ return config.character.mode === "fixed" ? sticky === config.character.name : candidates.includes(sticky);
1049
1068
  }
1050
1069
  // src/derived/project.ts
1051
1070
  function deriveProject(git, payload) {
@@ -1212,12 +1231,6 @@ var sessionDir = (root, id) => join(root, "sessions", id);
1212
1231
  var cacheDir = (root) => join(root, "cache");
1213
1232
  var analyticsDir = (root) => join(root, "analytics");
1214
1233
 
1215
- // src/sources/storage/engineRoot.ts
1216
- import { fileURLToPath } from "node:url";
1217
- function engineRoot(moduleUrl) {
1218
- return fileURLToPath(new URL("../", moduleUrl));
1219
- }
1220
-
1221
1234
  // src/sources/storage/atomicWrite.ts
1222
1235
  import { mkdirSync, renameSync, writeFileSync } from "node:fs";
1223
1236
  import { dirname } from "node:path";
@@ -1322,7 +1335,8 @@ function coerceStore(raw) {
1322
1335
  const project = entry["project"];
1323
1336
  const character = entry["character"];
1324
1337
  if (typeof project === "string" && typeof character === "string") {
1325
- out[key] = { project, character };
1338
+ const updatedMs = entry["updatedMs"];
1339
+ out[key] = typeof updatedMs === "number" ? { project, character, updatedMs } : { project, character };
1326
1340
  }
1327
1341
  }
1328
1342
  return out;
@@ -1341,7 +1355,7 @@ function upsertAttribution(root, sessionId, rec) {
1341
1355
  const path = storePath(root);
1342
1356
  withLock(`${path}.lock`, () => {
1343
1357
  const store = coerceStore(readJson(path, undefined));
1344
- 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 };
1345
1359
  atomicWrite(path, JSON.stringify(store));
1346
1360
  }, () => {});
1347
1361
  }
@@ -3525,41 +3539,8 @@ function readHelpfulEnv(cwd) {
3525
3539
  ...opt3("tfWorkspace", readTfWorkspace(cwd))
3526
3540
  };
3527
3541
  }
3528
- // src/sources/installed.ts
3529
- import { readdirSync, statSync as statSync3 } from "node:fs";
3530
- import { join as join10 } from "node:path";
3531
- var PREFIX = "pack-";
3532
- function listInstalledPacks(engineDir) {
3533
- const scope = join10(engineDir, "node_modules", "@ccsidekick");
3534
- let entries;
3535
- try {
3536
- entries = readdirSync(scope, { withFileTypes: true });
3537
- } catch {
3538
- return [];
3539
- }
3540
- const ids = [];
3541
- for (const entry of entries) {
3542
- if (!entry.name.startsWith(PREFIX) || entry.name.length <= PREFIX.length)
3543
- continue;
3544
- if (!isDir(entry, join10(scope, entry.name)))
3545
- continue;
3546
- ids.push(entry.name.slice(PREFIX.length));
3547
- }
3548
- return ids.sort();
3549
- }
3550
- function isDir(entry, path) {
3551
- if (entry.isDirectory())
3552
- return true;
3553
- if (!entry.isSymbolicLink())
3554
- return false;
3555
- try {
3556
- return statSync3(path).isDirectory();
3557
- } catch {
3558
- return false;
3559
- }
3560
- }
3561
3542
  // src/sources/markers.ts
3562
- import { readdirSync as readdirSync2 } from "node:fs";
3543
+ import { readdirSync } from "node:fs";
3563
3544
  import { dirname as dirname5, resolve as resolve2 } from "node:path";
3564
3545
  var FILE_MARKERS = {
3565
3546
  "package.json": "web",
@@ -3620,7 +3601,7 @@ function scanDir(dir, stacks) {
3620
3601
  let entries;
3621
3602
  let isRepoRoot = false;
3622
3603
  try {
3623
- entries = readdirSync2(dir);
3604
+ entries = readdirSync(dir);
3624
3605
  } catch {
3625
3606
  return false;
3626
3607
  }
@@ -3654,8 +3635,8 @@ function readMarkers(cwd) {
3654
3635
  return { stacks };
3655
3636
  }
3656
3637
  // src/sources/modelNames.ts
3657
- import { join as join11 } from "node:path";
3658
- var cachePath = (root) => join11(cacheDir(root), "model-names.json");
3638
+ import { join as join10 } from "node:path";
3639
+ var cachePath = (root) => join10(cacheDir(root), "model-names.json");
3659
3640
  function readModelNames(root) {
3660
3641
  return readJson(cachePath(root), {});
3661
3642
  }
@@ -3772,7 +3753,7 @@ function parsePayload(raw) {
3772
3753
  };
3773
3754
  }
3774
3755
  // src/sources/state.ts
3775
- import { join as join12 } from "node:path";
3756
+ import { join as join11 } from "node:path";
3776
3757
  var STATE_FILE = "state.json";
3777
3758
  var EMPTY2 = { pressureFired: [], milestones: [], helpful: {} };
3778
3759
  function isObject5(v) {
@@ -3797,7 +3778,7 @@ function asHelpful(v) {
3797
3778
  return out;
3798
3779
  }
3799
3780
  function readState(sessionDir2) {
3800
- const raw = readJson(join12(sessionDir2, STATE_FILE), undefined);
3781
+ const raw = readJson(join11(sessionDir2, STATE_FILE), undefined);
3801
3782
  if (!isObject5(raw))
3802
3783
  return EMPTY2;
3803
3784
  const character = raw["character"];
@@ -3827,14 +3808,14 @@ function mergeState(disk, next) {
3827
3808
  };
3828
3809
  }
3829
3810
  function writeState(sessionDir2, s) {
3830
- const path = join12(sessionDir2, STATE_FILE);
3811
+ const path = join11(sessionDir2, STATE_FILE);
3831
3812
  withLock(`${path}.lock`, () => {
3832
3813
  atomicWrite(path, JSON.stringify(mergeState(readState(sessionDir2), s)));
3833
3814
  }, () => {});
3834
3815
  }
3835
3816
  // src/sources/transcript.ts
3836
- import { readFileSync as readFileSync8, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
3837
- import { basename, dirname as dirname6, join as join13 } from "node:path";
3817
+ import { readFileSync as readFileSync8, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
3818
+ import { basename, dirname as dirname6, join as join12 } from "node:path";
3838
3819
  var asObj = (v) => v !== null && typeof v === "object" ? v : undefined;
3839
3820
  var asStr = (v) => typeof v === "string" ? v : undefined;
3840
3821
  var asNum = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
@@ -4059,7 +4040,7 @@ function reconstructTodos(lines) {
4059
4040
  }
4060
4041
  function statSafe(p) {
4061
4042
  try {
4062
- const s = statSync4(p);
4043
+ const s = statSync3(p);
4063
4044
  return { mtimeMs: s.mtimeMs, size: s.size };
4064
4045
  } catch {
4065
4046
  return null;
@@ -4149,15 +4130,15 @@ var repoRootCache = new Map;
4149
4130
  function* walkTree(dir, encodedDir) {
4150
4131
  let entries;
4151
4132
  try {
4152
- entries = readdirSync3(dir);
4133
+ entries = readdirSync2(dir);
4153
4134
  } catch {
4154
4135
  return;
4155
4136
  }
4156
4137
  for (const name of entries) {
4157
- const full = join13(dir, name);
4138
+ const full = join12(dir, name);
4158
4139
  let st;
4159
4140
  try {
4160
- st = statSync4(full);
4141
+ st = statSync3(full);
4161
4142
  } catch {
4162
4143
  continue;
4163
4144
  }
@@ -4171,12 +4152,12 @@ function* walkTree(dir, encodedDir) {
4171
4152
  function* walkJsonl(root) {
4172
4153
  let dirs;
4173
4154
  try {
4174
- dirs = readdirSync3(root);
4155
+ dirs = readdirSync2(root);
4175
4156
  } catch {
4176
4157
  return;
4177
4158
  }
4178
4159
  for (const encodedDir of dirs) {
4179
- yield* walkTree(join13(root, encodedDir), encodedDir);
4160
+ yield* walkTree(join12(root, encodedDir), encodedDir);
4180
4161
  }
4181
4162
  }
4182
4163
  function scanBounds(lines) {
@@ -4387,7 +4368,7 @@ function runClassify(stdin, env, clock) {
4387
4368
  }
4388
4369
  // src/cli/render.ts
4389
4370
  import { readFileSync as readFileSync10 } from "node:fs";
4390
- import { dirname as dirname7, join as join15 } from "node:path";
4371
+ import { dirname as dirname7, join as join14 } from "node:path";
4391
4372
 
4392
4373
  // src/compose/character.ts
4393
4374
  import { createHash as createHash5 } from "node:crypto";
@@ -4687,7 +4668,7 @@ var THEMES = {
4687
4668
  },
4688
4669
  solarizedDark: {
4689
4670
  displayName: "Solarized Dark",
4690
- hues: [32, 36, 100, 136, 168],
4671
+ hues: [32, 36, 178, 136, 168],
4691
4672
  comment: [32, 36],
4692
4673
  signals: { nominal: 46, caution: 136, critical: 167 },
4693
4674
  separator: 100
@@ -4715,14 +4696,14 @@ var THEMES = {
4715
4696
  },
4716
4697
  materialPalenight: {
4717
4698
  displayName: "Material Palenight",
4718
- hues: [111, 176, 117, 150, 217],
4699
+ hues: [111, 141, 45, 220, 203],
4719
4700
  comment: [60, 111, 176],
4720
4701
  signals: { nominal: 150, caution: 214, critical: 217 },
4721
4702
  separator: 150
4722
4703
  },
4723
4704
  nightOwl: {
4724
4705
  displayName: "Night Owl",
4725
- hues: [111, 176, 116, 149, 209],
4706
+ hues: [111, 122, 222, 149, 203],
4726
4707
  comment: [111, 116],
4727
4708
  signals: { nominal: 149, caution: 214, critical: 209 },
4728
4709
  separator: 66
@@ -4743,9 +4724,9 @@ var THEMES = {
4743
4724
  },
4744
4725
  darcula: {
4745
4726
  displayName: "Darcula",
4746
- hues: [173, 67, 103, 65, 221],
4727
+ hues: [173, 67, 103, 77, 221],
4747
4728
  comment: [173, 103],
4748
- signals: { nominal: 65, caution: 221, critical: 203 },
4729
+ signals: { nominal: 77, caution: 221, critical: 203 },
4749
4730
  separator: 173
4750
4731
  },
4751
4732
  xcodeDark: {
@@ -4792,35 +4773,35 @@ var THEMES = {
4792
4773
  },
4793
4774
  everforest: {
4794
4775
  displayName: "Everforest",
4795
- hues: [144, 180, 109, 108, 175],
4776
+ hues: [149, 222, 115, 173, 174],
4796
4777
  comment: [144, 174],
4797
4778
  signals: { nominal: 108, caution: 180, critical: 167 },
4798
4779
  separator: 144
4799
4780
  },
4800
4781
  rosePine: {
4801
4782
  displayName: "Rosé Pine",
4802
- hues: [181, 66, 152, 182, 216],
4783
+ hues: [181, 74, 152, 182, 216],
4803
4784
  comment: [60, 181, 182],
4804
4785
  signals: { nominal: 46, caution: 214, critical: 181 },
4805
4786
  separator: 66
4806
4787
  },
4807
4788
  rosePineMoon: {
4808
4789
  displayName: "Rosé Pine Moon",
4809
- hues: [174, 67, 152, 182, 216],
4790
+ hues: [217, 75, 122, 183, 223],
4810
4791
  comment: [60, 174, 182],
4811
4792
  signals: { nominal: 46, caution: 214, critical: 174 },
4812
4793
  separator: 174
4813
4794
  },
4814
4795
  kanagawa: {
4815
4796
  displayName: "Kanagawa",
4816
- hues: [110, 103, 66, 107, 180],
4797
+ hues: [110, 103, 79, 107, 180],
4817
4798
  comment: [110, 103],
4818
4799
  signals: { nominal: 107, caution: 180, critical: 203 },
4819
4800
  separator: 107
4820
4801
  },
4821
4802
  tomorrowNight: {
4822
4803
  displayName: "Tomorrow Night",
4823
- hues: [109, 139, 143, 222],
4804
+ hues: [110, 182, 149, 222, 167],
4824
4805
  comment: [109, 139],
4825
4806
  signals: { nominal: 46, caution: 214, critical: 203 },
4826
4807
  separator: 143
@@ -4834,7 +4815,7 @@ var THEMES = {
4834
4815
  },
4835
4816
  iceberg: {
4836
4817
  displayName: "Iceberg",
4837
- hues: [110, 140, 109, 144, 180],
4818
+ hues: [110, 140, 149, 180, 174],
4838
4819
  comment: [60, 110, 140],
4839
4820
  signals: { nominal: 46, caution: 214, critical: 203 },
4840
4821
  separator: 144
@@ -4876,7 +4857,7 @@ var THEMES = {
4876
4857
  },
4877
4858
  moonlight: {
4878
4859
  displayName: "Moonlight",
4879
- hues: [111, 141, 117, 150, 222],
4860
+ hues: [111, 177, 123, 211, 155],
4880
4861
  comment: [104, 111, 141],
4881
4862
  signals: { nominal: 150, caution: 222, critical: 203 },
4882
4863
  separator: 150
@@ -4897,14 +4878,14 @@ var THEMES = {
4897
4878
  },
4898
4879
  spacegray: {
4899
4880
  displayName: "Spacegray",
4900
- hues: [103, 139, 109, 144, 186],
4881
+ hues: [111, 182, 114, 180, 174],
4901
4882
  comment: [109, 139],
4902
4883
  signals: { nominal: 46, caution: 214, critical: 203 },
4903
4884
  separator: 144
4904
4885
  },
4905
4886
  palenight: {
4906
4887
  displayName: "Palenight",
4907
- hues: [176, 111, 117, 150, 209],
4888
+ hues: [177, 117, 215, 150, 204],
4908
4889
  comment: [60, 176, 111],
4909
4890
  signals: { nominal: 150, caution: 214, critical: 209 },
4910
4891
  separator: 209
@@ -4925,7 +4906,7 @@ var THEMES = {
4925
4906
  },
4926
4907
  githubDarkDimmed: {
4927
4908
  displayName: "GitHub Dark Dimmed",
4928
- hues: [75, 183, 114, 215, 203],
4909
+ hues: [75, 176, 78, 179, 174],
4929
4910
  comment: [75, 183],
4930
4911
  signals: { nominal: 114, caution: 215, critical: 203 },
4931
4912
  separator: 215
@@ -4965,107 +4946,16 @@ var THEMES = {
4965
4946
  signals: { nominal: 107, caution: 179, critical: 167 },
4966
4947
  separator: 179
4967
4948
  },
4968
- githubLight: {
4969
- displayName: "GitHub Light",
4970
- hues: [26, 98, 29, 130, 160],
4971
- comment: [26, 98],
4972
- signals: { nominal: 29, caution: 130, critical: 160 },
4973
- separator: 130
4974
- },
4975
- solarizedLight: {
4976
- displayName: "Solarized Light",
4977
- hues: [32, 36, 100, 136, 168],
4978
- comment: [32, 168],
4979
- signals: { nominal: 46, caution: 136, critical: 167 },
4980
- separator: 100
4981
- },
4982
- catppuccinLatte: {
4983
- displayName: "Catppuccin Latte",
4984
- hues: [99, 176, 27, 70, 202],
4985
- comment: [99, 176],
4986
- signals: { nominal: 70, caution: 214, critical: 203 },
4987
- separator: 70
4988
- },
4989
- gruvboxLight: {
4990
- displayName: "Gruvbox Light",
4991
- hues: [136, 130, 100, 24, 95],
4992
- comment: [136, 130],
4993
- signals: { nominal: 46, caution: 136, critical: 95 },
4994
- separator: 187
4995
- },
4996
- ayuLight: {
4997
- displayName: "Ayu Light",
4998
- hues: [209, 74, 106, 140, 204],
4999
- comment: [209, 74],
5000
- signals: { nominal: 46, caution: 214, critical: 209 },
5001
- separator: 106
5002
- },
5003
- oneLight: {
5004
- displayName: "One Light",
5005
- hues: [69, 71, 127, 31, 136],
5006
- comment: [69, 127],
5007
- signals: { nominal: 71, caution: 136, critical: 203 },
5008
- separator: 136
5009
- },
5010
- vscodeLightPlus: {
5011
- displayName: "VS Code Light+",
5012
- hues: [21, 128, 30, 29, 124],
5013
- comment: [28, 21, 30],
5014
- signals: { nominal: 29, caution: 214, critical: 124 },
5015
- separator: 124
5016
- },
5017
- xcodeLight: {
5018
- displayName: "Xcode Light",
5019
- hues: [133, 24, 20, 160],
5020
- comment: [66, 133, 20],
5021
- signals: { nominal: 46, caution: 214, critical: 160 },
5022
- separator: 160
5023
- },
5024
- quietLight: {
5025
- displayName: "Quiet Light",
5026
- hues: [62, 131, 64, 97, 136],
5027
- comment: [62, 64],
5028
- signals: { nominal: 64, caution: 136, critical: 131 },
5029
- separator: 64
5030
- },
5031
- tomorrowLight: {
5032
- displayName: "Tomorrow Light",
5033
- hues: [61, 97, 67, 64, 208],
5034
- comment: [61, 97],
5035
- signals: { nominal: 64, caution: 208, critical: 203 },
5036
- separator: 64
5037
- },
5038
- blulocoLight: {
5039
- displayName: "Bluloco Light",
5040
- hues: [26, 99, 32, 29, 166],
5041
- comment: [26, 32],
5042
- signals: { nominal: 29, caution: 166, critical: 203 },
5043
- separator: 166
5044
- },
5045
- rosePineDawn: {
5046
- displayName: "Rosé Pine Dawn",
5047
- hues: [174, 24, 67, 103, 179],
5048
- comment: [174, 103],
5049
- signals: { nominal: 46, caution: 179, critical: 174 },
5050
- separator: 179
5051
- },
5052
4949
  githubHCDark: {
5053
4950
  displayName: "GitHub HC Dark",
5054
- hues: [75, 183, 77, 215, 210],
4951
+ hues: [45, 135, 47, 220, 196],
5055
4952
  comment: [75, 183],
5056
4953
  signals: { nominal: 77, caution: 215, critical: 210 },
5057
4954
  separator: 77
5058
4955
  },
5059
- githubHCLight: {
5060
- displayName: "GitHub HC Light",
5061
- hues: [25, 55, 22, 52, 124],
5062
- comment: [25, 55],
5063
- signals: { nominal: 22, caution: 214, critical: 52 },
5064
- separator: 22
5065
- },
5066
4956
  vscodeHighContrast: {
5067
4957
  displayName: "VS Code HC",
5068
- hues: [74, 175, 79, 187, 174],
4958
+ hues: [45, 129, 118, 226, 203],
5069
4959
  comment: [107, 74],
5070
4960
  signals: { nominal: 79, caution: 214, critical: 203 },
5071
4961
  separator: 74
@@ -5238,7 +5128,13 @@ var truncateField = (f, budget) => {
5238
5128
  if (f.id === "model" && f.segments.length >= 2 && tail !== undefined) {
5239
5129
  const tailText = sanitizePackText(tail.text);
5240
5130
  const headText = f.segments.slice(0, -1).map((s) => sanitizePackText(s.text)).filter((t) => t !== "").join(" ");
5241
- const headBudget = Math.max(1, budget - displayWidth(tailText) - 1);
5131
+ const headBudget = budget - displayWidth(tailText) - 1;
5132
+ if (headBudget < 1) {
5133
+ return {
5134
+ id: f.id,
5135
+ segments: [{ role: "value", text: truncToWidth(fieldPlain(f), budget) }]
5136
+ };
5137
+ }
5242
5138
  return {
5243
5139
  id: f.id,
5244
5140
  segments: [
@@ -5249,6 +5145,21 @@ var truncateField = (f, budget) => {
5249
5145
  }
5250
5146
  return { id: f.id, segments: [{ role: "value", text: truncToWidth(fieldPlain(f), budget) }] };
5251
5147
  };
5148
+ var fitCells = (cells, avail, sepGlyph) => {
5149
+ if (cells.length === 0 || rowPlainWidth(cells, sepGlyph) <= avail)
5150
+ return [...cells];
5151
+ const last = cells[cells.length - 1];
5152
+ if (last === undefined)
5153
+ return [...cells];
5154
+ const head = cells.slice(0, -1);
5155
+ const sepWidth = displayWidth(` ${sepGlyph} `);
5156
+ const headWidth = head.length > 0 ? displayWidth(head.map(fieldPlain).join(` ${sepGlyph} `)) : 0;
5157
+ const budget = avail - headWidth - (head.length > 0 ? sepWidth : 0);
5158
+ if (budget >= 1)
5159
+ return [...head, truncateField(last, budget)];
5160
+ const headFitted = fitCells(head, Math.max(1, avail - sepWidth - 1), sepGlyph);
5161
+ return [...headFitted, truncateField(last, 1)];
5162
+ };
5252
5163
  var fitRow = (row, cells, avail, sepGlyph) => {
5253
5164
  const present = [...cells];
5254
5165
  for (const id of dropOrder(row)) {
@@ -5260,15 +5171,7 @@ var fitRow = (row, cells, avail, sepGlyph) => {
5260
5171
  if (idx >= 0)
5261
5172
  present.splice(idx, 1);
5262
5173
  }
5263
- if (rowPlainWidth(present, sepGlyph) <= avail)
5264
- return present;
5265
- const last = present[present.length - 1];
5266
- if (last === undefined)
5267
- return present;
5268
- const head = present.slice(0, -1);
5269
- const headWidth = head.length > 0 ? displayWidth(head.map(fieldPlain).join(` ${sepGlyph} `)) + displayWidth(` ${sepGlyph} `) : 0;
5270
- const budget = Math.max(1, avail - headWidth);
5271
- return [...head, truncateField(last, budget)];
5174
+ return fitCells(present, avail, sepGlyph);
5272
5175
  };
5273
5176
  var colorCell = (field, lineIdx, cellIdx, input, term) => {
5274
5177
  const { theme, moodShift, mood } = input;
@@ -6750,16 +6653,16 @@ function resolveHelpful(inputs, state, clock, minSeverity) {
6750
6653
  }
6751
6654
  // src/packs/load.ts
6752
6655
  import { readFileSync as readFileSync9 } from "node:fs";
6753
- import { fileURLToPath as fileURLToPath2 } from "node:url";
6656
+ import { fileURLToPath } from "node:url";
6754
6657
 
6755
6658
  // src/packs/allowlist.ts
6756
- var PREFIX2 = "@ccsidekick/pack-";
6659
+ var PREFIX = "@ccsidekick/pack-";
6757
6660
  var NAME_SEGMENT = /^[a-z0-9-]+$/;
6758
- var packPackageName = (name) => `${PREFIX2}${name}`;
6661
+ var packPackageName = (name) => `${PREFIX}${name}`;
6759
6662
  function isAllowedPackPackage(pkg) {
6760
- if (!pkg.startsWith(PREFIX2))
6663
+ if (!pkg.startsWith(PREFIX))
6761
6664
  return false;
6762
- return NAME_SEGMENT.test(pkg.slice(PREFIX2.length));
6665
+ return NAME_SEGMENT.test(pkg.slice(PREFIX.length));
6763
6666
  }
6764
6667
 
6765
6668
  // src/packs/validate.ts
@@ -6961,7 +6864,7 @@ var validatePack = (raw) => {
6961
6864
  };
6962
6865
 
6963
6866
  // src/packs/load.ts
6964
- var defaultResolver = (spec) => fileURLToPath2(import.meta.resolve(spec));
6867
+ var defaultResolver = (spec) => fileURLToPath(import.meta.resolve(spec));
6965
6868
  var errMessage = (e) => e instanceof Error ? e.message : String(e);
6966
6869
  function loadPack(name, resolver = defaultResolver) {
6967
6870
  if (!isAllowedPackPackage(packPackageName(name))) {
@@ -6991,17 +6894,38 @@ function loadPack(name, resolver = defaultResolver) {
6991
6894
  return { ok: false, reason: "pack failed validation" };
6992
6895
  return { ok: true, pack };
6993
6896
  }
6897
+ // src/packs/registry.ts
6898
+ var PACKS = [
6899
+ "barbie",
6900
+ "batman",
6901
+ "ben10",
6902
+ "darth-vader",
6903
+ "deadpool",
6904
+ "gandalf",
6905
+ "harry-potter",
6906
+ "hello-kitty",
6907
+ "iron-man",
6908
+ "james-bond",
6909
+ "joker",
6910
+ "naruto",
6911
+ "pikachu",
6912
+ "sherlock-holmes",
6913
+ "shinchan",
6914
+ "spiderman",
6915
+ "superman",
6916
+ "yoda"
6917
+ ];
6994
6918
  // src/cli/gc.ts
6995
- import { existsSync as existsSync2, readdirSync as readdirSync4, rmSync, statSync as statSync5 } from "node:fs";
6996
- import { join as join14 } from "node:path";
6919
+ import { existsSync as existsSync2, readdirSync as readdirSync3, rmSync, statSync as statSync4 } from "node:fs";
6920
+ import { join as join13 } from "node:path";
6997
6921
  var DAY_MS = 86400000;
6998
6922
  function newestMtime(dir) {
6999
6923
  let newest = 0;
7000
6924
  try {
7001
- newest = statSync5(dir).mtimeMs;
7002
- for (const name of readdirSync4(dir)) {
6925
+ newest = statSync4(dir).mtimeMs;
6926
+ for (const name of readdirSync3(dir)) {
7003
6927
  try {
7004
- const m = statSync5(join14(dir, name)).mtimeMs;
6928
+ const m = statSync4(join13(dir, name)).mtimeMs;
7005
6929
  if (m > newest)
7006
6930
  newest = m;
7007
6931
  } catch {}
@@ -7010,18 +6934,18 @@ function newestMtime(dir) {
7010
6934
  return newest;
7011
6935
  }
7012
6936
  function pruneSessions(root, now) {
7013
- const sessions = join14(root, "sessions");
6937
+ const sessions = join13(root, "sessions");
7014
6938
  let names;
7015
6939
  try {
7016
- names = readdirSync4(sessions);
6940
+ names = readdirSync3(sessions);
7017
6941
  } catch {
7018
6942
  return;
7019
6943
  }
7020
6944
  const cutoff = now - SESSION_TTL_DAYS * DAY_MS;
7021
6945
  for (const name of names) {
7022
- const dir = join14(sessions, name);
6946
+ const dir = join13(sessions, name);
7023
6947
  try {
7024
- if (!statSync5(dir).isDirectory())
6948
+ if (!statSync4(dir).isDirectory())
7025
6949
  continue;
7026
6950
  } catch {
7027
6951
  continue;
@@ -7093,13 +7017,12 @@ function applyThemeIcons(fields, theme, config) {
7093
7017
  };
7094
7018
  });
7095
7019
  }
7096
- function readGated(overrides, envInputs, config, root, dir, markers, sessionState, clock) {
7020
+ function readGated(overrides, envInputs, config, root, dir, markers, clock) {
7097
7021
  const creds = overrides?.creds !== undefined ? overrides.creds : envInputs.hasApiKey || config.network.usage_fetch ? readCreds() : null;
7098
7022
  const usage = overrides?.usage !== undefined ? overrides.usage : config.network.usage_fetch ? readUsageCached(root) : null;
7099
7023
  const balance = overrides?.balance !== undefined ? overrides.balance : config.network.balance_path !== "" ? readBalance(config.network.balance_path, clock) : null;
7100
7024
  const helpfulEnv = needsHelpfulEnv(markers) ? readHelpfulEnv(dir) : EMPTY_HELPFUL_ENV;
7101
- const installed = sessionState.character === undefined && config.character.mode === "random" && config.character.roster.length === 0 ? listInstalledPacks(engineRoot(import.meta.url)) : [];
7102
- return { creds, usage, balance, helpfulEnv, installed };
7025
+ return { creds, usage, balance, helpfulEnv };
7103
7026
  }
7104
7027
  var DEFAULT_EMBLEM = "❝";
7105
7028
  function lookupPackTheme(name) {
@@ -7128,10 +7051,10 @@ function build(stdin, env, term, clock, overrides) {
7128
7051
  return { line: "", persist: NOOP };
7129
7052
  const root = ccsidekickRoot(env);
7130
7053
  const base = dirname7(root);
7131
- const projectsRoot = join15(base, "projects");
7054
+ const projectsRoot = join14(base, "projects");
7132
7055
  const dir = payload.workspace.current_dir ?? payload.cwd ?? process.cwd();
7133
- const globalToml = readTextSafe(join15(root, "config.toml"));
7134
- const projectToml = readTextSafe(join15(dir, ".ccsidekick", "config.toml"));
7056
+ const globalToml = readTextSafe(join14(root, "config.toml"));
7057
+ const projectToml = readTextSafe(join14(dir, ".ccsidekick", "config.toml"));
7135
7058
  const config = loadConfig(globalToml, projectToml);
7136
7059
  const envInputs = readEnv(env);
7137
7060
  const aliases = readModelAliases(env);
@@ -7146,8 +7069,8 @@ function build(stdin, env, term, clock, overrides) {
7146
7069
  const attribution = readAttribution(root);
7147
7070
  const costCache = readCostCache(root);
7148
7071
  const fxTable = readFxCached(root);
7149
- const { creds, usage, balance, helpfulEnv, installed } = readGated(overrides, envInputs, config, root, dir, markers, sessionState, clock);
7150
- const persona = derivePersona(config, sessionState, session, installed);
7072
+ const { creds, usage, balance, helpfulEnv } = readGated(overrides, envInputs, config, root, dir, markers, clock);
7073
+ const persona = derivePersona(config, sessionState, session, PACKS, attribution);
7151
7074
  const loaded = loadPack(persona);
7152
7075
  const pack = loaded.ok ? loaded.pack : null;
7153
7076
  const project = deriveProject(git, payload);
@@ -7256,7 +7179,8 @@ function build(stdin, env, term, clock, overrides) {
7256
7179
  swallow(() => {
7257
7180
  upsertAttribution(root, String(session), {
7258
7181
  project: String(project),
7259
- character: persona
7182
+ character: persona,
7183
+ updatedMs: clock.now()
7260
7184
  });
7261
7185
  });
7262
7186
  if (!isDefault) {