@claudinho/cli 0.8.12 → 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.
- package/README.md +3 -0
- package/dist/index.js +107 -31
- 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
|
@@ -4918,6 +4918,37 @@ function writeMarketCache(source, competition, attempted, fetched, now = Date.no
|
|
|
4918
4918
|
}
|
|
4919
4919
|
}
|
|
4920
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
|
+
|
|
4921
4952
|
// src/clipboard.ts
|
|
4922
4953
|
import { spawnSync } from "child_process";
|
|
4923
4954
|
function clipboardTools(platform) {
|
|
@@ -4947,30 +4978,30 @@ function copyToClipboard(text, platform = process.platform) {
|
|
|
4947
4978
|
// src/cache.ts
|
|
4948
4979
|
import {
|
|
4949
4980
|
closeSync,
|
|
4950
|
-
mkdirSync as
|
|
4981
|
+
mkdirSync as mkdirSync3,
|
|
4951
4982
|
openSync,
|
|
4952
|
-
readFileSync as
|
|
4983
|
+
readFileSync as readFileSync3,
|
|
4953
4984
|
renameSync as renameSync2,
|
|
4954
4985
|
rmSync,
|
|
4955
4986
|
statSync,
|
|
4956
4987
|
writeSync
|
|
4957
4988
|
} from "fs";
|
|
4958
|
-
import { homedir as
|
|
4959
|
-
import { join as
|
|
4989
|
+
import { homedir as homedir3 } from "os";
|
|
4990
|
+
import { join as join3 } from "path";
|
|
4960
4991
|
var LOCK_STALE_MS = 6e4;
|
|
4961
4992
|
function cacheDir2() {
|
|
4962
|
-
const base = process.env.XDG_CACHE_HOME ||
|
|
4963
|
-
return
|
|
4993
|
+
const base = process.env.XDG_CACHE_HOME || join3(homedir3(), ".cache");
|
|
4994
|
+
return join3(base, "claudinho");
|
|
4964
4995
|
}
|
|
4965
4996
|
function cachePath2() {
|
|
4966
|
-
return
|
|
4997
|
+
return join3(cacheDir2(), "state.json");
|
|
4967
4998
|
}
|
|
4968
4999
|
function lockPath() {
|
|
4969
|
-
return
|
|
5000
|
+
return join3(cacheDir2(), "refresh.lock");
|
|
4970
5001
|
}
|
|
4971
5002
|
function readState() {
|
|
4972
5003
|
try {
|
|
4973
|
-
return JSON.parse(
|
|
5004
|
+
return JSON.parse(readFileSync3(cachePath2(), "utf8"));
|
|
4974
5005
|
} catch {
|
|
4975
5006
|
return void 0;
|
|
4976
5007
|
}
|
|
@@ -4981,8 +5012,8 @@ function readCurrentState(source, competition) {
|
|
|
4981
5012
|
}
|
|
4982
5013
|
function writeState(state) {
|
|
4983
5014
|
const dir = cacheDir2();
|
|
4984
|
-
|
|
4985
|
-
const tmp =
|
|
5015
|
+
mkdirSync3(dir, { recursive: true });
|
|
5016
|
+
const tmp = join3(dir, `state.${process.pid}.tmp`);
|
|
4986
5017
|
writeFileSync_(tmp, JSON.stringify(state));
|
|
4987
5018
|
renameSync2(tmp, cachePath2());
|
|
4988
5019
|
}
|
|
@@ -5007,7 +5038,7 @@ function fixturesAgeMs(state, now = Date.now()) {
|
|
|
5007
5038
|
function lockAgeMs(now = Date.now()) {
|
|
5008
5039
|
const lp = lockPath();
|
|
5009
5040
|
try {
|
|
5010
|
-
const contents =
|
|
5041
|
+
const contents = readFileSync3(lp, "utf8");
|
|
5011
5042
|
const written = Number.parseInt(contents.split(/\s+/)[1] ?? "", 10);
|
|
5012
5043
|
if (Number.isFinite(written)) return now - written;
|
|
5013
5044
|
} catch {
|
|
@@ -5023,7 +5054,7 @@ function isLockFresh(now = Date.now()) {
|
|
|
5023
5054
|
return lockAgeMs(now) < LOCK_STALE_MS;
|
|
5024
5055
|
}
|
|
5025
5056
|
function acquireLock(now = Date.now()) {
|
|
5026
|
-
|
|
5057
|
+
mkdirSync3(cacheDir2(), { recursive: true });
|
|
5027
5058
|
const lp = lockPath();
|
|
5028
5059
|
try {
|
|
5029
5060
|
const fd = openSync(lp, "wx");
|
|
@@ -5267,7 +5298,7 @@ function spawnRefresh(source) {
|
|
|
5267
5298
|
}
|
|
5268
5299
|
|
|
5269
5300
|
// src/cursorPayload.ts
|
|
5270
|
-
import { readFileSync as
|
|
5301
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
5271
5302
|
function parseCursorPayload(raw) {
|
|
5272
5303
|
try {
|
|
5273
5304
|
const trimmed = raw.trim();
|
|
@@ -5280,7 +5311,7 @@ function parseCursorPayload(raw) {
|
|
|
5280
5311
|
function readCursorPayload() {
|
|
5281
5312
|
try {
|
|
5282
5313
|
if (process.stdin.isTTY) return void 0;
|
|
5283
|
-
return parseCursorPayload(
|
|
5314
|
+
return parseCursorPayload(readFileSync4(0, "utf8"));
|
|
5284
5315
|
} catch {
|
|
5285
5316
|
return void 0;
|
|
5286
5317
|
}
|
|
@@ -5323,17 +5354,17 @@ ${meta}`;
|
|
|
5323
5354
|
import {
|
|
5324
5355
|
copyFileSync,
|
|
5325
5356
|
existsSync,
|
|
5326
|
-
mkdirSync as
|
|
5327
|
-
readFileSync as
|
|
5328
|
-
writeFileSync as
|
|
5357
|
+
mkdirSync as mkdirSync4,
|
|
5358
|
+
readFileSync as readFileSync5,
|
|
5359
|
+
writeFileSync as writeFileSync3
|
|
5329
5360
|
} from "fs";
|
|
5330
|
-
import { homedir as
|
|
5331
|
-
import { dirname, join as
|
|
5361
|
+
import { homedir as homedir4 } from "os";
|
|
5362
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
5332
5363
|
function claudeSettingsPath() {
|
|
5333
|
-
return
|
|
5364
|
+
return join4(homedir4(), ".claude", "settings.json");
|
|
5334
5365
|
}
|
|
5335
5366
|
function cursorCliConfigPath() {
|
|
5336
|
-
return
|
|
5367
|
+
return join4(homedir4(), ".cursor", "cli-config.json");
|
|
5337
5368
|
}
|
|
5338
5369
|
function isSameCommand(configured, requested) {
|
|
5339
5370
|
return configured === requested;
|
|
@@ -5364,7 +5395,7 @@ function restartMessage(target) {
|
|
|
5364
5395
|
function readSettings(path, snippet) {
|
|
5365
5396
|
if (!existsSync(path)) return {};
|
|
5366
5397
|
try {
|
|
5367
|
-
return JSON.parse(
|
|
5398
|
+
return JSON.parse(readFileSync5(path, "utf8"));
|
|
5368
5399
|
} catch {
|
|
5369
5400
|
return {
|
|
5370
5401
|
action: "manual",
|
|
@@ -5395,8 +5426,8 @@ function initStatuslineFor(target, opts = {}) {
|
|
|
5395
5426
|
}
|
|
5396
5427
|
backupOnce(path);
|
|
5397
5428
|
settings.statusLine = sl;
|
|
5398
|
-
|
|
5399
|
-
|
|
5429
|
+
mkdirSync4(dirname2(path), { recursive: true });
|
|
5430
|
+
writeFileSync3(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
5400
5431
|
const surface = target === "cursor" ? "Cursor CLI statusline" : "Statusline";
|
|
5401
5432
|
return {
|
|
5402
5433
|
action: "written",
|
|
@@ -5442,8 +5473,8 @@ function initHook(opts = {}) {
|
|
|
5442
5473
|
const hooks = settings.hooks;
|
|
5443
5474
|
hooks[CLAUDE_HOOK_EVENT] ??= [];
|
|
5444
5475
|
hooks[CLAUDE_HOOK_EVENT].push({ hooks: [{ type: "command", command }] });
|
|
5445
|
-
|
|
5446
|
-
|
|
5476
|
+
mkdirSync4(dirname2(path), { recursive: true });
|
|
5477
|
+
writeFileSync3(path, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
5447
5478
|
return {
|
|
5448
5479
|
action: "written",
|
|
5449
5480
|
path,
|
|
@@ -5568,6 +5599,7 @@ async function cmdToday(date, ctx) {
|
|
|
5568
5599
|
const src = dataSource(source, cfg.lang, c);
|
|
5569
5600
|
if (src) out(src);
|
|
5570
5601
|
out(disclaimer(t2, c));
|
|
5602
|
+
maybeStarNudge(ctx);
|
|
5571
5603
|
}
|
|
5572
5604
|
async function cmdLive(ctx) {
|
|
5573
5605
|
const { cfg, t: t2 } = ctx;
|
|
@@ -5594,6 +5626,7 @@ async function cmdLive(ctx) {
|
|
|
5594
5626
|
const src = dataSource(source, cfg.lang, c);
|
|
5595
5627
|
if (src) out(src);
|
|
5596
5628
|
out(disclaimer(t2, c));
|
|
5629
|
+
maybeStarNudge(ctx);
|
|
5597
5630
|
}
|
|
5598
5631
|
async function cmdNext(team, ctx) {
|
|
5599
5632
|
const { cfg, t: t2, now } = ctx;
|
|
@@ -5630,6 +5663,7 @@ async function cmdNext(team, ctx) {
|
|
|
5630
5663
|
const src = dataSource(source, cfg.lang, c);
|
|
5631
5664
|
if (src) out(src);
|
|
5632
5665
|
out(disclaimer(t2, c));
|
|
5666
|
+
maybeStarNudge(ctx);
|
|
5633
5667
|
}
|
|
5634
5668
|
async function cmdTable(group, ctx) {
|
|
5635
5669
|
const { cfg, t: t2 } = ctx;
|
|
@@ -5692,6 +5726,7 @@ async function cmdTable(group, ctx) {
|
|
|
5692
5726
|
const src = dataSource(source, cfg.lang, c);
|
|
5693
5727
|
if (src) out(src);
|
|
5694
5728
|
out(disclaimer(t2, c));
|
|
5729
|
+
maybeStarNudge(ctx);
|
|
5695
5730
|
}
|
|
5696
5731
|
var BRACKET_STAGES = /* @__PURE__ */ new Set(["R32", "R16", "QF", "SF", "3P", "F"]);
|
|
5697
5732
|
async function cmdBracket(stage, opts, ctx) {
|
|
@@ -5743,6 +5778,7 @@ async function cmdBracket(stage, opts, ctx) {
|
|
|
5743
5778
|
const src = dataSource(source, cfg.lang, c);
|
|
5744
5779
|
if (src) out(src);
|
|
5745
5780
|
out(disclaimer(t2, c));
|
|
5781
|
+
maybeStarNudge(ctx);
|
|
5746
5782
|
}
|
|
5747
5783
|
function cmdPrompt({ cfg }) {
|
|
5748
5784
|
try {
|
|
@@ -5788,13 +5824,19 @@ function printInitResult(res, cfg) {
|
|
|
5788
5824
|
out(`${mark} ${res.message}`);
|
|
5789
5825
|
}
|
|
5790
5826
|
function cmdInitStatusline(opts, { cfg }) {
|
|
5791
|
-
|
|
5827
|
+
const res = initStatusline({ print: opts.print, command: opts.command });
|
|
5828
|
+
printInitResult(res, cfg);
|
|
5829
|
+
if (!opts.print && res.action === "written") printInitStarCta(cfg);
|
|
5792
5830
|
}
|
|
5793
5831
|
function cmdInitHook(opts, { cfg }) {
|
|
5794
|
-
|
|
5832
|
+
const res = initHook({ print: opts.print, command: opts.command });
|
|
5833
|
+
printInitResult(res, cfg);
|
|
5834
|
+
if (!opts.print && res.action === "written") printInitStarCta(cfg);
|
|
5795
5835
|
}
|
|
5796
5836
|
function cmdInitCursorStatusline(opts, { cfg }) {
|
|
5797
|
-
|
|
5837
|
+
const res = initCursorStatusline({ print: opts.print, command: opts.command });
|
|
5838
|
+
printInitResult(res, cfg);
|
|
5839
|
+
if (!opts.print && res.action === "written") printInitStarCta(cfg);
|
|
5798
5840
|
}
|
|
5799
5841
|
var CURSOR_MCP_SNIPPET = `{
|
|
5800
5842
|
"mcpServers": {
|
|
@@ -5819,6 +5861,7 @@ function cmdInitCursor(opts, { cfg }) {
|
|
|
5819
5861
|
out("Tip: export CLAUDINHO_CURSOR_META=auto for a model + context line below the score.");
|
|
5820
5862
|
out("");
|
|
5821
5863
|
out("\u2192 Restart your agent session to see it.");
|
|
5864
|
+
printInitStarCta(cfg);
|
|
5822
5865
|
}
|
|
5823
5866
|
function cmdInitClaude(opts, { cfg }) {
|
|
5824
5867
|
if (opts.print) {
|
|
@@ -5839,6 +5882,7 @@ function cmdInitClaude(opts, { cfg }) {
|
|
|
5839
5882
|
out(` ${CLAUDE_MCP_ONELINER}`);
|
|
5840
5883
|
out("");
|
|
5841
5884
|
out("\u2192 Restart Claude Code to see it.");
|
|
5885
|
+
printInitStarCta(cfg);
|
|
5842
5886
|
}
|
|
5843
5887
|
async function cmdMatch(id, ctx) {
|
|
5844
5888
|
const { cfg, t: t2 } = ctx;
|
|
@@ -5887,6 +5931,7 @@ async function cmdMatch(id, ctx) {
|
|
|
5887
5931
|
const src = dataSource(liveSource, cfg.lang, c);
|
|
5888
5932
|
if (src) out(src);
|
|
5889
5933
|
out(disclaimer(t2, c));
|
|
5934
|
+
maybeStarNudge(ctx);
|
|
5890
5935
|
}
|
|
5891
5936
|
var MARKET_INFO = "Prediction-market data is informational only.";
|
|
5892
5937
|
function marketDisplayable(match, sig) {
|
|
@@ -6311,6 +6356,34 @@ function vibePool(todayLocal, fixtures = allFixtures()) {
|
|
|
6311
6356
|
if (todayLocal === last) return [...VIBES, ...VIBES_FINAL];
|
|
6312
6357
|
return VIBES;
|
|
6313
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
|
+
}
|
|
6314
6387
|
function cmdVibe(ctx) {
|
|
6315
6388
|
const { cfg } = ctx;
|
|
6316
6389
|
const pool = vibePool(localDate((ctx.now ?? /* @__PURE__ */ new Date()).toISOString(), cfg.tz));
|
|
@@ -6344,7 +6417,7 @@ function handlePipeError(stream) {
|
|
|
6344
6417
|
}
|
|
6345
6418
|
handlePipeError(process.stdout);
|
|
6346
6419
|
handlePipeError(process.stderr);
|
|
6347
|
-
var VERSION = "0.8.
|
|
6420
|
+
var VERSION = "0.8.13";
|
|
6348
6421
|
var DISCLAIMER = "Claudinho is an independent fan project. Not affiliated with or endorsed by FIFA or Anthropic.";
|
|
6349
6422
|
function ctxFrom(cmd) {
|
|
6350
6423
|
let root = cmd;
|
|
@@ -6468,6 +6541,9 @@ program.command("init-hook").description("wire the live-score hook into Claude C
|
|
|
6468
6541
|
program.command("vibe").description("print a matchday-coder one-liner (#VibingLaVidaLoca)").action((_opts, cmd) => {
|
|
6469
6542
|
cmdVibe(ctxFrom(cmd));
|
|
6470
6543
|
});
|
|
6544
|
+
program.command("star").description("how to support Claudinho \u2014 star the repo \u2B50").action((_opts, cmd) => {
|
|
6545
|
+
cmdStar(ctxFrom(cmd));
|
|
6546
|
+
});
|
|
6471
6547
|
var refreshCmd = new Command("_refresh").description("(internal) refresh the statusline cache").action(async (_opts, cmd) => {
|
|
6472
6548
|
try {
|
|
6473
6549
|
await cmdRefresh(ctxFrom(cmd));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claudinho/cli",
|
|
3
|
-
"version": "0.8.
|
|
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.
|
|
64
|
+
"@claudinho/core": "0.8.13"
|
|
65
65
|
},
|
|
66
66
|
"scripts": {
|
|
67
67
|
"build": "tsup",
|