@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 CHANGED
@@ -431,7 +431,7 @@ Get the whole set with `dz init --target claude-code --preset meta`, or pick one
431
431
 
432
432
  > **A skill and its npx toolkit are not duplicates — they're a graduation.** Several skills (e.g. `feature-adr`, `design-thinking`) exist BOTH as a skill inside a `dz` preset AND as a standalone `npx` package. The preset's SKILL.md is **fully functional on its own** (the whole methodology — modules + references — travels with it, and it auto-activates by description), and it's the only way to compile that capability to the **non-Claude platforms** (Codex/OpenCode/Hermes/OpenClaude) via `dz`. The npx package adds **project-level runtime governance** around the same skill: a slash command, governance rules, a context shard, and (for feature-adr) reward-learning + `/harvest`. So: pick the **skill/preset** for a working capability across platforms; pick the **npx toolkit** when you want it as a governed, command-driven fixture of one project.
433
433
 
434
- ## All Commands (50)
434
+ ## All Commands (51)
435
435
 
436
436
  ```
437
437
  dz setup --target <name> [--preset <name>] [--select id,id,...] [--skills-dir <dir>] [--memory agentdb] [--no-memory] [--no-hooks] [--install-driver] [--force]
@@ -484,6 +484,7 @@ dz retro [transcript] [--json] [--threshold N] [--no-teach] [--insta
484
484
  dz feature-adr-setup [--plan] [--from-spec <f>] [--apply] # guided project onboarding engine (behind the `configure-feature-adr` skill): --plan shows which docs exist/missing; --from-spec scaffolds vision/map/testing/project-skills (propose; --apply writes; augment-never-clobber)
485
485
  dz challenge --plan <plan.md> [--json] [--context-only] [--author <model>] # adversarial plan-gate (behind the `challenge-panel` skill): assemble a WIDE context pack (plan + vision + testing + map + degradations) + the fixed C1-C8 "break it" brief for a FRESH adversary (≠ plan author); advisory, never blocks
486
486
  dz routing [--stage <s>] [--json] # inspect the learned cost-optimal routing store: what `args.models.<stage>='auto-cost'` believes per (stage, complexity-tier, model) — gated attempts/successes/rate (feeds feature-adr model selection)
487
+ dz bto-optimize --split|--plan|--select|--scope-check|--diff [--json] # deterministic engine behind /bto-optimize: hold-out split + hard-capped budget + no-regress-on-holdout winner selection (defeats judge-gaming); prose-only, diff-confirmed, never auto-writes
487
488
  dz publish [--filter <name>] [--bump-only] [--claim-check <off|warn|error>] (dry-run by default; pass --yes/--confirm to go live; claim-check gate defaults to warn — surfaces README claim findings, never blocks)
488
489
  dz auto-canonicalize --source <github-url> --pack <skills-pack>
489
490
  dz sync-upstream [--package <dir>] [--list] [--all]
@@ -1490,6 +1491,30 @@ the coder** (a model never self-QEs), and the whole thing is **opt-in** — with
1490
1491
  pipeline is byte-identical and the store is never touched. Layered under the usage-adaptive override (a session
1491
1492
  near its limit still switches to Codex first). Storage is a plain JSON file — zero native deps.
1492
1493
 
1494
+ ### `dz bto-optimize` — when you want to improve a skill WITHOUT gaming its own benchmark
1495
+
1496
+ The BTO pack already grades a skill (`/bto`) and can evolve it (`/bto-optimize`) — but the evolutionary loop
1497
+ picks the highest score on the **same** eval it tuned on, so a variant that flatters the LLM judge panel
1498
+ (verbosity, buzzwords) can win even if it doesn't help real users (**Goodhart's law**). `dz bto-optimize` is the
1499
+ deterministic engine the `/bto-optimize` skill delegates to so acceptance is gated on **unseen** scenarios:
1500
+ ```bash
1501
+ dz bto-optimize --split --scenarios @scenarios.json --holdout 0.34 # deterministic tune/holdout split
1502
+ dz bto-optimize --plan --candidates 5 --rounds 1 --tune 4 --holdout 2 --max 24 # budget plan (trims to the cap)
1503
+ dz bto-optimize --select --baseline @baseline.json --candidates @cands.json # accept only on holdout no-regress
1504
+ ```
1505
+ ```
1506
+ budget plan: 5 candidates × 1 round(s) → 20 tune + 4 holdout = 24 judge run(s) (cap 24, within cap)
1507
+ ✓ winner: real — accepted on holdout: CORRECTNESS +2.00, aggregate +0.40, no regression
1508
+ ✗ no winner — gamer: lifted CORRECTNESS but regressed DEPTH on holdout
1509
+ ```
1510
+ A candidate is accepted **only** if it lifts the weakest dimension (METHODOLOGY/DEPTH/CORRECTNESS/USABILITY/
1511
+ ROBUSTNESS) on the **held-out** scenarios **without regressing** the others — a variant that wins on the tuning
1512
+ set but regresses on the holdout is rejected. Three guarantees: only the **directive prose** is mutated
1513
+ (frontmatter + headings are off-limits, `--scope-check`), the budget has a **hard cap** (the plan trims to fit
1514
+ and never lies about the run count), and the engine **never auto-writes** — it renders a diff you confirm.
1515
+ Grounded in dspy.ts's MIPROv2 (propose → tune → validate-on-held-out). In the skill: **"прогони bto-optimize с
1516
+ hold-out"** / **"optimize this skill with hold-out validation"**.
1517
+
1493
1518
  ## Recipes & FAQ
1494
1519
 
1495
1520
  Scenario → exact command → how to verify → gotcha. **Three gotchas worth calling out up front:**
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkOH,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAk8ID,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAoI5E"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAyOH,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAmhJD,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAsI5E"}
package/dist/cli.js CHANGED
@@ -9,7 +9,7 @@ import { fileURLToPath } from 'node:url';
9
9
  import { execSync } from 'node:child_process';
10
10
  import { homedir } from 'node:os';
11
11
  import { createRequire } from 'node:module';
12
- import { createSkill, getSkillInfo, getWorkflow, isTargetName, listSkills, runDoctor, runInit, benchmarkSkill, benchmarkSkills, scanMcp, reconcileCapabilities, RECONCILE_BANNER, buildRegistry, discoverSkillPackDirs, checkUpstream, compareSkills, checkAllUpstream, sweepSkillDrift, syncCanonicalSkill, checkUpgrades, discoverPackages, discoverSourcePackages, fetchAllDownloads, filterByCategory, pretrain, recommend, generatePlugin, publishPackages, runSetup, runMigrate, searchRegistry, runSync, runVerify, runInitAgentsMd, runInitGeminiMd, TARGET_NAMES, WORKFLOW_NAMES, importEcc, recordPattern, resolveLearningBackend, storeStats, consolidateSessions, pruneNoisePatterns, lessonDeltaReport, removePatternsByIds, snapshotStore, recallHybrid, teachGuard, mirrorPatternsToVector, vectorMirrorEnabled, vectorTierStatus, resolveVectorEngine, reindexVectorStore, harmonizeVectorStore, importRvfCheckpoint, statuslineData, writeFeatureAdrState, computeUsage, deriveUsageCalibration, normalizeClaudeUsageModelKey, readUsageLimits, claimCheck, summarize, queryBookKnowledge, loadStorePatternsSync, patternRecordId, loadStoreRecords, recordToPattern, bundleSkills, brainHome, listBrain, promoteProjectToBrain, updateBrainSource, queryBrain, groundPrompt, expandKu, reindexBrainVectors, buildPrimer, exportBrainSlice, importBrainSlice, registerKusToBrain, RECALL_USAGE_LOG_RELATIVE, RECALL_USAGE_LOG_MAX_BYTES, parseRecallUsageLog, buildRecallUsageReport, buildManifest, buildSbom, resolveTrustRoot, decideVerifyPolicy, decideProvenance, isInsideTree, signManifest, verifyManifest, assertKeyOutsideTree, decidePublishGate, MANIFEST_NAME, SBOM_NAME, buildArchitectureMap, renderMapHuman, findArchitectureDrift, renderDriftReport, scanWorkspacePackages, loadSubsystemManifest, loadProductVision, checkFeatureAgainstArchitecture, renderArchCheck, planProjectSkills, guidanceForStage, renderInjectionReport, analyzeCorpus, renderRakeReport, renderCriticSection, rakeAsLesson, rakeReward, DEFAULT_RAKE_THRESHOLDS, streamSessionEvents, findLatestTranscript, detectProcessRakes, buildRetro, renderRetro, retroLessonText, PROCESS_SIGNATURES, RETRO_DOMAIN, scanForSetup, buildSetupPlan, scaffoldFromSpec, renderScaffoldPreview, readExistingForScaffold, assembleChallengeContext, buildChallengeBrief, pickAdversaryModel, CHALLENGE_QUESTIONS, loadOutcomes, renderOutcomes, statsForKey, selectAutoCost, recordProvisional, finalizeOutcome, COST_LADDER, } from '@dzhechkov/harness-core';
12
+ import { createSkill, getSkillInfo, getWorkflow, isTargetName, listSkills, runDoctor, runInit, benchmarkSkill, benchmarkSkills, scanMcp, reconcileCapabilities, RECONCILE_BANNER, buildRegistry, discoverSkillPackDirs, checkUpstream, compareSkills, checkAllUpstream, sweepSkillDrift, syncCanonicalSkill, checkUpgrades, discoverPackages, discoverSourcePackages, fetchAllDownloads, filterByCategory, pretrain, recommend, generatePlugin, publishPackages, runSetup, runMigrate, searchRegistry, runSync, runVerify, runInitAgentsMd, runInitGeminiMd, TARGET_NAMES, WORKFLOW_NAMES, importEcc, recordPattern, resolveLearningBackend, storeStats, consolidateSessions, pruneNoisePatterns, lessonDeltaReport, removePatternsByIds, snapshotStore, recallHybrid, teachGuard, mirrorPatternsToVector, vectorMirrorEnabled, vectorTierStatus, resolveVectorEngine, reindexVectorStore, harmonizeVectorStore, importRvfCheckpoint, statuslineData, writeFeatureAdrState, computeUsage, deriveUsageCalibration, normalizeClaudeUsageModelKey, readUsageLimits, claimCheck, summarize, queryBookKnowledge, loadStorePatternsSync, patternRecordId, loadStoreRecords, recordToPattern, bundleSkills, brainHome, listBrain, promoteProjectToBrain, updateBrainSource, queryBrain, groundPrompt, expandKu, reindexBrainVectors, buildPrimer, exportBrainSlice, importBrainSlice, registerKusToBrain, RECALL_USAGE_LOG_RELATIVE, RECALL_USAGE_LOG_MAX_BYTES, parseRecallUsageLog, buildRecallUsageReport, buildManifest, buildSbom, resolveTrustRoot, decideVerifyPolicy, decideProvenance, isInsideTree, signManifest, verifyManifest, assertKeyOutsideTree, decidePublishGate, MANIFEST_NAME, SBOM_NAME, buildArchitectureMap, renderMapHuman, findArchitectureDrift, renderDriftReport, scanWorkspacePackages, loadSubsystemManifest, loadProductVision, checkFeatureAgainstArchitecture, renderArchCheck, planProjectSkills, guidanceForStage, renderInjectionReport, analyzeCorpus, renderRakeReport, renderCriticSection, rakeAsLesson, rakeReward, DEFAULT_RAKE_THRESHOLDS, streamSessionEvents, findLatestTranscript, detectProcessRakes, buildRetro, renderRetro, retroLessonText, PROCESS_SIGNATURES, RETRO_DOMAIN, scanForSetup, buildSetupPlan, scaffoldFromSpec, renderScaffoldPreview, readExistingForScaffold, assembleChallengeContext, buildChallengeBrief, pickAdversaryModel, CHALLENGE_QUESTIONS, loadOutcomes, renderOutcomes, statsForKey, selectAutoCost, recordProvisional, finalizeOutcome, COST_LADDER, splitScenarios, budgetPlan, selectWinner, proseScopeOk, renderProseDiff, readScenarioIds, DEFAULT_MAX_JUDGE_RUNS, } from '@dzhechkov/harness-core';
13
13
  import { getPreset, PRESET_NAMES } from '@dzhechkov/harness-presets';
14
14
  import { scanGitHub, analyzeRepo, generateReport, deepAnalyze, scanAllSources, ScoutMemory } from '@dzhechkov/scout';
15
15
  const USAGE = `dz - DZ cross-platform harness CLI
