@dzhechkov/harness-cli 0.3.224 → 0.3.225
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 +26 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +104 -1
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
- package/src/cli.ts +91 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dzhechkov/harness-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.225",
|
|
4
4
|
"description": "The dz CLI — install AI skills for Claude Code, Codex, OpenCode, Hermes, OpenClaude, GitHub Copilot. 35 commands, 13 presets, 6 platform targets.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@dzhechkov/skills-reverse-engineering": "^0.1.0",
|
|
56
56
|
"@dzhechkov/skills-presentation-storyteller": "^0.1.0",
|
|
57
57
|
"@dzhechkov/skills-website-cloner": "^0.1.0",
|
|
58
|
-
"@dzhechkov/harness-core": "0.3.
|
|
58
|
+
"@dzhechkov/harness-core": "0.3.119"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@types/node": "^25.6.0",
|
package/src/cli.ts
CHANGED
|
@@ -151,8 +151,15 @@ import {
|
|
|
151
151
|
recordProvisional,
|
|
152
152
|
finalizeOutcome,
|
|
153
153
|
COST_LADDER,
|
|
154
|
+
splitScenarios,
|
|
155
|
+
budgetPlan,
|
|
156
|
+
selectWinner,
|
|
157
|
+
proseScopeOk,
|
|
158
|
+
renderProseDiff,
|
|
159
|
+
readScenarioIds,
|
|
160
|
+
DEFAULT_MAX_JUDGE_RUNS,
|
|
154
161
|
} from '@dzhechkov/harness-core';
|
|
155
|
-
import type { Family, ModelRung } from '@dzhechkov/harness-core';
|
|
162
|
+
import type { Family, ModelRung, Candidate as BtoCandidate, DimScores } from '@dzhechkov/harness-core';
|
|
156
163
|
import type { SetupSpec } from '@dzhechkov/harness-core';
|
|
157
164
|
import type { ProvenanceMode, PackVerdict, ClaudeUsageModel, PatternRecord, TargetName, BookKU, HarmonizeReport, UsageCalibrationPlan, ClaimFinding, RecallUsagePatternRow } from '@dzhechkov/harness-core';
|
|
158
165
|
import { getPreset, PRESET_NAMES } from '@dzhechkov/harness-presets';
|
|
@@ -4623,6 +4630,87 @@ function cmdRouting(options: Map<string, string>, flags: Set<string>, cwd: strin
|
|
|
4623
4630
|
return 0;
|
|
4624
4631
|
}
|
|
4625
4632
|
|
|
4633
|
+
/**
|
|
4634
|
+
* `dz bto-optimize` — the deterministic engine BEHIND the `/bto-optimize` skill (feature bto-optimize-holdout).
|
|
4635
|
+
* Adds dspy-MIPROv2 rigor the current evolutionary loop lacks: a hold-out split, a hard budget cap, and a
|
|
4636
|
+
* no-regress-on-holdout winner selector. NOT a rival command — the skill delegates these steps; candidate-prose
|
|
4637
|
+
* generation + judge scoring stay skill-side. `--json` on every subcommand.
|
|
4638
|
+
* --split --scenarios <csv|@file> [--holdout <r>] deterministic tune/holdout split
|
|
4639
|
+
* --plan --candidates K --rounds R --tune N --holdout M [--max C] budget plan (trims to the cap)
|
|
4640
|
+
* --select --baseline <@json> --candidates <@json> [--tolerance t] accept only on holdout no-regress
|
|
4641
|
+
* --scope-check --original <f> --candidate <f> prose-only guard
|
|
4642
|
+
* --diff --original <f> --candidate <f> the prose diff to confirm
|
|
4643
|
+
*/
|
|
4644
|
+
function cmdBtoOptimize(options: Map<string, string>, flags: Set<string>, cwd: string, write: Write): number {
|
|
4645
|
+
const json = flags.has('json');
|
|
4646
|
+
const emit = (o: unknown): number => { write(JSON.stringify(o, null, json ? 2 : 0)); return 0; };
|
|
4647
|
+
const readContained = (rel: string): string => {
|
|
4648
|
+
// Containment (QE #traversal): an @file must stay under cwd — `@../../etc/passwd` must not read outside.
|
|
4649
|
+
const rootAbs = resolve(cwd);
|
|
4650
|
+
const abs = resolve(rootAbs, rel);
|
|
4651
|
+
if (abs !== rootAbs && !abs.startsWith(rootAbs + sep)) throw new Error(`path escapes the working directory: ${rel}`);
|
|
4652
|
+
return readFileSync(abs, 'utf-8');
|
|
4653
|
+
};
|
|
4654
|
+
const readJson = (spec: string | undefined): unknown => {
|
|
4655
|
+
if (spec === undefined) return undefined;
|
|
4656
|
+
const raw = spec.startsWith('@') ? readContained(spec.slice(1)) : spec;
|
|
4657
|
+
return JSON.parse(raw);
|
|
4658
|
+
};
|
|
4659
|
+
try {
|
|
4660
|
+
if (flags.has('split')) {
|
|
4661
|
+
const sc = options.get('scenarios');
|
|
4662
|
+
if (sc === undefined) { write('dz bto-optimize --split needs --scenarios <csv|@file>'); return 1; }
|
|
4663
|
+
const ids = sc.startsWith('@') ? readScenarioIds(resolve(cwd, sc.slice(1))) : sc.split(',').map((s) => s.trim()).filter(Boolean);
|
|
4664
|
+
const ratio = Number.parseFloat(options.get('holdout') ?? '');
|
|
4665
|
+
const split = splitScenarios(ids, Number.isFinite(ratio) ? ratio : undefined);
|
|
4666
|
+
if (json) return emit(split);
|
|
4667
|
+
write(`tune (${split.tune.length}): ${split.tune.join(', ')}`);
|
|
4668
|
+
write(`holdout (${split.holdout.length}): ${split.holdout.join(', ')}`);
|
|
4669
|
+
return 0;
|
|
4670
|
+
}
|
|
4671
|
+
if (flags.has('plan')) {
|
|
4672
|
+
const n = (k: string, d: number): number => { const v = Number.parseInt(options.get(k) ?? '', 10); return Number.isFinite(v) ? v : d; };
|
|
4673
|
+
const max = Number.parseInt(options.get('max') ?? '', 10);
|
|
4674
|
+
const plan = budgetPlan({ candidates: n('candidates', 5), rounds: n('rounds', 1), tuneCount: n('tune', 3), holdoutCount: n('holdout', 2) }, Number.isFinite(max) ? max : DEFAULT_MAX_JUDGE_RUNS);
|
|
4675
|
+
if (json) return emit(plan);
|
|
4676
|
+
write(`budget plan: ${plan.candidates} candidates × ${plan.rounds} round(s) → ${plan.tuneRuns} tune + ${plan.holdoutRuns} holdout = ${plan.totalRuns} judge run(s) (cap ${plan.cap}, ${plan.withinCap ? 'within cap' : 'OVER CAP'})`);
|
|
4677
|
+
if (plan.trimmed) write(` trimmed to fit: ${plan.trimmed}`);
|
|
4678
|
+
return 0;
|
|
4679
|
+
}
|
|
4680
|
+
if (flags.has('select')) {
|
|
4681
|
+
const baseline = readJson(options.get('baseline')) as { holdout: DimScores } | undefined;
|
|
4682
|
+
const candidates = readJson(options.get('candidates')) as BtoCandidate[] | undefined;
|
|
4683
|
+
if (!baseline || !Array.isArray(candidates)) { write('dz bto-optimize --select needs --baseline <@json {holdout}> and --candidates <@json [..]>'); return 1; }
|
|
4684
|
+
const tol = Number.parseFloat(options.get('tolerance') ?? '');
|
|
4685
|
+
const result = selectWinner(baseline, candidates, Number.isFinite(tol) ? { tolerance: tol } : {});
|
|
4686
|
+
if (json) return emit(result);
|
|
4687
|
+
write(result.winner ? `✓ winner: ${result.winner} — ${result.reason}` : `✗ no winner — ${result.reason}`);
|
|
4688
|
+
return 0;
|
|
4689
|
+
}
|
|
4690
|
+
if (flags.has('scope-check') || flags.has('diff')) {
|
|
4691
|
+
const o = options.get('original'); const c = options.get('candidate');
|
|
4692
|
+
if (o === undefined || c === undefined) { write('needs --original <file> and --candidate <file>'); return 1; }
|
|
4693
|
+
const origText = readContained(o);
|
|
4694
|
+
const candText = readContained(c);
|
|
4695
|
+
if (flags.has('scope-check')) {
|
|
4696
|
+
const r = proseScopeOk(origText, candText);
|
|
4697
|
+
if (json) return emit(r);
|
|
4698
|
+
write(r.ok ? `✓ prose-only: ${r.reason}` : `✗ out of scope: ${r.reason}`);
|
|
4699
|
+
return r.ok ? 0 : 1;
|
|
4700
|
+
}
|
|
4701
|
+
write(renderProseDiff(origText, candText));
|
|
4702
|
+
return 0;
|
|
4703
|
+
}
|
|
4704
|
+
write('dz bto-optimize: pass --split | --plan | --select | --scope-check | --diff (see docs)');
|
|
4705
|
+
return 1;
|
|
4706
|
+
} catch (e) {
|
|
4707
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
4708
|
+
if (json) { write(JSON.stringify({ error: msg })); return 1; }
|
|
4709
|
+
write(`dz bto-optimize: ${msg}`);
|
|
4710
|
+
return 1;
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
|
|
4626
4714
|
function cmdStats(cwd: string, write: Write): number {
|
|
4627
4715
|
const baseDir = join(cwd, 'packages', '@dzhechkov');
|
|
4628
4716
|
if (!existsSync(baseDir)) {
|
|
@@ -4901,6 +4989,8 @@ export async function runCli(argv: string[], io: CliIo = {}): Promise<number> {
|
|
|
4901
4989
|
return cmdChallenge(options, flags, cwd, write);
|
|
4902
4990
|
case 'routing':
|
|
4903
4991
|
return cmdRouting(options, flags, cwd, write);
|
|
4992
|
+
case 'bto-optimize':
|
|
4993
|
+
return cmdBtoOptimize(options, flags, cwd, write);
|
|
4904
4994
|
case 'dashboard':
|
|
4905
4995
|
return cmdDashboard(cwd, write);
|
|
4906
4996
|
case 'roam':
|