@dzhechkov/harness-cli 0.3.247 → 0.3.249

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dzhechkov/harness-cli",
3
- "version": "0.3.247",
3
+ "version": "0.3.249",
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",
@@ -41,6 +41,7 @@
41
41
  "sbom.json"
42
42
  ],
43
43
  "dependencies": {
44
+ "@dzhechkov/harness-core": "^0.3.140",
44
45
  "@dzhechkov/harness-presets": "^0.5.0",
45
46
  "@dzhechkov/scout": "^0.8.0",
46
47
  "@dzhechkov/skills-devops": "^0.3.0",
@@ -56,8 +57,7 @@
56
57
  "@dzhechkov/skills-idea2prd": "^0.1.0",
57
58
  "@dzhechkov/skills-reverse-engineering": "^0.1.0",
58
59
  "@dzhechkov/skills-presentation-storyteller": "^0.1.0",
59
- "@dzhechkov/skills-website-cloner": "^0.1.0",
60
- "@dzhechkov/harness-core": "0.3.138"
60
+ "@dzhechkov/skills-website-cloner": "^0.1.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@types/node": "^25.6.0",
package/sbom.json CHANGED
@@ -25,7 +25,7 @@
25
25
  "hashes": [
26
26
  {
27
27
  "alg": "SHA-256",
28
- "content": "129ee8a3fc96a1edda2258d02cd6dc4b8df2ab766f3b03ddb38f1559f0c0d9b9"
28
+ "content": "0a4369c88d48323b3b2ceb18b89ec3a0b6f6f7016778de3ac583f8397531b1b3"
29
29
  }
30
30
  ]
31
31
  },
@@ -105,7 +105,7 @@
105
105
  "hashes": [
106
106
  {
107
107
  "alg": "SHA-256",
108
- "content": "894d5779615292e57a6b5c204ebd04cef4b065e3fe4d8546c0f3dfbe0fb22529"
108
+ "content": "e176f74bf8ff0fe938e1bf9b25db392c1adbbd4b7b5b2b63520cb8baa99df82d"
109
109
  }
110
110
  ]
111
111
  },
@@ -115,7 +115,7 @@
115
115
  "hashes": [
116
116
  {
117
117
  "alg": "SHA-256",
118
- "content": "aaaf1d89fb27f39c125c3404b37d57f9251fe62d7993f79df5696944ae8bddcf"
118
+ "content": "8bfdc5551ea8d4897f8bb2ab50e374145965e73b4dca8d01325f5388412d804c"
119
119
  }
120
120
  ]
121
121
  },
@@ -125,7 +125,7 @@
125
125
  "hashes": [
126
126
  {
127
127
  "alg": "SHA-256",
128
- "content": "dfdfb4feeb07bf037194a1aa7da4f956e88da30ec8c1848fa2e989d82ed34a0b"
128
+ "content": "0ae60f97c9b1b9e7afabcc8aa540eae700950539f5adc511c6f02cbe634775bd"
129
129
  }
130
130
  ]
131
131
  },
@@ -185,7 +185,7 @@
185
185
  "hashes": [
186
186
  {
187
187
  "alg": "SHA-256",
188
- "content": "16065506fcf54ea10fab7342da217ff2416818c20e05dd998036a75075083330"
188
+ "content": "207f5a322bb9154eb34478746318abf1f67660f670a92d7bede2fd4a725d4962"
189
189
  }
190
190
  ]
191
191
  },
@@ -205,7 +205,7 @@
205
205
  "hashes": [
206
206
  {
207
207
  "alg": "SHA-256",
208
- "content": "967efe0f790775d915f31665e270490794a8ca4bdbecd0844562edda2edf42d9"
208
+ "content": "1d3e6fb9e19aa8fd589487335a8004cc6f7a6af43472b40e31f9139a482f7e85"
209
209
  }
210
210
  ]
211
211
  },
