@claudinho/cli 0.4.2 → 0.4.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 +14 -16
  2. package/dist/index.js +149 -58
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -20,10 +20,11 @@ npx @claudinho/cli today
20
20
  ```bash
21
21
  claudinho today [date] # a day's fixtures in your timezone (default: today), live scores inline
22
22
  claudinho live # matches in play right now
23
- claudinho next <TEAM> # a team's next fixture + countdown (e.g. next MEX)
23
+ claudinho next [TEAM] # a team's next fixture + countdown (default: $CLAUDINHO_TEAM)
24
24
  claudinho table [GROUP] # group standings (default: all groups)
25
25
  claudinho match <id> # a single match's detail
26
26
  claudinho markets [target] # prediction-market signals: today | <date> | <id> | next <TEAM>
27
+ # (next prefers the team's IN-PLAY match while one is live)
27
28
  claudinho share [target] # copy-pasteable match snippet: today | live | <date> | <id> | next <TEAM>
28
29
  claudinho prompt # one compact status line (for statusline/tmux/Starship)
29
30
  claudinho init-statusline # wire it into the Claude Code statusline
@@ -77,7 +78,7 @@ market-implied percentages — for a date, a match, or a team's next fixture:
77
78
  claudinho markets # today's signals
78
79
  claudinho markets 2026-06-11 # a specific date
79
80
  claudinho markets 760415 # one match by id
80
- claudinho markets next MEX # a team's next fixture
81
+ claudinho markets next MEX # a team's current-or-next fixture (in-play preferred)
81
82
  claudinho markets today --json # structured sidecar output
82
83
  ```
83
84
 
@@ -91,11 +92,12 @@ Opt out with `--no-markets` (per command) or `CLAUDINHO_MARKETS=off` (global). T
91
92
  statusline and hook **never** show market data — it stays off the hot path.
92
93
 
93
94
  > **How matches are matched:** event slugs are derived automatically from each
94
- > fixture (`fifwc-{home}-{away}-{date}`), so real odds appear for any match with a
95
+ > fixture (`fifwc-{home}-{away}-{date}`), so real signals appear for any match with a
95
96
  > live Polymarket market — no mapping needed (`mapping.2026.json` is for slug
96
97
  > *overrides* only). Matching fails closed, so an unmatched fixture simply shows
97
- > nothing. For an offline preview, set `CLAUDINHO_MARKETS_SOURCE=fake` to render
98
- > clearly-labeled synthetic **"demo data"** odds.
98
+ > nothing and finished matches never show one (market signals are pre-match
99
+ > and in-play reads). For an offline preview, set `CLAUDINHO_MARKETS_SOURCE=fake`
100
+ > to render clearly-labeled synthetic **"demo data"** signals.
99
101
 
100
102
  ## Shareable snippets
101
103
 
@@ -110,22 +112,18 @@ claudinho share 760415 # one match by id
110
112
  claudinho share next MEX --copy # …and copy it straight to the clipboard
111
113
  ```
112
114
 
113
- <!-- DEMO CARD: verbatim output of `claudinho share next MEX --tz America/Mexico_City`.
115
+ <!-- DEMO CARD: verbatim output of `claudinho share next USA --tz America/Los_Angeles`.
114
116
  REGENERATE immediately before merging — the market block is gate-conditional
115
117
  and the numbers drift. Never hand-edit. -->
116
118
  ```text
117
- Next up for Mexico
119
+ Next up for United States
118
120
 
119
- 🇲🇽 Mexico vs South Africa 🇿🇦
120
- Jun 11 · 13:00 America/Mexico_City
121
- Estadio Banorte, Mexico City, Mexico
122
-
123
- Prediction markets favor Mexico.
124
- Mexico 69% · Draw 20% · South Africa 10%
125
- Source: Polymarket · updated 08:15 UTC · informational only
121
+ 🇺🇸 United States vs Paraguay 🇵🇾
122
+ Jun 12 · 18:00 America/Los_Angeles
123
+ SoFi Stadium, Inglewood, California, USA
126
124
 
