@skill-harness/cli 0.20.0 → 0.21.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/dist/cli.d.ts +1 -1
- package/dist/cli.js +76 -45
- package/dist/serve.js +1 -0
- package/package.json +3 -3
package/dist/cli.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export declare function releaseExitCode(summaries: Array<{
|
|
|
22
22
|
};
|
|
23
23
|
};
|
|
24
24
|
}>): 0 | 1;
|
|
25
|
-
export declare function cmdRun(args: Args): Promise<void>;
|
|
25
|
+
export declare function cmdRun(args: Args, adapterOverride?: HarnessAdapter): Promise<void>;
|
|
26
26
|
export declare function cmdGrade(args: Args, adapterOverride?: HarnessAdapter): Promise<void>;
|
|
27
27
|
/**
|
|
28
28
|
* Re-evaluate needle gates against the saved staged diffs — no subject call. A rep
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFileSync, existsSync, mkdirSync, writeFileSync, mkdtempSync, rmSync } from "node:fs";
|
|
3
|
-
import { basename, dirname, join, resolve
|
|
3
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
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, recordedJudgeOrDefault, assertJudgeAllowed, assertNotDowngraded, downgradeWarning, isScoredMode, resolveArm, isFreeOfflineCommand, } from "@skill-harness/core";
|
|
6
6
|
import { getAdapter } from "@skill-harness/adapters";
|
|
@@ -82,6 +82,20 @@ function resolveModels(args) {
|
|
|
82
82
|
function nowIso() {
|
|
83
83
|
return new Date().toISOString();
|
|
84
84
|
}
|
|
85
|
+
function roots(args) {
|
|
86
|
+
return {
|
|
87
|
+
skills: flagStr(args, "skills", process.cwd()),
|
|
88
|
+
specs: flagStr(args, "specs", process.env.SKILL_HARNESS_SPECS) || undefined,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function discoverUsable(skillsRoot, specsRoot) {
|
|
92
|
+
const skills = discover(skillsRoot, specsRoot);
|
|
93
|
+
const duplicate = skills.find((skill) => skill.specSource === "both");
|
|
94
|
+
if (duplicate) {
|
|
95
|
+
throw new Error(`skill \`${duplicate.name}\` has a specification.yaml in both --skills and --specs; remove one (specs are not merged)`);
|
|
96
|
+
}
|
|
97
|
+
return skills;
|
|
98
|
+
}
|
|
85
99
|
/** Parse the run's reps + pass-threshold flags. Throws on an invalid provided value. */
|
|
86
100
|
export function parseRunTuning(args) {
|
|
87
101
|
let reps = 1;
|
|
@@ -104,31 +118,41 @@ export function parseRunTuning(args) {
|
|
|
104
118
|
}
|
|
105
119
|
// ---------------------------------------------------------------- commands
|
|
106
120
|
async function cmdList(args) {
|
|
107
|
-
const root =
|
|
108
|
-
const skills = discover(root);
|
|
121
|
+
const { skills: root, specs } = roots(args);
|
|
122
|
+
const skills = discover(root, specs);
|
|
123
|
+
let duplicate = false;
|
|
109
124
|
console.log(`skills under ${root}:`);
|
|
110
125
|
for (const s of skills) {
|
|
126
|
+
if (s.specSource === "both") {
|
|
127
|
+
duplicate = true;
|
|
128
|
+
console.log(` ✗ ${s.name} INVALID: specification.yaml exists in both --skills and --specs (specs are not merged)`);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
111
131
|
if (!s.hasSpec) {
|
|
112
|
-
|
|
132
|
+
const localOnly = specs && s.specSource === "skills" ? "; spec found under --skills" : "";
|
|
133
|
+
console.log(` ○ ${s.name} (no spec${localOnly})`);
|
|
113
134
|
continue;
|
|
114
135
|
}
|
|
115
136
|
try {
|
|
116
137
|
const spec = loadSpec(s.specPath);
|
|
117
138
|
const seeded = spec.scenarios.filter((x) => x.mode === "seeded").length;
|
|
118
139
|
const seededNote = seeded ? `, ${seeded} seeded` : "";
|
|
119
|
-
|
|
140
|
+
const sourceNote = specs ? `; spec: --${s.specSource}` : "";
|
|
141
|
+
console.log(` ● ${s.name} (${spec.scenarios.length} scenarios${seededNote}${sourceNote})`);
|
|
120
142
|
}
|
|
121
143
|
catch (e) {
|
|
122
144
|
console.log(` ✗ ${s.name} INVALID: ${e instanceof Error ? e.message : e}`);
|
|
123
145
|
}
|
|
124
146
|
}
|
|
125
147
|
console.log(`\n● = testable · ○ = no spec yet · ✗ = spec present but invalid`);
|
|
148
|
+
if (duplicate)
|
|
149
|
+
process.exitCode = 1;
|
|
126
150
|
}
|
|
127
151
|
export function releaseExitCode(summaries) {
|
|
128
152
|
return summaries.some(({ results }) => isScoredMode(results.mode) && !results.partial && !results.effective_grade.ship) ? 1 : 0;
|
|
129
153
|
}
|
|
130
|
-
export async function cmdRun(args) {
|
|
131
|
-
const root =
|
|
154
|
+
export async function cmdRun(args, adapterOverride) {
|
|
155
|
+
const { skills: root, specs } = roots(args);
|
|
132
156
|
const target = args._[0];
|
|
133
157
|
if (!target)
|
|
134
158
|
throw new Error("usage: skill-harness run <skill|all> --skills <root>");
|
|
@@ -150,7 +174,7 @@ export async function cmdRun(args) {
|
|
|
150
174
|
const onlyRaw = flagStr(args, "only");
|
|
151
175
|
const only = onlyRaw ? onlyRaw.split(",").map((x) => x.trim()).filter(Boolean) : undefined;
|
|
152
176
|
const harnessName = flagStr(args, "harness", "pi");
|
|
153
|
-
const adapter = getAdapter(harnessName);
|
|
177
|
+
const adapter = adapterOverride ?? getAdapter(harnessName);
|
|
154
178
|
if (!(await adapter.available()))
|
|
155
179
|
throw new Error(`harness \`${harnessName}\` is not on PATH`);
|
|
156
180
|
const mode = flagStr(args, "mode", "green") || "green";
|
|
@@ -165,8 +189,8 @@ export async function cmdRun(args) {
|
|
|
165
189
|
const armName = flagStr(args, "arm") || null;
|
|
166
190
|
const arm = resolveArm(root, armName);
|
|
167
191
|
const skills = target === "all"
|
|
168
|
-
?
|
|
169
|
-
: [resolveSkill(root, target)];
|
|
192
|
+
? discoverUsable(root, specs).filter((s) => s.hasSpec)
|
|
193
|
+
: [resolveSkill(root, target, specs)];
|
|
170
194
|
const summaries = [];
|
|
171
195
|
for (const skill of skills) {
|
|
172
196
|
if (!skill.hasSpec) {
|
|
@@ -175,7 +199,7 @@ export async function cmdRun(args) {
|
|
|
175
199
|
}
|
|
176
200
|
// A run from an older tool than the records already here would produce numbers
|
|
177
201
|
// that look comparable and are not. Checked per skill, before its first token.
|
|
178
|
-
assertNotDowngraded(skill.
|
|
202
|
+
assertNotDowngraded(dirname(skill.testsDir), "run");
|
|
179
203
|
const spec = loadSpec(skill.specPath);
|
|
180
204
|
for (const token of modelTokens) {
|
|
181
205
|
const model = parseModelRef(token);
|
|
@@ -187,6 +211,8 @@ export async function cmdRun(args) {
|
|
|
187
211
|
spec,
|
|
188
212
|
skillDir: skill.dir,
|
|
189
213
|
specPath: skill.specPath,
|
|
214
|
+
testsDir: skill.testsDir,
|
|
215
|
+
recordSourceRoots: skill.specSource === "specs",
|
|
190
216
|
adapter,
|
|
191
217
|
model,
|
|
192
218
|
modelToken: token,
|
|
@@ -208,15 +234,16 @@ export async function cmdRun(args) {
|
|
|
208
234
|
// Lift is derived from what's on disk, so it picks up a red baseline from
|
|
209
235
|
// any earlier run — the tag dir (<harness>-<modelslug>) is the join key.
|
|
210
236
|
const tag = basename(dirname(summary.runDir));
|
|
211
|
-
const
|
|
237
|
+
const resultsSkillDir = dirname(skill.testsDir);
|
|
238
|
+
const lift = collectLift(resultsSkillDir).find((l) => l.tag === tag);
|
|
212
239
|
// Stability is derived from history INCLUDING the run just written, and scoped to
|
|
213
240
|
// this tag + mode: another model's flips under this model's scorecard would be a
|
|
214
241
|
// worse error than not reporting them at all.
|
|
215
|
-
const stability = collectStability(
|
|
242
|
+
const stability = collectStability(resultsSkillDir).filter((c) => c.tag === tag && c.mode === summary.results.mode);
|
|
216
243
|
console.log("\n" + formatScorecard(summary, lift, stability) + "\n");
|
|
217
244
|
}
|
|
218
245
|
}
|
|
219
|
-
console.log(`\nReview interactively: skill-harness review ${skills[0]?.name ?? "<skill>"} --skills ${root}`);
|
|
246
|
+
console.log(`\nReview interactively: skill-harness review ${skills[0]?.name ?? "<skill>"} --skills ${root}${specs ? ` --specs ${specs}` : ""}`);
|
|
220
247
|
// A full delivered run is a release gate. NOT READY — including one critical
|
|
221
248
|
// failure hidden by a high aggregate — must be machine-visible to CI. Red
|
|
222
249
|
// baselines and partial branch feedback are deliberately excluded.
|
|
@@ -363,9 +390,9 @@ export async function cmdRegate(args, adapterOverride) {
|
|
|
363
390
|
* inventing freshness is the one thing this gate must never do.
|
|
364
391
|
*/
|
|
365
392
|
async function cmdRestamp(args) {
|
|
366
|
-
const root =
|
|
393
|
+
const { skills: root, specs } = roots(args);
|
|
367
394
|
const target = args._[0] ?? "all";
|
|
368
|
-
const skills = target === "all" ?
|
|
395
|
+
const skills = target === "all" ? discoverUsable(root, specs).filter((s) => s.hasSpec) : [resolveSkill(root, target, specs)];
|
|
369
396
|
if (skills.length === 0)
|
|
370
397
|
throw new Error(`no skills with a spec under ${root}`);
|
|
371
398
|
let runs = 0;
|
|
@@ -374,7 +401,7 @@ async function cmdRestamp(args) {
|
|
|
374
401
|
let unchanged = 0;
|
|
375
402
|
let partial = 0;
|
|
376
403
|
for (const skill of skills) {
|
|
377
|
-
const r = restampSkill(skill.dir, { from: flagStr(args, "from") });
|
|
404
|
+
const r = restampSkill(skill.dir, { from: flagStr(args, "from"), testsDir: skill.testsDir });
|
|
378
405
|
runs += r.runs;
|
|
379
406
|
upgraded += r.upgraded;
|
|
380
407
|
unprovable += r.unprovable;
|
|
@@ -396,7 +423,7 @@ async function cmdRestamp(args) {
|
|
|
396
423
|
`${partial > 0 ? ` (${partial} upgraded only in part.)` : ""} No models were called.`);
|
|
397
424
|
}
|
|
398
425
|
async function cmdStability(args) {
|
|
399
|
-
const root =
|
|
426
|
+
const { skills: root, specs } = roots(args);
|
|
400
427
|
const target = args._[0] ?? "all";
|
|
401
428
|
const windowRaw = flagStr(args, "window");
|
|
402
429
|
const window = windowRaw ? Number(windowRaw) : undefined;
|
|
@@ -404,12 +431,12 @@ async function cmdStability(args) {
|
|
|
404
431
|
throw new Error(`--window must be an integer >= 2 (got \`${windowRaw}\`) — one run has no run-over-run step`);
|
|
405
432
|
}
|
|
406
433
|
const showAll = flagBool(args, "all");
|
|
407
|
-
const skills = target === "all" ?
|
|
434
|
+
const skills = target === "all" ? discoverUsable(root, specs).filter((s) => s.hasSpec) : [resolveSkill(root, target, specs)];
|
|
408
435
|
if (skills.length === 0)
|
|
409
436
|
throw new Error(`no skills with a spec under ${root}`);
|
|
410
437
|
let boundaries = 0;
|
|
411
438
|
for (const skill of skills) {
|
|
412
|
-
const all = collectStability(skill.
|
|
439
|
+
const all = collectStability(dirname(skill.testsDir), { window });
|
|
413
440
|
if (all.length === 0) {
|
|
414
441
|
console.log(`\n${skill.name}: no scored runs yet — stability needs at least two runs of the same skill × model × mode`);
|
|
415
442
|
continue;
|
|
@@ -450,20 +477,20 @@ async function cmdStability(args) {
|
|
|
450
477
|
}
|
|
451
478
|
}
|
|
452
479
|
async function cmdReview(args) {
|
|
453
|
-
const root =
|
|
480
|
+
const { skills: root, specs } = roots(args);
|
|
454
481
|
const target = args._[0];
|
|
455
482
|
if (!target)
|
|
456
483
|
throw new Error("usage: skill-harness review <skill> --skills <root>");
|
|
457
|
-
const skill = resolveSkill(root, target);
|
|
484
|
+
const skill = resolveSkill(root, target, specs);
|
|
458
485
|
const port = Number(flagStr(args, "port", "0")) || 0;
|
|
459
|
-
await serveReview({ skillDir: skill.
|
|
486
|
+
await serveReview({ skillDir: dirname(skill.testsDir), skillName: skill.name, port });
|
|
460
487
|
}
|
|
461
488
|
async function cmdAddTest(args) {
|
|
462
|
-
const root =
|
|
489
|
+
const { skills: root, specs } = roots(args);
|
|
463
490
|
const target = args._[0];
|
|
464
491
|
if (!target)
|
|
465
492
|
throw new Error("usage: skill-harness add-test <skill> --skills <root> --id ... --title ... --turn ... --check ...");
|
|
466
|
-
const skill = resolveSkill(root, target);
|
|
493
|
+
const skill = resolveSkill(root, target, specs);
|
|
467
494
|
if (!skill.hasSpec)
|
|
468
495
|
throw new Error(`${target} has no spec yet — create tests/specification.yaml first`);
|
|
469
496
|
const id = flagStr(args, "id");
|
|
@@ -497,12 +524,12 @@ async function cmdAddTest(args) {
|
|
|
497
524
|
* reddens CI for it teaches people to add a token `covers:` to silence it.
|
|
498
525
|
*/
|
|
499
526
|
async function cmdCoverage(args) {
|
|
500
|
-
const root =
|
|
527
|
+
const { skills: root, specs } = roots(args);
|
|
501
528
|
const target = args._[0];
|
|
502
529
|
if (!target)
|
|
503
530
|
throw new Error("usage: skill-harness coverage <skill|all> --skills <root> [--strict]");
|
|
504
531
|
const strict = flagBool(args, "strict");
|
|
505
|
-
const skills = target === "all" ?
|
|
532
|
+
const skills = target === "all" ? discoverUsable(root, specs).filter((s) => s.hasSpec) : [resolveSkill(root, target, specs)];
|
|
506
533
|
let anyUncovered = false;
|
|
507
534
|
let anyBroken = false;
|
|
508
535
|
for (const skill of skills) {
|
|
@@ -510,13 +537,13 @@ async function cmdCoverage(args) {
|
|
|
510
537
|
continue;
|
|
511
538
|
const spec = loadSpec(skill.specPath);
|
|
512
539
|
const specDir = dirname(skill.specPath);
|
|
540
|
+
const skillRef = "../SKILL.md";
|
|
513
541
|
const report = computeCoverage({
|
|
514
542
|
specDir,
|
|
515
543
|
scenarios: spec.scenarios,
|
|
516
|
-
//
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
baseFiles: [relative(specDir, join(skill.dir, "SKILL.md")).split("\\").join("/")],
|
|
544
|
+
// Keep the spec-facing path stable even when SKILL.md lives in another repo.
|
|
545
|
+
baseFiles: [skillRef],
|
|
546
|
+
fileOverrides: { [skillRef]: join(skill.dir, "SKILL.md") },
|
|
520
547
|
});
|
|
521
548
|
console.log(formatCoverage(report, spec.skill));
|
|
522
549
|
if (report.uncovered.length)
|
|
@@ -543,11 +570,11 @@ function writeSpecFile(specPath, text) {
|
|
|
543
570
|
writeFileSync(specPath, text, "utf8");
|
|
544
571
|
}
|
|
545
572
|
export async function cmdInit(args) {
|
|
546
|
-
const root =
|
|
573
|
+
const { skills: root, specs } = roots(args);
|
|
547
574
|
const target = args._[0];
|
|
548
575
|
if (!target)
|
|
549
576
|
throw new Error("usage: skill-harness init <skill> --skills <root> [--force]");
|
|
550
|
-
const skill = resolveSkill(root, target);
|
|
577
|
+
const skill = resolveSkill(root, target, specs);
|
|
551
578
|
const force = flagStr(args, "force") !== undefined;
|
|
552
579
|
if (skill.hasSpec && !force) {
|
|
553
580
|
throw new Error(`${skill.specPath} exists — edit it, or pass --force to overwrite`);
|
|
@@ -558,14 +585,14 @@ export async function cmdInit(args) {
|
|
|
558
585
|
console.log(`wrote template ${skill.specPath} — fill it in, or run \`skill-harness suggest ${skill.name}\` to LLM-draft it.`);
|
|
559
586
|
}
|
|
560
587
|
export async function cmdSuggest(args, adapterOverride) {
|
|
561
|
-
const root =
|
|
588
|
+
const { skills: root, specs } = roots(args);
|
|
562
589
|
const target = args._[0];
|
|
563
590
|
if (!target)
|
|
564
591
|
throw new Error("usage: skill-harness suggest <skill> --skills <root> [--model prov:model] [--force]");
|
|
565
592
|
// resolveSkill throws a SKILL.md-specific error when the directory exists but
|
|
566
593
|
// lacks one, so we don't reimplement that check here; a resolved skill always
|
|
567
594
|
// has a SKILL.md at skill.dir.
|
|
568
|
-
const skill = resolveSkill(root, target);
|
|
595
|
+
const skill = resolveSkill(root, target, specs);
|
|
569
596
|
const skillMd = readFileSync(join(skill.dir, "SKILL.md"), "utf8");
|
|
570
597
|
// Overwrite without --force only when the target is absent or an *unedited*
|
|
571
598
|
// template. A file that still carries the sentinel but no longer matches the
|
|
@@ -619,7 +646,7 @@ export async function cmdSuggest(args, adapterOverride) {
|
|
|
619
646
|
}
|
|
620
647
|
writeSpecFile(skill.specPath, text);
|
|
621
648
|
console.log(`drafted ${count} scenario(s) → ${skill.specPath}`);
|
|
622
|
-
console.log(`review it (especially the proposed critical set), then \`skill-harness run ${skill.name} --skills ${root}\``);
|
|
649
|
+
console.log(`review it (especially the proposed critical set), then \`skill-harness run ${skill.name} --skills ${root}${specs ? ` --specs ${specs}` : ""}\``);
|
|
623
650
|
}
|
|
624
651
|
finally {
|
|
625
652
|
rmSync(cwd, { recursive: true, force: true });
|
|
@@ -635,30 +662,31 @@ export async function cmdSuggest(args, adapterOverride) {
|
|
|
635
662
|
* teach everyone to stop reading it.
|
|
636
663
|
*/
|
|
637
664
|
export async function cmdLint(args) {
|
|
638
|
-
const root =
|
|
665
|
+
const { skills: root, specs } = roots(args);
|
|
639
666
|
const target = args._[0] ?? "all";
|
|
640
|
-
let
|
|
667
|
+
let skills;
|
|
641
668
|
try {
|
|
642
|
-
|
|
643
|
-
?
|
|
644
|
-
: [resolveSkill(root, target)
|
|
669
|
+
skills = target === "all"
|
|
670
|
+
? discoverUsable(root, specs).filter((s) => s.hasSpec)
|
|
671
|
+
: [resolveSkill(root, target, specs)];
|
|
645
672
|
}
|
|
646
673
|
catch (e) {
|
|
647
674
|
console.error(`error: ${e instanceof Error ? e.message : e}`);
|
|
648
675
|
process.exitCode = 1;
|
|
649
676
|
return;
|
|
650
677
|
}
|
|
651
|
-
if (
|
|
678
|
+
if (skills.length === 0) {
|
|
652
679
|
console.error(`no skills with a spec under ${root}`);
|
|
653
680
|
process.exitCode = 1;
|
|
654
681
|
return;
|
|
655
682
|
}
|
|
656
683
|
const gha = process.env.GITHUB_ACTIONS === "true";
|
|
657
684
|
const findings = [];
|
|
658
|
-
for (const
|
|
685
|
+
for (const skill of skills) {
|
|
686
|
+
const dir = skill.dir;
|
|
659
687
|
let f;
|
|
660
688
|
try {
|
|
661
|
-
f = lintSkill(dir);
|
|
689
|
+
f = lintSkill(dir, skill.testsDir);
|
|
662
690
|
}
|
|
663
691
|
catch (e) {
|
|
664
692
|
f = [{ skill: dir, code: "lint-error", message: e instanceof Error ? e.message : String(e) }];
|
|
@@ -676,7 +704,7 @@ export async function cmdLint(args) {
|
|
|
676
704
|
}
|
|
677
705
|
const gating = findings.filter(failsGate).length;
|
|
678
706
|
const notes = findings.length - gating;
|
|
679
|
-
console.log(`\n${
|
|
707
|
+
console.log(`\n${skills.length} skill(s), ${gating} finding(s)${notes > 0 ? `, ${notes} note(s) (do not fail the gate)` : ""}`);
|
|
680
708
|
process.exitCode = gating > 0 ? 1 : 0;
|
|
681
709
|
}
|
|
682
710
|
// ---------------------------------------------------------------- dispatch
|
|
@@ -715,6 +743,9 @@ export function help() {
|
|
|
715
743
|
lint <skill|all> --skills <root> validate specs/fixtures + results-consistency (${free("lint")}; CI gate; exits non-zero on findings)
|
|
716
744
|
coverage <skill|all> --skills <root> [--strict] which instruction sections have a declared test (${free("coverage")})
|
|
717
745
|
|
|
746
|
+
Every command with --skills also accepts --specs <root> (default: SKILL_HARNESS_SPECS).
|
|
747
|
+
Skill text comes from --skills; test assets and results come from --specs.
|
|
748
|
+
|
|
718
749
|
version print ${HARNESS_VERSION} and exit (also --version / -v)
|
|
719
750
|
|
|
720
751
|
defaults: model=${DEFAULT_MODEL} judge=${defaultJudge()} mode=green harness=pi
|
package/dist/serve.js
CHANGED
|
@@ -175,6 +175,7 @@ export async function serveReview(opts) {
|
|
|
175
175
|
// with the one `rubric:` key this re-judge actually applied refreshed —
|
|
176
176
|
// the same doctrine `grade` follows (see refreshRubricHashes).
|
|
177
177
|
source_hashes: refreshRubricHashes(results.source_hashes, spec, [body.scenarioId]),
|
|
178
|
+
source_hash_roots: results.source_hash_roots,
|
|
178
179
|
}, scoreContextFor(results, spec));
|
|
179
180
|
ensureResultsGitignore(join(opts.skillDir, "tests", "results"));
|
|
180
181
|
const g = written.effective_grade;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skill-harness/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.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",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"prepack": "node ../../scripts/release-pack.mjs --guard"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@skill-harness/core": "0.
|
|
48
|
-
"@skill-harness/adapters": "0.
|
|
47
|
+
"@skill-harness/core": "0.21.0",
|
|
48
|
+
"@skill-harness/adapters": "0.21.0"
|
|
49
49
|
}
|
|
50
50
|
}
|