@dzhechkov/harness-cli 0.3.242 → 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 +7 -7
- package/README.md +16 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +55 -5
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
- package/sbom.json +6 -6
- package/src/cli.ts +55 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dzhechkov/harness-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.243",
|
|
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",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"@dzhechkov/skills-reverse-engineering": "^0.1.0",
|
|
58
58
|
"@dzhechkov/skills-presentation-storyteller": "^0.1.0",
|
|
59
59
|
"@dzhechkov/skills-website-cloner": "^0.1.0",
|
|
60
|
-
"@dzhechkov/harness-core": "0.3.
|
|
60
|
+
"@dzhechkov/harness-core": "0.3.134"
|
|
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": "
|
|
28
|
+
"content": "5f4e31653970ef6970b6d87c862a1c6ac1f2da92c3f4f913b4be4155284db1f4"
|
|
29
29
|
}
|
|
30
30
|
]
|
|
31
31
|
},
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
"hashes": [
|
|
106
106
|
{
|
|
107
107
|
"alg": "SHA-256",
|
|
108
|
-
"content": "
|
|
108
|
+
"content": "2bb00d2f42334250ee57b4d0f88d92968cbe3f010ec8d75bb748c0ee6f8de0b8"
|
|
109
109
|
}
|
|
110
110
|
]
|
|
111
111
|
},
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"hashes": [
|
|
116
116
|
{
|
|
117
117
|
"alg": "SHA-256",
|
|
118
|
-
"content": "
|
|
118
|
+
"content": "d959d7623b68286e440e04f90ee3ae7af0166c1fe506f2cf7e6e9e317439409d"
|
|
119
119
|
}
|
|
120
120
|
]
|
|
121
121
|
},
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
"hashes": [
|
|
126
126
|
{
|
|
127
127
|
"alg": "SHA-256",
|
|
128
|
-
"content": "
|
|
128
|
+
"content": "68e0bbf621169f7bae350a5b723f9425969ab12b6f7dd98ce26bb06d32c77c92"
|
|
129
129
|
}
|
|
130
130
|
]
|
|
131
131
|
},
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
"hashes": [
|
|
186
186
|
{
|
|
187
187
|
"alg": "SHA-256",
|
|
188
|
-
"content": "
|
|
188
|
+
"content": "e26250419d70ec922def52f25f63000b658eec8203b29a2135edad1be98f8d5d"
|
|
189
189
|
}
|
|
190
190
|
]
|
|
191
191
|
},
|
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
"hashes": [
|
|
206
206
|
{
|
|
207
207
|
"alg": "SHA-256",
|
|
208
|
-
"content": "
|
|
208
|
+
"content": "14a3db7155fcc858272439a55bc7a43a09c7f6fd7caa302e5a13c153fe1a6d16"
|
|
209
209
|
}
|
|
210
210
|
]
|
|
211
211
|
},
|
package/src/cli.ts
CHANGED
|
@@ -195,6 +195,9 @@ import {
|
|
|
195
195
|
scanSkillsLayout,
|
|
196
196
|
parseInitFacts,
|
|
197
197
|
verifyRegistration,
|
|
198
|
+
buildContentProbePrompt,
|
|
199
|
+
classifyContentProbe,
|
|
200
|
+
renderContentProbe,
|
|
198
201
|
findNonRegistrableSkillDirs,
|
|
199
202
|
registrationExitCode,
|
|
200
203
|
renderRegistrationReport,
|
|
@@ -5693,6 +5696,41 @@ function probeInitStream(projectDir: string, timeoutMs: number): Promise<{ strea
|
|
|
5693
5696
|
});
|
|
5694
5697
|
}
|
|
5695
5698
|
|
|
5699
|
+
/** Run a full `claude -p` turn and return its text. Unlike the init probe this NEEDS the answer. */
|
|
5700
|
+
function probeContent(projectDir: string, prompt: string, timeoutMs: number): Promise<{ text: string; error: string | null }> {
|
|
5701
|
+
return new Promise((resolveProbe) => {
|
|
5702
|
+
const env: NodeJS.ProcessEnv = { ...process.env };
|
|
5703
|
+
for (const key of PROBE_SCRUB_ENV) delete env[key];
|
|
5704
|
+
env.CLAUDE_PROJECT_DIR = projectDir;
|
|
5705
|
+
let out = '';
|
|
5706
|
+
let err = '';
|
|
5707
|
+
let settled = false;
|
|
5708
|
+
let child: ReturnType<typeof spawn>;
|
|
5709
|
+
const finish = (error: string | null): void => {
|
|
5710
|
+
if (settled) return;
|
|
5711
|
+
settled = true;
|
|
5712
|
+
clearTimeout(timer);
|
|
5713
|
+
try {
|
|
5714
|
+
child?.kill('SIGTERM');
|
|
5715
|
+
} catch {
|
|
5716
|
+
/* already gone */
|
|
5717
|
+
}
|
|
5718
|
+
resolveProbe({ text: out, error });
|
|
5719
|
+
};
|
|
5720
|
+
const timer = setTimeout(() => finish(`content probe timed out after ${Math.round(timeoutMs / 1000)}s`), timeoutMs);
|
|
5721
|
+
try {
|
|
5722
|
+
child = spawn('claude', ['-p', prompt, '--output-format', 'text'], { cwd: projectDir, env, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
5723
|
+
} catch (error) {
|
|
5724
|
+
finish(`cannot run \`claude\`: ${error instanceof Error ? error.message : String(error)}`);
|
|
5725
|
+
return;
|
|
5726
|
+
}
|
|
5727
|
+
child.stdout?.on('data', (c: Buffer) => { out += c.toString(); });
|
|
5728
|
+
child.stderr?.on('data', (c: Buffer) => { err += c.toString(); });
|
|
5729
|
+
child.on('error', (error: Error) => finish(`cannot run \`claude\`: ${error.message}`));
|
|
5730
|
+
child.on('close', () => finish(out.trim() === '' ? err.trim() || 'the content probe produced no output' : null));
|
|
5731
|
+
});
|
|
5732
|
+
}
|
|
5733
|
+
|
|
5696
5734
|
/**
|
|
5697
5735
|
* `dz skills-verify` — does this project's `.claude/skills/` actually REGISTER? (feature
|
|
5698
5736
|
* skills-verify, ADR-001.) L1 static scan is instant and CI-safe; the live layer reads the
|
|
@@ -5709,14 +5747,16 @@ async function cmdSkillsVerify(
|
|
|
5709
5747
|
write('dz skills-verify [--dir <project>] [--expect a,b] [--static] [--strict] [--timeout <s>] [--json]');
|
|
5710
5748
|
write(' Verifies that a project\'s .claude/skills/ actually register in Claude Code.');
|
|
5711
5749
|
write(' --static layout scan only (no Claude session, CI-safe): flags dirs that can never register');
|
|
5750
|
+
write(' --live-content ADVISORY extra turn: ask a live model to name the skills and quote one, proving');
|
|
5751
|
+
write(' the CONTENT is usable — registration is not usability. Never changes the exit code.');
|
|
5712
5752
|
write(' default also starts a real session and reads the authoritative system/init listing');
|
|
5713
5753
|
write(' exit: 0 pass · 1 fail · 2 inconclusive (--strict makes inconclusive exit 1)');
|
|
5714
5754
|
return 0;
|
|
5715
5755
|
}
|
|
5716
5756
|
|
|
5717
|
-
const allowedFlags = new Set(['json', 'help', 'static', 'strict']);
|
|
5757
|
+
const allowedFlags = new Set(['json', 'help', 'static', 'strict', 'live-content']);
|
|
5718
5758
|
const allowedOptions = new Set(['dir', 'expect', 'timeout']);
|
|
5719
|
-
const usage = ' allowed: --dir <project>, --expect a,b, --timeout <s>, --static, --strict, --json';
|
|
5759
|
+
const usage = ' allowed: --dir <project>, --expect a,b, --timeout <s>, --static, --strict, --live-content, --json';
|
|
5720
5760
|
if (options.has('_positional_0')) {
|
|
5721
5761
|
const message = `unexpected argument "${options.get('_positional_0')}"`;
|
|
5722
5762
|
write(json ? JSON.stringify({ error: message, exitCode: 1 }) : `dz skills-verify: ${message}\n${usage}`);
|
|
@@ -5796,8 +5836,19 @@ async function cmdSkillsVerify(
|
|
|
5796
5836
|
);
|
|
5797
5837
|
const exitCode = registrationExitCode(result.verdict, flags.has('strict'));
|
|
5798
5838
|
|
|
5799
|
-
|
|
5800
|
-
|
|
5839
|
+
// ADVISORY layer: registration is not usability. Costs a real model turn, so it is opt-in, and it
|
|
5840
|
+
// can never change the exit code — a model-mediated signal must not gate anything.
|
|
5841
|
+
let content: ReturnType<typeof classifyContentProbe> | null = null;
|
|
5842
|
+
if (flags.has('live-content') && result.verdict === 'pass' && expected.length > 0) {
|
|
5843
|
+
const probe = await probeContent(projectDir, buildContentProbePrompt(expected), timeoutMs);
|
|
5844
|
+
content = classifyContentProbe(probe.error === null ? probe.text : '', expected.slice(0, 6));
|
|
5845
|
+
}
|
|
5846
|
+
|
|
5847
|
+
if (json) write(JSON.stringify({ mode: 'live', ...result, ...(content ? { contentProbe: content } : {}), skillsRoot: scan.skillsRoot, exitCode }, null, 2));
|
|
5848
|
+
else {
|
|
5849
|
+
write(renderRegistrationReport(result, scan));
|
|
5850
|
+
if (content) write(renderContentProbe(content));
|
|
5851
|
+
}
|
|
5801
5852
|
return exitCode;
|
|
5802
5853
|
}
|
|
5803
5854
|
|