@claudinho/cli 0.8.18 โ†’ 0.8.19

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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +132 -34
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -186,7 +186,7 @@ The statusline reads from a local micro-cache and **never blocks on the
186
186
  network** (<150ms). When several matches are live it shows them all inline:
187
187
  `โšฝ ๐Ÿ‡ณ๐Ÿ‡ด 1โ€“1 ๐Ÿ‡ซ๐Ÿ‡ท 87' ยท ๐Ÿ‡ธ๐Ÿ‡ณ 1โ€“2 ๐Ÿ‡ฎ๐Ÿ‡ถ 86'`. Customize via env:
188
188
 
189
- - `CLAUDINHO_TEAM=MEX` โ€” show only your team's match; also the default team for `next`, `markets next`, and `share next` when the argument is omitted
189
+ - `CLAUDINHO_TEAM=MEX` โ€” show only your team's match (a nation name works too, e.g. `CLAUDINHO_TEAM=mexico`); also the default team for `next`, `markets next`, and `share next` when the argument is omitted
190
190
  - `CLAUDINHO_MAX=2` โ€” cap how many live matches show inline (rest collapse to `+N`; default: all)
191
191
  - `CLAUDINHO_COMPACT=0` โ€” show 3-letter codes alongside flags
192
192
  - `CLAUDINHO_FLAGS=off` โ€” drop emoji flags for 3-letter codes (statusline) / plain names (`today`, `live`, `table`, `next`, hook); already automatic on terminals that can't render flag emoji, e.g. Warp
package/dist/index.js CHANGED
@@ -500,6 +500,63 @@ function shiftUtcDate(dateISO, days) {
500
500
  const [y, m, d] = dateISO.slice(0, 10).split("-").map(Number);
501
501
  return new Date(Date.UTC(y ?? 1970, (m ?? 1) - 1, (d ?? 1) + days)).toISOString().slice(0, 10);
502
502
  }