127
125
  #VibingLaVidaLoca · Independent fan project · not affiliated with FIFA or Anthropic.
128
- Try it: npx @claudinho/cli next MEX
126
+ Try it: npx @claudinho/cli next USA
129
127
  ```
130
128
 
131
129
  Snippets are **plain text** (no color codes — they paste cleanly everywhere) and
@@ -161,7 +159,7 @@ The statusline reads from a local micro-cache and **never blocks on the
161
159
  network** (<150ms). When several matches are live it shows them all inline:
162
160
  `⚽ 🇳🇴 1–1 🇫🇷 87' · 🇸🇳 1–2 🇮🇶 86'`. Customize via env:
163
161
 
164
- - `CLAUDINHO_TEAM=MEX` — show only your team's match
162
+ - `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
165
163
  - `CLAUDINHO_MAX=2` — cap how many live matches show inline (rest collapse to `+N`; default: all)
166
164
  - `CLAUDINHO_COMPACT=0` — show 3-letter codes alongside flags
167
165
 
package/dist/index.js CHANGED
@@ -2610,6 +2610,13 @@ function nextFixtureForTeam(code, opts = {}) {
2610
2610
  (m) => new Date(m.kickoff).getTime() >= from.getTime()
2611
2611
  );
2612
2612
  }
