@skill-harness/cli 0.29.0 → 0.30.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 +19 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +55 -1
- package/dist/serve.js +9 -3
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ through Pi.
|
|
|
9
9
|
Most users should install the `skill-harness` meta-package (`npm i -g
|
|
10
10
|
skill-harness`) instead of this package directly.
|
|
11
11
|
|
|
12
|
-
Fresh subject execution
|
|
12
|
+
Fresh subject execution supports **Pi 1.0.4 and 1.1.0**. Other or unavailable
|
|
13
13
|
versions produce execution ERROR before a subject call. Retained results remain
|
|
14
14
|
readable; the historical Pi 0.83.0 raw parser is not current qualification.
|
|
15
15
|
|
|
@@ -19,12 +19,30 @@ readable; the historical Pi 0.83.0 raw parser is not current qualification.
|
|
|
19
19
|
npm i -g @skill-harness/cli
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
## Create a skill in Pi
|
|
23
|
+
|
|
24
|
+
Install the `skill-harness` Pi package, select a model, then use
|
|
25
|
+
`/skill-harness create <your idea>`. Authoring runs in Pi and asks one necessary
|
|
26
|
+
question at a time; the standalone CLI does not substitute a direct model API.
|
|
27
|
+
The workflow previews a skill, optional pi-daddy wrapper, and evals in the same
|
|
28
|
+
or a separate repository before applying files. Generated skills need only Pi;
|
|
29
|
+
the wrapper is optional and principal-pi-skills is not required.
|
|
30
|
+
|
|
31
|
+
The CLI supports the subsequent offline preflight, reviewed judge calibration,
|
|
32
|
+
scenario execution and saved-result diagnosis. See
|
|
33
|
+
[EVAL-AUTHORING.md](https://github.com/mojomanyana/skill-harness/blob/main/docs/EVAL-AUTHORING.md)
|
|
34
|
+
for the complete workflow and its evidence limits.
|
|
35
|
+
|
|
22
36
|
## Commands
|
|
23
37
|
|
|
24
38
|
| command | does |
|
|
25
39
|
|---|---|
|
|
26
40
|
| `skill-harness list --skills <root>` | discovered skills + spec status |
|
|
27
41
|
| `skill-harness lint <skill\|all> --skills <root>` | validate specs/fixtures; CI gate |
|
|
42
|
+
| `skill-harness preflight <skill\|all> --skills <root> [--specs <root>]` | inspect eval configuration and discoverability offline |
|
|
43
|
+
| `skill-harness calibrate <manifest.json>` | validate selected calibration evidence offline |
|
|
44
|
+
| `skill-harness calibrate <manifest.json> --run --judge p:m --out NEW_DIR` | judge reviewed controls through Pi; preserve a new result directory |
|
|
45
|
+
| `skill-harness diagnose <run-dir> --skills <root> [--specs <root>]` | inspect saved evidence without changing historical scores |
|
|
28
46
|
| `skill-harness run <skill\|all> --skills <root> [--model p:m ...] [--judge p:m]` | run scenarios, grade, score |
|
|
29
47
|
| `skill-harness grade <run-dir> [--judge p:m]` | re-grade saved transcripts |
|
|
30
48
|
| `skill-harness rescore <run-dir>...` | re-apply current score thresholds offline |
|
package/dist/cli.d.ts
CHANGED
|
@@ -51,4 +51,7 @@ export declare function cmdLint(args: Args): Promise<void>;
|
|
|
51
51
|
* prints none.
|
|
52
52
|
*/
|
|
53
53
|
export declare function help(): string;
|
|
54
|
+
export declare function cmdPreflight(args: Args): Promise<void>;
|
|
55
|
+
export declare function cmdDiagnose(args: Args): Promise<void>;
|
|
56
|
+
export declare function cmdCalibrate(args: Args, adapterOverride?: HarnessAdapter): Promise<void>;
|
|
54
57
|
export declare function main(argv: string[]): Promise<void>;
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { readFileSync, existsSync, mkdirSync, writeFileSync, mkdtempSync, rmSync } from "node:fs";
|
|
3
3
|
import { basename, dirname, join, resolve } from "node:path";
|
|
4
4
|
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, } from "@skill-harness/core";
|
|
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, } from "@skill-harness/core";
|
|
6
6
|
import { getAdapter } from "@skill-harness/adapters";
|
|
7
7
|
import { serveReview } from "./serve.js";
|
|
8
8
|
import { cmdDecision } from "./decision.js";
|
|
@@ -747,6 +747,10 @@ export function help() {
|
|
|
747
747
|
review <skill> --skills <root> [--port N] serve the interactive review UI
|
|
748
748
|
add-test <skill> --skills <root> --id ID --title T --turn ... --check ... [--critical] [--mode seeded --fixture path]
|
|
749
749
|
init <skill> --skills <root> [--force] scaffold a commented template spec (${free("init")})
|
|
750
|
+
preflight <skill|all> --skills <root> [--specs <root>] eval checks (${free("preflight")})
|
|
751
|
+
calibrate <manifest.json> [--run --judge p:m --out NEW_DIR] validate or judge reviewed controls
|
|
752
|
+
diagnose <run-dir> --skills <root> [--specs <root>] inspect saved evidence (${free("diagnose")})
|
|
753
|
+
In Pi: /skill-harness create <idea> guided skill + optional agent + eval creation
|
|
750
754
|
suggest <skill> --skills <root> [--model prov:model] [--force] LLM-draft a spec from SKILL.md (spends tokens)
|
|
751
755
|
list --skills <root> discovered skills + spec status (${free("list")})
|
|
752
756
|
lint <skill|all> --skills <root> validate specs/fixtures + results-consistency (${free("lint")}; CI gate; exits non-zero on findings)
|
|
@@ -766,6 +770,51 @@ defaults: model=${DEFAULT_MODEL} judge=${defaultJudge()} mode=green harness=p
|
|
|
766
770
|
the judge default runs through Pi on your ChatGPT subscription (\`openai-codex\`), not a metered
|
|
767
771
|
API key. Set SKILL_HARNESS_JUDGE to change it for a repo or a shell; --judge wins over both.`;
|
|
768
772
|
}
|
|
773
|
+
export async function cmdPreflight(args) {
|
|
774
|
+
const { skills, specs } = roots(args);
|
|
775
|
+
const target = args._[0];
|
|
776
|
+
if (!target)
|
|
777
|
+
throw new Error("usage: skill-harness preflight <skill|all> --skills <root> [--specs <root>]");
|
|
778
|
+
const selected = target === "all" ? discoverUsable(skills, specs).filter(s => s.hasSpec) : [resolveSkill(skills, target, specs)];
|
|
779
|
+
if (!selected.length)
|
|
780
|
+
throw new Error("No specifications found; in Pi use /skill-harness create to author a skill and its evals.");
|
|
781
|
+
const findings = selected.flatMap(s => lintSkill(s.dir, s.testsDir));
|
|
782
|
+
const errors = findings.filter(failsGate);
|
|
783
|
+
console.log(JSON.stringify({ status: errors.length ? "blocked" : "checked", providerCalls: 0, semanticValidity: "requires-reviewed-controls", findings }, null, 2));
|
|
784
|
+
if (errors.length)
|
|
785
|
+
process.exitCode = 1;
|
|
786
|
+
}
|
|
787
|
+
export async function cmdDiagnose(args) {
|
|
788
|
+
if (!args._[0])
|
|
789
|
+
throw new Error("usage: skill-harness diagnose <run-dir> --skills <root> [--specs <root>]");
|
|
790
|
+
const runDir = resolve(args._[0]);
|
|
791
|
+
const recorded = readResults(runDir);
|
|
792
|
+
const { skills, specs } = roots(args);
|
|
793
|
+
const selected = resolveSkill(skills, recorded.skill, specs);
|
|
794
|
+
console.log(JSON.stringify(diagnoseRun(runDir, selected.dir, selected.specPath), null, 2));
|
|
795
|
+
}
|
|
796
|
+
export async function cmdCalibrate(args, adapterOverride) {
|
|
797
|
+
if (!args._[0])
|
|
798
|
+
throw new Error("usage: skill-harness calibrate <manifest.json> [--run --judge provider:model --out NEW_DIR]");
|
|
799
|
+
const calibration = loadCalibration(resolve(args._[0]));
|
|
800
|
+
if (!flagBool(args, "run")) {
|
|
801
|
+
console.log(JSON.stringify({ status: calibration.ready ? "validated" : "needs-review", purpose: "judge-calibration-only", providerCalls: 0, cases: calibration.controls.length, issues: calibration.issues }));
|
|
802
|
+
if (!calibration.ready)
|
|
803
|
+
process.exitCode = 1;
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
const judgeToken = flagStr(args, "judge");
|
|
807
|
+
const output = flagStr(args, "out");
|
|
808
|
+
if (!judgeToken || !output)
|
|
809
|
+
throw new Error("Calibration --run needs an explicitly selected --judge and a new --out directory.");
|
|
810
|
+
const adapter = adapterOverride ?? getAdapter("pi");
|
|
811
|
+
if (!await adapter.available())
|
|
812
|
+
throw new Error("Pi is required for judge calibration. Configure the selected model in Pi first.");
|
|
813
|
+
const result = await runCalibration(calibration, { adapter, judge: parseModelRef(judgeToken), output: resolve(output), allowMeteredJudge: flagBool(args, "allow-metered-judge"), onProgress: p => console.log(`Judging control ${p.index + 1}/${p.total}: ${p.id}`) });
|
|
814
|
+
console.log(JSON.stringify(result, null, 2));
|
|
815
|
+
if (result.status !== "matched")
|
|
816
|
+
process.exitCode = 1;
|
|
817
|
+
}
|
|
769
818
|
export async function main(argv) {
|
|
770
819
|
const cmd = argv[0];
|
|
771
820
|
if (cmd === "decision")
|
|
@@ -773,6 +822,11 @@ export async function main(argv) {
|
|
|
773
822
|
const args = parseArgs(argv.slice(1));
|
|
774
823
|
assertNoRetiredFlags(cmd, args);
|
|
775
824
|
switch (cmd) {
|
|
825
|
+
case "create":
|
|
826
|
+
case "author": throw new Error("Skill creation uses your current Pi model. Start Pi with skill-harness installed, then use /skill-harness create followed by your idea; it asks one necessary question at a time.");
|
|
827
|
+
case "preflight": return cmdPreflight(args);
|
|
828
|
+
case "diagnose": return cmdDiagnose(args);
|
|
829
|
+
case "calibrate": return cmdCalibrate(args);
|
|
776
830
|
case "run": return cmdRun(args);
|
|
777
831
|
case "grade":
|
|
778
832
|
case "regrade": return cmdGrade(args);
|
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, refreshRubricHashes, findJudgeRawFiles, effectiveThreshold, scoreContextFor, isScoredMode, rebuildScenarioResult, mergeScenarioMetrics, carryRepObjectives, envFlag, recordedJudgeOrDefault, assertJudgeAllowed, } from "@skill-harness/core";
|
|
6
|
+
import { collectReport, renderReport, collectTrends, readResults, writeResults, applyOverride, preserveTranscript, findTranscriptFiles, ensureResultsGitignore, appendJournal, loadSpec, regradeScenario, refreshRubricHashes, findJudgeRawFiles, effectiveThreshold, scoreContextFor, isScoredMode, rebuildScenarioResult, mergeScenarioMetrics, carryRepObjectives, envFlag, recordedJudgeOrDefault, assertJudgeAllowed, parseModelRef, resolveJudgeReferences, } 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/. */
|
|
@@ -145,10 +145,16 @@ export async function serveReview(opts) {
|
|
|
145
145
|
}
|
|
146
146
|
const threshold = effectiveThreshold(prev, scenario);
|
|
147
147
|
try {
|
|
148
|
+
// The prompt and refreshed rubric must name the same selected bytes,
|
|
149
|
+
// even when a reference is edited while a multi-repetition judge runs.
|
|
150
|
+
const specDir = dirname(specPath);
|
|
151
|
+
const references = resolveJudgeReferences(scenario, specDir);
|
|
148
152
|
const rr = await regradeScenario({
|
|
149
153
|
runDir: column.runDir, spec, scenario, adapter, judge: resolvedRecordedJudge.judge,
|
|
150
|
-
specDir
|
|
154
|
+
specDir, threshold, mode: results.mode, references,
|
|
151
155
|
expectedReps: prev.reps ?? 1,
|
|
156
|
+
prior: prev, piVersion: results.harness_cli_version,
|
|
157
|
+
...(results.model.includes(":") ? { subject: parseModelRef(results.model) } : {}),
|
|
152
158
|
});
|
|
153
159
|
const merged = results.scenarios.map((s) => {
|
|
154
160
|
if (s.id !== body.scenarioId)
|
|
@@ -174,7 +180,7 @@ export async function serveReview(opts) {
|
|
|
174
180
|
// retired the staleness gate for any run re-judged from the UI. Carried,
|
|
175
181
|
// with the one `rubric:` key this re-judge actually applied refreshed —
|
|
176
182
|
// the same doctrine `grade` follows (see refreshRubricHashes).
|
|
177
|
-
source_hashes: refreshRubricHashes(results.source_hashes, spec, [body.scenarioId]),
|
|
183
|
+
source_hashes: refreshRubricHashes(results.source_hashes, spec, [body.scenarioId], specDir, { [body.scenarioId]: references }),
|
|
178
184
|
source_hash_roots: results.source_hash_roots,
|
|
179
185
|
}, scoreContextFor(results, spec));
|
|
180
186
|
ensureResultsGitignore(join(opts.skillDir, "tests", "results"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skill-harness/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.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.30.0",
|
|
48
|
+
"@skill-harness/adapters": "0.30.0"
|
|
49
49
|
}
|
|
50
50
|
}
|