rubric-chat 0.3.0 → 0.4.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 CHANGED
@@ -29,8 +29,36 @@ rubric-chat rate --file path # score a JSON file (ChatGPT or Claude.ai export)
29
29
  # Output modes
30
30
  rubric-chat rate <id> --json # machine-readable
31
31
  rubric-chat rate <id> --quiet # exit code + URL only
32
+
33
+ # Fully offline scoring — your prompts never leave your machine
34
+ rubric-chat rate <id> --local
35
+ ```
36
+
37
+ ## Local mode (`--local`)
38
+
39
+ `rate --local` scores the session **entirely on-device** with a small open-weight
40
+ model — no account, no API call, no monthly cap, nothing uploaded. The default is
41
+ **Gemma 4 E2B** (Apache 2.0) running on ONNX Runtime via transformers.js. The
42
+ first run downloads the model (~3.2 GB, cached in `~/.rubric/models/`); after
43
+ that it's offline forever.
44
+
45
+ ```bash
46
+ rubric-chat rate <id> --local # default (gemma-4-e2b, ~5 GB RAM)
47
+ rubric-chat rate <id> --local --model gemma-4-e4b # bigger judge (~8 GB RAM)
48
+ rubric-chat rate <id> --local --model gemma-3-1b # low-RAM machines (~2 GB)
49
+ rubric-chat rate <id> --local --yes # skip the first-run download prompt
32
50
  ```
33
51
 
52
+ Available models: `gemma-4-e2b` (default), `gemma-4-e4b`, `gemma-3-4b`,
53
+ `gemma-3-1b`. Gemma 4 runs on the ONNX engine; Gemma 3 runs on llama.cpp with
54
+ grammar-enforced output.
55
+
56
+ Local scores use the exact same rubric, weights, and strictness curve as the
57
+ server, but a small on-device judge is not a frontier judge — expect roughly
58
+ ±10 points (local tends generous) and plainer feedback. The report is badged
59
+ `LOCAL (approximate)`; run without `--local` for the calibrated score and a
60
+ shareable card.
61
+
34
62
  ## Supported sources
35
63
 
36
64
  | Source | How it's discovered | Status |
@@ -56,6 +84,7 @@ Only your turns. The CLI strips assistant text locally before any payload leaves
56
84
  | --- | --- | --- |
57
85
  | `RUBRIC_API_BASE_URL` | `https://api.rubric.chat` | Override for local dev |
58
86
  | `RUBRIC_WEB_BASE_URL` | `https://rubric.chat` | Override for local dev |
87
+ | `RUBRIC_LOCAL_MODEL_URI` | registry default | Override the `--local` GGUF model URI |
59
88
 
60
89
  Credentials are stored in the OS keychain (macOS Keychain, libsecret on Linux). Fallback: `~/.rubric/credentials` (chmod 600).
61
90
 
@@ -2,7 +2,8 @@ import kleur from "kleur";
2
2
  import prompts from "prompts";
3
3
  import { analyzeSession } from "../lib/api.js";
4
4
  import { getWebBaseUrl } from "../lib/config.js";
5
- import { renderReport, renderTeaser } from "../lib/render.js";
5
+ import { renderLocalReport, renderReport, renderTeaser } from "../lib/render.js";
6
+ import { analyzeLocally } from "../local/engine.js";
6
7
  import { loadGenericSession } from "../sources/generic.js";
7
8
  import { getSource, SOURCES } from "../sources/index.js";
