@claudinho/cli 0.6.0 → 0.6.2
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 +10 -5
- package/dist/index.js +89 -12
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -27,10 +27,12 @@ claudinho markets [target] # prediction-market signals: today | <date> | <id> |
|
|
|
27
27
|
# (next prefers the team's IN-PLAY match while one is live)
|
|
28
28
|
claudinho share [target] # copy-pasteable snippet: today | live | <date> | <id> | next <TEAM> | table <GROUP>
|
|
29
29
|
claudinho prompt # one compact status line (for statusline/tmux/Starship)
|
|
30
|
-
claudinho init-statusline
|
|
31
|
-
claudinho init
|
|
30
|
+
claudinho init cursor # one-step Cursor setup: statusline + MCP paste (--print for snippets)
|
|
31
|
+
claudinho init claude # one-step Claude Code setup: statusline + hook + MCP one-liner
|
|
32
|
+
claudinho init-statusline # (granular) wire just the Claude Code statusline
|
|
33
|
+
claudinho init-cursor-statusline # (granular) wire just the Cursor CLI statusline
|
|
32
34
|
claudinho hook # live-score context for a Claude Code hook (silent off-match)
|
|
33
|
-
claudinho init-hook # make Claude itself score-aware (UserPromptSubmit)
|
|
35
|
+
claudinho init-hook # (granular) make Claude itself score-aware (UserPromptSubmit)
|
|
34
36
|
claudinho vibe # a matchday-coder one-liner (#VibingLaVidaLoca)
|
|
35
37
|
```
|
|
36
38
|
|
|
@@ -164,6 +166,7 @@ network** (<150ms). When several matches are live it shows them all inline:
|
|
|
164
166
|
- `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
167
|
- `CLAUDINHO_MAX=2` — cap how many live matches show inline (rest collapse to `+N`; default: all)
|
|
166
168
|
- `CLAUDINHO_COMPACT=0` — show 3-letter codes alongside flags
|
|
169
|
+
- `CLAUDINHO_FLAGS=off` — drop emoji flags for 3-letter codes (statusline) / plain names (hook); already automatic on terminals that can't render flag emoji, e.g. Warp
|
|
167
170
|
|
|
168
171
|
Use the same `claudinho prompt` in **tmux** (`set -g status-right '#(claudinho prompt)'`)
|
|
169
172
|
or a **Starship** custom command — it works in any shell.
|
|
@@ -185,8 +188,10 @@ claudinho init-cursor-statusline # patches ~/.cursor/cli-config.json (b
|
|
|
185
188
|
claudinho init-cursor-statusline --print # just print the snippet
|
|
186
189
|
```
|
|
187
190
|
|
|
188
|
-
Uses the same `claudinho prompt` hot path as Claude Code
|
|
189
|
-
|
|
191
|
+
Uses the same `claudinho prompt` hot path as Claude Code — so the same
|
|
192
|
+
`CLAUDINHO_TEAM` / `CLAUDINHO_MAX` / `CLAUDINHO_COMPACT` customizations above apply
|
|
193
|
+
here too. Cursor-specific tuning is applied automatically (`updateIntervalMs: 1000`,
|
|
194
|
+
`timeoutMs: 1500`).
|
|
190
195
|
|
|
191
196
|
Optional second line with session meta (model, context %, worktree, vim mode) **below** the score:
|
|
192
197
|
|
package/dist/index.js
CHANGED
|
@@ -3979,6 +3979,13 @@ function releaseLock() {
|
|
|
3979
3979
|
|
|
3980
3980
|
// src/statusline.ts
|
|
3981
3981
|
var DISPLAY_STALE_MS = 5 * 6e4;
|
|
3982
|
+
var FLAGLESS_TERMINALS = /* @__PURE__ */ new Set(["WarpTerminal"]);
|
|
3983
|
+
function flagsEnabled(env = process.env) {
|
|
3984
|
+
const v = (env.CLAUDINHO_FLAGS ?? "").trim().toLowerCase();
|
|
3985
|
+
if (v === "off" || v === "0" || v === "no" || v === "false") return false;
|
|
3986
|
+
if (v === "on" || v === "1" || v === "yes" || v === "true") return true;
|
|
3987
|
+
return !FLAGLESS_TERMINALS.has(env.TERM_PROGRAM ?? "");
|
|
3988
|
+
}
|
|
3982
3989
|
var LIVE_TTL_MS = 15e3;
|
|
3983
3990
|
function inLiveWindow(now = Date.now(), fixtures = allFixtures()) {
|
|
3984
3991
|
return fixturesInLiveWindow(now, fixtures).length > 0;
|
|
@@ -3986,8 +3993,14 @@ function inLiveWindow(now = Date.now(), fixtures = allFixtures()) {
|
|
|
3986
3993
|
function nextOverall(now, fixtures = allFixtures()) {
|
|
3987
3994
|
return [...fixtures].sort(byKickoff).find((m) => Date.parse(m.kickoff) >= now);
|
|
3988
3995
|
}
|
|
3989
|
-
function
|
|
3996
|
+
function teamTok(t, flags) {
|
|
3997
|
+
return flags ? t.flag : t.code;
|
|
3998
|
+
}
|
|
3999
|
+
function matchSegment(m, compact, flags) {
|
|
3990
4000
|
const minute = m.status === "HT" ? "HT" : m.minute ? `${m.minute}'` : "LIVE";
|
|
4001
|
+
if (!flags) {
|
|
4002
|
+
return `${m.home.code} ${scoreline(m)} ${m.away.code} ${minute}`;
|
|
4003
|
+
}
|
|
3991
4004
|
const home = compact ? m.home.flag : `${m.home.flag} ${m.home.code}`;
|
|
3992
4005
|
const away = compact ? m.away.flag : `${m.away.code} ${m.away.flag}`;
|
|
3993
4006
|
return `${home} ${scoreline(m)} ${away} ${minute}`;
|
|
@@ -4003,15 +4016,16 @@ function renderPrompt(state, opts = {}) {
|
|
|
4003
4016
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
4004
4017
|
const nowMs = now.getTime();
|
|
4005
4018
|
const compact = opts.compact ?? true;
|
|
4019
|
+
const flags = opts.flags ?? true;
|
|
4006
4020
|
const team = opts.team?.toUpperCase();
|
|
4007
4021
|
const live = liveMatchesFromCache(state, nowMs);
|
|
4008
4022
|
if (team) {
|
|
4009
4023
|
const mine = live.find((m) => m.home?.code === team || m.away?.code === team);
|
|
4010
|
-
if (mine) return `\u26BD ${matchSegment(mine, compact)}`;
|
|
4024
|
+
if (mine) return `\u26BD ${matchSegment(mine, compact, flags)}`;
|
|
4011
4025
|
} else if (live.length > 0) {
|
|
4012
4026
|
const max = opts.max && opts.max > 0 ? opts.max : live.length;
|
|
4013
4027
|
const shown = live.slice(0, max);
|
|
4014
|
-
let line2 = "\u26BD " + shown.map((m) => matchSegment(m, compact)).join(" \xB7 ");
|
|
4028
|
+
let line2 = "\u26BD " + shown.map((m) => matchSegment(m, compact, flags)).join(" \xB7 ");
|
|
4015
4029
|
const overflow = live.length - shown.length;
|
|
4016
4030
|
if (overflow > 0) line2 += ` +${overflow}`;
|
|
4017
4031
|
return line2;
|
|
@@ -4024,24 +4038,27 @@ function renderPrompt(state, opts = {}) {
|
|
|
4024
4038
|
const first = win[0];
|
|
4025
4039
|
if (first) {
|
|
4026
4040
|
const more = win.length - 1;
|
|
4027
|
-
return `\u26BD ${first.home
|
|
4041
|
+
return `\u26BD ${teamTok(first.home, flags)} vs ${teamTok(first.away, flags)} live \xB7 syncing\u2026` + (more > 0 ? ` +${more}` : "");
|
|
4028
4042
|
}
|
|
4029
4043
|
}
|
|
4030
4044
|
const next = team ? nextFixtureForTeam(team, { from: now }) : nextOverall(nowMs);
|
|
4031
4045
|
if (next) {
|
|
4032
|
-
return `${next.home
|
|
4046
|
+
return `${teamTok(next.home, flags)} vs ${teamTok(next.away, flags)} in ${countdown(next.kickoff, now)}`;
|
|
4033
4047
|
}
|
|
4034
4048
|
return "\u26BD \u2014";
|
|
4035
4049
|
}
|
|
4036
4050
|
|
|
4037
4051
|
// src/hook.ts
|
|
4038
|
-
function line(m) {
|
|
4052
|
+
function line(m, flags) {
|
|
4039
4053
|
const minute = m.status === "HT" ? "half-time" : m.minute ? `${m.minute}'` : "live";
|
|
4040
|
-
|
|
4054
|
+
const home = flags ? `${m.home.flag} ${m.home.name}` : m.home.name;
|
|
4055
|
+
const away = flags ? `${m.away.name} ${m.away.flag}` : m.away.name;
|
|
4056
|
+
return `${home} ${scoreline(m)} ${away} (${minute})`;
|
|
4041
4057
|
}
|
|
4042
4058
|
function renderHook(state, opts = {}) {
|
|
4043
4059
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
4044
4060
|
const team = opts.team?.toUpperCase();
|
|
4061
|
+
const flags = opts.flags ?? true;
|
|
4045
4062
|
let live = liveMatchesFromCache(state, now.getTime());
|
|
4046
4063
|
if (live.length === 0) return "";
|
|
4047
4064
|
if (team) {
|
|
@@ -4051,7 +4068,7 @@ function renderHook(state, opts = {}) {
|
|
|
4051
4068
|
return aHas - bHas;
|
|
4052
4069
|
});
|
|
4053
4070
|
}
|
|
4054
|
-
const lines = live.map(line).join("\n");
|
|
4071
|
+
const lines = live.map((mm) => line(mm, flags)).join("\n");
|
|
4055
4072
|
return `[Claudinho \u2014 live football scores right now]
|
|
4056
4073
|
${lines}`;
|
|
4057
4074
|
}
|
|
@@ -4536,7 +4553,7 @@ function cmdPrompt({ cfg }) {
|
|
|
4536
4553
|
const maxRaw = Number.parseInt(process.env.CLAUDINHO_MAX ?? "", 10);
|
|
4537
4554
|
const max = Number.isFinite(maxRaw) && maxRaw > 0 ? maxRaw : void 0;
|
|
4538
4555
|
const state = readCurrentState(cfg.source, resolveCompetition());
|
|
4539
|
-
const scoreLine = renderPrompt(state, { team, compact, max });
|
|
4556
|
+
const scoreLine = renderPrompt(state, { team, compact, max, flags: flagsEnabled() });
|
|
4540
4557
|
out(renderPromptOutput(scoreLine, payload));
|
|
4541
4558
|
if (!state || shouldRefresh()) spawnRefresh(cfg.source);
|
|
4542
4559
|
} catch {
|
|
@@ -4547,7 +4564,7 @@ function cmdHook({ cfg }) {
|
|
|
4547
4564
|
try {
|
|
4548
4565
|
const team = process.env.CLAUDINHO_TEAM;
|
|
4549
4566
|
const state = readCurrentState(cfg.source, resolveCompetition());
|
|
4550
|
-
const ctx = renderHook(state, { team });
|
|
4567
|
+
const ctx = renderHook(state, { team, flags: flagsEnabled() });
|
|
4551
4568
|
if (ctx) out(ctx);
|
|
4552
4569
|
if (!state || shouldRefresh()) spawnRefresh(cfg.source);
|
|
4553
4570
|
} catch {
|
|
@@ -4574,6 +4591,50 @@ function cmdInitHook(opts, { cfg }) {
|
|
|
4574
4591
|
function cmdInitCursorStatusline(opts, { cfg }) {
|
|
4575
4592
|
printInitResult(initCursorStatusline({ print: opts.print, command: opts.command }), cfg);
|
|
4576
4593
|
}
|
|
4594
|
+
var CURSOR_MCP_SNIPPET = `{
|
|
4595
|
+
"mcpServers": {
|
|
4596
|
+
"claudinho": { "command": "npx", "args": ["-y", "@claudinho/mcp"] }
|
|
4597
|
+
}
|
|
4598
|
+
}`;
|
|
4599
|
+
var CLAUDE_MCP_ONELINER = "claude mcp add claudinho -- npx -y @claudinho/mcp";
|
|
4600
|
+
function cmdInitCursor(opts, { cfg }) {
|
|
4601
|
+
if (opts.print) {
|
|
4602
|
+
out("# 1) Cursor CLI statusline \u2192 ~/.cursor/cli-config.json");
|
|
4603
|
+
printInitResult(initCursorStatusline({ print: true }), cfg);
|
|
4604
|
+
out("");
|
|
4605
|
+
out("# 2) MCP tools (optional) \u2192 ~/.cursor/mcp.json (or project .cursor/mcp.json)");
|
|
4606
|
+
out(CURSOR_MCP_SNIPPET);
|
|
4607
|
+
return;
|
|
4608
|
+
}
|
|
4609
|
+
printInitResult(initCursorStatusline(), cfg);
|
|
4610
|
+
out("");
|
|
4611
|
+
out("Optional \u2014 live MCP tools in Cursor: add to ~/.cursor/mcp.json (or project .cursor/mcp.json):");
|
|
4612
|
+
out(CURSOR_MCP_SNIPPET);
|
|
4613
|
+
out("");
|
|
4614
|
+
out("Tip: export CLAUDINHO_CURSOR_META=auto for a model + context line below the score.");
|
|
4615
|
+
out("");
|
|
4616
|
+
out("\u2192 Restart your agent session to see it.");
|
|
4617
|
+
}
|
|
4618
|
+
function cmdInitClaude(opts, { cfg }) {
|
|
4619
|
+
if (opts.print) {
|
|
4620
|
+
out("# 1) Claude Code statusline \u2192 ~/.claude/settings.json");
|
|
4621
|
+
printInitResult(initStatusline({ print: true }), cfg);
|
|
4622
|
+
out("");
|
|
4623
|
+
out("# 2) Live-score hook \u2192 ~/.claude/settings.json");
|
|
4624
|
+
printInitResult(initHook({ print: true }), cfg);
|
|
4625
|
+
out("");
|
|
4626
|
+
out("# 3) MCP tools (run this):");
|
|
4627
|
+
out(CLAUDE_MCP_ONELINER);
|
|
4628
|
+
return;
|
|
4629
|
+
}
|
|
4630
|
+
printInitResult(initStatusline(), cfg);
|
|
4631
|
+
printInitResult(initHook(), cfg);
|
|
4632
|
+
out("");
|
|
4633
|
+
out("Next \u2014 add the MCP server:");
|
|
4634
|
+
out(` ${CLAUDE_MCP_ONELINER}`);
|
|
4635
|
+
out("");
|
|
4636
|
+
out("\u2192 Restart Claude Code to see it.");
|
|
4637
|
+
}
|
|
4577
4638
|
async function cmdMatch(id, ctx) {
|
|
4578
4639
|
const { cfg, t } = ctx;
|
|
4579
4640
|
precheck(cfg, t);
|
|
@@ -5003,10 +5064,11 @@ function handlePipeError(stream) {
|
|
|
5003
5064
|
}
|
|
5004
5065
|
handlePipeError(process.stdout);
|
|
5005
5066
|
handlePipeError(process.stderr);
|
|
5006
|
-
var VERSION = "0.6.
|
|
5067
|
+
var VERSION = "0.6.2";
|
|
5007
5068
|
var DISCLAIMER = "Claudinho is an independent fan project. Not affiliated with or endorsed by FIFA or Anthropic.";
|
|
5008
5069
|
function ctxFrom(cmd) {
|
|
5009
|
-
|
|
5070
|
+
let root = cmd;
|
|
5071
|
+
while (root.parent) root = root.parent;
|
|
5010
5072
|
const opts = root.opts();
|
|
5011
5073
|
const cfg = resolveConfig(opts);
|
|
5012
5074
|
return { cfg, t: makeT(cfg.lang) };
|
|
@@ -5077,6 +5139,21 @@ program.command("share").description("print a shareable, copy-pasteable match sn
|
|
|
5077
5139
|
program.command("prompt").description("print a status line (Claude Code / Cursor CLI statusline, tmux, Starship, \u2026)").action((_opts, cmd) => {
|
|
5078
5140
|
cmdPrompt(ctxFrom(cmd));
|
|
5079
5141
|
});
|
|
5142
|
+
var init = program.command("init").description("one-step setup for your agent: `init cursor` or `init claude`");
|
|
5143
|
+
init.command("cursor").description("set up claudinho for the Cursor CLI (statusline + MCP config)").option("--print", "print the config snippets for manual install instead of writing them").action((opts, cmd) => {
|
|
5144
|
+
try {
|
|
5145
|
+
cmdInitCursor(opts, ctxFrom(cmd));
|
|
5146
|
+
} catch (e) {
|
|
5147
|
+
fail(e);
|
|
5148
|
+
}
|
|
5149
|
+
});
|
|
5150
|
+
init.command("claude").description("set up claudinho for Claude Code (statusline + hook + MCP)").option("--print", "print the config snippets for manual install instead of writing them").action((opts, cmd) => {
|
|
5151
|
+
try {
|
|
5152
|
+
cmdInitClaude(opts, ctxFrom(cmd));
|
|
5153
|
+
} catch (e) {
|
|
5154
|
+
fail(e);
|
|
5155
|
+
}
|
|
5156
|
+
});
|
|
5080
5157
|
program.command("init-statusline").description("configure the Claude Code statusline to use claudinho").option("--print", "print the settings snippet instead of writing it").option("--command <cmd>", "command to run (default: claudinho prompt)").action((opts, cmd) => {
|
|
5081
5158
|
try {
|
|
5082
5159
|
cmdInitStatusline(opts, ctxFrom(cmd));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claudinho/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
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",
|
|
@@ -45,7 +45,11 @@
|
|
|
45
45
|
"claude-code",
|
|
46
46
|
"cursor-cli",
|
|
47
47
|
"2026",
|
|
48
|
-
"vibinglavidaloca"
|
|
48
|
+
"vibinglavidaloca",
|
|
49
|
+
"cursor",
|
|
50
|
+
"cursor-agent",
|
|
51
|
+
"mcp",
|
|
52
|
+
"agent"
|
|
49
53
|
],
|
|
50
54
|
"dependencies": {
|
|
51
55
|
"cli-table3": "^0.6.5",
|
|
@@ -57,7 +61,7 @@
|
|
|
57
61
|
"tsup": "^8.0.0",
|
|
58
62
|
"typescript": "^5.7.0",
|
|
59
63
|
"vitest": "^4.1.0",
|
|
60
|
-
"@claudinho/core": "0.6.
|
|
64
|
+
"@claudinho/core": "0.6.2"
|
|
61
65
|
},
|
|
62
66
|
"scripts": {
|
|
63
67
|
"build": "tsup",
|