503
+ var FEED_TEXT_MAX = 100;
504
+ function sanitizeFeedText(value, max = FEED_TEXT_MAX) {
505
+ let out2 = "";
506
+ let count = 0;
507
+ for (const ch of String(value)) {
508
+ const cp = ch.codePointAt(0) ?? 0;
509
+ const isWhitespaceControl = cp === 9 || cp === 10 || cp === 13;
510
+ if ((cp <= 31 || cp >= 127 && cp <= 159) && !isWhitespaceControl) continue;
511
+ if (count >= max) break;
512
+ out2 += isWhitespaceControl ? " " : ch;
513
+ count++;
514
+ }
515
+ return out2;
516
+ }
517
+ function sanitizeTeam(t2) {
518
+ return {
519
+ ...t2 ?? {},
520
+ code: sanitizeFeedText(t2?.code ?? ""),
521
+ name: sanitizeFeedText(t2?.name ?? ""),
522
+ flag: sanitizeFeedText(t2?.flag ?? "")
523
+ };
524
+ }
525
+ function finiteOrUndefined(v) {
526
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
527
+ }
528
+ function sanitizeScorePair(v) {
529
+ const home = finiteOrUndefined(v?.home);
530
+ const away = finiteOrUndefined(v?.away);
531
+ return home !== void 0 && away !== void 0 ? { home, away } : void 0;
532
+ }
533
+ function sanitizeMatchStrings(m) {
534
+ const score = sanitizeScorePair(m.score);
535
+ return {
536
+ ...m,
537
+ venue: sanitizeFeedText(m.venue ?? ""),
538
+ city: m.city == null ? m.city : sanitizeFeedText(m.city),
539
+ country: m.country == null ? m.country : sanitizeFeedText(m.country),
540
+ home: sanitizeTeam(m.home),
541
+ away: sanitizeTeam(m.away),
542
+ score,
543
+ shootout: score ? sanitizeScorePair(m.shootout) : void 0,
544
+ minute: finiteOrUndefined(m.minute)
545
+ };
546
+ }
547
+ var segmenter = new Intl.Segmenter();
548
+ var WIDE_CLUSTER = new RegExp("^(?:\\p{Regional_Indicator}|\\p{Extended_Pictographic})", "u");
549
+ function displayWidth(s) {
550
+ let w = 0;
551
+ for (const { segment } of segmenter.segment(s)) {
552
+ w += WIDE_CLUSTER.test(segment) ? 2 : 1;
553
+ }
554
+ return w;
555
+ }
556
+ function padVisible(s, width) {
557
+ const w = displayWidth(s);
558
+ return w >= width ? s : s + " ".repeat(width - w);
559
+ }
503
560
  function outcomeFromScore(home, away) {
504
561
  if (home > away) return "H";
505
562
  if (home < away) return "A";
@@ -2887,6 +2944,7 @@ var ESPN_SOCCER = "https://site.api.espn.com/apis/site/v2/sports/soccer";
2887
2944
  var DEFAULT_COMPETITION = "fifa.world";
2888
2945
  var DEFAULT_BASE = `${ESPN_SOCCER}/${DEFAULT_COMPETITION}`;
2889
2946
  var USER_AGENT = "claudinho/0.0 (+https://github.com/arturogarrido/claudinho)";
2947
+ var MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
2890
2948
  function competitionBase(slug) {
2891
2949
  return `${ESPN_SOCCER}/${slug}`;
2892
2950
  }
@@ -2930,9 +2988,15 @@ function toInt(s) {
2930
2988
  return Number.isFinite(n) ? n : void 0;
2931
2989
  }
2932
2990
  function toTeam(t2) {
2933
- const name = t2?.displayName ?? t2?.name ?? t2?.location ?? t2?.shortDisplayName ?? "TBD";
2934
- const code = (t2?.abbreviation ?? name.slice(0, 3)).toUpperCase();
2935
- return { code, name, flag: nationToFlag(t2?.displayName ?? t2?.abbreviation ?? name) };
2991
+ const name = sanitizeFeedText(
2992
+ t2?.displayName ?? t2?.name ?? t2?.location ?? t2?.shortDisplayName ?? "TBD"
2993
+ );
2994
+ const code = sanitizeFeedText(t2?.abbreviation ?? name.slice(0, 3)).toUpperCase();
2995
+ return {
2996
+ code,
2997
+ name,
2998
+ flag: nationToFlag(sanitizeFeedText(t2?.displayName ?? t2?.abbreviation ?? name))
2999
+ };
2936
3000
  }
2937
3001
  function mapEspnEvent(ev, ctx = {}) {
2938
3002
  const comp = ev.competitions?.[0];
@@ -2963,9 +3027,9 @@ function mapEspnEvent(ev, ctx = {}) {
2963
3027
  stage,
2964
3028
  group,
2965
3029
  kickoff: ev.date,
2966
- venue: comp?.venue?.fullName ?? "",
2967
- city: comp?.venue?.address?.city || void 0,
2968
- country: comp?.venue?.address?.country || void 0,
3030
+ venue: sanitizeFeedText(comp?.venue?.fullName ?? ""),
3031
+ city: sanitizeFeedText(comp?.venue?.address?.city ?? "") || void 0,
3032
+ country: sanitizeFeedText(comp?.venue?.address?.country ?? "") || void 0,
2969
3033
  home,
2970
3034
  away,
2971
3035
  score: hasScore ? { home: hs, away: as } : void 0,
@@ -3089,11 +3153,18 @@ var EspnAdapter = class {
3089
3153
  try {
3090
3154
  const res = await doFetch(url, {
3091
3155
  signal: controller.signal,
3156
+ // ESPN never legitimately redirects; following one would sidestep the
3157
+ // fixed-host guarantee, so treat any redirect as a failure (degraded).
3158
+ redirect: "error",
3092
3159
  headers: { Accept: "application/json", "User-Agent": USER_AGENT }
3093
3160
  });
3094
3161
  if (!res.ok) {
3095
3162
  throw new Error(`ESPN request failed: ${res.status} ${res.statusText}`);
3096
3163
  }
3164
+ const length = Number(res.headers?.get?.("content-length"));
3165
+ if (Number.isFinite(length) && length > MAX_RESPONSE_BYTES) {
3166
+ throw new Error(`ESPN response too large: ${length} bytes`);
3167
+ }
3097
3168
  return await res.json();
3098
3169
  } finally {
3099
3170
  clearTimeout(timer);
@@ -4249,12 +4320,19 @@ var PolymarketProvider = class {
4249
4320
  const doFetch = this.opts.fetchImpl ?? fetch;
4250
4321
  const res = await doFetch(url, {
4251
4322
  signal: AbortSignal.timeout(timeoutMs ?? this.opts.timeoutMs ?? DEFAULT_TIMEOUT_MS),
4323
+ // Gamma never legitimately redirects; following one would sidestep the
4324
+ // host allow-list (it only validates the base URL), so reject redirects.
4325
+ redirect: "error",
4252
4326
  headers: { Accept: "application/json", "User-Agent": USER_AGENT2 }
4253
4327
  });
4254
4328
  if (res.status === 404) return void 0;
4255
4329
  if (!res.ok) {
4256
4330
  throw new Error(`Polymarket request failed: ${res.status} ${res.statusText}`);
4257
4331
  }
4332
+ const length = Number(res.headers?.get?.("content-length"));
4333
+ if (Number.isFinite(length) && length > MAX_RESPONSE_BYTES) {
4334
+ throw new Error(`Polymarket response too large: ${length} bytes`);
4335
+ }
4258
4336
  const data = await res.json();
4259
4337
  const event = Array.isArray(data) ? data[0] : data;
4260
4338
  return event && typeof event === "object" ? event : void 0;
@@ -4753,7 +4831,6 @@ var EN2 = {
4753
4831
  "table.degraded": "Live standings unavailable \u2014 showing the group roster.",
4754
4832
  "table.empty": "No standings available.",
4755
4833
  "match.none": "No match found with id {id}.",
4756
- "status.scheduled": "scheduled",
4757
4834
  "status.live": "LIVE",
4758
4835
  "status.ht": "HT",
4759
4836
  "status.ft": "FT",
@@ -4791,7 +4868,6 @@ var ES2 = {
4791
4868
  "table.degraded": "Tabla en vivo no disponible \u2014 mostrando la lista del grupo.",
4792
4869
  "table.empty": "No hay clasificaci\xF3n disponible.",
4793
4870
  "match.none": "No se encontr\xF3 partido con id {id}.",
4794
- "status.scheduled": "programado",
4795
4871
  "status.live": "EN VIVO",
4796
4872
  "status.ht": "DESC",
4797
4873
  "status.ft": "FIN",
@@ -4829,7 +4905,6 @@ var PT2 = {
4829
4905
  "table.degraded": "Classifica\xE7\xE3o ao vivo indispon\xEDvel \u2014 mostrando os times do grupo.",
4830
4906
  "table.empty": "Classifica\xE7\xE3o indispon\xEDvel.",
4831
4907
  "match.none": "Nenhum jogo encontrado com id {id}.",
4832
- "status.scheduled": "agendado",
4833
4908
  "status.live": "AO VIVO",
4834
4909
  "status.ht": "INT",
4835
4910
  "status.ft": "FIM",
@@ -4867,7 +4942,6 @@ var FR2 = {
4867
4942
  "table.degraded": "Classement en direct indisponible \u2014 affichage de la composition du groupe.",
4868
4943
  "table.empty": "Aucun classement disponible.",
4869
4944
  "match.none": "Aucun match trouv\xE9 avec id {id}.",
4870
- "status.scheduled": "pr\xE9vu",
4871
4945
  "status.live": "DIRECT",
4872
4946
  "status.ht": "MT",
4873
4947
  "status.ft": "FIN",
@@ -4890,7 +4964,7 @@ function makeT(lang) {
4890
4964
  const dict = CATALOGS2[lang] ?? EN2;
4891
4965
  return (key, vars) => {
4892
4966
  let s = dict[key] ?? EN2[key] ?? key;
4893
- if (vars) for (const [k, v] of Object.entries(vars)) s = s.replace(`{${k}}`, v);
4967
+ if (vars) for (const [k, v] of Object.entries(vars)) s = s.replaceAll(`{${k}}`, v);
4894
4968
  return s;
4895
4969
  };
4896
4970
  }
@@ -4949,7 +5023,7 @@ function matchLine(m, cfg, t2, c, flags = true) {
4949
5023
  const home = flags ? `${m.home.flag} ${m.home.name}` : m.home.name;
4950
5024
  const away = flags ? `${m.away.name} ${m.away.flag}` : m.away.name;
4951
5025
  const mid2 = isLive(m.status) || m.status === "FT" ? c.bold(scoreline(m)) : c.dim("vs");
4952
- const left = `${home.padEnd(22)} ${mid2.padStart(3)} ${away}`;
5026
+ const left = `${padVisible(home, 22)} ${mid2.padStart(3)} ${away}`;
4953
5027
  let right = "";
4954
5028
  if (m.status === "SCHEDULED") {
4955
5029
  right = c.dim(
@@ -5218,6 +5292,10 @@ function inLiveWindow(now = Date.now(), fixtures = allFixtures()) {
5218
5292
  function isResolvedFixture(m) {
5219
5293
  return isResolvedNation(m.home) && isResolvedNation(m.away);
5220
5294
  }
5295
+ function isMatchShaped(m) {
5296
+ const x = m;
5297
+ return !!x && typeof x === "object" && typeof x.id === "string" && typeof x.kickoff === "string" && !!x.home?.code && !!x.away?.code;
5298
+ }
5221
5299
  function nextOverall(now, fixtures = allFixtures()) {
5222
5300
  return [...fixtures].sort(byKickoff).find((m) => Date.parse(m.kickoff) >= now && isResolvedFixture(m));
5223
5301
  }
@@ -5238,7 +5316,7 @@ function liveMatchesFromCache(state, nowMs = Date.now()) {
5238
5316
  const liveArr = fresh && Array.isArray(state?.live) ? state.live : [];
5239
5317
  return liveArr.filter(
5240
5318
  (m) => !!m && typeof m === "object" && isLive(m.status) && !!m.home?.code && !!m.away?.code
5241
- );
5319
+ ).map(sanitizeMatchStrings);
5242
5320
  }
5243
5321
  function renderPrompt(state, opts = {}) {
5244
5322
  const now = opts.now ?? /* @__PURE__ */ new Date();
@@ -5247,7 +5325,7 @@ function renderPrompt(state, opts = {}) {
5247
5325
  const flags = opts.flags ?? true;
5248
5326
  const team = opts.team?.toUpperCase();
5249
5327
  const live = liveMatchesFromCache(state, nowMs);
5250
- const cachedFixtures = Array.isArray(state?.fixtures) ? state.fixtures : [];
5328
+ const cachedFixtures = Array.isArray(state?.fixtures) ? state.fixtures.filter(isMatchShaped).map(sanitizeMatchStrings) : [];
5251
5329
  const schedule = cachedFixtures.length ? mergeLive(allFixtures(), cachedFixtures) : void 0;
5252
5330
  if (team) {
5253
5331
  const mine = live.find((m) => m.home?.code === team || m.away?.code === team);
@@ -5280,10 +5358,16 @@ function renderPrompt(state, opts = {}) {
5280
5358
  }
5281
5359
 
5282
5360
  // src/hook.ts
5361
+ function rosterPinned(t2) {
5362
+ const { team } = lookupTeam(t2.code);
5363
+ return team ? { ...t2, name: team.name, flag: team.flag } : t2;
5364
+ }
5283
5365
  function line(m, flags) {
5284
5366
  const minute = m.status === "HT" ? "half-time" : m.minute ? `${m.minute}'` : "live";
5285
- const home = flags ? `${m.home.flag} ${m.home.name}` : m.home.name;
5286
- const away = flags ? `${m.away.name} ${m.away.flag}` : m.away.name;
5367
+ const h = rosterPinned(m.home);
5368
+ const a = rosterPinned(m.away);
5369
+ const home = flags ? `${h.flag} ${h.name}` : h.name;
5370
+ const away = flags ? `${a.name} ${a.flag}` : a.name;
5287
5371
  return `${home} ${scoreline(m)} ${away} (${minute})`;
5288
5372
  }
5289
5373
  function renderHook(state, opts = {}) {
@@ -5399,7 +5483,10 @@ function spawnRefresh(source) {
5399
5483
  if (!entry) return;
5400
5484
  const child = spawn(process.execPath, [entry, "_refresh", "--source", source], {
5401
5485
  detached: true,
5402
- stdio: "ignore"
5486
+ stdio: "ignore",
5487
+ // Windows: a detached child gets its own console window without this โ€”
5488
+ // the statusline would flash one every ~12โ€“15s during live matches.
5489
+ windowsHide: true
5403
5490
  });
5404
5491
  child.unref();
5405
5492
  } catch {
@@ -5665,18 +5752,24 @@ function precheck(cfg, t2, date) {
5665
5752
  throw new InputError(t2("err.date", { date }));
5666
5753
  }
5667
5754
  }
5668
- function resolveTeamArg(team, usage) {
5755
+ function resolveTeamArg(team, usage, t2) {
5669
5756
  const raw = team ?? process.env.CLAUDINHO_TEAM;
5670
5757
  if (!raw) throw new InputError(usage);
5671
5758
  const { team: hit, matches } = lookupTeam(raw);
5672
5759
  if (hit) return hit.code;
5673
5760
  if (matches.length > 1) {
5674
5761
  throw new InputError(
5675
- `"${raw}" is ambiguous \u2014 did you mean ${matches.map((m) => `${m.name} (${m.code})`).join(", ")}? Use the 3-letter code.`
5762
+ `${t2("team.ambiguous", { query: raw })} ${matches.map((m) => `${m.name} (${m.code})`).join(", ")}`
5676
5763
  );
5677
5764
  }
5678
5765
  if (/^[A-Za-z]{3}$/.test(raw)) return raw.toUpperCase();
5679
- throw new InputError(`No team found for "${raw}". Use a nation name or 3-letter code (e.g. Mexico, MEX).`);
5766
+ throw new InputError(t2("team.none", { query: raw }));
5767
+ }
5768
+ function resolveEnvTeam(raw) {
5769
+ if (!raw) return void 0;
5770
+ const { team } = lookupTeam(raw);
5771
+ if (team) return team.code;
5772
+ return /^[A-Za-z]{3}$/.test(raw) ? raw.toUpperCase() : void 0;
5680
5773
  }
5681
5774
  async function cmdToday(date, ctx) {
5682
5775
  const { cfg, t: t2 } = ctx;
@@ -5748,7 +5841,7 @@ async function cmdLive(ctx) {
5748
5841
  async function cmdNext(team, ctx) {
5749
5842
  const { cfg, t: t2, now } = ctx;
5750
5843
  precheck(cfg, t2);
5751
- const code = resolveTeamArg(team, "Usage: claudinho next <team> (or set CLAUDINHO_TEAM)");
5844
+ const code = resolveTeamArg(team, "Usage: claudinho next <team> (or set CLAUDINHO_TEAM)", t2);
5752
5845
  const { fixture, degraded, source } = await getNextFixtureForTeam(
5753
5846
  adapterFor(ctx),
5754
5847
  code,
@@ -5770,7 +5863,7 @@ async function cmdNext(team, ctx) {
5770
5863
  out(header(t2("next.label", { team: code }), c));
5771
5864
  out();
5772
5865
  out(matchLine(fixture, cfg, t2, c, flags));
5773
- const stage = fixture.stage !== "GROUP" ? `${stageLabel(fixture)} \xB7 ` : "";
5866
+ const stage = fixture.stage !== "GROUP" ? `${stageLabelI18n(cfg.lang, fixture.stage)} \xB7 ` : "";
5774
5867
  out(
5775
5868
  " " + c.dim(
5776
5869
  `${stage}${formatKickoff(fixture.kickoff, { tz: cfg.tz, locale: cfg.lang })} \xB7 ` + t2("next.in", { countdown: countdown(fixture.kickoff) })
@@ -5931,7 +6024,7 @@ async function cmdBracket(stage, opts, ctx) {
5931
6024
  function cmdPrompt({ cfg }) {
5932
6025
  try {
5933
6026
  const payload = readCursorPayload();
5934
- const team = process.env.CLAUDINHO_TEAM;
6027
+ const team = resolveEnvTeam(process.env.CLAUDINHO_TEAM);
5935
6028
  const compact = !["0", "false", "no"].includes(
5936
6029
  (process.env.CLAUDINHO_COMPACT ?? "").toLowerCase()
5937
6030
  );
@@ -5949,7 +6042,7 @@ function cmdPrompt({ cfg }) {
5949
6042
  }
5950
6043
  function cmdHook({ cfg }) {
5951
6044
  try {
5952
- const team = process.env.CLAUDINHO_TEAM;
6045
+ const team = resolveEnvTeam(process.env.CLAUDINHO_TEAM);
5953
6046
  const state = readCurrentState(cfg.source, resolveCompetition());
5954
6047
  const ctx = renderHook(state, { team, flags: flagsEnabled() });
5955
6048
  if (ctx) out(ctx);
@@ -6001,7 +6094,8 @@ function cmdInitCursor(opts, { cfg }) {
6001
6094
  out(CURSOR_MCP_SNIPPET);
6002
6095
  return;
6003
6096
  }
6004
- printInitResult(initCursorStatusline(), cfg);
6097
+ const res = initCursorStatusline();
6098
+ printInitResult(res, cfg);
6005
6099
  out("");
6006
6100
  out("Optional \u2014 live MCP tools in Cursor: add to ~/.cursor/mcp.json (or project .cursor/mcp.json):");
6007
6101
  out(CURSOR_MCP_SNIPPET);
@@ -6009,7 +6103,7 @@ function cmdInitCursor(opts, { cfg }) {
6009
6103
  out("Tip: export CLAUDINHO_CURSOR_META=auto for a model + context line below the score.");
6010
6104
  out("");
6011
6105
  out("\u2192 Restart your agent session to see it.");
6012
- printInitStarCta(cfg);
6106
+ if (res.action === "written") printInitStarCta(cfg);
6013
6107
  }
6014
6108
  function cmdInitClaude(opts, { cfg }) {
6015
6109
  if (opts.print) {
@@ -6023,14 +6117,16 @@ function cmdInitClaude(opts, { cfg }) {
6023
6117
  out(CLAUDE_MCP_ONELINER);
6024
6118
  return;
6025
6119
  }
6026
- printInitResult(initStatusline(), cfg);
6027
- printInitResult(initHook(), cfg);
6120
+ const statusRes = initStatusline();
6121
+ const hookRes = initHook();
6122
+ printInitResult(statusRes, cfg);
6123
+ printInitResult(hookRes, cfg);
6028
6124
  out("");
6029
6125
  out("Next \u2014 add the MCP server:");
6030
6126
  out(` ${CLAUDE_MCP_ONELINER}`);
6031
6127
  out("");
6032
6128
  out("\u2192 Restart Claude Code to see it.");
6033
- printInitStarCta(cfg);
6129
+ if (statusRes.action === "written" || hookRes.action === "written") printInitStarCta(cfg);
6034
6130
  }
6035
6131
  async function cmdMatch(id, ctx) {
6036
6132
  const { cfg, t: t2 } = ctx;
@@ -6054,7 +6150,7 @@ async function cmdMatch(id, ctx) {
6054
6150
  out(disclaimer(t2, c));
6055
6151
  return;
6056
6152
  }
6057
- const stageLabelText = stageLabel(match);
6153
+ const stageLabelText = stageLabelI18n(cfg.lang, match.stage, match.group ?? void 0);
6058
6154
  out(header(`${match.home.name} ${scoreline(match)} ${match.away.name}`, c));
6059
6155
  out(" " + c.dim(`${stageLabelText} \xB7 ${matchLocation(match)}`));
6060
6156
  out(
@@ -6100,7 +6196,7 @@ async function cmdMarkets(target, team, ctx) {
6100
6196
  const { cfg, t: t2 } = ctx;
6101
6197
  if (target === "next") {
6102
6198
  precheck(cfg, t2);
6103
- const code = resolveTeamArg(team, "Usage: claudinho markets next <team> (or set CLAUDINHO_TEAM)");
6199
+ const code = resolveTeamArg(team, "Usage: claudinho markets next <team> (or set CLAUDINHO_TEAM)", t2);
6104
6200
  const now2 = ctx.now ?? /* @__PURE__ */ new Date();
6105
6201
  const { match: fixture, degraded } = await marketFixtureForTeam(adapterFor(ctx), code, now2);
6106
6202
  const sig = fixture && marketRelevant(fixture, now2) ? (await marketSignalsFor(ctx, [fixture], MARKETS_CMD_OPTS)).get(fixture.id) : void 0;
@@ -6376,7 +6472,7 @@ async function cmdShare(target, team, opts, ctx) {
6376
6472
  }
6377
6473
  if (target === "next") {
6378
6474
  precheck(cfg, t2);
6379
- const code = resolveTeamArg(team, "Usage: claudinho share next <team> (or set CLAUDINHO_TEAM)");
6475
+ const code = resolveTeamArg(team, "Usage: claudinho share next <team> (or set CLAUDINHO_TEAM)", t2);
6380
6476
  const { fixture, degraded: degraded2, source: source2 } = await getNextFixtureForTeam(
6381
6477
  adapterFor(ctx),
6382
6478
  code,
@@ -6528,6 +6624,7 @@ function maybeStarNudge(ctx) {
6528
6624
  out(c.dim(` \u2B50 Enjoying Claudinho? Star it \u2192 ${REPO_URL} (claudinho star)`));
6529
6625
  }
6530
6626
  function printInitStarCta(cfg) {
6627
+ if (cfg.json || !process.stdout.isTTY || process.env.CLAUDINHO_NO_STAR) return;
6531
6628
  const c = painterFor(cfg);
6532
6629
  out("");
6533
6630
  out(c.dim(`\u2B50 If this keeps you in the flow during the match, star the repo \u2192 ${REPO_URL}`));
@@ -6541,7 +6638,8 @@ function cmdVibe(ctx) {
6541
6638
  const state = readCurrentState(cfg.source, resolveCompetition());
6542
6639
  liveSeg = vibeLiveSegment(
6543
6640
  liveMatchesFromCache(state, (ctx.now ?? /* @__PURE__ */ new Date()).getTime()),
6544
- process.env.CLAUDINHO_TEAM
6641
+ // Name-or-code, matching the statusline/hook (offline lookup).
6642
+ resolveEnvTeam(process.env.CLAUDINHO_TEAM)
6545
6643
  );
6546
6644
  } catch {
6547
6645
  }
@@ -6565,7 +6663,7 @@ function handlePipeError(stream) {
6565
6663
  }
6566
6664
  handlePipeError(process.stdout);
6567
6665
  handlePipeError(process.stderr);
6568
- var VERSION = "0.8.18";
6666
+ var VERSION = "0.8.19";
6569
6667
  var DISCLAIMER = "Claudinho is an independent fan project. Not affiliated with or endorsed by FIFA or Anthropic.";
6570
6668
  function ctxFrom(cmd) {
6571
6669
  let root = cmd;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claudinho/cli",
3
- "version": "0.8.18",
3
+ "version": "0.8.19",
4
4
  "description": "Live scores, fixtures, group tables, and read-only prediction-market signals for the 2026 men's football tournament โ€” in your terminal, Claude Code, and Cursor CLI statusline. No API keys. Not affiliated with FIFA or Anthropic.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -61,7 +61,7 @@
61
61
  "tsup": "^8.0.0",
62
62
  "typescript": "^5.7.0",
63
63
  "vitest": "^4.1.9",
64
- "@claudinho/core": "0.8.18"
64
+ "@claudinho/core": "0.8.19"
65
65
  },
66
66
  "scripts": {
67
67
  "build": "tsup",