@skill-harness/cli 0.17.0 → 0.19.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 +0 -13
- package/assets/report.template.html +1 -43
- package/dist/cli.d.ts +0 -8
- package/dist/cli.js +17 -238
- package/dist/serve.js +2 -82
- package/package.json +4 -6
- package/dist/archive-learning.d.ts +0 -3
- package/dist/archive-learning.js +0 -153
- package/dist/archive.d.ts +0 -3
- package/dist/archive.js +0 -52
- package/dist/compare.d.ts +0 -26
- package/dist/compare.js +0 -232
- package/dist/learning.d.ts +0 -26
- package/dist/learning.js +0 -684
- package/dist/qualification.d.ts +0 -7
- package/dist/qualification.js +0 -210
- package/dist/session-learning.d.ts +0 -12
- package/dist/session-learning.js +0 -148
- package/docs/PRODUCT-GUIDE.md +0 -190
- package/docs/STATUS.md +0 -56
package/README.md
CHANGED
|
@@ -24,19 +24,6 @@ npm i -g @skill-harness/cli
|
|
|
24
24
|
| `skill-harness grade <run-dir> [--judge p:m]` | re-grade saved transcripts |
|
|
25
25
|
| `skill-harness review <skill> --skills <root> [--port N]` | serve the interactive review UI |
|
|
26
26
|
| `skill-harness add-test <skill> --skills <root> --id ID --title T ...` | scaffold a new scenario |
|
|
27
|
-
| `skill-harness qualification prepare\|start\|status\|poll\|validate\|abort ...` | durable external-config qualification lifecycle |
|
|
28
|
-
|
|
29
|
-
## Retained learning
|
|
30
|
-
|
|
31
|
-
`skill-harness learning` opens the guided offline workflow; `learning help` documents
|
|
32
|
-
named retained inputs, complete quality review, scoped trust, independent labels,
|
|
33
|
-
separate adoption decisions and later observations. No hand-authored manifests or model calls.
|
|
34
|
-
The Pi package exposes the same flow as `/skill-harness learning`; connected producer registry
|
|
35
|
-
activation/rollback remain separately authorized operations, never a side effect of reading.
|
|
36
|
-
`@skill-harness/cli/learning` exports `runLearningCommand`, `runLearningWizard` and
|
|
37
|
-
`reviewLearningComparison` for the existing producer dashboard bridge.
|
|
38
|
-
|
|
39
|
-
[Product guide](https://github.com/mojomanyana/skill-harness/blob/main/docs/factory/PRODUCT-GUIDE.md).
|
|
40
27
|
|
|
41
28
|
## More
|
|
42
29
|
|
|
@@ -255,13 +255,7 @@ function render() {
|
|
|
255
255
|
const objMark = obj
|
|
256
256
|
? `<span class='lm obj-${obj.status.toLowerCase()}' title='${escapeHtml(`objective ${obj.status}: ${obj.detail}`)}'>◉ ${escapeHtml(obj.status.toLowerCase())}</span>`
|
|
257
257
|
: "";
|
|
258
|
-
|
|
259
|
-
// that renders as a clean verdict is the whole failure this feature prevents.
|
|
260
|
-
const adj = cell.adjudication;
|
|
261
|
-
const adjMark = adj
|
|
262
|
-
? `<span class='lm adj-${adj.state === "unresolved" ? "unresolved" : "settled"}' title='${escapeHtml(`adjudication ${adj.state} (${adj.trigger}) · ${adj.detail}`)}'>⚖ ${adj.state === "unresolved" ? "unresolved" : `${adj.count} agree`}</span>`
|
|
263
|
-
: "";
|
|
264
|
-
html += `<td class='cell ${v}${sel}' data-col='${col.index}' data-id='${scn.id}'>${v}${ov}${suspectBadge}${liftMark}${stabMark}${objMark}${adjMark}${reps}</td>`;
|
|
258
|
+
html += `<td class='cell ${v}${sel}' data-col='${col.index}' data-id='${scn.id}'>${v}${ov}${suspectBadge}${liftMark}${stabMark}${objMark}${reps}</td>`;
|
|
265
259
|
});
|
|
266
260
|
html += "</tr>";
|
|
267
261
|
}
|
|
@@ -290,7 +284,6 @@ async function openPanel(colIndex, scenarioId) {
|
|
|
290
284
|
${cell.suspect ? `<div class="reason" style="color:#b45309"><b>⚠ suspect:</b> judge listed no failed item — re-judge before trusting this FAIL</div>` : ""}
|
|
291
285
|
${cell.stability ? `<div class="reason" style="color:#b45309"><b>⇄ boundary cell:</b> ${escapeHtml(cell.stability.note)}</div>` : ""}
|
|
292
286
|
${cell.objective ? `<div class="reason"><b>◉ objective ${escapeHtml(cell.objective.status)}:</b> ${escapeHtml(cell.objective.detail)}</div>` : ""}
|
|
293
|
-
${cell.adjudication ? `<div class="reason"${cell.adjudication.state === "unresolved" ? ' style="color:#b45309"' : ""}><b>⚖ adjudication ${escapeHtml(cell.adjudication.state)}</b> (${escapeHtml(cell.adjudication.trigger)}): ${escapeHtml(cell.adjudication.detail)}</div>` : ""}
|
|
294
287
|
${cell.metrics ? `<div class="reason"><b>cost/latency:</b> ${cell.metrics.input_tokens == null ? "subject tokens unavailable" : `${cell.metrics.input_tokens} input / ${cell.metrics.output_tokens || 0} output / ${cell.metrics.cache_read_tokens || 0} cache-read`} (${cell.metrics.subject_metrics_reps}/${cell.metrics.total_reps} reps) · ${cell.metrics.judge_calls} judge + ${cell.metrics.judge_rejudge_calls} re-judge · ${cell.metrics.wall_time_ms}ms · ${cell.metrics.tool_calls == null ? "tool calls unavailable" : `${cell.metrics.tool_calls} tools, ${cell.metrics.delegated_children || 0} delegated, max concurrency ${cell.metrics.max_concurrency || 0}`}</div>` : ""}
|
|
295
288
|
<div class="toggle">
|
|
296
289
|
<button data-v="PASS" class="PASS ${cell.override === 'PASS' ? 'active PASS' : ''}">PASS</button>
|
|
@@ -298,7 +291,6 @@ async function openPanel(colIndex, scenarioId) {
|
|
|
298
291
|
<button data-v="" class="JUDGE ${!cell.override ? 'active JUDGE' : ''}">use judge (${cell.judge_verdict})</button>
|
|
299
292
|
</div>
|
|
300
293
|
<div class="rejudge"><button id="rejudgeBtn">Re-judge (${escapeHtml(col.judge.provider + ":" + col.judge.model)})</button> <span class="saved" id="rejudged"></span></div>
|
|
301
|
-
<div class="rejudge"><button id="adjBtn">Adjudicate column…</button> <span class="saved" id="adjudged"></span></div>
|
|
302
294
|
<label class="fld">note <span class="saved" id="saved">saved ✓</span></label>
|
|
303
295
|
<textarea id="note" placeholder="why you overrode / what to fix in SKILL.md">${escapeHtml(cell.note || "")}</textarea>
|
|
304
296
|
<label class="fld">transcript</label>
|
|
@@ -323,40 +315,6 @@ async function openPanel(colIndex, scenarioId) {
|
|
|
323
315
|
location.reload(); // re-judge rewrote results.yaml; reload the fresh matrix + grades
|
|
324
316
|
} catch (e) { rj.disabled = false; rj.textContent = "Re-judge"; }
|
|
325
317
|
};
|
|
326
|
-
|
|
327
|
-
// Two requests on purpose. The first prices the work and spends nothing; the
|
|
328
|
-
// confirm shows that exact ceiling; only then does anything call a judge. A
|
|
329
|
-
// single endpoint could not disclose a count before charging for it.
|
|
330
|
-
const adjBtn = document.getElementById("adjBtn");
|
|
331
|
-
if (adjBtn) adjBtn.onclick = async () => {
|
|
332
|
-
const status = document.getElementById("adjudged");
|
|
333
|
-
const show = (msg, err) => { if (status) { status.textContent = msg; status.classList.toggle("err", !!err); status.classList.add("show"); } };
|
|
334
|
-
adjBtn.disabled = true;
|
|
335
|
-
try {
|
|
336
|
-
const pr = await fetch("/adjudicate", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ col: colIndex, step: "plan" }) });
|
|
337
|
-
const plan = await pr.json().catch(() => ({}));
|
|
338
|
-
if (!pr.ok || !plan.ok) { show(plan.error || "could not plan adjudication", true); adjBtn.disabled = false; return; }
|
|
339
|
-
if (!plan.triggered.length) { show("no cell triggered — nothing to adjudicate", false); adjBtn.disabled = false; return; }
|
|
340
|
-
|
|
341
|
-
// Counts, never dollars: the default judge is a Claude subscription and
|
|
342
|
-
// reports no per-call usage, so a cost figure here would be invented.
|
|
343
|
-
const ok = window.confirm(
|
|
344
|
-
`Adjudicate ${plan.triggered.length} cell(s) with ${plan.judge}?\n\n` +
|
|
345
|
-
`Up to ${plan.maxAdditionalCalls} additional judge call(s).\n` +
|
|
346
|
-
`No tie-break judge from the browser — a disagreement stays unresolved and blocks SHIP.\n\n` +
|
|
347
|
-
plan.detail.join("\n")
|
|
348
|
-
);
|
|
349
|
-
if (!ok) { show("cancelled — nothing spent", false); adjBtn.disabled = false; return; }
|
|
350
|
-
|
|
351
|
-
adjBtn.textContent = "Adjudicating…";
|
|
352
|
-
const rr = await fetch("/adjudicate", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ col: colIndex, step: "run" }) });
|
|
353
|
-
const done = await rr.json().catch(() => ({}));
|
|
354
|
-
if (!rr.ok || !done.ok) { show(done.error || "adjudication failed", true); adjBtn.disabled = false; adjBtn.textContent = "Adjudicate column…"; return; }
|
|
355
|
-
location.reload();
|
|
356
|
-
} catch (e) {
|
|
357
|
-
show("adjudication failed", true); adjBtn.disabled = false; adjBtn.textContent = "Adjudicate column…";
|
|
358
|
-
}
|
|
359
|
-
};
|
|
360
318
|
let t;
|
|
361
319
|
document.getElementById("note").addEventListener("input", (e) => {
|
|
362
320
|
cell.note = e.target.value;
|
package/dist/cli.d.ts
CHANGED
|
@@ -23,21 +23,13 @@ export declare function releaseExitCode(summaries: Array<{
|
|
|
23
23
|
};
|
|
24
24
|
}>): 0 | 1;
|
|
25
25
|
export declare function cmdRun(args: Args): Promise<void>;
|
|
26
|
-
export declare function cmdCompare(args: Args, adapterOverride?: HarnessAdapter): Promise<void>;
|
|
27
26
|
export declare function cmdGrade(args: Args, adapterOverride?: HarnessAdapter): Promise<void>;
|
|
28
|
-
/**
|
|
29
|
-
* Re-score saved runs against the current spec's thresholds — no model or judge calls.
|
|
30
|
-
* Reps are the measurement; thresholds are policy. When policy changes, recompute rather
|
|
31
|
-
* than reconcile two numbers in prose.
|
|
32
|
-
*/
|
|
33
|
-
export declare function cmdJudgeAgreement(args: Args): void;
|
|
34
27
|
/**
|
|
35
28
|
* Re-evaluate needle gates against the saved staged diffs — no subject call. A rep
|
|
36
29
|
* whose gate verdict flips from fail to pass was never judged and costs one judge call.
|
|
37
30
|
* The completion summary reports how many judge calls were made.
|
|
38
31
|
*/
|
|
39
32
|
export declare function cmdRegate(args: Args, adapterOverride?: HarnessAdapter): Promise<void>;
|
|
40
|
-
export declare function cmdScreen(args: Args): Promise<void>;
|
|
41
33
|
export declare function cmdInit(args: Args): Promise<void>;
|
|
42
34
|
export declare function cmdSuggest(args: Args, adapterOverride?: HarnessAdapter): Promise<void>;
|
|
43
35
|
/**
|
package/dist/cli.js
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync, existsSync, mkdirSync, writeFileSync, mkdtempSync, rmSync
|
|
3
|
-
import { load as yamlLoad } from "js-yaml";
|
|
2
|
+
import { readFileSync, existsSync, mkdirSync, writeFileSync, mkdtempSync, rmSync } from "node:fs";
|
|
4
3
|
import { basename, dirname, join, resolve, relative } from "node:path";
|
|
5
4
|
import { tmpdir } from "node:os";
|
|
6
|
-
import { discover, resolveSkill, loadSpec, parseSpec, appendScenario, parseModelRef, runSkillModel, formatScorecard, readResults, regradeRun, lintSkill, failsGate, renderTemplateSpec, isTemplateSpec, renderDraftSpec, buildSuggestPrompt, parseSuggestDraft, rescoreRun, regateRun, specPathForRunDir, collectLift, collectStability, boundaryCells, stabilityNote, PATH_LEGEND, restampSkill,
|
|
5
|
+
import { discover, resolveSkill, loadSpec, parseSpec, appendScenario, parseModelRef, runSkillModel, formatScorecard, readResults, regradeRun, lintSkill, failsGate, renderTemplateSpec, isTemplateSpec, renderDraftSpec, buildSuggestPrompt, parseSuggestDraft, rescoreRun, regateRun, specPathForRunDir, collectLift, collectStability, boundaryCells, stabilityNote, PATH_LEGEND, restampSkill, computeCoverage, formatCoverage, HARNESS_VERSION, defaultJudge, assertJudgeAllowed, assertNotDowngraded, downgradeWarning, isScoredMode, resolveArm, isFreeOfflineCommand, } from "@skill-harness/core";
|
|
7
6
|
import { getAdapter } from "@skill-harness/adapters";
|
|
8
7
|
import { serveReview } from "./serve.js";
|
|
9
|
-
import { runCompareCommand } from "./compare.js";
|
|
10
|
-
import { cmdQualification } from "./qualification.js";
|
|
11
|
-
import { cmdArchive } from "./archive.js";
|
|
12
|
-
import { runLearningCommand } from "./learning.js";
|
|
13
8
|
const DEFAULT_MODEL = "fireworks:accounts/fireworks/models/deepseek-v4-pro";
|
|
14
9
|
// The judge default lives in core (`defaultJudge()`), which resolves
|
|
15
10
|
// SKILL_HARNESS_JUDGE over a baked value — it was duplicated in three places
|
|
@@ -46,6 +41,16 @@ function parseArgs(argv) {
|
|
|
46
41
|
}
|
|
47
42
|
return { _, flags, multi };
|
|
48
43
|
}
|
|
44
|
+
function assertNoRetiredFlags(command, args) {
|
|
45
|
+
const retired = command === "run"
|
|
46
|
+
? ["affected"]
|
|
47
|
+
: command === "grade"
|
|
48
|
+
? ["auto-rejudge", "secondary-judge", "tie-break-judge"]
|
|
49
|
+
: [];
|
|
50
|
+
const found = retired.find((flag) => Object.hasOwn(args.flags, flag));
|
|
51
|
+
if (found)
|
|
52
|
+
throw new Error(`--${found} was removed; this command refuses to silently run with different behavior`);
|
|
53
|
+
}
|
|
49
54
|
export function flagStr(args, key, fallback) {
|
|
50
55
|
const v = args.flags[key];
|
|
51
56
|
if (typeof v === "string")
|
|
@@ -143,11 +148,7 @@ export async function cmdRun(args) {
|
|
|
143
148
|
// that was never the problem. Free, offline validation first — it is also why
|
|
144
149
|
// CI, which has no `pi`, could not test this refusal at all.
|
|
145
150
|
const onlyRaw = flagStr(args, "only");
|
|
146
|
-
|
|
147
|
-
const affected = flagBool(args, "affected");
|
|
148
|
-
if (affected && only) {
|
|
149
|
-
throw new Error("--affected and --only both choose the scenario set — pass one, not both");
|
|
150
|
-
}
|
|
151
|
+
const only = onlyRaw ? onlyRaw.split(",").map((x) => x.trim()).filter(Boolean) : undefined;
|
|
151
152
|
const harnessName = flagStr(args, "harness", "pi");
|
|
152
153
|
const adapter = getAdapter(harnessName);
|
|
153
154
|
if (!(await adapter.available()))
|
|
@@ -176,17 +177,6 @@ export async function cmdRun(args) {
|
|
|
176
177
|
// that look comparable and are not. Checked per skill, before its first token.
|
|
177
178
|
assertNotDowngraded(skill.dir, "run");
|
|
178
179
|
const spec = loadSpec(skill.specPath);
|
|
179
|
-
if (affected) {
|
|
180
|
-
// Reuses the exact `--only` machinery, so an affected run is partial and
|
|
181
|
-
// cannot report SHIP — the same guarantee, through the same code path.
|
|
182
|
-
const result = await computeAffected(args, spec.scenarios, skill.specPath);
|
|
183
|
-
console.log(formatAffected(result, spec.scenarios.length));
|
|
184
|
-
only = result.selected.map((sel) => sel.id);
|
|
185
|
-
if (only.length === 0) {
|
|
186
|
-
console.log(`skip ${skill.name}: no scenario is affected by this change`);
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
180
|
for (const token of modelTokens) {
|
|
191
181
|
const model = parseModelRef(token);
|
|
192
182
|
// The version is on the banner because a stale global install is otherwise
|
|
@@ -229,98 +219,10 @@ export async function cmdRun(args) {
|
|
|
229
219
|
console.log(`\nReview interactively: skill-harness review ${skills[0]?.name ?? "<skill>"} --skills ${root}`);
|
|
230
220
|
// A full delivered run is a release gate. NOT READY — including one critical
|
|
231
221
|
// failure hidden by a high aggregate — must be machine-visible to CI. Red
|
|
232
|
-
// baselines and partial
|
|
222
|
+
// baselines and partial branch feedback are deliberately excluded.
|
|
233
223
|
if (releaseExitCode(summaries) !== 0)
|
|
234
224
|
process.exitCode = 1;
|
|
235
225
|
}
|
|
236
|
-
export async function cmdCompare(args, adapterOverride) {
|
|
237
|
-
const target = args._[0];
|
|
238
|
-
const reference = flagStr(args, "reference");
|
|
239
|
-
const candidateRoot = flagStr(args, "candidate");
|
|
240
|
-
if (!target || !reference || !candidateRoot) {
|
|
241
|
-
throw new Error("usage: skill-harness compare <skill|all> --reference <git-ref-or-skills-root> --candidate <skills-root> --model <provider:model> --reps N");
|
|
242
|
-
}
|
|
243
|
-
const judgeFlag = flagStr(args, "judge");
|
|
244
|
-
const judgeToken = judgeFlag ?? defaultJudge();
|
|
245
|
-
const judge = parseModelRef(judgeToken);
|
|
246
|
-
assertJudgeAllowed(judge, {
|
|
247
|
-
source: judgeFlag ? "--judge" : "the default judge (SKILL_HARNESS_JUDGE or the baked value)",
|
|
248
|
-
allowMetered: flagBool(args, "allow-metered-judge"),
|
|
249
|
-
});
|
|
250
|
-
const harnessName = flagStr(args, "harness", "pi");
|
|
251
|
-
const adapter = adapterOverride ?? getAdapter(harnessName);
|
|
252
|
-
if (!(await adapter.available()))
|
|
253
|
-
throw new Error(`harness \`${harnessName}\` is not on PATH`);
|
|
254
|
-
const mode = (flagStr(args, "mode", "force") || "force");
|
|
255
|
-
if (mode === "red")
|
|
256
|
-
throw new Error("compare measures a skill candidate, so --mode must be green or force (red is a no-skill baseline)");
|
|
257
|
-
const tuning = parseRunTuning(args);
|
|
258
|
-
const onlyRaw = flagStr(args, "only");
|
|
259
|
-
let only = onlyRaw ? onlyRaw.split(",").map((id) => id.trim()).filter(Boolean) : undefined;
|
|
260
|
-
const affected = flagBool(args, "affected");
|
|
261
|
-
if (affected && only)
|
|
262
|
-
throw new Error("--affected and --only both choose the comparison scenario set — pass one, not both");
|
|
263
|
-
if (affected) {
|
|
264
|
-
if (target === "all")
|
|
265
|
-
throw new Error("compare --affected currently requires one skill so each selected ID has an unambiguous spec");
|
|
266
|
-
const skill = resolveSkill(candidateRoot, target);
|
|
267
|
-
const spec = loadSpec(skill.specPath);
|
|
268
|
-
const selectionArgs = {
|
|
269
|
-
...args,
|
|
270
|
-
flags: {
|
|
271
|
-
...args.flags,
|
|
272
|
-
// A git-ref reference is the natural diff base. A reference directory
|
|
273
|
-
// has no common history, so the caller must supply --base explicitly.
|
|
274
|
-
...(!flagStr(args, "base") && !existsSync(reference) ? { base: reference } : {}),
|
|
275
|
-
},
|
|
276
|
-
};
|
|
277
|
-
if (existsSync(reference) && !flagStr(args, "base")) {
|
|
278
|
-
throw new Error("compare --affected with a reference directory needs --base <git-ref> for candidate change selection");
|
|
279
|
-
}
|
|
280
|
-
const selected = await computeAffected(selectionArgs, spec.scenarios, skill.specPath);
|
|
281
|
-
console.log(formatAffected(selected, spec.scenarios.length));
|
|
282
|
-
only = selected.selected.map((entry) => entry.id);
|
|
283
|
-
if (only.length === 0) {
|
|
284
|
-
console.log("compare: no affected scenarios — 0 subject calls, 0 judge calls; no release claim produced");
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
const threshold = (name) => {
|
|
289
|
-
const raw = flagStr(args, name);
|
|
290
|
-
if (raw === undefined)
|
|
291
|
-
return undefined;
|
|
292
|
-
const value = Number(raw);
|
|
293
|
-
if (!Number.isFinite(value) || value < 0)
|
|
294
|
-
throw new Error(`--${name} must be a non-negative ratio (got \`${raw}\`)`);
|
|
295
|
-
return value;
|
|
296
|
-
};
|
|
297
|
-
const thresholds = {
|
|
298
|
-
max_subject_token_increase: threshold("max-subject-token-increase"),
|
|
299
|
-
max_wall_time_increase: threshold("max-wall-time-increase"),
|
|
300
|
-
max_tool_call_increase: threshold("max-tool-call-increase"),
|
|
301
|
-
};
|
|
302
|
-
const hasThreshold = Object.values(thresholds).some((value) => value !== undefined);
|
|
303
|
-
const result = await runCompareCommand({
|
|
304
|
-
target,
|
|
305
|
-
reference,
|
|
306
|
-
candidateRoot,
|
|
307
|
-
models: resolveModels(args),
|
|
308
|
-
judgeToken,
|
|
309
|
-
mode,
|
|
310
|
-
reps: tuning.reps,
|
|
311
|
-
passThreshold: tuning.passThreshold,
|
|
312
|
-
parallel: Math.max(1, Number(flagStr(args, "parallel", "1")) || 1),
|
|
313
|
-
only,
|
|
314
|
-
canary: flagBool(args, "canary"),
|
|
315
|
-
output: flagStr(args, "output") || undefined,
|
|
316
|
-
thresholds: hasThreshold ? thresholds : undefined,
|
|
317
|
-
adapter,
|
|
318
|
-
now: nowIso,
|
|
319
|
-
});
|
|
320
|
-
console.log(`\ncomparison artifacts: ${result.outputDir}`);
|
|
321
|
-
if (result.exitCode !== 0)
|
|
322
|
-
process.exitCode = result.exitCode;
|
|
323
|
-
}
|
|
324
226
|
export async function cmdGrade(args, adapterOverride) {
|
|
325
227
|
const runDir = args._[0];
|
|
326
228
|
if (!runDir)
|
|
@@ -360,30 +262,7 @@ export async function cmdGrade(args, adapterOverride) {
|
|
|
360
262
|
for (const s of results.scenarios) {
|
|
361
263
|
console.log(` ${s.id} → ${s.judge_verdict}: ${s.judge_reason}`);
|
|
362
264
|
}
|
|
363
|
-
|
|
364
|
-
// Adjudication is opt-in. Without --auto-rejudge nothing below runs and not one
|
|
365
|
-
// extra call is made — a spec may declare triggers, but spec configuration alone
|
|
366
|
-
// never authorizes spending.
|
|
367
|
-
const judges = resolveAdjudicationJudges({
|
|
368
|
-
enabled: flagBool(args, "auto-rejudge"),
|
|
369
|
-
primary: judge,
|
|
370
|
-
secondaryToken: flagStr(args, "secondary-judge"),
|
|
371
|
-
tieBreakToken: flagStr(args, "tie-break-judge"),
|
|
372
|
-
subjectToken: results.model,
|
|
373
|
-
parseRef: parseModelRef,
|
|
374
|
-
assertAllowed: (j, source) => assertJudgeAllowed(j, { source, allowMetered: flagBool(args, "allow-metered-judge") }),
|
|
375
|
-
resemblesSubject: judgeResemblesSubject,
|
|
376
|
-
warn: (m) => console.error(m),
|
|
377
|
-
});
|
|
378
|
-
if (judges) {
|
|
379
|
-
final = await adjudicateRun({
|
|
380
|
-
runDir, spec, adapter, results, primaryJudge: judge,
|
|
381
|
-
secondaryJudge: judges.secondary, tieBreakJudge: judges.tieBreak,
|
|
382
|
-
specDir: testsDir, now: nowIso,
|
|
383
|
-
log: (m) => console.log(m),
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
const g = final.effective_grade;
|
|
265
|
+
const g = results.effective_grade;
|
|
387
266
|
console.log(`\n re-graded with ${judge.provider}:${judge.model} → ${g.letter} (${g.pct}%) ${g.ship ? "SHIP" : "NOT READY"}`);
|
|
388
267
|
}
|
|
389
268
|
/**
|
|
@@ -391,20 +270,6 @@ export async function cmdGrade(args, adapterOverride) {
|
|
|
391
270
|
* Reps are the measurement; thresholds are policy. When policy changes, recompute rather
|
|
392
271
|
* than reconcile two numbers in prose.
|
|
393
272
|
*/
|
|
394
|
-
export function cmdJudgeAgreement(args) {
|
|
395
|
-
const raw = args._[0];
|
|
396
|
-
if (!raw)
|
|
397
|
-
throw new Error("usage: skill-harness judge-agreement <run-dir>");
|
|
398
|
-
const runDir = resolve(raw);
|
|
399
|
-
if (!existsSync(runDir))
|
|
400
|
-
throw new Error(`run dir not found: ${runDir}`);
|
|
401
|
-
const report = judgeAgreement(readResults(runDir));
|
|
402
|
-
for (const cell of report.cells) {
|
|
403
|
-
console.log(` ${cell.status === "agree" ? "=" : cell.status === "disagree" ? "≠" : "?"} ${cell.scenario}: ${cell.status} — ${cell.detail}${cell.judges.length ? ` (${cell.judges.join(" vs ")})` : ""}`);
|
|
404
|
-
}
|
|
405
|
-
console.log(`\njudge agreement: ${report.agree} agree / ${report.disagree} disagree / ${report.error} error; ${report.rate === null ? "n/a" : `${(report.rate * 100).toFixed(1)}%`} across ${report.comparable} comparable scenario(s).`);
|
|
406
|
-
console.log("offline report only; no model or judge calls.");
|
|
407
|
-
}
|
|
408
273
|
async function cmdRescore(args) {
|
|
409
274
|
const runDirs = args._;
|
|
410
275
|
if (runDirs.length === 0)
|
|
@@ -523,13 +388,6 @@ async function cmdRestamp(args) {
|
|
|
523
388
|
console.log(`\n${runs} run(s) examined: ${upgraded} upgraded, ${unprovable} left alone, ${unchanged} already current.` +
|
|
524
389
|
`${partial > 0 ? ` (${partial} upgraded only in part.)` : ""} No models were called.`);
|
|
525
390
|
}
|
|
526
|
-
export async function cmdScreen(args) {
|
|
527
|
-
if (args._.length === 0)
|
|
528
|
-
throw new Error("usage: skill-harness screen <run-dir> [<run-dir> ...]");
|
|
529
|
-
const results = args._.map(runDir => readResults(resolve(runDir)));
|
|
530
|
-
console.log(formatScreen(screenResults(results)));
|
|
531
|
-
console.log(`\n${results.length} retained result(s) screened; 0 subject calls, 0 judge calls.`);
|
|
532
|
-
}
|
|
533
391
|
async function cmdStability(args) {
|
|
534
392
|
const root = flagStr(args, "skills", process.cwd());
|
|
535
393
|
const target = args._[0] ?? "all";
|
|
@@ -652,7 +510,6 @@ async function cmdCoverage(args) {
|
|
|
652
510
|
// always points at, so report on it even when nothing references it —
|
|
653
511
|
// otherwise a skill with zero `covers` reports 0 sections and looks fine.
|
|
654
512
|
baseFiles: [relative(specDir, join(skill.dir, "SKILL.md")).split("\\").join("/")],
|
|
655
|
-
pendingCaptures: readPendingCaptures(specDir),
|
|
656
513
|
});
|
|
657
514
|
console.log(formatCoverage(report, spec.skill));
|
|
658
515
|
if (report.uncovered.length)
|
|
@@ -672,54 +529,6 @@ async function cmdCoverage(args) {
|
|
|
672
529
|
process.exitCode = 1;
|
|
673
530
|
}
|
|
674
531
|
}
|
|
675
|
-
/** Pending captures and the sections they are parked against. Free, offline, tolerant. */
|
|
676
|
-
function readPendingCaptures(specDir) {
|
|
677
|
-
const dir = join(specDir, "captures");
|
|
678
|
-
if (!existsSync(dir))
|
|
679
|
-
return [];
|
|
680
|
-
const out = [];
|
|
681
|
-
for (const file of readdirSync(dir).filter((f) => f.endsWith(".yaml"))) {
|
|
682
|
-
try {
|
|
683
|
-
const raw = yamlLoad(readFileSync(join(dir, file), "utf8"));
|
|
684
|
-
if (!raw || raw.status === "promoted")
|
|
685
|
-
continue;
|
|
686
|
-
const covers = Array.isArray(raw.covers) ? raw.covers.filter((c) => typeof c === "string") : [];
|
|
687
|
-
if (covers.length)
|
|
688
|
-
out.push({ id: String(raw.id ?? file.replace(/\.yaml$/, "")), covers });
|
|
689
|
-
}
|
|
690
|
-
catch {
|
|
691
|
-
// A malformed capture is the capture command's problem to report; coverage
|
|
692
|
-
// must not fail because a draft file is mid-edit.
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
return out;
|
|
696
|
-
}
|
|
697
|
-
/** `affected` — which scenarios a change could plausibly touch. Free and offline. */
|
|
698
|
-
async function cmdAffected(args) {
|
|
699
|
-
const root = flagStr(args, "skills", process.cwd());
|
|
700
|
-
const target = args._[0];
|
|
701
|
-
if (!target)
|
|
702
|
-
throw new Error("usage: skill-harness affected <skill> --skills <root> [--base <git-ref>]");
|
|
703
|
-
const skill = resolveSkill(root, target);
|
|
704
|
-
if (!skill.hasSpec)
|
|
705
|
-
throw new Error(`${target} has no spec`);
|
|
706
|
-
const spec = loadSpec(skill.specPath);
|
|
707
|
-
const result = await computeAffected(args, spec.scenarios, skill.specPath);
|
|
708
|
-
console.log(formatAffected(result, spec.scenarios.length));
|
|
709
|
-
}
|
|
710
|
-
/** Shared by `affected` and `run --affected`, so the two can never disagree. */
|
|
711
|
-
async function computeAffected(args, scenarios, specPath) {
|
|
712
|
-
const base = flagStr(args, "base", "HEAD");
|
|
713
|
-
const repoRoot = await gitRepoRoot(dirname(specPath));
|
|
714
|
-
const diff = await gitDiff(repoRoot, base);
|
|
715
|
-
return selectAffected({ scenarios, specDir: dirname(specPath), diff, repoRoot });
|
|
716
|
-
}
|
|
717
|
-
async function gitRepoRoot(from) {
|
|
718
|
-
const r = await exec("git", ["rev-parse", "--show-toplevel"], { cwd: from, timeoutMs: 30_000 });
|
|
719
|
-
if (r.code !== 0)
|
|
720
|
-
throw new Error(`not a git repository (from ${from}) — --affected needs one to diff against`);
|
|
721
|
-
return r.stdout.trim();
|
|
722
|
-
}
|
|
723
532
|
/** Write a spec to disk, creating its tests/ dir. The single choke point for spec
|
|
724
533
|
* writes (init/suggest) so a future atomic-write/backup/audit change lands in one place. */
|
|
725
534
|
function writeSpecFile(specPath, text) {
|
|
@@ -881,31 +690,16 @@ export function help() {
|
|
|
881
690
|
return `skill-harness ${HARNESS_VERSION} — test/optimize loop for agent skills (pi harness)
|
|
882
691
|
|
|
883
692
|
run <skill|all> --skills <root> [--model prov:model ...] [--models file] [--only A1,D2]
|
|
884
|
-
[--affected --base <git-ref>] run only the scenarios a change could touch (partial; never SHIPs)
|
|
885
693
|
[--mode red|green|force] [--judge prov:model] [--harness pi] [--label name] [--parallel N] [--reps N] [--pass-threshold T]
|
|
886
694
|
[--canary] green only: spend ONE probe proving the skill reached the model, and abort the run if it did not
|
|
887
|
-
[--structured]
|
|
695
|
+
[--structured] request structured capture (Pi uses it by default for subject usage)
|
|
888
696
|
[--arm <name>] measure under a named arm from <skills-root>/tests/arms.yaml
|
|
889
697
|
(loads its extensions, seeds pi-daddy definitions, tags the run dir)
|
|
890
|
-
compare <skill|all> --reference <git-ref-or-skills-root> --candidate <skills-root>
|
|
891
|
-
[--model prov:model ...] [--mode green|force] [--judge prov:model] [--reps N] [--only IDs | --affected --base ref]
|
|
892
|
-
[--output dir] [--max-subject-token-increase R] [--max-wall-time-increase R] [--max-tool-call-increase R]
|
|
893
|
-
paired setup (not seeded sampling); critical regression exit 2, ordinary regression exit 1
|
|
894
698
|
grade <run-dir> [--judge prov:model] [--suspect-only] re-grade saved transcripts (neutral judge)
|
|
895
|
-
[--auto-rejudge] [--secondary-judge p:m] [--tie-break-judge p:m]
|
|
896
|
-
ask again about untrustworthy cells (ambiguous / contradictory / non-unanimous /
|
|
897
|
-
ship-deciding). OFF by default; prints the exact MAX extra call count first.
|
|
898
|
-
archive ingest|inspect|watch --policy file --source id explicit external ingestion/metadata (${free("archive")})
|
|
899
|
-
learning [status|import|review|trust|decide|adoption|outcome|guide] guided retained learning (${free("learning")}; no models)
|
|
900
|
-
use learning help; quality, trust, adoption and later outcomes stay separate
|
|
901
|
-
archive weekly|trust|access --state /private/dir --request file durable learning/consent lifecycle (${free("archive")})
|
|
902
|
-
watch requires --max-polls N; optional --interval-ms N and --previous checkpoint
|
|
903
|
-
judge-agreement <run-dir> compare two distinct persisted judge votes per scenario (${free("judge-agreement")})
|
|
904
699
|
rescore <run-dir>... re-score saved reps vs current spec thresholds (${free("rescore")})
|
|
905
700
|
regate <run-dir>... [--judge prov:model] re-evaluate saved gates (no subject call; judges fail→pass reps)
|
|
906
701
|
restamp <skill|all> --skills <root> [--from <git-ref>] record the model-visible skill digest on runs that still match (${free("restamp")}; one-time migration)
|
|
907
702
|
stability <skill|all> --skills <root> [--window N] [--all] run-over-run verdict flips per scenario (${free("stability")})
|
|
908
|
-
screen <run-dir>... retained control/treatment + criterion rates (${free("screen")})
|
|
909
703
|
review <skill> --skills <root> [--port N] serve the interactive review UI
|
|
910
704
|
add-test <skill> --skills <root> --id ID --title T --turn ... --check ... [--critical] [--mode seeded --fixture path]
|
|
911
705
|
init <skill> --skills <root> [--force] scaffold a commented template spec (${free("init")})
|
|
@@ -913,12 +707,6 @@ export function help() {
|
|
|
913
707
|
list --skills <root> discovered skills + spec status (${free("list")})
|
|
914
708
|
lint <skill|all> --skills <root> validate specs/fixtures + results-consistency (${free("lint")}; CI gate; exits non-zero on findings)
|
|
915
709
|
coverage <skill|all> --skills <root> [--strict] which instruction sections have a declared test (${free("coverage")})
|
|
916
|
-
affected <skill> --skills <root> [--base ref] which scenarios a change could touch (${free("affected")})
|
|
917
|
-
qualification <prepare|start|status|poll|validate|panel|cell|abort> durable qualification-runner-v1 lifecycle + offline panel collapse
|
|
918
|
-
prepare --spool DIR --config FILE --request FILE [--expected-config-sha256 HEX] (required in production)
|
|
919
|
-
start|status|poll|abort --spool DIR --id ID (abort also requires --reason ID)
|
|
920
|
-
start may resume only with --continuation-authority-file <private-0600-prebound-one-use-capability>
|
|
921
|
-
validate --spool DIR (all calls use external, schema-validated arms; no automatic retry)
|
|
922
710
|
|
|
923
711
|
version print ${HARNESS_VERSION} and exit (also --version / -v)
|
|
924
712
|
|
|
@@ -931,22 +719,15 @@ defaults: model=${DEFAULT_MODEL} judge=${defaultJudge()} mode=green harness=p
|
|
|
931
719
|
}
|
|
932
720
|
export async function main(argv) {
|
|
933
721
|
const cmd = argv[0];
|
|
934
|
-
if (cmd === "learning") {
|
|
935
|
-
await runLearningCommand(argv.slice(1));
|
|
936
|
-
return;
|
|
937
|
-
}
|
|
938
722
|
const args = parseArgs(argv.slice(1));
|
|
723
|
+
assertNoRetiredFlags(cmd, args);
|
|
939
724
|
switch (cmd) {
|
|
940
725
|
case "run": return cmdRun(args);
|
|
941
|
-
case "compare": return cmdCompare(args);
|
|
942
726
|
case "grade": return cmdGrade(args);
|
|
943
|
-
case "archive": return cmdArchive(args);
|
|
944
|
-
case "judge-agreement": return cmdJudgeAgreement(args);
|
|
945
727
|
case "rescore": return cmdRescore(args);
|
|
946
728
|
case "regate": return cmdRegate(args);
|
|
947
729
|
case "restamp": return cmdRestamp(args);
|
|
948
730
|
case "stability": return cmdStability(args);
|
|
949
|
-
case "screen": return cmdScreen(args);
|
|
950
731
|
case "review": return cmdReview(args);
|
|
951
732
|
case "add-test": return cmdAddTest(args);
|
|
952
733
|
case "init": return cmdInit(args);
|
|
@@ -954,8 +735,6 @@ export async function main(argv) {
|
|
|
954
735
|
case "list": return cmdList(args);
|
|
955
736
|
case "lint": return cmdLint(args);
|
|
956
737
|
case "coverage": return cmdCoverage(args);
|
|
957
|
-
case "affected": return cmdAffected(args);
|
|
958
|
-
case "qualification": return cmdQualification(args);
|
|
959
738
|
case "version":
|
|
960
739
|
case "--version":
|
|
961
740
|
case "-v":
|
package/dist/serve.js
CHANGED
|
@@ -3,7 +3,7 @@ import { readFileSync, existsSync } from "node:fs";
|
|
|
3
3
|
import { join, dirname } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
|
-
import { collectReport, renderReport, collectTrends, readResults, writeResults, applyOverride, preserveTranscript, findTranscriptFiles, ensureResultsGitignore, appendJournal, loadSpec, regradeScenario,
|
|
6
|
+
import { collectReport, renderReport, collectTrends, readResults, writeResults, applyOverride, preserveTranscript, findTranscriptFiles, ensureResultsGitignore, appendJournal, loadSpec, regradeScenario, refreshRubricHashes, findJudgeRawFiles, effectiveThreshold, scoreContextFor, isScoredMode, rebuildScenarioResult, mergeScenarioMetrics, carryRepObjectives, envFlag, } from "@skill-harness/core";
|
|
7
7
|
import { getAdapter } from "@skill-harness/adapters";
|
|
8
8
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
/** Locate assets/report.template.html relative to dist/ or src/. */
|
|
@@ -142,15 +142,8 @@ export async function serveReview(opts) {
|
|
|
142
142
|
const merged = results.scenarios.map((s) => {
|
|
143
143
|
if (s.id !== body.scenarioId)
|
|
144
144
|
return s;
|
|
145
|
-
const judge_history = appendJudgeHistory(s, results.judge, {
|
|
146
|
-
judge: results.judge,
|
|
147
|
-
verdict: rr.judge_verdict,
|
|
148
|
-
reason: rr.judge_reason,
|
|
149
|
-
suspect: rr.suspect,
|
|
150
|
-
criteria: rr.rep_judgments?.find(panel => panel.repetition === 0)?.judgments[0]?.criteria,
|
|
151
|
-
});
|
|
152
145
|
// Same contract as `grade`, through the same choke point.
|
|
153
|
-
return rebuildScenarioResult({ ...rr, metrics: mergeScenarioMetrics(s.metrics, rr.metrics), rep_judgments: carryRepObjectives(rr.rep_judgments, s.rep_judgments)
|
|
146
|
+
return rebuildScenarioResult({ ...rr, metrics: mergeScenarioMetrics(s.metrics, rr.metrics), rep_judgments: carryRepObjectives(rr.rep_judgments, s.rep_judgments) }, s, { objective: "carry", adjudication: "drop" });
|
|
154
147
|
});
|
|
155
148
|
const written = writeResults(column.runDir, {
|
|
156
149
|
schema: results.schema, subject_invocations: results.subject_invocations,
|
|
@@ -187,79 +180,6 @@ export async function serveReview(opts) {
|
|
|
187
180
|
}
|
|
188
181
|
return;
|
|
189
182
|
}
|
|
190
|
-
/**
|
|
191
|
-
* Adjudication, in two steps: `plan` returns the exact call ceiling and
|
|
192
|
-
* spends nothing; `run` executes it.
|
|
193
|
-
*
|
|
194
|
-
* Two requests rather than one because the ceiling has to be disclosed
|
|
195
|
-
* BEFORE anything is spent, and a single endpoint that both prices and
|
|
196
|
-
* charges cannot do that — the UI would be showing a count for work that
|
|
197
|
-
* had already happened.
|
|
198
|
-
*/
|
|
199
|
-
if (req.method === "POST" && url.pathname === "/adjudicate") {
|
|
200
|
-
const body = JSON.parse((await readBody(req)) || "{}");
|
|
201
|
-
const data = collectReport(opts.skillDir);
|
|
202
|
-
const column = data.columns.find((c) => c.index === body.col);
|
|
203
|
-
if (!column) {
|
|
204
|
-
res.writeHead(404).end("unknown column");
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
const results = readResults(column.runDir);
|
|
208
|
-
if (!isScoredMode(results.mode)) {
|
|
209
|
-
res.writeHead(400, { "content-type": "application/json" });
|
|
210
|
-
res.end(JSON.stringify({ ok: false, error: `only scored runs (green/force) can be adjudicated — for a ${results.mode} run use \`skill-harness grade\`` }));
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
const specPath = join(opts.skillDir, "tests", "specification.yaml");
|
|
214
|
-
const spec = loadSpec(specPath);
|
|
215
|
-
const adapter = opts.adapter ?? getAdapter(results.harness);
|
|
216
|
-
// Priced exactly as `adjudicateRun` will perform it — including per-rep
|
|
217
|
-
// verdicts, without which the quoted ceiling can be lower than the spend.
|
|
218
|
-
const cells = cellsFromResults(column.runDir, results);
|
|
219
|
-
// No tie-break judge from the browser: adding a third judge is a judge
|
|
220
|
-
// CHOICE, and the UI has nowhere honest to make one. A disagreement here
|
|
221
|
-
// stays unresolved and blocks SHIP, which is the safe direction.
|
|
222
|
-
const plan = planAdjudication({
|
|
223
|
-
cells, scenarios: spec.scenarios, shipBar: spec.ship_bar, critical: spec.critical,
|
|
224
|
-
tieBreakAvailable: false,
|
|
225
|
-
});
|
|
226
|
-
if (body.step !== "run") {
|
|
227
|
-
res.writeHead(200, { "content-type": "application/json" });
|
|
228
|
-
res.end(JSON.stringify({
|
|
229
|
-
ok: true, step: "plan",
|
|
230
|
-
triggered: plan.triggered,
|
|
231
|
-
maxAdditionalCalls: plan.maxAdditionalCalls,
|
|
232
|
-
judge: `${results.judge.provider}:${results.judge.model}`,
|
|
233
|
-
detail: plan.decisions.filter((d) => d.triggers.length).map((d) => `${d.id}: ${d.triggers.join(", ")}`),
|
|
234
|
-
}));
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
if (!(await adapter.available())) {
|
|
238
|
-
res.writeHead(400, { "content-type": "application/json" });
|
|
239
|
-
res.end(JSON.stringify({ ok: false, error: `harness \`${results.harness}\` is not on PATH` }));
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
try {
|
|
243
|
-
assertJudgeAllowed(results.judge, { source: "the run's recorded judge", allowMetered: envFlag("SKILL_HARNESS_ALLOW_METERED_JUDGE") });
|
|
244
|
-
const written = await adjudicateRun({
|
|
245
|
-
runDir: column.runDir, spec, adapter, results,
|
|
246
|
-
primaryJudge: results.judge,
|
|
247
|
-
// Asked again as an independent draw. The judge-variance study measured
|
|
248
|
-
// ~2% self-disagreement on identical transcripts, so this is a real
|
|
249
|
-
// second opinion rather than a no-op.
|
|
250
|
-
secondaryJudge: results.judge,
|
|
251
|
-
specDir: dirname(specPath), now: () => new Date().toISOString(),
|
|
252
|
-
});
|
|
253
|
-
ensureResultsGitignore(join(opts.skillDir, "tests", "results"));
|
|
254
|
-
res.writeHead(200, { "content-type": "application/json" });
|
|
255
|
-
res.end(JSON.stringify({ ok: true, step: "run", grade: written.effective_grade }));
|
|
256
|
-
}
|
|
257
|
-
catch (e) {
|
|
258
|
-
res.writeHead(400, { "content-type": "application/json" });
|
|
259
|
-
res.end(JSON.stringify({ ok: false, error: e instanceof Error ? e.message : String(e) }));
|
|
260
|
-
}
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
183
|
if (req.method === "POST" && url.pathname === "/save") {
|
|
264
184
|
const body = JSON.parse((await readBody(req)) || "{}");
|
|
265
185
|
const data = collectReport(opts.skillDir);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skill-harness/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "skill-harness CLI — run, grade, review, and lint agent-skill scenarios on the pi harness",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
12
12
|
".": "./dist/cli.js",
|
|
13
|
-
"./serve": "./dist/serve.js"
|
|
14
|
-
"./learning": "./dist/learning.js"
|
|
13
|
+
"./serve": "./dist/serve.js"
|
|
15
14
|
},
|
|
16
15
|
"keywords": [
|
|
17
16
|
"agent-skills",
|
|
@@ -28,7 +27,6 @@
|
|
|
28
27
|
"dist/**/*.js",
|
|
29
28
|
"dist/**/*.d.ts",
|
|
30
29
|
"assets",
|
|
31
|
-
"docs/*.md",
|
|
32
30
|
"LICENSE",
|
|
33
31
|
"README.md"
|
|
34
32
|
],
|
|
@@ -46,7 +44,7 @@
|
|
|
46
44
|
"prepack": "node ../../scripts/release-pack.mjs --guard"
|
|
47
45
|
},
|
|
48
46
|
"dependencies": {
|
|
49
|
-
"@skill-harness/core": "0.
|
|
50
|
-
"@skill-harness/adapters": "0.
|
|
47
|
+
"@skill-harness/core": "0.19.0",
|
|
48
|
+
"@skill-harness/adapters": "0.19.0"
|
|
51
49
|
}
|
|
52
50
|
}
|