@claudinho/cli 0.9.1 → 0.9.3

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 +12 -0
  2. package/dist/index.js +57 -7
  3. package/package.json +5 -5
package/README.md CHANGED
@@ -240,6 +240,18 @@ state hits the network. Live scores come from **ESPN's** public scoreboard (a
240
240
  swappable provider, attributed in output as `Live data: ESPN`) and market signals
241
241
  from Polymarket; provider attribution and rate limits are respected.
242
242
 
243
+ ## Privacy Policy
244
+
245
+ No personal data collected — no accounts, no telemetry, no analytics, no tracking, and no
246
+ Claudinho server. To show live results, the CLI makes read-only requests to public services
247
+ (ESPN; Polymarket for informational-only market signals) with no account or personal data
248
+ attached, though those services still receive standard request metadata (such as your IP
249
+ address) like any HTTP call. It keeps a small cache of public match data in your local cache
250
+ directory (`~/.cache/claudinho`, or `$XDG_CACHE_HOME/claudinho`), and the optional `init`
251
+ commands update your Claude Code / Cursor settings file after saving a one-time
252
+ `.claudinho.bak` backup — all on your machine, never uploaded. Full policy:
253
+ [PRIVACY.md](https://github.com/arturogarrido/claudinho/blob/main/PRIVACY.md).
254
+
243
255
  ## License
244
256
 
245
257
  MIT © 2026 Arturo Garrido · [source & issues](https://github.com/arturogarrido/claudinho)
package/dist/index.js CHANGED
@@ -2855,10 +2855,22 @@ function nextFixtureForTeam(code, opts = {}) {
2855
2855
  );
2856
2856
  }
2857
2857
  var LIVE_WINDOW_MS = 140 * 6e4;
2858
+ var KNOCKOUT_EXTRA_TIME_MS = 60 * 6e4;
2859
+ function liveWindowMsFor(m) {
2860
+ return m.stage === "GROUP" || m.stage === "FRIENDLY" ? LIVE_WINDOW_MS : LIVE_WINDOW_MS + KNOCKOUT_EXTRA_TIME_MS;
2861
+ }
2862
+ function isTournamentWindowOver(now = Date.now(), fixtures = SCHEDULE) {
2863
+ if (fixtures.length === 0) return false;
2864
+ return fixtures.every((m) => {
2865
+ const k = Date.parse(m.kickoff);
2866
+ if (Number.isNaN(k)) return false;
2867
+ return now > k + liveWindowMsFor(m);
2868
+ });
2869
+ }
2858
2870
  function fixturesInLiveWindow(now = Date.now(), fixtures = SCHEDULE) {
2859
2871
  return fixtures.filter((m) => {
2860
2872
  const k = Date.parse(m.kickoff);
2861
- return now >= k && now <= k + LIVE_WINDOW_MS;
2873
+ return now >= k && now <= k + liveWindowMsFor(m);
2862
2874
  }).sort(byKickoff);
2863
2875
  }
2864
2876
  function groups(fixtures = SCHEDULE) {
@@ -2944,7 +2956,7 @@ function rosterAtZero(matches) {
2944
2956
  var ESPN_SOCCER = "https://site.api.espn.com/apis/site/v2/sports/soccer";
2945
2957
  var DEFAULT_COMPETITION = "fifa.world";
2946
2958
  var DEFAULT_BASE = `${ESPN_SOCCER}/${DEFAULT_COMPETITION}`;
2947
- var USER_AGENT = `claudinho/${"0.9.1"} (+https://github.com/arturogarrido/claudinho)`;
2959
+ var USER_AGENT = `claudinho/${"0.9.3"} (+https://github.com/arturogarrido/claudinho)`;
2948
2960
  var MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
2949
2961
  function competitionBase(slug) {
2950
2962
  return `${ESPN_SOCCER}/${slug}`;
@@ -4989,6 +5001,8 @@ var EN2 = {
4989
5001
  "today.none": "No matches scheduled for this date.",
4990
5002
  "live.title": "Live now",
4991
5003
  "live.none": "No matches in play right now.",
5004
+ "signoff.complete": "The World Cup is complete. Thanks for using Claudinho.",
5005
+ "signoff.star": "Star the project:",
4992
5006
  "live.degraded": "Live scores unavailable right now \u2014 couldn't reach the data provider.",
4993
5007
  "feed.degraded": "Live scores unavailable \u2014 showing the bundled schedule.",
4994
5008
  "next.none": "No upcoming fixture found for {team}.",
@@ -5027,6 +5041,8 @@ var ES2 = {
5027
5041
  "today.none": "No hay partidos para esta fecha.",
5028
5042
  "live.title": "En vivo",
5029
5043
  "live.none": "No hay partidos en juego ahora mismo.",
5044
+ "signoff.complete": "El Mundial ha terminado. Gracias por usar Claudinho.",
5045
+ "signoff.star": "Dale una estrella al proyecto:",
5030
5046
  "live.degraded": "Marcadores en vivo no disponibles \u2014 no se pudo conectar con el proveedor de datos.",
5031
5047
  "feed.degraded": "Marcadores en vivo no disponibles \u2014 mostrando el calendario.",
5032
5048
  "next.none": "No se encontr\xF3 pr\xF3ximo partido para {team}.",
@@ -5065,6 +5081,8 @@ var PT2 = {
5065
5081
  "today.none": "Nenhum jogo para esta data.",
5066
5082
  "live.title": "Ao vivo",
5067
5083
  "live.none": "Nenhum jogo em andamento agora.",
5084
+ "signoff.complete": "A Copa do Mundo terminou. Obrigado por usar o Claudinho.",
5085
+ "signoff.star": "D\xEA uma estrela ao projeto:",
5068
5086
  "live.degraded": "Placar ao vivo indispon\xEDvel \u2014 n\xE3o foi poss\xEDvel conectar ao provedor de dados.",
5069
5087
  "feed.degraded": "Placar ao vivo indispon\xEDvel \u2014 mostrando a tabela de jogos.",
5070
5088
  "next.none": "Nenhum pr\xF3ximo jogo encontrado para {team}.",
@@ -5103,6 +5121,8 @@ var FR2 = {
5103
5121
  "today.none": "Aucun match pr\xE9vu pour cette date.",
5104
5122
  "live.title": "En direct",
5105
5123
  "live.none": "Aucun match en cours pour l'instant.",
5124
+ "signoff.complete": "La Coupe du Monde est termin\xE9e. Merci d'avoir utilis\xE9 Claudinho.",
5125
+ "signoff.star": "Mettez une \xE9toile au projet :",
5106
5126
  "live.degraded": "Scores en direct indisponibles \u2014 impossible de joindre le fournisseur de donn\xE9es.",
5107
5127
  "feed.degraded": "Scores en direct indisponibles \u2014 affichage du calendrier.",
5108
5128
  "next.none": "Aucun prochain match trouv\xE9 pour {team}.",
@@ -5469,6 +5489,7 @@ function releaseLock() {
5469
5489
 
5470
5490
  // src/statusline.ts
5471
5491
  var DISPLAY_STALE_MS = 5 * 6e4;
5492
+ var TOURNAMENT_COMPLETE_LINE = "\u26BD World Cup 2026 is complete \xB7 Thanks for vibing with Claudinho";
5472
5493
  var FLAGLESS_TERMINALS = /* @__PURE__ */ new Set(["WarpTerminal"]);
5473
5494
  function flagsEnabled(env = process.env) {
5474
5495
  const v = (env.CLAUDINHO_FLAGS ?? "").trim().toLowerCase();
@@ -5512,6 +5533,7 @@ function liveMatchesFromCache(state, nowMs = Date.now()) {
5512
5533
  function renderPrompt(state, opts = {}) {
5513
5534
  const now = opts.now ?? /* @__PURE__ */ new Date();
5514
5535
  const nowMs = now.getTime();
5536
+ const defaultCompetition = opts.defaultCompetition ?? true;
5515
5537
  const compact = opts.compact ?? true;
5516
5538
  const flags = opts.flags ?? true;
5517
5539
  const team = opts.team?.toUpperCase();
@@ -5545,6 +5567,9 @@ function renderPrompt(state, opts = {}) {
5545
5567
  if (next && isResolvedFixture(next)) {
5546
5568
  return `${teamTok(next.home, flags)} vs ${teamTok(next.away, flags)} in ${countdown(next.kickoff, now)}`;
5547
5569
  }
5570
+ if (defaultCompetition && isTournamentWindowOver(nowMs, schedule)) {
5571
+ return TOURNAMENT_COMPLETE_LINE;
5572
+ }
5548
5573
  return "\u26BD \u2014";
5549
5574
  }
5550
5575
 
@@ -5995,7 +6020,7 @@ async function cmdToday(date, ctx) {
5995
6020
  const src = dataSource(source, cfg.lang, c);
5996
6021
  if (src) out(src);
5997
6022
  out(disclaimer(t2, c));
5998
- maybeStarNudge(ctx);
6023
+ endScoreCommand(ctx);
5999
6024
  }
6000
6025
  async function cmdLive(ctx) {
6001
6026
  const { cfg, t: t2 } = ctx;
@@ -6022,7 +6047,7 @@ async function cmdLive(ctx) {
6022
6047
  const src = dataSource(source, cfg.lang, c);
6023
6048
  if (src) out(src);
6024
6049
  out(disclaimer(t2, c));
6025
- maybeStarNudge(ctx);
6050
+ endScoreCommand(ctx);
6026
6051
  }
6027
6052
  async function cmdNext(team, ctx) {
6028
6053
  const { cfg, t: t2, now } = ctx;
@@ -6044,6 +6069,7 @@ async function cmdNext(team, ctx) {
6044
6069
  out(c.dim(" " + (degraded ? t2("live.degraded") : t2("next.none", { team: code }))));
6045
6070
  out();
6046
6071
  out(disclaimer(t2, c));
6072
+ endScoreCommand(ctx);
6047
6073
  return;
6048
6074
  }
6049
6075
  out(header(t2("next.label", { team: code }), c));
@@ -6059,7 +6085,7 @@ async function cmdNext(team, ctx) {
6059
6085
  const src = dataSource(source, cfg.lang, c);
6060
6086
  if (src) out(src);
6061
6087
  out(disclaimer(t2, c));
6062
- maybeStarNudge(ctx);
6088
+ endScoreCommand(ctx);
6063
6089
  }
6064
6090
  function cmdTeam(query, ctx) {
6065
6091
  const { cfg, t: t2 } = ctx;
@@ -6217,7 +6243,15 @@ function cmdPrompt({ cfg }, io = {}) {
6217
6243
  const maxRaw = Number.parseInt(process.env.CLAUDINHO_MAX ?? "", 10);
6218
6244
  const max = Number.isFinite(maxRaw) && maxRaw > 0 ? maxRaw : void 0;
6219
6245
  const state = readCurrentState(cfg.source, resolveCompetition());
6220
- const scoreLine = renderPrompt(state, { team, compact, max, flags: flagsEnabled() });
6246
+ const scoreLine = renderPrompt(state, {
6247
+ team,
6248
+ compact,
6249
+ max,
6250
+ flags: flagsEnabled(),
6251
+ // The bundled schedule describes the default competition only — see the
6252
+ // sign-off gate in renderPrompt.
6253
+ defaultCompetition: resolveCompetition() === DEFAULT_COMPETITION
6254
+ });
6221
6255
  out(renderPromptOutput(scoreLine, payload));
6222
6256
  if (!state && !isLockFresh() || shouldRefresh(Date.now(), state) || shouldRefreshFixtures(Date.now(), state)) {
6223
6257
  spawnRefresh(cfg.source);
@@ -6809,6 +6843,22 @@ function maybeStarNudge(ctx) {
6809
6843
  out();
6810
6844
  out(c.dim(` \u2B50 Enjoying Claudinho? Star it \u2192 ${REPO_URL} (claudinho star)`));
6811
6845
  }
6846
+ function endScoreCommand(ctx) {
6847
+ const over = resolveCompetition() === DEFAULT_COMPETITION && isTournamentWindowOver(ctx.now?.getTime() ?? Date.now());
6848
+ if (over) printTournamentSignOff(ctx);
6849
+ else maybeStarNudge(ctx);
6850
+ }
6851
+ function printTournamentSignOff(ctx) {
6852
+ const { cfg, t: t2 } = ctx;
6853
+ if (cfg.json) return;
6854
+ const c = painterFor(cfg);
6855
+ out();
6856
+ out(`${t2("signoff.complete")} ${SHARE_HASHTAG}`);
6857
+ if (!process.stdout.isTTY || process.env.CLAUDINHO_NO_STAR) return;
6858
+ out();
6859
+ out(c.dim(`\u2B50 ${t2("signoff.star")}`));
6860
+ out(c.cyan(REPO_URL));
6861
+ }
6812
6862
  function printInitStarCta(cfg) {
6813
6863
  if (cfg.json || !process.stdout.isTTY || process.env.CLAUDINHO_NO_STAR) return;
6814
6864
  const c = painterFor(cfg);
@@ -6849,7 +6899,7 @@ function handlePipeError(stream) {
6849
6899
  }
6850
6900
  handlePipeError(process.stdout);
6851
6901
  handlePipeError(process.stderr);
6852
- var VERSION = "0.9.1";
6902
+ var VERSION = "0.9.3";
6853
6903
  var DISCLAIMER = "Claudinho is an independent fan project. Not affiliated with or endorsed by FIFA or Anthropic.";
6854
6904
  function ctxFrom(cmd) {
6855
6905
  let root = cmd;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claudinho/cli",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "description": "Live 2026 World Cup scores, fixtures, group tables, and read-only prediction-market signals 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",
@@ -57,12 +57,12 @@
57
57
  "picocolors": "^1.1.1"
58
58
  },
59
59
  "devDependencies": {
60
- "@types/node": "^22.20.0",
61
- "@vitest/coverage-v8": "^4.1.9",
60
+ "@types/node": "^22.20.1",
61
+ "@vitest/coverage-v8": "^4.1.10",
62
62
  "tsup": "^8.0.0",
63
63
  "typescript": "^5.7.0",
64
- "vitest": "^4.1.9",
65
- "@claudinho/core": "0.9.1"
64
+ "vitest": "^4.1.10",
65
+ "@claudinho/core": "0.9.3"
66
66
  },
67
67
  "scripts": {
68
68
  "build": "tsup",