@przeprogramowani/10x-cli 1.17.0 → 1.19.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 +35 -0
- package/dist/index.mjs +42 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -62,6 +62,7 @@ Once installed, just tell your agent to **set up 10x-cli** and it will pick up t
|
|
|
62
62
|
| `10x sync` | Bulk-download / refresh lessons and report what changed upstream |
|
|
63
63
|
| `10x doctor` | Diagnose auth, API connectivity, and local config |
|
|
64
64
|
| `10x bench` | Live top-10 AI model leaderboard from [10xbench.ai](https://10xbench.ai) — no login needed |
|
|
65
|
+
| `10x bench-kit <action>` | Create (`init`) and update (`update`) a company benchmark instance from the [10x-bench-kit](https://github.com/przeprogramowani/10x-bench-kit) template |
|
|
65
66
|
|
|
66
67
|
### `10x get` Flags
|
|
67
68
|
|
|
@@ -180,6 +181,40 @@ color-coded score bars in your terminal. Public data, works without logging in.
|
|
|
180
181
|
The data refreshes whenever new benchmark results are published on 10xbench.ai.
|
|
181
182
|
Colors honor `NO_COLOR` and are disabled automatically when output is piped.
|
|
182
183
|
|
|
184
|
+
### `10x bench-kit`
|
|
185
|
+
|
|
186
|
+
Creates and maintains a **company benchmark instance** from the
|
|
187
|
+
[10x-bench-kit](https://github.com/przeprogramowani/10x-bench-kit) template —
|
|
188
|
+
a self-hosted benchmark that scores AI agents on tasks embedded in your own
|
|
189
|
+
repositories. The CLI is a thin installer/updater; tasks, assertions, and
|
|
190
|
+
scoring live in the template and the instance.
|
|
191
|
+
|
|
192
|
+
| Action | Description |
|
|
193
|
+
|--------|-------------|
|
|
194
|
+
| `init [dir]` | Materialize a fresh instance from the template (no git history, fresh `git init`), register the detected base repo, install runner dependencies |
|
|
195
|
+
| `update [dir]` | Upgrade the instance to a newer template: runtime zone replaced wholesale, skills proposed as a reviewable diff, company content untouched |
|
|
196
|
+
|
|
197
|
+
| Flag | Description |
|
|
198
|
+
|------|-------------|
|
|
199
|
+
| `--template-version <tag>` | Template tag to install (default: latest) |
|
|
200
|
+
| `--tool <id>` | Agent tool for skill placement (`claude-code`, `cursor`, `copilot`, `codex`, `windsurf`, `gemini`, `generic`) |
|
|
201
|
+
| `--yes` | Run non-interactively, accepting defaults |
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# Create an instance next to your product repo (run inside it to auto-register)
|
|
205
|
+
10x bench-kit init my-benchmark
|
|
206
|
+
|
|
207
|
+
# Pin the template version
|
|
208
|
+
10x bench-kit init my-benchmark --template-version v0.8.0
|
|
209
|
+
|
|
210
|
+
# Upgrade an existing instance to the latest template
|
|
211
|
+
10x bench-kit update
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Re-running `init` on an existing instance is a **repair**: missing template
|
|
215
|
+
files are restored, company content is never touched. After `update`, run the
|
|
216
|
+
instance's `bench validate` to confirm the content still matches the new kit.
|
|
217
|
+
|
|
183
218
|
### Global Flags
|
|
184
219
|
|
|
185
220
|
- `--json` — Machine-readable JSON output (auto-detected when piped)
|
package/dist/index.mjs
CHANGED
|
@@ -10173,7 +10173,7 @@ var cac = (name = "") => new CAC(name);
|
|
|
10173
10173
|
// package.json
|
|
10174
10174
|
var package_default = {
|
|
10175
10175
|
name: "@przeprogramowani/10x-cli",
|
|
10176
|
-
version: "1.
|
|
10176
|
+
version: "1.19.0",
|
|
10177
10177
|
description: "Open-source CLI for 10xDevs course content",
|
|
10178
10178
|
repository: {
|
|
10179
10179
|
type: "git",
|
|
@@ -11104,19 +11104,6 @@ import {
|
|
|
11104
11104
|
import { tmpdir } from "node:os";
|
|
11105
11105
|
import { basename, join as join4, resolve, sep } from "node:path";
|
|
11106
11106
|
|
|
11107
|
-
// src/lib/experimental.ts
|
|
11108
|
-
var EXPERIMENTAL_ENV = "TENX_CLI_EXPERIMENTAL";
|
|
11109
|
-
function experimentalEnabled(env = process.env) {
|
|
11110
|
-
const value = env[EXPERIMENTAL_ENV];
|
|
11111
|
-
return value === "1" || value === "true";
|
|
11112
|
-
}
|
|
11113
|
-
function requireExperimental(command, flags, env = process.env) {
|
|
11114
|
-
if (experimentalEnabled(env))
|
|
11115
|
-
return;
|
|
11116
|
-
const ctx = resolveContext(flags);
|
|
11117
|
-
outputError(ctx, "experimental_locked", `'10x ${command}' is experimental and currently locked.`, ExitCodes.FORBIDDEN, `Set ${EXPERIMENTAL_ENV}=1 and run '10x ${command}' again.`);
|
|
11118
|
-
}
|
|
11119
|
-
|
|
11120
11107
|
// src/lib/tool-detect.ts
|
|
11121
11108
|
import { existsSync as existsSync3 } from "node:fs";
|
|
11122
11109
|
import { join as join3 } from "node:path";
|
|
@@ -11461,11 +11448,9 @@ function topDetectedProfile(signals) {
|
|
|
11461
11448
|
var TEMPLATE_REPO_URL = "https://github.com/przeprogramowani/10x-bench-kit";
|
|
11462
11449
|
var SHARED_ROOT_FILES = ["AGENTS.md"];
|
|
11463
11450
|
var PLACEHOLDER_BASE_REPO = "demo-app";
|
|
11451
|
+
var BASE_REPOS_DIR = ".repos";
|
|
11464
11452
|
function registerBenchKitCommand(cli) {
|
|
11465
|
-
|
|
11466
|
-
return;
|
|
11467
|
-
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("--tool <id>", `Agent tool for skill placement (${Object.keys(PROFILES).join(", ")})`).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").example("10x bench-kit update").action(async (action, dir, options) => {
|
|
11468
|
-
requireExperimental(`bench-kit ${action}`, options);
|
|
11453
|
+
cli.command("bench-kit <action> [dir]", "Manage a benchmark instance (actions: init, update)").option("--template-version <tag>", "Template tag to install (default: latest)").option("--tool <id>", `Agent tool for skill placement (${Object.keys(PROFILES).join(", ")})`).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").example("10x bench-kit update").action(async (action, dir, options) => {
|
|
11469
11454
|
const ctx = resolveContext(options);
|
|
11470
11455
|
if (action === "init") {
|
|
11471
11456
|
await runBenchKitInit(ctx, dir, options);
|
|
@@ -11532,6 +11517,21 @@ async function runBenchKitInit(ctx, dirArg, options, deps = defaultDeps) {
|
|
|
11532
11517
|
}
|
|
11533
11518
|
}
|
|
11534
11519
|
}
|
|
11520
|
+
let baseRepoClone = "skipped";
|
|
11521
|
+
if (baseRepo !== null) {
|
|
11522
|
+
ensureIgnored(targetDir, `${BASE_REPOS_DIR}/`);
|
|
11523
|
+
const cloneDest = join4(targetDir, BASE_REPOS_DIR, baseRepo.name);
|
|
11524
|
+
verbose(ctx, `cloning base repo into ${BASE_REPOS_DIR}/${baseRepo.name}`);
|
|
11525
|
+
const cloned = await deps.cloneBaseRepo(baseRepo, cloneDest);
|
|
11526
|
+
if (cloned.ok) {
|
|
11527
|
+
baseRepoClone = "cloned";
|
|
11528
|
+
} else {
|
|
11529
|
+
baseRepoClone = "failed";
|
|
11530
|
+
rmSync3(cloneDest, { recursive: true, force: true });
|
|
11531
|
+
verbose(ctx, `base repo clone failed (${cloned.error.trim().split(`
|
|
11532
|
+
`).pop() ?? ""})`);
|
|
11533
|
+
}
|
|
11534
|
+
}
|
|
11535
11535
|
const runnerDeps = await installRunnerDependencies(ctx, deps, targetDir);
|
|
11536
11536
|
const manifest = {
|
|
11537
11537
|
templateVersion,
|
|
@@ -11556,6 +11556,11 @@ async function runBenchKitInit(ctx, dirArg, options, deps = defaultDeps) {
|
|
|
11556
11556
|
`Agent skills installed for ${toolName} under ${skillRootFor(toolId)}/.`,
|
|
11557
11557
|
baseRepo === null ? "No product repo detected here — add your base repos to bench.config.yaml." : `Registered '${baseRepo.name}' (${baseRepo.url}) as the first base repo in bench.config.yaml.`,
|
|
11558
11558
|
...demoTasksPinned > 0 ? [`Pinned the demo task to ${baseRepo?.headCommit.slice(0, 12)} (current HEAD of the base repo).`] : [],
|
|
11559
|
+
...baseRepoClone === "cloned" ? [
|
|
11560
|
+
`Cloned '${baseRepo?.name}' into ${BASE_REPOS_DIR}/${baseRepo?.name}/ — local working copy for the authoring skills (gitignored).`
|
|
11561
|
+
] : baseRepoClone === "failed" ? [
|
|
11562
|
+
`Base repo clone failed — run 'git clone ${baseRepo?.url} ${BASE_REPOS_DIR}/${baseRepo?.name}' yourself.`
|
|
11563
|
+
] : [],
|
|
11559
11564
|
...runnerDeps === "failed" ? ["Runner dependencies did not install — run 'npm ci --prefix .bench-kit/runner' yourself."] : runnerDeps === "installed" ? ["Runner dependencies installed (.bench-kit/runner/node_modules)."] : [],
|
|
11560
11565
|
committed ? "Initialized a fresh git repository with an initial commit." : "Initialized a fresh git repository (initial commit skipped — commit the files yourself).",
|
|
11561
11566
|
"Next: wire up secrets, then run 'bench validate' before the first run."
|
|
@@ -11570,6 +11575,7 @@ async function runBenchKitInit(ctx, dirArg, options, deps = defaultDeps) {
|
|
|
11570
11575
|
skillRoot: skillRootFor(toolId),
|
|
11571
11576
|
filesCopied: copied + skills.added,
|
|
11572
11577
|
baseRepo,
|
|
11578
|
+
baseRepoClone,
|
|
11573
11579
|
demoTasksPinned,
|
|
11574
11580
|
runnerDeps,
|
|
11575
11581
|
gitInitialized: !repair,
|
|
@@ -11859,6 +11865,18 @@ function toHttpsUrl(url) {
|
|
|
11859
11865
|
return `https://${ssh[1]}/${ssh[2]}`;
|
|
11860
11866
|
return null;
|
|
11861
11867
|
}
|
|
11868
|
+
function ensureIgnored(dir, entry) {
|
|
11869
|
+
const file = join4(dir, ".gitignore");
|
|
11870
|
+
const current = existsSync4(file) ? readFileSync3(file, "utf8") : "";
|
|
11871
|
+
if (current.split(`
|
|
11872
|
+
`).some((line) => line.trim() === entry))
|
|
11873
|
+
return;
|
|
11874
|
+
const prefix = current === "" || current.endsWith(`
|
|
11875
|
+
`) ? current : `${current}
|
|
11876
|
+
`;
|
|
11877
|
+
writeFileSync3(file, `${prefix}${entry}
|
|
11878
|
+
`);
|
|
11879
|
+
}
|
|
11862
11880
|
var PLACEHOLDER_COMMIT = /^0{40}$/;
|
|
11863
11881
|
async function pinPlaceholderTasks(tasksDir, repo) {
|
|
11864
11882
|
if (!existsSync4(tasksDir) || !/^[0-9a-f]{40}$/.test(repo.headCommit))
|
|
@@ -11963,6 +11981,12 @@ var defaultDeps = {
|
|
|
11963
11981
|
return null;
|
|
11964
11982
|
return { rootDir, name: basename(rootDir), url, headCommit };
|
|
11965
11983
|
},
|
|
11984
|
+
async cloneBaseRepo(repo, destDir) {
|
|
11985
|
+
const clone = await run("git", ["clone", "--quiet", repo.rootDir, destDir]);
|
|
11986
|
+
if (!clone.ok)
|
|
11987
|
+
return { ok: false, error: clone.error };
|
|
11988
|
+
return run("git", ["-C", destDir, "remote", "set-url", "origin", repo.url]);
|
|
11989
|
+
},
|
|
11966
11990
|
async remoteReachable(url) {
|
|
11967
11991
|
const result = await run("git", ["ls-remote", "--heads", url], undefined, {
|
|
11968
11992
|
GIT_TERMINAL_PROMPT: "0"
|