@root-signals/scorable-cli 0.3.0 → 0.4.1
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 +26 -6
- package/dist/auth.d.ts +1 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +21 -55
- package/dist/commands/auth/index.d.ts.map +1 -1
- package/dist/commands/auth/index.js +26 -1
- package/dist/commands/evaluator/generate.d.ts +3 -0
- package/dist/commands/evaluator/generate.d.ts.map +1 -0
- package/dist/commands/evaluator/generate.js +49 -0
- package/dist/commands/judge/generate.d.ts +3 -0
- package/dist/commands/judge/generate.d.ts.map +1 -0
- package/dist/commands/judge/generate.js +98 -0
- package/dist/commands/judge/index.d.ts.map +1 -1
- package/dist/commands/judge/index.js +2 -0
- package/dist/commands/prompt-test/index.js +1 -1
- package/dist/commands/prompt-test/run.d.ts.map +1 -1
- package/dist/commands/prompt-test/run.js +22 -4
- package/dist/commands/skills-add.d.ts +3 -0
- package/dist/commands/skills-add.d.ts.map +1 -0
- package/dist/commands/skills-add.js +29 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -8
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +27 -10
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
</a>
|
|
25
25
|
</p>
|
|
26
26
|
|
|
27
|
+
<p align="center">
|
|
28
|
+
<video src="https://github.com/user-attachments/assets/3475573e-b20c-405b-acc6-ee48709998fa" width="800" autoplay loop muted></video>
|
|
29
|
+
</p>
|
|
30
|
+
|
|
27
31
|
The `scorable` CLI is a command-line tool for interacting with the Scorable API. It lets you manage and execute Judges and Evaluators, view execution logs, and run prompt testing experiments directly from the terminal.
|
|
28
32
|
|
|
29
33
|
Requires **Node.js 20 or higher**.
|
|
@@ -48,7 +52,15 @@ npx @root-signals/scorable-cli judge list
|
|
|
48
52
|
|
|
49
53
|
## Authentication
|
|
50
54
|
|
|
51
|
-
|
|
55
|
+
**Option 1 — Free demo key** (no registration required):
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
scorable auth demo-key
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Creates a temporary key and saves it to `~/.scorable/settings.json`.
|
|
62
|
+
|
|
63
|
+
**Option 2 — Permanent key** from [scorable.ai/register](https://scorable.ai/register):
|
|
52
64
|
|
|
53
65
|
```bash
|
|
54
66
|
# Interactively
|
|
@@ -58,17 +70,25 @@ scorable auth set-key
|
|
|
58
70
|
scorable auth set-key sk-your-api-key
|
|
59
71
|
```
|
|
60
72
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
Alternatively, set it as an environment variable — this takes precedence over the saved key:
|
|
73
|
+
**Option 3 — Environment variable** (takes precedence over saved key):
|
|
64
74
|
|
|
65
75
|
```bash
|
|
66
76
|
export SCORABLE_API_KEY="sk-your-api-key"
|
|
67
77
|
```
|
|
68
78
|
|
|
69
|
-
|
|
79
|
+
The key lookup order is: `SCORABLE_API_KEY` env var → `api_key` in `~/.scorable/settings.json` → `temporary_api_key` in `~/.scorable/settings.json`.
|
|
80
|
+
|
|
81
|
+
## Scorable Skills for AI Coding Agents
|
|
82
|
+
|
|
83
|
+
Install Scorable skills into your project so your AI coding agent (Claude Code, Cursor, etc.) can integrate evaluators automatically:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
scorable skills-add
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Once installed, open your coding agent in your AI powered project and use the prompt:
|
|
70
90
|
|
|
71
|
-
|
|
91
|
+
> "Integrate scorable evaluators"
|
|
72
92
|
|
|
73
93
|
## Judge Management
|
|
74
94
|
|
package/dist/auth.d.ts
CHANGED
|
@@ -5,4 +5,5 @@ export declare function getApiKey(): string | undefined;
|
|
|
5
5
|
export declare function getBaseUrl(): string;
|
|
6
6
|
export declare function getSdkClient(apiKey: string): Scorable;
|
|
7
7
|
export declare function requireApiKey(): Promise<string>;
|
|
8
|
+
export declare function createDemoKey(): Promise<string>;
|
|
8
9
|
//# sourceMappingURL=auth.d.ts.map
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAYlD,wBAAgB,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAStD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CASvE;AAED,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAM9C;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAErD;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAYlD,wBAAgB,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAStD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CASvE;AAED,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAM9C;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAErD;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAQrD;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAarD"}
|
package/dist/auth.js
CHANGED
|
@@ -3,7 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { Scorable } from "@root-signals/scorable";
|
|
5
5
|
import { CliError } from "./types.js";
|
|
6
|
-
import { printError, printInfo
|
|
6
|
+
import { printError, printInfo } from "./output.js";
|
|
7
7
|
function configDir() {
|
|
8
8
|
return join(homedir(), ".scorable");
|
|
9
9
|
}
|
|
@@ -52,60 +52,26 @@ export async function requireApiKey() {
|
|
|
52
52
|
const key = getApiKey();
|
|
53
53
|
if (key)
|
|
54
54
|
return key;
|
|
55
|
-
printError("
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (!answer) {
|
|
71
|
-
printInfo("Aborted. Please set SCORABLE_API_KEY and try again.");
|
|
72
|
-
throw new CliError(1, "Aborted");
|
|
73
|
-
}
|
|
74
|
-
try {
|
|
75
|
-
const resp = await fetch(`${getBaseUrl()}/create-demo-user/`, { method: "POST" });
|
|
76
|
-
if (!resp.ok)
|
|
77
|
-
throw new Error(`HTTP ${resp.status}`);
|
|
78
|
-
const data = (await resp.json());
|
|
79
|
-
const tempKey = data["api_key"];
|
|
80
|
-
if (!tempKey) {
|
|
81
|
-
printError("Temporary key response did not include 'api_key'.");
|
|
82
|
-
throw new CliError(1, "Missing api_key in demo response");
|
|
83
|
-
}
|
|
84
|
-
process.env["SCORABLE_API_KEY"] = tempKey;
|
|
85
|
-
const settings = loadSettings();
|
|
86
|
-
settings["temporary_api_key"] = tempKey;
|
|
87
|
-
if (!saveSettings(settings)) {
|
|
88
|
-
printError("Failed to save temporary API key to ~/.scorable/settings.json");
|
|
89
|
-
throw new CliError(1, "Failed to save temporary API key");
|
|
90
|
-
}
|
|
91
|
-
printSuccess("Temporary API key saved to ~/.scorable/settings.json");
|
|
92
|
-
if (shell.includes("fish")) {
|
|
93
|
-
printInfo("To persist in your shell: set -x SCORABLE_API_KEY <paste_key_here>");
|
|
94
|
-
}
|
|
95
|
-
else {
|
|
96
|
-
printInfo("To persist in your shell: export SCORABLE_API_KEY='<paste_key_here>'");
|
|
97
|
-
}
|
|
98
|
-
return tempKey;
|
|
99
|
-
}
|
|
100
|
-
catch (e) {
|
|
101
|
-
if (e instanceof CliError)
|
|
102
|
-
throw e;
|
|
103
|
-
printError(`Failed to create temporary API key: ${e instanceof Error ? e.message : String(e)}`);
|
|
104
|
-
throw new CliError(1, "Failed to create temporary API key");
|
|
105
|
-
}
|
|
55
|
+
printError("No API key found.");
|
|
56
|
+
printInfo("Get a free demo key: scorable auth demo-key");
|
|
57
|
+
printInfo("Set a permanent key: scorable auth set-key <your-api-key>");
|
|
58
|
+
throw new CliError(1, "Missing API key");
|
|
59
|
+
}
|
|
60
|
+
export async function createDemoKey() {
|
|
61
|
+
try {
|
|
62
|
+
const resp = await fetch(`${getBaseUrl()}/create-demo-user/`, { method: "POST" });
|
|
63
|
+
if (!resp.ok)
|
|
64
|
+
throw new Error(`HTTP ${resp.status}`);
|
|
65
|
+
const data = (await resp.json());
|
|
66
|
+
const tempKey = data["api_key"];
|
|
67
|
+
if (!tempKey)
|
|
68
|
+
throw new Error("Response did not include 'api_key'");
|
|
69
|
+
return tempKey;
|
|
106
70
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
71
|
+
catch (e) {
|
|
72
|
+
if (e instanceof CliError)
|
|
73
|
+
throw e;
|
|
74
|
+
printError(`Failed to create demo key: ${e instanceof Error ? e.message : String(e)}`);
|
|
75
|
+
throw new CliError(1, "Failed to create demo key");
|
|
110
76
|
}
|
|
111
77
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgE3D"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import { loadSettings, saveSettings, createDemoKey } from "../../auth.js";
|
|
2
3
|
import { printSuccess, printError } from "../../output.js";
|
|
3
4
|
import { CliError } from "../../types.js";
|
|
4
5
|
export function registerAuthCommands(program) {
|
|
@@ -38,4 +39,28 @@ export function registerAuthCommands(program) {
|
|
|
38
39
|
}
|
|
39
40
|
printSuccess("API key saved to ~/.scorable/settings.json");
|
|
40
41
|
});
|
|
42
|
+
auth
|
|
43
|
+
.command("demo-key")
|
|
44
|
+
.description("Create a free demo API key and save it to ~/.scorable/settings.json")
|
|
45
|
+
.action(async () => {
|
|
46
|
+
const spinner = ora("Creating demo key...").start();
|
|
47
|
+
try {
|
|
48
|
+
const key = await createDemoKey();
|
|
49
|
+
spinner.stop();
|
|
50
|
+
const settings = loadSettings();
|
|
51
|
+
settings["temporary_api_key"] = key;
|
|
52
|
+
if (!saveSettings(settings)) {
|
|
53
|
+
printError("Failed to save demo key to ~/.scorable/settings.json");
|
|
54
|
+
throw new CliError(1, "Failed to save demo key");
|
|
55
|
+
}
|
|
56
|
+
printSuccess(`Demo key saved to ~/.scorable/settings.json`);
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
spinner.stop();
|
|
60
|
+
if (e instanceof CliError)
|
|
61
|
+
throw e;
|
|
62
|
+
printError(`Failed to create demo key: ${e instanceof Error ? e.message : String(e)}`);
|
|
63
|
+
throw new CliError(1, "Failed to create demo key");
|
|
64
|
+
}
|
|
65
|
+
});
|
|
41
66
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/evaluator/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAwDhE"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import { requireApiKey } from "../../auth.js";
|
|
3
|
+
import { apiRequest } from "../../client.js";
|
|
4
|
+
import { printSuccess, printError, printJson } from "../../output.js";
|
|
5
|
+
import { CliError } from "../../types.js";
|
|
6
|
+
export function registerGenerateCommand(evaluator) {
|
|
7
|
+
evaluator
|
|
8
|
+
.command("generate")
|
|
9
|
+
.description("Generate an evaluator from scored examples using AI")
|
|
10
|
+
.requiredOption("--examples <json>", 'JSON array of scored examples, e.g. \'[{"response":"Paris","score":1.0}]\'')
|
|
11
|
+
.option("--evaluation-approach <value>", "Evaluation approach (default: pointwise_score)", "pointwise_score")
|
|
12
|
+
.action(async (opts) => {
|
|
13
|
+
const apiKey = await requireApiKey();
|
|
14
|
+
let examples;
|
|
15
|
+
try {
|
|
16
|
+
examples = JSON.parse(opts.examples);
|
|
17
|
+
if (!Array.isArray(examples) || examples.length === 0) {
|
|
18
|
+
throw new Error("Must be a non-empty array");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
printError(`Invalid --examples JSON: ${e instanceof Error ? e.message : String(e)}`);
|
|
23
|
+
throw new CliError(1, "Invalid examples JSON");
|
|
24
|
+
}
|
|
25
|
+
const spinner = ora("Generating evaluator (this may take a moment)...").start();
|
|
26
|
+
try {
|
|
27
|
+
const result = (await apiRequest("POST", "v1/beta/evaluation-agents/generate", {
|
|
28
|
+
payload: {
|
|
29
|
+
examples,
|
|
30
|
+
evaluation_approach: opts.evaluationApproach,
|
|
31
|
+
},
|
|
32
|
+
apiKey,
|
|
33
|
+
}));
|
|
34
|
+
spinner.stop();
|
|
35
|
+
if (!result) {
|
|
36
|
+
throw new CliError(1, "Generate request failed");
|
|
37
|
+
}
|
|
38
|
+
printSuccess("Evaluator generated successfully!");
|
|
39
|
+
printJson(result);
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
spinner.stop();
|
|
43
|
+
if (e instanceof CliError)
|
|
44
|
+
throw e;
|
|
45
|
+
printError(`Failed to generate evaluator: ${e instanceof Error ? e.message : String(e)}`);
|
|
46
|
+
throw new CliError(1, "Failed to generate evaluator");
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CA4H5D"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { requireApiKey, getSdkClient } from "../../auth.js";
|
|
4
|
+
import { printSuccess, printError, printWarning, printInfo, printJson, handleSdkError, } from "../../output.js";
|
|
5
|
+
import { CliError } from "../../types.js";
|
|
6
|
+
export function registerGenerateCommand(judge) {
|
|
7
|
+
judge
|
|
8
|
+
.command("generate")
|
|
9
|
+
.description("Generate a judge from an intent description using AI")
|
|
10
|
+
.addHelpText("after", `
|
|
11
|
+
Example:
|
|
12
|
+
$ scorable judge generate \\
|
|
13
|
+
--intent "I am building a customer support chatbot for a hotel chain. \\
|
|
14
|
+
I want to evaluate that responses are helpful, accurate, and reflect our brand tone. \\
|
|
15
|
+
The chatbot must never make promises about pricing or availability, and should always \\
|
|
16
|
+
offer to connect the guest with a human agent when uncertain."`)
|
|
17
|
+
.requiredOption("--intent <text>", "Detailed description of what the judge should evaluate")
|
|
18
|
+
.option("--visibility <value>", "Visibility of the generated judge: private, public", "private")
|
|
19
|
+
.option("--name <name>", "Optional name for the generated judge")
|
|
20
|
+
.option("--stage <stage>", "Stage name for the judge")
|
|
21
|
+
.option("--reasoning-effort <value>", "Reasoning effort for generation: off, low, medium, high")
|
|
22
|
+
.option("--judge-id <id>", "ID of an existing judge to regenerate")
|
|
23
|
+
.option("--overwrite", "Overwrite existing judge with the same name", false)
|
|
24
|
+
.option("--extra-contexts <json>", 'JSON object of additional context key-value pairs, e.g. \'{"Tone Of Voice":"formal","Domain":"hotel chatbot"}\'')
|
|
25
|
+
.option("--context-aware", "Enable context-aware evaluators for RAG applications (hallucination detection, context drift, etc.)", false)
|
|
26
|
+
.action(async (opts) => {
|
|
27
|
+
const acceptedVisibilities = ["private", "public"];
|
|
28
|
+
const visibility = opts.visibility;
|
|
29
|
+
if (!acceptedVisibilities.includes(visibility)) {
|
|
30
|
+
printError(`Invalid --visibility value: "${visibility}". Accepted values: private, public.`);
|
|
31
|
+
throw new CliError(1, "invalid_visibility");
|
|
32
|
+
}
|
|
33
|
+
const apiVisibility = visibility === "private" ? "unlisted" : "public";
|
|
34
|
+
const apiKey = await requireApiKey();
|
|
35
|
+
let extra_contexts;
|
|
36
|
+
if (opts.extraContexts) {
|
|
37
|
+
try {
|
|
38
|
+
extra_contexts = JSON.parse(opts.extraContexts);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
printError("Invalid --extra-contexts JSON: must be a key-value object.");
|
|
42
|
+
throw new CliError(1, "Invalid extra-contexts JSON");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const spinner = ora("Generating judge (this may take a moment)...").start();
|
|
46
|
+
try {
|
|
47
|
+
const client = getSdkClient(apiKey);
|
|
48
|
+
const result = await client.judges.generate({
|
|
49
|
+
intent: opts.intent,
|
|
50
|
+
visibility: apiVisibility,
|
|
51
|
+
overwrite: opts.overwrite,
|
|
52
|
+
name: opts.name,
|
|
53
|
+
stage: opts.stage,
|
|
54
|
+
judge_id: opts.judgeId,
|
|
55
|
+
extra_contexts: extra_contexts ?? null,
|
|
56
|
+
enable_context_aware_evaluators: opts.contextAware || undefined,
|
|
57
|
+
...(opts.reasoningEffort && {
|
|
58
|
+
generating_model_params: {
|
|
59
|
+
reasoning_effort: opts.reasoningEffort,
|
|
60
|
+
},
|
|
61
|
+
}),
|
|
62
|
+
});
|
|
63
|
+
spinner.stop();
|
|
64
|
+
if (result.error_code === "multiple_stages") {
|
|
65
|
+
printWarning("Multiple evaluation stages detected. Each judge covers one stage.");
|
|
66
|
+
printInfo("Detected stages:");
|
|
67
|
+
for (const s of result.stages ?? []) {
|
|
68
|
+
console.log(` ${chalk.cyan("·")} ${s}`);
|
|
69
|
+
}
|
|
70
|
+
printInfo('Re-run with --stage "<stage name>" to target a specific stage.');
|
|
71
|
+
throw new CliError(1, "multiple_stages");
|
|
72
|
+
}
|
|
73
|
+
if (result.error_code) {
|
|
74
|
+
printError(`Generation failed with error code: ${result.error_code}`);
|
|
75
|
+
throw new CliError(1, result.error_code ?? "generation_failed");
|
|
76
|
+
}
|
|
77
|
+
const missingContext = result.missing_context_from_system_goal;
|
|
78
|
+
if (missingContext?.length) {
|
|
79
|
+
printSuccess("Judge created — but additional context would improve it:");
|
|
80
|
+
console.log();
|
|
81
|
+
for (const field of missingContext) {
|
|
82
|
+
console.log(` ${chalk.bold(field.form_field_name)}`);
|
|
83
|
+
console.log(` ${chalk.dim(field.form_field_description)}`);
|
|
84
|
+
console.log();
|
|
85
|
+
}
|
|
86
|
+
printInfo(`Re-run with --judge-id "${result.judge_id}" and --extra-contexts to refine.`);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
printSuccess("Judge generated successfully!");
|
|
90
|
+
}
|
|
91
|
+
printJson(result);
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
spinner.stop();
|
|
95
|
+
handleSdkError(e);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/judge/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgB5D"}
|
|
@@ -7,6 +7,7 @@ import { registerDeleteCommand } from "./delete.js";
|
|
|
7
7
|
import { registerExecuteCommand } from "./execute.js";
|
|
8
8
|
import { registerExecuteByNameCommand } from "./execute-by-name.js";
|
|
9
9
|
import { registerDuplicateCommand } from "./duplicate.js";
|
|
10
|
+
import { registerGenerateCommand } from "./generate.js";
|
|
10
11
|
import { registerExecOpenaiCommand } from "./exec-openai.js";
|
|
11
12
|
import { registerExecOpenaiGenericCommand } from "./exec-openai-generic.js";
|
|
12
13
|
export function registerJudgeCommands(program) {
|
|
@@ -19,6 +20,7 @@ export function registerJudgeCommands(program) {
|
|
|
19
20
|
registerExecuteCommand(judge);
|
|
20
21
|
registerExecuteByNameCommand(judge);
|
|
21
22
|
registerDuplicateCommand(judge);
|
|
23
|
+
registerGenerateCommand(judge);
|
|
22
24
|
registerExecOpenaiCommand(judge);
|
|
23
25
|
registerExecOpenaiGenericCommand(judge);
|
|
24
26
|
program.addCommand(judge);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/commands/prompt-test/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/commands/prompt-test/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6GpC,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,UAAU,EAAE,MAAM,EAClB,KAAK,GAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAiD,GACnF,OAAO,CAAC,IAAI,CAAC,CAiHf;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAYpD"}
|
|
@@ -1,9 +1,27 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import yaml from "js-yaml";
|
|
3
|
+
import chalk from "chalk";
|
|
3
4
|
import Table from "cli-table3";
|
|
4
5
|
import { requireApiKey } from "../../auth.js";
|
|
5
6
|
import { apiRequest } from "../../client.js";
|
|
6
7
|
import { printInfo, printSuccess, printWarning, printError, handleSdkError } from "../../output.js";
|
|
8
|
+
const UNICODE_CHARS = {
|
|
9
|
+
top: "─",
|
|
10
|
+
"top-mid": "┬",
|
|
11
|
+
"top-left": "┌",
|
|
12
|
+
"top-right": "┐",
|
|
13
|
+
bottom: "─",
|
|
14
|
+
"bottom-mid": "┴",
|
|
15
|
+
"bottom-left": "└",
|
|
16
|
+
"bottom-right": "┘",
|
|
17
|
+
left: "│",
|
|
18
|
+
"left-mid": "├",
|
|
19
|
+
mid: "─",
|
|
20
|
+
"mid-mid": "┼",
|
|
21
|
+
right: "│",
|
|
22
|
+
"right-mid": "┤",
|
|
23
|
+
middle: "│",
|
|
24
|
+
};
|
|
7
25
|
import { CliError } from "../../types.js";
|
|
8
26
|
function isPromptTestComplete(exp) {
|
|
9
27
|
if (!exp.tasks.length)
|
|
@@ -12,8 +30,8 @@ function isPromptTestComplete(exp) {
|
|
|
12
30
|
}
|
|
13
31
|
function displayProgressTable(experiments) {
|
|
14
32
|
const table = new Table({
|
|
15
|
-
head: ["Prompt Test ID", "Status", "Tasks Completed"],
|
|
16
|
-
|
|
33
|
+
head: ["Prompt Test ID", "Status", "Tasks Completed"].map((h) => chalk.bold.cyan(h)),
|
|
34
|
+
chars: UNICODE_CHARS,
|
|
17
35
|
});
|
|
18
36
|
for (const exp of experiments) {
|
|
19
37
|
const done = exp.tasks.filter((t) => t.status === "completed" || t.status === "failed").length;
|
|
@@ -48,8 +66,8 @@ function displayAggregatedResults(experiments) {
|
|
|
48
66
|
"Latency (s)",
|
|
49
67
|
"Output",
|
|
50
68
|
...sortedEvalIds.map((id) => allEvaluators.get(id)),
|
|
51
|
-
],
|
|
52
|
-
|
|
69
|
+
].map((h) => chalk.bold.cyan(h)),
|
|
70
|
+
chars: UNICODE_CHARS,
|
|
53
71
|
wordWrap: true,
|
|
54
72
|
});
|
|
55
73
|
for (const exp of experiments) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills-add.d.ts","sourceRoot":"","sources":["../../src/commands/skills-add.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0B/D"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { CliError } from "../types.js";
|
|
3
|
+
import { printSuccess, printInfo } from "../output.js";
|
|
4
|
+
export function registerSkillsAddCommand(program) {
|
|
5
|
+
program
|
|
6
|
+
.command("skills-add")
|
|
7
|
+
.description("Install Scorable Skills for coding agents")
|
|
8
|
+
.action(async () => {
|
|
9
|
+
await new Promise((resolve, reject) => {
|
|
10
|
+
const child = spawn("npx", ["skills", "add", "root-signals/scorable-skills"], {
|
|
11
|
+
stdio: "inherit",
|
|
12
|
+
});
|
|
13
|
+
child.on("close", (code) => {
|
|
14
|
+
if (code !== 0) {
|
|
15
|
+
reject(new CliError(code ?? 1, `skills add exited with code ${code}`));
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
resolve();
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
child.on("error", (err) => {
|
|
22
|
+
reject(new CliError(1, `Failed to run npx skills: ${err.message}`));
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
console.log();
|
|
26
|
+
printSuccess("Scorable skills installed.");
|
|
27
|
+
printInfo('Open your coding agent in your AI powered project and use the prompt: "Integrate scorable evaluators"');
|
|
28
|
+
});
|
|
29
|
+
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgDpC,wBAAgB,SAAS,IAAI,OAAO,CAoBnC"}
|
package/dist/index.js
CHANGED
|
@@ -3,30 +3,64 @@ import { realpathSync } from "node:fs";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { Command } from "commander";
|
|
6
|
+
import chalk from "chalk";
|
|
6
7
|
import { registerJudgeCommands } from "./commands/judge/index.js";
|
|
7
8
|
import { registerPromptTestCommands } from "./commands/prompt-test/index.js";
|
|
8
9
|
import { registerEvaluatorCommands } from "./commands/evaluator/index.js";
|
|
9
10
|
import { registerExecutionLogCommands } from "./commands/execution-log/index.js";
|
|
10
11
|
import { registerAuthCommands } from "./commands/auth/index.js";
|
|
12
|
+
import { registerSkillsAddCommand } from "./commands/skills-add.js";
|
|
11
13
|
const { version } = createRequire(import.meta.url)("../package.json");
|
|
14
|
+
function buildBanner(ver) {
|
|
15
|
+
const logo = chalk.hex("#4D9FFF");
|
|
16
|
+
const name = chalk.bold.hex("#4D9FFF")("scorable");
|
|
17
|
+
const tag = chalk.dim(`v${ver}`);
|
|
18
|
+
const tagline = chalk.dim("Measurement & Control for LLM Automations");
|
|
19
|
+
const line = chalk.dim("─".repeat(52));
|
|
20
|
+
return [
|
|
21
|
+
"",
|
|
22
|
+
` ${logo("┌─┐")} ${name} ${tag}`,
|
|
23
|
+
` ${logo("╰◈╯")} ${tagline}`,
|
|
24
|
+
` ${line}`,
|
|
25
|
+
].join("\n");
|
|
26
|
+
}
|
|
27
|
+
function buildGettingStarted() {
|
|
28
|
+
const header = chalk.bold("Getting started");
|
|
29
|
+
const line = chalk.dim("─".repeat(52));
|
|
30
|
+
const num = (n) => chalk.dim(n + ".");
|
|
31
|
+
const cmd = (s) => chalk.cyan(s);
|
|
32
|
+
return [
|
|
33
|
+
"",
|
|
34
|
+
` ${header}`,
|
|
35
|
+
` ${line}`,
|
|
36
|
+
` ${num("1")} Authenticate ${chalk.dim("(pick one)")}:`,
|
|
37
|
+
` ${cmd("$ scorable auth demo-key")} ${chalk.dim("# for quick testing")}`,
|
|
38
|
+
` ${cmd("$ scorable auth set-key <api-key>")} ${chalk.dim("# permanent key")}`,
|
|
39
|
+
` ${num("2")} Install skills for your AI coding agent:`,
|
|
40
|
+
` ${cmd("$ scorable skills-add")}`,
|
|
41
|
+
` ${num("3")} Explore resources:`,
|
|
42
|
+
` ${cmd("$ scorable judge list")}`,
|
|
43
|
+
` ${cmd("$ scorable evaluator list")}`,
|
|
44
|
+
"",
|
|
45
|
+
].join("\n");
|
|
46
|
+
}
|
|
12
47
|
export function createCli() {
|
|
13
48
|
const program = new Command()
|
|
14
49
|
.name("scorable")
|
|
15
50
|
.description("A CLI tool to interact with the Scorable API")
|
|
16
51
|
.version(version, "-V, --version", "Print version number")
|
|
17
|
-
.addHelpText("
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
$ scorable judge list`)
|
|
24
|
-
.exitOverride();
|
|
52
|
+
.addHelpText("before", buildBanner(version))
|
|
53
|
+
.addHelpText("after", buildGettingStarted())
|
|
54
|
+
.exitOverride()
|
|
55
|
+
.action(() => {
|
|
56
|
+
program.outputHelp();
|
|
57
|
+
});
|
|
25
58
|
registerJudgeCommands(program);
|
|
26
59
|
registerPromptTestCommands(program);
|
|
27
60
|
registerEvaluatorCommands(program);
|
|
28
61
|
registerExecutionLogCommands(program);
|
|
29
62
|
registerAuthCommands(program);
|
|
63
|
+
registerSkillsAddCommand(program);
|
|
30
64
|
return program;
|
|
31
65
|
}
|
|
32
66
|
if (realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1])) {
|
package/dist/output.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAoBzF,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAE7C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,KAAK,CAKhD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE9C;AAMD,wBAAgB,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAmB1E;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,iBAAiB,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAmB9F;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAoB1F"}
|
package/dist/output.js
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import Table from "cli-table3";
|
|
3
3
|
import { CliError } from "./types.js";
|
|
4
|
+
const UNICODE_CHARS = {
|
|
5
|
+
top: "─",
|
|
6
|
+
"top-mid": "┬",
|
|
7
|
+
"top-left": "┌",
|
|
8
|
+
"top-right": "┐",
|
|
9
|
+
bottom: "─",
|
|
10
|
+
"bottom-mid": "┴",
|
|
11
|
+
"bottom-left": "└",
|
|
12
|
+
"bottom-right": "┘",
|
|
13
|
+
left: "│",
|
|
14
|
+
"left-mid": "├",
|
|
15
|
+
mid: "─",
|
|
16
|
+
"mid-mid": "┼",
|
|
17
|
+
right: "│",
|
|
18
|
+
"right-mid": "┤",
|
|
19
|
+
middle: "│",
|
|
20
|
+
};
|
|
4
21
|
export function printJson(data) {
|
|
5
22
|
console.log(JSON.stringify(data, null, 2));
|
|
6
23
|
}
|
|
7
24
|
export function printError(msg) {
|
|
8
|
-
console.error(chalk.
|
|
25
|
+
console.error(chalk.red("✖") + " " + msg);
|
|
9
26
|
}
|
|
10
27
|
export function handleSdkError(e) {
|
|
11
28
|
if (e instanceof CliError)
|
|
@@ -15,13 +32,13 @@ export function handleSdkError(e) {
|
|
|
15
32
|
throw new CliError(1, message);
|
|
16
33
|
}
|
|
17
34
|
export function printSuccess(msg) {
|
|
18
|
-
console.log(chalk.
|
|
35
|
+
console.log(chalk.green("✔") + " " + msg);
|
|
19
36
|
}
|
|
20
37
|
export function printInfo(msg) {
|
|
21
|
-
console.log(chalk.
|
|
38
|
+
console.log(chalk.cyan("›") + " " + msg);
|
|
22
39
|
}
|
|
23
40
|
export function printWarning(msg) {
|
|
24
|
-
console.log(chalk.
|
|
41
|
+
console.log(chalk.yellow("⚠") + " " + msg);
|
|
25
42
|
}
|
|
26
43
|
export function printMessage(msg) {
|
|
27
44
|
console.log(msg);
|
|
@@ -31,8 +48,8 @@ function truncate(s, max) {
|
|
|
31
48
|
}
|
|
32
49
|
export function printJudgeTable(judges, nextCursor) {
|
|
33
50
|
const table = new Table({
|
|
34
|
-
head: ["ID", "Name", "Intent", "Created At"],
|
|
35
|
-
|
|
51
|
+
head: ["ID", "Name", "Intent", "Created At"].map((h) => chalk.bold.cyan(h)),
|
|
52
|
+
chars: UNICODE_CHARS,
|
|
36
53
|
colWidths: [38, 30, 52, 12],
|
|
37
54
|
wordWrap: true,
|
|
38
55
|
});
|
|
@@ -48,8 +65,8 @@ export function printJudgeTable(judges, nextCursor) {
|
|
|
48
65
|
}
|
|
49
66
|
export function printEvaluatorTable(evaluators, nextCursor) {
|
|
50
67
|
const table = new Table({
|
|
51
|
-
head: ["ID", "Name", "Created At"],
|
|
52
|
-
|
|
68
|
+
head: ["ID", "Name", "Created At"].map((h) => chalk.bold.cyan(h)),
|
|
69
|
+
chars: UNICODE_CHARS,
|
|
53
70
|
colWidths: [38, 30, 12],
|
|
54
71
|
wordWrap: true,
|
|
55
72
|
});
|
|
@@ -65,8 +82,8 @@ export function printEvaluatorTable(evaluators, nextCursor) {
|
|
|
65
82
|
}
|
|
66
83
|
export function printExecutionLogTable(logs, nextCursor) {
|
|
67
84
|
const table = new Table({
|
|
68
|
-
head: ["ID", "Item Name", "Type", "Score", "Created At"],
|
|
69
|
-
|
|
85
|
+
head: ["ID", "Item Name", "Type", "Score", "Created At"].map((h) => chalk.bold.cyan(h)),
|
|
86
|
+
chars: UNICODE_CHARS,
|
|
70
87
|
colWidths: [38, 30, 15, 8, 12],
|
|
71
88
|
wordWrap: true,
|
|
72
89
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@root-signals/scorable-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "CLI for Scorable",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Scorable",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@inquirer/prompts": "^8.3.2",
|
|
34
|
-
"@root-signals/scorable": "^0.3.
|
|
34
|
+
"@root-signals/scorable": "^0.3.1",
|
|
35
35
|
"chalk": "^5.6.2",
|
|
36
36
|
"cli-table3": "^0.6.5",
|
|
37
37
|
"commander": "^14.0.3",
|