evalify-cli 0.1.1 → 0.1.3
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/dist/index.js +5288 -308
- package/package.json +5 -4
- package/src/commands/pull.ts +169 -6
- package/tsup.config.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "evalify-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "CLI tool for the Evalify eval criteria registry",
|
|
5
5
|
"homepage": "https://evalify.sh",
|
|
6
6
|
"repository": "https://github.com/AppVerse-cc/evalify",
|
|
@@ -14,13 +14,14 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"chalk": "^5.4.1",
|
|
17
|
-
"commander": "^12.1.0"
|
|
17
|
+
"commander": "^12.1.0",
|
|
18
|
+
"prompts": "^2.4.2"
|
|
18
19
|
},
|
|
19
20
|
"devDependencies": {
|
|
20
21
|
"@evalify/frameworks": "workspace:*",
|
|
21
22
|
"@types/node": "^22.0.0",
|
|
23
|
+
"@types/prompts": "^2.4.9",
|
|
22
24
|
"tsup": "^8.5.1",
|
|
23
25
|
"typescript": "^5.7.0"
|
|
24
|
-
}
|
|
25
|
-
"type": "module"
|
|
26
|
+
}
|
|
26
27
|
}
|
package/src/commands/pull.ts
CHANGED
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import fs from "node:fs/promises";
|
|
4
|
+
import prompts from "prompts";
|
|
3
5
|
import { header, success, info, dim, error } from "../format.js";
|
|
4
6
|
|
|
5
7
|
const REGISTRY_URL = "https://evalify.sh/api/registry";
|
|
6
8
|
|
|
9
|
+
async function detectSkills(skillsDir: string): Promise<{ name: string; evalCount: number }[]> {
|
|
10
|
+
try {
|
|
11
|
+
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
|
|
12
|
+
const skills: { name: string; evalCount: number }[] = [];
|
|
13
|
+
|
|
14
|
+
for (const entry of entries) {
|
|
15
|
+
if (!entry.isDirectory()) continue;
|
|
16
|
+
const evalsFile = path.join(skillsDir, entry.name, "evals.json");
|
|
17
|
+
try {
|
|
18
|
+
const content = await fs.readFile(evalsFile, "utf-8");
|
|
19
|
+
const parsed = JSON.parse(content);
|
|
20
|
+
skills.push({ name: entry.name, evalCount: (parsed.evals ?? []).length });
|
|
21
|
+
} catch {
|
|
22
|
+
// Folder exists but no valid evals.json — skip
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return skills;
|
|
27
|
+
} catch {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
7
32
|
export async function pull(slug: string): Promise<void> {
|
|
8
33
|
header();
|
|
9
34
|
|
|
@@ -47,9 +72,142 @@ export async function pull(slug: string): Promise<void> {
|
|
|
47
72
|
return;
|
|
48
73
|
}
|
|
49
74
|
|
|
50
|
-
|
|
75
|
+
// Ask install location
|
|
76
|
+
const locationResponse = await prompts({
|
|
77
|
+
type: "select",
|
|
78
|
+
name: "location",
|
|
79
|
+
message: "Where do you want to install?",
|
|
80
|
+
choices: [
|
|
81
|
+
{
|
|
82
|
+
title: "Current directory",
|
|
83
|
+
description: `evals/${pack.slug}/evals.json`,
|
|
84
|
+
value: "current",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
title: "Project",
|
|
88
|
+
description: ".claude/skills/<name>/evals.json",
|
|
89
|
+
value: "project",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
title: "Global",
|
|
93
|
+
description: "~/.claude/skills/<name>/evals.json",
|
|
94
|
+
value: "global",
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
initial: 0,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (!locationResponse.location) {
|
|
101
|
+
console.log();
|
|
102
|
+
dim("Cancelled.");
|
|
103
|
+
console.log();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const location: "current" | "project" | "global" = locationResponse.location;
|
|
108
|
+
|
|
109
|
+
let skillName = pack.slug;
|
|
110
|
+
|
|
111
|
+
if (location === "project" || location === "global") {
|
|
112
|
+
const skillsDir =
|
|
113
|
+
location === "project"
|
|
114
|
+
? path.resolve(process.cwd(), ".claude", "skills")
|
|
115
|
+
: path.resolve(os.homedir(), ".claude", "skills");
|
|
116
|
+
|
|
117
|
+
const existing = await detectSkills(skillsDir);
|
|
118
|
+
|
|
119
|
+
const baseChoices = existing.map((s) => ({
|
|
120
|
+
title: s.name,
|
|
121
|
+
description: `${s.evalCount} eval${s.evalCount !== 1 ? "s" : ""}`,
|
|
122
|
+
value: s.name,
|
|
123
|
+
}));
|
|
124
|
+
|
|
125
|
+
const pickResponse = await prompts({
|
|
126
|
+
type: "autocomplete",
|
|
127
|
+
name: "skill",
|
|
128
|
+
message:
|
|
129
|
+
existing.length > 0
|
|
130
|
+
? `Skill folder (${existing.length} found — type to filter or create new):`
|
|
131
|
+
: "Skill folder:",
|
|
132
|
+
choices: baseChoices,
|
|
133
|
+
initial: pack.slug,
|
|
134
|
+
suggest: async (input: string, choices: any[]) => {
|
|
135
|
+
const term = (input || "").toLowerCase();
|
|
136
|
+
const filtered = choices.filter((c) => c.title.toLowerCase().includes(term));
|
|
137
|
+
const exactMatch = choices.find((c) => c.title === (input || pack.slug));
|
|
138
|
+
if (!exactMatch) {
|
|
139
|
+
filtered.push({ title: input || pack.slug, value: input || pack.slug });
|
|
140
|
+
}
|
|
141
|
+
return filtered;
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
if (!pickResponse.skill) {
|
|
146
|
+
console.log();
|
|
147
|
+
dim("Cancelled.");
|
|
148
|
+
console.log();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
skillName = (pickResponse.skill as string).trim();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let targetDir: string;
|
|
156
|
+
if (location === "current") {
|
|
157
|
+
targetDir = path.resolve(process.cwd(), "evals", pack.slug);
|
|
158
|
+
} else if (location === "project") {
|
|
159
|
+
targetDir = path.resolve(process.cwd(), ".claude", "skills", skillName);
|
|
160
|
+
} else {
|
|
161
|
+
targetDir = path.resolve(os.homedir(), ".claude", "skills", skillName);
|
|
162
|
+
}
|
|
163
|
+
|
|
51
164
|
const targetFile = path.join(targetDir, "evals.json");
|
|
52
165
|
|
|
166
|
+
// Check for existing evals.json and ask append vs override
|
|
167
|
+
let writeMode: "override" | "append" = "override";
|
|
168
|
+
let existingEvals: { prompt: string; expectations: string[] }[] = [];
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
const existing = await fs.readFile(targetFile, "utf-8");
|
|
172
|
+
const parsed = JSON.parse(existing);
|
|
173
|
+
existingEvals = parsed.evals ?? [];
|
|
174
|
+
|
|
175
|
+
if (existingEvals.length > 0) {
|
|
176
|
+
const conflictResponse = await prompts({
|
|
177
|
+
type: "select",
|
|
178
|
+
name: "mode",
|
|
179
|
+
message: `Found ${existingEvals.length} existing eval${existingEvals.length !== 1 ? "s" : ""} in ${skillName}. What do you want to do?`,
|
|
180
|
+
choices: [
|
|
181
|
+
{
|
|
182
|
+
title: "Append",
|
|
183
|
+
description: `Add ${pack.evals.length} new eval${pack.evals.length !== 1 ? "s" : ""} to the existing ${existingEvals.length}`,
|
|
184
|
+
value: "append",
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
title: "Override",
|
|
188
|
+
description: "Replace all existing evals with the pulled set",
|
|
189
|
+
value: "override",
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
initial: 0,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
if (!conflictResponse.mode) {
|
|
196
|
+
console.log();
|
|
197
|
+
dim("Cancelled.");
|
|
198
|
+
console.log();
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
writeMode = conflictResponse.mode;
|
|
203
|
+
}
|
|
204
|
+
} catch {
|
|
205
|
+
// No existing file — fresh write
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const evalsToWrite =
|
|
209
|
+
writeMode === "append" ? [...existingEvals, ...pack.evals] : pack.evals;
|
|
210
|
+
|
|
53
211
|
try {
|
|
54
212
|
await fs.mkdir(targetDir, { recursive: true });
|
|
55
213
|
|
|
@@ -61,21 +219,26 @@ export async function pull(slug: string): Promise<void> {
|
|
|
61
219
|
domain: pack.domain,
|
|
62
220
|
author: pack.author,
|
|
63
221
|
tags: pack.tags,
|
|
64
|
-
evals:
|
|
222
|
+
evals: evalsToWrite,
|
|
65
223
|
};
|
|
66
224
|
|
|
67
225
|
await fs.writeFile(targetFile, JSON.stringify(output, null, 2) + "\n");
|
|
68
226
|
|
|
227
|
+
const action = writeMode === "append" ? "Appended" : "Wrote";
|
|
228
|
+
console.log();
|
|
69
229
|
success(`Pulled ${pack.displayName} v${pack.version}`);
|
|
70
|
-
success(
|
|
230
|
+
success(
|
|
231
|
+
`${action} ${pack.evals.length} eval${pack.evals.length !== 1 ? "s" : ""}` +
|
|
232
|
+
(writeMode === "append" ? ` (${evalsToWrite.length} total)` : "") +
|
|
233
|
+
` to ${targetFile}`
|
|
234
|
+
);
|
|
71
235
|
console.log();
|
|
72
236
|
dim(`Author: ${pack.author}`);
|
|
73
237
|
dim(`Domain: ${pack.domain}`);
|
|
74
|
-
dim(`
|
|
75
|
-
dim(`To validate: evalify validate evals/${slug}`);
|
|
238
|
+
dim(`To validate: evalify validate ${targetDir}`);
|
|
76
239
|
} catch (err) {
|
|
77
240
|
error(`Failed to write file: ${(err as Error).message}`);
|
|
78
241
|
}
|
|
79
242
|
|
|
80
243
|
console.log();
|
|
81
|
-
}
|
|
244
|
+
}
|