package/src/cli.ts CHANGED
@@ -200,6 +200,8 @@ import {
200
200
  renderContentProbe,
201
201
  findNonRegistrableSkillDirs,
202
202
  assembleCompoundingReport,
203
+ scoreRun,
204
+ renderScorecard,
203
205
  renderCompoundingReport,
204
206
  readReinforcementState,
205
207
  readQuarantineState,
@@ -237,6 +239,7 @@ Usage:
237
239
  dz delivery-check --slug <slug> [--context-only] [--findings <f.json>] [--strict] [--author <model>] [--json] (portable Step-10 Delivery Gate: prints the 4-plane review brief + artifact probes; --findings classifies a fed-back review into a fail-closed ready|blocked hand-off and writes features/<slug>/10_delivery_review.md; --strict exits 1 on blocked)
238
240
  dz skills-verify [--dir <project>] [--expect a,b] [--static] [--strict] [--json] (does .claude/skills/ actually REGISTER? --static = instant layout scan for CI; default reads the authoritative system/init listing from a real session. exit 0 pass / 1 fail / 2 inconclusive — never a false pass)
239
241
  dz compounding [--project <dir>] [--json] (honest learning-loop payoff report: pool write-only ratio, guard repeat-violation trajectory, cold-vs-warm replay readiness, instrumentation health — a gate without enough data says INSUFFICIENT_DATA, never a fake verdict)
242
+ dz score --slug <feature> [--project <dir>] [--json] (process scorecard for ONE feature-adr run, from its artifacts: ADR confirmation, discrimination, cross-model QE grade, live verification, README-first, learning loop, amendments — descriptive-only, a low score exits 0)
240
243
  dz setup --target <name> [--preset <name>] [--select id,id,...] [--skills-dir <dir>] [--project <dir>] [--memory agentdb] [--no-memory] [--no-hooks] [--install-driver] [--force] [--enrich]
241
244
  dz teach "<pattern>" [--reward <0-1>] [--domain <name>] [--type rule|success-pattern|lesson-learned] [--project <dir>] [--no-mirror] (--project pins the learned store to <dir>/.dz, not the cwd — pin to a canonical brain)
242
245
  dz teach --from-json <file> [--project <dir>] [--no-mirror] (bulk-import a 'dz recall --all --json' export — share a learned store across machines)
@@ -5625,6 +5628,87 @@ function nameFor(t: { file: string; name?: string }, outcome: 'pass' | 'fail' |
5625
5628
  * --author <model> cosmetic: the reviewer to dispatch, printed alongside the brief
5626
5629
  * --json machine contract { planesChecked, planesSkipped, findings, handoff, artifact }
5627
5630
  */
5631
+ /**
5632
+ * `dz score --slug <feature>` — score a feature-adr RUN's process discipline from its artifacts
5633
+ * (feature dz-score, Reading C). Descriptive-only: exit 0 on any score; non-zero only on usage errors.
5634
+ */
5635
+ function cmdScore(options: Map<string, string>, flags: Set<string>, cwd: string, write: Write): number {
5636
+ const json = flags.has('json');
5637
+ if (flags.has('help')) {
5638
+ const usage = 'dz score --slug <feature> [--project <dir>] [--json] — process scorecard for one feature-adr run (descriptive-only, never a gate)';
5639
+ if (json) write(JSON.stringify({ help: usage, exitCode: 0 })); // --json stays ONE document even for help
5640
+ else {
5641
+ write(usage);
5642
+ write(' disciplines: ADR confirmation · discrimination · cross-model QE · live verification · README-first · learning loop · amendments');
5643
+ write(' descriptive-only, never a gate: a low score exits 0');
5644
+ }
5645
+ return 0;
5646
+ }
5647
+ for (const flag of flags) {
5648
+ if (!new Set(['json', 'help']).has(flag)) {
5649
+ write(json ? JSON.stringify({ error: `unknown option --${flag}`, exitCode: 1 }) : `dz score: unknown option --${flag}\n allowed: --slug <feature>, --project <dir>, --json`);
5650
+ return 1;
5651
+ }
5652
+ }
5653
+ for (const key of options.keys()) {
5654
+ if (key.startsWith('_positional_') || !new Set(['slug', 'project']).has(key)) {
5655
+ const what = key.startsWith('_positional_') ? `unexpected argument "${options.get(key)}"` : `unknown option --${key}`;
5656
+ write(json ? JSON.stringify({ error: what, exitCode: 1 }) : `dz score: ${what}\n allowed: --slug <feature>, --project <dir>, --json`);
5657
+ return 1;
5658
+ }
5659
+ }
5660
+ const slug = options.get('slug') ?? '';
5661
+ // The delivery-check traversal lesson, upgraded to a WHITELIST: `.` slipped the blacklist and
5662
+ // silently aggregated the entire features/ tree as one "run" (Codex QE #2).
5663
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(slug) || slug === '.' || slug === '..' || slug.includes('..')) {
5664
+ write(json ? JSON.stringify({ error: 'a plain --slug <feature> is required (no path separators)', exitCode: 1 }) : 'dz score: a plain --slug <feature> is required (no path separators)');
5665
+ return 1;
5666
+ }
5667
+ const root = resolve(cwd, options.get('project') ?? '.');
5668
+ const dir = join(root, 'features', slug);
5669
+ if (!existsSync(dir)) {
5670
+ write(json ? JSON.stringify({ error: `no run at features/${slug}/`, exitCode: 1 }) : `dz score: no run at features/${slug}/`);
5671
+ return 1;
5672
+ }
5673
+
5674
+ // Gather the artifact texts (bounded, best-effort — an unreadable file is skipped, not fatal).
5675
+ const artifacts: Record<string, string> = {};
5676
+ let fileBudget = 200; // a run has ~a dozen artifacts; a wide tree must degrade, not exhaust memory
5677
+ const walk = (d: string, prefix: string, depth: number): void => {
5678
+ if (depth > 3 || fileBudget <= 0) return;
5679
+ let entries: string[];
5680
+ try {
5681
+ entries = readdirSync(d);
5682
+ } catch {
5683
+ return;
5684
+ }
5685
+ for (const name of entries) {
5686
+ if (fileBudget <= 0) return;
5687
+ const full = join(d, name);
5688
+ const rel = prefix === '' ? name : `${prefix}/${name}`;
5689
+ try {
5690
+ // lstat: a symlinked artifact (or a symlinked features/<slug> subtree) could smuggle
5691
+ // OUTSIDE text into the scorecard's evidence (Codex QE #5).
5692
+ const st = lstatSync(full);
5693
+ if (st.isSymbolicLink()) continue;
5694
+ if (st.isDirectory()) walk(full, rel, depth + 1);
5695
+ else if (st.isFile() && name.endsWith('.md') && st.size < 512 * 1024) {
5696
+ artifacts[rel] = readFileSync(full, 'utf-8');
5697
+ fileBudget -= 1;
5698
+ }
5699
+ } catch {
5700
+ /* skip unreadable */
5701
+ }
5702
+ }
5703
+ };
5704
+ walk(dir, '', 0);
5705
+
5706
+ const card = scoreRun(slug, artifacts);
5707
+ if (json) write(JSON.stringify({ ...card, exitCode: 0 }, null, 2));
5708
+ else write(renderScorecard(card));
5709
+ return 0;
5710
+ }
5711
+
5628
5712
  /**
5629
5713
  * `dz compounding` — does the learning loop actually PAY? (feature compounding, scout C2.)
5630
5714
  * Gathers the facts (store rows, apply-leg usage log, guard audit) and hands them to the PURE
@@ -6495,6 +6579,8 @@ export async function runCli(argv: string[], io: CliIo = {}): Promise<number> {
6495
6579
  return cmdSkillsVerify(options, flags, cwd, write);
6496
6580
  case 'compounding':
6497
6581
  return cmdCompounding(options, flags, cwd, write);
6582
+ case 'score':
6583
+ return cmdScore(options, flags, cwd, write);
6498
6584
  case 'routing':
6499
6585
  return cmdRouting(options, flags, cwd, write);
6500
6586
  case 'bto-optimize':