@przeprogramowani/10x-cli 1.16.0 → 1.18.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 +86 -23
- 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.18.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";
|
|
@@ -11462,10 +11449,7 @@ 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";
|
|
11464
11451
|
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);
|
|
11452
|
+
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
11453
|
const ctx = resolveContext(options);
|
|
11470
11454
|
if (action === "init") {
|
|
11471
11455
|
await runBenchKitInit(ctx, dir, options);
|
|
@@ -11512,15 +11496,27 @@ async function runBenchKitInit(ctx, dirArg, options, deps = defaultDeps) {
|
|
|
11512
11496
|
const skills = syncDir(join4(scratch, skillSource), join4(targetDir, skillRootFor(toolId)), { overwrite: !repair });
|
|
11513
11497
|
installWorkflows(targetDir, { skipExisting: repair });
|
|
11514
11498
|
let baseRepo = null;
|
|
11499
|
+
let demoTasksPinned = 0;
|
|
11515
11500
|
if (!repair) {
|
|
11516
11501
|
const detected = await deps.detectBaseRepo(process.cwd());
|
|
11517
11502
|
if (detected !== null && resolve(detected.rootDir) !== targetDir) {
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11503
|
+
let repo = detected;
|
|
11504
|
+
const https = toHttpsUrl(detected.url);
|
|
11505
|
+
if (https !== null && await deps.remoteReachable(https)) {
|
|
11506
|
+
repo = { ...detected, url: https };
|
|
11507
|
+
verbose(ctx, `repo answers over https — using ${https} instead of SSH`);
|
|
11508
|
+
}
|
|
11509
|
+
if (await registerBaseRepo(join4(targetDir, "bench.config.yaml"), repo)) {
|
|
11510
|
+
baseRepo = repo;
|
|
11511
|
+
verbose(ctx, `registered base repo ${repo.name} (${repo.url})`);
|
|
11512
|
+
demoTasksPinned = await pinPlaceholderTasks(join4(targetDir, "tasks"), repo);
|
|
11513
|
+
if (demoTasksPinned > 0) {
|
|
11514
|
+
verbose(ctx, `pinned ${demoTasksPinned} demo task(s) to ${repo.headCommit.slice(0, 12)}`);
|
|
11515
|
+
}
|
|
11521
11516
|
}
|
|
11522
11517
|
}
|
|
11523
11518
|
}
|
|
11519
|
+
const runnerDeps = await installRunnerDependencies(ctx, deps, targetDir);
|
|
11524
11520
|
const manifest = {
|
|
11525
11521
|
templateVersion,
|
|
11526
11522
|
templateRef: requestedRef ?? "latest",
|
|
@@ -11543,6 +11539,8 @@ async function runBenchKitInit(ctx, dirArg, options, deps = defaultDeps) {
|
|
|
11543
11539
|
`Created a benchmark instance in '${targetDir}' from template ${templateVersion}.`,
|
|
11544
11540
|
`Agent skills installed for ${toolName} under ${skillRootFor(toolId)}/.`,
|
|
11545
11541
|
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.`,
|
|
11542
|
+
...demoTasksPinned > 0 ? [`Pinned the demo task to ${baseRepo?.headCommit.slice(0, 12)} (current HEAD of the base repo).`] : [],
|
|
11543
|
+
...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)."] : [],
|
|
11546
11544
|
committed ? "Initialized a fresh git repository with an initial commit." : "Initialized a fresh git repository (initial commit skipped — commit the files yourself).",
|
|
11547
11545
|
"Next: wire up secrets, then run 'bench validate' before the first run."
|
|
11548
11546
|
];
|
|
@@ -11556,6 +11554,8 @@ async function runBenchKitInit(ctx, dirArg, options, deps = defaultDeps) {
|
|
|
11556
11554
|
skillRoot: skillRootFor(toolId),
|
|
11557
11555
|
filesCopied: copied + skills.added,
|
|
11558
11556
|
baseRepo,
|
|
11557
|
+
demoTasksPinned,
|
|
11558
|
+
runnerDeps,
|
|
11559
11559
|
gitInitialized: !repair,
|
|
11560
11560
|
committed
|
|
11561
11561
|
});
|
|
@@ -11623,9 +11623,11 @@ async function runBenchKitUpdate(ctx, dirArg, options, deps = defaultDeps) {
|
|
|
11623
11623
|
for (const file of SHARED_ROOT_FILES) {
|
|
11624
11624
|
addSync(shared, syncFile(join4(scratch, file), join4(targetDir, file)));
|
|
11625
11625
|
}
|
|
11626
|
+
const runnerDeps = await installRunnerDependencies(ctx, deps, targetDir);
|
|
11626
11627
|
output(ctx, [
|
|
11627
11628
|
`Updated the benchmark instance from template ${currentVersion} to ${newVersion}.`,
|
|
11628
11629
|
" .bench-kit/ replaced wholesale (runtime zone)",
|
|
11630
|
+
...runnerDeps === "failed" ? [" .bench-kit/runner/ npm ci FAILED — run 'npm ci --prefix .bench-kit/runner' yourself"] : runnerDeps === "installed" ? [" .bench-kit/runner/ dependencies reinstalled (npm ci)"] : [],
|
|
11629
11631
|
` .github/workflows/ ${describeSync(workflows)}`,
|
|
11630
11632
|
` ${`${skillRootFor(toolId)}/`.padEnd(23)}${describeSync(skills)} — proposal, review before committing`,
|
|
11631
11633
|
` ${SHARED_ROOT_FILES.join(", ").padEnd(23)}${describeSync(shared)} — proposal, review before committing`,
|
|
@@ -11641,6 +11643,7 @@ async function runBenchKitUpdate(ctx, dirArg, options, deps = defaultDeps) {
|
|
|
11641
11643
|
templateRef: updatedManifest.templateRef,
|
|
11642
11644
|
tool: toolId,
|
|
11643
11645
|
skillRoot: skillRootFor(toolId),
|
|
11646
|
+
runnerDeps,
|
|
11644
11647
|
zones: { benchKit: "replaced", workflows, skills, shared }
|
|
11645
11648
|
});
|
|
11646
11649
|
} finally {
|
|
@@ -11831,6 +11834,53 @@ async function registerBaseRepo(configPath, repo) {
|
|
|
11831
11834
|
writeFileSync3(configPath, doc.toString());
|
|
11832
11835
|
return true;
|
|
11833
11836
|
}
|
|
11837
|
+
function toHttpsUrl(url) {
|
|
11838
|
+
const scp = url.match(/^git@([^:/]+):(.+)$/);
|
|
11839
|
+
if (scp !== null)
|
|
11840
|
+
return `https://${scp[1]}/${scp[2]}`;
|
|
11841
|
+
const ssh = url.match(/^ssh:\/\/(?:[^@/]+@)?([^:/]+)(?::\d+)?\/(.+)$/);
|
|
11842
|
+
if (ssh !== null)
|
|
11843
|
+
return `https://${ssh[1]}/${ssh[2]}`;
|
|
11844
|
+
return null;
|
|
11845
|
+
}
|
|
11846
|
+
var PLACEHOLDER_COMMIT = /^0{40}$/;
|
|
11847
|
+
async function pinPlaceholderTasks(tasksDir, repo) {
|
|
11848
|
+
if (!existsSync4(tasksDir) || !/^[0-9a-f]{40}$/.test(repo.headCommit))
|
|
11849
|
+
return 0;
|
|
11850
|
+
const { parseDocument } = await Promise.resolve().then(() => __toESM(require_dist(), 1));
|
|
11851
|
+
let pinned = 0;
|
|
11852
|
+
for (const entry of readdirSync(tasksDir, { withFileTypes: true })) {
|
|
11853
|
+
if (!entry.isDirectory())
|
|
11854
|
+
continue;
|
|
11855
|
+
const taskYaml = join4(tasksDir, entry.name, "task.yaml");
|
|
11856
|
+
if (!existsSync4(taskYaml))
|
|
11857
|
+
continue;
|
|
11858
|
+
const doc = parseDocument(readFileSync3(taskYaml, "utf8"));
|
|
11859
|
+
if (doc.getIn(["repo"]) !== PLACEHOLDER_BASE_REPO)
|
|
11860
|
+
continue;
|
|
11861
|
+
const commit = doc.getIn(["commit"]);
|
|
11862
|
+
doc.setIn(["repo"], repo.name);
|
|
11863
|
+
if (typeof commit === "string" && PLACEHOLDER_COMMIT.test(commit)) {
|
|
11864
|
+
doc.setIn(["commit"], repo.headCommit);
|
|
11865
|
+
}
|
|
11866
|
+
writeFileSync3(taskYaml, doc.toString());
|
|
11867
|
+
pinned++;
|
|
11868
|
+
}
|
|
11869
|
+
return pinned;
|
|
11870
|
+
}
|
|
11871
|
+
async function installRunnerDependencies(ctx, deps, targetDir) {
|
|
11872
|
+
const runnerDir = join4(targetDir, ".bench-kit", "runner");
|
|
11873
|
+
if (!existsSync4(join4(runnerDir, "package.json")))
|
|
11874
|
+
return "skipped";
|
|
11875
|
+
verbose(ctx, "installing runner dependencies (npm ci in .bench-kit/runner)");
|
|
11876
|
+
const result = await deps.installRunnerDeps(runnerDir);
|
|
11877
|
+
if (!result.ok) {
|
|
11878
|
+
verbose(ctx, `npm ci failed (${result.error.trim().split(`
|
|
11879
|
+
`).pop() ?? ""})`);
|
|
11880
|
+
return "failed";
|
|
11881
|
+
}
|
|
11882
|
+
return "installed";
|
|
11883
|
+
}
|
|
11834
11884
|
async function freshGitInit(ctx, deps, dir, templateVersion) {
|
|
11835
11885
|
const init = await deps.runGit(["init"], dir);
|
|
11836
11886
|
if (!init.ok) {
|
|
@@ -11844,9 +11894,13 @@ async function freshGitInit(ctx, deps, dir, templateVersion) {
|
|
|
11844
11894
|
}
|
|
11845
11895
|
return true;
|
|
11846
11896
|
}
|
|
11847
|
-
function run(cmd, args, cwd) {
|
|
11897
|
+
function run(cmd, args, cwd, env) {
|
|
11848
11898
|
return new Promise((resolvePromise) => {
|
|
11849
|
-
const child = spawn(cmd, args, {
|
|
11899
|
+
const child = spawn(cmd, args, {
|
|
11900
|
+
cwd,
|
|
11901
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
11902
|
+
...env === undefined ? {} : { env: { ...process.env, ...env } }
|
|
11903
|
+
});
|
|
11850
11904
|
let stdout = "";
|
|
11851
11905
|
let stderr = "";
|
|
11852
11906
|
child.stdout?.on("data", (chunk) => {
|
|
@@ -11893,6 +11947,15 @@ var defaultDeps = {
|
|
|
11893
11947
|
return null;
|
|
11894
11948
|
return { rootDir, name: basename(rootDir), url, headCommit };
|
|
11895
11949
|
},
|
|
11950
|
+
async remoteReachable(url) {
|
|
11951
|
+
const result = await run("git", ["ls-remote", "--heads", url], undefined, {
|
|
11952
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
11953
|
+
});
|
|
11954
|
+
return result.ok;
|
|
11955
|
+
},
|
|
11956
|
+
installRunnerDeps(runnerDir) {
|
|
11957
|
+
return run("npm", ["ci", "--no-audit", "--no-fund"], runnerDir);
|
|
11958
|
+
},
|
|
11896
11959
|
detectToolSignals(cwd) {
|
|
11897
11960
|
return detectTools(cwd);
|
|
11898
11961
|
},
|