vigiles 30.0.2 → 31.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.js +9 -15
- package/dist/claude-code.js +5 -0
- package/dist/cli-main.js +21 -23
- package/dist/codex.js +5 -0
- package/dist/core/compile.d.ts +5 -5
- package/dist/core/compile.js +28 -25
- package/dist/core/config-schema.d.ts +32 -32
- package/dist/core/refs.d.ts +2 -2
- package/dist/core/refs.js +9 -12
- package/dist/core/spec.js +5 -0
- package/dist/core/symbols.d.ts +25 -19
- package/dist/core/symbols.js +51 -74
- package/dist/core/tree-sitter-wasm.d.ts +24 -0
- package/dist/core/tree-sitter-wasm.js +128 -0
- package/dist/eval-surface.js +5 -0
- package/dist/hook-install.js +23 -10
- package/dist/hook.js +5 -0
- package/dist/jest.js +5 -0
- package/dist/linting.d.ts +11 -30
- package/dist/linting.js +22 -35
- package/dist/test.js +5 -0
- package/dist/vitest.mjs +5 -0
- package/package.json +2 -6
package/dist/core/refs.js
CHANGED
|
@@ -70,11 +70,11 @@ function symbolRefs(markdown) {
|
|
|
70
70
|
* file must exist and define the named symbol. `basePath` is the directory the
|
|
71
71
|
* paths resolve against (the instruction file's own directory).
|
|
72
72
|
*/
|
|
73
|
-
function verifySymbolRefs(markdown, basePath) {
|
|
73
|
+
async function verifySymbolRefs(markdown, basePath) {
|
|
74
74
|
const errors = [];
|
|
75
75
|
for (const ref of symbolRefs(markdown)) {
|
|
76
76
|
const full = (0, node_path_1.resolve)(basePath, ref.file);
|
|
77
|
-
const support = (0, symbols_js_1.langForFile)(ref.file);
|
|
77
|
+
const support = await (0, symbols_js_1.langForFile)(ref.file);
|
|
78
78
|
if (!(0, node_fs_1.existsSync)(full)) {
|
|
79
79
|
errors.push({ ...ref, reason: `File not found: "${ref.file}"` });
|
|
80
80
|
}
|
|
@@ -84,15 +84,12 @@ function verifySymbolRefs(markdown, basePath) {
|
|
|
84
84
|
reason: `Unsupported language for symbol check: "${ref.file}"`,
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
|
-
else if (support.kind === "grammar-
|
|
88
|
-
// NOT "unsupported": the language is one this tool parses,
|
|
89
|
-
//
|
|
90
|
-
errors.push({
|
|
91
|
-
...ref,
|
|
92
|
-
reason: `Symbol not checked: the ${support.id} grammar is not installed (npm i -D ${support.pkg})`,
|
|
93
|
-
});
|
|
87
|
+
else if (support.kind === "grammar-load-failed") {
|
|
88
|
+
// NOT "unsupported" and NOT "not defined": the language is one this tool parses, its
|
|
89
|
+
// grammar failed to load here. Either other wording would report an un-run check as a verdict.
|
|
90
|
+
errors.push({ ...ref, reason: (0, symbols_js_1.notCheckedReason)(support) });
|
|
94
91
|
}
|
|
95
|
-
else if (!(0, symbols_js_1.fileDefinesSymbol)(full, ref.symbol)) {
|
|
92
|
+
else if (!(await (0, symbols_js_1.fileDefinesSymbol)(full, ref.symbol))) {
|
|
96
93
|
errors.push({
|
|
97
94
|
...ref,
|
|
98
95
|
reason: `"${ref.symbol}" is not defined in ${ref.file}`,
|
|
@@ -149,9 +146,9 @@ function unmarkedCodeRefs(markdown) {
|
|
|
149
146
|
* code-shaped span that ought to be a mark. The shared detector behind both the
|
|
150
147
|
* `vigiles refs` CLI and the PostToolUse refs-hook.
|
|
151
148
|
*/
|
|
152
|
-
function collectRefIssues(markdown, basePath) {
|
|
149
|
+
async function collectRefIssues(markdown, basePath) {
|
|
153
150
|
const out = [];
|
|
154
|
-
for (const b of verifySymbolRefs(markdown, basePath)) {
|
|
151
|
+
for (const b of await verifySymbolRefs(markdown, basePath)) {
|
|
155
152
|
out.push(`line ${String(b.line)}: ${b.reason}`);
|
|
156
153
|
}
|
|
157
154
|
for (const u of unmarkedCodeRefs(markdown)) {
|
package/dist/core/spec.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// 🔴 PUBLIC ENTRY POINT `vigiles/spec` — every export here is a promise to users. The default for a
|
|
3
|
+
// symbol is INTERNAL. It is exported only if (a) a NAMED external consumer uses it, or (b) it is
|
|
4
|
+
// a deliberate extension point listed in STABILITY.md (the adapter kit is the example). "Might
|
|
5
|
+
// be useful" is neither. Review point: the diff of `api-surface/vigiles-spec.api.md`, which
|
|
6
|
+
// `npm run api:check` fails on.
|
|
2
7
|
/**
|
|
3
8
|
* vigiles v2 — Executable specification system.
|
|
4
9
|
*
|
package/dist/core/symbols.d.ts
CHANGED
|
@@ -1,30 +1,33 @@
|
|
|
1
1
|
import { Lang } from "@ast-grep/napi";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
type LangKey = Lang | string;
|
|
2
|
+
import { type WasmLang } from "./tree-sitter-wasm.js";
|
|
3
|
+
/** A language key: an `@ast-grep/napi` built-in, or one of the WASM grammars. */
|
|
4
|
+
type LangKey = Lang | WasmLang;
|
|
6
5
|
/**
|
|
7
6
|
* Whether this file's language can be parsed HERE, and if not, which of the two reasons.
|
|
8
7
|
*
|
|
9
|
-
* 🔴 THE THREE CASES ARE SEPARATE MEMBERS BECAUSE THEY ARE SEPARATE FACTS.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
8
|
+
* 🔴 THE THREE CASES ARE SEPARATE MEMBERS BECAUSE THEY ARE SEPARATE FACTS. "Extension not in
|
|
9
|
+
* the table" and "the language IS ours but its grammar failed to load in this process" must not
|
|
10
|
+
* share one `null`: both callers would print the first sentence, and a check that did not run
|
|
11
|
+
* would read as a verdict. With WASM grammars shipped as a regular dependency there is no
|
|
12
|
+
* install or platform gap left, so `grammar-load-failed` should not happen — but if the runtime
|
|
13
|
+
* genuinely cannot load, the report carries the loader's own error text, never "not defined"
|
|
14
|
+
* and never an install instruction the user cannot act on.
|
|
16
15
|
*/
|
|
17
16
|
export type LangSupport = {
|
|
18
17
|
readonly kind: "ready";
|
|
19
18
|
readonly lang: LangKey;
|
|
20
19
|
} | {
|
|
21
|
-
readonly kind: "grammar-
|
|
22
|
-
readonly
|
|
23
|
-
readonly
|
|
20
|
+
readonly kind: "grammar-load-failed";
|
|
21
|
+
readonly lang: WasmLang;
|
|
22
|
+
readonly error: string;
|
|
24
23
|
} | {
|
|
25
24
|
readonly kind: "unsupported";
|
|
26
25
|
};
|
|
27
|
-
export declare function langForFile(file: string): LangSupport
|
|
26
|
+
export declare function langForFile(file: string): Promise<LangSupport>;
|
|
27
|
+
/** The one sentence both callers print for a symbol reference that could not be checked. */
|
|
28
|
+
export declare function notCheckedReason(support: Extract<LangSupport, {
|
|
29
|
+
kind: "grammar-load-failed";
|
|
30
|
+
}>): string;
|
|
28
31
|
/** A symbol definition found in a file. */
|
|
29
32
|
export interface SymbolDef {
|
|
30
33
|
/** The defined identifier, e.g. "parseConfig". */
|
|
@@ -36,10 +39,13 @@ export interface SymbolDef {
|
|
|
36
39
|
/** 1-based line of the definition. */
|
|
37
40
|
readonly line: number;
|
|
38
41
|
}
|
|
39
|
-
/**
|
|
40
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Extract the symbols defined in a single file's source. Async because the Python / Ruby / Rust
|
|
44
|
+
* grammars are WebAssembly, whose instantiation is async; the napi languages resolve at once.
|
|
45
|
+
*/
|
|
46
|
+
export declare function definedSymbols(code: string, lang: LangKey): Promise<SymbolDef[]>;
|
|
41
47
|
/** Defined symbols for a file on disk, or [] if unreadable/unsupported. */
|
|
42
|
-
export declare function definedSymbolsInFile(file: string): SymbolDef[]
|
|
48
|
+
export declare function definedSymbolsInFile(file: string): Promise<SymbolDef[]>;
|
|
43
49
|
/**
|
|
44
50
|
* Whether `file` defines a top-level (or scoped) symbol named `name`. This is
|
|
45
51
|
* the whole check for a file-qualified reference (`path#symbol`): we parse the
|
|
@@ -47,6 +53,6 @@ export declare function definedSymbolsInFile(file: string): SymbolDef[];
|
|
|
47
53
|
* fallback we also consult a co-located declaration file (`.rbi` / `.d.ts`), so
|
|
48
54
|
* typed dynamic symbols resolve without running Sorbet / the TS compiler.
|
|
49
55
|
*/
|
|
50
|
-
export declare function fileDefinesSymbol(file: string, name: string): boolean
|
|
56
|
+
export declare function fileDefinesSymbol(file: string, name: string): Promise<boolean>;
|
|
51
57
|
export {};
|
|
52
58
|
//# sourceMappingURL=symbols.d.ts.map
|
package/dist/core/symbols.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.installedGrammars = installedGrammars;
|
|
4
3
|
exports.langForFile = langForFile;
|
|
4
|
+
exports.notCheckedReason = notCheckedReason;
|
|
5
5
|
exports.definedSymbols = definedSymbols;
|
|
6
6
|
exports.definedSymbolsInFile = definedSymbolsInFile;
|
|
7
7
|
exports.fileDefinesSymbol = fileDefinesSymbol;
|
|
@@ -21,60 +21,32 @@ exports.fileDefinesSymbol = fileDefinesSymbol;
|
|
|
21
21
|
* delegated. Ambiguity (a name defined in several files) is reported, not guessed.
|
|
22
22
|
*/
|
|
23
23
|
const node_fs_1 = require("node:fs");
|
|
24
|
-
const
|
|
24
|
+
const promises_1 = require("node:fs/promises");
|
|
25
25
|
const node_path_1 = require("node:path");
|
|
26
26
|
const napi_1 = require("@ast-grep/napi");
|
|
27
|
+
const tree_sitter_wasm_js_1 = require("./tree-sitter-wasm.js");
|
|
27
28
|
/**
|
|
28
|
-
*
|
|
29
|
+
* Python, Ruby and Rust are parsed by WebAssembly builds of their tree-sitter grammars
|
|
30
|
+
* (`./tree-sitter-wasm.ts`); TypeScript, TSX, JavaScript and CSS by the grammars built into
|
|
31
|
+
* `@ast-grep/napi`.
|
|
29
32
|
*
|
|
30
|
-
* 🔴 WHY
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* grammars
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* CommonJS (`"main": "index.js"`, no `exports`), so a synchronous require works and NOTHING in
|
|
40
|
-
* this module's public surface has to become async. Measured, not assumed.
|
|
33
|
+
* 🔴 WHY NOT `@ast-grep/lang-*` ANY MORE (#257). Those native grammars were the only packages in
|
|
34
|
+
* the tree carrying a `postinstall`, and since pnpm 10 an unapproved dependency build script
|
|
35
|
+
* FAILS a consumer's install (`ERR_PNPM_IGNORED_BUILDS`) — for every `pnpm add vigiles`, whether
|
|
36
|
+
* or not the project has a single `.py` file. Making them optional peers stopped the failure but
|
|
37
|
+
* moved the cost onto the user: a `.py` reference then reported "grammar not installed" after an
|
|
38
|
+
* upgrade. The WASM grammars are a plain dependency with no install script and no native
|
|
39
|
+
* binary, so they are simply there. `src/package-install-scripts.e2e.test.ts` holds both halves
|
|
40
|
+
* against the packed tarball: no install script in the tree, and a `.py` lookup that works
|
|
41
|
+
* right after a default install.
|
|
41
42
|
*/
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// mistake that made every hook fail there — and these grammars are OUR optional dependencies,
|
|
50
|
-
// so they resolve from where this file lives. `__filename` rather than `import.meta.url`
|
|
51
|
-
// because this package compiles to CommonJS (`module: Node16`, `main: ./dist/test.js`).
|
|
52
|
-
const require_ = (0, node_module_1.createRequire)(__filename);
|
|
53
|
-
/** Registered grammar ids, populated on first use. `null` until then. */
|
|
54
|
-
let loaded = null;
|
|
55
|
-
function ensureRegistered() {
|
|
56
|
-
if (loaded)
|
|
57
|
-
return loaded;
|
|
58
|
-
const dynamic = {};
|
|
59
|
-
const present = new Set();
|
|
60
|
-
for (const [id, pkg] of Object.entries(OPTIONAL_GRAMMARS)) {
|
|
61
|
-
try {
|
|
62
|
-
dynamic[id] = require_(pkg);
|
|
63
|
-
present.add(id);
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
// Absent by design: an optional dependency the consumer did not install. The caller is
|
|
67
|
-
// told WHICH id is missing (see `langForFile`), so "not checked" never reads as "clean".
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
if (present.size > 0)
|
|
71
|
-
(0, napi_1.registerDynamicLanguage)(dynamic);
|
|
72
|
-
loaded = present;
|
|
73
|
-
return loaded;
|
|
74
|
-
}
|
|
75
|
-
/** Which optional grammars this process actually has. Exported so a report can say so. */
|
|
76
|
-
function installedGrammars() {
|
|
77
|
-
return ensureRegistered();
|
|
43
|
+
const WASM_LANGS = new Set([
|
|
44
|
+
"python",
|
|
45
|
+
"ruby",
|
|
46
|
+
"rust",
|
|
47
|
+
]);
|
|
48
|
+
function isWasmLang(key) {
|
|
49
|
+
return typeof key === "string" && WASM_LANGS.has(key);
|
|
78
50
|
}
|
|
79
51
|
const EXT_LANG = {
|
|
80
52
|
".ts": napi_1.Lang.TypeScript,
|
|
@@ -93,28 +65,26 @@ const EXT_LANG = {
|
|
|
93
65
|
".rb": "ruby",
|
|
94
66
|
".rbi": "ruby",
|
|
95
67
|
};
|
|
96
|
-
function langForFile(file) {
|
|
68
|
+
async function langForFile(file) {
|
|
97
69
|
const key = file.endsWith(".d.ts")
|
|
98
70
|
? napi_1.Lang.TypeScript
|
|
99
71
|
: EXT_LANG[(0, node_path_1.extname)(file).toLowerCase()];
|
|
100
72
|
if (key === undefined)
|
|
101
73
|
return { kind: "unsupported" };
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
74
|
+
if (isWasmLang(key)) {
|
|
75
|
+
// Lazy: the first .py/.rb/.rs reference starts the runtime; later ones hit the cache.
|
|
76
|
+
const error = await (0, tree_sitter_wasm_js_1.loadWasmGrammar)(key);
|
|
77
|
+
if (error !== null)
|
|
78
|
+
return { kind: "grammar-load-failed", lang: key, error };
|
|
106
79
|
}
|
|
107
80
|
return { kind: "ready", lang: key };
|
|
108
81
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
"interface_declaration",
|
|
116
|
-
"enum_declaration",
|
|
117
|
-
]);
|
|
82
|
+
/** The one sentence both callers print for a symbol reference that could not be checked. */
|
|
83
|
+
function notCheckedReason(support) {
|
|
84
|
+
return `Symbol not checked: the ${support.lang} grammar failed to load: ${support.error}`;
|
|
85
|
+
}
|
|
86
|
+
const ID_KINDS = new Set(tree_sitter_wasm_js_1.ID_KINDS);
|
|
87
|
+
const SCOPE_KINDS = new Set(tree_sitter_wasm_js_1.SCOPE_KINDS);
|
|
118
88
|
function recordNode(node, scope, out) {
|
|
119
89
|
const line = node.range().start.line + 1;
|
|
120
90
|
const nameNode = node.field("name");
|
|
@@ -128,9 +98,13 @@ function recordNode(node, scope, out) {
|
|
|
128
98
|
out.push({ name: left.text(), kind: node.kind(), scope, line });
|
|
129
99
|
}
|
|
130
100
|
}
|
|
131
|
-
/**
|
|
132
|
-
|
|
133
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Extract the symbols defined in a single file's source. Async because the Python / Ruby / Rust
|
|
103
|
+
* grammars are WebAssembly, whose instantiation is async; the napi languages resolve at once.
|
|
104
|
+
*/
|
|
105
|
+
async function definedSymbols(code, lang) {
|
|
106
|
+
if (isWasmLang(lang))
|
|
107
|
+
return (0, tree_sitter_wasm_js_1.wasmDefinedSymbols)(code, lang);
|
|
134
108
|
const out = [];
|
|
135
109
|
const walk = (node, scope) => {
|
|
136
110
|
recordNode(node, scope, out);
|
|
@@ -143,17 +117,20 @@ function definedSymbols(code, lang) {
|
|
|
143
117
|
return out;
|
|
144
118
|
}
|
|
145
119
|
/** Defined symbols for a file on disk, or [] if unreadable/unsupported. */
|
|
146
|
-
function definedSymbolsInFile(file) {
|
|
147
|
-
const support = langForFile(file);
|
|
120
|
+
async function definedSymbolsInFile(file) {
|
|
121
|
+
const support = await langForFile(file);
|
|
148
122
|
if (support.kind !== "ready")
|
|
149
123
|
return [];
|
|
150
|
-
|
|
124
|
+
let code;
|
|
151
125
|
try {
|
|
152
|
-
|
|
126
|
+
code = await (0, promises_1.readFile)(file, "utf-8");
|
|
153
127
|
}
|
|
154
128
|
catch {
|
|
155
129
|
return [];
|
|
156
130
|
}
|
|
131
|
+
// Deliberately OUTSIDE the try: a parser failure is not an empty file. Swallowing it would
|
|
132
|
+
// turn "could not parse" into "is not defined" — a check that did not run, read as a verdict.
|
|
133
|
+
return definedSymbols(code, support.lang);
|
|
157
134
|
}
|
|
158
135
|
// A co-located declaration file that may declare symbols the source defines
|
|
159
136
|
// dynamically (Sorbet `.rbi`, TypeScript `.d.ts`) — checked as a fallback so a
|
|
@@ -173,15 +150,15 @@ const DECL_SIBLING = {
|
|
|
173
150
|
* fallback we also consult a co-located declaration file (`.rbi` / `.d.ts`), so
|
|
174
151
|
* typed dynamic symbols resolve without running Sorbet / the TS compiler.
|
|
175
152
|
*/
|
|
176
|
-
function fileDefinesSymbol(file, name) {
|
|
177
|
-
if (definedSymbolsInFile(file).some((d) => d.name === name))
|
|
153
|
+
async function fileDefinesSymbol(file, name) {
|
|
154
|
+
if ((await definedSymbolsInFile(file)).some((d) => d.name === name))
|
|
178
155
|
return true;
|
|
179
156
|
const ext = (0, node_path_1.extname)(file);
|
|
180
157
|
const decl = DECL_SIBLING[ext];
|
|
181
158
|
if (decl && !file.endsWith(decl)) {
|
|
182
159
|
const sibling = file.slice(0, -ext.length) + decl;
|
|
183
160
|
if ((0, node_fs_1.existsSync)(sibling) &&
|
|
184
|
-
definedSymbolsInFile(sibling).some((d) => d.name === name)) {
|
|
161
|
+
(await definedSymbolsInFile(sibling)).some((d) => d.name === name)) {
|
|
185
162
|
return true;
|
|
186
163
|
}
|
|
187
164
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** The grammars this module parses, keyed by the id used in `tree-sitter-<id>.wasm`. */
|
|
2
|
+
export type WasmLang = "python" | "ruby" | "rust";
|
|
3
|
+
/** A definition found by the walk — the same shape `symbols.ts` exports as `SymbolDef`. */
|
|
4
|
+
export interface WasmSymbolDef {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly kind: string;
|
|
7
|
+
readonly scope: string;
|
|
8
|
+
readonly line: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Node kinds that open a scope (their `name` becomes the `scope` of everything inside), and the
|
|
12
|
+
* kinds a `left` field must have to count as an assignment-style definition. ONE list for both
|
|
13
|
+
* walkers — the napi one in `symbols.ts` and the one below — so they cannot drift apart.
|
|
14
|
+
*/
|
|
15
|
+
export declare const SCOPE_KINDS: readonly string[];
|
|
16
|
+
export declare const ID_KINDS: readonly string[];
|
|
17
|
+
/**
|
|
18
|
+
* Load `lang`'s grammar once per process. Resolves to `null` on success, or the load error's own
|
|
19
|
+
* text — the caller reports it verbatim as "not checked", never as a missing symbol.
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadWasmGrammar(lang: WasmLang): Promise<string | null>;
|
|
22
|
+
/** Parse `code` and return its definitions. Rejects on a parse failure — never resolves []. */
|
|
23
|
+
export declare function wasmDefinedSymbols(code: string, lang: WasmLang): Promise<WasmSymbolDef[]>;
|
|
24
|
+
//# sourceMappingURL=tree-sitter-wasm.d.ts.map
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ID_KINDS = exports.SCOPE_KINDS = void 0;
|
|
4
|
+
exports.loadWasmGrammar = loadWasmGrammar;
|
|
5
|
+
exports.wasmDefinedSymbols = wasmDefinedSymbols;
|
|
6
|
+
/**
|
|
7
|
+
* vigiles — the Python / Ruby / Rust parsers, as WebAssembly, on the main thread.
|
|
8
|
+
*
|
|
9
|
+
* WHY WASM (#257). These three used to be `@ast-grep/lang-*` native grammars, the only packages
|
|
10
|
+
* in our tree with a `postinstall`; since pnpm 10 an unapproved dependency build script fails a
|
|
11
|
+
* consumer's install (`ERR_PNPM_IGNORED_BUILDS`). `@vscode/tree-sitter-wasm` (Microsoft, MIT)
|
|
12
|
+
* ships prebuilt `.wasm` grammars plus the web-tree-sitter runtime with no dependencies and no
|
|
13
|
+
* install scripts: a plain dependency, nothing for the consumer to approve or add.
|
|
14
|
+
*
|
|
15
|
+
* WHY ASYNC, AND WHY NO WORKER. WebAssembly instantiation is async (`Parser.init()`,
|
|
16
|
+
* `Language.load()`). The first cut kept the symbol check synchronous behind a worker thread
|
|
17
|
+
* and `Atomics.wait`, because `compileClaude` & co. were public and synchronous. They are no
|
|
18
|
+
* longer public (same major), so the check is async end to end and this module simply awaits.
|
|
19
|
+
*
|
|
20
|
+
* LAZY. Nothing loads at import: the runtime is `import()`ed on the first `.py` / `.rb` / `.rs`
|
|
21
|
+
* reference, and each grammar is loaded once per process.
|
|
22
|
+
*/
|
|
23
|
+
const promises_1 = require("node:fs/promises");
|
|
24
|
+
const node_module_1 = require("node:module");
|
|
25
|
+
const node_path_1 = require("node:path");
|
|
26
|
+
/**
|
|
27
|
+
* Node kinds that open a scope (their `name` becomes the `scope` of everything inside), and the
|
|
28
|
+
* kinds a `left` field must have to count as an assignment-style definition. ONE list for both
|
|
29
|
+
* walkers — the napi one in `symbols.ts` and the one below — so they cannot drift apart.
|
|
30
|
+
*/
|
|
31
|
+
exports.SCOPE_KINDS = [
|
|
32
|
+
"class_declaration",
|
|
33
|
+
"class_definition",
|
|
34
|
+
"class",
|
|
35
|
+
"module",
|
|
36
|
+
"interface_declaration",
|
|
37
|
+
"enum_declaration",
|
|
38
|
+
];
|
|
39
|
+
exports.ID_KINDS = [
|
|
40
|
+
"identifier",
|
|
41
|
+
"constant",
|
|
42
|
+
"type_identifier",
|
|
43
|
+
];
|
|
44
|
+
// Anchored on THIS file, like every other resolution in the package: under pnpm the consumer's
|
|
45
|
+
// root does not contain our dependencies. `__filename` because the package compiles to CommonJS.
|
|
46
|
+
const require_ = (0, node_module_1.createRequire)(__filename);
|
|
47
|
+
const PKG = "@vscode/tree-sitter-wasm";
|
|
48
|
+
let runtime = null;
|
|
49
|
+
const languages = new Map();
|
|
50
|
+
async function startRuntime() {
|
|
51
|
+
const entry = require_.resolve(PKG);
|
|
52
|
+
// The runtime is a UMD/CommonJS module: `import()` exposes it as `default`.
|
|
53
|
+
const mod = (await import(PKG));
|
|
54
|
+
const rt = mod.default ?? mod;
|
|
55
|
+
await rt.Parser.init();
|
|
56
|
+
return { rt, dir: (0, node_path_1.dirname)(entry), parser: new rt.Parser() };
|
|
57
|
+
}
|
|
58
|
+
async function language(id) {
|
|
59
|
+
runtime ??= startRuntime();
|
|
60
|
+
const { rt, dir } = await runtime;
|
|
61
|
+
let lang = languages.get(id);
|
|
62
|
+
if (lang === undefined) {
|
|
63
|
+
lang = (0, promises_1.readFile)((0, node_path_1.join)(dir, `tree-sitter-${id}.wasm`)).then((bytes) => rt.Language.load(bytes));
|
|
64
|
+
languages.set(id, lang);
|
|
65
|
+
}
|
|
66
|
+
return lang;
|
|
67
|
+
}
|
|
68
|
+
/** First line only: Node appends a multi-line "Require stack:" that would split one finding. */
|
|
69
|
+
function firstLine(e) {
|
|
70
|
+
const text = e instanceof Error ? e.message : String(e);
|
|
71
|
+
return text.split("\n")[0];
|
|
72
|
+
}
|
|
73
|
+
const loadResult = new Map();
|
|
74
|
+
/**
|
|
75
|
+
* Load `lang`'s grammar once per process. Resolves to `null` on success, or the load error's own
|
|
76
|
+
* text — the caller reports it verbatim as "not checked", never as a missing symbol.
|
|
77
|
+
*/
|
|
78
|
+
function loadWasmGrammar(lang) {
|
|
79
|
+
let result = loadResult.get(lang);
|
|
80
|
+
if (result === undefined) {
|
|
81
|
+
result = language(lang).then(() => null, (e) => firstLine(e));
|
|
82
|
+
loadResult.set(lang, result);
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* THE PREVIOUS WALK, NOT A QUERY: every node with a `name` field (its text), plus every node whose
|
|
88
|
+
* `left` field is a bare identifier / constant / type_identifier, with the nearest enclosing
|
|
89
|
+
* class/module name as `scope`. The native-grammar code had no per-language patterns to
|
|
90
|
+
* translate; a tags.scm-style query would be a DIFFERENT, narrower definition of "defined".
|
|
91
|
+
*/
|
|
92
|
+
function extract(root) {
|
|
93
|
+
const scopeKinds = new Set(exports.SCOPE_KINDS);
|
|
94
|
+
const idKinds = new Set(exports.ID_KINDS);
|
|
95
|
+
const out = [];
|
|
96
|
+
const walk = (node, scope) => {
|
|
97
|
+
const line = node.startPosition.row + 1;
|
|
98
|
+
const name = node.childForFieldName("name");
|
|
99
|
+
if (name)
|
|
100
|
+
out.push({ name: name.text, kind: node.type, scope, line });
|
|
101
|
+
const left = node.childForFieldName("left");
|
|
102
|
+
if (left && idKinds.has(left.type))
|
|
103
|
+
out.push({ name: left.text, kind: node.type, scope, line });
|
|
104
|
+
const next = scopeKinds.has(node.type) && name ? name.text : scope;
|
|
105
|
+
for (const child of node.children)
|
|
106
|
+
walk(child, next);
|
|
107
|
+
};
|
|
108
|
+
walk(root, "");
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
/** Parse `code` and return its definitions. Rejects on a parse failure — never resolves []. */
|
|
112
|
+
async function wasmDefinedSymbols(code, lang) {
|
|
113
|
+
const grammar = await language(lang);
|
|
114
|
+
// `runtime` is set by `language()`; one parser, re-pointed per call. Safe without a lock:
|
|
115
|
+
// setLanguage → parse → walk runs to completion with no `await` in between.
|
|
116
|
+
const { parser } = await runtime;
|
|
117
|
+
parser.setLanguage(grammar);
|
|
118
|
+
const tree = parser.parse(code);
|
|
119
|
+
if (!tree)
|
|
120
|
+
throw new Error(`tree-sitter (${lang}) returned no syntax tree`);
|
|
121
|
+
try {
|
|
122
|
+
return extract(tree.rootNode);
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
tree.delete();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=tree-sitter-wasm.js.map
|
package/dist/eval-surface.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// 🔴 PUBLIC ENTRY POINT `vigiles/eval` — every export here is a promise to users. The default for a
|
|
3
|
+
// symbol is INTERNAL. It is exported only if (a) a NAMED external consumer uses it, or (b) it is
|
|
4
|
+
// a deliberate extension point listed in STABILITY.md (the adapter kit is the example). "Might
|
|
5
|
+
// be useful" is neither. Review point: the diff of `api-surface/vigiles-eval.api.md`, which
|
|
6
|
+
// `npm run api:check` fails on.
|
|
2
7
|
/**
|
|
3
8
|
* `vigiles/eval` — the **paid** surface: every runtime export here can call a
|
|
4
9
|
* model, and therefore can spend money.
|
package/dist/hook-install.js
CHANGED
|
@@ -265,15 +265,30 @@ function mergeHooksJson(existing, compiled, hookPath) {
|
|
|
265
265
|
const before = existing.hooks ?? {};
|
|
266
266
|
const rewritten = Object.fromEntries(Object.entries(compiled).map(([event, entries]) => [
|
|
267
267
|
event,
|
|
268
|
-
[
|
|
269
|
-
...(before[event] ?? [])
|
|
270
|
-
.map((e) => withoutHookCommands(e, hookPath))
|
|
271
|
-
.filter((e) => e !== null),
|
|
272
|
-
...entries,
|
|
273
|
-
],
|
|
268
|
+
replaceInPlace(before[event] ?? [], (e) => withoutHookCommands(e, hookPath), entries),
|
|
274
269
|
]));
|
|
275
270
|
return { ...existing, hooks: { ...before, ...rewritten } };
|
|
276
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Put `fresh` where this hook's old entry stood, not at the end. Appending made
|
|
274
|
+
* a recompile whose wiring changed read as a reshuffle: measured on a real
|
|
275
|
+
* settings.json, one guard gained `|| exit 2` and the diff showed three entries
|
|
276
|
+
* swapping places. `strip` returns the entry unchanged when it is not ours, a
|
|
277
|
+
* narrowed entry when it shared a matcher with ours, or null when it was only
|
|
278
|
+
* ours. A hook wired for the first time is still appended.
|
|
279
|
+
*/
|
|
280
|
+
function replaceInPlace(list, strip, fresh) {
|
|
281
|
+
const at = list.findIndex((e) => strip(e) !== e);
|
|
282
|
+
const slot = at === -1 ? list.length : at;
|
|
283
|
+
return [
|
|
284
|
+
...list.flatMap((e, i) => {
|
|
285
|
+
const kept = strip(e);
|
|
286
|
+
const here = kept === null ? [] : [kept];
|
|
287
|
+
return i === slot ? [...here, ...fresh] : here;
|
|
288
|
+
}),
|
|
289
|
+
...(slot === list.length ? fresh : []),
|
|
290
|
+
];
|
|
291
|
+
}
|
|
277
292
|
/** Flatten a CC-shaped entry to Codex's flat `{matcher?, command}` form. */
|
|
278
293
|
function toTomlEntries(entries) {
|
|
279
294
|
return entries.flatMap((e) => e.hooks.map((h) => e.matcher === undefined
|
|
@@ -291,10 +306,8 @@ function mergeHooksToml(existing, compiled, hookPath) {
|
|
|
291
306
|
const before = existing.hooks ?? {};
|
|
292
307
|
const rewritten = Object.fromEntries(Object.entries(compiled).map(([event, entries]) => [
|
|
293
308
|
event,
|
|
294
|
-
[
|
|
295
|
-
|
|
296
|
-
...toTomlEntries(entries),
|
|
297
|
-
],
|
|
309
|
+
replaceInPlace(before[event] ?? [], (e) => managesHook({ hooks: [{ type: "command", command: e.command }] }, hookPath) ? null : e, // prettier-ignore
|
|
310
|
+
toTomlEntries(entries)),
|
|
298
311
|
]));
|
|
299
312
|
return { ...existing, hooks: { ...before, ...rewritten } };
|
|
300
313
|
}
|
package/dist/hook.js
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.admissibleWrites = exports.isStateWrite = exports.isStateNeed = exports.isValidStateKey = exports.stateFact = exports.record = exports.state = exports.provider = exports.defineProvider = exports.dangerously = exports.provide = exports.HookCompileError = exports.verifyHookStamp = exports.stampHook = exports.checkHookImports = exports.compileHookProgram = exports.invalidToolPatterns = exports.matchesTool = exports.outcomeWrites = exports.injectionOf = exports.hookNeeds = exports.hookRouting = exports.dispatchKind = exports.decisionExitCode = exports.runHookProgram = exports.runReact = exports.runInject = exports.decideStopGate = exports.decidePromptGate = exports.decideFileGate = exports.decideProgram = exports.responseView = exports.nothing = exports.notice = exports.run = exports.experimental_defineReact = exports.inject = exports.experimental_defineInject = exports.hookMode = exports.gateAction = exports.pathView = exports.commandView = exports.ask = exports.deny = exports.allow = exports.tools = exports.experimental_defineStopGate = exports.experimental_definePromptGate = exports.experimental_defineFileGate = exports.experimental_defineHook = void 0;
|
|
4
4
|
exports.leafCommandsNormalized = exports.HookStateError = exports.durationSeconds = void 0;
|
|
5
|
+
// 🔴 PUBLIC ENTRY POINT `vigiles/hook` — every export here is a promise to users. The default for a
|
|
6
|
+
// symbol is INTERNAL. It is exported only if (a) a NAMED external consumer uses it, or (b) it is
|
|
7
|
+
// a deliberate extension point listed in STABILITY.md (the adapter kit is the example). "Might
|
|
8
|
+
// be useful" is neither. Review point: the diff of `api-surface/vigiles-hook.api.md`, which
|
|
9
|
+
// `npm run api:check` fails on.
|
|
5
10
|
/**
|
|
6
11
|
* `vigiles/hook` — the **closed vocabulary** for authoring a compiled hook.
|
|
7
12
|
*
|
package/dist/jest.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
// 🔴 PUBLIC ENTRY POINT `vigiles/jest` — every export here is a promise to users. The default for a
|
|
4
|
+
// symbol is INTERNAL. It is exported only if (a) a NAMED external consumer uses it, or (b) it is
|
|
5
|
+
// a deliberate extension point listed in STABILITY.md (the adapter kit is the example). "Might
|
|
6
|
+
// be useful" is neither. Review point: the diff of `api-surface/vigiles-jest.api.md`, which
|
|
7
|
+
// `npm run api:check` fails on.
|
|
3
8
|
/* eslint-disable max-params, @typescript-eslint/no-explicit-any --
|
|
4
9
|
The matcher signatures mirror the runtime vigilesMatchers (positional args);
|
|
5
10
|
`Check<any>` matches the runtime generic for the type-only augmentation. */
|
package/dist/linting.d.ts
CHANGED
|
@@ -1,34 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `vigiles/linting` — Pillar 1 entry point:
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* `vigiles/linting` — Pillar 1 entry point: what a spec AUTHOR imports to describe a CLAUDE.md,
|
|
3
|
+
* a SKILL.md or an agent — the builders, the verified-reference constructors and their types.
|
|
4
|
+
* Compiling is the CLI's job (`vigiles compile` / `vigiles lint`), not this subpath's.
|
|
5
5
|
*
|
|
6
|
-
* Curated (named, not `export *`) so the internal compiler validators, hash
|
|
7
|
-
*
|
|
8
|
-
* the api reports, and the docs site (the CLI imports those from the source).
|
|
6
|
+
* Curated (named, not `export *`) so the internal compiler, validators, hash helpers and the
|
|
7
|
+
* linter cross-reference ENGINE stay out of the public surface, the api reports and the docs.
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* documentation of a decision and were descriptions of the opposite one; a
|
|
17
|
-
* header nobody re-reads is where an `export *` hides best.
|
|
18
|
-
*
|
|
19
|
-
* WHAT THE CURATION DROPS (28 symbols, measured — nothing in this repo imported
|
|
20
|
-
* any of them from here; every in-repo user takes them from `vigiles/spec`, and
|
|
21
|
-
* the one live consumer of this subpath in the docs takes `compileAgent`): the
|
|
22
|
-
* typed-COMPOSITION family — `experimental_pipe`/`_pipeStep`/`_start`/
|
|
23
|
-
* `_andThen`/`_needs`, `Pipeline`, `PipeStep`, `Supplies`, `Handoff`,
|
|
24
|
-
* `NeedsContract`, `OkOf`, `TypedAgentSpec`, `TypedOutcome`, `Shape`,
|
|
25
|
-
* `OutputFieldType` and the `result()` builder. Those verify HANDOFFS between
|
|
26
|
-
* workers; they compile to nothing and lint nothing, so they are not pillar 1.
|
|
27
|
-
*
|
|
28
|
-
* ⚠️ The cut is not a clean slice along that line, and pretending otherwise
|
|
29
|
-
* would strand a signature: the `result()` FUNCTION leaves, but the
|
|
30
|
-
* `OutputContract` TYPE stays, because `compileAgent`/`compileSkill` name it in
|
|
31
|
-
* their own types. A consumer who needs to BUILD one imports `vigiles/spec`.
|
|
9
|
+
* WHAT THE CURATION DROPS (28 symbols, measured 2026-08-21 — nothing in this repo imported any
|
|
10
|
+
* of them from here; every in-repo user takes them from `vigiles/spec`): the typed-COMPOSITION
|
|
11
|
+
* family — `experimental_pipe`/`_pipeStep`/`_start`/`_andThen`/`_needs`, `Pipeline`, `PipeStep`,
|
|
12
|
+
* `Supplies`, `Handoff`, `NeedsContract`, `OkOf`, `TypedAgentSpec`, `TypedOutcome`, `Shape`,
|
|
13
|
+
* `OutputFieldType` and the `result()` builder. Those verify HANDOFFS between workers; they
|
|
14
|
+
* compile to nothing and lint nothing, so they are not pillar 1.
|
|
32
15
|
*/
|
|
33
16
|
export { instructionFile, prose, experimental_effect, enforce, guidance, guard, file, cmd, symbol, ref, dir, glob, project, experimental_skill, experimental_agent,
|
|
34
17
|
/** @deprecated Renamed to `instructionFile`. Removed one major AFTER the one that introduces it. */
|
|
@@ -38,6 +21,4 @@ instructions,
|
|
|
38
21
|
/** @deprecated Renamed to `experimental_agent`. Removed one major AFTER the one that introduces it. */
|
|
39
22
|
agent, } from "./core/spec.js";
|
|
40
23
|
export { BUILTIN_LINTERS, type BuiltinLinter, type LinterRule, type VigilesRef, type EnforcementRef, type KnownLinterRules, type KnownProjectFiles, type KnownNpmScripts, type KnownAgentName, type StrictLinterRule, type StrictFile, type StrictCmd, type ToolVocabulary, type OpenToolVocabulary, type AllowedAt, type AuthoredPurity, type EnforceRule, type GuidanceRule, type GuardRule, type Rule, type VerifiedPath, type VerifiedCmd, type VerifiedRef, type VerifiedDir, type VerifiedGlob, type FileRef, type CmdRef, type SkillRef, type SymbolRef, type DirRef, type GlobRef, type Ref, type EffectRegion, type InstructionFragment, type InstructionTarget, type ClaudeSpec, type Gate, type RoleGate, type ProjectRole, type SkillInput, type SkillStep, type SkillSpec, type SkillSpecInput, type AgentSpec, type AgentSpecInput, type Railway, type RailwayStep, type OutputContract, } from "./core/spec.js";
|
|
41
|
-
export { compileClaude, compileSkill, compileAgent, compileRailway, CompileError, } from "./core/compile.js";
|
|
42
|
-
export type { CompileClaudeOptions, CompileClaudeResult, CompileSkillResult, CompileAgentResult, CompileRailwayOptions, CompileRailwayResult, } from "./core/compile.js";
|
|
43
24
|
//# sourceMappingURL=linting.d.ts.map
|