2613
+ var LIVE_WINDOW_MS = 140 * 6e4;
2614
+ function fixturesInLiveWindow(now = Date.now(), fixtures = SCHEDULE) {
2615
+ return fixtures.filter((m) => {
2616
+ const k = Date.parse(m.kickoff);
2617
+ return now >= k && now <= k + LIVE_WINDOW_MS;
2618
+ }).sort(byKickoff);
2619
+ }
2613
2620
  function groups(fixtures = SCHEDULE) {
2614
2621
  const set = /* @__PURE__ */ new Set();
2615
2622
  for (const m of fixtures) if (m.group) set.add(m.group);
@@ -2867,6 +2874,33 @@ function shiftUtcDate(dateISO, days) {
2867
2874
  const [y, m, d] = dateISO.slice(0, 10).split("-").map(Number);
2868
2875
  return new Date(Date.UTC(y ?? 1970, (m ?? 1) - 1, (d ?? 1) + days)).toISOString().slice(0, 10);
2869
2876
  }
2877
+ var EXTRA_TIME_SLACK_MS = 60 * 6e4;
2878
+ async function marketFixtureForTeam(adapter, code, now = /* @__PURE__ */ new Date()) {
2879
+ const nowMs = now.getTime();
2880
+ const candidate = fixturesByTeam(code).find((m) => {
2881
+ const k = Date.parse(m.kickoff);
2882
+ return nowMs >= k && nowMs <= k + LIVE_WINDOW_MS + EXTRA_TIME_SLACK_MS;
2883
+ });
2884
+ if (candidate) {
2885
+ const r = await getMatchById(adapter, candidate.id);
2886
+ const m = r.match ?? candidate;
2887
+ if (!isFinished(m.status)) return { ...r, match: m };
2888
+ }
2889
+ const next = nextFixtureForTeam(code, { from: now });
2890
+ return { match: next, degraded: false };
2891
+ }
2892
+ async function getMatchById(adapter, id) {
2893
+ const base = allFixtures().find((m) => m.id === id);
2894
+ if (!base) return { match: void 0, degraded: false };
2895
+ const day = base.kickoff.slice(0, 10);
2896
+ try {
2897
+ const live = adapter.fetchWindow ? await adapter.fetchWindow(shiftUtcDate(day, -1), shiftUtcDate(day, 1)) : await adapter.fetchByDate(day);
2898
+ const hit = live.find((m) => m.id === id);
2899
+ return { match: hit ?? base, degraded: false, source: hit ? adapter.name : void 0 };
2900
+ } catch {
2901
+ return { match: base, degraded: true };
2902
+ }
2903
+ }
2870
2904
  async function getLiveMatches(adapter) {
2871
2905
  try {
2872
2906
  return { matches: await adapter.fetchLive(), degraded: false, source: adapter.name };
@@ -2875,6 +2909,12 @@ async function getLiveMatches(adapter) {
2875
2909
  }
2876
2910
  }
2877
2911
  var DEFAULT_MAX_AGE_MS = 15 * 6e4;
2912
+ function marketRelevant(match, now = /* @__PURE__ */ new Date()) {
2913
+ if (isLive(match.status)) return true;
2914
+ if (isFinished(match.status)) return false;
2915
+ const k = Date.parse(match.kickoff);
2916
+ return !Number.isFinite(k) || now.getTime() <= k + LIVE_WINDOW_MS;
2917
+ }
2878
2918
  function normalizeOutcomes(outcomes) {
2879
2919
  const sum = outcomes.reduce(
2880
2920
  (s, o) => s + (Number.isFinite(o.probability) && o.probability > 0 ? o.probability : 0),
@@ -3812,15 +3852,10 @@ function releaseLock() {
3812
3852
  }
3813
3853
 
3814
3854
  // src/statusline.ts
3815
- var LIVE_WINDOW_MS = 140 * 6e4;
3816
3855
  var DISPLAY_STALE_MS = 5 * 6e4;
3817
3856
  var LIVE_TTL_MS = 15e3;
3818
3857
  function inLiveWindow(now = Date.now(), fixtures = allFixtures()) {
3819
- for (const m of fixtures) {
3820
- const k = Date.parse(m.kickoff);
3821
- if (now >= k && now <= k + LIVE_WINDOW_MS) return true;
3822
- }
3823
- return false;
3858
+ return fixturesInLiveWindow(now, fixtures).length > 0;
3824
3859
  }
3825
3860
  function nextOverall(now, fixtures = allFixtures()) {
3826
3861
  return [...fixtures].sort(byKickoff).find((m) => Date.parse(m.kickoff) >= now);
@@ -3855,6 +3890,17 @@ function renderPrompt(state, opts = {}) {
3855
3890
  if (overflow > 0) line2 += ` +${overflow}`;
3856
3891
  return line2;
3857
3892
  }
3893
+ const cacheFresh = !!state && state.degraded !== true && ageMs(state, nowMs) < DISPLAY_STALE_MS;
3894
+ if (!cacheFresh) {
3895
+ const win = fixturesInLiveWindow(nowMs).filter(
3896
+ (m) => !team || m.home.code === team || m.away.code === team
3897
+ );
3898
+ const first = win[0];
3899
+ if (first) {
3900
+ const more = win.length - 1;
3901
+ return `\u26BD ${first.home.flag} vs ${first.away.flag} live \xB7 syncing\u2026` + (more > 0 ? ` +${more}` : "");
3902
+ }
3903
+ }
3858
3904
  const next = team ? nextFixtureForTeam(team, { from: now }) : nextOverall(nowMs);
3859
3905
  if (next) {
3860
3906
  return `${next.home.flag} vs ${next.away.flag} in ${countdown(next.kickoff, now)}`;
@@ -4070,8 +4116,10 @@ async function marketSignalsFor(ctx, matches, opts = {}) {
4070
4116
  }
4071
4117
  async function reliableMarketSignals(ctx, matches) {
4072
4118
  if (ctx.cfg.markets === false) return /* @__PURE__ */ new Map();
4073
- const raw = await marketSignalsFor(ctx, matches, DEFAULT_ON_MARKET_OPTS);
4074
- const now = /* @__PURE__ */ new Date();
4119
+ const now = ctx.now ?? /* @__PURE__ */ new Date();
4120
+ const relevant = matches.filter((m) => marketRelevant(m, now));
4121
+ if (relevant.length === 0) return /* @__PURE__ */ new Map();
4122
+ const raw = await marketSignalsFor(ctx, relevant, DEFAULT_ON_MARKET_OPTS);
4075
4123
  const out2 = /* @__PURE__ */ new Map();
4076
4124
  for (const [id, s] of raw) if (isReliableMarketSignal(s, { now })) out2.set(id, s);
4077
4125
  return out2;
@@ -4094,10 +4142,22 @@ function precheck(cfg, t, date) {
4094
4142
  if (cfg.tz && !isValidTimeZone(cfg.tz)) {
4095
4143
  process.stderr.write(t("warn.tz", { tz: cfg.tz }) + "\n");
4096
4144
  }
4145
+ const competition = resolveCompetition();
4146
+ if (competition !== DEFAULT_COMPETITION) {
4147
+ process.stderr.write(
4148
+ `claudinho: CLAUDINHO_COMPETITION=${competition} \u2014 live data follows a different competition than the bundled 2026 schedule.
4149
+ `
4150
+ );
4151
+ }
4097
4152
  if (date !== void 0 && !isValidDate(date)) {
4098
4153
  throw new InputError(t("err.date", { date }));
4099
4154
  }
4100
4155
  }
4156
+ function resolveTeamArg(team, usage) {
4157
+ const code = team ?? process.env.CLAUDINHO_TEAM;
4158
+ if (!code) throw new InputError(usage);
4159
+ return code.toUpperCase();
4160
+ }
4101
4161
  async function cmdToday(date, ctx) {
4102
4162
  const { cfg, t } = ctx;
4103
4163
  precheck(cfg, t, date);
@@ -4160,7 +4220,7 @@ async function cmdLive(ctx) {
4160
4220
  }
4161
4221
  async function cmdNext(team, { cfg, t }) {
4162
4222
  precheck(cfg, t);
4163
- const code = team.toUpperCase();
4223
+ const code = resolveTeamArg(team, "Usage: claudinho next <team> (or set CLAUDINHO_TEAM)");
4164
4224
  const fixture = nextFixtureForTeam(code);
4165
4225
  if (cfg.json) {
4166
4226
  emitJson({ team: code, fixture: fixture ?? null });
@@ -4298,20 +4358,15 @@ function cmdInitHook(opts, { cfg }) {
4298
4358
  async function cmdMatch(id, ctx) {
4299
4359
  const { cfg, t } = ctx;
4300
4360
  precheck(cfg, t);
4301
- const adapter = adapterFor(ctx);
4302
- let match = allFixtures().find((m) => m.id === id);
4303
- let liveSource;
4304
- try {
4305
- if (match) {
4306
- const live = await adapter.fetchByDate(match.kickoff.slice(0, 10));
4307
- match = live.find((m) => m.id === id) ?? match;
4308
- liveSource = adapter.name;
4309
- }
4310
- } catch {
4311
- }
4361
+ const { match, degraded, source: liveSource } = await getMatchById(adapterFor(ctx), id);
4312
4362
  const marketSignal = match ? await reliableMarketSignalFor(ctx, match) : void 0;
4313
4363
  if (cfg.json) {
4314
- emitJson({ match: match ?? null, source: liveSource ?? null, marketSignal: marketSignal ?? null });
4364
+ emitJson({
4365
+ degraded,
4366
+ match: match ?? null,
4367
+ source: liveSource ?? null,
4368
+ marketSignal: marketSignal ?? null
4369
+ });
4315
4370
  return;
4316
4371
  }
4317
4372
  const c = painterFor(cfg);
@@ -4351,8 +4406,13 @@ var MARKET_INFO = "Prediction-market data is informational only.";
4351
4406
  function marketDisplayable(sig) {
4352
4407
  return !sig.ambiguous && sig.favorite != null && hasSaneDistribution(sig.outcomes);
4353
4408
  }
4354
- function marketHeaderLine(m) {
4355
- return `${m.home.flag} ${m.home.name} vs ${m.away.name} ${m.away.flag}`;
4409
+ function marketHeaderLine(m, cfg) {
4410
+ const when = formatDate(m.kickoff, { tz: cfg.tz, locale: cfg.lang });
4411
+ return `${m.home.flag} ${m.home.name} vs ${m.away.name} ${m.away.flag} \xB7 ${when}`;
4412
+ }
4413
+ function noSignalLine(m, now) {
4414
+ if (marketRelevant(m, now)) return "No market signal for this match.";
4415
+ return isFinished(m.status) ? "Match has finished \u2014 market signals are pre-match and in-play reads." : "Match appears to have finished \u2014 market signals are pre-match and in-play reads.";
4356
4416
  }
4357
4417
  function printMarketBlock(m, sig, c) {
4358
4418
  for (const line2 of marketBlock(sig, m)) out(" " + c.dim(line2));
@@ -4361,10 +4421,10 @@ async function cmdMarkets(target, team, ctx) {
4361
4421
  const { cfg, t } = ctx;
4362
4422
  if (target === "next") {
4363
4423
  precheck(cfg, t);
4364
- if (!team) throw new InputError("Usage: claudinho markets next <team>");
4365
- const code = team.toUpperCase();
4366
- const fixture = nextFixtureForTeam(code);
4367
- const sig = fixture ? (await marketSignalsFor(ctx, [fixture], MARKETS_CMD_OPTS)).get(fixture.id) : void 0;
4424
+ const code = resolveTeamArg(team, "Usage: claudinho markets next <team> (or set CLAUDINHO_TEAM)");
4425
+ const now2 = ctx.now ?? /* @__PURE__ */ new Date();
4426
+ const { match: fixture } = await marketFixtureForTeam(adapterFor(ctx), code, now2);
4427
+ const sig = fixture && marketRelevant(fixture, now2) ? (await marketSignalsFor(ctx, [fixture], MARKETS_CMD_OPTS)).get(fixture.id) : void 0;
4368
4428
  const shown = sig && marketDisplayable(sig) ? sig : void 0;
4369
4429
  if (cfg.json) {
4370
4430
  emitJson({
@@ -4380,10 +4440,10 @@ async function cmdMarkets(target, team, ctx) {
4380
4440
  if (!fixture) {
4381
4441
  out(c2.dim(" " + t("next.none", { team: code })));
4382
4442
  } else {
4383
- out(header(marketHeaderLine(fixture), c2));
4443
+ out(header(marketHeaderLine(fixture, cfg), c2));
4384
4444
  out();
4385
4445
  if (shown) printMarketBlock(fixture, shown, c2);
4386
- else out(c2.dim(" No market signal for this match."));
4446
+ else out(c2.dim(" " + noSignalLine(fixture, now2)));
4387
4447
  }
4388
4448
  out();
4389
4449
  out(disclaimer(t, c2));
@@ -4392,8 +4452,9 @@ async function cmdMarkets(target, team, ctx) {
4392
4452
  }
4393
4453
  if (target && target !== "today" && !isValidDate(target)) {
4394
4454
  precheck(cfg, t);
4395
- const match = allFixtures().find((m) => m.id === target);
4396
- const sig = match ? (await marketSignalsFor(ctx, [match], MARKETS_CMD_OPTS)).get(match.id) : void 0;
4455
+ const now2 = ctx.now ?? /* @__PURE__ */ new Date();
4456
+ const { match } = await getMatchById(adapterFor(ctx), target);
4457
+ const sig = match && marketRelevant(match, now2) ? (await marketSignalsFor(ctx, [match], MARKETS_CMD_OPTS)).get(match.id) : void 0;
4397
4458
  const shown = sig && marketDisplayable(sig) ? sig : void 0;
4398
4459
  if (cfg.json) {
4399
4460
  emitJson({ matchId: target, informationalOnly: true, signal: shown ?? null });
@@ -4404,10 +4465,10 @@ async function cmdMarkets(target, team, ctx) {
4404
4465
  if (!match) {
4405
4466
  out(c2.dim(" " + t("match.none", { id: target })));
4406
4467
  } else {
4407
- out(header(marketHeaderLine(match), c2));
4468
+ out(header(marketHeaderLine(match, cfg), c2));
4408
4469
  out();
4409
4470
  if (shown) printMarketBlock(match, shown, c2);
4410
- else out(c2.dim(" No market signal for this match."));
4471
+ else out(c2.dim(" " + noSignalLine(match, now2)));
4411
4472
  }
4412
4473
  out();
4413
4474
  out(disclaimer(t, c2));
@@ -4416,11 +4477,13 @@ async function cmdMarkets(target, team, ctx) {
4416
4477
  }
4417
4478
  const explicitDate = target && target !== "today" ? target : void 0;
4418
4479
  precheck(cfg, t, explicitDate);
4419
- const date = explicitDate ?? localDate((/* @__PURE__ */ new Date()).toISOString(), cfg.tz);
4480
+ const now = ctx.now ?? /* @__PURE__ */ new Date();
4481
+ const date = explicitDate ?? localDate(now.toISOString(), cfg.tz);
4420
4482
  const { matches } = await getMatchesForDate(adapterFor(ctx), date);
4421
4483
  const todays = fixturesByDate(date, matches, cfg.tz);
4422
- const signals = await marketSignalsFor(ctx, todays, MARKETS_CMD_OPTS);
4423
- const rows = todays.map((m) => ({ match: m, signal: signals.get(m.id) })).filter(
4484
+ const relevant = todays.filter((m) => marketRelevant(m, now));
4485
+ const signals = await marketSignalsFor(ctx, relevant, MARKETS_CMD_OPTS);
4486
+ const rows = relevant.map((m) => ({ match: m, signal: signals.get(m.id) })).filter(
4424
4487
  (r) => !!r.signal && marketDisplayable(r.signal)
4425
4488
  );
4426
4489
  if (cfg.json) {
@@ -4437,7 +4500,7 @@ async function cmdMarkets(target, team, ctx) {
4437
4500
  out(c.dim(` No market signals available for ${date}.`));
4438
4501
  } else {
4439
4502
  for (const { match, signal } of rows) {
4440
- out(" " + c.bold(marketHeaderLine(match)));
4503
+ out(" " + c.bold(marketHeaderLine(match, cfg)));
4441
4504
  printMarketBlock(match, signal, c);
4442
4505
  out();
4443
4506
  }
@@ -4513,8 +4576,7 @@ async function cmdShare(target, team, opts, ctx) {
4513
4576
  }
4514
4577
  if (target === "next") {
4515
4578
  precheck(cfg, t);
4516
- if (!team) throw new InputError("Usage: claudinho share next <team>");
4517
- const code = team.toUpperCase();
4579
+ const code = resolveTeamArg(team, "Usage: claudinho share next <team> (or set CLAUDINHO_TEAM)");
4518
4580
  const fixture = nextFixtureForTeam(code);
4519
4581
  const matches = fixture ? [fixture] : [];
4520
4582
  const signals2 = await reliableShareSignals(ctx, matches);
@@ -4542,17 +4604,7 @@ async function cmdShare(target, team, opts, ctx) {
4542
4604
  }
4543
4605
  if (target && target !== "today" && !isValidDate(target)) {
4544
4606
  precheck(cfg, t);
4545
- const adapter = adapterFor(ctx);
4546
- let match = allFixtures().find((m) => m.id === target);
4547
- let source2;
4548
- try {
4549
- if (match) {
4550
- const live = await adapter.fetchByDate(match.kickoff.slice(0, 10));
4551
- match = live.find((m) => m.id === target) ?? match;
4552
- source2 = adapter.name;
4553
- }
4554
- } catch {
4555
- }
4607
+ const { match, source: source2 } = await getMatchById(adapterFor(ctx), target);
4556
4608
  const matches = match ? [match] : [];
4557
4609
  const signals2 = await reliableShareSignals(ctx, matches);
4558
4610
  emitShare(
@@ -4614,16 +4666,53 @@ var VIBES = [
4614
4666
  "Stoppage time and a clean stack trace.",
4615
4667
  "Coding into extra time."
4616
4668
  ];
4669
+ var VIBES_OPENER = [
4670
+ "Day one of the tournament. Save your work \u2014 it\u2019s about to get loud.",
4671
+ "Opening day: fresh bracket, fresh branch."
4672
+ ];
4673
+ var VIBES_FINAL = [
4674
+ "Final day. One last build, one last whistle.",
4675
+ "Ship it before the trophy does."
4676
+ ];
4677
+ function vibeLiveSegment(live, team) {
4678
+ const code = team?.toUpperCase();
4679
+ const pick2 = (code && live.find((m) => m.home.code === code || m.away.code === code)) ?? live[0];
4680
+ if (!pick2) return void 0;
4681
+ const minute = pick2.status === "HT" ? "HT" : pick2.minute ? `${pick2.minute}'` : "LIVE";
4682
+ return `${pick2.home.flag} ${scoreline(pick2)} ${pick2.away.flag} ${minute}`;
4683
+ }
4684
+ function vibePool(todayLocal, fixtures = allFixtures()) {
4685
+ let first;
4686
+ let last;
4687
+ for (const m of fixtures) {
4688
+ const d = m.kickoff.slice(0, 10);
4689
+ if (!first || d < first) first = d;
4690
+ if (!last || d > last) last = d;
4691
+ }
4692
+ if (todayLocal === first) return [...VIBES, ...VIBES_OPENER];
4693
+ if (todayLocal === last) return [...VIBES, ...VIBES_FINAL];
4694
+ return VIBES;
4695
+ }
4617
4696
  function cmdVibe(ctx) {
4618
4697
  const { cfg } = ctx;
4619
- const line2 = VIBES[Math.floor(Math.random() * VIBES.length)];
4698
+ const pool = vibePool(localDate((ctx.now ?? /* @__PURE__ */ new Date()).toISOString(), cfg.tz));
4699
+ const line2 = pool[Math.floor(Math.random() * pool.length)];
4700
+ let liveSeg;
4701
+ try {
4702
+ const state = readCurrentState(cfg.source, resolveCompetition());
4703
+ liveSeg = vibeLiveSegment(
4704
+ liveMatchesFromCache(state, (ctx.now ?? /* @__PURE__ */ new Date()).getTime()),
4705
+ process.env.CLAUDINHO_TEAM
4706
+ );
4707
+ } catch {
4708
+ }
4620
4709
  if (cfg.json) {
4621
- emitJson({ vibe: line2, tag: "#VibingLaVidaLoca" });
4710
+ emitJson({ vibe: line2, tag: "#VibingLaVidaLoca", ...liveSeg ? { live: liveSeg } : {} });
4622
4711
  return;
4623
4712
  }
4624
4713
  const c = painterFor(cfg);
4625
4714
  out();
4626
- out(" \u26BD " + c.bold(line2));
4715
+ out(" \u26BD " + (liveSeg ? `${liveSeg} \u2014 ` : "") + c.bold(line2 ?? ""));
4627
4716
  out(" " + c.cyan("#VibingLaVidaLoca"));
4628
4717
  out();
4629
4718
  }
@@ -4637,7 +4726,7 @@ function handlePipeError(stream) {
4637
4726
  }
4638
4727
  handlePipeError(process.stdout);
4639
4728
  handlePipeError(process.stderr);
4640
- var VERSION = "0.4.2";
4729
+ var VERSION = "0.4.3";
4641
4730
  var DISCLAIMER = "Claudinho is an independent fan project. Not affiliated with or endorsed by FIFA or Anthropic.";
4642
4731
  function ctxFrom(cmd) {
4643
4732
  const root = cmd.parent ?? cmd;
@@ -4652,7 +4741,9 @@ function fail(err) {
4652
4741
  process.exit(1);
4653
4742
  }
4654
4743
  var program = new Command();
4655
- program.name("claudinho").description("The 2026 football tournament in your terminal.\n" + DISCLAIMER).version(VERSION, "-v, --version").option("--lang <code>", "language: en, es, pt, fr").option("--tz <zone>", "IANA timezone, e.g. America/Mexico_City").option("--json", "output JSON (for scripting)").option("--no-color", "disable ANSI colors").option("--source <name>", "live data provider (advanced)").option("--flavor <level>", "commentary flair: off, subtle, full (default: full)").option("--no-markets", "hide prediction-market signals (informational odds)");
4744
+ program.name("claudinho").description(
4745
+ "The 2026 men\u2019s football tournament in your terminal, your Claude Code statusline, and any MCP client.\n" + DISCLAIMER
4746
+ ).version(VERSION, "-v, --version").option("--lang <code>", "language: en, es, pt, fr").option("--tz <zone>", "IANA timezone, e.g. America/Mexico_City").option("--json", "output JSON (for scripting)").option("--no-color", "disable ANSI colors").option("--source <name>", "live data provider (advanced)").option("--flavor <level>", "commentary flair: off, subtle, full (default: full)").option("--no-markets", "hide prediction-market signals (informational only)");
4656
4747
  program.addHelpText("after", "\n#VibingLaVidaLoca \u26BD");
4657
4748
  program.command("today").description("show a day's fixtures (default: today)").argument("[date]", "date as YYYY-MM-DD").action(async (date, _opts, cmd) => {
4658
4749
  try {
@@ -4668,7 +4759,7 @@ program.command("live").description("show matches in play right now").action(asy
4668
4759
  fail(e);
4669
4760
  }
4670
4761
  });
4671
- program.command("next").description("show a team's next fixture").argument("<team>", "team code, e.g. MEX").action(async (team, _opts, cmd) => {
4762
+ program.command("next").description("show a team's next fixture").argument("[team]", "team code, e.g. MEX (default: $CLAUDINHO_TEAM)").action(async (team, _opts, cmd) => {
4672
4763
  try {
4673
4764
  await cmdNext(team, ctxFrom(cmd));
4674
4765
  } catch (e) {
@@ -4689,14 +4780,14 @@ program.command("match").description("show a single match by id").argument("<id>
4689
4780
  fail(e);
4690
4781
  }
4691
4782
  });
4692
- program.command("markets").description("show prediction-market signals (read-only, informational only)").argument("[target]", 'date (YYYY-MM-DD), match id, "today", or "next"').argument("[team]", 'team code when target is "next" (e.g. MEX)').action(async (target, team, _opts, cmd) => {
4783
+ program.command("markets").description("show prediction-market signals (read-only, informational only)").argument("[target]", 'date (YYYY-MM-DD), match id, "today", or "next"').argument("[team]", 'team code when target is "next" (default: $CLAUDINHO_TEAM)').action(async (target, team, _opts, cmd) => {
4693
4784
  try {
4694
4785
  await cmdMarkets(target, team, ctxFrom(cmd));
4695
4786
  } catch (e) {
4696
4787
  fail(e);
4697
4788
  }
4698
4789
  });
4699
- program.command("share").description("print a shareable, copy-pasteable match snippet (#VibingLaVidaLoca)").argument("[target]", '"today" (default), "live", a date, a match id, or "next"').argument("[team]", 'team code when target is "next" (e.g. MEX)').option("--style <style>", "snippet style: social (default) or compact").option("--copy", "also copy the snippet to the clipboard (best-effort)").option("--no-hashtag", "omit the #VibingLaVidaLoca tag").option("--no-install-line", "omit the install/run cue").action(async (target, team, opts, cmd) => {
4790
+ program.command("share").description("print a shareable, copy-pasteable match snippet (#VibingLaVidaLoca)").argument("[target]", '"today" (default), "live", a date, a match id, or "next"').argument("[team]", 'team code when target is "next" (default: $CLAUDINHO_TEAM)').option("--style <style>", "snippet style: social (default) or compact").option("--copy", "also copy the snippet to the clipboard (best-effort)").option("--no-hashtag", "omit the #VibingLaVidaLoca tag").option("--no-install-line", "omit the install/run cue").action(async (target, team, opts, cmd) => {
4700
4791
  try {
4701
4792
  await cmdShare(target, team, opts, ctxFrom(cmd));
4702
4793
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claudinho/cli",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Live scores, fixtures, group tables, and read-only prediction-market signals for the 2026 men's football tournament — in your terminal and Claude Code statusline. No API keys. Not affiliated with FIFA or Anthropic.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -56,7 +56,7 @@
56
56
  "tsup": "^8.0.0",
57
57
  "typescript": "^5.7.0",
58
58
  "vitest": "^4.1.0",
59
- "@claudinho/core": "0.4.2"
59
+ "@claudinho/core": "0.4.3"
60
60
  },
61
61
  "scripts": {
62
62
  "build": "tsup",