@claudinho/cli 0.8.11 → 0.8.13

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 +3 -0
  2. package/dist/index.js +159 -58
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  **The 2026 men's football tournament, right in your terminal.** Live scores, fixtures, group tables, and market signals — TZ-aware, localized, scriptable. No API key, no signup.
4
4
 
5
+ > ⭐ Installing via `npx` or globally? **[Star the repo](https://github.com/arturogarrido/claudinho)** — a fan project runs on stars. (`claudinho star` shows you how anytime.)
6
+
5
7
  ## Install
6
8
 
7
9
  ```bash
@@ -48,6 +50,7 @@ claudinho init-cursor-statusline # (granular) wire just the Cursor CLI statusli
48
50
  claudinho hook # live-score context for a Claude Code hook (silent off-match)
49
51
  claudinho init-hook # (granular) make Claude itself score-aware (UserPromptSubmit)
50
52
  claudinho vibe # a matchday-coder one-liner (#VibingLaVidaLoca)
53
+ claudinho star # how to support the project (star the repo ⭐)
51
54
  ```
52
55
 
53
56
  ### Examples
package/dist/index.js CHANGED
@@ -496,6 +496,10 @@ function localDate(iso, tz) {
496
496
  timeZone: zone
497
497
  }).format(new Date(iso));
498
498
  }
499
+ function shiftUtcDate(dateISO, days) {
500
+ const [y, m, d] = dateISO.slice(0, 10).split("-").map(Number);
501
+ return new Date(Date.UTC(y ?? 1970, (m ?? 1) - 1, (d ?? 1) + days)).toISOString().slice(0, 10);
502
+ }
499
503
  function outcomeFromScore(home, away) {
500
504
  if (home > away) return "H";
501
505
  if (home < away) return "A";
@@ -3835,14 +3839,19 @@ async function getBracket(adapter, opts = {}) {
3835
3839
  source
3836
3840
  };
3837
3841
  }
3838
- function shiftUtcDate(dateISO, days) {
3839
- const [y, m, d] = dateISO.slice(0, 10).split("-").map(Number);
3840
- return new Date(Date.UTC(y ?? 1970, (m ?? 1) - 1, (d ?? 1) + days)).toISOString().slice(0, 10);
3841
- }
3842
3842
  var EXTRA_TIME_SLACK_MS = 60 * 6e4;
