@dzhechkov/harness-cli 0.3.223 → 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 +56 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +173 -1
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
- package/src/cli.ts +157 -0
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
|
@@ -144,7 +144,22 @@ import {
|
|
|
144
144
|
buildChallengeBrief,
|
|
145
145
|
pickAdversaryModel,
|
|
146
146
|
CHALLENGE_QUESTIONS,
|
|
147
|
+
loadOutcomes,
|
|
148
|
+
renderOutcomes,
|
|
149
|
+
statsForKey,
|
|
150
|
+
selectAutoCost,
|
|
151
|
+
recordProvisional,
|
|
152
|
+
finalizeOutcome,
|
|
153
|
+
COST_LADDER,
|
|
154
|
+
splitScenarios,
|
|
155
|
+
budgetPlan,
|
|
156
|
+
selectWinner,
|
|
157
|
+
proseScopeOk,
|
|
158
|
+
renderProseDiff,
|
|
159
|
+
readScenarioIds,
|
|
160
|
+
DEFAULT_MAX_JUDGE_RUNS,
|
|
147
161
|
} from '@dzhechkov/harness-core';
|
|
162
|
+
import type { Family, ModelRung, Candidate as BtoCandidate, DimScores } from '@dzhechkov/harness-core';
|
|
148
163
|
import type { SetupSpec } from '@dzhechkov/harness-core';
|
|
149
164
|
import type { ProvenanceMode, PackVerdict, ClaudeUsageModel, PatternRecord, TargetName, BookKU, HarmonizeReport, UsageCalibrationPlan, ClaimFinding, RecallUsagePatternRow } from '@dzhechkov/harness-core';
|
|
150
165
|
import { getPreset, PRESET_NAMES } from '@dzhechkov/harness-presets';
|
|
@@ -4558,6 +4573,144 @@ function cmdChallenge(options: Map<string, string>, flags: Set<string>, cwd: str
|
|
|
4558
4573
|
return 0;
|
|
4559
4574
|
}
|
|
4560
4575
|
|
|
4576
|
+
/**
|
|
4577
|
+
* `dz routing` — inspect the learned cost-optimal routing outcome store (feature learned-cost-routing). Shows
|
|
4578
|
+
* what `args.models[stage]='auto-cost'` currently believes per (stage, complexity-tier, model): gated
|
|
4579
|
+
* attempts / successes / rate. Read-only.
|
|
4580
|
+
* --stage <s> filter to one pipeline stage (code, qe, plan, …)
|
|
4581
|
+
* --json raw store JSON
|
|
4582
|
+
*/
|
|
4583
|
+
function cmdRouting(options: Map<string, string>, flags: Set<string>, cwd: string, write: Write): number {
|
|
4584
|
+
let repoRoot = cwd;
|
|
4585
|
+
try { repoRoot = execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' }).trim() || cwd; } catch { /* not git */ }
|
|
4586
|
+
|
|
4587
|
+
const stage = options.get('stage');
|
|
4588
|
+
const tier = options.get('tier');
|
|
4589
|
+
const model = options.get('model');
|
|
4590
|
+
|
|
4591
|
+
// --select: resolve an `auto-cost` stage → the concrete model + escalate chain (the workflow shells out here
|
|
4592
|
+
// because it is sandboxed with no fs). --family restricts to the coder's cross-family (qe guard); --ladder is
|
|
4593
|
+
// the probe-filtered id set (account-specific Codex ids that answered).
|
|
4594
|
+
if (flags.has('select')) {
|
|
4595
|
+
if (!stage || !tier) { write('dz routing --select needs --stage and --tier'); return 1; }
|
|
4596
|
+
const fam = options.get('family');
|
|
4597
|
+
const family: Family | undefined = fam === 'claude' || fam === 'openai' ? fam : undefined;
|
|
4598
|
+
const ladderCsv = options.get('ladder');
|
|
4599
|
+
let ladder: readonly ModelRung[] | undefined;
|
|
4600
|
+
if (ladderCsv !== undefined) {
|
|
4601
|
+
const ids = new Set(ladderCsv.split(',').map((s) => s.trim()).filter(Boolean));
|
|
4602
|
+
ladder = COST_LADDER.filter((r) => ids.has(r.id));
|
|
4603
|
+
}
|
|
4604
|
+
const statsFor = statsForKey(loadOutcomes(repoRoot), stage, tier);
|
|
4605
|
+
const pick = selectAutoCost(stage, tier, statsFor, { ...(family ? { family } : {}), ...(ladder ? { ladder } : {}) });
|
|
4606
|
+
write(JSON.stringify(pick));
|
|
4607
|
+
return 0;
|
|
4608
|
+
}
|
|
4609
|
+
|
|
4610
|
+
// --record-provisional / --finalize: the two-phase outcome label. The workflow calls these at stage end
|
|
4611
|
+
// (provisional) and at the downstream gate (finalize, authoritative).
|
|
4612
|
+
if (flags.has('record-provisional')) {
|
|
4613
|
+
if (!stage || !tier || !model) { write('dz routing --record-provisional needs --stage --tier --model'); return 1; }
|
|
4614
|
+
recordProvisional(repoRoot, stage, tier, model, flags.has('weak'));
|
|
4615
|
+
write(`recorded provisional: ${stage}/${tier}/${model}${flags.has('weak') ? ' (weak-credit)' : ''}`);
|
|
4616
|
+
return 0;
|
|
4617
|
+
}
|
|
4618
|
+
if (flags.has('finalize')) {
|
|
4619
|
+
if (!stage || !tier || !model) { write('dz routing --finalize needs --stage --tier --model --success <true|false>'); return 1; }
|
|
4620
|
+
const success = options.get('success') === 'true';
|
|
4621
|
+
finalizeOutcome(repoRoot, stage, tier, model, success);
|
|
4622
|
+
write(`finalized: ${stage}/${tier}/${model} → ${success ? 'success' : 'FAILURE'}`);
|
|
4623
|
+
return 0;
|
|
4624
|
+
}
|
|
4625
|
+
|
|
4626
|
+
// default / --json: inspect the learned table.
|
|
4627
|
+
const store = loadOutcomes(repoRoot);
|
|
4628
|
+
if (flags.has('json')) { write(JSON.stringify(store, null, 2)); return 0; }
|
|
4629
|
+
write(renderOutcomes(store, stage));
|
|
4630
|
+
return 0;
|
|
4631
|
+
}
|
|
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
|
+
|
|
4561
4714
|
function cmdStats(cwd: string, write: Write): number {
|
|
4562
4715
|
const baseDir = join(cwd, 'packages', '@dzhechkov');
|
|
4563
4716
|
if (!existsSync(baseDir)) {
|
|
@@ -4834,6 +4987,10 @@ export async function runCli(argv: string[], io: CliIo = {}): Promise<number> {
|
|
|
4834
4987
|
return cmdFeatureAdrSetup(options, flags, cwd, write);
|
|
4835
4988
|
case 'challenge':
|
|
4836
4989
|
return cmdChallenge(options, flags, cwd, write);
|
|
4990
|
+
case 'routing':
|
|
4991
|
+
return cmdRouting(options, flags, cwd, write);
|
|
4992
|
+
case 'bto-optimize':
|
|
4993
|
+
return cmdBtoOptimize(options, flags, cwd, write);
|
|
4837
4994
|
case 'dashboard':
|
|
4838
4995
|
return cmdDashboard(cwd, write);
|
|
4839
4996
|
case 'roam':
|