reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "fixture",
|
||||
"plugins": [
|
||||
{ "name": "sample", "source": { "source": "local", "path": "./plugins/sample" } }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "fixture",
|
||||
"owner": { "name": "Fixture" },
|
||||
"description": "Local plugin fixture.",
|
||||
"version": "1.0.0",
|
||||
"plugins": [
|
||||
{ "name": "sample", "description": "Sample local plugin.", "version": "1.2.3", "source": "./plugins/sample" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "fixture",
|
||||
"owner": { "name": "Fixture" },
|
||||
"metadata": { "description": "Local plugin fixture.", "version": "1.0.0" },
|
||||
"plugins": [
|
||||
{ "name": "sample", "description": "Sample local plugin.", "version": "1.2.3", "source": "./plugins/sample" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "sample",
|
||||
"version": "1.2.3",
|
||||
"description": "Sample local plugin."
|
||||
}
|
||||
6
vendor-new/win-dev-skills/scripts/tests/fixtures/local-plugin/plugins/sample/plugin.json
vendored
Normal file
6
vendor-new/win-dev-skills/scripts/tests/fixtures/local-plugin/plugins/sample/plugin.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
||||
"name": "sample",
|
||||
"version": "1.2.3",
|
||||
"description": "Sample local plugin."
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
name: sample-skill
|
||||
description: Sample skill used to test the catalog tooling. Use when testing CI. Not for real work.
|
||||
---
|
||||
|
||||
# Sample skill
|
||||
|
||||
This skill exists only to exercise the catalog check and lint.
|
||||
@@ -0,0 +1,47 @@
|
||||
// Proves the catalog tooling handles a plugin stored in this repo, using the
|
||||
// fixture in scripts/tests/fixtures/local-plugin. Needs `npm ci --prefix scripts/vally`.
|
||||
import { spawnSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
const fixture = path.join(here, "fixtures", "local-plugin");
|
||||
const checkScript = path.join(here, "..", "check-catalogs.mjs");
|
||||
const lintScript = path.join(here, "..", "vally", "lint-skills.mjs");
|
||||
|
||||
let failed = 0;
|
||||
function expect(label, script, root, wantExit, wantText) {
|
||||
const r = spawnSync(process.execPath, [script], { env: { ...process.env, CATALOG_ROOT: root }, encoding: "utf8" });
|
||||
const out = `${r.stdout}${r.stderr}`;
|
||||
const ok = r.status === wantExit && (!wantText || out.includes(wantText));
|
||||
console.log(`${ok ? "pass" : "FAIL"} ${label} (exit ${r.status})`);
|
||||
if (!ok) {
|
||||
failed++;
|
||||
console.log(out);
|
||||
}
|
||||
}
|
||||
|
||||
function mutatedCopy(file, from, to) {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "catalog-fixture-"));
|
||||
fs.cpSync(fixture, dir, { recursive: true });
|
||||
const target = path.join(dir, file);
|
||||
fs.writeFileSync(target, fs.readFileSync(target, "utf8").replace(from, to));
|
||||
return dir;
|
||||
}
|
||||
|
||||
expect("catalog check accepts a local plugin", checkScript, fixture, 0, "ok codex sample@1.2.3 ← ./plugins/sample (plugins/sample/plugin.json)");
|
||||
expect("vally lints the local plugin", lintScript, fixture, 0, "sample-skill");
|
||||
|
||||
const badVersion = mutatedCopy("plugins/sample/.claude-plugin/plugin.json", '"1.2.3"', '"1.2.4"');
|
||||
expect("catalog check rejects a manifest version mismatch", checkScript, badVersion, 1, "has version '1.2.4'");
|
||||
|
||||
const missingCodex = mutatedCopy(".agents/plugins/marketplace.json", '"name": "sample"', '"name": "other"');
|
||||
expect("catalog check rejects catalogs that disagree", checkScript, missingCodex, 1, "is missing from");
|
||||
|
||||
const badSkill = mutatedCopy("plugins/sample/skills/sample-skill/SKILL.md", "name: sample-skill", "name: Not A Valid Name");
|
||||
expect("vally rejects an invalid skill", lintScript, badSkill, 1);
|
||||
|
||||
for (const dir of [badVersion, missingCodex, badSkill]) fs.rmSync(dir, { recursive: true, force: true });
|
||||
process.exitCode = failed ? 1 : 0;
|
||||
@@ -0,0 +1,134 @@
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const repositoryRoot = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"..",
|
||||
"..",
|
||||
);
|
||||
const validator = path.join(
|
||||
repositoryRoot,
|
||||
"plugins",
|
||||
"windbg",
|
||||
"skills",
|
||||
"windbg-diagnostic-method",
|
||||
"scripts",
|
||||
"validate-diagnosis-output.ps1",
|
||||
);
|
||||
const temporaryDirectory = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), "diagnosis-validator-"),
|
||||
);
|
||||
|
||||
const validReport = `## Analysis
|
||||
Observed evidence.
|
||||
|
||||
## Root Cause
|
||||
Candidate cause.
|
||||
|
||||
## Fix
|
||||
Candidate fix.
|
||||
|
||||
## Reasoning Chain
|
||||
OBSERVE: evidence.
|
||||
|
||||
## Alternatives Considered
|
||||
- Alternative one
|
||||
- Alternative two
|
||||
|
||||
## Trigger Verification
|
||||
| Evidence | Status |
|
||||
|---|---|
|
||||
| Trigger | VERIFIED |
|
||||
|
||||
## Mermaid
|
||||
\`\`\`mermaid
|
||||
sequenceDiagram
|
||||
A->>B: request
|
||||
B-->>A: response
|
||||
\`\`\`
|
||||
|
||||
## Contrarian Verdict
|
||||
ACCEPTED
|
||||
|
||||
contrarian_loopback: false
|
||||
|
||||
## JSON Output Contract Summary
|
||||
\`\`\`json
|
||||
{"diagnosis_status":"final","routing_path":"validate","root_cause":"cause","confidence":0.8,"fix_confidence":0.7,"fix_code_path_coverage":"symbol-or-disassembly","contrarian_review":"ACCEPTED","contrarian_loopback":false}
|
||||
\`\`\`
|
||||
`;
|
||||
|
||||
const invalidReport = `## Analysis
|
||||
The words OBSERVE and VERIFIED appear outside their required sections.
|
||||
|
||||
## Root Cause
|
||||
Candidate cause.
|
||||
|
||||
## Fix
|
||||
Candidate fix.
|
||||
|
||||
## Reasoning Chain
|
||||
No phase marker appears here.
|
||||
|
||||
## Alternatives Considered
|
||||
- Only one alternative
|
||||
|
||||
## Trigger Verification
|
||||
No table appears here.
|
||||
|
||||
## Mermaid
|
||||
\`\`\`mermaid
|
||||
sequenceDiagram
|
||||
participant A
|
||||
\`\`\`
|
||||
|
||||
## Contrarian Verdict
|
||||
ACCEPTED
|
||||
|
||||
contrarian_loopback: 1
|
||||
|
||||
## JSON Output Contract Summary
|
||||
\`\`\`json
|
||||
{"diagnosis_status":"final","routing_path":"validate","root_cause":"cause","confidence":0.8,"fix_confidence":0.7,"fix_code_path_coverage":"symbol-or-disassembly","contrarian_review":"ACCEPTED","contrarian_loopback":1}
|
||||
\`\`\`
|
||||
`;
|
||||
|
||||
function runValidator(file) {
|
||||
return spawnSync(
|
||||
"pwsh",
|
||||
["-NoProfile", "-File", validator, "-Path", file],
|
||||
{ encoding: "utf8" },
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const validPath = path.join(temporaryDirectory, "valid.md");
|
||||
const invalidPath = path.join(temporaryDirectory, "invalid.md");
|
||||
fs.writeFileSync(validPath, validReport);
|
||||
fs.writeFileSync(invalidPath, invalidReport);
|
||||
|
||||
const validResult = runValidator(validPath);
|
||||
assert.equal(validResult.status, 0, validResult.stderr || validResult.stdout);
|
||||
const validOutput = JSON.parse(validResult.stdout);
|
||||
assert.equal(validOutput.Pass, true);
|
||||
assert.equal(validOutput.Checks.MermaidDiagram, true);
|
||||
|
||||
const invalidResult = runValidator(invalidPath);
|
||||
assert.equal(invalidResult.status, 1, invalidResult.stderr);
|
||||
const invalidOutput = JSON.parse(invalidResult.stdout);
|
||||
assert.equal(invalidOutput.Pass, false);
|
||||
assert.equal(invalidOutput.Checks.ReasoningChain, false);
|
||||
assert.equal(invalidOutput.Checks.Alternatives, false);
|
||||
assert.equal(invalidOutput.Checks.TriggerVerification, false);
|
||||
assert.equal(invalidOutput.Checks.MermaidDiagram, false);
|
||||
assert.equal(invalidOutput.Checks.ContrarianVerdict, false);
|
||||
assert.equal(invalidOutput.Checks.JsonSummary, false);
|
||||
|
||||
console.log("Diagnosis validator accepts valid reports and rejects malformed sections.");
|
||||
} finally {
|
||||
fs.rmSync(temporaryDirectory, { recursive: true, force: true });
|
||||
}
|
||||
Reference in New Issue
Block a user