8
9
  export async function runRate(target, options) {
@@ -51,10 +52,45 @@ export async function runRate(target, options) {
51
52
  process.exitCode = 1;
52
53
  return;
53
54
  }
55
+ if (options.local) {
56
+ if (!options.quiet) {
57
+ console.log(kleur.dim(`Scoring ${session.turns.length} user turns on-device…`));
58
+ }
59
+ const localReport = await analyzeLocally(session, {
60
+ modelName: options.model,
61
+ yes: options.yes,
62
+ quiet: options.quiet
63
+ });
64
+ if (options.json) {
65
+ console.log(JSON.stringify(localReport));
66
+ }
67
+ else if (options.quiet) {
68
+ console.log(localReport.score);
69
+ }
70
+ else {
71
+ console.log(renderLocalReport(localReport));
72
+ }
73
+ return;
74
+ }
54
75
  if (!options.quiet) {
55
76
  console.log(kleur.dim(`Analyzing ${session.turns.length} user turns…`));
56
77
  }
57
- const response = await analyzeSession(session);
78
+ let response;
79
+ try {
80
+ response = await analyzeSession(session);
81
+ }
82
+ catch (err) {
83
+ const message = err instanceof Error ? err.message : String(err);
84
+ if (/free tier cap/i.test(message)) {
85
+ console.error(kleur.red(message));
86
+ console.error(kleur.dim("Tip: score on-device with ") +
87
+ kleur.cyan("--local") +
88
+ kleur.dim(" — it's free, offline, and doesn't count against the cap."));
89
+ process.exitCode = 1;
90
+ return;
91
+ }
92
+ throw err;
93
+ }
58
94
  const webBase = getWebBaseUrl();
59
95
  if (options.json) {
60
96
  console.log(JSON.stringify(response));
@@ -1 +1 @@
1
- {"version":3,"file":"rate.js","sourceRoot":"","sources":["../../src/commands/rate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rate.js","sourceRoot":"","sources":["../../src/commands/rate.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -58,6 +58,9 @@ program
58
58
  .option("--json", "Print the analyze response as JSON")
59
59
  .option("--quiet", "Print only the score")
60
60
  .option("--interactive", "Pick a session from a list")
61
+ .option("--local", "Score fully on-device (offline, approximate, never uploads your prompts)")
62
+ .option("--model <name>", "Local model: gemma-4-e2b (default) | gemma-4-e4b | gemma-3-4b | gemma-3-1b")
63
+ .option("--yes", "Skip the first-run model download confirmation")
61
64
  .action(async (sessionId, opts) => {
62
65
  await runRate(sessionId, opts);
63
66
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -31,6 +31,26 @@ export function getWebBaseUrl() {
31
31
  async function ensureHome() {
32
32
  await mkdir(RUBRIC_HOME, { recursive: true });
33
33
  }
34
+ // --- Local-analysis config (~/.rubric/config.json + ~/.rubric/models/) ---
35
+ const LOCAL_CONFIG_FILE = join(RUBRIC_HOME, "config.json");
36
+ export function getModelsDir() {
37
+ return join(RUBRIC_HOME, "models");
38
+ }
39
+ export async function readLocalConfig() {
40
+ try {
41
+ const raw = await readFile(LOCAL_CONFIG_FILE, "utf8");
42
+ const parsed = JSON.parse(raw);
43
+ return typeof parsed === "object" && parsed !== null ? parsed : {};
44
+ }
45
+ catch {
46
+ return {};
47
+ }
48
+ }
49
+ export async function writeLocalConfig(config) {
50
+ await ensureHome();
51
+ await mkdir(getModelsDir(), { recursive: true });
52
+ await writeFile(LOCAL_CONFIG_FILE, JSON.stringify(config, null, 2), "utf8");
53
+ }
34
54
  async function loadKeytar() {
35
55
  try {
36
56
  const mod = await import("keytar");
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import kleur from "kleur";
2
2
  export function renderTeaser(teaser, webBase) {
3
3
  return [
4
4
  "",
5
- ` ${kleur.bold(`Score`)} ${kleur.yellow(String(teaser.score))}${kleur.dim("/100")} ${kleur.dim("(strict median ~50)")}`,
5
+ ` ${kleur.bold(`Score`)} ${kleur.yellow(String(teaser.score))}${kleur.dim("/100")}`,
6
6
  ` ${kleur.bold("Archetype")} ${teaser.archetype_name}`,
7
7
  ` ${kleur.dim(teaser.archetype_tagline)}`,
8
8
  "",
@@ -32,6 +32,31 @@ export function renderReport(report, webBase) {
32
32
  lines.push("");
33
33
  return lines.join("\n");
34
34
  }
35
+ export function renderLocalReport(report) {
36
+ const badge = report.mode === "model"
37
+ ? kleur.bgYellow().black(" LOCAL (approximate) ")
38
+ : kleur.bgRed().white(" HEURISTIC ESTIMATE ");
39
+ const lines = [];
40
+ lines.push("");
41
+ lines.push(` ${badge}`);
42
+ lines.push("");
43
+ lines.push(` ${kleur.bold("Score")} ${kleur.yellow(String(report.score))}${kleur.dim("/100")} ${kleur.dim(`(raw ${report.raw_score})`)}`);
44
+ lines.push(` ${kleur.bold(report.archetype.name)} — ${kleur.dim(report.archetype.tagline)}`);
45
+ lines.push("");
46
+ lines.push(` ${kleur.dim(report.summary)}`);
47
+ lines.push("");
48
+ for (const dimension of report.dimensions) {
49
+ const bar = renderBar(dimension.score);
50
+ lines.push(` ${dimension.dimension.padEnd(20)} ${bar} ${String(dimension.score).padStart(3)} ${kleur.dim(dimension.feedback)}`);
51
+ }
52
+ lines.push("");
53
+ if (report.mode === "model") {
54
+ lines.push(kleur.dim(` Scored on-device by ${report.model_used} — expect ±10 vs the server score.`));
55
+ }
56
+ lines.push(kleur.dim(" Nothing left your machine. Run without --local for the calibrated score + share card."));
57
+ lines.push("");
58
+ return lines.join("\n");
59
+ }
35
60
  function renderBar(score, width = 24) {
36
61
  const filled = Math.max(0, Math.min(width, Math.round((score / 100) * width)));
37
62
  return kleur.green("█".repeat(filled)) + kleur.dim("░".repeat(width - filled));
@@ -1 +1 @@
1
- {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/lib/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,UAAU,YAAY,CAAC,MAAoB,EAAE,OAAe;IAChE,OAAO;QACL,EAAE;QACF,KAAK,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,qBAAqB,CAAC,EAAE;QACzH,KAAK,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC,cAAc,EAAE;QACvD,KAAK,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE;QAC1C,EAAE;QACF,KAAK,CAAC,GAAG,CAAC,mCAAmC,CAAC;QAC9C,KAAK,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE;QACtC,KAAK,CAAC,GAAG,CAAC,cAAc,OAAO,2BAA2B,CAAC;QAC3D,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,OAAe;IAC1D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,QAAQ,MAAM,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5I,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9F,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC7C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACvC,KAAK,CAAC,IAAI,CACR,KAAK,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,GAAG,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CACtH,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,WAAW,MAAM,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IACjG,IAAI,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACnF,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,SAAS,CAAC,KAAa,EAAE,KAAK,GAAG,EAAE;IAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/E,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC;AACjF,CAAC"}
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/lib/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,UAAU,YAAY,CAAC,MAAoB,EAAE,OAAe;IAChE,OAAO;QACL,EAAE;QACF,KAAK,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;QACpF,KAAK,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC,cAAc,EAAE;QACvD,KAAK,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE;QAC1C,EAAE;QACF,KAAK,CAAC,GAAG,CAAC,mCAAmC,CAAC;QAC9C,KAAK,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE;QACtC,KAAK,CAAC,GAAG,CAAC,cAAc,OAAO,2BAA2B,CAAC;QAC3D,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,OAAe;IAC1D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,QAAQ,MAAM,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5I,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9F,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC7C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACvC,KAAK,CAAC,IAAI,CACR,KAAK,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,GAAG,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CACtH,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,WAAW,MAAM,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IACjG,IAAI,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACnF,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAmB;IACnD,MAAM,KAAK,GACT,MAAM,CAAC,IAAI,KAAK,OAAO;QACrB,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,uBAAuB,CAAC;QACjD,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAClD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;IACzB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CACR,KAAK,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,QAAQ,MAAM,CAAC,SAAS,GAAG,CAAC,EAAE,CAChI,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9F,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC7C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACvC,KAAK,CAAC,IAAI,CACR,KAAK,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,GAAG,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CACtH,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,CACR,KAAK,CAAC,GAAG,CACP,yBAAyB,MAAM,CAAC,UAAU,oCAAoC,CAC/E,CACF,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI,CACR,KAAK,CAAC,GAAG,CAAC,yFAAyF,CAAC,CACrG,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,SAAS,CAAC,KAAa,EAAE,KAAK,GAAG,EAAE;IAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/E,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC;AACjF,CAAC"}
@@ -0,0 +1,102 @@
1
+ export const ARCHETYPES = {
2
+ sniper: {
3
+ slug: "sniper",
4
+ name: "The Sniper",
5
+ tagline: "Short, sharp, and sometimes under-scoped.",
6
+ description: "You tend to ask concise questions and avoid unnecessary context. This can work well for narrow tasks, but the assistant often has to infer constraints. Your next improvement is adding just enough success criteria to reduce guesswork.",
7
+ share_color: "#2563eb",
8
+ growth_tips: [
9
+ "Add one sentence of context before the ask.",
10
+ "Define what a successful answer should include.",
11
+ "Use follow-ups to refine, not just redirect."
12
+ ]
13
+ },
14
+ sculptor: {
15
+ slug: "sculptor",
16
+ name: "The Sculptor",
17
+ tagline: "Careful setup with targeted refinements.",
18
+ description: "You usually shape the output through a strong opener and deliberate follow-ups. Your best sessions show clear intent and useful course correction. Watch for overloading the first prompt when a staged workflow would be cleaner.",
19
+ share_color: "#0891b2",
20
+ growth_tips: [
21
+ "Separate background from the actual task.",
22
+ "Ask for checkpoints on complex work.",
23
+ "Name what changed between follow-ups."
24
+ ]
25
+ },
26
+ interrogator: {
27
+ slug: "interrogator",
28
+ name: "The Interrogator",
29
+ tagline: "Many focused follow-ups that drill down.",
30
+ description: "You learn by asking a sequence of smaller questions. This can expose details quickly, but may lose a coherent end goal. Stronger prompts would state the larger objective before the drill-down begins.",
31
+ share_color: "#0f766e",
32
+ growth_tips: [
33
+ "State the final decision you are trying to make.",
34
+ "Group related follow-ups into one structured prompt.",
35
+ "Summarize what you learned before pivoting."
36
+ ]
37
+ },
38
+ firehose: {
39
+ slug: "firehose",
40
+ name: "The Firehose",
41
+ tagline: "Everything arrives at once.",
42
+ description: "You provide lots of material, often in a single heavy prompt. That can be useful for context-rich work, but it risks burying the actual instruction. The main opportunity is structure: labels, priorities, and explicit output requirements.",
43
+ share_color: "#dc2626",
44
+ growth_tips: [
45
+ "Put the core ask at the top.",
46
+ "Use headings for context, constraints, and output.",
47
+ "Mark which details are essential versus optional."
48
+ ]
49
+ },
50
+ wanderer: {
51
+ slug: "wanderer",
52
+ name: "The Wanderer",
53
+ tagline: "Curious, but the thread drifts.",
54
+ description: "Your session moves across topics or goals without a stable through-line. This can be good for exploration, but it makes high-quality assistance harder. You will score higher by anchoring each turn to the current objective.",
55
+ share_color: "#7c3aed",
56
+ growth_tips: [
57
+ "Name the current objective before each pivot.",
58
+ "Start a new session for unrelated work.",
59
+ "Ask the assistant to track open questions."
60
+ ]
61
+ },
62
+ repeater: {
63
+ slug: "repeater",
64
+ name: "The Repeater",
65
+ tagline: "Persistent, but often not more specific.",
66
+ description: "You tend to retry similar asks when the answer misses. Persistence helps, but repeated phrasing gives the assistant little new signal. Better diagnosis and sharper constraints will improve results quickly.",
67
+ share_color: "#ea580c",
68
+ growth_tips: [
69
+ "Explain what was wrong with the last answer.",
70
+ "Change the constraint, example, or output format.",
71
+ "Quote the part that needs correction."
72
+ ]
73
+ },
74
+ architect: {
75
+ slug: "architect",
76
+ name: "The Architect",
77
+ tagline: "Structured prompts with clear scaffolding.",
78
+ description: "You often use sections, formats, roles, or constraints to guide the assistant. This makes your intent easier to follow and usually improves output quality. The next step is balancing structure with room for useful initiative.",
79
+ share_color: "#16a34a",
80
+ growth_tips: [
81
+ "Keep sections short and purposeful.",
82
+ "Specify acceptance criteria for complex tasks.",
83
+ "Invite the assistant to surface blockers."
84
+ ]
85
+ },
86
+ explorer: {
87
+ slug: "explorer",
88
+ name: "The Explorer",
89
+ tagline: "Open-ended, curious, and iterative.",
90
+ description: "You use the assistant to investigate and expand possibilities. This creates breadth, but the asks can be too loose to evaluate. Convert curiosity into decisions by naming constraints and expected outputs.",
91
+ share_color: "#ca8a04",
92
+ growth_tips: [
93
+ "Define the decision or artifact you need.",
94
+ "Ask for options with tradeoffs.",
95
+ "Close exploration with a synthesis prompt."
96
+ ]
97
+ }
98
+ };
99
+ export function getArchetype(slug) {
100
+ return ARCHETYPES[slug] ?? ARCHETYPES.explorer;
101
+ }
102
+ //# sourceMappingURL=archetypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archetypes.js","sourceRoot":"","sources":["../../src/local/archetypes.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,UAAU,GAA8B;IACnD,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,2CAA2C;QACpD,WAAW,EACT,2OAA2O;QAC7O,WAAW,EAAE,SAAS;QACtB,WAAW,EAAE;YACX,6CAA6C;YAC7C,iDAAiD;YACjD,8CAA8C;SAC/C;KACF;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,0CAA0C;QACnD,WAAW,EACT,oOAAoO;QACtO,WAAW,EAAE,SAAS;QACtB,WAAW,EAAE;YACX,2CAA2C;YAC3C,sCAAsC;YACtC,uCAAuC;SACxC;KACF;IACD,YAAY,EAAE;QACZ,IAAI,EAAE,cAAc;QACpB,IAAI,EAAE,kBAAkB;QACxB,OAAO,EAAE,0CAA0C;QACnD,WAAW,EACT,yMAAyM;QAC3M,WAAW,EAAE,SAAS;QACtB,WAAW,EAAE;YACX,kDAAkD;YAClD,sDAAsD;YACtD,6CAA6C;SAC9C;KACF;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,6BAA6B;QACtC,WAAW,EACT,+OAA+O;QACjP,WAAW,EAAE,SAAS;QACtB,WAAW,EAAE;YACX,8BAA8B;YAC9B,oDAAoD;YACpD,mDAAmD;SACpD;KACF;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,iCAAiC;QAC1C,WAAW,EACT,gOAAgO;QAClO,WAAW,EAAE,SAAS;QACtB,WAAW,EAAE;YACX,+CAA+C;YAC/C,yCAAyC;YACzC,4CAA4C;SAC7C;KACF;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,0CAA0C;QACnD,WAAW,EACT,+MAA+M;QACjN,WAAW,EAAE,SAAS;QACtB,WAAW,EAAE;YACX,8CAA8C;YAC9C,mDAAmD;YACnD,uCAAuC;SACxC;KACF;IACD,SAAS,EAAE;QACT,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,eAAe;QACrB,OAAO,EAAE,4CAA4C;QACrD,WAAW,EACT,mOAAmO;QACrO,WAAW,EAAE,SAAS;QACtB,WAAW,EAAE;YACX,qCAAqC;YACrC,gDAAgD;YAChD,2CAA2C;SAC5C;KACF;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,qCAAqC;QAC9C,WAAW,EACT,8MAA8M;QAChN,WAAW,EAAE,SAAS;QACtB,WAAW,EAAE;YACX,2CAA2C;YAC3C,iCAAiC;YACjC,4CAA4C;SAC7C;KACF;CACF,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC;AACjD,CAAC"}
@@ -0,0 +1,130 @@
1
+ import { ARCHETYPES, getArchetype } from "./archetypes.js";
2
+ import { candidateArchetypes, heuristicDimensions } from "./heuristics.js";
3
+ import { generateLocalJudgment } from "./model.js";
4
+ import { applyStrictness, DIMENSION_WEIGHTS, LOCAL_SYSTEM_PROMPT } from "./rubric.js";
5
+ // Much smaller than the server's 240k-char budget: prompt processing on CPU
6
+ // dominates local latency, and ~24k chars (~6k tokens) keeps a session under
7
+ // a minute on most machines while preserving the same sampling algorithm.
8
+ export const LOCAL_MAX_ANALYSIS_CHARS = 24_000;
9
+ export function truncateSession(session, maxChars) {
10
+ const total = session.turns.reduce((sum, turn) => sum + turn.text.length, 0);
11
+ if (total <= maxChars)
12
+ return { session, wasTruncated: false };
13
+ const turns = session.turns;
14
+ const keep = new Set();
15
+ for (let i = 0; i < Math.min(5, turns.length); i++)
16
+ keep.add(i);
17
+ for (let i = Math.max(0, turns.length - 10); i < turns.length; i++)
18
+ keep.add(i);
19
+ if (turns.length > 15) {
20
+ const middleStart = 5;
21
+ const middleEnd = turns.length - 10;
22
+ const middleSlots = 10;
23
+ const span = Math.max(1, middleEnd - middleStart);
24
+ for (let slot = 0; slot < middleSlots; slot++) {
25
+ keep.add(middleStart +
26
+ Math.min(span - 1, Math.round((slot / Math.max(1, middleSlots - 1)) * (span - 1))));
27
+ }
28
+ }
29
+ const kept = turns.filter((_, index) => keep.has(index));
30
+ return { session: { ...session, turns: kept }, wasTruncated: true };
31
+ }
32
+ export function buildUserPrompt(session, candidates) {
33
+ const archetypeLines = candidates
34
+ .map((slug) => `- ${slug}: ${ARCHETYPES[slug].name} — ${ARCHETYPES[slug].tagline}`)
35
+ .join("\n");
36
+ const turns = session.turns.map((turn) => `[TURN ${turn.index}]\n${turn.text}`).join("\n\n");
37
+ return `Here are the user turns from a session with ${session.target_tool}.
38
+ Total user turns: ${session.turns.length}.
39
+ Candidate archetypes:
40
+ ${archetypeLines}
41
+
42
+ User turns:
43
+ ${turns}`;
44
+ }
45
+ export function normalizeDimensions(parsed, session) {
46
+ const byName = new Map(parsed.dimensions.map((dimension) => [dimension.dimension, dimension]));
47
+ const maxIndex = Math.max(...session.turns.map((turn) => turn.index));
48
+ const fallbackIndex = session.turns[0].index;
49
+ const normalized = [];
50
+ for (const [name, weight] of Object.entries(DIMENSION_WEIGHTS)) {
51
+ const source = byName.get(name);
52
+ let score;
53
+ let feedback;
54
+ let evidence;
55
+ if (!source) {
56
+ score = 50;
57
+ feedback = "The model did not return feedback for this dimension.";
58
+ evidence = [fallbackIndex];
59
+ }
60
+ else {
61
+ score = Math.max(0, Math.min(100, Math.round(source.score)));
62
+ feedback = source.feedback.trim();
63
+ evidence = source.evidence_turn_indices.filter((index) => index >= 0 && index <= maxIndex);
64
+ if (evidence.length === 0)
65
+ evidence = [fallbackIndex];
66
+ }
67
+ normalized.push({ dimension: name, score, weight, feedback, evidence_turn_indices: evidence });
68
+ }
69
+ return normalized;
70
+ }
71
+ export function weightedRawScore(dimensions) {
72
+ return dimensions.reduce((sum, dimension) => sum + dimension.score * dimension.weight, 0);
73
+ }
74
+ export async function analyzeLocally(session, options = {}) {
75
+ const { session: trimmed, wasTruncated } = truncateSession(session, LOCAL_MAX_ANALYSIS_CHARS);
76
+ const candidates = candidateArchetypes(trimmed);
77
+ const prompt = buildUserPrompt(trimmed, candidates);
78
+ let parsed = null;
79
+ let modelUsed = "heuristic";
80
+ let heuristicReason = null;
81
+ try {
82
+ const generation = await generateLocalJudgment({
83
+ systemPrompt: LOCAL_SYSTEM_PROMPT,
84
+ userPrompt: prompt,
85
+ candidates,
86
+ timeoutMs: options.timeoutMs ?? 180_000,
87
+ ...options
88
+ });
89
+ parsed = generation.parsed;
90
+ modelUsed = generation.modelName;
91
+ }
92
+ catch (err) {
93
+ heuristicReason = err instanceof Error ? err.message : String(err);
94
+ }
95
+ let dimensions;
96
+ let summary;
97
+ let archetypeSlug;
98
+ let mode;
99
+ if (parsed) {
100
+ dimensions = normalizeDimensions(parsed, trimmed);
101
+ summary = parsed.summary.trim();
102
+ archetypeSlug = parsed.archetype_slug in ARCHETYPES ? parsed.archetype_slug : candidates[0];
103
+ mode = "model";
104
+ }
105
+ else {
106
+ dimensions = heuristicDimensions(trimmed);
107
+ summary =
108
+ "Heuristic estimate only — the local model was unavailable" +
109
+ (heuristicReason ? ` (${heuristicReason})` : "") +
110
+ ". Run without --local for a calibrated, model-judged score.";
111
+ archetypeSlug = candidates[0];
112
+ mode = "heuristic";
113
+ }
114
+ const rawScore = weightedRawScore(dimensions);
115
+ return {
116
+ kind: "local",
117
+ mode,
118
+ model_used: modelUsed,
119
+ score: applyStrictness(rawScore),
120
+ raw_score: Math.round(rawScore),
121
+ archetype: getArchetype(archetypeSlug),
122
+ dimensions,
123
+ summary,
124
+ was_truncated: wasTruncated,
125
+ target_tool: session.target_tool,
126
+ turn_count: session.turns.length,
127
+ created_at: new Date().toISOString()
128
+ };
129
+ }
130
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/local/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,156 @@
1
+ // Parity tests: the local scoring math must match the backend
2
+ // (apps/backend/app/analysis/{rubric,engine,heuristics}.py) exactly.
3
+ import { describe, expect, it } from "vitest";
4
+ import { buildUserPrompt, normalizeDimensions, truncateSession, weightedRawScore } from "./engine.js";
5
+ import { candidateArchetypes, heuristicDimensions } from "./heuristics.js";
6
+ import { applyStrictness, DIMENSION_WEIGHTS } from "./rubric.js";
7
+ function makeSession(texts) {
8
+ const turns = texts.map((text, index) => ({ index, text }));
9
+ return {
10
+ schema_version: "1.0",
11
+ target_tool: "chatgpt",
12
+ source_client: "cli",
13
+ source_client_version: "test",
14
+ ingested_at: new Date().toISOString(),
15
+ turns
16
+ };
17
+ }
18
+ describe("applyStrictness (server parity)", () => {
19
+ it("matches the server curve at known points", () => {
20
+ // round(raw**1.15 / 100**0.15) — values verified against the Python impl.
21
+ expect(applyStrictness(0)).toBe(0);
22
+ expect(applyStrictness(50)).toBe(45);
23
+ expect(applyStrictness(100)).toBe(100);
24
+ expect(applyStrictness(70)).toBe(66);
25
+ });
26
+ });
27
+ describe("DIMENSION_WEIGHTS", () => {
28
+ it("sums to 1.0 with the server's six dimensions", () => {
29
+ const total = Object.values(DIMENSION_WEIGHTS).reduce((a, b) => a + b, 0);
30
+ expect(total).toBeCloseTo(1.0, 10);
31
+ expect(Object.keys(DIMENSION_WEIGHTS)).toEqual([
32
+ "specificity",
33
+ "context_provision",
34
+ "structure",
35
+ "iteration_quality",
36
+ "scope_discipline",
37
+ "meta_prompting"
38
+ ]);
39
+ });
40
+ });
41
+ describe("truncateSession", () => {
42
+ it("returns untouched session under the budget", () => {
43
+ const session = makeSession(["short", "turns"]);
44
+ const result = truncateSession(session, 1000);
45
+ expect(result.wasTruncated).toBe(false);
46
+ expect(result.session.turns).toHaveLength(2);
47
+ });
48
+ it("keeps first 5 + last 10 + sampled middle when over budget", () => {
49
+ const session = makeSession(Array.from({ length: 30 }, (_, i) => `turn ${i} ${"x".repeat(100)}`));
50
+ const result = truncateSession(session, 500);
51
+ expect(result.wasTruncated).toBe(true);
52
+ const indices = result.session.turns.map((turn) => turn.index);
53
+ // First five and last ten always survive.
54
+ for (const i of [0, 1, 2, 3, 4])
55
+ expect(indices).toContain(i);
56
+ for (const i of [20, 21, 22, 23, 24, 25, 26, 27, 28, 29])
57
+ expect(indices).toContain(i);
58
+ expect(indices.length).toBeLessThanOrEqual(25);
59
+ // Order preserved.
60
+ expect([...indices].sort((a, b) => a - b)).toEqual(indices);
61
+ });
62
+ });
63
+ describe("normalizeDimensions", () => {
64
+ const session = makeSession(["one", "two", "three"]);
65
+ it("fills missing dimensions with score 50 and fallback evidence", () => {
66
+ const parsed = {
67
+ raw_score: 60,
68
+ archetype_slug: "sniper",
69
+ dimensions: [
70
+ { dimension: "specificity", score: 70, feedback: "good", evidence_turn_indices: [1] }
71
+ ],
72
+ summary: "ok"
73
+ };
74
+ const normalized = normalizeDimensions(parsed, session);
75
+ expect(normalized).toHaveLength(6);
76
+ const structure = normalized.find((d) => d.dimension === "structure");
77
+ expect(structure.score).toBe(50);
78
+ expect(structure.evidence_turn_indices).toEqual([0]);
79
+ const specificity = normalized.find((d) => d.dimension === "specificity");
80
+ expect(specificity.score).toBe(70);
81
+ expect(specificity.weight).toBe(0.2);
82
+ });
83
+ it("clamps out-of-range evidence indices to the first turn", () => {
84
+ const parsed = {
85
+ raw_score: 50,
86
+ archetype_slug: "sniper",
87
+ dimensions: [
88
+ { dimension: "specificity", score: 55, feedback: "x", evidence_turn_indices: [99, -1] }
89
+ ],
90
+ summary: "ok"
91
+ };
92
+ const normalized = normalizeDimensions(parsed, session);
93
+ const specificity = normalized.find((d) => d.dimension === "specificity");
94
+ expect(specificity.evidence_turn_indices).toEqual([0]);
95
+ });
96
+ });
97
+ describe("weightedRawScore", () => {
98
+ it("returns the score when all dimensions are equal", () => {
99
+ const session = makeSession(["a"]);
100
+ const parsed = {
101
+ raw_score: 50,
102
+ archetype_slug: "sniper",
103
+ dimensions: [],
104
+ summary: ""
105
+ };
106
+ const normalized = normalizeDimensions(parsed, session); // all 50s
107
+ expect(weightedRawScore(normalized)).toBeCloseTo(50, 10);
108
+ });
109
+ });
110
+ describe("candidateArchetypes (server parity)", () => {
111
+ it("flags a short two-turn session as sniper", () => {
112
+ const session = makeSession(["fix this bug", "thanks"]);
113
+ expect(candidateArchetypes(session)[0]).toBe("sniper");
114
+ });
115
+ it("flags a huge single opener as firehose", () => {
116
+ const session = makeSession(["x".repeat(2000)]);
117
+ expect(candidateArchetypes(session)).toContain("firehose");
118
+ });
119
+ it("flags structured prompts as architect", () => {
120
+ const session = makeSession([
121
+ "Role: senior dev\nConstraints: keep it short\nOutput: a list",
122
+ "format: table please"
123
+ ]);
124
+ expect(candidateArchetypes(session)).toContain("architect");
125
+ });
126
+ it("always includes the baseline spread, capped at 5", () => {
127
+ const session = makeSession(["hello"]);
128
+ const candidates = candidateArchetypes(session);
129
+ expect(candidates.length).toBeLessThanOrEqual(5);
130
+ for (const slug of ["sniper", "architect", "sculptor"]) {
131
+ expect(candidates).toContain(slug);
132
+ }
133
+ });
134
+ });
135
+ describe("heuristicDimensions", () => {
136
+ it("returns all six dimensions clamped to 30-65", () => {
137
+ const session = makeSession(["short ask", "another", "ok"]);
138
+ const dims = heuristicDimensions(session);
139
+ expect(dims).toHaveLength(6);
140
+ for (const dim of dims) {
141
+ expect(dim.score).toBeGreaterThanOrEqual(30);
142
+ expect(dim.score).toBeLessThanOrEqual(65);
143
+ }
144
+ });
145
+ });
146
+ describe("buildUserPrompt", () => {
147
+ it("renders [TURN n] blocks and candidate lines", () => {
148
+ const session = makeSession(["first ask", "second ask"]);
149
+ const prompt = buildUserPrompt(session, ["sniper", "explorer"]);
150
+ expect(prompt).toContain("[TURN 0]\nfirst ask");
151
+ expect(prompt).toContain("[TURN 1]\nsecond ask");
152
+ expect(prompt).toContain("- sniper: The Sniper");
153
+ expect(prompt).toContain("Total user turns: 2");
154
+ });
155
+ });
156
+ //# sourceMappingURL=engine.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.test.js","sourceRoot":"","sources":["../../src/local/engine.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,127 @@
1
+ import { DIMENSION_WEIGHTS } from "./rubric.js";
2
+ export function candidateArchetypes(session) {
3
+ const turns = session.turns.map((turn) => turn.text);
4
+ const turnCount = turns.length;
5
+ const lengths = turns.map((text) => text.length);
6
+ const avgLen = lengths.reduce((a, b) => a + b, 0) / Math.max(1, turnCount);
7
+ const firstLen = lengths[0] ?? 0;
8
+ const joined = turns.join("\n").toLowerCase();
9
+ const markers = [
10
+ "\n#",
11
+ "\n-",
12
+ "\n1.",
13
+ "format:",
14
+ "role:",
15
+ "constraints:",
16
+ "output:",
17
+ "acceptance criteria",
18
+ "edge case",
19
+ "tradeoff",
20
+ "deliverable"
21
+ ];
22
+ const structuredMarkers = markers.reduce((sum, marker) => sum + countOccurrences(joined, marker), 0);
23
+ const repeatedTerms = repeatedOpeningTerms(turns);
24
+ const candidates = [];
25
+ if (turnCount <= 2 && avgLen < 500)
26
+ candidates.push("sniper");
27
+ if (firstLen > 1800 && turnCount <= 3)
28
+ candidates.push("firehose");
29
+ if (structuredMarkers >= 2)
30
+ candidates.push("architect");
31
+ if (turnCount >= 7 && avgLen < 500)
32
+ candidates.push("interrogator");
33
+ if (turnCount >= 4 && repeatedTerms >= 2)
34
+ candidates.push("repeater");
35
+ if ((turnCount >= 4 && avgLen >= 650) || (turnCount >= 2 && firstLen >= 400 && structuredMarkers >= 1)) {
36
+ candidates.push("sculptor");
37
+ }
38
+ if (turnCount >= 5 && topicDriftSignal(joined))
39
+ candidates.push("wanderer");
40
+ for (const fallback of ["architect", "sculptor", "sniper", "explorer"]) {
41
+ if (!candidates.includes(fallback))
42
+ candidates.push(fallback);
43
+ }
44
+ return [...new Set(candidates)].slice(0, 5);
45
+ }
46
+ export function repeatedOpeningTerms(turns) {
47
+ const firstWords = [];
48
+ for (const turn of turns) {
49
+ const words = turn
50
+ .split(/\s+/)
51
+ .slice(0, 8)
52
+ .map((word) => word.replace(/^[.,!?;:]+|[.,!?;:]+$/g, "").toLowerCase());
53
+ firstWords.push(...words.filter((word) => word.length > 4));
54
+ }
55
+ const counts = new Map();
56
+ for (const word of firstWords)
57
+ counts.set(word, (counts.get(word) ?? 0) + 1);
58
+ let repeated = 0;
59
+ for (const count of counts.values())
60
+ if (count >= 3)
61
+ repeated += 1;
62
+ return repeated;
63
+ }
64
+ export function topicDriftSignal(text) {
65
+ const domains = [
66
+ ["code", "bug", "api", "test", "function"],
67
+ ["write", "essay", "copy", "email", "headline"],
68
+ ["plan", "trip", "hotel", "flight", "itinerary"],
69
+ ["recipe", "meal", "cook", "ingredient"],
70
+ ["legal", "contract", "policy", "clause"]
71
+ ];
72
+ const hits = domains.reduce((sum, words) => sum + (words.some((word) => text.includes(word)) ? 1 : 0), 0);
73
+ return hits >= 3;
74
+ }
75
+ function countOccurrences(haystack, needle) {
76
+ let count = 0;
77
+ let index = haystack.indexOf(needle);
78
+ while (index !== -1) {
79
+ count += 1;
80
+ index = haystack.indexOf(needle, index + needle.length);
81
+ }
82
+ return count;
83
+ }
84
+ /**
85
+ * Heuristic-only dimension scores for when no model is available. Derived
86
+ * from the same signals the candidate heuristics use, clamped to 30–65 so a
87
+ * guess never masquerades as a confident judgment in either direction.
88
+ */
89
+ export function heuristicDimensions(session) {
90
+ const turns = session.turns.map((turn) => turn.text);
91
+ const turnCount = turns.length;
92
+ const lengths = turns.map((text) => text.length);
93
+ const avgLen = lengths.reduce((a, b) => a + b, 0) / Math.max(1, turnCount);
94
+ const firstLen = lengths[0] ?? 0;
95
+ const joined = turns.join("\n").toLowerCase();
96
+ const firstIndex = session.turns[0]?.index ?? 0;
97
+ const structuredMarkers = [
98
+ "\n#",
99
+ "\n-",
100
+ "\n1.",
101
+ "format:",
102
+ "role:",
103
+ "constraints:",
104
+ "output:",
105
+ "acceptance criteria"
106
+ ].reduce((sum, marker) => sum + countOccurrences(joined, marker), 0);
107
+ const metaMarkers = ["format", "role", "act as", "respond in", "output", "step by step", "concise"].reduce((sum, marker) => sum + countOccurrences(joined, marker), 0);
108
+ const repeated = repeatedOpeningTerms(turns);
109
+ const drift = topicDriftSignal(joined);
110
+ const clamp = (value) => Math.max(30, Math.min(65, Math.round(value)));
111
+ const scores = {
112
+ specificity: clamp(35 + Math.min(20, avgLen / 60)),
113
+ context_provision: clamp(32 + Math.min(25, firstLen / 80)),
114
+ structure: clamp(34 + structuredMarkers * 6),
115
+ iteration_quality: clamp(50 - repeated * 7 + (turnCount >= 3 ? 5 : 0)),
116
+ scope_discipline: clamp(drift ? 36 : 54),
117
+ meta_prompting: clamp(33 + metaMarkers * 5)
118
+ };
119
+ return Object.keys(DIMENSION_WEIGHTS).map((dimension) => ({
120
+ dimension,
121
+ score: scores[dimension],
122
+ weight: DIMENSION_WEIGHTS[dimension],
123
+ feedback: "Heuristic estimate — run without --local for model-judged feedback.",
124
+ evidence_turn_indices: [firstIndex]
125
+ }));
126
+ }
127
+ //# sourceMappingURL=heuristics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heuristics.js","sourceRoot":"","sources":["../../src/local/heuristics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,313 @@
1
+ // Local-model runner for `rate --local`. Two engines, both optional deps
2
+ // loaded via dynamic import (same pattern as keytar) so a failed native
3
+ // install never breaks the core CLI:
4
+ // - "llama" → node-llama-cpp + GGUF. Has GBNF grammar enforcement,
5
+ // but its bundled llama.cpp predates the gemma4 arch.
6
+ // - "transformers" → @huggingface/transformers (ONNX Runtime). Runs the
7
+ // official onnx-community Gemma 4 exports today; no
8
+ // grammar, so output is parsed defensively instead.
9
+ import { totalmem } from "node:os";
10
+ import kleur from "kleur";
11
+ import prompts from "prompts";
12
+ import { readLocalConfig, writeLocalConfig, getModelsDir } from "../lib/config.js";
13
+ import { DIMENSION_NAMES } from "./rubric.js";
14
+ // Ungated repos so no Hugging Face account is needed. Override with
15
+ // RUBRIC_LOCAL_MODEL_URI if a repo moves or you want a different quant.
16
+ export const MODEL_REGISTRY = {
17
+ // Default. Gemma 4 E2B via the official onnx-community export — verified
18
+ // working on @huggingface/transformers 4.2.0 (the GGUF path is blocked
19
+ // until node-llama-cpp ships a llama.cpp with gemma4 support).
20
+ "gemma-4-e2b": {
21
+ name: "gemma-4-e2b",
22
+ engine: "transformers",
23
+ uri: "onnx-community/gemma-4-E2B-it-ONNX",
24
+ dtype: "q4f16",
25
+ approxDownload: "~3.2 GB",
26
+ approxRamGb: 5,
27
+ licenseLine: "Gemma 4 — Apache 2.0 (Google)"
28
+ },
29
+ "gemma-4-e4b": {
30
+ name: "gemma-4-e4b",
31
+ engine: "transformers",
32
+ uri: "onnx-community/gemma-4-E4B-it-ONNX",
33
+ dtype: "q4f16",
34
+ approxDownload: "~5 GB",
35
+ approxRamGb: 8,
36
+ licenseLine: "Gemma 4 — Apache 2.0 (Google)"
37
+ },
38
+ "gemma-3-4b": {
39
+ name: "gemma-3-4b",
40
+ engine: "llama",
41
+ // Google's QAT Q4_0 — markedly better than post-hoc Q4 at the same size.
42
+ uri: "hf:ggml-org/gemma-3-4b-it-qat-GGUF/gemma-3-4b-it-qat-Q4_0.gguf",
43
+ approxDownload: "~2.5 GB",
44
+ approxRamGb: 4,
45
+ licenseLine: "Gemma 3 — Gemma Terms of Use (ai.google.dev/gemma/terms)"
46
+ },
47
+ "gemma-3-1b": {
48
+ name: "gemma-3-1b",
49
+ engine: "llama",
50
+ uri: "hf:ggml-org/gemma-3-1b-it-GGUF:Q4_K_M",
51
+ approxDownload: "~0.8 GB",
52
+ approxRamGb: 2,
53
+ licenseLine: "Gemma 3 — Gemma Terms of Use (ai.google.dev/gemma/terms)"
54
+ }
55
+ };
56
+ export function defaultModelName() {
57
+ const ramGb = totalmem() / 1024 ** 3;
58
+ return ramGb >= 8 ? "gemma-4-e2b" : "gemma-3-1b";
59
+ }
60
+ export async function generateLocalJudgment(args) {
61
+ const spec = resolveSpec(args.modelName);
62
+ const config = await readLocalConfig();
63
+ const firstRun = !config.downloadedModels?.includes(spec.name);
64
+ if (firstRun) {
65
+ await confirmDownload(spec, args);
66
+ }
67
+ // Small models drift to 0-5 star-rating scales and bare-label feedback —
68
+ // anchor both right at the end of the prompt where attention is strongest.
69
+ const scaleReminder = "\n\nRemember: every score (raw_score and each dimension score) is an integer from 0 to 100. " +
70
+ "A typical competent session lands between 45 and 55 per dimension. Never use a 0-5 or 0-10 scale. " +
71
+ "Each dimension's feedback must be 1-2 full sentences describing what the user concretely did " +
72
+ "and how to improve — never a bare label like \"Good\".";
73
+ // The llama engine enforces shape with a grammar; the transformers engine
74
+ // has no grammar, so the shape must be spelled out in the prompt.
75
+ const shapeInstructions = spec.engine === "transformers"
76
+ ? "\n\nOutput ONLY a JSON object, no markdown fences, no prose, with exactly this shape:\n" +
77
+ `{"raw_score": <int>, "archetype_slug": "<one of: ${args.candidates.join(", ")}>", ` +
78
+ `"dimensions": [one entry for EACH of ${DIMENSION_NAMES.join(", ")}: ` +
79
+ `{"dimension": "<name>", "score": <int>, "feedback": "<1-2 sentences>", "evidence_turn_indices": [<int>]}], ` +
80
+ `"summary": "<2-3 sentence verdict>"}`
81
+ : "";
82
+ const fullPrompt = args.userPrompt + scaleReminder + shapeInstructions;
83
+ const runner = spec.engine === "transformers"
84
+ ? await makeTransformersRunner(spec, args, fullPrompt)
85
+ : await makeLlamaRunner(spec, args, fullPrompt);
86
+ try {
87
+ if (firstRun) {
88
+ // The runner factory finished the download — remember it so the next
89
+ // run skips the consent prompt.
90
+ await writeLocalConfig({
91
+ ...config,
92
+ localModel: spec.name,
93
+ downloadedModels: [...(config.downloadedModels ?? []), spec.name]
94
+ });
95
+ }
96
+ let parsed = await runner.generate(0.1);
97
+ if (isDegenerate(parsed)) {
98
+ // One retry a bit warmer — small models occasionally emit flat,
99
+ // all-identical scores at near-zero temperature.
100
+ parsed = await runner.generate(0.6);
101
+ }
102
+ if (isDegenerate(parsed)) {
103
+ // Still junk after the retry: surface it as a failure so the caller
104
+ // falls back to the clearly-labeled heuristic estimate instead of
105
+ // rendering a fake model judgment.
106
+ throw new Error(`${spec.name} produced degenerate output twice`);
107
+ }
108
+ return { parsed, modelName: spec.name };
109
+ }
110
+ finally {
111
+ await runner.dispose();
112
+ }
113
+ }
114
+ async function makeLlamaRunner(spec, args, fullPrompt) {
115
+ let llamaCpp;
116
+ try {
117
+ llamaCpp = await import("node-llama-cpp");
118
+ }
119
+ catch {
120
+ throw new Error("node-llama-cpp is not installed (it's an optional dependency). " +
121
+ "Reinstall with `npm install -g rubric-chat` and check that native builds aren't being skipped.");
122
+ }
123
+ const modelPath = await llamaCpp.resolveModelFile(process.env.RUBRIC_LOCAL_MODEL_URI ?? spec.uri, { directory: getModelsDir(), cli: !args.quiet });
124
+ const llama = await llamaCpp.getLlama();
125
+ const model = await llama.loadModel({ modelPath });
126
+ const grammar = await llama.createGrammarForJsonSchema(reportJsonSchema(args.candidates));
127
+ const context = await model.createContext();
128
+ const session = new llamaCpp.LlamaChatSession({
129
+ contextSequence: context.getSequence(),
130
+ systemPrompt: args.systemPrompt
131
+ });
132
+ return {
133
+ async generate(temperature) {
134
+ const controller = new AbortController();
135
+ const timer = setTimeout(() => controller.abort(), args.timeoutMs);
136
+ try {
137
+ const raw = await session.prompt(fullPrompt, {
138
+ grammar,
139
+ temperature,
140
+ maxTokens: 1600,
141
+ signal: controller.signal
142
+ });
143
+ return JSON.parse(raw);
144
+ }
145
+ finally {
146
+ clearTimeout(timer);
147
+ }
148
+ },
149
+ async dispose() {
150
+ await model.dispose();
151
+ }
152
+ };
153
+ }
154
+ async function makeTransformersRunner(spec, args, fullPrompt) {
155
+ let tjs;
156
+ try {
157
+ tjs = await import("@huggingface/transformers");
158
+ }
159
+ catch {
160
+ throw new Error("@huggingface/transformers is not installed (it's an optional dependency). " +
161
+ "Reinstall with `npm install -g rubric-chat`.");
162
+ }
163
+ tjs.env.cacheDir = getModelsDir();
164
+ let lastPct = -1;
165
+ const generator = await tjs.pipeline("text-generation", process.env.RUBRIC_LOCAL_MODEL_URI ?? spec.uri, {
166
+ dtype: spec.dtype ?? "q4f16",
167
+ device: "cpu",
168
+ progress_callback: args.quiet
169
+ ? undefined
170
+ : (p) => {
171
+ if (p.status === "progress" && p.file?.includes("_data") && p.progress != null) {
172
+ const pct = Math.floor(p.progress);
173
+ if (pct !== lastPct) {
174
+ lastPct = pct;
175
+ process.stderr.write(`\r downloading ${p.file.split("/").pop()}: ${pct}% `);
176
+ }
177
+ }
178
+ }
179
+ });
180
+ if (!args.quiet && lastPct >= 0)
181
+ process.stderr.write("\n");
182
+ const messages = [
183
+ { role: "system", content: args.systemPrompt },
184
+ { role: "user", content: fullPrompt }
185
+ ];
186
+ return {
187
+ async generate(temperature) {
188
+ const generation = generator(messages, {
189
+ max_new_tokens: 1600,
190
+ do_sample: temperature > 0.2,
191
+ temperature,
192
+ return_full_text: false
193
+ });
194
+ const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error("local generation timed out")), args.timeoutMs));
195
+ const out = await Promise.race([generation, timeout]);
196
+ const generated = out[0]?.generated_text;
197
+ const text = typeof generated === "string"
198
+ ? generated
199
+ : (generated?.at(-1)?.content ?? "");
200
+ return extractReportJson(text);
201
+ },
202
+ async dispose() {
203
+ await generator.dispose?.();
204
+ }
205
+ };
206
+ }
207
+ /** Defensive JSON extraction for engines without grammar enforcement. */
208
+ export function extractReportJson(text) {
209
+ const cleaned = text.replace(/```(?:json)?/gi, "");
210
+ const match = cleaned.match(/\{[\s\S]*\}/);
211
+ if (!match)
212
+ throw new Error("no JSON object in model output");
213
+ const parsed = JSON.parse(match[0]);
214
+ const rawDimensions = Array.isArray(parsed.dimensions) ? parsed.dimensions : [];
215
+ const dimensions = rawDimensions
216
+ .filter((d) => typeof d === "object" && d !== null)
217
+ .map((d) => ({
218
+ dimension: String(d.dimension ?? "")
219
+ .toLowerCase()
220
+ .replace(/[\s-]+/g, "_"),
221
+ score: Number(d.score ?? 0),
222
+ feedback: String(d.feedback ?? ""),
223
+ evidence_turn_indices: Array.isArray(d.evidence_turn_indices)
224
+ ? d.evidence_turn_indices.map(Number).filter(Number.isFinite)
225
+ : [0]
226
+ }));
227
+ return {
228
+ raw_score: Number(parsed.raw_score ?? 0),
229
+ archetype_slug: String(parsed.archetype_slug ?? ""),
230
+ dimensions,
231
+ summary: String(parsed.summary ?? "")
232
+ };
233
+ }
234
+ function resolveSpec(modelName) {
235
+ const name = modelName ?? defaultModelName();
236
+ const spec = MODEL_REGISTRY[name];
237
+ if (!spec) {
238
+ throw new Error(`Unknown local model "${name}". Available: ${Object.keys(MODEL_REGISTRY).join(", ")}`);
239
+ }
240
+ return spec;
241
+ }
242
+ async function confirmDownload(spec, args) {
243
+ if (!args.quiet) {
244
+ console.log("");
245
+ console.log(` Local mode needs the ${kleur.bold(spec.name)} model (${spec.approxDownload} download,`);
246
+ console.log(` ~${spec.approxRamGb} GB RAM while scoring). Cached in ~/.rubric/models/ after the first run.`);
247
+ console.log(` ${kleur.dim(spec.licenseLine)}`);
248
+ console.log("");
249
+ }
250
+ if (args.yes || !process.stdin.isTTY)
251
+ return;
252
+ const response = await prompts({
253
+ type: "confirm",
254
+ name: "ok",
255
+ message: `Download ${spec.name} now?`,
256
+ initial: true
257
+ });
258
+ if (!response.ok) {
259
+ throw new Error("model download declined — rerun with --yes to skip this prompt");
260
+ }
261
+ }
262
+ function reportJsonSchema(candidates) {
263
+ // Each dimension is a fixed-order prefixItem with a `const` name so the
264
+ // grammar forces the model to score all six — small models otherwise emit
265
+ // an empty array and the whole report degrades to 50s.
266
+ const dimensionSlot = (name) => ({
267
+ type: "object",
268
+ properties: {
269
+ dimension: { const: name },
270
+ score: { type: "integer" },
271
+ feedback: { type: "string" },
272
+ evidence_turn_indices: {
273
+ type: "array",
274
+ items: { type: "integer" },
275
+ minItems: 1,
276
+ maxItems: 4
277
+ }
278
+ },
279
+ required: ["dimension", "score", "feedback", "evidence_turn_indices"]
280
+ });
281
+ return {
282
+ type: "object",
283
+ properties: {
284
+ raw_score: { type: "integer" },
285
+ archetype_slug: { enum: candidates },
286
+ dimensions: {
287
+ type: "array",
288
+ prefixItems: DIMENSION_NAMES.map(dimensionSlot),
289
+ minItems: DIMENSION_NAMES.length,
290
+ maxItems: DIMENSION_NAMES.length
291
+ },
292
+ summary: { type: "string" }
293
+ },
294
+ required: ["raw_score", "archetype_slug", "dimensions", "summary"]
295
+ };
296
+ }
297
+ function isDegenerate(parsed) {
298
+ if (parsed.dimensions.length < 4)
299
+ return true;
300
+ if (parsed.dimensions.some((dimension) => !dimension.feedback.trim()))
301
+ return true;
302
+ const scores = parsed.dimensions.map((dimension) => dimension.score);
303
+ // Star-rating drift: everything ≤10 means the model ignored the 0-100 scale.
304
+ if (Math.max(...scores) <= 10)
305
+ return true;
306
+ const uniqueScores = new Set(scores);
307
+ if (uniqueScores.size === 1 && parsed.dimensions.length >= 4)
308
+ return true;
309
+ // Copy-pasted feedback across dimensions is the other tiny-model failure mode.
310
+ const feedbacks = new Set(parsed.dimensions.map((dimension) => dimension.feedback.trim()));
311
+ return feedbacks.size === 1 && parsed.dimensions.length >= 4;
312
+ }
313
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/local/model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ export const DIMENSION_WEIGHTS = {
2
+ specificity: 0.2,
3
+ context_provision: 0.15,
4
+ structure: 0.1,
5
+ iteration_quality: 0.2,
6
+ scope_discipline: 0.15,
7
+ meta_prompting: 0.2
8
+ };
9
+ export const DIMENSION_NAMES = Object.keys(DIMENSION_WEIGHTS);
10
+ export function applyStrictness(raw) {
11
+ // Python's round() is banker's rounding; Math.round rounds half up. The
12
+ // curve almost never lands on an exact .5, so the drift is theoretical.
13
+ const adjusted = Math.pow(raw, 1.15) / Math.pow(100, 0.15);
14
+ return Math.max(0, Math.min(100, Math.round(adjusted)));
15
+ }
16
+ // Adapted from the server SYSTEM_PROMPT for a small on-device judge: same
17
+ // dimensions and calibration, plus explicit score anchors because 2–4B models
18
+ // skew generous. JSON shape instructions are omitted — the GBNF grammar
19
+ // enforces the output shape at the sampler level.
20
+ export const LOCAL_SYSTEM_PROMPT = `
21
+ You are a harsh, calibrated judge of prompting skill. Median competent users score 45-55.
22
+ Scores above 80 require exceptional, professional-grade prompting throughout the session.
23
+
24
+ Calibration anchors — follow them strictly:
25
+ - A bare one-line ask with no context, constraints, or success criteria: 20-35.
26
+ - A clear ask with some context but no output requirements: 40-55.
27
+ - Structured prompts with constraints, examples, and explicit output shape: 60-75.
28
+ - Only flawless professional-grade sessions throughout: 80+.
29
+
30
+ Analyze ONLY the user's turns. Do not assume access to assistant response content.
31
+
32
+ Score these dimensions from 0 to 100:
33
+ - specificity: concrete asks, measurable success criteria, clear desired outcome.
34
+ - context_provision: background, constraints, examples, prior attempts.
35
+ - structure: organized sections, lists, separation of ask from background.
36
+ - iteration_quality: diagnosis and refinement across turns instead of repeating or abandoning.
37
+ - scope_discipline: coherent thread instead of unrelated scattered asks.
38
+ - meta_prompting: instructions on role, format, length, reasoning style, and output shape.
39
+
40
+ For every dimension, cite specific user turn indices as evidence.
41
+ Choose exactly one archetype slug from the provided candidates.
42
+ Keep feedback to 1-2 short sentences per dimension and a 2-3 sentence summary.
43
+ `.trim();
44
+ //# sourceMappingURL=rubric.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rubric.js","sourceRoot":"","sources":["../../src/local/rubric.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,iBAAiB,GAAkC;IAC9D,WAAW,EAAE,GAAG;IAChB,iBAAiB,EAAE,IAAI;IACvB,SAAS,EAAE,GAAG;IACd,iBAAiB,EAAE,GAAG;IACtB,gBAAgB,EAAE,IAAI;IACtB,cAAc,EAAE,GAAG;CACpB,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAoB,CAAC;AAEjF,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,wEAAwE;IACxE,wEAAwE;IACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,0EAA0E;AAC1E,8EAA8E;AAC9E,wEAAwE;AACxE,kDAAkD;AAClD,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;CAuBlC,CAAC,IAAI,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rubric-chat",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "A strict 0–100 score for AI conversations. Auto-discovers Claude Code, Codex CLI, and Cursor sessions; also accepts ChatGPT and Claude.ai exports. Six dimensions, eight archetypes, shareable score card.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -54,7 +54,9 @@
54
54
  "prompts": "^2.4.2"
55
55
  },
56
56
  "optionalDependencies": {
57
- "keytar": "^7.9.0"
57
+ "@huggingface/transformers": "^4.2.0",
58
+ "keytar": "^7.9.0",
59
+ "node-llama-cpp": "^3.18.0"
58
60
  },
59
61
  "devDependencies": {
60
62
  "@types/better-sqlite3": "^7.6.13",