@skill-harness/cli 0.32.3 → 0.33.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/assets/report.template.html +3 -2
- package/dist/cli.js +29 -14
- package/dist/diagnose-format.d.ts +3 -0
- package/dist/diagnose-format.js +37 -0
- package/package.json +48 -48
|
@@ -177,6 +177,7 @@ function render() {
|
|
|
177
177
|
const errorNote = g.errors > 0 ? ` — ${g.errors} infrastructure error${g.errors === 1 ? "" : "s"}` : "";
|
|
178
178
|
gradeHtml = `${g.letter} (${g.pct}%) · ${g.passed}/${g.total}${suspectNote}${errorNote} ${badge}`;
|
|
179
179
|
}
|
|
180
|
+
const summaryHtml = col.summaryLines ? `<details><summary>Evidence, profiles and usage</summary><div class="meta">${col.summaryLines.map(line => escapeHtml(line)).join("<br>")}</div></details>` : "";
|
|
180
181
|
const m = col.metrics;
|
|
181
182
|
const metricHtml = m && m.total_reps > 0
|
|
182
183
|
? `<div class='grade' title='subject metrics reported by ${m.subject_metrics_reps}/${m.total_reps} reps'>` +
|
|
@@ -200,7 +201,7 @@ function render() {
|
|
|
200
201
|
: `lift 0 · no effect`;
|
|
201
202
|
const inc = ls.inconclusive > 0 ? ` · ${ls.inconclusive} inconclusive` : "";
|
|
202
203
|
const part = col.lift.partial ? " · partial" : "";
|
|
203
|
-
html += `<th><div>${escapeHtml(col.label)}</div><div class='grade'>${gradeHtml}</div>${metricHtml}` +
|
|
204
|
+
html += `<th><div>${escapeHtml(col.label)}</div><div class='grade'>${gradeHtml}</div>${metricHtml}${summaryHtml}` +
|
|
204
205
|
`<div class='lift ${cls}' title='vs red baseline ${escapeHtml(col.lift.redTimestamp)} — ${ls.kept} passed without the skill too'>${body}${inc}${part}</div></th>`;
|
|
205
206
|
return;
|
|
206
207
|
}
|
|
@@ -210,7 +211,7 @@ function render() {
|
|
|
210
211
|
if (none) {
|
|
211
212
|
liftHtml = `<div class='lift none' title='${escapeHtml(none.title)}'>${escapeHtml(none.text)}</div>`;
|
|
212
213
|
}
|
|
213
|
-
html += `<th><div>${escapeHtml(col.label)}</div><div class='grade'>${gradeHtml}</div>${metricHtml}${liftHtml}</th>`;
|
|
214
|
+
html += `<th><div>${escapeHtml(col.label)}</div><div class='grade'>${gradeHtml}</div>${metricHtml}${summaryHtml}${liftHtml}</th>`;
|
|
214
215
|
});
|
|
215
216
|
html += "</tr></thead><tbody>";
|
|
216
217
|
for (const scn of DATA.scenarios) {
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { withThinking, modelProfileLabel } from "@skill-harness/core";
|
|
2
3
|
import { readFileSync, existsSync, mkdirSync, writeFileSync, mkdtempSync, rmSync } from "node:fs";
|
|
3
4
|
import { basename, dirname, join, resolve } from "node:path";
|
|
4
5
|
import { tmpdir } from "node:os";
|
|
5
|
-
import { discover, resolveSkill, loadSpec, parseSpec, appendScenario, parseModelRef, runSkillModel, formatScorecard, readResults, regradeRun, lintSkill, failsGate, renderTemplateSpec, isTemplateSpec, renderDraftSpec, buildSuggestPrompt, parseSuggestDraft, rescoreRun, regateRun, specPathForRunDir, collectLift, collectReport, collectStability, boundaryCells, stabilityNote, PATH_LEGEND, restampSkill, computeCoverage, formatCoverage, HARNESS_VERSION, defaultJudge, recordedJudgeOrDefault, assertJudgeAllowed, assertNotDowngraded, downgradeWarning, isScoredMode, resolveArm, isFreeOfflineCommand, diagnoseRun, loadCalibration, runCalibration, planCalibration, forkSavedRun, withProcessScope, } from "@skill-harness/core";
|
|
6
|
+
import { discover, resolveSkill, loadSpec, parseSpec, appendScenario, parseModelRef, runSkillModel, formatScorecard, readResults, regradeRun, lintSkill, failsGate, renderTemplateSpec, isTemplateSpec, renderDraftSpec, buildSuggestPrompt, parseSuggestDraft, rescoreRun, regateRun, specPathForRunDir, collectLift, collectReport, collectStability, boundaryCells, stabilityNote, PATH_LEGEND, restampSkill, computeCoverage, formatCoverage, HARNESS_VERSION, defaultJudge, recordedJudgeOrDefault, assertJudgeAllowed, assertNotDowngraded, downgradeWarning, isScoredMode, resolveArm, isFreeOfflineCommand, diagnoseRun, requestedSubject, loadCalibration, runCalibration, planCalibration, forkSavedRun, withProcessScope, } from "@skill-harness/core";
|
|
6
7
|
import { getAdapter } from "@skill-harness/adapters";
|
|
7
8
|
import { serveReview } from "./serve.js";
|
|
8
9
|
import { cmdDecision } from "./decision.js";
|
|
10
|
+
import { formatDiagnosis } from "./diagnose-format.js";
|
|
9
11
|
const DEFAULT_MODEL = "fireworks:accounts/fireworks/models/deepseek-v4-pro";
|
|
10
12
|
// The judge default lives in core (`defaultJudge()`), which resolves
|
|
11
13
|
// SKILL_HARNESS_JUDGE over a baked value — it was duplicated in three places
|
|
@@ -172,7 +174,7 @@ export async function cmdRun(args, adapterOverride) {
|
|
|
172
174
|
// any subject tokens are spent: a refusal that arrives after the model has been
|
|
173
175
|
// paid for is a worse version of the problem it exists to prevent.
|
|
174
176
|
const judgeFlagRun = flagStr(args, "judge");
|
|
175
|
-
const judge = parseModelRef(judgeFlagRun ?? defaultJudge());
|
|
177
|
+
const judge = withThinking(parseModelRef(judgeFlagRun ?? defaultJudge()), flagStr(args, "judge-thinking"));
|
|
176
178
|
assertJudgeAllowed(judge, {
|
|
177
179
|
source: judgeFlagRun ? "--judge" : "the default judge (SKILL_HARNESS_JUDGE or the baked value)",
|
|
178
180
|
allowMetered: flagBool(args, "allow-metered-judge"),
|
|
@@ -184,6 +186,7 @@ export async function cmdRun(args, adapterOverride) {
|
|
|
184
186
|
// that was never the problem. Free, offline validation first — it is also why
|
|
185
187
|
// CI, which has no `pi`, could not test this refusal at all.
|
|
186
188
|
const only = selectedIds(args);
|
|
189
|
+
const models = resolveModels(args).map(token => ({ token, model: withThinking(parseModelRef(token), flagStr(args, "thinking")) }));
|
|
187
190
|
const harnessName = flagStr(args, "harness", "pi");
|
|
188
191
|
const adapter = adapterOverride ?? getAdapter(harnessName);
|
|
189
192
|
if (!(await adapter.available()))
|
|
@@ -194,7 +197,6 @@ export async function cmdRun(args, adapterOverride) {
|
|
|
194
197
|
const label = flagStr(args, "label") || null;
|
|
195
198
|
const parallel = Math.max(1, Number(flagStr(args, "parallel", "1")) || 1);
|
|
196
199
|
const { reps, passThreshold } = parseRunTuning(args);
|
|
197
|
-
const modelTokens = resolveModels(args);
|
|
198
200
|
// Resolved ONCE, before the skill loop: a typo'd arm name fails before any
|
|
199
201
|
// token is spent on any skill.
|
|
200
202
|
const armName = flagStr(args, "arm") || null;
|
|
@@ -212,12 +214,11 @@ export async function cmdRun(args, adapterOverride) {
|
|
|
212
214
|
// that look comparable and are not. Checked per skill, before its first token.
|
|
213
215
|
assertNotDowngraded(dirname(skill.testsDir), "run");
|
|
214
216
|
const spec = loadSpec(skill.specPath);
|
|
215
|
-
for (const token of
|
|
216
|
-
const model = parseModelRef(token);
|
|
217
|
+
for (const { token, model } of models) {
|
|
217
218
|
// The version is on the banner because a stale global install is otherwise
|
|
218
219
|
// invisible: a 0.1.0 binary grades a 0.3.x corpus, produces plausible
|
|
219
220
|
// numbers, and nothing on screen says which tool made them.
|
|
220
|
-
console.log(`\n▶ ${spec.skill} · ${harnessName}:${token} · mode=${mode} ·
|
|
221
|
+
console.log(`\n▶ ${spec.skill} · ${harnessName}:${token} · mode=${mode} · subject=${modelProfileLabel(model)} · judge=${modelProfileLabel(judge)} · skill-harness ${HARNESS_VERSION}`);
|
|
221
222
|
const summary = await runSkillModel({
|
|
222
223
|
spec,
|
|
223
224
|
skillDir: skill.dir,
|
|
@@ -279,10 +280,10 @@ export async function cmdGrade(args, adapterOverride) {
|
|
|
279
280
|
// the current Pi default.
|
|
280
281
|
const judgeFlag = flagStr(args, "judge");
|
|
281
282
|
const onlyUnparsed = flagBool(args, "unparsed-only");
|
|
282
|
-
if (onlyUnparsed && judgeFlag !== undefined)
|
|
283
|
+
if (onlyUnparsed && (judgeFlag !== undefined || flagStr(args, "judge-thinking") !== undefined))
|
|
283
284
|
throw new Error("--unparsed-only uses the recorded judge; --judge cannot be combined with it");
|
|
284
285
|
const resolvedRecordedJudge = judgeFlag ? undefined : recordedJudgeOrDefault(prev?.judge);
|
|
285
|
-
const judge = judgeFlag ? parseModelRef(judgeFlag) : resolvedRecordedJudge.judge;
|
|
286
|
+
const judge = withThinking(judgeFlag ? parseModelRef(judgeFlag) : resolvedRecordedJudge.judge, flagStr(args, "judge-thinking"));
|
|
286
287
|
if (onlyUnparsed && resolvedRecordedJudge?.migratedFrom)
|
|
287
288
|
throw new Error("--unparsed-only cannot use this retired recorded judge; use grade to migrate it explicitly");
|
|
288
289
|
if (resolvedRecordedJudge?.migratedFrom) {
|
|
@@ -368,7 +369,7 @@ export async function cmdRegate(args, adapterOverride) {
|
|
|
368
369
|
const spec = loadSpec(specPath);
|
|
369
370
|
const prev = readResults(runDir);
|
|
370
371
|
const resolvedRecordedJudge = judgeFlag ? undefined : recordedJudgeOrDefault(prev.judge);
|
|
371
|
-
const judge = judgeFlag ? parseModelRef(judgeFlag) : resolvedRecordedJudge.judge;
|
|
372
|
+
const judge = withThinking(judgeFlag ? parseModelRef(judgeFlag) : resolvedRecordedJudge.judge, flagStr(args, "judge-thinking"));
|
|
372
373
|
if (resolvedRecordedJudge?.migratedFrom) {
|
|
373
374
|
console.error(`skill-harness: recorded judge ${resolvedRecordedJudge.migratedFrom.provider}:${resolvedRecordedJudge.migratedFrom.model} was removed; re-judging through Pi with ${judge.provider}:${judge.model}`);
|
|
374
375
|
}
|
|
@@ -750,6 +751,7 @@ export function help() {
|
|
|
750
751
|
return `skill-harness ${HARNESS_VERSION} — test/optimize loop for agent skills (pi harness)
|
|
751
752
|
|
|
752
753
|
run <skill|all> --skills <root> [--model prov:model ...] [--models file] [--only A1,D2]
|
|
754
|
+
[--thinking LEVEL] [--judge-thinking LEVEL]
|
|
753
755
|
[--mode red|green|force] [--judge prov:model] [--harness pi] [--label name] [--parallel N] [--reps N] [--pass-threshold T]
|
|
754
756
|
[--canary] green only: one instructed-loading probe; autonomous selection stays untested
|
|
755
757
|
[--structured] request structured capture (Pi uses it by default for subject usage)
|
|
@@ -768,7 +770,7 @@ export function help() {
|
|
|
768
770
|
preflight <skill|all> --skills <root> [--specs <root>] eval checks (${free("preflight")})
|
|
769
771
|
calibrate <manifest.json> [--run --judge p:m --out NEW_DIR] validate or judge reviewed controls
|
|
770
772
|
[--plan --judge p:m] [--only c1,c2] [--reuse PRIOR_DIR ...] exact historical reuse, no new subject calls
|
|
771
|
-
diagnose <run-dir> --skills <root> [--specs <root>] inspect saved evidence (${free("diagnose")})
|
|
773
|
+
diagnose <run-dir> --skills <root> [--specs <root>] [--format json|text] inspect saved evidence (${free("diagnose")})
|
|
772
774
|
Paid commands accept --max-memory-mb N / --max-write-mb N for sampled Linux process limits.
|
|
773
775
|
run/grade/calibrate save bounded private resources-*.json; unsupported host metrics stay unknown.
|
|
774
776
|
In Pi: /skill-harness create <idea> guided skill + optional agent + eval creation
|
|
@@ -807,12 +809,25 @@ export async function cmdPreflight(args) {
|
|
|
807
809
|
}
|
|
808
810
|
export async function cmdDiagnose(args) {
|
|
809
811
|
if (!args._[0])
|
|
810
|
-
throw new Error("usage: skill-harness diagnose <run-dir> --skills <root> [--specs <root>]");
|
|
812
|
+
throw new Error("usage: skill-harness diagnose <run-dir> --skills <root> [--specs <root>] [--format json|text]");
|
|
813
|
+
const format = flagStr(args, "format") ?? "json";
|
|
814
|
+
if (format !== "json" && format !== "text")
|
|
815
|
+
throw new Error("diagnose --format must be json or text");
|
|
811
816
|
const runDir = resolve(args._[0]);
|
|
812
817
|
const recorded = readResults(runDir);
|
|
813
818
|
const { skills, specs } = roots(args);
|
|
814
819
|
const selected = resolveSkill(skills, recorded.skill, specs);
|
|
815
|
-
|
|
820
|
+
if ((args.multi.model?.length ?? 0) > 1)
|
|
821
|
+
throw new Error("diagnose previews one subject profile at a time");
|
|
822
|
+
const modelToken = args.multi.model?.[0] ?? flagStr(args, "model"), judgeToken = flagStr(args, "judge");
|
|
823
|
+
if (modelToken?.includes(","))
|
|
824
|
+
throw new Error("diagnose previews one subject profile at a time");
|
|
825
|
+
const profiles = {
|
|
826
|
+
subject: modelToken !== undefined || Object.hasOwn(args.flags, "thinking") ? withThinking(modelToken !== undefined ? parseModelRef(modelToken) : requestedSubject(recorded), flagStr(args, "thinking")) : undefined,
|
|
827
|
+
judge: judgeToken !== undefined || Object.hasOwn(args.flags, "judge-thinking") ? withThinking(judgeToken !== undefined ? parseModelRef(judgeToken) : recorded.judge, flagStr(args, "judge-thinking")) : undefined,
|
|
828
|
+
};
|
|
829
|
+
const report = diagnoseRun(runDir, selected.dir, selected.specPath, profiles);
|
|
830
|
+
console.log(format === "text" ? formatDiagnosis(report) : JSON.stringify(report, null, 2));
|
|
816
831
|
}
|
|
817
832
|
export async function cmdCalibrate(args, adapterOverride) {
|
|
818
833
|
if (!args._[0])
|
|
@@ -825,7 +840,7 @@ export async function cmdCalibrate(args, adapterOverride) {
|
|
|
825
840
|
if (flagBool(args, "plan")) {
|
|
826
841
|
if (!judgeToken)
|
|
827
842
|
throw new Error("Calibration --plan needs an explicit --judge; it makes zero provider calls.");
|
|
828
|
-
const plan = planCalibration(calibration, { judge: parseModelRef(judgeToken), only, reuse });
|
|
843
|
+
const plan = planCalibration(calibration, { judge: withThinking(parseModelRef(judgeToken), flagStr(args, "judge-thinking")), only, reuse });
|
|
829
844
|
console.log(JSON.stringify({ ...plan, providerCalls: 0 }, null, 2));
|
|
830
845
|
if (!plan.ready)
|
|
831
846
|
process.exitCode = 1;
|
|
@@ -843,7 +858,7 @@ export async function cmdCalibrate(args, adapterOverride) {
|
|
|
843
858
|
const adapter = adapterOverride ?? getAdapter("pi");
|
|
844
859
|
if (!await adapter.available())
|
|
845
860
|
throw new Error("Pi is required for judge calibration. Configure the selected model in Pi first.");
|
|
846
|
-
const result = await runCalibration(calibration, { adapter, judge: parseModelRef(judgeToken), only, reuse, output: resolve(output), allowMeteredJudge: flagBool(args, "allow-metered-judge"), onProgress: p => console.log(`Judging control ${p.index + 1}/${p.total}: ${p.id}`) });
|
|
861
|
+
const result = await runCalibration(calibration, { adapter, judge: withThinking(parseModelRef(judgeToken), flagStr(args, "judge-thinking")), only, reuse, output: resolve(output), allowMeteredJudge: flagBool(args, "allow-metered-judge"), onProgress: p => console.log(`Judging control ${p.index + 1}/${p.total}: ${p.id}`) });
|
|
847
862
|
console.log(JSON.stringify(result, null, 2));
|
|
848
863
|
if (result.status !== "matched")
|
|
849
864
|
process.exitCode = 1;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { modelProfileLabel } from "@skill-harness/core";
|
|
2
|
+
/** Human view of the same offline report; never a second scoring policy. */
|
|
3
|
+
export function formatDiagnosis(report) {
|
|
4
|
+
const grouped = report.criterionDimensions;
|
|
5
|
+
const lines = [
|
|
6
|
+
`${grouped.skill} · ${grouped.model} · ${grouped.mode}`,
|
|
7
|
+
"Saved criterion votes — current author-declared grouping, not a new assessment.",
|
|
8
|
+
"Counts are criterion observations, not passing answers. Mixed criteria remain indivisible.",
|
|
9
|
+
"No model calls; saved results and acceptance rules unchanged.",
|
|
10
|
+
`Coverage: ${grouped.coverage.availableVotes}/${grouped.coverage.expectedVotes} known-size votes available; ${grouped.coverage.unknownScenarioSizes} scenario sizes unknown.`,
|
|
11
|
+
`Mapping: ${grouped.metadataSha256}`,
|
|
12
|
+
];
|
|
13
|
+
if (report.summary) {
|
|
14
|
+
const summary = report.summary;
|
|
15
|
+
lines.push(`Subject requested: ${modelProfileLabel(summary.subject.requested)}; effective thinking: unknown.`, `Judge requested: ${modelProfileLabel(summary.judge.requested)}; effective thinking: unknown.`, `Retained token observations: ${summary.usage.subject.recordedInvocations} subject invocations (total unknown); ${summary.usage.judge.recordedInvocations}/${summary.usage.judge.expectedInvocations ?? "unknown"} judge attempts. Full per-counter coverage: --format json. Costs are separate.`, summary.limits);
|
|
16
|
+
}
|
|
17
|
+
for (const scenario of grouped.scenarios) {
|
|
18
|
+
lines.push("", `${scenario.id}: recorded ${scenario.original.judge_verdict}; override ${scenario.original.override ?? "none"}; objective ${scenario.original.objective?.status ?? "not recorded"}`);
|
|
19
|
+
for (const [dimension, counts] of Object.entries(scenario.counts)) {
|
|
20
|
+
if (counts.pass + counts.fail + counts.unavailable > 0)
|
|
21
|
+
lines.push(` ${dimension}: ${counts.pass} PASS, ${counts.fail} FAIL, ${counts.unavailable} unavailable`);
|
|
22
|
+
}
|
|
23
|
+
const reasons = new Set([scenario.unavailableReason, ...scenario.repetitions.map(rep => rep.unavailableReason)].filter(Boolean));
|
|
24
|
+
for (const reason of reasons)
|
|
25
|
+
lines.push(` Unavailable: ${reason}`);
|
|
26
|
+
}
|
|
27
|
+
if (report.issues.length) {
|
|
28
|
+
lines.push("", "Evidence and next checks:");
|
|
29
|
+
for (const issue of report.issues)
|
|
30
|
+
lines.push(`- ${issue.scenario ? issue.scenario + ": " : ""}${issue.category}: ${issue.evidence}\n Next: ${issue.next}`);
|
|
31
|
+
}
|
|
32
|
+
lines.push("", "Reuse plan:");
|
|
33
|
+
for (const item of report.reusePlan.scenarios)
|
|
34
|
+
lines.push(`- ${item.id}: ${item.action} — ${item.note}`);
|
|
35
|
+
return lines.join("\n");
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=diagnose-format.js.map
|
package/package.json
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
2
|
+
"name": "@skill-harness/cli",
|
|
3
|
+
"version": "0.33.0",
|
|
4
|
+
"description": "skill-harness CLI \u2014 run, grade, review, and lint agent-skill scenarios on the pi harness",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/cli.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"skill-harness": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./dist/cli.js",
|
|
13
|
+
"./serve": "./dist/serve.js"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"agent-skills",
|
|
17
|
+
"skill",
|
|
18
|
+
"llm",
|
|
19
|
+
"testing",
|
|
20
|
+
"eval",
|
|
21
|
+
"grading",
|
|
22
|
+
"pi",
|
|
23
|
+
"ci",
|
|
24
|
+
"harness"
|
|
25
|
+
],
|
|
26
|
+
"files": [
|
|
27
|
+
"dist/**/*.js",
|
|
28
|
+
"dist/**/*.d.ts",
|
|
29
|
+
"assets",
|
|
30
|
+
"LICENSE",
|
|
31
|
+
"README.md"
|
|
32
|
+
],
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/mojomanyana/skill-harness.git"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=20"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"prepack": "node ../../scripts/release-pack.mjs --guard"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@skill-harness/core": "0.33.0",
|
|
48
|
+
"@skill-harness/adapters": "0.33.0"
|
|
49
|
+
}
|
|
50
50
|
}
|