codiedev 0.9.76 → 0.9.78
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/commands/doctor.js +4 -2
- package/dist/commands/traces.d.ts +4 -0
- package/dist/commands/traces.js +21 -12
- package/dist/selfUpdate.d.ts +8 -0
- package/dist/selfUpdate.js +22 -0
- package/package.json +1 -1
package/dist/commands/doctor.js
CHANGED
|
@@ -310,10 +310,12 @@ async function runDoctor(_args) {
|
|
|
310
310
|
const workspaceMode = workspaceScan.repoCount > 0;
|
|
311
311
|
checks.push({
|
|
312
312
|
name: "git on PATH",
|
|
313
|
-
|
|
313
|
+
// A warning, not a failure: sessions still capture without git, they just
|
|
314
|
+
// aren't tagged to a repo. Non-engineer installs often skip git entirely.
|
|
315
|
+
status: readiness.gitOnPath ? "pass" : "warn",
|
|
314
316
|
detail: readiness.gitOnPath
|
|
315
317
|
? undefined
|
|
316
|
-
: "not found — capture can't
|
|
318
|
+
: "not found — sessions still capture but can't be tagged to a repo (install git if you work in repos)",
|
|
317
319
|
});
|
|
318
320
|
checks.push({
|
|
319
321
|
name: "Inside a git work tree (current dir)",
|
|
@@ -27,6 +27,9 @@ interface CasesResponse {
|
|
|
27
27
|
ok: boolean;
|
|
28
28
|
/** The suite pulled, when --suite was given. */
|
|
29
29
|
suite: string | null;
|
|
30
|
+
/** The saved dataset version pulled, when --version was given; cases deleted since are counted in `missing`. */
|
|
31
|
+
version?: number | null;
|
|
32
|
+
missing?: number;
|
|
30
33
|
/** How the suite is judged, when it has settings. */
|
|
31
34
|
judge: {
|
|
32
35
|
model: string | null;
|
|
@@ -44,6 +47,7 @@ interface CasesResponse {
|
|
|
44
47
|
export interface ResultsFile {
|
|
45
48
|
agent: string;
|
|
46
49
|
suite: string | null;
|
|
50
|
+
version?: number | null;
|
|
47
51
|
model: string;
|
|
48
52
|
judgeModel: string | null;
|
|
49
53
|
harness: string | null;
|
package/dist/commands/traces.js
CHANGED
|
@@ -55,10 +55,10 @@ exports.TRACES_HELP = `
|
|
|
55
55
|
codiedev traces — test cases saved from Codiedev → Traces
|
|
56
56
|
|
|
57
57
|
codiedev traces agents Agents you maintain, with saved case counts
|
|
58
|
-
codiedev traces cases <agent> [--dataset name] [--case id[,id]] [--model id] [--out dir] [--json]
|
|
58
|
+
codiedev traces cases <agent> [--dataset name [--version n]] [--case id[,id]] [--model id] [--out dir] [--json]
|
|
59
59
|
Write an agent's cases (one dataset, or just
|
|
60
60
|
the cases named) into this repo, default
|
|
61
|
-
.codiedev/traces/<agent>[/<dataset>]/, then
|
|
61
|
+
.codiedev/traces/<agent>[/<dataset>[/v<n>]]/, then
|
|
62
62
|
ask your coding agent to run them.
|
|
63
63
|
--model runs them on another model, e.g.
|
|
64
64
|
the replacement for a retiring one
|
|
@@ -78,7 +78,7 @@ const FLAG_LABEL = {
|
|
|
78
78
|
};
|
|
79
79
|
const TRIES = 3;
|
|
80
80
|
const plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
81
|
-
const recordedModel = (res) => res.cases.flatMap((c) => c.steps.map((s) => s.model))[0] ?? "the
|
|
81
|
+
const recordedModel = (res) => res.cases.flatMap((c) => c.steps.map((s) => s.model)).filter(Boolean)[0] ?? "the agent's current model";
|
|
82
82
|
function slugify(name) {
|
|
83
83
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
|
|
84
84
|
}
|
|
@@ -90,7 +90,7 @@ function casesBrief(res, casesFile, opts = {}) {
|
|
|
90
90
|
const target = opts.model;
|
|
91
91
|
const results = opts.resultsFile ? path.basename(opts.resultsFile) : "results.json";
|
|
92
92
|
const lines = [
|
|
93
|
-
`# Test cases for ${res.agent.name}${res.suite ? ` · ${res.suite}` : ""}${target ? ` on ${target}` : ""}`,
|
|
93
|
+
`# Test cases for ${res.agent.name}${res.suite ? ` · ${res.suite}` : ""}${res.version ? ` v${res.version}` : ""}${target ? ` on ${target}` : ""}`,
|
|
94
94
|
"",
|
|
95
95
|
res.agent.description ? `${res.agent.description}\n` : "",
|
|
96
96
|
`Saved from Codiedev → Traces. ${plural(res.cases.length, "case")} in \`${path.basename(casesFile)}\` (one JSON object per line):`,
|
|
@@ -101,7 +101,7 @@ function casesBrief(res, casesFile, opts = {}) {
|
|
|
101
101
|
if (target) {
|
|
102
102
|
lines.push("## This is a model check", "", `Run every case on **${target}** instead of ${recordedModel(res)} (the model the cases were recorded on). Change only the model the agent asks the gateway for; keep its prompts and tools as they are, so the result says how ${target} does on the same work.`, "");
|
|
103
103
|
}
|
|
104
|
-
lines.push("## How to run them", "", "1. Find where this agent builds its model requests and calls the Codiedev gateway.", `2. For each case, replay \`steps[0].request\` (the first prompt the agent sent) through the agent's own code path, so tools and prompts are the current version, not the recorded one${target ? `, with the model set to ${target}` : ""}.`, "3. Judge each try against the case:", " - A case's `expected`, when present, is what a passing answer looks like (a reference reply or criteria). It wins over everything below.", " - `fix` cases: the run must not reproduce its `flags` (listed below), and must finish (no failed call, no `finish_reason: \"length\"`, no repeated identical tool call).", " - `keep` cases: the final reply must still do what the recorded final reply did. Compare meaning, not wording.", " - A case's `note`, when present, is the engineer's extra context.");
|
|
104
|
+
lines.push("## How to run them", "", "1. Find where this agent builds its model requests and calls the Codiedev gateway.", `2. For each case, replay \`steps[0].request\` (the first prompt the agent sent) through the agent's own code path, so tools and prompts are the current version, not the recorded one${target ? `, with the model set to ${target}` : ""}.`, "3. Judge each try against the case:", " - A case's `expected`, when present, is what a passing answer looks like (a reference reply or criteria). It wins over everything below.", " - `fix` cases: the run must not reproduce its `flags` (listed below), and must finish (no failed call, no `finish_reason: \"length\"`, no repeated identical tool call).", " - `keep` cases: the final reply must still do what the recorded final reply did. Compare meaning, not wording.", " - A case's `note`, when present, is the engineer's extra context.", " - Cases whose `runId` starts with `manual:` or `import:` were written by hand, not recorded: `steps[0].request` is the input and there's no recorded reply, so judge them on `expected`.");
|
|
105
105
|
if (res.judge?.model || res.judge?.rubric) {
|
|
106
106
|
lines.push(` - Where passing needs judgement, judge with **${res.judge.model ?? "a strong model"}** through the gateway${res.judge.rubric ? `, using this rubric: "${res.judge.rubric}"` : ""}. Use the same judge for every model so results compare.`);
|
|
107
107
|
}
|
|
@@ -121,6 +121,7 @@ function resultsTemplate(res, model) {
|
|
|
121
121
|
return {
|
|
122
122
|
agent: res.agent.name,
|
|
123
123
|
suite: res.suite,
|
|
124
|
+
...(res.version ? { version: res.version } : {}),
|
|
124
125
|
model: model ?? recordedModel(res),
|
|
125
126
|
judgeModel: res.judge?.model ?? null,
|
|
126
127
|
harness: null,
|
|
@@ -140,7 +141,7 @@ function parseCasesArgs(args) {
|
|
|
140
141
|
for (let i = 0; i < args.length; i++) {
|
|
141
142
|
const a = args[i];
|
|
142
143
|
let matched = false;
|
|
143
|
-
for (const [name, key] of [["--out", "out"], ["-o", "out"], ["--dataset", "suite"], ["--suite", "suite"], ["--model", "model"], ["--case", "caseIds"]]) {
|
|
144
|
+
for (const [name, key] of [["--out", "out"], ["-o", "out"], ["--dataset", "suite"], ["--suite", "suite"], ["--model", "model"], ["--case", "caseIds"], ["--version", "version"]]) {
|
|
144
145
|
const f = flag(args, i, name);
|
|
145
146
|
if (f.skip >= 0) {
|
|
146
147
|
parsed[key] = f.value;
|
|
@@ -182,20 +183,26 @@ async function runAgents(config) {
|
|
|
182
183
|
console.log("\nPull one: codiedev traces cases <agent>");
|
|
183
184
|
}
|
|
184
185
|
async function runCases(args, config) {
|
|
185
|
-
const { agent, suite, model, caseIds, out, json } = parseCasesArgs(args);
|
|
186
|
+
const { agent, suite, model, caseIds, version, out, json } = parseCasesArgs(args);
|
|
186
187
|
if (!agent) {
|
|
187
|
-
console.error("Usage: codiedev traces cases <agent> [--dataset name] [--model id] [--out dir] [--json]");
|
|
188
|
+
console.error("Usage: codiedev traces cases <agent> [--dataset name [--version n]] [--model id] [--out dir] [--json]");
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
if (version !== undefined && (!suite || !/^[1-9]\d*$/.test(version))) {
|
|
192
|
+
console.error("--version takes a whole number and needs --dataset: codiedev traces cases <agent> --dataset <name> --version <n>");
|
|
188
193
|
process.exit(1);
|
|
189
194
|
}
|
|
190
195
|
const start = Date.now();
|
|
191
196
|
let res;
|
|
192
197
|
try {
|
|
193
|
-
res = await (0, shared_1.apiRequest)("GET", "/api/cli/traces/cases", { config, query: { agent, ...(suite ? { suite } : {}), ...(caseIds ? { case: caseIds } : {}) } });
|
|
198
|
+
res = await (0, shared_1.apiRequest)("GET", "/api/cli/traces/cases", { config, query: { agent, ...(suite ? { suite } : {}), ...(version ? { version } : {}), ...(caseIds ? { case: caseIds } : {}) } });
|
|
194
199
|
}
|
|
195
200
|
catch (err) {
|
|
196
201
|
const e = err;
|
|
197
202
|
(0, shared_1.recordAgentEvent)(config, { tool: "codiedev_traces_cases", ok: false, latencyMs: Date.now() - start, error: e.message });
|
|
198
|
-
if (e.status === 404)
|
|
203
|
+
if (e.status === 404 && version && /No version/.test(e.message))
|
|
204
|
+
console.error(`No version ${version} of "${suite}". See its versions in Codiedev → Traces → Test cases.`);
|
|
205
|
+
else if (e.status === 404)
|
|
199
206
|
console.error(`No agent called "${agent}". See your agents with: codiedev traces agents`);
|
|
200
207
|
else if (e.status === 403)
|
|
201
208
|
console.error(`Only ${agent}'s maintainers can pull its test cases.`);
|
|
@@ -214,7 +221,7 @@ async function runCases(args, config) {
|
|
|
214
221
|
: `${res.agent.name} has no saved test cases. Open a run in Codiedev → Traces and click "Save as test case".`);
|
|
215
222
|
return;
|
|
216
223
|
}
|
|
217
|
-
const dir = path.resolve(process.cwd(), out ?? path.join(".codiedev", "traces", slugify(res.agent.name), ...(res.suite ? [slugify(res.suite)] : []), ...(caseIds && res.cases.length === 1 ? [slugify(res.cases[0].name)] : []), ...(model ? [slugify(model)] : [])));
|
|
224
|
+
const dir = path.resolve(process.cwd(), out ?? path.join(".codiedev", "traces", slugify(res.agent.name), ...(res.suite ? [slugify(res.suite)] : []), ...(res.version ? [`v${res.version}`] : []), ...(caseIds && res.cases.length === 1 ? [slugify(res.cases[0].name)] : []), ...(model ? [slugify(model)] : [])));
|
|
218
225
|
fs.mkdirSync(dir, { recursive: true });
|
|
219
226
|
const casesFile = path.join(dir, "cases.jsonl");
|
|
220
227
|
fs.writeFileSync(casesFile, res.cases.map((c) => JSON.stringify(c)).join("\n") + "\n", "utf8");
|
|
@@ -224,7 +231,9 @@ async function runCases(args, config) {
|
|
|
224
231
|
fs.writeFileSync(briefFile, casesBrief(res, casesFile, { model, resultsFile }), "utf8");
|
|
225
232
|
const fix = res.cases.filter((c) => c.kind === "fix").length;
|
|
226
233
|
const rel = (file) => path.relative(process.cwd(), file) || file;
|
|
227
|
-
console.log(`✓ ${plural(res.cases.length, "case")} for ${res.agent.name}${res.suite ? ` · ${res.suite}` : ""}${model ? ` on ${model}` : ""} (${fix} to fix, ${res.cases.length - fix} to keep passing)`);
|
|
234
|
+
console.log(`✓ ${plural(res.cases.length, "case")} for ${res.agent.name}${res.suite ? ` · ${res.suite}` : ""}${res.version ? ` v${res.version}` : ""}${model ? ` on ${model}` : ""} (${fix} to fix, ${res.cases.length - fix} to keep passing)`);
|
|
235
|
+
if (res.missing)
|
|
236
|
+
console.log(` ${plural(res.missing, "case")} in v${res.version} were deleted since and can't be pulled.`);
|
|
228
237
|
console.log(` ${rel(casesFile)}`);
|
|
229
238
|
console.log(` ${rel(briefFile)}`);
|
|
230
239
|
console.log(` ${rel(resultsFile)} (filled in by your coding agent, then: codiedev traces report ${rel(resultsFile)})`);
|
package/dist/selfUpdate.d.ts
CHANGED
|
@@ -45,6 +45,14 @@ export declare function maybeSpawnSelfUpdate(hookEntry: string | undefined): voi
|
|
|
45
45
|
* one small file read. Honors the same opt-outs as the self-update.
|
|
46
46
|
*/
|
|
47
47
|
export declare function maybeRefreshInstructions(refresh?: () => string[]): void;
|
|
48
|
+
export declare const BIN_NAMES: readonly ["codiedev", "codiedev-hook", "codie"];
|
|
49
|
+
/**
|
|
50
|
+
* The .ps1 shims missing from a Windows npm prefix. The IT kit deletes them so
|
|
51
|
+
* PowerShell falls back to the .cmd shims, which run under the Restricted
|
|
52
|
+
* execution policy Windows client machines default to; `npm i -g` puts them
|
|
53
|
+
* back, so an update must delete again exactly the ones that were gone.
|
|
54
|
+
*/
|
|
55
|
+
export declare function missingPs1Shims(prefix: string, exists?: (p: string) => boolean): string[];
|
|
48
56
|
/**
|
|
49
57
|
* Where the just-installed copy lives. npm/bun reinstall in place. pnpm's
|
|
50
58
|
* `root -g` is <root>/node_modules up to v10 but the bare global/v11 parent
|
package/dist/selfUpdate.js
CHANGED
|
@@ -44,11 +44,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
44
44
|
};
|
|
45
45
|
})();
|
|
46
46
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.BIN_NAMES = void 0;
|
|
47
48
|
exports.planSelfUpdate = planSelfUpdate;
|
|
48
49
|
exports.isSelfUpdateDue = isSelfUpdateDue;
|
|
49
50
|
exports.readSelfUpdateState = readSelfUpdateState;
|
|
50
51
|
exports.maybeSpawnSelfUpdate = maybeSpawnSelfUpdate;
|
|
51
52
|
exports.maybeRefreshInstructions = maybeRefreshInstructions;
|
|
53
|
+
exports.missingPs1Shims = missingPs1Shims;
|
|
52
54
|
exports.findInstalledHookScript = findInstalledHookScript;
|
|
53
55
|
exports.runSelfUpdate = runSelfUpdate;
|
|
54
56
|
const fs = __importStar(require("fs"));
|
|
@@ -290,6 +292,17 @@ function installCommand(plan, version, env) {
|
|
|
290
292
|
return null;
|
|
291
293
|
}
|
|
292
294
|
}
|
|
295
|
+
// package.json "bin" names. npm writes a .cmd, a .ps1 and a bare shim for each.
|
|
296
|
+
exports.BIN_NAMES = ["codiedev", "codiedev-hook", "codie"];
|
|
297
|
+
/**
|
|
298
|
+
* The .ps1 shims missing from a Windows npm prefix. The IT kit deletes them so
|
|
299
|
+
* PowerShell falls back to the .cmd shims, which run under the Restricted
|
|
300
|
+
* execution policy Windows client machines default to; `npm i -g` puts them
|
|
301
|
+
* back, so an update must delete again exactly the ones that were gone.
|
|
302
|
+
*/
|
|
303
|
+
function missingPs1Shims(prefix, exists = fs.existsSync) {
|
|
304
|
+
return exports.BIN_NAMES.map((name) => path.join(prefix, `${name}.ps1`)).filter((p) => !exists(p));
|
|
305
|
+
}
|
|
293
306
|
function readVersionAt(hookScript) {
|
|
294
307
|
try {
|
|
295
308
|
const pkg = JSON.parse(fs.readFileSync(path.join(path.dirname(path.dirname(hookScript)), "package.json"), "utf8"));
|
|
@@ -397,6 +410,7 @@ async function runSelfUpdate(hookEntry) {
|
|
|
397
410
|
const install = installCommand(plan, latest, env);
|
|
398
411
|
if (!install)
|
|
399
412
|
return finish("skip:no-installer");
|
|
413
|
+
const droppedShims = plan.kind === "npm" && process.platform === "win32" ? missingPs1Shims(plan.prefix) : [];
|
|
400
414
|
const result = (0, child_process_1.spawnSync)(install.cmd, install.args, {
|
|
401
415
|
stdio: "ignore",
|
|
402
416
|
timeout: INSTALL_TIMEOUT_MS,
|
|
@@ -407,6 +421,14 @@ async function runSelfUpdate(hookEntry) {
|
|
|
407
421
|
if (result.error || result.status !== 0) {
|
|
408
422
|
return finish(`error:install-exit-${result.status ?? "spawn"}`, { toVersion: latest });
|
|
409
423
|
}
|
|
424
|
+
for (const shim of droppedShims) {
|
|
425
|
+
try {
|
|
426
|
+
fs.rmSync(shim, { force: true });
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
// Best-effort: a leftover .ps1 only matters under a Restricted policy.
|
|
430
|
+
}
|
|
431
|
+
}
|
|
410
432
|
// Re-point the launcher. npm/bun reinstall in place; pnpm lands in a new
|
|
411
433
|
// hashed dir. Only switch once the new copy is on disk at the new version,
|
|
412
434
|
// so a half-finished install can never strand the hooks.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codiedev",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.78",
|
|
4
4
|
"description": "Connect Claude Code, Codex, Cursor, OpenCode, or VS Code Copilot to Codiedev for team session capture and artifact collaboration — and run the governed Codie harness",
|
|
5
5
|
"bin": {
|
|
6
6
|
"codiedev": "dist/cli.js",
|