@@ -4316,6 +4316,107 @@ function cmdRouting(options, flags, cwd, write) {
4316
4316
  write(renderOutcomes(store, stage));
4317
4317
  return 0;
4318
4318
  }
4319
+ /**
4320
+ * `dz bto-optimize` — the deterministic engine BEHIND the `/bto-optimize` skill (feature bto-optimize-holdout).
4321
+ * Adds dspy-MIPROv2 rigor the current evolutionary loop lacks: a hold-out split, a hard budget cap, and a
4322
+ * no-regress-on-holdout winner selector. NOT a rival command — the skill delegates these steps; candidate-prose
4323
+ * generation + judge scoring stay skill-side. `--json` on every subcommand.
4324
+ * --split --scenarios <csv|@file> [--holdout <r>] deterministic tune/holdout split
4325
+ * --plan --candidates K --rounds R --tune N --holdout M [--max C] budget plan (trims to the cap)
4326
+ * --select --baseline <@json> --candidates <@json> [--tolerance t] accept only on holdout no-regress
4327
+ * --scope-check --original <f> --candidate <f> prose-only guard
4328
+ * --diff --original <f> --candidate <f> the prose diff to confirm
4329
+ */
4330
+ function cmdBtoOptimize(options, flags, cwd, write) {
4331
+ const json = flags.has('json');
4332
+ const emit = (o) => { write(JSON.stringify(o, null, json ? 2 : 0)); return 0; };
4333
+ const readContained = (rel) => {
4334
+ // Containment (QE #traversal): an @file must stay under cwd — `@../../etc/passwd` must not read outside.
4335
+ const rootAbs = resolve(cwd);
4336
+ const abs = resolve(rootAbs, rel);
4337
+ if (abs !== rootAbs && !abs.startsWith(rootAbs + sep))
4338
+ throw new Error(`path escapes the working directory: ${rel}`);
4339
+ return readFileSync(abs, 'utf-8');
4340
+ };
4341
+ const readJson = (spec) => {
4342
+ if (spec === undefined)
4343
+ return undefined;
4344
+ const raw = spec.startsWith('@') ? readContained(spec.slice(1)) : spec;
4345
+ return JSON.parse(raw);
4346
+ };
4347
+ try {
4348
+ if (flags.has('split')) {
4349
+ const sc = options.get('scenarios');
4350
+ if (sc === undefined) {
4351
+ write('dz bto-optimize --split needs --scenarios <csv|@file>');
4352
+ return 1;
4353
+ }
4354
+ const ids = sc.startsWith('@') ? readScenarioIds(resolve(cwd, sc.slice(1))) : sc.split(',').map((s) => s.trim()).filter(Boolean);
4355
+ const ratio = Number.parseFloat(options.get('holdout') ?? '');
4356
+ const split = splitScenarios(ids, Number.isFinite(ratio) ? ratio : undefined);
4357
+ if (json)
4358
+ return emit(split);
4359
+ write(`tune (${split.tune.length}): ${split.tune.join(', ')}`);
4360
+ write(`holdout (${split.holdout.length}): ${split.holdout.join(', ')}`);
4361
+ return 0;
4362
+ }
4363
+ if (flags.has('plan')) {
4364
+ const n = (k, d) => { const v = Number.parseInt(options.get(k) ?? '', 10); return Number.isFinite(v) ? v : d; };
4365
+ const max = Number.parseInt(options.get('max') ?? '', 10);
4366
+ 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);
4367
+ if (json)
4368
+ return emit(plan);
4369
+ 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'})`);
4370
+ if (plan.trimmed)
4371
+ write(` trimmed to fit: ${plan.trimmed}`);
4372
+ return 0;
4373
+ }
4374
+ if (flags.has('select')) {
4375
+ const baseline = readJson(options.get('baseline'));
4376
+ const candidates = readJson(options.get('candidates'));
4377
+ if (!baseline || !Array.isArray(candidates)) {
4378
+ write('dz bto-optimize --select needs --baseline <@json {holdout}> and --candidates <@json [..]>');
4379
+ return 1;
4380
+ }
4381
+ const tol = Number.parseFloat(options.get('tolerance') ?? '');
4382
+ const result = selectWinner(baseline, candidates, Number.isFinite(tol) ? { tolerance: tol } : {});
4383
+ if (json)
4384
+ return emit(result);
4385
+ write(result.winner ? `✓ winner: ${result.winner} — ${result.reason}` : `✗ no winner — ${result.reason}`);
4386
+ return 0;
4387
+ }
4388
+ if (flags.has('scope-check') || flags.has('diff')) {
4389
+ const o = options.get('original');
4390
+ const c = options.get('candidate');
4391
+ if (o === undefined || c === undefined) {
4392
+ write('needs --original <file> and --candidate <file>');
4393
+ return 1;
4394
+ }
4395
+ const origText = readContained(o);
4396
+ const candText = readContained(c);
4397
+ if (flags.has('scope-check')) {
4398
+ const r = proseScopeOk(origText, candText);
4399
+ if (json)
4400
+ return emit(r);
4401
+ write(r.ok ? `✓ prose-only: ${r.reason}` : `✗ out of scope: ${r.reason}`);
4402
+ return r.ok ? 0 : 1;
4403
+ }
4404
+ write(renderProseDiff(origText, candText));
4405
+ return 0;
4406
+ }
4407
+ write('dz bto-optimize: pass --split | --plan | --select | --scope-check | --diff (see docs)');
4408
+ return 1;
4409
+ }
4410
+ catch (e) {
4411
+ const msg = e instanceof Error ? e.message : String(e);
4412
+ if (json) {
4413
+ write(JSON.stringify({ error: msg }));
4414
+ return 1;
4415
+ }
4416
+ write(`dz bto-optimize: ${msg}`);
4417
+ return 1;
4418
+ }
4419
+ }
4319
4420
  function cmdStats(cwd, write) {
4320
4421
  const baseDir = join(cwd, 'packages', '@dzhechkov');
4321
4422
  if (!existsSync(baseDir)) {
@@ -4577,6 +4678,8 @@ export async function runCli(argv, io = {}) {
4577
4678
  return cmdChallenge(options, flags, cwd, write);
4578
4679
  case 'routing':
4579
4680
  return cmdRouting(options, flags, cwd, write);
4681
+ case 'bto-optimize':
4682
+ return cmdBtoOptimize(options, flags, cwd, write);
4580
4683
  case 'dashboard':
4581
4684
  return cmdDashboard(cwd, write);
4582
4685
  case 'roam':