@dzhechkov/harness-cli 0.3.222 → 0.3.224
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 +62 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +126 -1
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
- package/src/cli.ts +131 -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.224",
|
|
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.118"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@types/node": "^25.6.0",
|
package/src/cli.ts
CHANGED
|
@@ -140,7 +140,19 @@ import {
|
|
|
140
140
|
scaffoldFromSpec,
|
|
141
141
|
renderScaffoldPreview,
|
|
142
142
|
readExistingForScaffold,
|
|
143
|
+
assembleChallengeContext,
|
|
144
|
+
buildChallengeBrief,
|
|
145
|
+
pickAdversaryModel,
|
|
146
|
+
CHALLENGE_QUESTIONS,
|
|
147
|
+
loadOutcomes,
|
|
148
|
+
renderOutcomes,
|
|
149
|
+
statsForKey,
|
|
150
|
+
selectAutoCost,
|
|
151
|
+
recordProvisional,
|
|
152
|
+
finalizeOutcome,
|
|
153
|
+
COST_LADDER,
|
|
143
154
|
} from '@dzhechkov/harness-core';
|
|
155
|
+
import type { Family, ModelRung } from '@dzhechkov/harness-core';
|
|
144
156
|
import type { SetupSpec } from '@dzhechkov/harness-core';
|
|
145
157
|
import type { ProvenanceMode, PackVerdict, ClaudeUsageModel, PatternRecord, TargetName, BookKU, HarmonizeReport, UsageCalibrationPlan, ClaimFinding, RecallUsagePatternRow } from '@dzhechkov/harness-core';
|
|
146
158
|
import { getPreset, PRESET_NAMES } from '@dzhechkov/harness-presets';
|
|
@@ -4485,6 +4497,13 @@ function cmdFeatureAdrSetup(options: Map<string, string>, flags: Set<string>, cw
|
|
|
4485
4497
|
for (const f of result.files) {
|
|
4486
4498
|
if (f.action === 'unchanged') continue; // never clobber
|
|
4487
4499
|
const abs = resolve(repoRoot, f.path);
|
|
4500
|
+
// Write-boundary augment-never-clobber guard (defense in depth): a 'create' must NEVER overwrite an
|
|
4501
|
+
// existing file, even if the plan (built from a caller-supplied `existing`) said create. 'augment' has
|
|
4502
|
+
// already union-merged the prior content, so overwriting there is the merged result, not a clobber.
|
|
4503
|
+
if (f.action === 'create' && existsSync(abs)) {
|
|
4504
|
+
write(` · skipped ${f.path} — already exists (not overwritten)`);
|
|
4505
|
+
continue;
|
|
4506
|
+
}
|
|
4488
4507
|
try {
|
|
4489
4508
|
mkdirSync(dirname(abs), { recursive: true });
|
|
4490
4509
|
writeFileSync(abs, f.content);
|
|
@@ -4496,6 +4515,114 @@ function cmdFeatureAdrSetup(options: Map<string, string>, flags: Set<string>, cw
|
|
|
4496
4515
|
return 0;
|
|
4497
4516
|
}
|
|
4498
4517
|
|
|
4518
|
+
/**
|
|
4519
|
+
* `dz challenge` — the deterministic "cartridge" behind the `challenge-panel` adversarial plan-gate (R6).
|
|
4520
|
+
* Assembles a WIDE context pack (plan + vision + testing + map + degradations) and prints the fixed C1-C8
|
|
4521
|
+
* "break it, don't confirm it" brief + the verdict schema. It runs NO LLM and writes nothing — the
|
|
4522
|
+
* `challenge-panel` SKILL fires the brief at a FRESH adversary (a model ≠ the plan author) + a mandatory
|
|
4523
|
+
* cross-validator. ADVISE, never block.
|
|
4524
|
+
* --plan <plan.md> the plan under review (required)
|
|
4525
|
+
* --json emit the assembled context + brief as JSON
|
|
4526
|
+
* --context-only just the assembled context summary (what the panel will read)
|
|
4527
|
+
* --author <model> the plan-author model → prints the cross-family adversary to dispatch (FR-4)
|
|
4528
|
+
*/
|
|
4529
|
+
function cmdChallenge(options: Map<string, string>, flags: Set<string>, cwd: string, write: Write): number {
|
|
4530
|
+
let repoRoot = cwd;
|
|
4531
|
+
try { repoRoot = execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' }).trim() || cwd; } catch { /* not git */ }
|
|
4532
|
+
|
|
4533
|
+
const planPath = options.get('plan');
|
|
4534
|
+
if (planPath === undefined) {
|
|
4535
|
+
write('dz challenge: pass --plan <plan.md> (the implementation plan to challenge).');
|
|
4536
|
+
return 1;
|
|
4537
|
+
}
|
|
4538
|
+
|
|
4539
|
+
const ctx = assembleChallengeContext(repoRoot, planPath);
|
|
4540
|
+
if (ctx.plan === '') {
|
|
4541
|
+
write(`dz challenge: plan not found or empty: ${planPath}`);
|
|
4542
|
+
return 1;
|
|
4543
|
+
}
|
|
4544
|
+
|
|
4545
|
+
const adversary = pickAdversaryModel(options.get('author') ?? 'claude');
|
|
4546
|
+
|
|
4547
|
+
if (flags.has('json')) {
|
|
4548
|
+
write(JSON.stringify({ context: ctx, brief: buildChallengeBrief(ctx), adversary, questions: CHALLENGE_QUESTIONS }, null, 2));
|
|
4549
|
+
return 0;
|
|
4550
|
+
}
|
|
4551
|
+
|
|
4552
|
+
if (flags.has('context-only')) {
|
|
4553
|
+
const has = (v: string | undefined): string => (v === undefined ? '✗ (less calibration)' : `✓ ${v.length} chars`);
|
|
4554
|
+
write(`challenge context for ${planPath}:`);
|
|
4555
|
+
write(` plan: ✓ ${ctx.plan.length} chars`);
|
|
4556
|
+
write(` vision: ${has(ctx.vision)}`);
|
|
4557
|
+
write(` testing: ${has(ctx.testing)}`);
|
|
4558
|
+
write(` map: ${has(ctx.map)}`);
|
|
4559
|
+
write(` degradations:${has(ctx.degradations)}`);
|
|
4560
|
+
write(` → adversary: ${adversary.model} — ${adversary.note}`);
|
|
4561
|
+
return 0;
|
|
4562
|
+
}
|
|
4563
|
+
|
|
4564
|
+
write(buildChallengeBrief(ctx));
|
|
4565
|
+
write(`\n── dispatch (panel ≠ plan author) ──\n${adversary.model}: ${adversary.note}`);
|
|
4566
|
+
return 0;
|
|
4567
|
+
}
|
|
4568
|
+
|
|
4569
|
+
/**
|
|
4570
|
+
* `dz routing` — inspect the learned cost-optimal routing outcome store (feature learned-cost-routing). Shows
|
|
4571
|
+
* what `args.models[stage]='auto-cost'` currently believes per (stage, complexity-tier, model): gated
|
|
4572
|
+
* attempts / successes / rate. Read-only.
|
|
4573
|
+
* --stage <s> filter to one pipeline stage (code, qe, plan, …)
|
|
4574
|
+
* --json raw store JSON
|
|
4575
|
+
*/
|
|
4576
|
+
function cmdRouting(options: Map<string, string>, flags: Set<string>, cwd: string, write: Write): number {
|
|
4577
|
+
let repoRoot = cwd;
|
|
4578
|
+
try { repoRoot = execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' }).trim() || cwd; } catch { /* not git */ }
|
|
4579
|
+
|
|
4580
|
+
const stage = options.get('stage');
|
|
4581
|
+
const tier = options.get('tier');
|
|
4582
|
+
const model = options.get('model');
|
|
4583
|
+
|
|
4584
|
+
// --select: resolve an `auto-cost` stage → the concrete model + escalate chain (the workflow shells out here
|
|
4585
|
+
// because it is sandboxed with no fs). --family restricts to the coder's cross-family (qe guard); --ladder is
|
|
4586
|
+
// the probe-filtered id set (account-specific Codex ids that answered).
|
|
4587
|
+
if (flags.has('select')) {
|
|
4588
|
+
if (!stage || !tier) { write('dz routing --select needs --stage and --tier'); return 1; }
|
|
4589
|
+
const fam = options.get('family');
|
|
4590
|
+
const family: Family | undefined = fam === 'claude' || fam === 'openai' ? fam : undefined;
|
|
4591
|
+
const ladderCsv = options.get('ladder');
|
|
4592
|
+
let ladder: readonly ModelRung[] | undefined;
|
|
4593
|
+
if (ladderCsv !== undefined) {
|
|
4594
|
+
const ids = new Set(ladderCsv.split(',').map((s) => s.trim()).filter(Boolean));
|
|
4595
|
+
ladder = COST_LADDER.filter((r) => ids.has(r.id));
|
|
4596
|
+
}
|
|
4597
|
+
const statsFor = statsForKey(loadOutcomes(repoRoot), stage, tier);
|
|
4598
|
+
const pick = selectAutoCost(stage, tier, statsFor, { ...(family ? { family } : {}), ...(ladder ? { ladder } : {}) });
|
|
4599
|
+
write(JSON.stringify(pick));
|
|
4600
|
+
return 0;
|
|
4601
|
+
}
|
|
4602
|
+
|
|
4603
|
+
// --record-provisional / --finalize: the two-phase outcome label. The workflow calls these at stage end
|
|
4604
|
+
// (provisional) and at the downstream gate (finalize, authoritative).
|
|
4605
|
+
if (flags.has('record-provisional')) {
|
|
4606
|
+
if (!stage || !tier || !model) { write('dz routing --record-provisional needs --stage --tier --model'); return 1; }
|
|
4607
|
+
recordProvisional(repoRoot, stage, tier, model, flags.has('weak'));
|
|
4608
|
+
write(`recorded provisional: ${stage}/${tier}/${model}${flags.has('weak') ? ' (weak-credit)' : ''}`);
|
|
4609
|
+
return 0;
|
|
4610
|
+
}
|
|
4611
|
+
if (flags.has('finalize')) {
|
|
4612
|
+
if (!stage || !tier || !model) { write('dz routing --finalize needs --stage --tier --model --success <true|false>'); return 1; }
|
|
4613
|
+
const success = options.get('success') === 'true';
|
|
4614
|
+
finalizeOutcome(repoRoot, stage, tier, model, success);
|
|
4615
|
+
write(`finalized: ${stage}/${tier}/${model} → ${success ? 'success' : 'FAILURE'}`);
|
|
4616
|
+
return 0;
|
|
4617
|
+
}
|
|
4618
|
+
|
|
4619
|
+
// default / --json: inspect the learned table.
|
|
4620
|
+
const store = loadOutcomes(repoRoot);
|
|
4621
|
+
if (flags.has('json')) { write(JSON.stringify(store, null, 2)); return 0; }
|
|
4622
|
+
write(renderOutcomes(store, stage));
|
|
4623
|
+
return 0;
|
|
4624
|
+
}
|
|
4625
|
+
|
|
4499
4626
|
function cmdStats(cwd: string, write: Write): number {
|
|
4500
4627
|
const baseDir = join(cwd, 'packages', '@dzhechkov');
|
|
4501
4628
|
if (!existsSync(baseDir)) {
|
|
@@ -4770,6 +4897,10 @@ export async function runCli(argv: string[], io: CliIo = {}): Promise<number> {
|
|
|
4770
4897
|
return await cmdRetro(options, flags, cwd, write);
|
|
4771
4898
|
case 'feature-adr-setup':
|
|
4772
4899
|
return cmdFeatureAdrSetup(options, flags, cwd, write);
|
|
4900
|
+
case 'challenge':
|
|
4901
|
+
return cmdChallenge(options, flags, cwd, write);
|
|
4902
|
+
case 'routing':
|
|
4903
|
+
return cmdRouting(options, flags, cwd, write);
|
|
4773
4904
|
case 'dashboard':
|
|
4774
4905
|
return cmdDashboard(cwd, write);
|
|
4775
4906
|
case 'roam':
|