@przeprogramowani/10x-cli 1.10.0 → 1.12.0
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 +17 -0
- package/dist/index.mjs +550 -124
- package/package.json +1 -1
- package/skills/10x-cli-guide/SKILL.md +17 -1
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ Once installed, just tell your agent to **set up 10x-cli** and it will pick up t
|
|
|
49
49
|
10x get m1l1 # Fetch and apply lesson artifacts
|
|
50
50
|
10x sync # Update everything you've downloaded; show what changed
|
|
51
51
|
10x doctor # Check everything is working
|
|
52
|
+
10x bench # Show the 10xBench top-10 model leaderboard
|
|
52
53
|
```
|
|
53
54
|
|
|
54
55
|
## Commands
|
|
@@ -60,6 +61,7 @@ Once installed, just tell your agent to **set up 10x-cli** and it will pick up t
|
|
|
60
61
|
| `10x get <ref>` | Fetch a lesson and apply artifacts to your workspace |
|
|
61
62
|
| `10x sync` | Bulk-download / refresh lessons and report what changed upstream |
|
|
62
63
|
| `10x doctor` | Diagnose auth, API connectivity, and local config |
|
|
64
|
+
| `10x bench` | Live top-10 AI model leaderboard from [10xbench.ai](https://10xbench.ai) — no login needed |
|
|
63
65
|
|
|
64
66
|
### `10x get` Flags
|
|
65
67
|
|
|
@@ -163,6 +165,21 @@ all. **Change visibility covers skills and prompts** — configs are create-only
|
|
|
163
165
|
conflict is reported, not a failure), `1` if any lesson failed to fetch. The full
|
|
164
166
|
report is still emitted on a partial failure.
|
|
165
167
|
|
|
168
|
+
### `10x bench`
|
|
169
|
+
|
|
170
|
+
Shows the [10xBench](https://10xbench.ai) leaderboard — the top 10 AI models
|
|
171
|
+
benchmarked on vibe-coding the Przeprogramowani.pl website — rendered as
|
|
172
|
+
color-coded score bars in your terminal. Public data, works without logging in.
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
10x bench # Top 10 models with scores, runs, and cost per run
|
|
176
|
+
10x bench --limit 5 # Just the top 5
|
|
177
|
+
10x bench --json # Machine-readable (also auto-selected when piped)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The data refreshes whenever new benchmark results are published on 10xbench.ai.
|
|
181
|
+
Colors honor `NO_COLOR` and are disabled automatically when output is piped.
|
|
182
|
+
|
|
166
183
|
### Global Flags
|
|
167
184
|
|
|
168
185
|
- `--json` — Machine-readable JSON output (auto-detected when piped)
|
package/dist/index.mjs
CHANGED
|
@@ -2271,7 +2271,7 @@ var cac = (name = "") => new CAC(name);
|
|
|
2271
2271
|
// package.json
|
|
2272
2272
|
var package_default = {
|
|
2273
2273
|
name: "@przeprogramowani/10x-cli",
|
|
2274
|
-
version: "1.
|
|
2274
|
+
version: "1.12.0",
|
|
2275
2275
|
description: "Open-source CLI for 10xDevs course content",
|
|
2276
2276
|
repository: {
|
|
2277
2277
|
type: "git",
|
|
@@ -3262,6 +3262,10 @@ function verbose(ctx, message) {
|
|
|
3262
3262
|
process.stderr.write(`[verbose] ${message}
|
|
3263
3263
|
`);
|
|
3264
3264
|
}
|
|
3265
|
+
function exitNotImplemented(command, phase, flags = {}) {
|
|
3266
|
+
const ctx = resolveContext(flags);
|
|
3267
|
+
outputError(ctx, "not_implemented", `'10x ${command}' lands in ${phase}.`, ExitCodes.ERROR, "See thoughts/shared/plans/2026-04-07-10x-cli-design.md for the full roadmap.");
|
|
3268
|
+
}
|
|
3265
3269
|
|
|
3266
3270
|
// src/lib/auth-guard.ts
|
|
3267
3271
|
var DEFAULT_REFRESH_WINDOW_MS = 5 * 60 * 1000;
|
|
@@ -3521,9 +3525,451 @@ function handleLoginError(ctx, status, code, error) {
|
|
|
3521
3525
|
outputError(ctx, code || "auth_error", "Authentication failed.", ExitCodes.ERROR, error ? `Server said: ${error}` : undefined);
|
|
3522
3526
|
}
|
|
3523
3527
|
|
|
3528
|
+
// src/lib/bench-client.ts
|
|
3529
|
+
var DEFAULT_BENCH_BASE = "https://10xbench.ai";
|
|
3530
|
+
var PROD_HOSTNAME2 = "10xbench.ai";
|
|
3531
|
+
var DEV_HOSTNAMES2 = new Set(["localhost", "127.0.0.1"]);
|
|
3532
|
+
function resolveBenchBase() {
|
|
3533
|
+
const override = process.env["BENCH_BASE_URL"];
|
|
3534
|
+
if (!override)
|
|
3535
|
+
return DEFAULT_BENCH_BASE;
|
|
3536
|
+
let url;
|
|
3537
|
+
try {
|
|
3538
|
+
url = new URL(override);
|
|
3539
|
+
} catch {
|
|
3540
|
+
throw new Error(`BENCH_BASE_URL is not a valid URL: ${JSON.stringify(override)}`);
|
|
3541
|
+
}
|
|
3542
|
+
if (url.pathname !== "/" && url.pathname !== "") {
|
|
3543
|
+
throw new Error(`BENCH_BASE_URL must not include a path (got ${JSON.stringify(url.pathname)})`);
|
|
3544
|
+
}
|
|
3545
|
+
if (url.search !== "" || url.hash !== "") {
|
|
3546
|
+
throw new Error("BENCH_BASE_URL must not include a query string or fragment");
|
|
3547
|
+
}
|
|
3548
|
+
if (url.protocol === "https:" && url.hostname === PROD_HOSTNAME2) {
|
|
3549
|
+
return `${url.protocol}//${url.host}`;
|
|
3550
|
+
}
|
|
3551
|
+
if (url.protocol === "http:" && DEV_HOSTNAMES2.has(url.hostname)) {
|
|
3552
|
+
return `${url.protocol}//${url.host}`;
|
|
3553
|
+
}
|
|
3554
|
+
throw new Error(`BENCH_BASE_URL must be ${DEFAULT_BENCH_BASE} or http://localhost[:port] (got ${JSON.stringify(override)})`);
|
|
3555
|
+
}
|
|
3556
|
+
var SUPPORTED_LEADERBOARD_SCHEMA = 1;
|
|
3557
|
+
var LEADERBOARD_PATH = "/api/leaderboard.json";
|
|
3558
|
+
var DEFAULT_TIMEOUT_MS3 = 30000;
|
|
3559
|
+
function isLeaderboardResponse(value) {
|
|
3560
|
+
if (value === null || typeof value !== "object")
|
|
3561
|
+
return false;
|
|
3562
|
+
const v2 = value;
|
|
3563
|
+
return typeof v2.schemaVersion === "number" && typeof v2.generatedAt === "string" && typeof v2.totalModels === "number" && Array.isArray(v2.leaderboard);
|
|
3564
|
+
}
|
|
3565
|
+
async function fetchLeaderboard(options = {}) {
|
|
3566
|
+
const url = `${resolveBenchBase()}${LEADERBOARD_PATH}`;
|
|
3567
|
+
let response;
|
|
3568
|
+
try {
|
|
3569
|
+
response = await fetch(url, {
|
|
3570
|
+
method: "GET",
|
|
3571
|
+
headers: {
|
|
3572
|
+
accept: "application/json",
|
|
3573
|
+
"user-agent": "10x-cli"
|
|
3574
|
+
},
|
|
3575
|
+
signal: options.signal ?? AbortSignal.timeout(DEFAULT_TIMEOUT_MS3)
|
|
3576
|
+
});
|
|
3577
|
+
} catch (err) {
|
|
3578
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3579
|
+
return { ok: false, status: 0, code: "network_error", error: message };
|
|
3580
|
+
}
|
|
3581
|
+
const text = await response.text();
|
|
3582
|
+
if (!response.ok) {
|
|
3583
|
+
return {
|
|
3584
|
+
ok: false,
|
|
3585
|
+
status: response.status,
|
|
3586
|
+
code: `http_${response.status}`,
|
|
3587
|
+
error: response.statusText || "Leaderboard request failed."
|
|
3588
|
+
};
|
|
3589
|
+
}
|
|
3590
|
+
let parsed;
|
|
3591
|
+
try {
|
|
3592
|
+
parsed = JSON.parse(text);
|
|
3593
|
+
} catch {
|
|
3594
|
+
return {
|
|
3595
|
+
ok: false,
|
|
3596
|
+
status: response.status,
|
|
3597
|
+
code: "bench_unavailable",
|
|
3598
|
+
error: "The leaderboard endpoint returned a non-JSON response."
|
|
3599
|
+
};
|
|
3600
|
+
}
|
|
3601
|
+
if (!isLeaderboardResponse(parsed)) {
|
|
3602
|
+
return {
|
|
3603
|
+
ok: false,
|
|
3604
|
+
status: response.status,
|
|
3605
|
+
code: "bench_unavailable",
|
|
3606
|
+
error: "The leaderboard endpoint returned an unexpected shape."
|
|
3607
|
+
};
|
|
3608
|
+
}
|
|
3609
|
+
return {
|
|
3610
|
+
ok: true,
|
|
3611
|
+
status: response.status,
|
|
3612
|
+
data: parsed,
|
|
3613
|
+
responseHeaders: response.headers,
|
|
3614
|
+
rawBody: text
|
|
3615
|
+
};
|
|
3616
|
+
}
|
|
3617
|
+
|
|
3618
|
+
// src/lib/format.ts
|
|
3619
|
+
function formatReleaseAt(iso, now = new Date) {
|
|
3620
|
+
const date = new Date(iso);
|
|
3621
|
+
if (!Number.isFinite(date.getTime()))
|
|
3622
|
+
return iso;
|
|
3623
|
+
const absolute = date.toLocaleDateString("en-US", {
|
|
3624
|
+
year: "numeric",
|
|
3625
|
+
month: "long",
|
|
3626
|
+
day: "numeric",
|
|
3627
|
+
timeZone: "UTC"
|
|
3628
|
+
});
|
|
3629
|
+
const msPerDay = 24 * 60 * 60 * 1000;
|
|
3630
|
+
const startOfTarget = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
|
|
3631
|
+
const startOfNow = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
|
|
3632
|
+
const days = Math.round((startOfTarget - startOfNow) / msPerDay);
|
|
3633
|
+
if (days <= 0)
|
|
3634
|
+
return absolute;
|
|
3635
|
+
if (days === 1)
|
|
3636
|
+
return `${absolute} (tomorrow)`;
|
|
3637
|
+
return `${absolute} (in ${days} days)`;
|
|
3638
|
+
}
|
|
3639
|
+
|
|
3640
|
+
// src/commands/bench.ts
|
|
3641
|
+
function registerBenchCommand(cli) {
|
|
3642
|
+
cli.command("bench", "Show the 10xBench top-10 model leaderboard (10xbench.ai)").option("--limit <n>", "Show only the top N models").action(async (options) => {
|
|
3643
|
+
const ctx = resolveContext(options);
|
|
3644
|
+
await runBench(ctx, options);
|
|
3645
|
+
});
|
|
3646
|
+
}
|
|
3647
|
+
async function runBench(ctx, options) {
|
|
3648
|
+
const limit = parseLimit(ctx, options.limit);
|
|
3649
|
+
verbose(ctx, `fetching leaderboard from ${resolveBenchBase()}`);
|
|
3650
|
+
const result = await fetchLeaderboard();
|
|
3651
|
+
if (!result.ok) {
|
|
3652
|
+
handleBenchError(ctx, result.status, result.code, result.error);
|
|
3653
|
+
}
|
|
3654
|
+
const data = result.data;
|
|
3655
|
+
if (data.schemaVersion !== SUPPORTED_LEADERBOARD_SCHEMA) {
|
|
3656
|
+
outputError(ctx, "unsupported_schema", `The leaderboard now uses format version ${data.schemaVersion}, but this CLI only understands version ${SUPPORTED_LEADERBOARD_SCHEMA}.`, ExitCodes.ERROR, "Run 'npm install -g @przeprogramowani/10x-cli@latest' to update, or visit https://10xbench.ai.");
|
|
3657
|
+
}
|
|
3658
|
+
renderLeaderboard(ctx, data, limit);
|
|
3659
|
+
}
|
|
3660
|
+
function parseLimit(ctx, raw) {
|
|
3661
|
+
if (raw === undefined)
|
|
3662
|
+
return null;
|
|
3663
|
+
const n = typeof raw === "number" ? raw : Number.parseInt(raw, 10);
|
|
3664
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
3665
|
+
outputError(ctx, "invalid_limit", `'${raw}' is not a valid limit.`, ExitCodes.USAGE, "Pass a positive whole number, for example '10x bench --limit 5'.");
|
|
3666
|
+
}
|
|
3667
|
+
return n;
|
|
3668
|
+
}
|
|
3669
|
+
function handleBenchError(ctx, status, code, error) {
|
|
3670
|
+
if (status === 0) {
|
|
3671
|
+
outputError(ctx, "network_error", "Could not reach 10xbench.ai.", ExitCodes.ERROR, "Check your internet connection and run '10x bench' again.");
|
|
3672
|
+
}
|
|
3673
|
+
if (code === "bench_unavailable") {
|
|
3674
|
+
outputError(ctx, "bench_unavailable", "The 10xBench leaderboard is temporarily unavailable.", ExitCodes.ERROR, "Try again in a few minutes, or visit https://10xbench.ai directly.");
|
|
3675
|
+
}
|
|
3676
|
+
outputError(ctx, code || "bench_failed", "Failed to load the leaderboard.", ExitCodes.ERROR, error ? `Server said: ${error}` : undefined);
|
|
3677
|
+
}
|
|
3678
|
+
function colorsEnabled() {
|
|
3679
|
+
return process.env["NO_COLOR"] === undefined && process.stderr.isTTY === true;
|
|
3680
|
+
}
|
|
3681
|
+
var STYLE = {
|
|
3682
|
+
bold: "1",
|
|
3683
|
+
dim: "2",
|
|
3684
|
+
red: "31",
|
|
3685
|
+
green: "32",
|
|
3686
|
+
yellow: "33",
|
|
3687
|
+
cyan: "36"
|
|
3688
|
+
};
|
|
3689
|
+
function paint(style, text) {
|
|
3690
|
+
if (!colorsEnabled())
|
|
3691
|
+
return text;
|
|
3692
|
+
return `\x1B[${style}m${text}\x1B[0m`;
|
|
3693
|
+
}
|
|
3694
|
+
function scoreStyle(percentage) {
|
|
3695
|
+
if (percentage >= 90)
|
|
3696
|
+
return STYLE.green;
|
|
3697
|
+
if (percentage >= 60)
|
|
3698
|
+
return STYLE.yellow;
|
|
3699
|
+
return STYLE.red;
|
|
3700
|
+
}
|
|
3701
|
+
var BAR_WIDTH = 20;
|
|
3702
|
+
function scoreBar(percentage) {
|
|
3703
|
+
const clamped = Math.min(Math.max(percentage, 0), 100);
|
|
3704
|
+
const filled = Math.round(clamped / 100 * BAR_WIDTH);
|
|
3705
|
+
return "█".repeat(filled) + "░".repeat(BAR_WIDTH - filled);
|
|
3706
|
+
}
|
|
3707
|
+
function renderLeaderboard(ctx, data, limit) {
|
|
3708
|
+
const ranked = limit === null ? data.leaderboard : data.leaderboard.slice(0, limit);
|
|
3709
|
+
if (ctx.json) {
|
|
3710
|
+
output(ctx, "", {
|
|
3711
|
+
source: resolveBenchBase(),
|
|
3712
|
+
generatedAt: data.generatedAt,
|
|
3713
|
+
totalAttempts: data.totalAttempts,
|
|
3714
|
+
totalModels: data.totalModels,
|
|
3715
|
+
models: ranked.map((e2, i) => ({
|
|
3716
|
+
rank: i + 1,
|
|
3717
|
+
model: e2.modelName,
|
|
3718
|
+
modelBaseId: e2.modelBaseId,
|
|
3719
|
+
score: e2.averageScore,
|
|
3720
|
+
maxScore: e2.averageMaxScore,
|
|
3721
|
+
percentage: e2.averagePercentage,
|
|
3722
|
+
attempts: e2.attemptCount,
|
|
3723
|
+
environment: e2.agentEnvironment,
|
|
3724
|
+
averageCost: e2.averageCost
|
|
3725
|
+
}))
|
|
3726
|
+
});
|
|
3727
|
+
return;
|
|
3728
|
+
}
|
|
3729
|
+
if (ranked.length === 0) {
|
|
3730
|
+
output(ctx, "The leaderboard has no results yet.", undefined);
|
|
3731
|
+
return;
|
|
3732
|
+
}
|
|
3733
|
+
const names = ranked.map((e2) => sanitize(e2.modelName));
|
|
3734
|
+
const details = ranked.map((e2) => formatDetail(e2));
|
|
3735
|
+
const nameWidth = Math.max(...names.map((n) => n.length));
|
|
3736
|
+
const detailWidth = Math.max(...details.map((d2) => d2.length));
|
|
3737
|
+
const rankWidth = String(ranked.length).length;
|
|
3738
|
+
const lines = [];
|
|
3739
|
+
lines.push(paint(STYLE.bold, `10xBench — Top ${ranked.length} of ${data.totalModels} models`) + paint(STYLE.dim, ` (${data.totalAttempts} attempts scored)`));
|
|
3740
|
+
lines.push("");
|
|
3741
|
+
for (let i = 0;i < ranked.length; i++) {
|
|
3742
|
+
lines.push(formatRow(ranked[i], i + 1, names[i], details[i], rankWidth, nameWidth, detailWidth));
|
|
3743
|
+
}
|
|
3744
|
+
lines.push("");
|
|
3745
|
+
lines.push(paint(STYLE.dim, `Data as of ${formatReleaseAt(data.generatedAt)} — ${resolveBenchBase()}`));
|
|
3746
|
+
output(ctx, lines.join(`
|
|
3747
|
+
`), undefined);
|
|
3748
|
+
}
|
|
3749
|
+
function formatDetail(entry) {
|
|
3750
|
+
const score = `${entry.averageScore.toFixed(1)}/${entry.averageMaxScore.toFixed(0)}`;
|
|
3751
|
+
const runs = `${entry.attemptCount} run${entry.attemptCount === 1 ? "" : "s"}`;
|
|
3752
|
+
const cost = entry.averageCost === null || entry.averageCost === undefined ? "" : `, ~$${entry.averageCost.toFixed(2)}/run`;
|
|
3753
|
+
return `(${score}, ${runs}${cost})`;
|
|
3754
|
+
}
|
|
3755
|
+
function formatRow(entry, rank, name, detail, rankWidth, nameWidth, detailWidth) {
|
|
3756
|
+
const style = scoreStyle(entry.averagePercentage);
|
|
3757
|
+
const rankCell = String(rank).padStart(rankWidth);
|
|
3758
|
+
const nameCell = name.padEnd(nameWidth);
|
|
3759
|
+
const pctCell = `${entry.averagePercentage.toFixed(1)}%`.padStart(6);
|
|
3760
|
+
return [
|
|
3761
|
+
` ${paint(rank <= 3 ? STYLE.bold : STYLE.dim, rankCell)}`,
|
|
3762
|
+
rank <= 3 ? paint(STYLE.bold, nameCell) : nameCell,
|
|
3763
|
+
paint(style, scoreBar(entry.averagePercentage)),
|
|
3764
|
+
paint(style, pctCell),
|
|
3765
|
+
detail.padEnd(detailWidth),
|
|
3766
|
+
paint(STYLE.dim, sanitize(entry.agentEnvironment))
|
|
3767
|
+
].join(" ");
|
|
3768
|
+
}
|
|
3769
|
+
|
|
3770
|
+
// src/commands/bench-kit.ts
|
|
3771
|
+
import { spawn } from "node:child_process";
|
|
3772
|
+
import {
|
|
3773
|
+
cpSync,
|
|
3774
|
+
existsSync as existsSync2,
|
|
3775
|
+
mkdirSync as mkdirSync3,
|
|
3776
|
+
mkdtempSync,
|
|
3777
|
+
readFileSync as readFileSync2,
|
|
3778
|
+
readdirSync,
|
|
3779
|
+
rmSync as rmSync2,
|
|
3780
|
+
writeFileSync as writeFileSync2
|
|
3781
|
+
} from "node:fs";
|
|
3782
|
+
import { tmpdir } from "node:os";
|
|
3783
|
+
import { join as join2, resolve } from "node:path";
|
|
3784
|
+
|
|
3785
|
+
// src/lib/experimental.ts
|
|
3786
|
+
var EXPERIMENTAL_ENV = "TENX_CLI_EXPERIMENTAL";
|
|
3787
|
+
function experimentalEnabled(env = process.env) {
|
|
3788
|
+
const value = env[EXPERIMENTAL_ENV];
|
|
3789
|
+
return value === "1" || value === "true";
|
|
3790
|
+
}
|
|
3791
|
+
function requireExperimental(command, flags, env = process.env) {
|
|
3792
|
+
if (experimentalEnabled(env))
|
|
3793
|
+
return;
|
|
3794
|
+
const ctx = resolveContext(flags);
|
|
3795
|
+
outputError(ctx, "experimental_locked", `'10x ${command}' is experimental and currently locked.`, ExitCodes.FORBIDDEN, `Set ${EXPERIMENTAL_ENV}=1 and run '10x ${command}' again.`);
|
|
3796
|
+
}
|
|
3797
|
+
|
|
3798
|
+
// src/commands/bench-kit.ts
|
|
3799
|
+
var TEMPLATE_REPO_URL = "https://github.com/przeprogramowani/10x-bench-kit";
|
|
3800
|
+
function registerBenchKitCommand(cli) {
|
|
3801
|
+
if (!experimentalEnabled())
|
|
3802
|
+
return;
|
|
3803
|
+
cli.command("bench-kit <action> [dir]", "Manage a benchmark instance (actions: init, update; experimental)").option("--template-version <tag>", "Template tag to install (default: latest)").option("--yes", "Run non-interactively, accepting defaults").example("10x bench-kit init my-benchmark").example("10x bench-kit init my-benchmark --template-version v0.1.0").action(async (action, dir, options) => {
|
|
3804
|
+
requireExperimental(`bench-kit ${action}`, options);
|
|
3805
|
+
const ctx = resolveContext(options);
|
|
3806
|
+
if (action === "init") {
|
|
3807
|
+
await runBenchKitInit(ctx, dir, options);
|
|
3808
|
+
return;
|
|
3809
|
+
}
|
|
3810
|
+
if (action === "update") {
|
|
3811
|
+
exitNotImplemented("bench-kit update", "a later bench-kit phase", options);
|
|
3812
|
+
}
|
|
3813
|
+
outputError(ctx, "unknown_action", `'${action}' is not a bench-kit action.`, ExitCodes.USAGE, "Run '10x bench-kit init [dir]' or '10x bench-kit update'.");
|
|
3814
|
+
});
|
|
3815
|
+
}
|
|
3816
|
+
async function runBenchKitInit(ctx, dirArg, options, deps = defaultDeps) {
|
|
3817
|
+
const targetDir = resolve(dirArg ?? ".");
|
|
3818
|
+
const requestedRef = normalizeRef(ctx, options.templateVersion);
|
|
3819
|
+
await preflight(ctx, deps);
|
|
3820
|
+
const existingVersion = readInstanceVersion(targetDir);
|
|
3821
|
+
const repair = existingVersion !== null;
|
|
3822
|
+
if (repair && requestedRef !== null) {
|
|
3823
|
+
outputError(ctx, "version_conflict", `This directory already holds a benchmark instance on template version ${existingVersion}.`, ExitCodes.USAGE, "Run '10x bench-kit update' to change the template version of an existing instance.");
|
|
3824
|
+
}
|
|
3825
|
+
if (!repair && existsSync2(targetDir) && readdirSync(targetDir).length > 0) {
|
|
3826
|
+
outputError(ctx, "target_not_empty", `Directory '${targetDir}' is not empty and is not a benchmark instance.`, ExitCodes.ERROR, "Run '10x bench-kit init <dir>' with an empty or new directory.");
|
|
3827
|
+
}
|
|
3828
|
+
const scratch = mkdtempSync(join2(tmpdir(), "bench-kit-"));
|
|
3829
|
+
try {
|
|
3830
|
+
verbose(ctx, `cloning ${TEMPLATE_REPO_URL} (${requestedRef ?? "latest"}) into ${scratch}`);
|
|
3831
|
+
const clone = await deps.cloneTemplate(requestedRef, scratch);
|
|
3832
|
+
if (!clone.ok) {
|
|
3833
|
+
outputError(ctx, "clone_failed", `Could not download the template from ${TEMPLATE_REPO_URL}.`, ExitCodes.ERROR, clone.error ? `Git said: ${clone.error.trim()}` : "Check your internet connection and run '10x bench-kit init' again.");
|
|
3834
|
+
}
|
|
3835
|
+
const templateVersion = readTemplateVersion(ctx, scratch);
|
|
3836
|
+
mkdirSync3(targetDir, { recursive: true });
|
|
3837
|
+
const copied = materialize(scratch, targetDir, { skipExisting: repair });
|
|
3838
|
+
const manifest = {
|
|
3839
|
+
templateVersion,
|
|
3840
|
+
templateRef: requestedRef ?? "latest",
|
|
3841
|
+
templateSource: TEMPLATE_REPO_URL,
|
|
3842
|
+
initializedAt: deps.now().toISOString()
|
|
3843
|
+
};
|
|
3844
|
+
writeFileSync2(join2(targetDir, ".bench-kit", "instance.json"), `${JSON.stringify(manifest, null, 2)}
|
|
3845
|
+
`);
|
|
3846
|
+
let committed = false;
|
|
3847
|
+
if (!repair) {
|
|
3848
|
+
committed = await freshGitInit(ctx, deps, targetDir, templateVersion);
|
|
3849
|
+
}
|
|
3850
|
+
const humanLines = repair ? [
|
|
3851
|
+
`Repaired the benchmark instance in '${targetDir}' (template ${templateVersion}).`,
|
|
3852
|
+
`Restored ${copied} missing file${copied === 1 ? "" : "s"}; your tasks, evaluation pool and config were not touched.`
|
|
3853
|
+
] : [
|
|
3854
|
+
`Created a benchmark instance in '${targetDir}' from template ${templateVersion}.`,
|
|
3855
|
+
committed ? "Initialized a fresh git repository with an initial commit." : "Initialized a fresh git repository (initial commit skipped — commit the files yourself).",
|
|
3856
|
+
"Next: wire up secrets, then run 'bench validate' before the first run."
|
|
3857
|
+
];
|
|
3858
|
+
output(ctx, humanLines.join(`
|
|
3859
|
+
`), {
|
|
3860
|
+
dir: targetDir,
|
|
3861
|
+
mode: repair ? "repair" : "init",
|
|
3862
|
+
templateVersion,
|
|
3863
|
+
templateRef: manifest.templateRef,
|
|
3864
|
+
filesCopied: copied,
|
|
3865
|
+
gitInitialized: !repair,
|
|
3866
|
+
committed
|
|
3867
|
+
});
|
|
3868
|
+
} finally {
|
|
3869
|
+
rmSync2(scratch, { recursive: true, force: true });
|
|
3870
|
+
}
|
|
3871
|
+
}
|
|
3872
|
+
function normalizeRef(ctx, raw) {
|
|
3873
|
+
if (raw === undefined)
|
|
3874
|
+
return null;
|
|
3875
|
+
const ref = raw.trim();
|
|
3876
|
+
if (ref === "" || ref === "latest")
|
|
3877
|
+
return null;
|
|
3878
|
+
if (!/^[A-Za-z0-9._/-]+$/.test(ref)) {
|
|
3879
|
+
outputError(ctx, "invalid_template_version", `'${raw}' is not a valid template tag.`, ExitCodes.USAGE, "Pass a tag name, for example '10x bench-kit init --template-version v0.1.0'.");
|
|
3880
|
+
}
|
|
3881
|
+
return ref;
|
|
3882
|
+
}
|
|
3883
|
+
async function preflight(ctx, deps) {
|
|
3884
|
+
if (!await deps.toolAvailable("git")) {
|
|
3885
|
+
outputError(ctx, "preflight_failed", "Git is required to download the template and initialize the instance.", ExitCodes.ERROR, "Install git (https://git-scm.com) and run '10x bench-kit init' again.");
|
|
3886
|
+
}
|
|
3887
|
+
const hasEngine = await deps.toolAvailable("docker") || await deps.toolAvailable("podman");
|
|
3888
|
+
if (!hasEngine) {
|
|
3889
|
+
verbose(ctx, "neither docker nor podman found — benchmark runs will need one later");
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
function readInstanceVersion(dir) {
|
|
3893
|
+
const versionFile = join2(dir, ".bench-kit", "VERSION");
|
|
3894
|
+
if (!existsSync2(versionFile))
|
|
3895
|
+
return null;
|
|
3896
|
+
return readFileSync2(versionFile, "utf8").trim();
|
|
3897
|
+
}
|
|
3898
|
+
function readTemplateVersion(ctx, cloneDir) {
|
|
3899
|
+
const versionFile = join2(cloneDir, ".bench-kit", "VERSION");
|
|
3900
|
+
if (!existsSync2(versionFile)) {
|
|
3901
|
+
outputError(ctx, "invalid_template", "The downloaded template has no .bench-kit/VERSION file.", ExitCodes.ERROR, "Pass a valid tag via '10x bench-kit init --template-version <tag>'.");
|
|
3902
|
+
}
|
|
3903
|
+
return readFileSync2(versionFile, "utf8").trim();
|
|
3904
|
+
}
|
|
3905
|
+
function materialize(srcDir, destDir, opts) {
|
|
3906
|
+
let copied = 0;
|
|
3907
|
+
const walk = (rel) => {
|
|
3908
|
+
for (const entry of readdirSync(join2(srcDir, rel), { withFileTypes: true })) {
|
|
3909
|
+
if (rel === "" && entry.name === ".git")
|
|
3910
|
+
continue;
|
|
3911
|
+
const relPath = join2(rel, entry.name);
|
|
3912
|
+
const from = join2(srcDir, relPath);
|
|
3913
|
+
const to = join2(destDir, relPath);
|
|
3914
|
+
if (entry.isDirectory()) {
|
|
3915
|
+
mkdirSync3(to, { recursive: true });
|
|
3916
|
+
walk(relPath);
|
|
3917
|
+
continue;
|
|
3918
|
+
}
|
|
3919
|
+
if (opts.skipExisting && existsSync2(to))
|
|
3920
|
+
continue;
|
|
3921
|
+
cpSync(from, to);
|
|
3922
|
+
copied++;
|
|
3923
|
+
}
|
|
3924
|
+
};
|
|
3925
|
+
walk("");
|
|
3926
|
+
return copied;
|
|
3927
|
+
}
|
|
3928
|
+
async function freshGitInit(ctx, deps, dir, templateVersion) {
|
|
3929
|
+
const init = await deps.runGit(["init"], dir);
|
|
3930
|
+
if (!init.ok) {
|
|
3931
|
+
outputError(ctx, "git_init_failed", "Could not initialize a git repository in the instance directory.", ExitCodes.ERROR, init.error ? `Git said: ${init.error.trim()}` : undefined);
|
|
3932
|
+
}
|
|
3933
|
+
const add = await deps.runGit(["add", "-A"], dir);
|
|
3934
|
+
const commit = add.ok ? await deps.runGit(["commit", "-m", `chore: bench-kit init (template ${templateVersion})`], dir) : add;
|
|
3935
|
+
if (!commit.ok) {
|
|
3936
|
+
verbose(ctx, `initial commit failed (${commit.error.trim()}) — files are staged, commit manually`);
|
|
3937
|
+
return false;
|
|
3938
|
+
}
|
|
3939
|
+
return true;
|
|
3940
|
+
}
|
|
3941
|
+
function run(cmd, args, cwd) {
|
|
3942
|
+
return new Promise((resolvePromise) => {
|
|
3943
|
+
const child = spawn(cmd, args, { cwd, stdio: ["ignore", "ignore", "pipe"] });
|
|
3944
|
+
let stderr = "";
|
|
3945
|
+
child.stderr?.on("data", (chunk) => {
|
|
3946
|
+
stderr += chunk.toString();
|
|
3947
|
+
});
|
|
3948
|
+
child.on("error", (err) => resolvePromise({ ok: false, error: err.message }));
|
|
3949
|
+
child.on("close", (code) => resolvePromise({ ok: code === 0, error: stderr }));
|
|
3950
|
+
});
|
|
3951
|
+
}
|
|
3952
|
+
var defaultDeps = {
|
|
3953
|
+
async toolAvailable(cmd) {
|
|
3954
|
+
const result = await run(cmd, ["--version"]);
|
|
3955
|
+
return result.ok;
|
|
3956
|
+
},
|
|
3957
|
+
cloneTemplate(ref, destDir) {
|
|
3958
|
+
const args = ["clone", "--depth", "1"];
|
|
3959
|
+
if (ref !== null)
|
|
3960
|
+
args.push("--branch", ref);
|
|
3961
|
+
args.push(TEMPLATE_REPO_URL, destDir);
|
|
3962
|
+
return run("git", args);
|
|
3963
|
+
},
|
|
3964
|
+
runGit(args, cwd) {
|
|
3965
|
+
return run("git", args, cwd);
|
|
3966
|
+
},
|
|
3967
|
+
now: () => new Date
|
|
3968
|
+
};
|
|
3969
|
+
|
|
3524
3970
|
// src/commands/doctor.ts
|
|
3525
|
-
import { existsSync as
|
|
3526
|
-
import { join as
|
|
3971
|
+
import { existsSync as existsSync3, accessSync, constants } from "node:fs";
|
|
3972
|
+
import { join as join3 } from "node:path";
|
|
3527
3973
|
|
|
3528
3974
|
// src/lib/signing.ts
|
|
3529
3975
|
import { createHash, createPublicKey, verify } from "node:crypto";
|
|
@@ -3732,28 +4178,6 @@ function apiBaseUrl() {
|
|
|
3732
4178
|
return resolveApiBase();
|
|
3733
4179
|
}
|
|
3734
4180
|
|
|
3735
|
-
// src/lib/format.ts
|
|
3736
|
-
function formatReleaseAt(iso, now = new Date) {
|
|
3737
|
-
const date = new Date(iso);
|
|
3738
|
-
if (!Number.isFinite(date.getTime()))
|
|
3739
|
-
return iso;
|
|
3740
|
-
const absolute = date.toLocaleDateString("en-US", {
|
|
3741
|
-
year: "numeric",
|
|
3742
|
-
month: "long",
|
|
3743
|
-
day: "numeric",
|
|
3744
|
-
timeZone: "UTC"
|
|
3745
|
-
});
|
|
3746
|
-
const msPerDay = 24 * 60 * 60 * 1000;
|
|
3747
|
-
const startOfTarget = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
|
|
3748
|
-
const startOfNow = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
|
|
3749
|
-
const days = Math.round((startOfTarget - startOfNow) / msPerDay);
|
|
3750
|
-
if (days <= 0)
|
|
3751
|
-
return absolute;
|
|
3752
|
-
if (days === 1)
|
|
3753
|
-
return `${absolute} (tomorrow)`;
|
|
3754
|
-
return `${absolute} (in ${days} days)`;
|
|
3755
|
-
}
|
|
3756
|
-
|
|
3757
4181
|
// src/lib/tool-profile.ts
|
|
3758
4182
|
var SENTINEL_BEGIN = "<!-- BEGIN @przeprogramowani/10x-cli -->";
|
|
3759
4183
|
var SENTINEL_END = "<!-- END @przeprogramowani/10x-cli -->";
|
|
@@ -4041,7 +4465,7 @@ async function checkApiConnectivity() {
|
|
|
4041
4465
|
}
|
|
4042
4466
|
function checkConfigDirectory() {
|
|
4043
4467
|
const dir = configDir();
|
|
4044
|
-
if (!
|
|
4468
|
+
if (!existsSync3(dir)) {
|
|
4045
4469
|
return {
|
|
4046
4470
|
name: "config",
|
|
4047
4471
|
label: "Config",
|
|
@@ -4106,8 +4530,8 @@ function checkToolDirectory() {
|
|
|
4106
4530
|
const toolId = readToolConfig()?.tool ?? DEFAULT_TOOL;
|
|
4107
4531
|
const profile = PROFILES[toolId] ?? PROFILES[DEFAULT_TOOL];
|
|
4108
4532
|
const dirName = profile.manifestDir;
|
|
4109
|
-
const toolDir =
|
|
4110
|
-
if (!
|
|
4533
|
+
const toolDir = join3(cwd, dirName);
|
|
4534
|
+
if (!existsSync3(toolDir)) {
|
|
4111
4535
|
return {
|
|
4112
4536
|
name: "tool-dir",
|
|
4113
4537
|
label: profile.displayName,
|
|
@@ -4139,7 +4563,7 @@ function checkToolDirectory() {
|
|
|
4139
4563
|
}
|
|
4140
4564
|
|
|
4141
4565
|
// src/commands/get.ts
|
|
4142
|
-
import { join as
|
|
4566
|
+
import { join as join8 } from "node:path";
|
|
4143
4567
|
|
|
4144
4568
|
// src/lib/conflict-prompt.ts
|
|
4145
4569
|
function createConflictResolver(tty) {
|
|
@@ -4224,8 +4648,8 @@ function parseModuleRef(raw) {
|
|
|
4224
4648
|
|
|
4225
4649
|
// src/lib/manifest.ts
|
|
4226
4650
|
import { createHash as createHash2 } from "node:crypto";
|
|
4227
|
-
import { existsSync as
|
|
4228
|
-
import { dirname as dirname2, join as
|
|
4651
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync3, renameSync as renameSync2, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
4652
|
+
import { dirname as dirname2, join as join4 } from "node:path";
|
|
4229
4653
|
var MANIFEST_FILENAME = ".10x-cli-manifest.json";
|
|
4230
4654
|
var CLI_PACKAGE_NAME = "@przeprogramowani/10x-cli";
|
|
4231
4655
|
var MANIFEST_VERSION = 3;
|
|
@@ -4233,12 +4657,12 @@ function contentHash(content) {
|
|
|
4233
4657
|
return createHash2("sha256").update(content, "utf8").digest("hex");
|
|
4234
4658
|
}
|
|
4235
4659
|
function readManifest(dir) {
|
|
4236
|
-
const manifestPath =
|
|
4237
|
-
if (!
|
|
4660
|
+
const manifestPath = join4(dir, MANIFEST_FILENAME);
|
|
4661
|
+
if (!existsSync4(manifestPath))
|
|
4238
4662
|
return null;
|
|
4239
4663
|
let raw;
|
|
4240
4664
|
try {
|
|
4241
|
-
raw =
|
|
4665
|
+
raw = readFileSync3(manifestPath, "utf8");
|
|
4242
4666
|
} catch {
|
|
4243
4667
|
return null;
|
|
4244
4668
|
}
|
|
@@ -4253,14 +4677,14 @@ function readManifest(dir) {
|
|
|
4253
4677
|
return parsed;
|
|
4254
4678
|
}
|
|
4255
4679
|
function writeManifest(dir, manifest) {
|
|
4256
|
-
if (!
|
|
4257
|
-
|
|
4680
|
+
if (!existsSync4(dir)) {
|
|
4681
|
+
mkdirSync4(dir, { recursive: true });
|
|
4258
4682
|
}
|
|
4259
|
-
const manifestPath =
|
|
4260
|
-
|
|
4683
|
+
const manifestPath = join4(dir, MANIFEST_FILENAME);
|
|
4684
|
+
mkdirSync4(dirname2(manifestPath), { recursive: true });
|
|
4261
4685
|
const tmp = `${manifestPath}.tmp`;
|
|
4262
|
-
|
|
4263
|
-
|
|
4686
|
+
rmSync3(tmp, { force: true });
|
|
4687
|
+
writeFileSync3(tmp, `${JSON.stringify(manifest, null, 2)}
|
|
4264
4688
|
`);
|
|
4265
4689
|
renameSync2(tmp, manifestPath);
|
|
4266
4690
|
}
|
|
@@ -4360,10 +4784,10 @@ function buildUnionFiles(lessons) {
|
|
|
4360
4784
|
}
|
|
4361
4785
|
|
|
4362
4786
|
// src/lib/tool-detect.ts
|
|
4363
|
-
import { existsSync as
|
|
4364
|
-
import { join as
|
|
4787
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
4788
|
+
import { join as join5 } from "node:path";
|
|
4365
4789
|
function detectTools(projectRoot) {
|
|
4366
|
-
const hit = (rel) =>
|
|
4790
|
+
const hit = (rel) => existsSync5(join5(projectRoot, rel));
|
|
4367
4791
|
const signals = [];
|
|
4368
4792
|
if (hit(".claude/" + MANIFEST_FILENAME)) {
|
|
4369
4793
|
signals.push({
|
|
@@ -4471,23 +4895,23 @@ function topDetectedProfile(signals) {
|
|
|
4471
4895
|
|
|
4472
4896
|
// src/lib/tool-switch.ts
|
|
4473
4897
|
import {
|
|
4474
|
-
existsSync as
|
|
4898
|
+
existsSync as existsSync7,
|
|
4475
4899
|
lstatSync,
|
|
4476
|
-
mkdirSync as
|
|
4477
|
-
readFileSync as
|
|
4900
|
+
mkdirSync as mkdirSync6,
|
|
4901
|
+
readFileSync as readFileSync6,
|
|
4478
4902
|
renameSync as renameSync3,
|
|
4479
4903
|
rmdirSync as rmdirSync2,
|
|
4480
|
-
rmSync as
|
|
4904
|
+
rmSync as rmSync5,
|
|
4481
4905
|
statSync,
|
|
4482
|
-
writeFileSync as
|
|
4906
|
+
writeFileSync as writeFileSync5
|
|
4483
4907
|
} from "node:fs";
|
|
4484
|
-
import { dirname as dirname4, join as
|
|
4908
|
+
import { dirname as dirname4, join as join7 } from "node:path";
|
|
4485
4909
|
|
|
4486
4910
|
// src/lib/fs-utils.ts
|
|
4487
|
-
import { readFileSync as
|
|
4911
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
4488
4912
|
function readFileOrNull(path) {
|
|
4489
4913
|
try {
|
|
4490
|
-
return
|
|
4914
|
+
return readFileSync4(path, "utf8");
|
|
4491
4915
|
} catch {
|
|
4492
4916
|
return null;
|
|
4493
4917
|
}
|
|
@@ -4563,15 +4987,15 @@ function stripBlock(content, begin, end, label, warnings) {
|
|
|
4563
4987
|
// src/lib/writer.ts
|
|
4564
4988
|
import {
|
|
4565
4989
|
chmodSync as chmodSync2,
|
|
4566
|
-
existsSync as
|
|
4567
|
-
mkdirSync as
|
|
4568
|
-
readFileSync as
|
|
4569
|
-
readdirSync,
|
|
4990
|
+
existsSync as existsSync6,
|
|
4991
|
+
mkdirSync as mkdirSync5,
|
|
4992
|
+
readFileSync as readFileSync5,
|
|
4993
|
+
readdirSync as readdirSync2,
|
|
4570
4994
|
rmdirSync,
|
|
4571
|
-
rmSync as
|
|
4572
|
-
writeFileSync as
|
|
4995
|
+
rmSync as rmSync4,
|
|
4996
|
+
writeFileSync as writeFileSync4
|
|
4573
4997
|
} from "node:fs";
|
|
4574
|
-
import { dirname as dirname3, join as
|
|
4998
|
+
import { dirname as dirname3, join as join6 } from "node:path";
|
|
4575
4999
|
var CLI_VERSION = package_default.version;
|
|
4576
5000
|
var DEFAULT_COURSE = "10xdevs3";
|
|
4577
5001
|
async function applyBundle(bundle, projectRoot, options = {}) {
|
|
@@ -4581,7 +5005,7 @@ async function applyBundle(bundle, projectRoot, options = {}) {
|
|
|
4581
5005
|
const profile = options.profile ?? PROFILES[DEFAULT_TOOL];
|
|
4582
5006
|
const onConflict = options.onConflict;
|
|
4583
5007
|
const applyCourseRules = options.applyCourseRules !== false;
|
|
4584
|
-
const manifestDir =
|
|
5008
|
+
const manifestDir = join6(projectRoot, profile.manifestDir);
|
|
4585
5009
|
const prevManifest = readManifest(manifestDir);
|
|
4586
5010
|
const plan = planBundle(bundle, projectRoot, { profile, applyCourseRules });
|
|
4587
5011
|
const nextSkillHashes = {};
|
|
@@ -4683,7 +5107,7 @@ async function applyBundle(bundle, projectRoot, options = {}) {
|
|
|
4683
5107
|
}
|
|
4684
5108
|
prompts.push({ name: prompt.name, path: target, action: finalAction, userBackupPath });
|
|
4685
5109
|
}
|
|
4686
|
-
const rulesFilePath =
|
|
5110
|
+
const rulesFilePath = join6(projectRoot, profile.rulesFile);
|
|
4687
5111
|
const existingRules = readFileOrEmpty(rulesFilePath);
|
|
4688
5112
|
const { action: rulesAction, content: newRulesContent } = planRules(existingRules, bundle, profile, applyCourseRules);
|
|
4689
5113
|
if (!dryRun && rulesAction !== "unchanged") {
|
|
@@ -4705,24 +5129,24 @@ async function applyBundle(bundle, projectRoot, options = {}) {
|
|
|
4705
5129
|
for (const entry of removed.skillDirs) {
|
|
4706
5130
|
removalResult.skills.push({ name: entry.name, path: entry.path, action: "removed" });
|
|
4707
5131
|
if (!dryRun && !partial)
|
|
4708
|
-
|
|
5132
|
+
rmSync4(entry.path, { recursive: true, force: true });
|
|
4709
5133
|
}
|
|
4710
5134
|
for (const entry of removed.skillFiles) {
|
|
4711
5135
|
removalResult.skills.push({ name: entry.name, path: entry.path, action: "removed" });
|
|
4712
5136
|
if (!dryRun && !partial) {
|
|
4713
|
-
|
|
5137
|
+
rmSync4(entry.path, { force: true });
|
|
4714
5138
|
removeEmptyParentDirs(entry.path, entry.skillDirAbs);
|
|
4715
5139
|
}
|
|
4716
5140
|
}
|
|
4717
5141
|
for (const entry of removed.prompts) {
|
|
4718
5142
|
removalResult.prompts.push({ name: entry.name, path: entry.path, action: "removed" });
|
|
4719
5143
|
if (!dryRun && !partial)
|
|
4720
|
-
|
|
5144
|
+
rmSync4(entry.path, { force: true });
|
|
4721
5145
|
}
|
|
4722
5146
|
for (const entry of removed.configs) {
|
|
4723
5147
|
removalResult.configs.push({ name: entry.name, path: entry.path, action: "removed" });
|
|
4724
5148
|
if (!dryRun && !partial)
|
|
4725
|
-
|
|
5149
|
+
rmSync4(entry.path, { force: true });
|
|
4726
5150
|
}
|
|
4727
5151
|
if (!dryRun && !partial) {
|
|
4728
5152
|
const catalogContentHash = options.catalogContentHash ?? prevManifest?.lessons?.[bundle.lessonId]?.catalogContentHash;
|
|
@@ -4803,13 +5227,13 @@ function planBundle(bundle, projectRoot, options = {}) {
|
|
|
4803
5227
|
assertSafeName(prompt.name, "prompt");
|
|
4804
5228
|
for (const config of bundle.configs)
|
|
4805
5229
|
assertSafeName(config.name, "config");
|
|
4806
|
-
const manifestDir =
|
|
5230
|
+
const manifestDir = join6(projectRoot, profile.manifestDir);
|
|
4807
5231
|
const prevManifest = readManifest(manifestDir);
|
|
4808
5232
|
const skills = bundle.skills.map((skill) => {
|
|
4809
|
-
const skillDir =
|
|
5233
|
+
const skillDir = join6(projectRoot, profile.skillDir(skill.name));
|
|
4810
5234
|
const prevEntry = prevManifest?.files.skills[skill.name];
|
|
4811
5235
|
const files = skill.files.map((file) => {
|
|
4812
|
-
const target =
|
|
5236
|
+
const target = join6(skillDir, file.path);
|
|
4813
5237
|
const storedHash = prevEntry?.contentHashes?.[file.path];
|
|
4814
5238
|
const { action, isConflict } = computeFileAction(target, file.content, storedHash);
|
|
4815
5239
|
return {
|
|
@@ -4823,7 +5247,7 @@ function planBundle(bundle, projectRoot, options = {}) {
|
|
|
4823
5247
|
return { name: skill.name, files };
|
|
4824
5248
|
});
|
|
4825
5249
|
const prompts = bundle.prompts.map((prompt) => {
|
|
4826
|
-
const target =
|
|
5250
|
+
const target = join6(projectRoot, profile.promptPath(prompt.name));
|
|
4827
5251
|
const storedHash = prevManifest?.files.promptHashes?.[`${prompt.name}.md`];
|
|
4828
5252
|
const { action, isConflict } = computeFileAction(target, prompt.content, storedHash);
|
|
4829
5253
|
return {
|
|
@@ -4834,12 +5258,12 @@ function planBundle(bundle, projectRoot, options = {}) {
|
|
|
4834
5258
|
upstreamChanged: computeUpstreamChanged(prompt.content, storedHash, action)
|
|
4835
5259
|
};
|
|
4836
5260
|
});
|
|
4837
|
-
const rulesFilePath =
|
|
5261
|
+
const rulesFilePath = join6(projectRoot, profile.rulesFile);
|
|
4838
5262
|
const existingRules = readFileOrEmpty(rulesFilePath);
|
|
4839
5263
|
const { action: rulesAction } = planRules(existingRules, bundle, profile, applyCourseRules);
|
|
4840
5264
|
const configs = bundle.configs.map((config) => {
|
|
4841
|
-
const target =
|
|
4842
|
-
const action =
|
|
5265
|
+
const target = join6(projectRoot, profile.configPath(config.name));
|
|
5266
|
+
const action = existsSync6(target) ? "skipped" : "created";
|
|
4843
5267
|
return {
|
|
4844
5268
|
name: config.name,
|
|
4845
5269
|
path: target,
|
|
@@ -4893,11 +5317,11 @@ function planRules(existingRules, bundle, profile, applyCourseRules) {
|
|
|
4893
5317
|
return { action, content: newRules };
|
|
4894
5318
|
}
|
|
4895
5319
|
function computeFileAction(filePath, newContent, storedHash) {
|
|
4896
|
-
if (!
|
|
5320
|
+
if (!existsSync6(filePath))
|
|
4897
5321
|
return { action: "created", isConflict: false };
|
|
4898
5322
|
let current;
|
|
4899
5323
|
try {
|
|
4900
|
-
current =
|
|
5324
|
+
current = readFileSync5(filePath, "utf8");
|
|
4901
5325
|
} catch {
|
|
4902
5326
|
return { action: "updated", isConflict: false };
|
|
4903
5327
|
}
|
|
@@ -4918,21 +5342,21 @@ function buildUserBackupPath(filePath) {
|
|
|
4918
5342
|
return `${filePath.slice(0, lastDot)}.user${filePath.slice(lastDot)}`;
|
|
4919
5343
|
}
|
|
4920
5344
|
function copyFileSync(src, dest) {
|
|
4921
|
-
|
|
4922
|
-
|
|
5345
|
+
mkdirSync5(dirname3(dest), { recursive: true });
|
|
5346
|
+
writeFileSync4(dest, readFileSync5(src));
|
|
4923
5347
|
}
|
|
4924
5348
|
function readFileOrEmpty(filePath) {
|
|
4925
|
-
if (!
|
|
5349
|
+
if (!existsSync6(filePath))
|
|
4926
5350
|
return "";
|
|
4927
5351
|
try {
|
|
4928
|
-
return
|
|
5352
|
+
return readFileSync5(filePath, "utf8");
|
|
4929
5353
|
} catch {
|
|
4930
5354
|
return "";
|
|
4931
5355
|
}
|
|
4932
5356
|
}
|
|
4933
5357
|
function writeFileAt(filePath, content) {
|
|
4934
|
-
|
|
4935
|
-
|
|
5358
|
+
mkdirSync5(dirname3(filePath), { recursive: true });
|
|
5359
|
+
writeFileSync4(filePath, content);
|
|
4936
5360
|
}
|
|
4937
5361
|
function computeRemovals(prevManifest, bundle, profile, projectRoot) {
|
|
4938
5362
|
const empty = {
|
|
@@ -4975,7 +5399,7 @@ function computeRemovals(prevManifest, bundle, profile, projectRoot) {
|
|
|
4975
5399
|
for (const [skillName, skill] of Object.entries(prevLessonEntry.skills)) {
|
|
4976
5400
|
if (!isSafeName(skillName))
|
|
4977
5401
|
continue;
|
|
4978
|
-
const skillDirAbs =
|
|
5402
|
+
const skillDirAbs = join6(projectRoot, profile.skillDir(skillName));
|
|
4979
5403
|
if (!currentSkills.has(skillName)) {
|
|
4980
5404
|
if (protectedSkills.has(skillName)) {
|
|
4981
5405
|
const prot = protectedSkills.get(skillName);
|
|
@@ -4986,7 +5410,7 @@ function computeRemovals(prevManifest, bundle, profile, projectRoot) {
|
|
|
4986
5410
|
continue;
|
|
4987
5411
|
removed.skillFiles.push({
|
|
4988
5412
|
name: `${skillName}/${relPath}`,
|
|
4989
|
-
path:
|
|
5413
|
+
path: join6(skillDirAbs, relPath),
|
|
4990
5414
|
skillDirAbs
|
|
4991
5415
|
});
|
|
4992
5416
|
}
|
|
@@ -5005,7 +5429,7 @@ function computeRemovals(prevManifest, bundle, profile, projectRoot) {
|
|
|
5005
5429
|
continue;
|
|
5006
5430
|
removed.skillFiles.push({
|
|
5007
5431
|
name: `${skillName}/${relPath}`,
|
|
5008
|
-
path:
|
|
5432
|
+
path: join6(skillDirAbs, relPath),
|
|
5009
5433
|
skillDirAbs
|
|
5010
5434
|
});
|
|
5011
5435
|
}
|
|
@@ -5020,7 +5444,7 @@ function computeRemovals(prevManifest, bundle, profile, projectRoot) {
|
|
|
5020
5444
|
const promptName = promptFile.replace(/\.md$/, "");
|
|
5021
5445
|
removed.prompts.push({
|
|
5022
5446
|
name: promptFile,
|
|
5023
|
-
path:
|
|
5447
|
+
path: join6(projectRoot, profile.promptPath(promptName))
|
|
5024
5448
|
});
|
|
5025
5449
|
}
|
|
5026
5450
|
for (const configFile of prevLessonEntry.configs) {
|
|
@@ -5032,7 +5456,7 @@ function computeRemovals(prevManifest, bundle, profile, projectRoot) {
|
|
|
5032
5456
|
continue;
|
|
5033
5457
|
removed.configs.push({
|
|
5034
5458
|
name: configFile,
|
|
5035
|
-
path:
|
|
5459
|
+
path: join6(projectRoot, profile.configPath(configFile))
|
|
5036
5460
|
});
|
|
5037
5461
|
}
|
|
5038
5462
|
return removed;
|
|
@@ -5041,7 +5465,7 @@ function removeEmptyParentDirs(filePath, skillDirAbs) {
|
|
|
5041
5465
|
let dir = dirname3(filePath);
|
|
5042
5466
|
while (dir.startsWith(skillDirAbs) && dir !== skillDirAbs) {
|
|
5043
5467
|
try {
|
|
5044
|
-
if (
|
|
5468
|
+
if (readdirSync2(dir).length > 0)
|
|
5045
5469
|
return;
|
|
5046
5470
|
rmdirSync(dir);
|
|
5047
5471
|
} catch {
|
|
@@ -5055,10 +5479,10 @@ function findOrphanedManifests(projectRoot, currentProfile) {
|
|
|
5055
5479
|
for (const profile of Object.values(PROFILES)) {
|
|
5056
5480
|
if (profile.toolId === currentProfile.toolId)
|
|
5057
5481
|
continue;
|
|
5058
|
-
const manifestPath =
|
|
5059
|
-
if (!
|
|
5482
|
+
const manifestPath = join6(projectRoot, profile.manifestDir, MANIFEST_FILENAME);
|
|
5483
|
+
if (!existsSync6(manifestPath))
|
|
5060
5484
|
continue;
|
|
5061
|
-
const manifest = readManifest(
|
|
5485
|
+
const manifest = readManifest(join6(projectRoot, profile.manifestDir));
|
|
5062
5486
|
if (!manifest)
|
|
5063
5487
|
continue;
|
|
5064
5488
|
out.push({ profile, manifestPath, manifest });
|
|
@@ -5137,8 +5561,8 @@ function migrateArtifacts(projectRoot, orphan, newProfile) {
|
|
|
5137
5561
|
summary.skipped.push({ path: skillName, reason: "unsafe name in manifest" });
|
|
5138
5562
|
continue;
|
|
5139
5563
|
}
|
|
5140
|
-
const fromSkillDir =
|
|
5141
|
-
const toSkillDir =
|
|
5564
|
+
const fromSkillDir = join7(projectRoot, oldProfile.skillDir(skillName));
|
|
5565
|
+
const toSkillDir = join7(projectRoot, newProfile.skillDir(skillName));
|
|
5142
5566
|
let anyMoved = false;
|
|
5143
5567
|
for (const relPath of entry.files) {
|
|
5144
5568
|
if (!isSafeSkillFilePath(relPath)) {
|
|
@@ -5148,8 +5572,8 @@ function migrateArtifacts(projectRoot, orphan, newProfile) {
|
|
|
5148
5572
|
});
|
|
5149
5573
|
continue;
|
|
5150
5574
|
}
|
|
5151
|
-
const from =
|
|
5152
|
-
const to =
|
|
5575
|
+
const from = join7(fromSkillDir, relPath);
|
|
5576
|
+
const to = join7(toSkillDir, relPath);
|
|
5153
5577
|
if (moveIfSafe(from, to, summary.skipped))
|
|
5154
5578
|
anyMoved = true;
|
|
5155
5579
|
}
|
|
@@ -5163,8 +5587,8 @@ function migrateArtifacts(projectRoot, orphan, newProfile) {
|
|
|
5163
5587
|
continue;
|
|
5164
5588
|
}
|
|
5165
5589
|
const promptName = promptFile.replace(/\.md$/, "");
|
|
5166
|
-
const from =
|
|
5167
|
-
const to =
|
|
5590
|
+
const from = join7(projectRoot, oldProfile.promptPath(promptName));
|
|
5591
|
+
const to = join7(projectRoot, newProfile.promptPath(promptName));
|
|
5168
5592
|
if (moveIfSafe(from, to, summary.skipped)) {
|
|
5169
5593
|
summary.movedOrRemoved.prompts.push(promptFile);
|
|
5170
5594
|
}
|
|
@@ -5174,15 +5598,15 @@ function migrateArtifacts(projectRoot, orphan, newProfile) {
|
|
|
5174
5598
|
summary.skipped.push({ path: configFile, reason: "unsafe name in manifest" });
|
|
5175
5599
|
continue;
|
|
5176
5600
|
}
|
|
5177
|
-
const from =
|
|
5178
|
-
const to =
|
|
5601
|
+
const from = join7(projectRoot, oldProfile.configPath(configFile));
|
|
5602
|
+
const to = join7(projectRoot, newProfile.configPath(configFile));
|
|
5179
5603
|
if (moveIfSafe(from, to, summary.skipped)) {
|
|
5180
5604
|
summary.movedOrRemoved.configs.push(configFile);
|
|
5181
5605
|
}
|
|
5182
5606
|
}
|
|
5183
5607
|
summary.sentinelStripped = stripSentinelFromRulesFile(projectRoot, oldProfile);
|
|
5184
|
-
|
|
5185
|
-
tryRemoveEmptyDir(
|
|
5608
|
+
rmSync5(orphan.manifestPath, { force: true });
|
|
5609
|
+
tryRemoveEmptyDir(join7(projectRoot, oldProfile.manifestDir));
|
|
5186
5610
|
return summary;
|
|
5187
5611
|
}
|
|
5188
5612
|
function deleteArtifacts(projectRoot, orphan) {
|
|
@@ -5199,7 +5623,7 @@ function deleteArtifacts(projectRoot, orphan) {
|
|
|
5199
5623
|
summary.skipped.push({ path: skillName, reason: "unsafe name in manifest" });
|
|
5200
5624
|
continue;
|
|
5201
5625
|
}
|
|
5202
|
-
const skillDir =
|
|
5626
|
+
const skillDir = join7(projectRoot, oldProfile.skillDir(skillName));
|
|
5203
5627
|
for (const relPath of entry.files) {
|
|
5204
5628
|
if (!isSafeSkillFilePath(relPath)) {
|
|
5205
5629
|
summary.skipped.push({
|
|
@@ -5208,9 +5632,9 @@ function deleteArtifacts(projectRoot, orphan) {
|
|
|
5208
5632
|
});
|
|
5209
5633
|
continue;
|
|
5210
5634
|
}
|
|
5211
|
-
|
|
5635
|
+
rmSync5(join7(skillDir, relPath), { force: true });
|
|
5212
5636
|
}
|
|
5213
|
-
|
|
5637
|
+
rmSync5(skillDir, { recursive: true, force: true });
|
|
5214
5638
|
summary.movedOrRemoved.skills.push(skillName);
|
|
5215
5639
|
}
|
|
5216
5640
|
for (const promptFile of orphan.manifest.files.prompts) {
|
|
@@ -5219,7 +5643,7 @@ function deleteArtifacts(projectRoot, orphan) {
|
|
|
5219
5643
|
continue;
|
|
5220
5644
|
}
|
|
5221
5645
|
const promptName = promptFile.replace(/\.md$/, "");
|
|
5222
|
-
|
|
5646
|
+
rmSync5(join7(projectRoot, oldProfile.promptPath(promptName)), { force: true });
|
|
5223
5647
|
summary.movedOrRemoved.prompts.push(promptFile);
|
|
5224
5648
|
}
|
|
5225
5649
|
for (const configFile of orphan.manifest.files.configs) {
|
|
@@ -5227,27 +5651,27 @@ function deleteArtifacts(projectRoot, orphan) {
|
|
|
5227
5651
|
summary.skipped.push({ path: configFile, reason: "unsafe name in manifest" });
|
|
5228
5652
|
continue;
|
|
5229
5653
|
}
|
|
5230
|
-
|
|
5654
|
+
rmSync5(join7(projectRoot, oldProfile.configPath(configFile)), { force: true });
|
|
5231
5655
|
summary.movedOrRemoved.configs.push(configFile);
|
|
5232
5656
|
}
|
|
5233
5657
|
summary.sentinelStripped = stripSentinelFromRulesFile(projectRoot, oldProfile);
|
|
5234
|
-
|
|
5235
|
-
const manifestDirAbs =
|
|
5236
|
-
tryRemoveEmptyDir(
|
|
5237
|
-
tryRemoveEmptyDir(
|
|
5238
|
-
tryRemoveEmptyDir(
|
|
5658
|
+
rmSync5(orphan.manifestPath, { force: true });
|
|
5659
|
+
const manifestDirAbs = join7(projectRoot, oldProfile.manifestDir);
|
|
5660
|
+
tryRemoveEmptyDir(join7(manifestDirAbs, "skills"));
|
|
5661
|
+
tryRemoveEmptyDir(join7(manifestDirAbs, "prompts"));
|
|
5662
|
+
tryRemoveEmptyDir(join7(manifestDirAbs, "config-templates"));
|
|
5239
5663
|
tryRemoveEmptyDir(manifestDirAbs);
|
|
5240
5664
|
return summary;
|
|
5241
5665
|
}
|
|
5242
5666
|
function moveIfSafe(from, to, skipped) {
|
|
5243
|
-
if (!
|
|
5667
|
+
if (!existsSync7(from)) {
|
|
5244
5668
|
return false;
|
|
5245
5669
|
}
|
|
5246
5670
|
if (lstatSync(from).isSymbolicLink()) {
|
|
5247
5671
|
skipped.push({ path: from, reason: "source is a symlink" });
|
|
5248
5672
|
return false;
|
|
5249
5673
|
}
|
|
5250
|
-
if (
|
|
5674
|
+
if (existsSync7(to)) {
|
|
5251
5675
|
if (!filesAreIdentical(from, to)) {
|
|
5252
5676
|
skipped.push({
|
|
5253
5677
|
path: to,
|
|
@@ -5255,22 +5679,22 @@ function moveIfSafe(from, to, skipped) {
|
|
|
5255
5679
|
});
|
|
5256
5680
|
return false;
|
|
5257
5681
|
}
|
|
5258
|
-
|
|
5682
|
+
rmSync5(from, { force: true });
|
|
5259
5683
|
return true;
|
|
5260
5684
|
}
|
|
5261
|
-
|
|
5685
|
+
mkdirSync6(dirname4(to), { recursive: true });
|
|
5262
5686
|
try {
|
|
5263
5687
|
renameSync3(from, to);
|
|
5264
5688
|
} catch (err) {
|
|
5265
5689
|
const code = err?.code;
|
|
5266
5690
|
if (code !== "EXDEV")
|
|
5267
5691
|
throw err;
|
|
5268
|
-
const bytes =
|
|
5692
|
+
const bytes = readFileSync6(from);
|
|
5269
5693
|
const tmp = `${to}.tmp`;
|
|
5270
|
-
|
|
5694
|
+
writeFileSync5(tmp, bytes);
|
|
5271
5695
|
renameSync3(tmp, to);
|
|
5272
5696
|
try {
|
|
5273
|
-
|
|
5697
|
+
rmSync5(from, { force: true });
|
|
5274
5698
|
} catch {
|
|
5275
5699
|
skipped.push({
|
|
5276
5700
|
path: from,
|
|
@@ -5282,8 +5706,8 @@ function moveIfSafe(from, to, skipped) {
|
|
|
5282
5706
|
return true;
|
|
5283
5707
|
}
|
|
5284
5708
|
function stripSentinelFromRulesFile(projectRoot, oldProfile) {
|
|
5285
|
-
const rulesPath =
|
|
5286
|
-
if (!
|
|
5709
|
+
const rulesPath = join7(projectRoot, oldProfile.rulesFile);
|
|
5710
|
+
if (!existsSync7(rulesPath))
|
|
5287
5711
|
return false;
|
|
5288
5712
|
const existing = readFileOrNull(rulesPath);
|
|
5289
5713
|
if (existing === null)
|
|
@@ -5292,9 +5716,9 @@ function stripSentinelFromRulesFile(projectRoot, oldProfile) {
|
|
|
5292
5716
|
if (!removed)
|
|
5293
5717
|
return false;
|
|
5294
5718
|
if (content.trim().length === 0) {
|
|
5295
|
-
|
|
5719
|
+
rmSync5(rulesPath, { force: true });
|
|
5296
5720
|
} else {
|
|
5297
|
-
|
|
5721
|
+
writeFileSync5(rulesPath, content);
|
|
5298
5722
|
}
|
|
5299
5723
|
return true;
|
|
5300
5724
|
}
|
|
@@ -5304,13 +5728,13 @@ function filesAreIdentical(a2, b2) {
|
|
|
5304
5728
|
const bStat = statSync(b2);
|
|
5305
5729
|
if (aStat.size !== bStat.size)
|
|
5306
5730
|
return false;
|
|
5307
|
-
return
|
|
5731
|
+
return readFileSync6(a2).equals(readFileSync6(b2));
|
|
5308
5732
|
} catch {
|
|
5309
5733
|
return false;
|
|
5310
5734
|
}
|
|
5311
5735
|
}
|
|
5312
5736
|
function tryRemoveEmptyDir(dir) {
|
|
5313
|
-
if (!
|
|
5737
|
+
if (!existsSync7(dir))
|
|
5314
5738
|
return;
|
|
5315
5739
|
try {
|
|
5316
5740
|
rmdirSync2(dir);
|
|
@@ -5517,7 +5941,7 @@ async function runGet(ctx, ref, options) {
|
|
|
5517
5941
|
const isFiltered = options.type !== undefined;
|
|
5518
5942
|
const bundle = filterBundle(ctx, result.data, options);
|
|
5519
5943
|
const isTTY = process.stdout.isTTY === true;
|
|
5520
|
-
const prevManifest = readManifest(
|
|
5944
|
+
const prevManifest = readManifest(join8(process.cwd(), profile.manifestDir));
|
|
5521
5945
|
if (prevManifest && prevManifest.manifestVersion === 2) {
|
|
5522
5946
|
showUpgradeNotice(isTTY);
|
|
5523
5947
|
}
|
|
@@ -5887,7 +6311,7 @@ function renderModuleDetail(ctx, module) {
|
|
|
5887
6311
|
}
|
|
5888
6312
|
|
|
5889
6313
|
// src/commands/sync.ts
|
|
5890
|
-
import { join as
|
|
6314
|
+
import { join as join9 } from "node:path";
|
|
5891
6315
|
var DEFAULT_COURSE4 = "10xdevs3";
|
|
5892
6316
|
var SUPPORTED_LANGS = ["en", "pl"];
|
|
5893
6317
|
function registerSyncCommand(cli) {
|
|
@@ -5933,7 +6357,7 @@ async function runSync(ctx, options) {
|
|
|
5933
6357
|
}
|
|
5934
6358
|
return true;
|
|
5935
6359
|
});
|
|
5936
|
-
const manifest = readManifest(
|
|
6360
|
+
const manifest = readManifest(join9(process.cwd(), profile.manifestDir));
|
|
5937
6361
|
const manifestLessonIds = new Set(manifest?.lessons ? Object.keys(manifest.lessons) : []);
|
|
5938
6362
|
let targets = unlocked;
|
|
5939
6363
|
if (moduleFilter !== undefined)
|
|
@@ -6297,6 +6721,8 @@ registerGetCommand(cli);
|
|
|
6297
6721
|
registerListCommand(cli);
|
|
6298
6722
|
registerSyncCommand(cli);
|
|
6299
6723
|
registerDoctorCommand(cli);
|
|
6724
|
+
registerBenchCommand(cli);
|
|
6725
|
+
registerBenchKitCommand(cli);
|
|
6300
6726
|
cli.help();
|
|
6301
6727
|
cli.version(package_default.version);
|
|
6302
6728
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: 10x-cli-guide
|
|
3
|
-
description: "Invoke this skill when the user asks how to USE the 10x-cli day-to-day — fetching lessons, listing modules, switching AI tools, troubleshooting errors, understanding where artifacts land, or working on a specific OS (Windows, Linux, macOS). Covers commands (get, list, doctor, auth --status/--logout), tool profiles, artifact locations, common errors, and platform-specific tips. Excludes: first-time installation and onboarding (use 10x-cli-setup instead), developing or contributing to 10x-cli source code, and general programming help."
|
|
3
|
+
description: "Invoke this skill when the user asks how to USE the 10x-cli day-to-day — fetching lessons, listing modules, switching AI tools, troubleshooting errors, understanding where artifacts land, checking the 10xBench model leaderboard, or working on a specific OS (Windows, Linux, macOS). Covers commands (get, list, doctor, auth --status/--logout, bench), tool profiles, artifact locations, common errors, and platform-specific tips. Excludes: first-time installation and onboarding (use 10x-cli-setup instead), developing or contributing to 10x-cli source code, and general programming help."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# 10x-cli Daily Usage Guide
|
|
@@ -117,6 +117,22 @@ Exit code 78 means at least one check failed.
|
|
|
117
117
|
|
|
118
118
|
Sessions refresh transparently — if a token is near expiry, the next command refreshes it automatically. You only need to re-auth manually if the session has fully expired.
|
|
119
119
|
|
|
120
|
+
### `10x bench` — 10xBench model leaderboard
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
10x bench # Top 10 AI models with color-coded score bars
|
|
124
|
+
10x bench --limit 5 # Just the top 5
|
|
125
|
+
10x bench --json # Machine-readable envelope (also when piped)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Shows the live leaderboard from [10xbench.ai](https://10xbench.ai) — AI models
|
|
129
|
+
benchmarked on vibe-coding the Przeprogramowani.pl website. Each row has the
|
|
130
|
+
average score, number of runs, cost per run where measured, and the agent
|
|
131
|
+
harness used. **No authentication needed** — this works before `10x auth`.
|
|
132
|
+
Data updates when new benchmark results are published; superseded model
|
|
133
|
+
versions are already filtered out upstream. If it reports the leaderboard as
|
|
134
|
+
unavailable, the site may be mid-deploy — retry in a few minutes.
|
|
135
|
+
|
|
120
136
|
---
|
|
121
137
|
|
|
122
138
|
## Switching Tools
|