vigiles 33.0.0 → 33.2.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.js +14 -10
- package/dist/adapter.d.ts +2 -0
- package/dist/adapters/claude-code/layout.js +3 -0
- package/dist/adapters/claude-code/output-style.d.ts +3 -0
- package/dist/adapters/claude-code/output-style.js +81 -0
- package/dist/adapters/claude-code/replies.d.ts +14 -0
- package/dist/adapters/claude-code/replies.js +78 -0
- package/dist/audit-report.d.ts +5 -0
- package/dist/audit-report.js +29 -20
- package/dist/audit-report.template.html +25 -25
- package/dist/audit-score.js +2 -2
- package/dist/check.d.ts +20 -0
- package/dist/check.js +87 -2
- package/dist/cli-main.js +25 -13
- package/dist/core/config-schema.d.ts +9 -0
- package/dist/core/config-schema.js +1 -0
- package/dist/core/eval-driver.d.ts +10 -0
- package/dist/core/frame.d.ts +1 -1
- package/dist/core/frontmatter-read.d.ts +31 -0
- package/dist/core/frontmatter-read.js +4 -2
- package/dist/core/harness-driver.d.ts +2 -0
- package/dist/core/layout.d.ts +7 -0
- package/dist/core/output-style.d.ts +84 -0
- package/dist/core/output-style.js +90 -0
- package/dist/core/rule-meta.d.ts +1 -1
- package/dist/core/rule-meta.js +8 -0
- package/dist/core/types.d.ts +2 -0
- package/dist/coverage-evidence.d.ts +42 -0
- package/dist/coverage-evidence.js +39 -0
- package/dist/eval.d.ts +26 -3
- package/dist/eval.js +54 -5
- package/dist/harness-test.d.ts +30 -1
- package/dist/harness-test.js +65 -15
- package/dist/optimize.js +1 -5
- package/dist/output-style-arms.d.ts +27 -0
- package/dist/output-style-arms.js +46 -0
- package/dist/scan-files.js +1 -1
- package/dist/scan.d.ts +6 -0
- package/dist/scan.js +14 -4
- package/dist/score-core.d.ts +3 -0
- package/dist/score-core.js +5 -1
- package/dist/setup-plan.d.ts +1 -1
- package/dist/setup-plan.js +1 -0
- package/dist/test-coverage-files.d.ts +3 -1
- package/dist/test-coverage-files.js +20 -6
- package/dist/test-coverage.d.ts +18 -1
- package/dist/test-coverage.js +64 -14
- package/dist/test.d.ts +3 -1
- package/dist/test.js +8 -2
- package/dist/vigilesrc.schema.json +134 -0
- package/package.json +6 -3
- package/packages/eslint-config/README.md +17 -0
- package/packages/eslint-config/index.mjs +344 -0
|
@@ -21,6 +21,19 @@ const event_capability_js_1 = require("./core/event-capability.js");
|
|
|
21
21
|
const dialect_js_1 = require("./core/dialect.js");
|
|
22
22
|
const layout_js_1 = require("./core/layout.js");
|
|
23
23
|
const tmp_root_js_1 = require("./core/tmp-root.js");
|
|
24
|
+
/** Every optional path a layout names, with the field it sits in. */
|
|
25
|
+
function optionalPaths(layout) {
|
|
26
|
+
return [
|
|
27
|
+
["rulesDir", layout.rulesDir],
|
|
28
|
+
["outputStyles.dir", layout.outputStyles?.dir],
|
|
29
|
+
["hookScriptsDir", layout.hookScriptsDir],
|
|
30
|
+
["hooksConventionPath", layout.hooksConventionPath],
|
|
31
|
+
["userSurfaceRoot", layout.userSurfaceRoot],
|
|
32
|
+
["surfaces.skill", layout.surfaces.skill],
|
|
33
|
+
["surfaces.agent", layout.surfaces.agent],
|
|
34
|
+
["surfaces.command", layout.surfaces.command],
|
|
35
|
+
];
|
|
36
|
+
}
|
|
24
37
|
/** Check an adapter against the port contracts; returns the (possibly empty) failure list. */
|
|
25
38
|
function checkAdapterConformance(adapter) {
|
|
26
39
|
const failures = [];
|
|
@@ -76,16 +89,7 @@ function checkAdapterConformance(adapter) {
|
|
|
76
89
|
// second spelling of absent" rule is a test for every optional path. An
|
|
77
90
|
// absent key already means "this harness has no such thing"; `""` would be a
|
|
78
91
|
// second one, and every reader would have to remember to test for both.
|
|
79
|
-
const
|
|
80
|
-
["rulesDir", adapter.layout.rulesDir],
|
|
81
|
-
["hookScriptsDir", adapter.layout.hookScriptsDir],
|
|
82
|
-
["hooksConventionPath", adapter.layout.hooksConventionPath],
|
|
83
|
-
["userSurfaceRoot", adapter.layout.userSurfaceRoot],
|
|
84
|
-
["surfaces.skill", adapter.layout.surfaces.skill],
|
|
85
|
-
["surfaces.agent", adapter.layout.surfaces.agent],
|
|
86
|
-
["surfaces.command", adapter.layout.surfaces.command],
|
|
87
|
-
];
|
|
88
|
-
for (const [field, value] of optionalPaths)
|
|
92
|
+
for (const [field, value] of optionalPaths(adapter.layout))
|
|
89
93
|
need(value === undefined || value.length > 0, `layout.${field} is "" — absence is spelled by omitting the key, never by an empty string`);
|
|
90
94
|
// A7: `hooksConventionPath` names a FILE, and every reader treats it as one
|
|
91
95
|
// (dirname it, parse it, round-trip it). `opencodeLayout` used to name the
|
package/dist/adapter.d.ts
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
export type { HarnessAdapter, AdapterCapabilities } from "./core/adapter.js";
|
|
19
19
|
export type { HarnessDialect } from "./core/dialect.js";
|
|
20
20
|
export type { PluginLayout, SurfaceDirs, SurfaceKind } from "./core/layout.js";
|
|
21
|
+
export type { OutputStyle, OutputStyleRules } from "./core/output-style.js";
|
|
22
|
+
export type { ModelRequest } from "./core/harness-driver.js";
|
|
21
23
|
export { executableSourceDirs, materializePrefix, surfaceDirs, } from "./core/layout.js";
|
|
22
24
|
/**
|
|
23
25
|
* The instruction-chain vocabulary `PluginLayout.instructionChain` speaks.
|
|
@@ -15,6 +15,7 @@ const layout_js_1 = require("../../core/layout.js");
|
|
|
15
15
|
const instruction_chain_js_1 = require("../../core/instruction-chain.js");
|
|
16
16
|
const settings_codec_js_1 = require("../../core/settings-codec.js");
|
|
17
17
|
const instruction_chain_js_2 = require("./instruction-chain.js");
|
|
18
|
+
const output_style_js_1 = require("./output-style.js");
|
|
18
19
|
exports.claudeCodeLayout = {
|
|
19
20
|
name: "claude-code",
|
|
20
21
|
manifestPath: ".claude-plugin/plugin.json",
|
|
@@ -30,6 +31,8 @@ exports.claudeCodeLayout = {
|
|
|
30
31
|
userSurfaceRoot: ".claude",
|
|
31
32
|
// `.claude/rules/*.md` — path-scoped project instructions (see PluginLayout).
|
|
32
33
|
rulesDir: "rules",
|
|
34
|
+
// `.claude/output-styles/**/*.md` — see output-style.ts for how a style is named and selected.
|
|
35
|
+
outputStyles: output_style_js_1.claudeCodeOutputStyles,
|
|
33
36
|
// `.claude/settings.local.json` — the per-machine layer, gitignored by
|
|
34
37
|
// `init`. Declared because only this adapter knows the word; Codex and
|
|
35
38
|
// OpenCode name none, so none is synthesized for them.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.claudeCodeOutputStyles = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* How Claude Code keeps output styles and switches one on. Every rule here was
|
|
6
|
+
* measured on Claude Code 2.1.291; the tests beside this file hold the cases.
|
|
7
|
+
*/
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
|
+
// `yaml` is the lazy loader: this module hangs off the layout, which is on the
|
|
10
|
+
// hook path, so a top-level `js-yaml` import would load into every hook decision.
|
|
11
|
+
const frontmatter_read_js_1 = require("../../core/frontmatter-read.js");
|
|
12
|
+
/** Claude Code puts this line, then the body, into the first user message. */
|
|
13
|
+
const HEADER = "# Output Style: ";
|
|
14
|
+
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
15
|
+
/** YAML as Claude Code reads it: the core schema, and a repeated key keeps the last value. */
|
|
16
|
+
const parse = (block) => {
|
|
17
|
+
try {
|
|
18
|
+
const { CORE_SCHEMA, load } = (0, frontmatter_read_js_1.yaml)();
|
|
19
|
+
const value = load(block, { schema: CORE_SCHEMA, json: true });
|
|
20
|
+
if (value === null || value === undefined)
|
|
21
|
+
return {};
|
|
22
|
+
return isRecord(value) ? value : null;
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
if (e instanceof (0, frontmatter_read_js_1.yaml)().YAMLException)
|
|
26
|
+
return null;
|
|
27
|
+
throw e;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
/** `name: Status: Block` is not YAML, but Claude Code takes the rest of the line. */
|
|
31
|
+
const quoteColonValues = (block) => block
|
|
32
|
+
.split("\n")
|
|
33
|
+
.map((line) => {
|
|
34
|
+
const m = /^([A-Za-z0-9_-]+): (.*)$/.exec(line.replace(/\r$/, ""));
|
|
35
|
+
if (m === null)
|
|
36
|
+
return line;
|
|
37
|
+
const [, key = "", value = ""] = m;
|
|
38
|
+
const needsQuotes = /: |:$/.test(value) && !/^["'[{|>]/.test(value);
|
|
39
|
+
return needsQuotes ? `${key}: ${JSON.stringify(value)}` : line;
|
|
40
|
+
})
|
|
41
|
+
.join("\n");
|
|
42
|
+
const readBlock = (block) => parse(block) ?? parse(quoteColonValues(block));
|
|
43
|
+
/** A declared name if there is one, else the file name; null when unknowable. */
|
|
44
|
+
const nameFrom = (text, stem) => {
|
|
45
|
+
const { block } = (0, frontmatter_read_js_1.readFrontmatter)(text);
|
|
46
|
+
if (block === null)
|
|
47
|
+
return stem;
|
|
48
|
+
const data = readBlock(block);
|
|
49
|
+
if (data === null)
|
|
50
|
+
return null;
|
|
51
|
+
const name = data["name"];
|
|
52
|
+
if (typeof name === "string")
|
|
53
|
+
return name === "" ? stem : name;
|
|
54
|
+
if (typeof name === "number" || typeof name === "boolean")
|
|
55
|
+
return String(name);
|
|
56
|
+
return name === null || name === undefined ? stem : null;
|
|
57
|
+
};
|
|
58
|
+
const squash = (s) => s.replace(/\s+/g, " ").trim();
|
|
59
|
+
const read = (path, text) => ({
|
|
60
|
+
path,
|
|
61
|
+
name: nameFrom(text, (0, node_path_1.basename)(path).replace(/\.md$/, "")),
|
|
62
|
+
body: (0, frontmatter_read_js_1.frontmatterBody)(text).trim(),
|
|
63
|
+
});
|
|
64
|
+
/** The header and the whole body, in the system prompt or in any message. */
|
|
65
|
+
const reached = (style, request) => {
|
|
66
|
+
if (style.name === null)
|
|
67
|
+
return false;
|
|
68
|
+
const probe = squash(`${HEADER}${style.name}\n${style.body}`);
|
|
69
|
+
return [request.system, ...request.messages.map((m) => m.text)].some((text) => squash(text).includes(probe));
|
|
70
|
+
};
|
|
71
|
+
exports.claudeCodeOutputStyles = {
|
|
72
|
+
dir: "output-styles",
|
|
73
|
+
isStyleFile: (pathInDir) => pathInDir.endsWith(".md"),
|
|
74
|
+
read,
|
|
75
|
+
select: (name) => ({ outputStyle: name }),
|
|
76
|
+
// A user-wide `outputStyle` in `~/.claude/settings.json` loads in every
|
|
77
|
+
// project; the project's own `default` wins over it (measured on 2.1.292).
|
|
78
|
+
selectNone: { outputStyle: "default" },
|
|
79
|
+
reached,
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=output-style.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Whether the model answered at all: any `assistant` event, subagents included. */
|
|
2
|
+
export declare function reachedModel(streamJson: string): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* The main agent's replies, one per message that ended a turn. A message
|
|
5
|
+
* streamed as several events (one per content block) is joined under its
|
|
6
|
+
* `message.id`. Left out: a subagent's messages (a non-null
|
|
7
|
+
* `parent_tool_use_id`); a message that also calls a tool, whose text is
|
|
8
|
+
* narration on the way to the call (measured on Claude Code 2.1.292: the
|
|
9
|
+
* narration and the `tool_use` share one id); a message with no text.
|
|
10
|
+
* `undefined` when the output is not a stream, so a check can say the run
|
|
11
|
+
* was not streamed instead of reading "no replies".
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseReplies(streamJson: string): readonly string[] | undefined;
|
|
14
|
+
//# sourceMappingURL=replies.d.ts.map
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reachedModel = reachedModel;
|
|
4
|
+
exports.parseReplies = parseReplies;
|
|
5
|
+
/**
|
|
6
|
+
* Every reply the agent ended a turn with, in order — read off Claude Code's
|
|
7
|
+
* `--output-format stream-json`. The `result` event carries only the LAST one,
|
|
8
|
+
* so a reply a Stop hook made the agent follow up on was invisible to a test,
|
|
9
|
+
* and with it anything that reply printed (a second status footer, measured on
|
|
10
|
+
* Claude Code 2.1.291: two `assistant` events, one `result` holding the second).
|
|
11
|
+
*/
|
|
12
|
+
const zod_1 = require("zod");
|
|
13
|
+
/** The parts of an `assistant` stream line this reads; the rest is ignored. */
|
|
14
|
+
const AssistantLineSchema = zod_1.z.object({
|
|
15
|
+
type: zod_1.z.literal("assistant"),
|
|
16
|
+
parent_tool_use_id: zod_1.z.string().nullish(),
|
|
17
|
+
message: zod_1.z.object({
|
|
18
|
+
id: zod_1.z.string().optional(),
|
|
19
|
+
content: zod_1.z.array(zod_1.z.unknown()),
|
|
20
|
+
}),
|
|
21
|
+
});
|
|
22
|
+
const TextBlock = zod_1.z.object({ type: zod_1.z.literal("text"), text: zod_1.z.string() });
|
|
23
|
+
const textOf = (content) => content
|
|
24
|
+
.map((block) => TextBlock.safeParse(block))
|
|
25
|
+
.flatMap((r) => (r.success ? [r.data.text] : []))
|
|
26
|
+
.join("");
|
|
27
|
+
const parseJson = (line) => {
|
|
28
|
+
try {
|
|
29
|
+
return JSON.parse(line);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const parseLine = (line) => {
|
|
36
|
+
const r = AssistantLineSchema.safeParse(parseJson(line));
|
|
37
|
+
return r.success ? r.data : null;
|
|
38
|
+
};
|
|
39
|
+
/** A stream-json run opens with a `system` event; plain `json` output has none. */
|
|
40
|
+
const StreamStartSchema = zod_1.z.object({ type: zod_1.z.literal("system") });
|
|
41
|
+
const isStream = (lines) => lines.some((l) => StreamStartSchema.safeParse(parseJson(l)).success ||
|
|
42
|
+
parseLine(l) !== null);
|
|
43
|
+
const ToolUseBlock = zod_1.z.object({ type: zod_1.z.literal("tool_use") });
|
|
44
|
+
/** Whether the model answered at all: any `assistant` event, subagents included. */
|
|
45
|
+
function reachedModel(streamJson) {
|
|
46
|
+
return streamJson.split("\n").some((l) => parseLine(l) !== null);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The main agent's replies, one per message that ended a turn. A message
|
|
50
|
+
* streamed as several events (one per content block) is joined under its
|
|
51
|
+
* `message.id`. Left out: a subagent's messages (a non-null
|
|
52
|
+
* `parent_tool_use_id`); a message that also calls a tool, whose text is
|
|
53
|
+
* narration on the way to the call (measured on Claude Code 2.1.292: the
|
|
54
|
+
* narration and the `tool_use` share one id); a message with no text.
|
|
55
|
+
* `undefined` when the output is not a stream, so a check can say the run
|
|
56
|
+
* was not streamed instead of reading "no replies".
|
|
57
|
+
*/
|
|
58
|
+
function parseReplies(streamJson) {
|
|
59
|
+
const lines = streamJson.split("\n");
|
|
60
|
+
if (!isStream(lines))
|
|
61
|
+
return undefined;
|
|
62
|
+
const events = lines.map(parseLine).flatMap((e, i) => e !== null && (e.parent_tool_use_id ?? null) === null
|
|
63
|
+
? [
|
|
64
|
+
{
|
|
65
|
+
id: e.message.id ?? `#${String(i)}`,
|
|
66
|
+
text: textOf(e.message.content),
|
|
67
|
+
callsTool: e.message.content.some((b) => ToolUseBlock.safeParse(b).success),
|
|
68
|
+
},
|
|
69
|
+
]
|
|
70
|
+
: []);
|
|
71
|
+
const ids = [...new Set(events.map((e) => e.id))];
|
|
72
|
+
return ids
|
|
73
|
+
.map((id) => events.filter((e) => e.id === id))
|
|
74
|
+
.filter((parts) => !parts.some((e) => e.callsTool))
|
|
75
|
+
.map((parts) => parts.map((e) => e.text).join(""))
|
|
76
|
+
.filter((text) => text !== "");
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=replies.js.map
|
package/dist/audit-report.d.ts
CHANGED
|
@@ -55,6 +55,11 @@ export interface AuditInventory {
|
|
|
55
55
|
readonly agents: number;
|
|
56
56
|
readonly hooks: number;
|
|
57
57
|
readonly commands: number;
|
|
58
|
+
/**
|
|
59
|
+
* Output styles found, or `"not-supported"` when the harness has none.
|
|
60
|
+
* Omitted when there are zero, so a report without styles is unchanged.
|
|
61
|
+
*/
|
|
62
|
+
readonly outputStyles?: number | "not-supported";
|
|
58
63
|
readonly mcp: boolean;
|
|
59
64
|
/** Surfaces covered by NEITHER tier — the union count (unchanged). */
|
|
60
65
|
readonly untested: number;
|
package/dist/audit-report.js
CHANGED
|
@@ -46,6 +46,34 @@ function buildAdoptable(surfaces) {
|
|
|
46
46
|
createAllCommand: CREATE_ALL_COMMAND,
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
+
/** What the scanned harness ships, as the report's `inventory`. */
|
|
50
|
+
function inventoryOf(report) {
|
|
51
|
+
return {
|
|
52
|
+
skills: report.skills.length,
|
|
53
|
+
agents: report.agents.length,
|
|
54
|
+
// All hooks, file-backed + inline — matches formatScanReport and the
|
|
55
|
+
// emptiness/scoring count, so a JSON/HTML "What it ships" never reports 0
|
|
56
|
+
// hooks for an inline-hook-only harness.
|
|
57
|
+
hooks: report.hooks.length + report.inlineHooks,
|
|
58
|
+
commands: report.commands,
|
|
59
|
+
...outputStylesField(report.outputStyles),
|
|
60
|
+
mcp: report.mcp,
|
|
61
|
+
untested: report.untested,
|
|
62
|
+
...(report.untestedHarness !== undefined
|
|
63
|
+
? { untestedHarness: report.untestedHarness }
|
|
64
|
+
: {}),
|
|
65
|
+
...(report.unevaluated !== undefined
|
|
66
|
+
? { unevaluated: report.unevaluated }
|
|
67
|
+
: {}),
|
|
68
|
+
...(report.coverageEvidence
|
|
69
|
+
? { coverageEvidence: report.coverageEvidence }
|
|
70
|
+
: {}),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** The inventory's `outputStyles` entry: present unless there are none to report. */
|
|
74
|
+
function outputStylesField(n) {
|
|
75
|
+
return n === undefined || n === 0 ? {} : { outputStyles: n };
|
|
76
|
+
}
|
|
49
77
|
/**
|
|
50
78
|
* Assemble the versioned {@link AuditReport} from a scan report — pure, no clock.
|
|
51
79
|
* The CLI attaches `meta.generatedAt` when it writes the JSON artifact; the
|
|
@@ -68,26 +96,7 @@ function buildAuditReport(report, opts) {
|
|
|
68
96
|
score,
|
|
69
97
|
verdict,
|
|
70
98
|
recommendations,
|
|
71
|
-
inventory:
|
|
72
|
-
skills: report.skills.length,
|
|
73
|
-
agents: report.agents.length,
|
|
74
|
-
// All hooks, file-backed + inline — matches formatScanReport and the
|
|
75
|
-
// emptiness/scoring count, so a JSON/HTML "What it ships" never reports 0
|
|
76
|
-
// hooks for an inline-hook-only harness.
|
|
77
|
-
hooks: report.hooks.length + report.inlineHooks,
|
|
78
|
-
commands: report.commands,
|
|
79
|
-
mcp: report.mcp,
|
|
80
|
-
untested: report.untested,
|
|
81
|
-
...(report.untestedHarness !== undefined
|
|
82
|
-
? { untestedHarness: report.untestedHarness }
|
|
83
|
-
: {}),
|
|
84
|
-
...(report.unevaluated !== undefined
|
|
85
|
-
? { unevaluated: report.unevaluated }
|
|
86
|
-
: {}),
|
|
87
|
-
...(report.coverageEvidence
|
|
88
|
-
? { coverageEvidence: report.coverageEvidence }
|
|
89
|
-
: {}),
|
|
90
|
-
},
|
|
99
|
+
inventory: inventoryOf(report),
|
|
91
100
|
...(report.danglingRefs.length
|
|
92
101
|
? { brokenReferences: report.danglingRefs }
|
|
93
102
|
: {}),
|