git:20260608.0702a11 to git:20260608.5ff76b6

40 added, 2 removed. Audit A to A.

# AGENTS.md for @webjsdev/intellisense
A **tsserver plugin** that gives editors (VS Code, Neovim, JetBrains)
webjs-aware intelligence inside `` html`` `` tagged templates:
go-to-definition on custom-element tag names / attributes / CSS classes,
binding-aware completions, in-template diagnostics, and hover, all driven
by webjs's OWN HTML-in-template parser. It is **standalone** as of Phase 3
(#386): no `ts-lit-plugin` dependency, no Lit code. The `webjs` VS Code
extension bundles it; Neovim / JetBrains wire it via `tsconfig.json`.
Framework-wide rules (workflow, JSDoc-in-`packages/`, no-build,
commit conventions, autonomous-mode behaviour, scaffold rules) live
in the **framework root [`../../AGENTS.md`](../../AGENTS.md)** and
apply here. Read that first.
This file only covers what's specific to `@webjsdev/intellisense`.
+ ## Editing `src/`: re-vendor before you commit (REQUIRED)
+
+ This package is the SOURCE OF TRUTH for two downstream consumers, and one of
+ them keeps a COMMITTED copy that a CI drift test enforces. After ANY change
+ under `src/` (even a one-line edit), run, in this order, BEFORE committing:
+
+ ```sh
+ node packages/editors/nvim/scripts/vendor-intellisense.mjs
+ git add -f packages/editors/nvim/vendor # the copy lives under a gitignored node_modules/
+ ```
+
+ - **webjs.nvim** ships a verbatim copy at
+ `packages/editors/nvim/vendor/node_modules/@webjsdev/intellisense/src` (it has
+ no install-time build step). The drift guard
+ `packages/editors/nvim/test/vendor-sync.test.mjs` FAILS CI ("vendored
+ intellisense src is byte-identical ...") whenever `src/` and the vendored copy
+ diverge. Forgetting the re-vendor is the single most common way an
+ intellisense edit reds CI.
+ - **The `webjs` VS Code extension** bundles this package via esbuild at vsix
+ package time, so it picks up `src/` changes automatically (no committed copy,
+ nothing to re-vendor there).
+
+ So the rule of thumb: an intellisense `src/` edit is not done until the nvim
+ vendor copy is re-synced and force-added on the same commit (or a follow-up
+ commit on the same PR). Run `node --test packages/editors/nvim/test/vendor-sync.test.mjs`
+ to confirm green before pushing.
+
## Role
The plugin owns webjs's in-template intelligence. It is **standalone** as of
Phase 3 (#386): it decorates the stock tsserver language service directly and
has no `ts-lit-plugin` dependency (no loader, no wrapping). The plugin:
1. Scans the program at boot for `Class.register('tag', …)` /
`customElements.define('tag', Class)` registrations into a registry of
per-member records (`{ propName, attrName, state }`, where `attrName` is
the hyphenated form of `propName`).
2. For every `.ts` / `.js` file being edited, computes its **import
graph** transitively. Only tags whose registering file is reachable
from the current file count as "available". This gates every feature.
3. Parses the markup inside each `` html`` `` template into an AST
(`src/template/parse.js`) with absolute source spans and binding-modifier
classification (`@event` / `.property` / `?boolean` / plain).
4. **Go-to-definition** on a custom-element tag (→ class), an attribute /
property / event name (→ the `declare` member or `static properties`
key), and a CSS class inside `class="…"` (→ the `css\`\`` rule).
5. **Completions**: reachable custom tag names after `<` / `</`, and
binding-aware attribute completions keyed by prefix (`.` → property
names, plain / `?` → hyphenated attribute names; `@event` is permissive).
6. **Diagnostics**: incompatible-type bindings (plain / `.prop` / `@event`
- callable), unquoted `@`/`.`/`?` bindings (invariant 4, code 9002), and
- expressionless `.prop` bindings (code 9003). Deliberately NO blanket
+ callable, code 9001), unquoted `@`/`.`/`?` bindings (invariant 4, code
+ 9002), expressionless `.prop` bindings (code 9003), and duplicate
+ custom-element tag registrations (code 9004), the live underline on a tag
+ registered more than once across the program, matching the
+ `no-duplicate-tag` `webjs check` rule that is the CI gate. The 9004 check is program-wide
+ and NOT import-graph gated (a collision is a runtime hazard regardless of
+ imports) and runs under its own try/catch in the `getSemanticDiagnostics`
+ decorator, independent of the in-template rules. It sees every file in the
+ tsserver program, so it can underline an on-disk duplicate that the
+ `no-duplicate-tag` `webjs check` rule deliberately skips (the CI rule
+ excludes gitignored / generated copies; the editor surfaces them as live
+ authoring feedback). That divergence is intentional: CI polices committed
+ source, the editor warns on whatever is open in the project. Deliberately NO blanket
unknown-tag / unknown-attribute (webjs has no element type map, so it
would false-positive on third-party customs).
7. **Hover**: a tag shows its class; an attribute / property / event shows
its declared type.
## Module map
```
src/
index.js The language-service decorator: registry, reachability,
completions, diagnostics, definitions, hover.
template/parse.js The html`` HTML-in-template parser (length-preserving
${} masking → node/attr AST with absolute spans).
test/plugin/
intellisense.test.mjs Language-service behaviour (definitions, completions,
diagnostics, hover) via a real in-memory tsserver.
template-parse.test.mjs The parser in isolation.
README.md User-facing setup instructions.
```
## Package-specific invariants
1. **Tag recognition is gated on the import graph of the current file.**
A tag registered somewhere in the program but not imported (directly
or transitively) by the file you're editing is NOT treated as
available, since runtime would fail too and surfacing the diagnostic is
the correct prompt to add the import.
2. **Static (non-interpolated) attribute text is not type-checked.**
At runtime it's plain template text, so any string is valid. Only
interpolated holes (`\${expr}`) are checked against the declared
prop type.
3. **The plugin must not error.** If anything goes wrong (parse error,
missing source file, type-checker quirk), fall back to passthrough.
The user's editor must never break because of this plugin. Wrap risky
logic in try/catch with a silent return.
4. **No `ts-lit-plugin` dependency.** The plugin is self-contained: its own
parser, completions, diagnostics, and hover never require it, and the
source must never `require('ts-lit-plugin')` (a test asserts this). A
single `tsconfig.json` plugin entry, `{ "name": "@webjsdev/intellisense" }`.
5. **No blanket unknown-tag / unknown-attribute diagnostics.** webjs has no
`HTMLElementTagNameMap`, so flagging an unrecognised tag/attribute would
false-positive on legitimate third-party custom elements. Only
zero-false-positive rules ship.
## Tests
`packages/editors/intellisense/test/plugin/intellisense.test.mjs` boots a real
tsserver instance against fixture sources and asserts definition,
completion, diagnostic, and hover behaviour. Covers tag / attribute /
CSS-class resolution, binding-aware completions (incl. hyphenation and
`.prop` vs plain), the diagnostic rules, hover, import-graph gating, and
the "lit-plugin diagnostic suppression only when imported" path.
`template-parse.test.mjs` covers the parser in isolation.
The file is `.mjs` because `@webjsdev/intellisense` itself is a
CommonJS package (`"type": "commonjs"`); the test uses ESM
imports so the extension forces the right module type.
See [`../../agent-docs/testing.md`](../../agent-docs/testing.md)
for the overall layout. Run `npm test` from the repo root.
---
Framework-wide rules and full API reference:
@../../AGENTS.md