@dzhechkov/harness-cli 0.3.241 → 0.3.243
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/.dz-manifest.json +8 -8
- package/README.md +41 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +108 -6
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
- package/sbom.json +7 -7
- package/src/cli.ts +99 -5
package/.dz-manifest.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
"path": "README.md",
|
|
12
|
-
"sha256": "
|
|
12
|
+
"sha256": "5f4e31653970ef6970b6d87c862a1c6ac1f2da92c3f4f913b4be4155284db1f4"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"path": "coverage/coverage-final.json",
|
|
@@ -41,15 +41,15 @@
|
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"path": "dist/cli.d.ts.map",
|
|
44
|
-
"sha256": "
|
|
44
|
+
"sha256": "2bb00d2f42334250ee57b4d0f88d92968cbe3f010ec8d75bb748c0ee6f8de0b8"
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
"path": "dist/cli.js",
|
|
48
|
-
"sha256": "
|
|
48
|
+
"sha256": "d959d7623b68286e440e04f90ee3ae7af0166c1fe506f2cf7e6e9e317439409d"
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
"path": "dist/cli.js.map",
|
|
52
|
-
"sha256": "
|
|
52
|
+
"sha256": "68e0bbf621169f7bae350a5b723f9425969ab12b6f7dd98ce26bb06d32c77c92"
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
"path": "dist/index.d.ts",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
"path": "package.json",
|
|
76
|
-
"sha256": "
|
|
76
|
+
"sha256": "e26250419d70ec922def52f25f63000b658eec8203b29a2135edad1be98f8d5d"
|
|
77
77
|
},
|
|
78
78
|
{
|
|
79
79
|
"path": "src/bin.ts",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
"path": "src/cli.ts",
|
|
84
|
-
"sha256": "
|
|
84
|
+
"sha256": "14a3db7155fcc858272439a55bc7a43a09c7f6fd7caa302e5a13c153fe1a6d16"
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
"path": "src/index.ts",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
},
|
|
90
90
|
{
|
|
91
91
|
"path": "test/cli.test.ts",
|
|
92
|
-
"sha256": "
|
|
92
|
+
"sha256": "a82b15dd9d166764479528e9f9ede45fc39d1b4068a96f19f158aa5a4c03369b"
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
"path": "tsconfig.json",
|
|
@@ -101,5 +101,5 @@
|
|
|
101
101
|
}
|
|
102
102
|
]
|
|
103
103
|
},
|
|
104
|
-
"signature": "
|
|
104
|
+
"signature": "gGmbCW5Q8VH8CPNbUxHMrZBTxa6wqihi7F0bfUw+dyfN09I/hBQJZzhkautvL4TeFLGJGxkzzXVvpBqjoLHEAw=="
|
|
105
105
|
}
|
package/README.md
CHANGED
|
@@ -434,6 +434,31 @@ dz parity — codex (capabilities: shell, skills, mcp)
|
|
|
434
434
|
**When to use:** before promising a workflow to a teammate on Cursor/Codex/Hermes; when choosing
|
|
435
435
|
a target for a project; as the requirements input for porting a feature to more targets.
|
|
436
436
|
|
|
437
|
+
### Usage estimates you can act on — `dz usage`
|
|
438
|
+
|
|
439
|
+
`dz usage` estimates how much of your Claude session (5h block) and week you have spent, from local
|
|
440
|
+
transcripts. It drives feature-adr's pre-emptive "switch to Codex before the limit" routing.
|
|
441
|
+
|
|
442
|
+
Two things changed to make it mean something:
|
|
443
|
+
|
|
444
|
+
- **Cost-weighted, not a raw sum.** A flat token sum is 89–99.7% cache-read (MEASURED), which grows
|
|
445
|
+
with *conversation length*, not with work done — two sessions doing identical work differed by
|
|
446
|
+
orders of magnitude. Tokens are now **input-equivalents** (input 1x, cache-write 1.25x — 2x for a
|
|
447
|
+
1-hour TTL write, cache-read 0.1x, output 5x).
|
|
448
|
+
- **Subagent transcripts count.** `<session>/subagents/*.jsonl` carry real, non-duplicated usage and
|
|
449
|
+
were silently excluded.
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
dz usage # session ~13% (resets 10:24) · week ~15% (resets 08:59) · estimated
|
|
453
|
+
dz usage --json # machine-readable; pct is null when limits are unconfigured
|
|
454
|
+
dz usage --calibrate --session 42 --weekly 61 # teach it YOUR real numbers from claude.ai
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
**`pct` is `null` until you configure limits — that is deliberate**, an unconfigured estimate is not
|
|
458
|
+
a guess dressed as a number. The authoritative calibration is `--calibrate` with the percentages
|
|
459
|
+
shown on claude.ai/settings/usage; absent that, limits set from your own observed peak mean "unusually
|
|
460
|
+
heavy **for you**", which is exactly what a pre-emptive routing switch needs.
|
|
461
|
+
|
|
437
462
|
### Do your skills actually register? — `dz skills-verify`
|
|
438
463
|
|
|
439
464
|
Shipping a skill pack is not the same as a skill **registering**. A layout test that asserts
|
|
@@ -492,6 +517,22 @@ binary, a login prompt, a timeout, or a session that read a *different* project
|
|
|
492
517
|
missing, so its listing is refused rather than believed.) A layout so broken that nothing is
|
|
493
518
|
registrable fails too, instead of passing vacuously on an empty expectation.
|
|
494
519
|
|
|
520
|
+
**Registration is not usability — `--live-content`.** A skill can be in the session listing and still
|
|
521
|
+
be unusable content. That was the third, manual step of the health-advisor verification: invoke it and
|
|
522
|
+
check the model really loaded its `SKILL.md`. `--live-content` automates it — an extra turn that asks a
|
|
523
|
+
live model to name the expected skills and **quote a heading verbatim** from one:
|
|
524
|
+
|
|
525
|
+
```
|
|
526
|
+
$ dz skills-verify --expect api-design,debugging,explore --live-content
|
|
527
|
+
dz skills-verify: PASS — all 3 expected skill(s) are registered
|
|
528
|
+
content probe (ADVISORY, model-mediated — never a gate): coherent — every probed skill was named and one was quoted
|
|
529
|
+
quoted: "## When to use"
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
It is **advisory by construction and always will be**: the answer comes from a model, so it never
|
|
533
|
+
changes the exit code and never promotes a verdict. Naming a skill without quoting anything is
|
|
534
|
+
`partial` (naming is not proof the content loaded); an empty answer is `unreadable`, not a pass.
|
|
535
|
+
|
|
495
536
|
**Use it when:** publishing a package that installs skills, after `dz setup` in a new project, or in
|
|
496
537
|
CI (`--static` needs no Claude session at all).
|
|
497
538
|
|
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;AAwRH,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;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,iBAAiB,CAAC;CAC5C;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,KACvD;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AA20L9E,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAoJ5E"}
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
9
9
|
import { execSync, spawn } from 'node:child_process';
|
|
10
10
|
import { homedir, tmpdir } 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, buildParityMatrix, TARGET_CAPABILITIES, TARGET_SHORT_LABELS, WORKFLOW_NAMES, importEcc, recordPattern, resolveLearningBackend, storeStats, consolidateSessions, pruneNoisePatterns, lessonDeltaReport, removePatternsByIds, snapshotStore, recallHybrid, teachGuard, mirrorPatternsToVector, mirrorEntriesToVector, patternVectorEntry, readMemoryLearningConfig, promotePatterns, quarantineExpiryCandidates, pruneQuarantinePatterns, clearAgentdbQuarantine, 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, generateSigningKeypair, evaluateGuard, resolveRules, auditRecord, guardExitCode, DEFAULT_RULES, decideProvenance, isInsideTree, signManifest, verifyManifest, listSignablePackFiles, assertKeyOutsideTree, decidePublishGate, collectPackageFacts, planReleaseGates, selectAffectedPackages, classifyGateExecutions, buildFailureIssue, buildReleaseNotes, releaseTagName, firstOutputLine, formatPublishError, 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, planDiscriminationCheck, classifyDiscrimination, pickAdversaryModel, CHALLENGE_QUESTIONS, loadOutcomes, renderOutcomes, statsForKey, selectAutoCost, recordProvisional, finalizeOutcome, COST_LADDER, splitScenarios, budgetPlan, selectWinner, proseScopeOk, renderProseDiff, readScenarioIds, DEFAULT_MAX_JUDGE_RUNS, collectDeliveryFacts, planDeliveryCheck, renderDeliveryBrief, classifyDelivery, isUsablePlaneResult, renderDeliveryReview, scanSkillsLayout, parseInitFacts, verifyRegistration, findNonRegistrableSkillDirs, registrationExitCode, renderRegistrationReport, } 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, buildParityMatrix, TARGET_CAPABILITIES, TARGET_SHORT_LABELS, WORKFLOW_NAMES, importEcc, recordPattern, resolveLearningBackend, storeStats, consolidateSessions, pruneNoisePatterns, lessonDeltaReport, removePatternsByIds, snapshotStore, recallHybrid, teachGuard, mirrorPatternsToVector, mirrorEntriesToVector, patternVectorEntry, readMemoryLearningConfig, promotePatterns, quarantineExpiryCandidates, pruneQuarantinePatterns, clearAgentdbQuarantine, 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, generateSigningKeypair, evaluateGuard, resolveRules, auditRecord, guardExitCode, DEFAULT_RULES, decideProvenance, isInsideTree, signManifest, verifyManifest, listSignablePackFiles, assertKeyOutsideTree, decidePublishGate, collectPackageFacts, planReleaseGates, selectAffectedPackages, classifyGateExecutions, buildFailureIssue, buildReleaseNotes, releaseTagName, firstOutputLine, formatPublishError, 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, planDiscriminationCheck, classifyDiscrimination, pickAdversaryModel, CHALLENGE_QUESTIONS, loadOutcomes, renderOutcomes, statsForKey, selectAutoCost, recordProvisional, finalizeOutcome, COST_LADDER, splitScenarios, budgetPlan, selectWinner, proseScopeOk, renderProseDiff, readScenarioIds, DEFAULT_MAX_JUDGE_RUNS, collectDeliveryFacts, planDeliveryCheck, renderDeliveryBrief, classifyDelivery, isUsablePlaneResult, renderDeliveryReview, scanSkillsLayout, parseInitFacts, verifyRegistration, buildContentProbePrompt, classifyContentProbe, renderContentProbe, findNonRegistrableSkillDirs, registrationExitCode, renderRegistrationReport, } 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
|
|
@@ -4047,7 +4047,52 @@ function cmdClaimCheck(options, _optionLists, flags, cwd, write) {
|
|
|
4047
4047
|
break;
|
|
4048
4048
|
paths.push(p);
|
|
4049
4049
|
}
|
|
4050
|
-
|
|
4050
|
+
// A DIRECTORY argument used to hit EISDIR, be counted as "skipped", and still exit 0 — the gate
|
|
4051
|
+
// reported PASS having scanned nothing (MEASURED: `dz claim-check docs` said "1 skipped" and exited
|
|
4052
|
+
// 0 while `dz claim-check docs/*.md` found 7). Expand a directory into its markdown instead.
|
|
4053
|
+
const expandDir = (dir, depth = 0) => {
|
|
4054
|
+
if (depth > 6)
|
|
4055
|
+
return [];
|
|
4056
|
+
let entries;
|
|
4057
|
+
try {
|
|
4058
|
+
entries = readdirSync(dir);
|
|
4059
|
+
}
|
|
4060
|
+
catch {
|
|
4061
|
+
return [];
|
|
4062
|
+
}
|
|
4063
|
+
const out = [];
|
|
4064
|
+
for (const name of entries) {
|
|
4065
|
+
if (name.startsWith('.') || name === 'node_modules' || name === 'dist')
|
|
4066
|
+
continue;
|
|
4067
|
+
const full = join(dir, name);
|
|
4068
|
+
try {
|
|
4069
|
+
const st = statSync(full);
|
|
4070
|
+
if (st.isDirectory())
|
|
4071
|
+
out.push(...expandDir(full, depth + 1));
|
|
4072
|
+
else if (st.isFile() && name.toLowerCase().endsWith('.md'))
|
|
4073
|
+
out.push(full);
|
|
4074
|
+
}
|
|
4075
|
+
catch {
|
|
4076
|
+
/* unreadable entry — nothing to scan */
|
|
4077
|
+
}
|
|
4078
|
+
}
|
|
4079
|
+
return out;
|
|
4080
|
+
};
|
|
4081
|
+
const requested = paths.length > 0 ? paths.map((p) => resolve(root, p)) : defaultClaimScanSet(root);
|
|
4082
|
+
const scanSet = [];
|
|
4083
|
+
for (const p of requested) {
|
|
4084
|
+
let isDir = false;
|
|
4085
|
+
try {
|
|
4086
|
+
isDir = statSync(p).isDirectory();
|
|
4087
|
+
}
|
|
4088
|
+
catch {
|
|
4089
|
+
/* missing path stays in the set so it is reported as skipped, never silently dropped */
|
|
4090
|
+
}
|
|
4091
|
+
if (isDir)
|
|
4092
|
+
scanSet.push(...expandDir(p));
|
|
4093
|
+
else
|
|
4094
|
+
scanSet.push(p);
|
|
4095
|
+
}
|
|
4051
4096
|
const findings = [];
|
|
4052
4097
|
const scanned = [];
|
|
4053
4098
|
for (const abs of scanSet) {
|
|
@@ -4088,6 +4133,13 @@ function cmdClaimCheck(options, _optionLists, flags, cwd, write) {
|
|
|
4088
4133
|
write(`\n ${scanned.length} file(s) in scan set, ${skipped.length} skipped.`);
|
|
4089
4134
|
for (const s of skipped)
|
|
4090
4135
|
write(` skipped ${s.path} (${s.reason})`);
|
|
4136
|
+
// A gate that examined NOTHING must never report a pass. Previously an unreadable argument was
|
|
4137
|
+
// counted as "skipped" and the command still exited 0 — a clean bill of health over zero evidence.
|
|
4138
|
+
const readCount = scanned.filter((s) => s.status === 'scanned').length;
|
|
4139
|
+
if (readCount === 0 && scanned.length > 0) {
|
|
4140
|
+
write(' NOTHING was read — this is not a pass. Check the paths above.');
|
|
4141
|
+
return 1;
|
|
4142
|
+
}
|
|
4091
4143
|
// Additive per-group rollup (ADR-001 C4): the widened default set can produce thousands of
|
|
4092
4144
|
// findings; this summarizes WHERE they land (root README, packages/…, features/<slug>,
|
|
4093
4145
|
// docs/<subdir>) WITHOUT hiding any — display-only, so `--json` shape and exit codes are
|
|
@@ -5392,6 +5444,44 @@ function probeInitStream(projectDir, timeoutMs) {
|
|
|
5392
5444
|
});
|
|
5393
5445
|
});
|
|
5394
5446
|
}
|
|
5447
|
+
/** Run a full `claude -p` turn and return its text. Unlike the init probe this NEEDS the answer. */
|
|
5448
|
+
function probeContent(projectDir, prompt, timeoutMs) {
|
|
5449
|
+
return new Promise((resolveProbe) => {
|
|
5450
|
+
const env = { ...process.env };
|
|
5451
|
+
for (const key of PROBE_SCRUB_ENV)
|
|
5452
|
+
delete env[key];
|
|
5453
|
+
env.CLAUDE_PROJECT_DIR = projectDir;
|
|
5454
|
+
let out = '';
|
|
5455
|
+
let err = '';
|
|
5456
|
+
let settled = false;
|
|
5457
|
+
let child;
|
|
5458
|
+
const finish = (error) => {
|
|
5459
|
+
if (settled)
|
|
5460
|
+
return;
|
|
5461
|
+
settled = true;
|
|
5462
|
+
clearTimeout(timer);
|
|
5463
|
+
try {
|
|
5464
|
+
child?.kill('SIGTERM');
|
|
5465
|
+
}
|
|
5466
|
+
catch {
|
|
5467
|
+
/* already gone */
|
|
5468
|
+
}
|
|
5469
|
+
resolveProbe({ text: out, error });
|
|
5470
|
+
};
|
|
5471
|
+
const timer = setTimeout(() => finish(`content probe timed out after ${Math.round(timeoutMs / 1000)}s`), timeoutMs);
|
|
5472
|
+
try {
|
|
5473
|
+
child = spawn('claude', ['-p', prompt, '--output-format', 'text'], { cwd: projectDir, env, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
5474
|
+
}
|
|
5475
|
+
catch (error) {
|
|
5476
|
+
finish(`cannot run \`claude\`: ${error instanceof Error ? error.message : String(error)}`);
|
|
5477
|
+
return;
|
|
5478
|
+
}
|
|
5479
|
+
child.stdout?.on('data', (c) => { out += c.toString(); });
|
|
5480
|
+
child.stderr?.on('data', (c) => { err += c.toString(); });
|
|
5481
|
+
child.on('error', (error) => finish(`cannot run \`claude\`: ${error.message}`));
|
|
5482
|
+
child.on('close', () => finish(out.trim() === '' ? err.trim() || 'the content probe produced no output' : null));
|
|
5483
|
+
});
|
|
5484
|
+
}
|
|
5395
5485
|
/**
|
|
5396
5486
|
* `dz skills-verify` — does this project's `.claude/skills/` actually REGISTER? (feature
|
|
5397
5487
|
* skills-verify, ADR-001.) L1 static scan is instant and CI-safe; the live layer reads the
|
|
@@ -5403,13 +5493,15 @@ async function cmdSkillsVerify(options, flags, cwd, write) {
|
|
|
5403
5493
|
write('dz skills-verify [--dir <project>] [--expect a,b] [--static] [--strict] [--timeout <s>] [--json]');
|
|
5404
5494
|
write(' Verifies that a project\'s .claude/skills/ actually register in Claude Code.');
|
|
5405
5495
|
write(' --static layout scan only (no Claude session, CI-safe): flags dirs that can never register');
|
|
5496
|
+
write(' --live-content ADVISORY extra turn: ask a live model to name the skills and quote one, proving');
|
|
5497
|
+
write(' the CONTENT is usable — registration is not usability. Never changes the exit code.');
|
|
5406
5498
|
write(' default also starts a real session and reads the authoritative system/init listing');
|
|
5407
5499
|
write(' exit: 0 pass · 1 fail · 2 inconclusive (--strict makes inconclusive exit 1)');
|
|
5408
5500
|
return 0;
|
|
5409
5501
|
}
|
|
5410
|
-
const allowedFlags = new Set(['json', 'help', 'static', 'strict']);
|
|
5502
|
+
const allowedFlags = new Set(['json', 'help', 'static', 'strict', 'live-content']);
|
|
5411
5503
|
const allowedOptions = new Set(['dir', 'expect', 'timeout']);
|
|
5412
|
-
const usage = ' allowed: --dir <project>, --expect a,b, --timeout <s>, --static, --strict, --json';
|
|
5504
|
+
const usage = ' allowed: --dir <project>, --expect a,b, --timeout <s>, --static, --strict, --live-content, --json';
|
|
5413
5505
|
if (options.has('_positional_0')) {
|
|
5414
5506
|
const message = `unexpected argument "${options.get('_positional_0')}"`;
|
|
5415
5507
|
write(json ? JSON.stringify({ error: message, exitCode: 1 }) : `dz skills-verify: ${message}\n${usage}`);
|
|
@@ -5483,10 +5575,20 @@ async function cmdSkillsVerify(options, flags, cwd, write) {
|
|
|
5483
5575
|
...(options.has('expect') ? { expected } : {}),
|
|
5484
5576
|
}, { resolvePath: canonical });
|
|
5485
5577
|
const exitCode = registrationExitCode(result.verdict, flags.has('strict'));
|
|
5578
|
+
// ADVISORY layer: registration is not usability. Costs a real model turn, so it is opt-in, and it
|
|
5579
|
+
// can never change the exit code — a model-mediated signal must not gate anything.
|
|
5580
|
+
let content = null;
|
|
5581
|
+
if (flags.has('live-content') && result.verdict === 'pass' && expected.length > 0) {
|
|
5582
|
+
const probe = await probeContent(projectDir, buildContentProbePrompt(expected), timeoutMs);
|
|
5583
|
+
content = classifyContentProbe(probe.error === null ? probe.text : '', expected.slice(0, 6));
|
|
5584
|
+
}
|
|
5486
5585
|
if (json)
|
|
5487
|
-
write(JSON.stringify({ mode: 'live', ...result, skillsRoot: scan.skillsRoot, exitCode }, null, 2));
|
|
5488
|
-
else
|
|
5586
|
+
write(JSON.stringify({ mode: 'live', ...result, ...(content ? { contentProbe: content } : {}), skillsRoot: scan.skillsRoot, exitCode }, null, 2));
|
|
5587
|
+
else {
|
|
5489
5588
|
write(renderRegistrationReport(result, scan));
|
|
5589
|
+
if (content)
|
|
5590
|
+
write(renderContentProbe(content));
|
|
5591
|
+
}
|
|
5490
5592
|
return exitCode;
|
|
5491
5593
|
}
|
|
5492
5594
|
function cmdDeliveryCheck(options, flags, cwd, write) {
|