lens-engine 0.1.20 → 0.1.21

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/cli.js CHANGED
@@ -3838,6 +3838,79 @@ async function dashboardCommand() {
3838
3838
  openBrowser(url);
3839
3839
  }
3840
3840
 
3841
+ // packages/cli/src/commands/eval.ts
3842
+ function pct(n) {
3843
+ return `${Math.round(n * 100)}%`;
3844
+ }
3845
+ function printTable(summary) {
3846
+ const w = process.stdout.write.bind(process.stdout);
3847
+ w("\n LENS Eval \u2014 Baseline Results\n");
3848
+ w(" \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n");
3849
+ w(" Overall\n");
3850
+ w(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
3851
+ w(` Queries: ${summary.total}
3852
+ `);
3853
+ w(` Hit@1: ${pct(summary.hit_at_1)}
3854
+ `);
3855
+ w(` Hit@3: ${pct(summary.hit_at_3)}
3856
+ `);
3857
+ w(` Entry@1: ${pct(summary.entry_hit_at_1)}
3858
+ `);
3859
+ w(` Entry@3: ${pct(summary.entry_hit_at_3)}
3860
+ `);
3861
+ w(` Recall@5: ${pct(summary.avg_recall_at_5)}
3862
+ `);
3863
+ w(` Avg Duration: ${summary.avg_duration_ms}ms
3864
+
3865
+ `);
3866
+ if (summary.by_kind.length > 1) {
3867
+ w(" By Kind\n");
3868
+ w(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
3869
+ for (const k of summary.by_kind) {
3870
+ w(` ${k.kind} (n=${k.count})
3871
+ `);
3872
+ w(
3873
+ ` Hit@1: ${pct(k.hit_at_1)} Hit@3: ${pct(k.hit_at_3)} Recall@5: ${pct(k.avg_recall_at_5)} Avg: ${k.avg_duration_ms}ms
3874
+ `
3875
+ );
3876
+ }
3877
+ w("\n");
3878
+ }
3879
+ w(" Per Query\n");
3880
+ w(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
3881
+ for (const r of summary.results) {
3882
+ const mark = r.hit_at_3 ? "+" : "-";
3883
+ const entryMark = r.entry_hit_at_3 ? "+" : "-";
3884
+ w(` [${mark}] ${r.id} ${r.query.slice(0, 50).padEnd(50)} `);
3885
+ w(
3886
+ `H@1:${r.hit_at_1 ? "Y" : "N"} H@3:${r.hit_at_3 ? "Y" : "N"} E@3:${entryMark} R@5:${pct(r.recall_at_5).padStart(4)} ${r.duration_ms}ms
3887
+ `
3888
+ );
3889
+ if (!r.hit_at_3) {
3890
+ w(
3891
+ ` expected: ${r.expected_files.slice(0, 2).map((f) => f.split("/").pop()).join(", ")}
3892
+ `
3893
+ );
3894
+ w(
3895
+ ` got top3: ${r.returned_files.slice(0, 3).map((f) => f.split("/").pop()).join(", ")}
3896
+ `
3897
+ );
3898
+ }
3899
+ }
3900
+ w("\n");
3901
+ }
3902
+ async function evalCommand(opts) {
3903
+ const { repo_id } = await ensureRepo();
3904
+ const body = { repo_id };
3905
+ if (opts.kind) body.filter_kind = opts.kind;
3906
+ const summary = await post("/eval/run", body);
3907
+ if (opts.json) {
3908
+ output(summary, true);
3909
+ } else {
3910
+ printTable(summary);
3911
+ }
3912
+ }
3913
+
3841
3914
  // packages/cli/src/util/progress.ts
3842
3915
  var dim = (s) => `\x1B[2m${s}\x1B[0m`;
3843
3916
  var green = (s) => `\x1B[32m${s}\x1B[0m`;
@@ -4018,8 +4091,8 @@ async function showProgress(repoId, name, timeoutMs = 18e5) {
4018
4091
  await sleep2(RENDER_INTERVAL);
4019
4092
  }
4020
4093
  }
4021
- function createBar(pct, width) {
4022
- const clamped = Math.min(pct, 100);
4094
+ function createBar(pct2, width) {
4095
+ const clamped = Math.min(pct2, 100);
4023
4096
  const filled = Math.round(clamped / 100 * width);
4024
4097
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
4025
4098
  return `\x1B[32m${bar}\x1B[0m`;
@@ -4813,6 +4886,7 @@ repo.command("watch-status").description("Show file watcher status for current r
4813
4886
  repo.command("mcp").description("Write .mcp.json for MCP agent integration").action(() => run(() => mcpCommand()));
4814
4887
  program2.command("context <goal>").description("Build an intelligent context pack for a goal").option("--json", "Output as JSON", false).action((goal, opts) => run(() => contextCommand(goal, opts), "context"));
4815
4888
  program2.command("index").description("Index the current repo").option("--json", "Output as JSON", false).option("--force", "Full re-scan (default: diff scan, changed files only)", false).option("--status", "Show index status", false).action((opts) => run(() => indexCommand(opts), "index"));
4889
+ program2.command("eval").description("Run evaluation harness against current repo").option("--json", "Output as JSON", false).option("--kind <kind>", "Filter by query kind (natural, symbol, error_message)").action((opts) => run(() => evalCommand(opts), "eval"));
4816
4890
  program2.command("status").description("Show repo index/embedding status").option("--json", "Output as JSON", false).action((opts) => run(() => statusCommand(opts), "status"));
4817
4891
  var daemon = program2.command("daemon").description("Daemon management");
4818
4892
  daemon.command("stats").description("Show global daemon statistics").option("--json", "Output as JSON", false).action((opts) => run(() => daemonStatsCommand(opts)));