vigiles 16.1.1 → 16.1.3
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.js +17 -0
- package/dist/adapters/claude-code/dialect.d.ts +19 -13
- package/dist/adapters/claude-code/dialect.js +40 -62
- package/dist/adapters/claude-code/vocabulary.d.ts +133 -0
- package/dist/adapters/claude-code/vocabulary.js +208 -0
- package/dist/core/bash-effects.d.ts +57 -0
- package/dist/core/bash-effects.js +147 -0
- package/dist/core/compile.js +6 -1
- package/dist/core/dialect.d.ts +27 -0
- package/dist/core/hook-events.d.ts +32 -15
- package/dist/core/hook-events.js +23 -29
- package/dist/core/hook-program.js +12 -4
- package/dist/core/markdown.d.ts +53 -0
- package/dist/core/markdown.js +99 -0
- package/dist/core/rule-meta.js +2 -2
- package/dist/core/skill-resources.js +58 -57
- package/dist/core/source-refs.d.ts +118 -0
- package/dist/core/source-refs.js +206 -0
- package/dist/core/tool-contract.d.ts +69 -30
- package/dist/core/tool-contract.js +59 -57
- package/dist/core/vocabulary-consistency.d.ts +35 -0
- package/dist/core/vocabulary-consistency.js +81 -0
- package/dist/core/vocabulary.d.ts +138 -0
- package/dist/core/vocabulary.js +262 -0
- package/dist/coverage-evidence.js +15 -3
- package/dist/plugin-loader.js +22 -28
- package/dist/scan-core.d.ts +8 -1
- package/dist/scan-core.js +105 -20
- package/dist/scan-files.js +17 -20
- package/dist/scan.d.ts +24 -0
- package/dist/scan.js +8 -1
- package/package.json +1 -1
|
@@ -1,73 +1,77 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.authoringIssues = exports.advisoryIssues = exports.scoredIssues = void 0;
|
|
4
|
+
exports.subagentToolVocabulary = subagentToolVocabulary;
|
|
3
5
|
exports.closestTool = closestTool;
|
|
4
|
-
exports.confidentToolIssues = confidentToolIssues;
|
|
5
6
|
exports.disallowedToolIssues = disallowedToolIssues;
|
|
6
7
|
exports.verifyToolContract = verifyToolContract;
|
|
7
|
-
const
|
|
8
|
+
const vocabulary_js_1 = require("./vocabulary.js");
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* `
|
|
12
|
-
* `TaskGet → Task?` (distance 3) is a real tool set, not a typo of `Task`.
|
|
10
|
+
* The wire-shape `kind` each verdict maps to. `never-available` and `unknown`
|
|
11
|
+
* predate the vocabulary and keep their meaning for existing consumers;
|
|
12
|
+
* `conditional` is the new one the two-way split could not express.
|
|
13
13
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
const TOOL_ISSUE_KIND = {
|
|
15
|
+
withheld: "never-available",
|
|
16
|
+
conditional: "conditional",
|
|
17
|
+
unrecognised: "unknown",
|
|
18
|
+
// `available` never reaches here — termIssue returns null for it.
|
|
19
|
+
available: "unknown",
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The tool vocabulary this dialect verifies against — its declared one, else a
|
|
23
|
+
* synthesised one from the flat lists so a legacy adapter keeps working.
|
|
24
|
+
*/
|
|
25
|
+
function subagentToolVocabulary(dialect) {
|
|
26
|
+
return (dialect.subagentToolVocabulary ??
|
|
27
|
+
(0, vocabulary_js_1.vocabularyFromLists)(`${dialect.name} subagent tool`, `${dialect.name} adapter (no recorded capture)`, dialect.builtinAgentTools, dialect.neverAvailableTools));
|
|
25
28
|
}
|
|
26
29
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* typo suggestion (`Edt → Edit`). A bare `unknown` with no near match is NOT
|
|
32
|
-
* flagged here — it is more likely a tool vigiles doesn't know than a defect
|
|
33
|
-
* (sweeping real plugins surfaced a 280★ plugin using `TaskCreate/TaskGet/…`
|
|
34
|
-
* consistently; flagging those would be crying wolf). `compileAgent` stays strict
|
|
35
|
-
* — when you author your OWN spec, every unrecognized tool is worth an error.
|
|
30
|
+
* Closest known built-in tool by edit distance (≤ 2), for a "did you mean" hint.
|
|
31
|
+
* A MESSAGE DECORATION, never a gate: whether to report is already settled by
|
|
32
|
+
* the verdict before this is called. The ≤ 2 bound stays tight because a loose
|
|
33
|
+
* bound mis-suggests — `TaskGet → Task?` is a different real tool, not a typo.
|
|
36
34
|
*/
|
|
37
|
-
function
|
|
38
|
-
return
|
|
35
|
+
function closestTool(tool, dialect) {
|
|
36
|
+
return (0, vocabulary_js_1.suggest)(subagentToolVocabulary(dialect), tool);
|
|
39
37
|
}
|
|
40
38
|
/**
|
|
41
39
|
* Verify a subagent's `disallowedTools:` BLOCK-list — the mirror of the allow
|
|
42
40
|
* contract. A typo here is dangerous: you meant to block `Bash` but wrote `Bsh`,
|
|
43
41
|
* so nothing is blocked and the dangerous tool stays available, silently. Returns
|
|
44
|
-
* one {@link ToolIssue} per entry that's a CLOSE TYPO of a real
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* legitimate plugin tool to block), or a bare unknown with no near match
|
|
48
|
-
*
|
|
49
|
-
*
|
|
42
|
+
* one {@link ToolIssue} per entry that's a CLOSE TYPO of a real tool.
|
|
43
|
+
* Deliberately NOT flagged: any name the vocabulary knows (blocking it is the
|
|
44
|
+
* point — including a withheld one, which is merely redundant), an MCP tool (a
|
|
45
|
+
* legitimate plugin tool to block), or a bare unknown with no near match.
|
|
46
|
+
*
|
|
47
|
+
* This is the ONE place a near match still gates a finding, and it is not the
|
|
48
|
+
* confidence proxy the allow-side check was rightly stripped of. On a block-list
|
|
49
|
+
* the risk inverts: an entry naming nothing is harmless UNLESS you meant a real
|
|
50
|
+
* tool and mistyped it, and "meant a real tool" is precisely what a one-character
|
|
51
|
+
* distance evidences. `disallowedTools: [Zzzz]` blocks nothing and nobody
|
|
52
|
+
* intended otherwise; `disallowedTools: [Bsh]` leaves `Bash` wide open. So the
|
|
53
|
+
* distance here is the actual semantic signal, not a stand-in for one.
|
|
50
54
|
*/
|
|
51
55
|
function disallowedToolIssues(tools, dialect) {
|
|
52
|
-
const
|
|
56
|
+
const vocab = subagentToolVocabulary(dialect);
|
|
53
57
|
const issues = [];
|
|
54
58
|
for (const raw of tools) {
|
|
55
59
|
const tool = raw.split("(")[0].trim();
|
|
56
60
|
if (tool === "" || tool === "*")
|
|
57
61
|
continue;
|
|
58
|
-
if (dialect.builtinAgentTools.includes(tool))
|
|
59
|
-
continue; // legitimately blocked
|
|
60
|
-
if (never.has(tool))
|
|
61
|
-
continue; // harmless to list (already unavailable)
|
|
62
62
|
if (dialect.mcpToolPattern.test(tool))
|
|
63
63
|
continue; // a real plugin/MCP tool to block
|
|
64
|
-
|
|
64
|
+
if ((0, vocabulary_js_1.classify)(vocab, tool).kind !== "unrecognised")
|
|
65
|
+
continue; // a real name — blocking it is fine
|
|
66
|
+
const near = (0, vocabulary_js_1.suggest)(vocab, tool);
|
|
65
67
|
if (near === null)
|
|
66
68
|
continue; // bare unknown → likely a plugin tool, not a typo
|
|
67
69
|
issues.push({
|
|
68
70
|
tool,
|
|
71
|
+
verdict: "unrecognised",
|
|
69
72
|
kind: "unknown",
|
|
70
73
|
suggestion: near,
|
|
74
|
+
severity: "scored",
|
|
71
75
|
message: `disallowedTools entry "${tool}" matches no real tool — it blocks nothing. Did you mean "${near}"?`,
|
|
72
76
|
});
|
|
73
77
|
}
|
|
@@ -80,34 +84,32 @@ function disallowedToolIssues(tools, dialect) {
|
|
|
80
84
|
* is stripped to its base tool before checking.
|
|
81
85
|
*/
|
|
82
86
|
function verifyToolContract(tools, dialect) {
|
|
83
|
-
const
|
|
87
|
+
const vocab = subagentToolVocabulary(dialect);
|
|
84
88
|
const issues = [];
|
|
85
89
|
for (const raw of tools) {
|
|
86
90
|
const tool = raw.split("(")[0].trim(); // strip a Tool(restriction) suffix
|
|
87
91
|
if (tool === "" || tool === "*")
|
|
88
92
|
continue; // "" / "*" = wildcard, inherits all
|
|
89
|
-
if (never.has(tool)) {
|
|
90
|
-
issues.push({
|
|
91
|
-
tool,
|
|
92
|
-
kind: "never-available",
|
|
93
|
-
suggestion: null,
|
|
94
|
-
message: `Tool "${tool}" is never available to a subagent — remove it from the tools list.`,
|
|
95
|
-
});
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
if (dialect.builtinAgentTools.includes(tool))
|
|
99
|
-
continue;
|
|
100
93
|
if (dialect.mcpToolPattern.test(tool))
|
|
101
94
|
continue;
|
|
102
|
-
const
|
|
103
|
-
const
|
|
95
|
+
const verdict = (0, vocabulary_js_1.classify)(vocab, tool);
|
|
96
|
+
const issue = (0, vocabulary_js_1.termIssue)(vocab, verdict, "Tool", "the subagent never gets it");
|
|
97
|
+
if (issue === null)
|
|
98
|
+
continue;
|
|
104
99
|
issues.push({
|
|
105
100
|
tool,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
101
|
+
verdict: verdict.kind,
|
|
102
|
+
kind: TOOL_ISSUE_KIND[verdict.kind],
|
|
103
|
+
suggestion: issue.suggestion,
|
|
104
|
+
...(issue.condition !== undefined ? { condition: issue.condition } : {}),
|
|
105
|
+
severity: issue.severity,
|
|
106
|
+
message: issue.message,
|
|
109
107
|
});
|
|
110
108
|
}
|
|
111
109
|
return issues;
|
|
112
110
|
}
|
|
111
|
+
var vocabulary_js_2 = require("./vocabulary.js");
|
|
112
|
+
Object.defineProperty(exports, "scoredIssues", { enumerable: true, get: function () { return vocabulary_js_2.scoredIssues; } });
|
|
113
|
+
Object.defineProperty(exports, "advisoryIssues", { enumerable: true, get: function () { return vocabulary_js_2.advisoryIssues; } });
|
|
114
|
+
Object.defineProperty(exports, "authoringIssues", { enumerable: true, get: function () { return vocabulary_js_2.authoringIssues; } });
|
|
113
115
|
//# sourceMappingURL=tool-contract.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The invariant that was missing when `Agent` sat in two catalogs at once.
|
|
3
|
+
*
|
|
4
|
+
* A `HarnessDialect` carries several name lists that describe the SAME
|
|
5
|
+
* vocabulary from different angles — `builtinAgentTools` (declarable),
|
|
6
|
+
* `neverAvailableTools` (dead), `sideEffectingTools` (a subset of declarable).
|
|
7
|
+
* Nothing checked that they agreed. So `Agent` could be listed as
|
|
8
|
+
* never-available while its own alias `Task` sat in the built-in catalog, and
|
|
9
|
+
* `dialect-drift.ts` could read `Agent` out of the vendor's shipped
|
|
10
|
+
* `sdk-tools.d.ts` every run, for months, without anything noticing the
|
|
11
|
+
* contradiction. The lists were consistent with nothing, including each other.
|
|
12
|
+
*
|
|
13
|
+
* These checks are cheap, total, and adapter-agnostic, so they run in the
|
|
14
|
+
* adapter conformance kit — every adapter, present and future, third-party
|
|
15
|
+
* included. A dialect that contradicts itself now fails LOUDLY at the point an
|
|
16
|
+
* author would first run the kit, instead of silently producing a confident
|
|
17
|
+
* wrong finding in someone else's repo.
|
|
18
|
+
*
|
|
19
|
+
* Deliberately NOT here: any judgement about whether a name is *correct*. This
|
|
20
|
+
* cannot tell you the platform renamed `Task` to `Agent` — only that you cannot
|
|
21
|
+
* claim both at once. Freshness against the real platform is
|
|
22
|
+
* `dialect-drift.ts`'s job; agreement between our own claims is this one's.
|
|
23
|
+
*/
|
|
24
|
+
import type { HarnessDialect } from "./dialect.js";
|
|
25
|
+
import type { HarnessVocabulary } from "./vocabulary.js";
|
|
26
|
+
/** Human-readable violations of the dialect's internal name invariants. */
|
|
27
|
+
export declare function dialectVocabularyProblems(dialect: HarnessDialect): string[];
|
|
28
|
+
/**
|
|
29
|
+
* When a dialect declares a vocabulary, its legacy name lists must be exactly
|
|
30
|
+
* that vocabulary's projections. This is what stops the two from drifting once
|
|
31
|
+
* both exist: a dialect can carry the richer catalog AND the flat arrays other
|
|
32
|
+
* code still reads, but it cannot let them disagree.
|
|
33
|
+
*/
|
|
34
|
+
export declare function vocabularyProjectionProblems(vocab: HarnessVocabulary, builtinAgentTools: readonly string[], neverAvailableTools: readonly string[]): string[];
|
|
35
|
+
//# sourceMappingURL=vocabulary-consistency.d.ts.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dialectVocabularyProblems = dialectVocabularyProblems;
|
|
4
|
+
exports.vocabularyProjectionProblems = vocabularyProjectionProblems;
|
|
5
|
+
/** Human-readable violations of the dialect's internal name invariants. */
|
|
6
|
+
function dialectVocabularyProblems(dialect) {
|
|
7
|
+
const problems = [];
|
|
8
|
+
const builtin = new Set(dialect.builtinAgentTools);
|
|
9
|
+
const never = new Set(dialect.neverAvailableTools);
|
|
10
|
+
// The exact state that shipped: a name claimed as both declarable and dead.
|
|
11
|
+
for (const tool of never)
|
|
12
|
+
if (builtin.has(tool))
|
|
13
|
+
problems.push(`tool "${tool}" is in BOTH builtinAgentTools and neverAvailableTools — ` +
|
|
14
|
+
`it cannot be both declarable and never available`);
|
|
15
|
+
// A side-effecting tool outside the catalog can never be reached by
|
|
16
|
+
// `classifyToolEffect` (rule 1 only fires for names rule 2 could see), so the
|
|
17
|
+
// entry is dead weight that reads as protection.
|
|
18
|
+
for (const tool of dialect.sideEffectingTools ?? [])
|
|
19
|
+
if (!builtin.has(tool))
|
|
20
|
+
problems.push(`tool "${tool}" is in sideEffectingTools but not in builtinAgentTools — ` +
|
|
21
|
+
`the effect classification can never reach it`);
|
|
22
|
+
// A block-semantics subset that names an event the dialect doesn't fire is a
|
|
23
|
+
// rule about nothing.
|
|
24
|
+
const events = new Set(dialect.hookEvents);
|
|
25
|
+
for (const [field, list] of [
|
|
26
|
+
["noEffectHookEvents", dialect.noEffectHookEvents ?? []],
|
|
27
|
+
[
|
|
28
|
+
"permissionDecisionHookEvents",
|
|
29
|
+
dialect.permissionDecisionHookEvents ?? [],
|
|
30
|
+
],
|
|
31
|
+
])
|
|
32
|
+
for (const event of list)
|
|
33
|
+
if (!events.has(event))
|
|
34
|
+
problems.push(`hook event "${event}" is in ${field} but not in hookEvents — ` +
|
|
35
|
+
`it describes an event this dialect says never fires`);
|
|
36
|
+
return problems;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* When a dialect declares a vocabulary, its legacy name lists must be exactly
|
|
40
|
+
* that vocabulary's projections. This is what stops the two from drifting once
|
|
41
|
+
* both exist: a dialect can carry the richer catalog AND the flat arrays other
|
|
42
|
+
* code still reads, but it cannot let them disagree.
|
|
43
|
+
*/
|
|
44
|
+
function vocabularyProjectionProblems(vocab, builtinAgentTools, neverAvailableTools) {
|
|
45
|
+
const problems = [];
|
|
46
|
+
const declarable = new Set(vocab.terms.filter((t) => t.status !== "withheld").map((t) => t.name));
|
|
47
|
+
const withheld = new Set(vocab.terms.filter((t) => t.status === "withheld").map((t) => t.name));
|
|
48
|
+
const diff = (label, expected, actual) => {
|
|
49
|
+
const got = new Set(actual);
|
|
50
|
+
for (const n of expected)
|
|
51
|
+
if (!got.has(n))
|
|
52
|
+
problems.push(`${label} is missing "${n}", which the vocabulary declares`);
|
|
53
|
+
for (const n of got)
|
|
54
|
+
if (!expected.has(n))
|
|
55
|
+
problems.push(`${label} has "${n}", which the vocabulary does not declare`);
|
|
56
|
+
};
|
|
57
|
+
diff("builtinAgentTools", declarable, builtinAgentTools);
|
|
58
|
+
diff("neverAvailableTools", withheld, neverAvailableTools);
|
|
59
|
+
// A conditional term with no condition cannot be reported as one — the whole
|
|
60
|
+
// reason the status exists is to quote the platform's qualifier back.
|
|
61
|
+
for (const t of vocab.terms)
|
|
62
|
+
if (t.status === "conditional" && (t.condition ?? "").trim() === "")
|
|
63
|
+
problems.push(`term "${t.name}" is conditional but states no condition — ` +
|
|
64
|
+
`a condition we cannot quote is one we cannot report`);
|
|
65
|
+
// An alias pointing at a name the vocabulary doesn't hold sends the reader
|
|
66
|
+
// somewhere that doesn't exist.
|
|
67
|
+
for (const t of vocab.terms)
|
|
68
|
+
if (t.aliasOf !== undefined &&
|
|
69
|
+
!vocab.terms.some((o) => o.name === t.aliasOf))
|
|
70
|
+
problems.push(`term "${t.name}" is an alias of "${t.aliasOf}", which this vocabulary ` +
|
|
71
|
+
`does not contain`);
|
|
72
|
+
// Two entries for one name make `classify` order-dependent.
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
for (const t of vocab.terms) {
|
|
75
|
+
if (seen.has(t.name))
|
|
76
|
+
problems.push(`term "${t.name}" appears more than once in the vocabulary`);
|
|
77
|
+
seen.add(t.name);
|
|
78
|
+
}
|
|
79
|
+
return problems;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=vocabulary-consistency.js.map
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/** What the platform does with a term, per the vendor's own documentation. */
|
|
2
|
+
export type TermStatus = "available" | "withheld" | "conditional";
|
|
3
|
+
/** One word of a harness's vocabulary, with what the platform does with it. */
|
|
4
|
+
export interface VocabularyTerm {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly status: TermStatus;
|
|
7
|
+
/**
|
|
8
|
+
* The vendor's stated condition, near-verbatim. REQUIRED when `status` is
|
|
9
|
+
* `"conditional"` — a condition we cannot quote is a condition we cannot
|
|
10
|
+
* report, and reporting it is the whole point of the status.
|
|
11
|
+
*/
|
|
12
|
+
readonly condition?: string;
|
|
13
|
+
/**
|
|
14
|
+
* The current name this term is a still-working deprecated alias of (e.g.
|
|
15
|
+
* `Task` → `Agent`, renamed in Claude Code 2.1.63). An alias is NOT a defect:
|
|
16
|
+
* the platform keeps honouring it.
|
|
17
|
+
*/
|
|
18
|
+
readonly aliasOf?: string;
|
|
19
|
+
}
|
|
20
|
+
/** A named set of platform terms, tagged with where and when it was captured. */
|
|
21
|
+
export interface HarnessVocabulary {
|
|
22
|
+
/** Which vocabulary this is — used in messages, so it must read as English. */
|
|
23
|
+
readonly kind: string;
|
|
24
|
+
/**
|
|
25
|
+
* The exact vendor artifact + version this catalog was read from, e.g.
|
|
26
|
+
* `"code.claude.com/docs/en/hooks § Hook events (claude-code 2.1.233)"`.
|
|
27
|
+
* Printed with every `unrecognised` advisory, so our staleness is visible to
|
|
28
|
+
* the person who hit it rather than only to us.
|
|
29
|
+
*/
|
|
30
|
+
readonly capturedFrom: string;
|
|
31
|
+
readonly terms: readonly VocabularyTerm[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* What the catalog says about one name. Total — there is no absent answer, and
|
|
35
|
+
* deliberately no near-match on the `unrecognised` branch (see the module note:
|
|
36
|
+
* a distance in scope at the decision point is what produced the bugs).
|
|
37
|
+
*/
|
|
38
|
+
export type TermVerdict = {
|
|
39
|
+
readonly kind: "available";
|
|
40
|
+
readonly term: VocabularyTerm;
|
|
41
|
+
} | {
|
|
42
|
+
readonly kind: "withheld";
|
|
43
|
+
readonly term: VocabularyTerm;
|
|
44
|
+
} | {
|
|
45
|
+
readonly kind: "conditional";
|
|
46
|
+
readonly term: VocabularyTerm;
|
|
47
|
+
} | {
|
|
48
|
+
readonly kind: "unrecognised";
|
|
49
|
+
readonly name: string;
|
|
50
|
+
};
|
|
51
|
+
/** How much weight a finding carries — the ONLY input to whether it is scored. */
|
|
52
|
+
export type IssueSeverity =
|
|
53
|
+
/** A defect in the audited repo. Enters the grade. */
|
|
54
|
+
"scored"
|
|
55
|
+
/** True but not actionable, or a statement about vigiles. Never scored. */
|
|
56
|
+
| "advisory";
|
|
57
|
+
/** Look the name up. Total: always one of the four verdicts, never null. */
|
|
58
|
+
export declare function classify(vocab: HarnessVocabulary, name: string): TermVerdict;
|
|
59
|
+
/**
|
|
60
|
+
* Closest catalog name within edit distance 2, else null — a MESSAGE decoration
|
|
61
|
+
* only. Never call this to decide whether to report something; the verdict has
|
|
62
|
+
* already decided that. The ≤2 bound stays tight for the reason it always was:
|
|
63
|
+
* a loose bound mis-suggests (`TaskGet → Task?` is a different real tool, not a
|
|
64
|
+
* typo). Only `available` terms are offered — suggesting a name the platform
|
|
65
|
+
* withholds would trade one dead reference for another.
|
|
66
|
+
*/
|
|
67
|
+
export declare function suggest(vocab: HarnessVocabulary, name: string): string | null;
|
|
68
|
+
/** A vocabulary finding: the message to show and whether it counts. */
|
|
69
|
+
export interface TermIssue {
|
|
70
|
+
/** Which verdict produced this — the input to every downstream policy. */
|
|
71
|
+
readonly verdict: TermVerdict["kind"];
|
|
72
|
+
readonly severity: IssueSeverity;
|
|
73
|
+
readonly message: string;
|
|
74
|
+
/** Near-match for the message only; null unless the term is unrecognised. */
|
|
75
|
+
readonly suggestion: string | null;
|
|
76
|
+
/**
|
|
77
|
+
* The vendor condition, present only for a `conditional` verdict. Carried so a
|
|
78
|
+
* report can GROUP the tools that share one condition instead of repeating the
|
|
79
|
+
* same sentence per tool — a delegating subagent legitimately declares eight of
|
|
80
|
+
* them, and eight identical paragraphs is noise from a tool that sells itself
|
|
81
|
+
* on not crying wolf.
|
|
82
|
+
*/
|
|
83
|
+
readonly condition?: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Turn a verdict into the finding to report, or null when there is nothing to
|
|
87
|
+
* say. The severity is decided HERE, once, from the verdict — callers never
|
|
88
|
+
* invent their own policy, which is what let `scan` and `lint` drift apart from
|
|
89
|
+
* `compileAgent` before.
|
|
90
|
+
*
|
|
91
|
+
* `noun` names the thing in the message ("hook event" / "tool"); `subject`
|
|
92
|
+
* describes what listing it does, e.g. "a hook here never fires".
|
|
93
|
+
*/
|
|
94
|
+
export declare function termIssue(vocab: HarnessVocabulary, verdict: TermVerdict, noun: string, deadConsequence: string): TermIssue | null;
|
|
95
|
+
/**
|
|
96
|
+
* The issues that count toward a grade. Replaces the per-check
|
|
97
|
+
* `confidentToolIssues` / `confidentHookEventIssues` helpers, which asked "is
|
|
98
|
+
* there a near match?" — a question about spelling, answered by a helper each
|
|
99
|
+
* caller had to remember to apply and which `compileAgent` did not, so `scan`,
|
|
100
|
+
* `lint` and authoring could disagree about which issues were real. Severity now
|
|
101
|
+
* travels ON the issue, decided once in {@link termIssue}, so the split is the
|
|
102
|
+
* same wherever it is taken.
|
|
103
|
+
*/
|
|
104
|
+
export declare function scoredIssues<T extends {
|
|
105
|
+
readonly severity: IssueSeverity;
|
|
106
|
+
}>(issues: readonly T[]): T[];
|
|
107
|
+
/**
|
|
108
|
+
* The issues that are surfaced but never scored — `conditional` tools and any
|
|
109
|
+
* name newer than our capture. Kept out of the grade on purpose: vigiles's own
|
|
110
|
+
* staleness must not cost someone a letter.
|
|
111
|
+
*/
|
|
112
|
+
export declare function advisoryIssues<T extends {
|
|
113
|
+
readonly severity: IssueSeverity;
|
|
114
|
+
}>(issues: readonly T[]): T[];
|
|
115
|
+
/**
|
|
116
|
+
* The issues an AUTHORING path treats as errors — everything except
|
|
117
|
+
* `conditional`. Authoring is a CLOSED world: you are writing this spec now,
|
|
118
|
+
* against the vigiles you have, so an unrecognised name is a typo worth stopping
|
|
119
|
+
* for. Auditing is an OPEN world: someone else wrote the file, possibly against
|
|
120
|
+
* a newer platform, so there the same verdict is only an advisory.
|
|
121
|
+
*
|
|
122
|
+
* `conditional` is an error in NEITHER. The tool is real and declaring it is
|
|
123
|
+
* correct; erroring on it is exactly what told delegating subagents to drop
|
|
124
|
+
* `Agent`, and what made `tools: Agent, Read, Bash` — a worked example in the
|
|
125
|
+
* vendor's own docs — fail to compile.
|
|
126
|
+
*/
|
|
127
|
+
export declare function authoringIssues<T extends {
|
|
128
|
+
readonly verdict: TermVerdict["kind"];
|
|
129
|
+
}>(issues: readonly T[]): T[];
|
|
130
|
+
/**
|
|
131
|
+
* Build a vocabulary from a dialect that predates this module — `available` from
|
|
132
|
+
* its built-in catalog, `withheld` from its never-available list. A dialect on
|
|
133
|
+
* the legacy shape keeps working and its unknowns become `unrecognised`
|
|
134
|
+
* ADVISORIES rather than silence, which is the honest reading: a catalog with no
|
|
135
|
+
* recorded capture cannot claim a name is invalid.
|
|
136
|
+
*/
|
|
137
|
+
export declare function vocabularyFromLists(kind: string, capturedFrom: string, available: readonly string[], withheld?: readonly string[]): HarnessVocabulary;
|
|
138
|
+
//# sourceMappingURL=vocabulary.d.ts.map
|