@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.
- package/README.md +1 -1
- package/dist/index.js +132 -34
- 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 =
|
|
2934
|
-
|
|
2935
|
-
|
|
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.
|
|
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
|
|
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
|
|
5286
|
-
const
|
|
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
|
-
|
|
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(
|
|
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" ? `${
|
|
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
|
-
|
|
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
|
-
|
|
6027
|
-
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
64
|
+
"@claudinho/core": "0.8.19"
|
|
65
65
|
},
|
|
66
66
|
"scripts": {
|
|
67
67
|
"build": "tsup",
|