@przeprogramowani/10x-cli 1.15.0 → 1.17.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.
Files changed (2) hide show
  1. package/dist/index.mjs +112 -7
  2. package/package.json +1 -1
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.15.0",
10176
+ version: "1.17.0",
10177
10177
  description: "Open-source CLI for 10xDevs course content",
10178
10178
  repository: {
10179
10179
  type: "git",
@@ -11459,6 +11459,7 @@ function topDetectedProfile(signals) {
11459
11459
 
11460
11460
  // src/commands/bench-kit.ts
11461
11461
  var TEMPLATE_REPO_URL = "https://github.com/przeprogramowani/10x-bench-kit";
11462
+ var SHARED_ROOT_FILES = ["AGENTS.md"];
11462
11463
  var PLACEHOLDER_BASE_REPO = "demo-app";
11463
11464
  function registerBenchKitCommand(cli) {
11464
11465
  if (!experimentalEnabled())
@@ -11511,15 +11512,27 @@ async function runBenchKitInit(ctx, dirArg, options, deps = defaultDeps) {
11511
11512
  const skills = syncDir(join4(scratch, skillSource), join4(targetDir, skillRootFor(toolId)), { overwrite: !repair });
11512
11513
  installWorkflows(targetDir, { skipExisting: repair });
11513
11514
  let baseRepo = null;
11515
+ let demoTasksPinned = 0;
11514
11516
  if (!repair) {
11515
11517
  const detected = await deps.detectBaseRepo(process.cwd());
11516
11518
  if (detected !== null && resolve(detected.rootDir) !== targetDir) {
11517
- if (await registerBaseRepo(join4(targetDir, "bench.config.yaml"), detected)) {
11518
- baseRepo = detected;
11519
- verbose(ctx, `registered base repo ${detected.name} (${detected.url})`);
11519
+ let repo = detected;
11520
+ const https = toHttpsUrl(detected.url);
11521
+ if (https !== null && await deps.remoteReachable(https)) {
11522
+ repo = { ...detected, url: https };
11523
+ verbose(ctx, `repo answers over https — using ${https} instead of SSH`);
11524
+ }
11525
+ if (await registerBaseRepo(join4(targetDir, "bench.config.yaml"), repo)) {
11526
+ baseRepo = repo;
11527
+ verbose(ctx, `registered base repo ${repo.name} (${repo.url})`);
11528
+ demoTasksPinned = await pinPlaceholderTasks(join4(targetDir, "tasks"), repo);
11529
+ if (demoTasksPinned > 0) {
11530
+ verbose(ctx, `pinned ${demoTasksPinned} demo task(s) to ${repo.headCommit.slice(0, 12)}`);
11531
+ }
11520
11532
  }
11521
11533
  }
11522
11534
  }
11535
+ const runnerDeps = await installRunnerDependencies(ctx, deps, targetDir);
11523
11536
  const manifest = {
11524
11537
  templateVersion,
11525
11538
  templateRef: requestedRef ?? "latest",
@@ -11542,6 +11555,8 @@ async function runBenchKitInit(ctx, dirArg, options, deps = defaultDeps) {
11542
11555
  `Created a benchmark instance in '${targetDir}' from template ${templateVersion}.`,
11543
11556
  `Agent skills installed for ${toolName} under ${skillRootFor(toolId)}/.`,
11544
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
+ ...demoTasksPinned > 0 ? [`Pinned the demo task to ${baseRepo?.headCommit.slice(0, 12)} (current HEAD of the base repo).`] : [],
11559
+ ...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)."] : [],
11545
11560
  committed ? "Initialized a fresh git repository with an initial commit." : "Initialized a fresh git repository (initial commit skipped — commit the files yourself).",
11546
11561
  "Next: wire up secrets, then run 'bench validate' before the first run."
11547
11562
  ];
@@ -11555,6 +11570,8 @@ async function runBenchKitInit(ctx, dirArg, options, deps = defaultDeps) {
11555
11570
  skillRoot: skillRootFor(toolId),
11556
11571
  filesCopied: copied + skills.added,
11557
11572
  baseRepo,
11573
+ demoTasksPinned,
11574
+ runnerDeps,
11558
11575
  gitInitialized: !repair,
11559
11576
  committed
11560
11577
  });
@@ -11618,11 +11635,18 @@ async function runBenchKitUpdate(ctx, dirArg, options, deps = defaultDeps) {
11618
11635
  const skills = syncDir(join4(scratch, skillSource), join4(targetDir, skillRootFor(toolId)), {
11619
11636
  overwrite: true
11620
11637
  });
11638
+ const shared = { added: 0, updated: 0, unchanged: 0 };
11639
+ for (const file of SHARED_ROOT_FILES) {
11640
+ addSync(shared, syncFile(join4(scratch, file), join4(targetDir, file)));
11641
+ }
11642
+ const runnerDeps = await installRunnerDependencies(ctx, deps, targetDir);
11621
11643
  output(ctx, [
11622
11644
  `Updated the benchmark instance from template ${currentVersion} to ${newVersion}.`,
11623
11645
  " .bench-kit/ replaced wholesale (runtime zone)",
11646
+ ...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)"] : [],
11624
11647
  ` .github/workflows/ ${describeSync(workflows)}`,
11625
11648
  ` ${`${skillRootFor(toolId)}/`.padEnd(23)}${describeSync(skills)} — proposal, review before committing`,
11649
+ ` ${SHARED_ROOT_FILES.join(", ").padEnd(23)}${describeSync(shared)} — proposal, review before committing`,
11626
11650
  " tasks/, evaluation-pool/, bench.config.yaml untouched (company zone)",
11627
11651
  "Next: review 'git diff', run 'bench validate' (it flags any schema changes to fix), then commit via PR."
11628
11652
  ].join(`
@@ -11635,7 +11659,8 @@ async function runBenchKitUpdate(ctx, dirArg, options, deps = defaultDeps) {
11635
11659
  templateRef: updatedManifest.templateRef,
11636
11660
  tool: toolId,
11637
11661
  skillRoot: skillRootFor(toolId),
11638
- zones: { benchKit: "replaced", workflows, skills }
11662
+ runnerDeps,
11663
+ zones: { benchKit: "replaced", workflows, skills, shared }
11639
11664
  });
11640
11665
  } finally {
11641
11666
  rmSync3(scratch, { recursive: true, force: true });
@@ -11758,6 +11783,26 @@ function syncDir(srcDir, destDir, opts) {
11758
11783
  function describeSync(counts) {
11759
11784
  return `${counts.added} added, ${counts.updated} updated, ${counts.unchanged} unchanged`;
11760
11785
  }
11786
+ function syncFile(from, to) {
11787
+ const counts = { added: 0, updated: 0, unchanged: 0 };
11788
+ if (!existsSync4(from))
11789
+ return counts;
11790
+ if (!existsSync4(to)) {
11791
+ cpSync(from, to);
11792
+ counts.added++;
11793
+ } else if (readFileSync3(from).equals(readFileSync3(to))) {
11794
+ counts.unchanged++;
11795
+ } else {
11796
+ cpSync(from, to);
11797
+ counts.updated++;
11798
+ }
11799
+ return counts;
11800
+ }
11801
+ function addSync(into, counts) {
11802
+ into.added += counts.added;
11803
+ into.updated += counts.updated;
11804
+ into.unchanged += counts.unchanged;
11805
+ }
11761
11806
  function materialize(srcDir, destDir, opts) {
11762
11807
  let copied = 0;
11763
11808
  const walk = (rel) => {
@@ -11805,6 +11850,53 @@ async function registerBaseRepo(configPath, repo) {
11805
11850
  writeFileSync3(configPath, doc.toString());
11806
11851
  return true;
11807
11852
  }
11853
+ function toHttpsUrl(url) {
11854
+ const scp = url.match(/^git@([^:/]+):(.+)$/);
11855
+ if (scp !== null)
11856
+ return `https://${scp[1]}/${scp[2]}`;
11857
+ const ssh = url.match(/^ssh:\/\/(?:[^@/]+@)?([^:/]+)(?::\d+)?\/(.+)$/);
11858
+ if (ssh !== null)
11859
+ return `https://${ssh[1]}/${ssh[2]}`;
11860
+ return null;
11861
+ }
11862
+ var PLACEHOLDER_COMMIT = /^0{40}$/;
11863
+ async function pinPlaceholderTasks(tasksDir, repo) {
11864
+ if (!existsSync4(tasksDir) || !/^[0-9a-f]{40}$/.test(repo.headCommit))
11865
+ return 0;
11866
+ const { parseDocument } = await Promise.resolve().then(() => __toESM(require_dist(), 1));
11867
+ let pinned = 0;
11868
+ for (const entry of readdirSync(tasksDir, { withFileTypes: true })) {
11869
+ if (!entry.isDirectory())
11870
+ continue;
11871
+ const taskYaml = join4(tasksDir, entry.name, "task.yaml");
11872
+ if (!existsSync4(taskYaml))
11873
+ continue;
11874
+ const doc = parseDocument(readFileSync3(taskYaml, "utf8"));
11875
+ if (doc.getIn(["repo"]) !== PLACEHOLDER_BASE_REPO)
11876
+ continue;
11877
+ const commit = doc.getIn(["commit"]);
11878
+ doc.setIn(["repo"], repo.name);
11879
+ if (typeof commit === "string" && PLACEHOLDER_COMMIT.test(commit)) {
11880
+ doc.setIn(["commit"], repo.headCommit);
11881
+ }
11882
+ writeFileSync3(taskYaml, doc.toString());
11883
+ pinned++;
11884
+ }
11885
+ return pinned;
11886
+ }
11887
+ async function installRunnerDependencies(ctx, deps, targetDir) {
11888
+ const runnerDir = join4(targetDir, ".bench-kit", "runner");
11889
+ if (!existsSync4(join4(runnerDir, "package.json")))
11890
+ return "skipped";
11891
+ verbose(ctx, "installing runner dependencies (npm ci in .bench-kit/runner)");
11892
+ const result = await deps.installRunnerDeps(runnerDir);
11893
+ if (!result.ok) {
11894
+ verbose(ctx, `npm ci failed (${result.error.trim().split(`
11895
+ `).pop() ?? ""})`);
11896
+ return "failed";
11897
+ }
11898
+ return "installed";
11899
+ }
11808
11900
  async function freshGitInit(ctx, deps, dir, templateVersion) {
11809
11901
  const init = await deps.runGit(["init"], dir);
11810
11902
  if (!init.ok) {
@@ -11818,9 +11910,13 @@ async function freshGitInit(ctx, deps, dir, templateVersion) {
11818
11910
  }
11819
11911
  return true;
11820
11912
  }
11821
- function run(cmd, args, cwd) {
11913
+ function run(cmd, args, cwd, env) {
11822
11914
  return new Promise((resolvePromise) => {
11823
- const child = spawn(cmd, args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
11915
+ const child = spawn(cmd, args, {
11916
+ cwd,
11917
+ stdio: ["ignore", "pipe", "pipe"],
11918
+ ...env === undefined ? {} : { env: { ...process.env, ...env } }
11919
+ });
11824
11920
  let stdout = "";
11825
11921
  let stderr = "";
11826
11922
  child.stdout?.on("data", (chunk) => {
@@ -11867,6 +11963,15 @@ var defaultDeps = {
11867
11963
  return null;
11868
11964
  return { rootDir, name: basename(rootDir), url, headCommit };
11869
11965
  },
11966
+ async remoteReachable(url) {
11967
+ const result = await run("git", ["ls-remote", "--heads", url], undefined, {
11968
+ GIT_TERMINAL_PROMPT: "0"
11969
+ });
11970
+ return result.ok;
11971
+ },
11972
+ installRunnerDeps(runnerDir) {
11973
+ return run("npm", ["ci", "--no-audit", "--no-fund"], runnerDir);
11974
+ },
11870
11975
  detectToolSignals(cwd) {
11871
11976
  return detectTools(cwd);
11872
11977
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@przeprogramowani/10x-cli",
3
- "version": "1.15.0",
3
+ "version": "1.17.0",
4
4
  "description": "Open-source CLI for 10xDevs course content",
5
5
  "repository": {
6
6
  "type": "git",