3843
3843
  async function marketFixtureForTeam(adapter, code, now = /* @__PURE__ */ new Date()) {
3844
3844
  const nowMs = now.getTime();
3845
- const candidate = fixturesByTeam(code).find((m) => {
3845
+ let fixtures = allFixtures();
3846
+ let overlayFailed = false;
3847
+ try {
3848
+ if (adapter.fetchWindow) {
3849
+ fixtures = mergeLive(fixtures, await adapter.fetchWindow(KNOCKOUT_WINDOW_START, KNOCKOUT_WINDOW_END));
3850
+ }
3851
+ } catch {
3852
+ overlayFailed = true;
3853
+ }
3854
+ const candidate = fixturesByTeam(code, fixtures).find((m) => {
3846
3855
  const k = Date.parse(m.kickoff);
3847
3856
  return nowMs >= k && nowMs <= k + LIVE_WINDOW_MS + EXTRA_TIME_SLACK_MS;
3848
3857
  });
@@ -3851,8 +3860,8 @@ async function marketFixtureForTeam(adapter, code, now = /* @__PURE__ */ new Dat
3851
3860
  const m = r.match ?? candidate;
3852
3861
  if (!isFinished(m.status)) return { ...r, match: m };
3853
3862
  }
3854
- const next = nextFixtureForTeam(code, { from: now });
3855
- return { match: next, degraded: false };
3863
+ const next = nextFixtureForTeam(code, { from: now, fixtures });
3864
+ return { match: next, degraded: overlayFailed };
3856
3865
  }
3857
3866
  async function getNextFixtureForTeam(adapter, code, now = /* @__PURE__ */ new Date()) {
3858
3867
  const base = allFixtures();
@@ -3959,6 +3968,9 @@ function mapsCleanly(match, outcomes) {
3959
3968
  if (match.stage === "GROUP" && !draw) return false;
3960
3969
  return true;
3961
3970
  }
3971
+ function marketSignalRendersFor(match, signal) {
3972
+ return signal.matchId === match.id && mapsCleanly(match, signal.outcomes);
3973
+ }
3962
3974
  function hasSaneDistribution(outcomes) {
3963
3975
  const priced = outcomes.filter((o) => Number.isFinite(o.probability) && o.probability > 0);
3964
3976
  if (priced.length < 2) return false;
@@ -4154,12 +4166,15 @@ var PolymarketProvider = class {
4154
4166
  */
4155
4167
  async resolveOne(match, options) {
4156
4168
  const entry = (this.opts.mapping ?? BUNDLED_MAPPING)[match.id];
4157
- const eventSlug = entry?.eventSlug ?? deriveEventSlug(match);
4158
- if (!eventSlug) return { checked: true };
4169
+ const slugs = entry?.eventSlug ? [entry.eventSlug] : deriveEventSlugs(match);
4170
+ if (slugs.length === 0) return { checked: true };
4159
4171
  try {
4160
- const event = await this.fetchEvent(eventSlug, options?.timeoutMs);
4161
- const signal = event ? this.toSignal(match, eventSlug, event, options) : void 0;
4162
- return { signal, checked: true };
4172
+ for (const slug of slugs) {
4173
+ const event = await this.fetchEvent(slug, options?.timeoutMs);
4174
+ const signal = event ? this.toSignal(match, slug, event, options) : void 0;
4175
+ if (signal) return { signal, checked: true };
4176
+ }
4177
+ return { checked: true };
4163
4178
  } catch {
4164
4179
  return { checked: false };
4165
4180
  }
@@ -4235,14 +4250,17 @@ var PolymarketProvider = class {
4235
4250
  return signal.ambiguous ? void 0 : signal;
4236
4251
  }
4237
4252
  };
4238
- function deriveEventSlug(match) {
4253
+ function deriveEventSlugs(match) {
4239
4254
  const home = match.home.code.toLowerCase();
4240
4255
  const away = match.away.code.toLowerCase();
4241
- if (home === away || home === "tbd" || away === "tbd") return void 0;
4242
- if (!/^[a-z]{3}$/.test(home) || !/^[a-z]{3}$/.test(away)) return void 0;
4243
- const date = match.kickoff.slice(0, 10);
4244
- if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) return void 0;
4245
- return `fifwc-${home}-${away}-${date}`;
4256
+ if (home === away || home === "tbd" || away === "tbd") return [];
4257
+ if (!/^[a-z]{3}$/.test(home) || !/^[a-z]{3}$/.test(away)) return [];
4258
+ const utcDate = match.kickoff.slice(0, 10);
4259
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(utcDate)) return [];
4260
+ return [
4261
+ `fifwc-${home}-${away}-${utcDate}`,
4262
+ `fifwc-${home}-${away}-${shiftUtcDate(utcDate, -1)}`
4263
+ ];
4246
4264
  }
4247
4265
  function slugToken(m) {
4248
4266
  return (m.slug ?? "").toLowerCase().split("-").pop() ?? "";
@@ -4900,6 +4918,37 @@ function writeMarketCache(source, competition, attempted, fetched, now = Date.no
4900
4918
  }
4901
4919
  }
4902
4920
 
4921
+ // src/starNudge.ts
4922
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
4923
+ import { homedir as homedir2 } from "os";
4924
+ import { dirname, join as join2 } from "path";
4925
+ var REPO_URL = "https://github.com/arturogarrido/claudinho";
4926
+ var NUDGE_EVERY = 5;
4927
+ function counterPath() {
4928
+ const base = process.env.XDG_CACHE_HOME || join2(homedir2(), ".cache");
4929
+ return join2(base, "claudinho", "runs.json");
4930
+ }
4931
+ function shouldNudge(runCount, every = NUDGE_EVERY) {
4932
+ return runCount > 0 && runCount % every === 0;
4933
+ }
4934
+ function bumpRunCount(path = counterPath()) {
4935
+ try {
4936
+ let count = 0;
4937
+ try {
4938
+ const raw = JSON.parse(readFileSync2(path, "utf8"));
4939
+ if (typeof raw.count === "number" && Number.isFinite(raw.count)) count = raw.count;
4940
+ } catch {
4941
+ count = 0;
4942
+ }
4943
+ count += 1;
4944
+ mkdirSync2(dirname(path), { recursive: true });
4945
+ writeFileSync2(path, JSON.stringify({ count }), "utf8");
4946
+ return count;
4947
+ } catch {
4948
+ return void 0;
4949
+ }
4950
+ }
4951
+
4903
4952
  // src/clipboard.ts
4904
4953
  import { spawnSync } from "child_process";
4905
4954
  function clipboardTools(platform) {
@@ -4929,30 +4978,30 @@ function copyToClipboard(text, platform = process.platform) {
4929
4978
  // src/cache.ts
4930
4979
  import {
4931
4980
  closeSync,
4932
- mkdirSync as mkdirSync2,
4981
+ mkdirSync as mkdirSync3,
4933
4982
  openSync,
4934
- readFileSync as readFileSync2,
4983
+ readFileSync as readFileSync3,
4935
4984
  renameSync as renameSync2,
4936
4985
  rmSync,
4937
4986
  statSync,
4938
4987
  writeSync
4939
4988
  } from "fs";
4940
- import { homedir as homedir2 } from "os";
4941
- import { join as join2 } from "path";
4989
+ import { homedir as homedir3 } from "os";
4990
+ import { join as join3 } from "path";
4942
4991
  var LOCK_STALE_MS = 6e4;
4943
4992
  function cacheDir2() {
4944
- const base = process.env.XDG_CACHE_HOME || join2(homedir2(), ".cache");
4945
- return join2(base, "claudinho");
4993
+ const base = process.env.XDG_CACHE_HOME || join3(homedir3(), ".cache");
4994
+ return join3(base, "claudinho");
4946
4995
  }
4947
4996
  function cachePath2() {
4948
- return join2(cacheDir2(), "state.json");
4997
+ return join3(cacheDir2(), "state.json");
4949
4998
  }
4950
4999
  function lockPath() {
4951
- return join2(cacheDir2(), "refresh.lock");
5000
+ return join3(cacheDir2(), "refresh.lock");
4952
5001
  }
4953
5002
  function readState() {
4954
5003
  try {
4955
- return JSON.parse(readFileSync2(cachePath2(), "utf8"));
5004
+ return JSON.parse(readFileSync3(cachePath2(), "utf8"));
4956
5005
  } catch {
4957
5006
  return void 0;
4958
5007
  }
@@ -4963,8 +5012,8 @@ function readCurrentState(source, competition) {
4963
5012
  }
4964
5013
  function writeState(state) {
4965
5014
  const dir = cacheDir2();
4966
- mkdirSync2(dir, { recursive: true });
4967
- const tmp = join2(dir, `state.${process.pid}.tmp`);
5015
+ mkdirSync3(dir, { recursive: true });
5016
+ const tmp = join3(dir, `state.${process.pid}.tmp`);
4968
5017
  writeFileSync_(tmp, JSON.stringify(state));
4969
5018
  renameSync2(tmp, cachePath2());
4970
5019
  }
@@ -4989,7 +5038,7 @@ function fixturesAgeMs(state, now = Date.now()) {
4989
5038
  function lockAgeMs(now = Date.now()) {
4990
5039
  const lp = lockPath();
4991
5040
  try {
4992
- const contents = readFileSync2(lp, "utf8");
5041
+ const contents = readFileSync3(lp, "utf8");
4993
5042
  const written = Number.parseInt(contents.split(/\s+/)[1] ?? "", 10);
4994
5043
  if (Number.isFinite(written)) return now - written;
4995
5044
  } catch {
@@ -5005,7 +5054,7 @@ function isLockFresh(now = Date.now()) {
5005
5054
  return lockAgeMs(now) < LOCK_STALE_MS;
5006
5055
  }
5007
5056
  function acquireLock(now = Date.now()) {
5008
- mkdirSync2(cacheDir2(), { recursive: true });
5057
+ mkdirSync3(cacheDir2(), { recursive: true });
5009
5058
  const lp = lockPath();
5010
5059
  try {
5011
5060
  const fd = openSync(lp, "wx");
@@ -5249,7 +5298,7 @@ function spawnRefresh(source) {
5249
5298
  }
5250
5299
 
5251
5300
  // src/cursorPayload.ts
5252
- import { readFileSync as readFileSync3 } from "fs";
5301
+ import { readFileSync as readFileSync4 } from "fs";
5253
5302
  function parseCursorPayload(raw) {
5254
5303
  try {
5255
5304
  const trimmed = raw.trim();
@@ -5262,7 +5311,7 @@ function parseCursorPayload(raw) {
5262
5311
  function readCursorPayload() {
5263
5312
  try {
5264
5313
  if (process.stdin.isTTY) return void 0;
5265
- return parseCursorPayload(readFileSync3(0, "utf8"));
5314
+ return parseCursorPayload(readFileSync4(0, "utf8"));
5266
5315
  } catch {
5267
5316
  return void 0;
5268
5317
  }
@@ -5305,17 +5354,17 @@ ${meta}`;
5305
5354
  import {
5306
5355
  copyFileSync,
5307
5356
  existsSync,
5308
- mkdirSync as mkdirSync3,
5309
- readFileSync as readFileSync4,
5310
- writeFileSync as writeFileSync2
5357
+ mkdirSync as mkdirSync4,
5358
+ readFileSync as readFileSync5,
5359
+ writeFileSync as writeFileSync3
5311
5360
  } from "fs";
5312
- import { homedir as homedir3 } from "os";
5313
- import { dirname, join as join3 } from "path";
5361
+ import { homedir as homedir4 } from "os";
5362
+ import { dirname as dirname2, join as join4 } from "path";
5314
5363
  function claudeSettingsPath() {
5315
- return join3(homedir3(), ".claude", "settings.json");
5364
+ return join4(homedir4(), ".claude", "settings.json");
5316
5365
  }
5317
5366
  function cursorCliConfigPath() {
5318
- return join3(homedir3(), ".cursor", "cli-config.json");
5367
+ return join4(homedir4(), ".cursor", "cli-config.json");
5319
5368
  }
5320
5369
  function isSameCommand(configured, requested) {
5321
5370
  return configured === requested;
@@ -5346,7 +5395,7 @@ function restartMessage(target) {
5346
5395
  function readSettings(path, snippet) {
5347
5396
  if (!existsSync(path)) return {};
5348
5397
  try {
5349
- return JSON.parse(readFileSync4(path, "utf8"));
5398
+ return JSON.parse(readFileSync5(path, "utf8"));
5350
5399
  } catch {
5351
5400
  return {
5352
5401
  action: "manual",
@@ -5377,8 +5426,8 @@ function initStatuslineFor(target, opts = {}) {
5377
5426
  }
5378
5427
  backupOnce(path);
5379
5428
  settings.statusLine = sl;
5380
- mkdirSync3(dirname(path), { recursive: true });
5381
- writeFileSync2(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
5429
+ mkdirSync4(dirname2(path), { recursive: true });
5430
+ writeFileSync3(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
5382
5431
  const surface = target === "cursor" ? "Cursor CLI statusline" : "Statusline";
5383
5432
  return {
5384
5433
  action: "written",
@@ -5424,8 +5473,8 @@ function initHook(opts = {}) {
5424
5473
  const hooks = settings.hooks;
5425
5474
  hooks[CLAUDE_HOOK_EVENT] ??= [];
5426
5475
  hooks[CLAUDE_HOOK_EVENT].push({ hooks: [{ type: "command", command }] });
5427
- mkdirSync3(dirname(path), { recursive: true });
5428
- writeFileSync2(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
5476
+ mkdirSync4(dirname2(path), { recursive: true });
5477
+ writeFileSync3(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
5429
5478
  return {
5430
5479
  action: "written",
5431
5480
  path,
@@ -5472,7 +5521,10 @@ async function reliableMarketSignals(ctx, matches) {
5472
5521
  if (relevant.length === 0) return /* @__PURE__ */ new Map();
5473
5522
  const raw = await marketSignalsFor(ctx, relevant, DEFAULT_ON_MARKET_OPTS);
5474
5523
  const out2 = /* @__PURE__ */ new Map();
5475
- for (const [id, s] of raw) if (isReliableMarketSignal(s, { now })) out2.set(id, s);
5524
+ for (const [id, s] of raw) {
5525
+ const m = relevant.find((x) => x.id === id);
5526
+ if (m && isReliableMarketSignal(s, { now }) && marketSignalRendersFor(m, s)) out2.set(id, s);
5527
+ }
5476
5528
  return out2;
5477
5529
  }
5478
5530
  async function reliableMarketSignalFor(ctx, match) {
@@ -5547,6 +5599,7 @@ async function cmdToday(date, ctx) {
5547
5599
  const src = dataSource(source, cfg.lang, c);
5548
5600
  if (src) out(src);
5549
5601
  out(disclaimer(t2, c));
5602
+ maybeStarNudge(ctx);
5550
5603
  }
5551
5604
  async function cmdLive(ctx) {
5552
5605
  const { cfg, t: t2 } = ctx;
@@ -5573,6 +5626,7 @@ async function cmdLive(ctx) {
5573
5626
  const src = dataSource(source, cfg.lang, c);
5574
5627
  if (src) out(src);
5575
5628
  out(disclaimer(t2, c));
5629
+ maybeStarNudge(ctx);
5576
5630
  }
5577
5631
  async function cmdNext(team, ctx) {
5578
5632
  const { cfg, t: t2, now } = ctx;
@@ -5609,6 +5663,7 @@ async function cmdNext(team, ctx) {
5609
5663
  const src = dataSource(source, cfg.lang, c);
5610
5664
  if (src) out(src);
5611
5665
  out(disclaimer(t2, c));
5666
+ maybeStarNudge(ctx);
5612
5667
  }
5613
5668
  async function cmdTable(group, ctx) {
5614
5669
  const { cfg, t: t2 } = ctx;
@@ -5671,6 +5726,7 @@ async function cmdTable(group, ctx) {
5671
5726
  const src = dataSource(source, cfg.lang, c);
5672
5727
  if (src) out(src);
5673
5728
  out(disclaimer(t2, c));
5729
+ maybeStarNudge(ctx);
5674
5730
  }
5675
5731
  var BRACKET_STAGES = /* @__PURE__ */ new Set(["R32", "R16", "QF", "SF", "3P", "F"]);
5676
5732
  async function cmdBracket(stage, opts, ctx) {
@@ -5722,6 +5778,7 @@ async function cmdBracket(stage, opts, ctx) {
5722
5778
  const src = dataSource(source, cfg.lang, c);
5723
5779
  if (src) out(src);
5724
5780
  out(disclaimer(t2, c));
5781
+ maybeStarNudge(ctx);
5725
5782
  }
5726
5783
  function cmdPrompt({ cfg }) {
5727
5784
  try {
@@ -5767,13 +5824,19 @@ function printInitResult(res, cfg) {
5767
5824
  out(`${mark} ${res.message}`);
5768
5825
  }
5769
5826
  function cmdInitStatusline(opts, { cfg }) {
5770
- printInitResult(initStatusline({ print: opts.print, command: opts.command }), cfg);
5827
+ const res = initStatusline({ print: opts.print, command: opts.command });
5828
+ printInitResult(res, cfg);
5829
+ if (!opts.print && res.action === "written") printInitStarCta(cfg);
5771
5830
  }
5772
5831
  function cmdInitHook(opts, { cfg }) {
5773
- printInitResult(initHook({ print: opts.print, command: opts.command }), cfg);
5832
+ const res = initHook({ print: opts.print, command: opts.command });
5833
+ printInitResult(res, cfg);
5834
+ if (!opts.print && res.action === "written") printInitStarCta(cfg);
5774
5835
  }
5775
5836
  function cmdInitCursorStatusline(opts, { cfg }) {
5776
- printInitResult(initCursorStatusline({ print: opts.print, command: opts.command }), cfg);
5837
+ const res = initCursorStatusline({ print: opts.print, command: opts.command });
5838
+ printInitResult(res, cfg);
5839
+ if (!opts.print && res.action === "written") printInitStarCta(cfg);
5777
5840
  }
5778
5841
  var CURSOR_MCP_SNIPPET = `{
5779
5842
  "mcpServers": {
@@ -5798,6 +5861,7 @@ function cmdInitCursor(opts, { cfg }) {
5798
5861
  out("Tip: export CLAUDINHO_CURSOR_META=auto for a model + context line below the score.");
5799
5862
  out("");
5800
5863
  out("\u2192 Restart your agent session to see it.");
5864
+ printInitStarCta(cfg);
5801
5865
  }
5802
5866
  function cmdInitClaude(opts, { cfg }) {
5803
5867
  if (opts.print) {
@@ -5818,6 +5882,7 @@ function cmdInitClaude(opts, { cfg }) {
5818
5882
  out(` ${CLAUDE_MCP_ONELINER}`);
5819
5883
  out("");
5820
5884
  out("\u2192 Restart Claude Code to see it.");
5885
+ printInitStarCta(cfg);
5821
5886
  }
5822
5887
  async function cmdMatch(id, ctx) {
5823
5888
  const { cfg, t: t2 } = ctx;
@@ -5866,10 +5931,11 @@ async function cmdMatch(id, ctx) {
5866
5931
  const src = dataSource(liveSource, cfg.lang, c);
5867
5932
  if (src) out(src);
5868
5933
  out(disclaimer(t2, c));
5934
+ maybeStarNudge(ctx);
5869
5935
  }
5870
5936
  var MARKET_INFO = "Prediction-market data is informational only.";
5871
- function marketDisplayable(sig) {
5872
- return !sig.ambiguous && sig.favorite != null && hasSaneDistribution(sig.outcomes);
5937
+ function marketDisplayable(match, sig) {
5938
+ return marketSignalRendersFor(match, sig) && !sig.ambiguous && sig.favorite != null && hasSaneDistribution(sig.outcomes);
5873
5939
  }
5874
5940
  function marketHeaderLine(m, cfg) {
5875
5941
  const when = formatDate(m.kickoff, { tz: cfg.tz, locale: cfg.lang });
@@ -5888,13 +5954,14 @@ async function cmdMarkets(target, team, ctx) {
5888
5954
  precheck(cfg, t2);
5889
5955
  const code = resolveTeamArg(team, "Usage: claudinho markets next <team> (or set CLAUDINHO_TEAM)");
5890
5956
  const now2 = ctx.now ?? /* @__PURE__ */ new Date();
5891
- const { match: fixture } = await marketFixtureForTeam(adapterFor(ctx), code, now2);
5957
+ const { match: fixture, degraded } = await marketFixtureForTeam(adapterFor(ctx), code, now2);
5892
5958
  const sig = fixture && marketRelevant(fixture, now2) ? (await marketSignalsFor(ctx, [fixture], MARKETS_CMD_OPTS)).get(fixture.id) : void 0;
5893
- const shown = sig && marketDisplayable(sig) ? sig : void 0;
5959
+ const shown = fixture && sig && marketDisplayable(fixture, sig) ? sig : void 0;
5894
5960
  if (cfg.json) {
5895
5961
  emitJson({
5896
5962
  team: code,
5897
5963
  matchId: fixture?.id ?? null,
5964
+ degraded,
5898
5965
  informationalOnly: true,
5899
5966
  signal: shown ?? null
5900
5967
  });
@@ -5903,7 +5970,7 @@ async function cmdMarkets(target, team, ctx) {
5903
5970
  const c2 = painterFor(cfg);
5904
5971
  out();
5905
5972
  if (!fixture) {
5906
- out(c2.dim(" " + t2("next.none", { team: code })));
5973
+ out(c2.dim(" " + (degraded ? t2("live.degraded") : t2("next.none", { team: code }))));
5907
5974
  } else {
5908
5975
  out(header(marketHeaderLine(fixture, cfg), c2));
5909
5976
  out();
@@ -5920,7 +5987,7 @@ async function cmdMarkets(target, team, ctx) {
5920
5987
  const now2 = ctx.now ?? /* @__PURE__ */ new Date();
5921
5988
  const { match } = await getMatchById(adapterFor(ctx), target);
5922
5989
  const sig = match && marketRelevant(match, now2) ? (await marketSignalsFor(ctx, [match], MARKETS_CMD_OPTS)).get(match.id) : void 0;
5923
- const shown = sig && marketDisplayable(sig) ? sig : void 0;
5990
+ const shown = match && sig && marketDisplayable(match, sig) ? sig : void 0;
5924
5991
  if (cfg.json) {
5925
5992
  emitJson({ matchId: target, informationalOnly: true, signal: shown ?? null });
5926
5993
  return;
@@ -5949,7 +6016,7 @@ async function cmdMarkets(target, team, ctx) {
5949
6016
  const relevant = todays.filter((m) => marketRelevant(m, now));
5950
6017
  const signals = await marketSignalsFor(ctx, relevant, MARKETS_CMD_OPTS);
5951
6018
  const rows = relevant.map((m) => ({ match: m, signal: signals.get(m.id) })).filter(
5952
- (r) => !!r.signal && marketDisplayable(r.signal)
6019
+ (r) => !!r.signal && marketDisplayable(r.match, r.signal)
5953
6020
  );
5954
6021
  if (cfg.json) {
5955
6022
  const marketSignals = {};
@@ -5979,7 +6046,10 @@ function pickShareStyle(v) {
5979
6046
  async function reliableShareSignals(ctx, matches) {
5980
6047
  const raw = await reliableMarketSignals(ctx, matches);
5981
6048
  const out2 = /* @__PURE__ */ new Map();
5982
- for (const [id, s] of raw) if (marketDisplayable(s)) out2.set(id, s);
6049
+ for (const [id, s] of raw) {
6050
+ const m = matches.find((x) => x.id === id);
6051
+ if (m && marketDisplayable(m, s)) out2.set(id, s);
6052
+ }
5983
6053
  return out2;
5984
6054
  }
5985
6055
  function emitShare(ctx, e, copy) {
@@ -6286,6 +6356,34 @@ function vibePool(todayLocal, fixtures = allFixtures()) {
6286
6356
  if (todayLocal === last) return [...VIBES, ...VIBES_FINAL];
6287
6357
  return VIBES;
6288
6358
  }
6359
+ function cmdStar(ctx) {
6360
+ const { cfg } = ctx;
6361
+ if (cfg.json) {
6362
+ emitJson({ repo: REPO_URL, hashtag: "#VibingLaVidaLoca" });
6363
+ return;
6364
+ }
6365
+ const c = painterFor(cfg);
6366
+ out();
6367
+ out(" " + c.bold("\u2B50 Star Claudinho on GitHub"));
6368
+ out(" " + c.cyan(REPO_URL));
6369
+ out();
6370
+ out(" " + c.dim("Built for devs & fans \xB7 #VibingLaVidaLoca \u26BD"));
6371
+ out(" " + c.dim("Live World Cup scores in your terminal, Claude Code & Cursor \u2014 no API keys."));
6372
+ out();
6373
+ }
6374
+ function maybeStarNudge(ctx) {
6375
+ if (ctx.cfg.json || !process.stdout.isTTY || process.env.CLAUDINHO_NO_STAR) return;
6376
+ const n = bumpRunCount();
6377
+ if (n === void 0 || !shouldNudge(n)) return;
6378
+ const c = painterFor(ctx.cfg);
6379
+ out();
6380
+ out(c.dim(` \u2B50 Enjoying Claudinho? Star it \u2192 ${REPO_URL} (claudinho star)`));
6381
+ }
6382
+ function printInitStarCta(cfg) {
6383
+ const c = painterFor(cfg);
6384
+ out("");
6385
+ out(c.dim(`\u2B50 If this keeps you in the flow during the match, star the repo \u2192 ${REPO_URL}`));
6386
+ }
6289
6387
  function cmdVibe(ctx) {
6290
6388
  const { cfg } = ctx;
6291
6389
  const pool = vibePool(localDate((ctx.now ?? /* @__PURE__ */ new Date()).toISOString(), cfg.tz));
@@ -6319,7 +6417,7 @@ function handlePipeError(stream) {
6319
6417
  }
6320
6418
  handlePipeError(process.stdout);
6321
6419
  handlePipeError(process.stderr);
6322
- var VERSION = "0.8.11";
6420
+ var VERSION = "0.8.13";
6323
6421
  var DISCLAIMER = "Claudinho is an independent fan project. Not affiliated with or endorsed by FIFA or Anthropic.";
6324
6422
  function ctxFrom(cmd) {
6325
6423
  let root = cmd;
@@ -6443,6 +6541,9 @@ program.command("init-hook").description("wire the live-score hook into Claude C
6443
6541
  program.command("vibe").description("print a matchday-coder one-liner (#VibingLaVidaLoca)").action((_opts, cmd) => {
6444
6542
  cmdVibe(ctxFrom(cmd));
6445
6543
  });
6544
+ program.command("star").description("how to support Claudinho \u2014 star the repo \u2B50").action((_opts, cmd) => {
6545
+ cmdStar(ctxFrom(cmd));
6546
+ });
6446
6547
  var refreshCmd = new Command("_refresh").description("(internal) refresh the statusline cache").action(async (_opts, cmd) => {
6447
6548
  try {
6448
6549
  await cmdRefresh(ctxFrom(cmd));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claudinho/cli",
3
- "version": "0.8.11",
3
+ "version": "0.8.13",
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.11"
64
+ "@claudinho/core": "0.8.13"
65
65
  },
66
66
  "scripts": {
67
67
  "build": "tsup",