@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.
- package/README.md +12 -0
- package/dist/index.js +57 -7
- 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 +
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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, {
|
|
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.
|
|
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.
|
|
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.
|
|
61
|
-
"@vitest/coverage-v8": "^4.1.
|
|
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.
|
|
65
|
-
"@claudinho/core": "0.9.
|
|
64
|
+
"vitest": "^4.1.10",
|
|
65
|
+
"@claudinho/core": "0.9.3"
|
|
66
66
|
},
|
|
67
67
|
"scripts": {
|
|
68
68
|
"build": "tsup",
|