vigiles 29.1.0 → 30.0.0
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/dist/adapter-conformance.d.ts +1 -1
- package/dist/adapter-conformance.js +106 -25
- package/dist/adapter-registry.d.ts +61 -14
- package/dist/adapter-registry.js +78 -10
- package/dist/adapter.d.ts +23 -2
- package/dist/adapter.js +13 -1
- package/dist/adapters/claude-code/adapter.d.ts +32 -2
- package/dist/adapters/claude-code/adapter.js +44 -23
- package/dist/adapters/claude-code/dialect.js +87 -21
- package/dist/adapters/claude-code/hook-protocol.js +16 -0
- package/dist/adapters/claude-code/instruction-chain.d.ts +25 -0
- package/dist/adapters/claude-code/instruction-chain.js +626 -0
- package/dist/adapters/claude-code/layout.d.ts +2 -2
- package/dist/adapters/claude-code/layout.js +42 -8
- package/dist/adapters/claude-code/model-access.d.ts +41 -0
- package/dist/adapters/claude-code/model-access.js +46 -0
- package/dist/adapters/claude-code/skill-reachability.d.ts +125 -0
- package/dist/adapters/claude-code/skill-reachability.js +111 -0
- package/dist/adapters/codex/adapter.d.ts +39 -2
- package/dist/adapters/codex/adapter.js +29 -29
- package/dist/adapters/codex/dialect.js +11 -6
- package/dist/adapters/codex/eval.d.ts +10 -0
- package/dist/adapters/codex/eval.js +48 -1
- package/dist/adapters/codex/hook-protocol.d.ts +2 -1
- package/dist/adapters/codex/hook-protocol.js +10 -0
- package/dist/adapters/codex/instruction-chain.d.ts +40 -0
- package/dist/adapters/codex/instruction-chain.js +105 -0
- package/dist/adapters/codex/layout.d.ts +1 -1
- package/dist/adapters/codex/layout.js +41 -14
- package/dist/adapters/opencode/adapter.d.ts +33 -2
- package/dist/adapters/opencode/adapter.js +36 -36
- package/dist/adapters/opencode/dialect.js +2 -2
- package/dist/adapters/opencode/instruction-chain.d.ts +37 -0
- package/dist/adapters/opencode/instruction-chain.js +70 -0
- package/dist/adapters/opencode/layout.d.ts +19 -0
- package/dist/adapters/opencode/layout.js +34 -15
- package/dist/adoptability.d.ts +31 -1
- package/dist/adoptability.js +57 -0
- package/dist/cli-main.js +180 -102
- package/dist/core/adapter.d.ts +213 -61
- package/dist/core/compile.d.ts +2 -2
- package/dist/core/compile.js +57 -46
- package/dist/core/compose.d.ts +5 -3
- package/dist/core/compose.js +5 -3
- package/dist/core/config-schema.d.ts +14 -2
- package/dist/core/config-schema.js +20 -7
- package/dist/core/dialect.d.ts +54 -12
- package/dist/core/dialect.js +56 -0
- package/dist/core/eval-driver.d.ts +194 -0
- package/dist/core/eval-driver.js +3 -0
- package/dist/core/frontmatter-read.d.ts +10 -0
- package/dist/core/frontmatter-read.js +30 -3
- package/dist/core/hook-program.d.ts +27 -2
- package/dist/core/hook-program.js +29 -24
- package/dist/core/hook-protocol.d.ts +54 -0
- package/dist/core/install-reader.d.ts +18 -0
- package/dist/core/install-reader.js +88 -0
- package/dist/core/instruction-chain.d.ts +444 -0
- package/dist/core/instruction-chain.js +292 -0
- package/dist/core/instruction-weight.d.ts +96 -14
- package/dist/core/instruction-weight.js +65 -30
- package/dist/core/layout.d.ts +220 -33
- package/dist/core/layout.js +115 -1
- package/dist/core/lethal-trifecta.d.ts +12 -7
- package/dist/core/lethal-trifecta.js +13 -13
- package/dist/core/live-driver.d.ts +137 -0
- package/dist/core/live-driver.js +14 -0
- package/dist/core/markdown.d.ts +23 -0
- package/dist/core/markdown.js +77 -28
- package/dist/core/orphans.js +9 -7
- package/dist/core/settings-codec.d.ts +17 -0
- package/dist/core/settings-codec.js +56 -0
- package/dist/core/surface-discovery.d.ts +2 -2
- package/dist/core/surface-discovery.js +24 -12
- package/dist/core/surface-scopes.d.ts +26 -6
- package/dist/core/surface-scopes.js +52 -11
- package/dist/core/validate.js +16 -3
- package/dist/eval.d.ts +16 -108
- package/dist/eval.js +34 -1
- package/dist/harness-test.d.ts +3 -63
- package/dist/hook-install.d.ts +12 -1
- package/dist/hook-install.js +12 -1
- package/dist/plugin-loader.d.ts +1 -1
- package/dist/plugin-loader.js +43 -36
- package/dist/scan-behavioral.d.ts +34 -25
- package/dist/scan-behavioral.js +122 -58
- package/dist/scan-core.js +37 -18
- package/dist/scan-files.d.ts +1 -1
- package/dist/scan-files.js +53 -33
- package/dist/scan-trigger-suggest.d.ts +0 -21
- package/dist/scan-trigger-suggest.js +0 -23
- package/dist/scan.d.ts +4 -4
- package/dist/scan.js +120 -84
- package/dist/skill-harness.d.ts +21 -5
- package/dist/skill-harness.js +29 -11
- package/dist/surface-discovery-fs.d.ts +2 -0
- package/dist/surface-discovery-fs.js +108 -6
- package/dist/test-coverage-files.js +24 -17
- package/dist/test-coverage.d.ts +9 -3
- package/dist/test-coverage.js +32 -22
- package/dist/verify-plugin-guards.js +1 -1
- package/package.json +1 -1
- package/dist/skill-reachability.d.ts +0 -68
- package/dist/skill-reachability.js +0 -205
- /package/dist/{dialect-drift.d.ts → adapters/claude-code/dialect-drift.d.ts} +0 -0
- /package/dist/{dialect-drift.js → adapters/claude-code/dialect-drift.js} +0 -0
package/dist/cli-main.js
CHANGED
|
@@ -36,8 +36,7 @@ const test_coverage_js_1 = require("./test-coverage.js");
|
|
|
36
36
|
const scan_js_1 = require("./scan.js");
|
|
37
37
|
const surface_discovery_js_1 = require("./core/surface-discovery.js");
|
|
38
38
|
const scan_trigger_suggest_js_1 = require("./scan-trigger-suggest.js");
|
|
39
|
-
const
|
|
40
|
-
const skill_reachability_js_1 = require("./skill-reachability.js");
|
|
39
|
+
const install_reader_js_1 = require("./core/install-reader.js");
|
|
41
40
|
const plugin_declaration_js_1 = require("./plugin-declaration.js");
|
|
42
41
|
const scan_behavioral_js_1 = require("./scan-behavioral.js");
|
|
43
42
|
const adapter_registry_js_1 = require("./adapter-registry.js");
|
|
@@ -1500,7 +1499,7 @@ async function runLint(restArgs, flags, excludes, config) {
|
|
|
1500
1499
|
// hand-edit of a compiled subagent slipped past `lint`. A hand-written agent
|
|
1501
1500
|
// (no hash header) stays a no-op, and require-instructions-spec is filename-
|
|
1502
1501
|
// scoped to CLAUDE/AGENTS so agents are never falsely flagged as spec-less.
|
|
1503
|
-
const files = findInstructionFiles(restArgs, excludes, adapter.layout.
|
|
1502
|
+
const files = findInstructionFiles(restArgs, excludes, adapter.layout.surfaces.agent ?? "");
|
|
1504
1503
|
// 1. Verify hashes and structure
|
|
1505
1504
|
if (!silent) {
|
|
1506
1505
|
if (files.length > 0) {
|
|
@@ -2822,7 +2821,26 @@ async function promptSetup() {
|
|
|
2822
2821
|
rl.close();
|
|
2823
2822
|
}
|
|
2824
2823
|
}
|
|
2825
|
-
|
|
2824
|
+
/**
|
|
2825
|
+
* The instruction filenames `init` probes for on disk — DERIVED from the
|
|
2826
|
+
* registered adapters, never listed.
|
|
2827
|
+
*
|
|
2828
|
+
* 🔴 THIS IS THE SAME DEFECT CLASS THE PORT REDESIGN REMOVES: a fact restated
|
|
2829
|
+
* by hand beside the registry that owns it. The literal read `["CLAUDE.md",
|
|
2830
|
+
* "AGENTS.md"]` and was correct only because the adapters happen to say that
|
|
2831
|
+
* today — it could not have noticed a third adapter, and it did not notice when
|
|
2832
|
+
* Claude Code's `instructionTargets` GAINED `AGENTS.md` (it was already there
|
|
2833
|
+
* by coincidence).
|
|
2834
|
+
*
|
|
2835
|
+
* `instructionTargets` rather than `layout.instructionFile`, because the
|
|
2836
|
+
* question here is "which files might exist and want a spec", which is every
|
|
2837
|
+
* name a registered harness READS — a repository with only an `AGENTS.md` is
|
|
2838
|
+
* one `init` must still see. Verified identical before and after over the
|
|
2839
|
+
* registry as it stands: union = `["CLAUDE.md", "AGENTS.md"]`.
|
|
2840
|
+
*/
|
|
2841
|
+
const KNOWN_INSTRUCTION_FILES = [
|
|
2842
|
+
...new Set(adapter_registry_js_1.ADAPTERS.flatMap((a) => a.dialect.instructionTargets)),
|
|
2843
|
+
];
|
|
2826
2844
|
const KNOWN_OTHER_CONFIGS = {
|
|
2827
2845
|
".cursorrules": "Cursor",
|
|
2828
2846
|
".github/copilot-instructions.md": "GitHub Copilot",
|
|
@@ -2903,13 +2921,20 @@ function detectProject() {
|
|
|
2903
2921
|
function determineTargets(detected, targetValue, harnesses) {
|
|
2904
2922
|
if (targetValue)
|
|
2905
2923
|
return [targetValue];
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2924
|
+
// 🔴 WHICH FILE A HARNESS COMPILES INTO IS THE LAYOUT'S ANSWER, not a pair of
|
|
2925
|
+
// name literals here. This read `harnesses.includes("claude") -> "CLAUDE.md"`
|
|
2926
|
+
// and `includes("codex") -> "AGENTS.md"` — the registry restated, in the one
|
|
2927
|
+
// place a third adapter would be forgotten. `getAdapter` is alias-aware, so
|
|
2928
|
+
// the short `"claude"` that `init` uses resolves exactly as it did.
|
|
2929
|
+
//
|
|
2930
|
+
// ITERATING THE REGISTRY, NOT `harnesses`, KEEPS THE ORDER FIXED: the old
|
|
2931
|
+
// code always produced CLAUDE.md before AGENTS.md whatever order the config
|
|
2932
|
+
// listed, and that order reaches the printed spec targets. Verified over all
|
|
2933
|
+
// five inputs (each harness alone, both orders, and none): identical.
|
|
2934
|
+
const selected = new Set(harnesses.map((h) => (0, adapter_registry_js_1.getAdapter)(h)?.name).filter((n) => n !== undefined));
|
|
2935
|
+
const targets = adapter_registry_js_1.ADAPTERS.filter((a) => selected.has(a.name)).map((a) => a.layout.instructionFile);
|
|
2911
2936
|
if (targets.length === 0)
|
|
2912
|
-
targets.push(
|
|
2937
|
+
targets.push(adapter_registry_js_1.defaultAdapter.layout.instructionFile);
|
|
2913
2938
|
// Any existing instruction file without a spec also gets one.
|
|
2914
2939
|
for (const f of detected.instructionFiles) {
|
|
2915
2940
|
if (!f.hasSpec && !targets.includes(f.path))
|
|
@@ -3257,6 +3282,7 @@ function ensureVigilesDevDep() {
|
|
|
3257
3282
|
* (`claude`). Everything else passes through trimmed + lowercased. */
|
|
3258
3283
|
function shortHarness(name) {
|
|
3259
3284
|
const n = name.trim().toLowerCase();
|
|
3285
|
+
// eslint-disable-next-line no-restricted-syntax -- COMPOSITION ROOT'S OWN UI, not a harness decision: `init` keys its install/plan path on a short form, and this is the one place the two spellings are related. Nothing about the harness is read from the answer.
|
|
3260
3286
|
return n === "claude-code" ? "claude" : n;
|
|
3261
3287
|
}
|
|
3262
3288
|
/** Which harnesses to set up, in precedence order: an explicit `--harness=`
|
|
@@ -3631,7 +3657,7 @@ function checkSubagentToolContracts(config, silent, adapter, scanRoot) {
|
|
|
3631
3657
|
const sev = (0, types_js_1.ruleSeverity)(config?.rules?.["subagent-tool-contract"]);
|
|
3632
3658
|
if (!sev)
|
|
3633
3659
|
return { issues: 0, errors: 0 };
|
|
3634
|
-
if (!adapter.
|
|
3660
|
+
if (!adapter.subagents) {
|
|
3635
3661
|
reportNotApplicable("Subagent tool-contract check", "subagents", adapter, silent);
|
|
3636
3662
|
return { issues: 0, errors: 0 };
|
|
3637
3663
|
}
|
|
@@ -3674,7 +3700,7 @@ function checkHookEvents(config, silent, adapter, scanRoot) {
|
|
|
3674
3700
|
const sev = (0, types_js_1.ruleSeverity)(config?.rules?.["hook-events"]);
|
|
3675
3701
|
if (!sev)
|
|
3676
3702
|
return { issues: 0, errors: 0 };
|
|
3677
|
-
if (!adapter.
|
|
3703
|
+
if (!adapter.shellHooks) {
|
|
3678
3704
|
reportNotApplicable("Hook-event check", "shell hooks", adapter, silent);
|
|
3679
3705
|
return { issues: 0, errors: 0 };
|
|
3680
3706
|
}
|
|
@@ -3705,7 +3731,7 @@ function checkFrontmatterSchema(config, silent, adapter, scanRoot) {
|
|
|
3705
3731
|
const sev = (0, types_js_1.ruleSeverity)(config?.rules?.["subagent-frontmatter"]);
|
|
3706
3732
|
if (!sev)
|
|
3707
3733
|
return { issues: 0, errors: 0 };
|
|
3708
|
-
if (!adapter.
|
|
3734
|
+
if (!adapter.subagents) {
|
|
3709
3735
|
reportNotApplicable("Subagent-frontmatter check", "subagents", adapter, silent);
|
|
3710
3736
|
return { issues: 0, errors: 0 };
|
|
3711
3737
|
}
|
|
@@ -3818,7 +3844,7 @@ function checkDisallowedTools(config, silent, adapter, scanRoot) {
|
|
|
3818
3844
|
const sev = (0, types_js_1.ruleSeverity)(config?.rules?.["disallowed-tools-contract"]);
|
|
3819
3845
|
if (!sev)
|
|
3820
3846
|
return { issues: 0, errors: 0 };
|
|
3821
|
-
if (!adapter.
|
|
3847
|
+
if (!adapter.subagents) {
|
|
3822
3848
|
reportNotApplicable("Disallowed-tools check", "subagents", adapter, silent);
|
|
3823
3849
|
return { issues: 0, errors: 0 };
|
|
3824
3850
|
}
|
|
@@ -4102,7 +4128,7 @@ function checkHookBlockIneffective(config, silent, adapter, scanRoot) {
|
|
|
4102
4128
|
const sev = (0, types_js_1.ruleSeverity)(config?.rules?.["hook-block-ineffective"]);
|
|
4103
4129
|
if (!sev)
|
|
4104
4130
|
return { issues: 0, errors: 0 };
|
|
4105
|
-
if (!adapter.
|
|
4131
|
+
if (!adapter.shellHooks) {
|
|
4106
4132
|
reportNotApplicable("Hook-block check", "shell hooks", adapter, silent);
|
|
4107
4133
|
return { issues: 0, errors: 0 };
|
|
4108
4134
|
}
|
|
@@ -4136,7 +4162,7 @@ function checkHookMatcher(config, silent, adapter, scanRoot) {
|
|
|
4136
4162
|
const sev = (0, types_js_1.ruleSeverity)(config?.rules?.["hook-matcher"]);
|
|
4137
4163
|
if (!sev)
|
|
4138
4164
|
return { issues: 0, errors: 0 };
|
|
4139
|
-
if (!adapter.
|
|
4165
|
+
if (!adapter.shellHooks) {
|
|
4140
4166
|
reportNotApplicable("Hook-matcher check", "shell hooks", adapter, silent);
|
|
4141
4167
|
return { issues: 0, errors: 0 };
|
|
4142
4168
|
}
|
|
@@ -4167,7 +4193,7 @@ function checkMcpHookTargets(config, silent, adapter, scanRoot) {
|
|
|
4167
4193
|
const sev = (0, types_js_1.ruleSeverity)(config?.rules?.["mcp-hook-target-resolves"]);
|
|
4168
4194
|
if (!sev)
|
|
4169
4195
|
return { issues: 0, errors: 0 };
|
|
4170
|
-
if (!adapter.
|
|
4196
|
+
if (!adapter.shellHooks) {
|
|
4171
4197
|
reportNotApplicable("MCP hook-target check", "shell hooks", adapter, silent);
|
|
4172
4198
|
return { issues: 0, errors: 0 };
|
|
4173
4199
|
}
|
|
@@ -4199,7 +4225,7 @@ function checkHookScriptExists(config, silent, adapter, scanRoot) {
|
|
|
4199
4225
|
const sev = (0, types_js_1.ruleSeverity)(config?.rules?.["hook-script-exists"]);
|
|
4200
4226
|
if (!sev)
|
|
4201
4227
|
return { issues: 0, errors: 0 };
|
|
4202
|
-
if (!adapter.
|
|
4228
|
+
if (!adapter.shellHooks) {
|
|
4203
4229
|
reportNotApplicable("Hook-script existence check", "shell hooks", adapter, silent);
|
|
4204
4230
|
return { issues: 0, errors: 0 };
|
|
4205
4231
|
}
|
|
@@ -4234,7 +4260,7 @@ function checkMcpToolResolves(config, silent, adapter, scanRoot) {
|
|
|
4234
4260
|
const sev = (0, types_js_1.ruleSeverity)(config?.rules?.["mcp-tool-resolves"]);
|
|
4235
4261
|
if (!sev)
|
|
4236
4262
|
return { issues: 0, errors: 0 };
|
|
4237
|
-
if (!adapter.
|
|
4263
|
+
if (!adapter.subagents) {
|
|
4238
4264
|
reportNotApplicable("MCP tool-resolution check", "subagents", adapter, silent);
|
|
4239
4265
|
return { issues: 0, errors: 0 };
|
|
4240
4266
|
}
|
|
@@ -4408,7 +4434,6 @@ async function handleMeasure(restArgs, args) {
|
|
|
4408
4434
|
const json = args.includes("--json");
|
|
4409
4435
|
const harnessFlag = harnessFlagFrom(args);
|
|
4410
4436
|
const adapter = resolveCommandHarness(dir, harnessFlag).adapter;
|
|
4411
|
-
const harness = adapter.name === "codex" ? "codex" : "claude-code";
|
|
4412
4437
|
const promptsPath = flagValue(args, "--prompts");
|
|
4413
4438
|
if (!promptsPath) {
|
|
4414
4439
|
console.error("measure needs --prompts=<file.json> (a map of skill name → { prompts, irrelevant }).");
|
|
@@ -4437,13 +4462,13 @@ async function handleMeasure(restArgs, args) {
|
|
|
4437
4462
|
concurrency,
|
|
4438
4463
|
minPrompts: num("--min-prompts"),
|
|
4439
4464
|
model,
|
|
4440
|
-
|
|
4465
|
+
adapter,
|
|
4441
4466
|
});
|
|
4442
4467
|
const collisions = await (0, scan_behavioral_js_1.measurePluginSelection)(dir, promptSet, {
|
|
4443
4468
|
concurrency,
|
|
4444
4469
|
trials: num("--trials"),
|
|
4445
4470
|
model,
|
|
4446
|
-
|
|
4471
|
+
adapter,
|
|
4447
4472
|
});
|
|
4448
4473
|
if (json) {
|
|
4449
4474
|
console.log(JSON.stringify({ trigger, collisions }, null, 2));
|
|
@@ -5857,25 +5882,34 @@ async function installHookFile(file, adapter, registeredProviders = []) {
|
|
|
5857
5882
|
`${(0, hook_install_js_1.hookGateRef)(ref, adapter.layout.projectRootTokens)} || exit ${(0, hook_install_js_1.hookRuntimeMissingExit)((0, hook_program_js_1.dispatchKind)(program))}`,
|
|
5858
5883
|
dialect: adapter.dialect,
|
|
5859
5884
|
hookProtocol: adapter.hookProtocol,
|
|
5860
|
-
|
|
5885
|
+
settings: adapter.layout.settings,
|
|
5861
5886
|
registeredProviders,
|
|
5862
5887
|
});
|
|
5863
5888
|
// Tamper-evident stamp beside the source (one dir → basename is unique).
|
|
5864
5889
|
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)((0, hook_runtime_js_1.hookStampPath)(file)), { recursive: true });
|
|
5865
5890
|
(0, node_fs_1.writeFileSync)((0, hook_runtime_js_1.hookStampPath)(file), JSON.stringify({ file, stamp: compiled.stamp }, null, 2) + "\n");
|
|
5866
5891
|
// Merge into the harness's native config, idempotently.
|
|
5867
|
-
|
|
5892
|
+
//
|
|
5893
|
+
// 🔴 THREE FORMAT BRANCHES BECAME ZERO, and they were three because the ONE
|
|
5894
|
+
// enum they all read was standing in for two different questions. Reading
|
|
5895
|
+
// and writing the file is the ENCODING (`layout.settings`, a codec); folding
|
|
5896
|
+
// our registrations into what is already there is the entry SHAPE
|
|
5897
|
+
// (`hookProtocol.mergeRegistrations` — CC nests several commands under one
|
|
5898
|
+
// matcher, Codex carries one command per entry). A TOML harness with
|
|
5899
|
+
// CC-shaped entries was expressible under the enum and would have been
|
|
5900
|
+
// merged wrong; neither port can be wrong about its own half.
|
|
5868
5901
|
const settingsAbs = (0, node_path_1.resolve)(process.cwd(), adapter.layout.settingsPath);
|
|
5869
5902
|
const existing = (0, node_fs_1.existsSync)(settingsAbs)
|
|
5870
|
-
?
|
|
5871
|
-
? (0, toml_1.parse)((0, node_fs_1.readFileSync)(settingsAbs, "utf-8"))
|
|
5872
|
-
: JSON.parse((0, node_fs_1.readFileSync)(settingsAbs, "utf-8"))
|
|
5903
|
+
? adapter.layout.settings.parse((0, node_fs_1.readFileSync)(settingsAbs, "utf-8"))
|
|
5873
5904
|
: {};
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5905
|
+
// `shellHooks` narrows the adapter union: a harness whose hooks are code
|
|
5906
|
+
// modules has no settings block to install into, and the type says so.
|
|
5907
|
+
if (!adapter.shellHooks) {
|
|
5908
|
+
throw new Error(`Harness "${adapter.name}" has no shell-hook settings to install into (hooks are code modules).`);
|
|
5909
|
+
}
|
|
5910
|
+
const merged = adapter.hookProtocol.mergeRegistrations(existing, compiled.hooks, ref);
|
|
5877
5911
|
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(settingsAbs), { recursive: true });
|
|
5878
|
-
(0, node_fs_1.writeFileSync)(settingsAbs,
|
|
5912
|
+
(0, node_fs_1.writeFileSync)(settingsAbs, adapter.layout.settings.render(merged));
|
|
5879
5913
|
// No silent skips: warn loudly only where a hook's OUTPUT genuinely may not
|
|
5880
5914
|
// apply on this harness. INJECT's `additionalContext` shape is now CONFIRMED
|
|
5881
5915
|
// shared with Codex (per the official hooks docs), so an inject hook only
|
|
@@ -5900,6 +5934,7 @@ async function installHookFile(file, adapter, registeredProviders = []) {
|
|
|
5900
5934
|
`(stderr at exit 0 goes to the debug log, not the transcript, and the model ` +
|
|
5901
5935
|
`never sees it). Injectable here: ${injectable.join(", ") || "(none)"}. ` +
|
|
5902
5936
|
`Move the hook to one of those events, or use run() if you meant an action.`;
|
|
5937
|
+
// eslint-disable-next-line no-restricted-syntax -- KEPT ON PURPOSE (design §2 "the eighth site" / §6). This is a per-channel MEASUREMENT STATUS — react output is CONFIRMED only on Claude Code — and there is no port to read it from: `EventCapability` carries `carries`/`honours`/`matcher`/`denyShape` and no verified-status column. The fix is that column, owned by the capability table, not by this port. Until it exists, a name check that says so is honest and a fabricated capability field would not be.
|
|
5903
5938
|
}
|
|
5904
5939
|
else if (adapter.name !== "claude-code") {
|
|
5905
5940
|
if (role === "inject" && !injectable.includes(event)) {
|
|
@@ -6191,7 +6226,6 @@ async function runAuditServe(report, adoptable, cliErr) {
|
|
|
6191
6226
|
*/
|
|
6192
6227
|
async function runAutoTrigger(dir, report, adapter, args) {
|
|
6193
6228
|
const json = args.includes("--json");
|
|
6194
|
-
const harness = adapter.name === "codex" ? "codex" : "claude-code";
|
|
6195
6229
|
const skills = report.skills
|
|
6196
6230
|
.filter((s) => s.hasDescription && !s.userInvoked && s.description)
|
|
6197
6231
|
.map((s) => ({ name: s.name, description: s.description ?? "" }));
|
|
@@ -6210,7 +6244,7 @@ async function runAutoTrigger(dir, report, adapter, args) {
|
|
|
6210
6244
|
minPrompts: audit_prompts_js_1.AUTO_RECALL_COUNT,
|
|
6211
6245
|
minDistance: audit_prompts_js_1.AUTO_MIN_DISTANCE,
|
|
6212
6246
|
model,
|
|
6213
|
-
|
|
6247
|
+
adapter,
|
|
6214
6248
|
// Discover candidates with the resolved adapter's layout/dialect — a Codex
|
|
6215
6249
|
// repo's skills live under the Codex layout, not the default CC one.
|
|
6216
6250
|
layout: adapter.layout,
|
|
@@ -6222,14 +6256,14 @@ async function runAutoTrigger(dir, report, adapter, args) {
|
|
|
6222
6256
|
// this proves the wrong one actually fires). Only meaningful with ≥2 model-
|
|
6223
6257
|
// invocable skills (a lone skill can't collide); reuses the same auto prompts.
|
|
6224
6258
|
const collisions = skills.length >= 2
|
|
6225
|
-
? await (0, scan_behavioral_js_1.measurePluginSelection)(dir, promptSet, { model,
|
|
6259
|
+
? await (0, scan_behavioral_js_1.measurePluginSelection)(dir, promptSet, { model, adapter })
|
|
6226
6260
|
: null;
|
|
6227
6261
|
// Third behavioral eval (same consent): adversarial-gate — do enforcement-gate
|
|
6228
6262
|
// skills HOLD when the agent is told to violate them? Auto-derives its own
|
|
6229
6263
|
// attacks; a no-op (no model calls) when the plugin declares no gate skills.
|
|
6230
6264
|
const gates = await (0, scan_behavioral_js_1.measureGateAdversarial)(dir, {
|
|
6231
6265
|
model,
|
|
6232
|
-
|
|
6266
|
+
adapter,
|
|
6233
6267
|
layout: adapter.layout,
|
|
6234
6268
|
dialect: adapter.dialect,
|
|
6235
6269
|
});
|
|
@@ -6264,7 +6298,7 @@ async function runAutoTrigger(dir, report, adapter, args) {
|
|
|
6264
6298
|
*
|
|
6265
6299
|
* Returns `execute` (run the executing checks?) + a `note` to print at the end.
|
|
6266
6300
|
*/
|
|
6267
|
-
async function resolveExecution(s, decision, json,
|
|
6301
|
+
async function resolveExecution(s, decision, json, access) {
|
|
6268
6302
|
if (decision.kind === "run")
|
|
6269
6303
|
return { execute: true, note: null };
|
|
6270
6304
|
if (decision.kind === "skip")
|
|
@@ -6273,7 +6307,7 @@ async function resolveExecution(s, decision, json, harness) {
|
|
|
6273
6307
|
note: json ? null : (0, scan_trigger_suggest_js_1.formatExecuteSkip)(decision.reason),
|
|
6274
6308
|
};
|
|
6275
6309
|
// ask — prompt once, then remember the answer.
|
|
6276
|
-
const answer = await askOnce(buildExecuteDisclosure(s,
|
|
6310
|
+
const answer = await askOnce(buildExecuteDisclosure(s, access));
|
|
6277
6311
|
const yes = /^y(es)?$/i.test(answer); // default NO (executes your hooks / servers)
|
|
6278
6312
|
rememberAuditMeasure(yes);
|
|
6279
6313
|
return {
|
|
@@ -6284,10 +6318,11 @@ async function resolveExecution(s, decision, json, harness) {
|
|
|
6284
6318
|
};
|
|
6285
6319
|
}
|
|
6286
6320
|
/** The bundled consent prompt — discloses exactly what will execute (and what it
|
|
6287
|
-
* costs) so the yes is informed. Default NO. Harness-aware
|
|
6288
|
-
*
|
|
6289
|
-
*
|
|
6290
|
-
|
|
6321
|
+
* costs) so the yes is informed. Default NO. Harness-aware WITHOUT knowing which
|
|
6322
|
+
* harness: the cost wording comes from the driving adapter's own `access`, so a
|
|
6323
|
+
* repo on any harness is disclosed in that harness's terms — the old version had
|
|
6324
|
+
* to name Codex to stop the prompt falsely reading "no model access". */
|
|
6325
|
+
function buildExecuteDisclosure(s, access) {
|
|
6291
6326
|
const lines = ["\nRun the executing checks against your harness?"];
|
|
6292
6327
|
if (s.hasMcp)
|
|
6293
6328
|
lines.push(" · start your MCP servers — connects to their backends");
|
|
@@ -6297,29 +6332,51 @@ function buildExecuteDisclosure(s, harness) {
|
|
|
6297
6332
|
const what = s.triggerableSkills >= 2
|
|
6298
6333
|
? "measure whether skills fire and collide"
|
|
6299
6334
|
: "measure whether skills fire";
|
|
6300
|
-
lines.push(` · ${what} (${triggerCostWording(
|
|
6335
|
+
lines.push(` · ${what} (${triggerCostWording(access)})`);
|
|
6301
6336
|
}
|
|
6302
6337
|
if (s.gateSkills > 0) {
|
|
6303
6338
|
// The adversarial-gate eval runs the FULL (unstubbed) skill — the most
|
|
6304
6339
|
// expensive check — so disclose it separately when gate skills are present.
|
|
6305
|
-
lines.push(` · test whether ${String(s.gateSkills)} enforcement-gate skill${s.gateSkills === 1 ? "" : "s"} hold under pressure — runs the full skill (${triggerCostWording(
|
|
6340
|
+
lines.push(` · test whether ${String(s.gateSkills)} enforcement-gate skill${s.gateSkills === 1 ? "" : "s"} hold under pressure — runs the full skill (${triggerCostWording(access)})`);
|
|
6306
6341
|
}
|
|
6307
6342
|
if (s.adoptableRefs) {
|
|
6308
|
-
lines.push(` · draft + verify your instruction file's references (${triggerCostWording(
|
|
6343
|
+
lines.push(` · draft + verify your instruction file's references (${triggerCostWording(access)})`);
|
|
6309
6344
|
}
|
|
6310
6345
|
lines.push("Asked once — remembered in .vigilesrc.json. [y/N] ");
|
|
6311
6346
|
return lines.join("\n");
|
|
6312
6347
|
}
|
|
6313
|
-
/**
|
|
6314
|
-
*
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6348
|
+
/**
|
|
6349
|
+
* Cost/availability wording for the trigger tier, read off the DRIVING HARNESS's
|
|
6350
|
+
* own answer instead of branching on its name.
|
|
6351
|
+
*
|
|
6352
|
+
* 🔴 IT REPLACES THREE THINGS AT ONCE: `harness === "codex"`, and the pair of
|
|
6353
|
+
* Claude-env predicates that ran for every other harness. The old Codex arm was a
|
|
6354
|
+
* guess ("skips if `codex` isn't on PATH") because nothing had asked; `access`
|
|
6355
|
+
* asks, so the prompt now discloses what will actually happen.
|
|
6356
|
+
*/
|
|
6357
|
+
function triggerCostWording(access) {
|
|
6358
|
+
switch (access.kind) {
|
|
6359
|
+
case "none":
|
|
6360
|
+
return `needs model access — none detected, will skip (${access.fix})`;
|
|
6361
|
+
case "metered":
|
|
6362
|
+
return "⚠ spends API credits";
|
|
6363
|
+
case "subscription":
|
|
6364
|
+
return "your subscription, $0 metered";
|
|
6365
|
+
}
|
|
6366
|
+
}
|
|
6367
|
+
/**
|
|
6368
|
+
* This adapter's answer to "is a real model reachable, and on whose bill".
|
|
6369
|
+
*
|
|
6370
|
+
* A reference-only harness has no executing tier at all, which is a `none` whose
|
|
6371
|
+
* `fix` says so rather than a Claude-shaped credential hint.
|
|
6372
|
+
*/
|
|
6373
|
+
async function modelAccessFor(adapter) {
|
|
6374
|
+
if (!adapter.harnessTesting)
|
|
6375
|
+
return {
|
|
6376
|
+
kind: "none",
|
|
6377
|
+
fix: `${adapter.name} has no executing-tier driver in this build`,
|
|
6378
|
+
};
|
|
6379
|
+
return (await adapter.liveDriver()).access(process.env);
|
|
6323
6380
|
}
|
|
6324
6381
|
/** Run the trigger tier: a curated `--prompts` file, else auto-generated probes. */
|
|
6325
6382
|
async function runTriggerTier(dir, report, adapter, args) {
|
|
@@ -6509,7 +6566,14 @@ async function main() {
|
|
|
6509
6566
|
const json = args.includes("--json");
|
|
6510
6567
|
// A single dir that's a marketplace (e.g. wshobson/agents' 80+ plugins
|
|
6511
6568
|
// under one marketplace.json) expands into its members and ranks them.
|
|
6512
|
-
|
|
6569
|
+
// The layout is passed rather than defaulted: `inspectMarketplace` reads
|
|
6570
|
+
// `dirname(layout.manifestPath)`, which is a different directory per
|
|
6571
|
+
// harness, and it used to fall back to the Claude Code layout inside the
|
|
6572
|
+
// harness-agnostic detector. `harnessLayoutFor` is the same resolution
|
|
6573
|
+
// the audit itself uses a few lines down.
|
|
6574
|
+
const market = dirs.length === 1
|
|
6575
|
+
? (0, scan_js_1.inspectMarketplace)((0, node_path_1.resolve)(dirs[0]), harnessLayoutFor((0, node_path_1.resolve)(dirs[0]), config, harnessFlagFrom(args)))
|
|
6576
|
+
: null;
|
|
6513
6577
|
// A marketplace whose members are all EXTERNAL expands to nothing, and the
|
|
6514
6578
|
// fallback below already says what to do about that: the target is the
|
|
6515
6579
|
// directory itself.
|
|
@@ -6659,21 +6723,13 @@ async function main() {
|
|
|
6659
6723
|
if (selection.kind === "notice") {
|
|
6660
6724
|
console.log(`⚠ ${selection.notice}`);
|
|
6661
6725
|
}
|
|
6662
|
-
//
|
|
6663
|
-
//
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
// skills it ships? `npm install` drops them in node_modules, which
|
|
6670
|
-
// Claude Code never scans — the plugin install is what wires them,
|
|
6671
|
-
// and until it runs the whole teaching surface is silently absent.
|
|
6672
|
-
// Advisory only (machine state, not repo state) — never scored.
|
|
6673
|
-
const reach = (0, skill_reachability_js_1.formatSkillReachability)((0, skill_reachability_js_1.checkSkillReachability)(root));
|
|
6674
|
-
if (reach)
|
|
6675
|
-
console.log(reach);
|
|
6676
|
-
}
|
|
6726
|
+
// Install diagnostics from the DRIVING harness — "is what vigiles
|
|
6727
|
+
// assumes about this harness's install true on this machine". Advisory
|
|
6728
|
+
// only (machine state, not repo state), never scored, printed as-is.
|
|
6729
|
+
// This used to be `if (adapter.name === "claude-code") { …two Claude
|
|
6730
|
+
// Code checks… }`; the checks were right and the gate was the defect.
|
|
6731
|
+
for (const line of adapter.advisories((0, install_reader_js_1.buildInstallReader)(adapter, root)))
|
|
6732
|
+
console.log(line);
|
|
6677
6733
|
console.log("");
|
|
6678
6734
|
}
|
|
6679
6735
|
// The versioned AuditReport is the product boundary — the same JSON the
|
|
@@ -6693,12 +6749,19 @@ async function main() {
|
|
|
6693
6749
|
// `Evaluated` ring needs to know whether this run will ever ask the
|
|
6694
6750
|
// skill-firing question — see `firingMeasured` below.
|
|
6695
6751
|
const isForeign = root !== process.cwd();
|
|
6752
|
+
// ONE read of "can we reach a model, and on whose bill", from the driving
|
|
6753
|
+
// adapter. It replaces a name check OR-ed with one harness's env vars at
|
|
6754
|
+
// four sites below, and the two hard-coded "authenticate the `claude` CLI"
|
|
6755
|
+
// strings that printed whatever harness was driving.
|
|
6756
|
+
const modelAccess = await modelAccessFor(adapter);
|
|
6696
6757
|
const surfaces = {
|
|
6697
6758
|
hasMcp: report.mcp && !isForeign,
|
|
6698
6759
|
triggerableSkills: report.skills.filter((s) => s.hasDescription && !s.userInvoked).length,
|
|
6699
6760
|
gateSkills: (0, scan_behavioral_js_1.detectGateSkills)(report.skills).length,
|
|
6700
|
-
|
|
6701
|
-
|
|
6761
|
+
// NOT gated on a harness name any more. The drafter is the adapter's
|
|
6762
|
+
// own eval driver and the verifier is deterministic and harness-free,
|
|
6763
|
+
// so an instruction file is an instruction file: prose, on any harness.
|
|
6764
|
+
adoptableRefs: (0, node_fs_1.existsSync)((0, node_path_1.resolve)(root, adapter.layout.instructionFile)),
|
|
6702
6765
|
};
|
|
6703
6766
|
// `decideExecute` is PURE and prompt-free, so the read-vs-run outcome can
|
|
6704
6767
|
// be known before anything prints. Only a settled `run` (a remembered yes)
|
|
@@ -6718,7 +6781,7 @@ async function main() {
|
|
|
6718
6781
|
});
|
|
6719
6782
|
const firingMeasured = execDecision.kind === "run" &&
|
|
6720
6783
|
surfaces.triggerableSkills > 0 &&
|
|
6721
|
-
|
|
6784
|
+
modelAccess.kind !== "none";
|
|
6722
6785
|
// The report scaffold WITHOUT the rule map — the map's catalog
|
|
6723
6786
|
// enrichment (enabled-state / "documented but OFF") enumerates the repo's
|
|
6724
6787
|
// linter, which is gated on the SAME audit.measure consent as the
|
|
@@ -6814,7 +6877,7 @@ async function main() {
|
|
|
6814
6877
|
// that isn't shipped, so it lives in the vigiles testing API.)
|
|
6815
6878
|
// `surfaces` + `execDecision` were resolved above (the ring needed them);
|
|
6816
6879
|
// this only turns an `ask` into a prompt and remembers the answer.
|
|
6817
|
-
const { execute, note: execNote } = await resolveExecution(surfaces, execDecision, json,
|
|
6880
|
+
const { execute, note: execNote } = await resolveExecution(surfaces, execDecision, json, modelAccess);
|
|
6818
6881
|
// Consent is now settled (and remembered via .vigilesrc.json, which
|
|
6819
6882
|
// computeRuleRouting re-reads) — route the prose rules, enumerating the
|
|
6820
6883
|
// live catalog when consented + own-repo. Feeds the JSON report, the
|
|
@@ -6881,17 +6944,14 @@ async function main() {
|
|
|
6881
6944
|
// of the same consent (`execute`), and only when a model is reachable;
|
|
6882
6945
|
// otherwise it's a one-line note (never a hang).
|
|
6883
6946
|
if (execute && surfaces.triggerableSkills > 0) {
|
|
6884
|
-
//
|
|
6885
|
-
//
|
|
6886
|
-
//
|
|
6887
|
-
|
|
6888
|
-
// unavailable. (harness-parity: never block Codex behind a CC check.)
|
|
6889
|
-
const modelReachable = adapter.name === "codex" || (0, scan_trigger_suggest_js_1.hasModelAccess)(process.env);
|
|
6890
|
-
if (modelReachable) {
|
|
6947
|
+
// The adapter answered this once, above. `fix` is THIS harness's own
|
|
6948
|
+
// sentence: the string here used to say "authenticate the `claude` CLI
|
|
6949
|
+
// or set ANTHROPIC_API_KEY" whatever harness the repo was on.
|
|
6950
|
+
if (modelAccess.kind !== "none") {
|
|
6891
6951
|
await runTriggerTier(targets[0], report, adapter, args);
|
|
6892
6952
|
}
|
|
6893
6953
|
else if (!json) {
|
|
6894
|
-
console.log(
|
|
6954
|
+
console.log(`\nℹ skill firing not measured — no model access (${modelAccess.fix}).`);
|
|
6895
6955
|
}
|
|
6896
6956
|
}
|
|
6897
6957
|
// The adoption preview — "what would vigiles catch in YOUR repo?" The model
|
|
@@ -6899,34 +6959,52 @@ async function main() {
|
|
|
6899
6959
|
// VERIFIES each, so "M broken right now" is trustworthy though the extraction
|
|
6900
6960
|
// is probabilistic. Same consent as the trigger tier (`surfaces.adoptableRefs`
|
|
6901
6961
|
// makes a bare instruction-file repo consent-eligible — the prime adoption
|
|
6902
|
-
// target).
|
|
6903
|
-
//
|
|
6904
|
-
//
|
|
6962
|
+
// target).
|
|
6963
|
+
//
|
|
6964
|
+
// 🔴 NO LONGER CLAUDE CODE ONLY, and the feature gate that said so is
|
|
6965
|
+
// gone rather than widened. The drafter is the driving adapter's own eval
|
|
6966
|
+
// driver (`draftWith`); the verifier was always deterministic and
|
|
6967
|
+
// harness-free. What made this CC-only was that `defaultDraft` defaulted
|
|
6968
|
+
// to the Claude runner and parser and nobody had passed anything else —
|
|
6969
|
+
// a TODO, which the old `adapter.name === "claude-code"` froze into the
|
|
6970
|
+
// report as if it were a vendor fact.
|
|
6905
6971
|
let adoptabilityResult;
|
|
6906
6972
|
if (execute && surfaces.adoptableRefs) {
|
|
6907
|
-
if (
|
|
6973
|
+
if (modelAccess.kind !== "none" && adapter.harnessTesting) {
|
|
6908
6974
|
const instrPath = (0, node_path_1.resolve)(root, adapter.layout.instructionFile);
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6975
|
+
// 🔴 CAUGHT, because a FAILED RUN and an EMPTY RESULT are different
|
|
6976
|
+
// answers and this tier used to print the second for both. It
|
|
6977
|
+
// reaches the harness binary directly — no behavioral probe in
|
|
6978
|
+
// front of it to self-report unavailability — so on a machine
|
|
6979
|
+
// without that CLI the drafter would exit non-zero with no output
|
|
6980
|
+
// and the report would say "no machine-verifiable references
|
|
6981
|
+
// found". `DraftRunFailed` is thrown for exactly that; anything
|
|
6982
|
+
// else is a real bug and is left to propagate.
|
|
6983
|
+
try {
|
|
6984
|
+
adoptabilityResult = await (0, adoptability_js_1.runAdoptabilityTier)({
|
|
6985
|
+
instructionContent: (0, node_fs_1.readFileSync)(instrPath, "utf-8"),
|
|
6986
|
+
basePath: root,
|
|
6987
|
+
draft: (0, adoptability_js_1.draftWith)((await adapter.liveDriver()).evalDriver),
|
|
6988
|
+
});
|
|
6989
|
+
if (!json)
|
|
6990
|
+
console.log("\n" +
|
|
6991
|
+
(0, adoptability_js_1.formatAdoptability)(adoptabilityResult, adapter.layout.instructionFile));
|
|
6992
|
+
}
|
|
6993
|
+
catch (e) {
|
|
6994
|
+
if (!(e instanceof adoptability_js_1.DraftRunFailed))
|
|
6995
|
+
throw e;
|
|
6996
|
+
if (!json)
|
|
6997
|
+
console.log(`\nℹ adoptability not measured — ${e.message}.`);
|
|
6998
|
+
}
|
|
6916
6999
|
}
|
|
6917
7000
|
else if (!json && surfaces.triggerableSkills === 0) {
|
|
6918
7001
|
// Only when the trigger tier didn't already print the same note.
|
|
6919
|
-
console.log(
|
|
7002
|
+
console.log(`\nℹ adoptability not measured — no model access (${modelAccess.kind === "none" ? modelAccess.fix : `${adapter.name} has no executing-tier driver`}).`);
|
|
6920
7003
|
}
|
|
6921
7004
|
}
|
|
6922
|
-
//
|
|
6923
|
-
//
|
|
6924
|
-
//
|
|
6925
|
-
if (!json &&
|
|
6926
|
-
adapter.name === "codex" &&
|
|
6927
|
-
(0, node_fs_1.existsSync)((0, node_path_1.resolve)(root, adapter.layout.instructionFile))) {
|
|
6928
|
-
console.log("\nℹ adoptability preview (what vigiles would catch in your repo) is Claude Code only for now — Codex support is a follow-up.");
|
|
6929
|
-
}
|
|
7005
|
+
// The "adoptability preview is Claude Code only for now — Codex support
|
|
7006
|
+
// is a follow-up" note used to print here. It is DELETED, not reworded:
|
|
7007
|
+
// Codex is not a follow-up, so there is nothing to defer.
|
|
6930
7008
|
// The loud read-vs-run nudge for a headless / remembered-no skip — printed
|
|
6931
7009
|
// AFTER the report so the deterministic read leads.
|
|
6932
7010
|
if (execNote)
|