@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/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 (
|
|
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]
|
|
@@ -483,6 +483,8 @@ dz mr-rakes [--json] [--candidate N --confirmed N] [--teach] [--gen-cri
|
|
|
483
483
|
dz retro [transcript] [--json] [--threshold N] [--no-teach] [--install-hook] # per-session retro: mine the current session for recurring PROCESS rakes (claimed-done-without-verify, committed-without-verify, n-fix-cycles, ignored-correction), drill the user (socratic + checklist) AND teach the agent — co-learning via the dz teach store
|
|
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
|
+
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
|
|
486
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)
|
|
487
489
|
dz auto-canonicalize --source <github-url> --pack <skills-pack>
|
|
488
490
|
dz sync-upstream [--package <dir>] [--list] [--all]
|
|
@@ -1460,6 +1462,59 @@ yields shallow findings). In the feature-adr pipeline this runs automatically at
|
|
|
1460
1462
|
just say **"прогони challenge на этот план"** / **"challenge this plan"**. Scaffold the degradations registry with
|
|
1461
1463
|
`dz feature-adr-setup --from-spec <spec with {"degradations":true}> --apply`.
|
|
1462
1464
|
|
|
1465
|
+
### `dz routing` — when you want feature-adr to LEARN which model is worth paying for
|
|
1466
|
+
|
|
1467
|
+
feature-adr can route a stage to a fixed model (`args.models.code='opus'`), but you may not know which model is
|
|
1468
|
+
actually worth it. The **`auto-cost`** spec learns from real outcomes: it picks the **cheapest** model whose
|
|
1469
|
+
learned success-rate is **≥0.7** at `(stage, complexity-tier)`, and a model whose code later **fails the Step-8
|
|
1470
|
+
QE gate is down-ranked** (success is *passing the gate*, not "returned something"). Grounded in rUv's shipped
|
|
1471
|
+
`MetaHarnessRouter`. Turn it on per stage:
|
|
1472
|
+
```js
|
|
1473
|
+
Workflow({ scriptPath: '.claude/workflows/feature-adr.js',
|
|
1474
|
+
args: { slug, description, tier: 'L', models: { code: 'auto-cost', qe: 'auto-cost' } } })
|
|
1475
|
+
```
|
|
1476
|
+
```bash
|
|
1477
|
+
dz routing # the learned table — what auto-cost currently believes
|
|
1478
|
+
dz routing --stage code # just the code stage
|
|
1479
|
+
dz routing --json # raw store (.dz/routing-outcomes.json)
|
|
1480
|
+
```
|
|
1481
|
+
```
|
|
1482
|
+
Learned routing outcomes (what `auto-cost` currently believes):
|
|
1483
|
+
|
|
1484
|
+
## code
|
|
1485
|
+
L sonnet 100% (4/4 gated, 4 provisional)
|
|
1486
|
+
L opus 60% (3/5 gated)
|
|
1487
|
+
```
|
|
1488
|
+
Cold-start (no history) walks a cheapest-first chain and escalates on failure; once a model clears the bar it is
|
|
1489
|
+
chosen directly. Two invariants held hard: `auto-cost` on the **qe** stage only ever ranks the **cross-family of
|
|
1490
|
+
the coder** (a model never self-QEs), and the whole thing is **opt-in** — with no `auto-cost` spec anywhere the
|
|
1491
|
+
pipeline is byte-identical and the store is never touched. Layered under the usage-adaptive override (a session
|
|
1492
|
+
near its limit still switches to Codex first). Storage is a plain JSON file — zero native deps.
|
|
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
|
+
|
|
1463
1518
|
## Recipes & FAQ
|
|
1464
1519
|
|
|
1465
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;
|
|
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, } 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
|
|
@@ -4249,6 +4249,174 @@ function cmdChallenge(options, flags, cwd, write) {
|
|
|
4249
4249
|
write(`\n── dispatch (panel ≠ plan author) ──\n${adversary.model}: ${adversary.note}`);
|
|
4250
4250
|
return 0;
|
|
4251
4251
|
}
|
|
4252
|
+
/**
|
|
4253
|
+
* `dz routing` — inspect the learned cost-optimal routing outcome store (feature learned-cost-routing). Shows
|
|
4254
|
+
* what `args.models[stage]='auto-cost'` currently believes per (stage, complexity-tier, model): gated
|
|
4255
|
+
* attempts / successes / rate. Read-only.
|
|
4256
|
+
* --stage <s> filter to one pipeline stage (code, qe, plan, …)
|
|
4257
|
+
* --json raw store JSON
|
|
4258
|
+
*/
|
|
4259
|
+
function cmdRouting(options, flags, cwd, write) {
|
|
4260
|
+
let repoRoot = cwd;
|
|
4261
|
+
try {
|
|
4262
|
+
repoRoot = execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' }).trim() || cwd;
|
|
4263
|
+
}
|
|
4264
|
+
catch { /* not git */ }
|
|
4265
|
+
const stage = options.get('stage');
|
|
4266
|
+
const tier = options.get('tier');
|
|
4267
|
+
const model = options.get('model');
|
|
4268
|
+
// --select: resolve an `auto-cost` stage → the concrete model + escalate chain (the workflow shells out here
|
|
4269
|
+
// because it is sandboxed with no fs). --family restricts to the coder's cross-family (qe guard); --ladder is
|
|
4270
|
+
// the probe-filtered id set (account-specific Codex ids that answered).
|
|
4271
|
+
if (flags.has('select')) {
|
|
4272
|
+
if (!stage || !tier) {
|
|
4273
|
+
write('dz routing --select needs --stage and --tier');
|
|
4274
|
+
return 1;
|
|
4275
|
+
}
|
|
4276
|
+
const fam = options.get('family');
|
|
4277
|
+
const family = fam === 'claude' || fam === 'openai' ? fam : undefined;
|
|
4278
|
+
const ladderCsv = options.get('ladder');
|
|
4279
|
+
let ladder;
|
|
4280
|
+
if (ladderCsv !== undefined) {
|
|
4281
|
+
const ids = new Set(ladderCsv.split(',').map((s) => s.trim()).filter(Boolean));
|
|
4282
|
+
ladder = COST_LADDER.filter((r) => ids.has(r.id));
|
|
4283
|
+
}
|
|
4284
|
+
const statsFor = statsForKey(loadOutcomes(repoRoot), stage, tier);
|
|
4285
|
+
const pick = selectAutoCost(stage, tier, statsFor, { ...(family ? { family } : {}), ...(ladder ? { ladder } : {}) });
|
|
4286
|
+
write(JSON.stringify(pick));
|
|
4287
|
+
return 0;
|
|
4288
|
+
}
|
|
4289
|
+
// --record-provisional / --finalize: the two-phase outcome label. The workflow calls these at stage end
|
|
4290
|
+
// (provisional) and at the downstream gate (finalize, authoritative).
|
|
4291
|
+
if (flags.has('record-provisional')) {
|
|
4292
|
+
if (!stage || !tier || !model) {
|
|
4293
|
+
write('dz routing --record-provisional needs --stage --tier --model');
|
|
4294
|
+
return 1;
|
|
4295
|
+
}
|
|
4296
|
+
recordProvisional(repoRoot, stage, tier, model, flags.has('weak'));
|
|
4297
|
+
write(`recorded provisional: ${stage}/${tier}/${model}${flags.has('weak') ? ' (weak-credit)' : ''}`);
|
|
4298
|
+
return 0;
|
|
4299
|
+
}
|
|
4300
|
+
if (flags.has('finalize')) {
|
|
4301
|
+
if (!stage || !tier || !model) {
|
|
4302
|
+
write('dz routing --finalize needs --stage --tier --model --success <true|false>');
|
|
4303
|
+
return 1;
|
|
4304
|
+
}
|
|
4305
|
+
const success = options.get('success') === 'true';
|
|
4306
|
+
finalizeOutcome(repoRoot, stage, tier, model, success);
|
|
4307
|
+
write(`finalized: ${stage}/${tier}/${model} → ${success ? 'success' : 'FAILURE'}`);
|
|
4308
|
+
return 0;
|
|
4309
|
+
}
|
|
4310
|
+
// default / --json: inspect the learned table.
|
|
4311
|
+
const store = loadOutcomes(repoRoot);
|
|
4312
|
+
if (flags.has('json')) {
|
|
4313
|
+
write(JSON.stringify(store, null, 2));
|
|
4314
|
+
return 0;
|
|
4315
|
+
}
|
|
4316
|
+
write(renderOutcomes(store, stage));
|
|
4317
|
+
return 0;
|
|
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
|
+
}
|
|
4252
4420
|
function cmdStats(cwd, write) {
|
|
4253
4421
|
const baseDir = join(cwd, 'packages', '@dzhechkov');
|
|
4254
4422
|
if (!existsSync(baseDir)) {
|
|
@@ -4508,6 +4676,10 @@ export async function runCli(argv, io = {}) {
|
|
|
4508
4676
|
return cmdFeatureAdrSetup(options, flags, cwd, write);
|
|
4509
4677
|
case 'challenge':
|
|
4510
4678
|
return cmdChallenge(options, flags, cwd, write);
|
|
4679
|
+
case 'routing':
|
|
4680
|
+
return cmdRouting(options, flags, cwd, write);
|
|
4681
|
+
case 'bto-optimize':
|
|
4682
|
+
return cmdBtoOptimize(options, flags, cwd, write);
|
|
4511
4683
|
case 'dashboard':
|
|
4512
4684
|
return cmdDashboard(cwd, write);
|
|
4513
4685
|
case 'roam':
|