Skip to content

feat(lsp): ast-grep LSP as the rule engine — auxiliary adopter (Phase 1) then consolidate/retire napi runner (Phase 2) #239

Description

@apmantza

Goal

Consolidate pi-lens''s rule-based ast-grep onto a single full-engine path: the ast-grep lsp server, driven by the repo''s own sgconfig.yml when present, else by pi-lens''s shipped rules via --config. This retires the napi subset interpreter (which ignores field:, has no bare-regex/anon-kind candidates) for rule scanning, expands language coverage (~7 napi file types → ~15 LSP languages), and unlocks engine-driven autofix via codeAction.

Verified feasibility: ast-grep lsp -c, --config <CONFIG_FILE> exists (checked the installed binary). So the no-sgconfig fallback — launch the LSP pointed at a pi-lens-synthesized sgconfig of our shipped rules — is supported. pi-lens already synthesizes an sgconfig into a session dir (sg-runner.ts:470), so the machinery exists.

NOT in scope

  • The pilens_ast_grep_search / pilens_ast_grep_replace MCP tools stay on napi (ad-hoc pattern queries; the LSP doesn''t expose those). "Replace the runner" != "remove napi."

Phase 1 — ast-grep LSP as auxiliary-diagnostic-LSP adopter (sgconfig repos only) — LOW RISK, additive

Reuse the Opengrep auxiliary-LSP capability (clients/dispatch/auxiliary-lsp.ts, role:"auxiliary", with-auxiliary touchFile scope, profile registry, allowBlocking(cwd)).

  • Register ast-grep lsp server gated on repo-root sgconfig.y[a]ml (its root_markers + workspace_required).
  • Profile: map LSP source -> tool:"ast-grep" + semantic policy; blocking-eligible (sgconfig = curated repo rules, same logic as Opengrep''s curated-rules gate).
  • Wire ast-grep rule fix: -> LSP codeAction -> pi-lens autofix / fixSuggestion -> actionable-warnings routing.
  • Smoke: add a fixture to scripts/smoke-tools.mjs --lsp (auxiliaryServerIds) — sgconfig + a rule + a violating file; assert the ast-grep-sourced diagnostic returns alongside the primary.
  • Does not touch the napi runner — purely adds the team-rules surface for sgconfig repos.

Phase 2 — consolidate the baseline + retire the napi runner — GATED

  • Extend to the no-sgconfig path: ast-grep lsp --config <synthesized sgconfig of shipped rules> (advisory baseline, mirroring Opengrep auto-set = advisory).
  • Port the napi runner''s policy (the bulk of the work): MAX_BLOCKING_RULE_COMPLEXITY, isOverlyBroadPattern, MAX_MATCHES_PER_RULE/MAX_TOTAL_DIAGNOSTICS caps, classifyDefect taxonomy, fixable/fixSuggestion routing, hasEslintConfig gating → into the auxiliary-LSP profile/aggregation.
  • GATE A — latency: measure warm-LSP single-file edit latency vs in-process napi. napi is in-process (no spawn); the LSP must not regress the hot path. Retire napi only if comparable/better.
  • GATE B — binary-absent degrade: napi is bundled (offline, zero-install); routing the universal baseline through the LSP makes @ast-grep/cli a hard dependency. Define the fallback when the binary is missing (degrade to napi vs skip).
  • Only after both gates: retire the napi runner (keep napi for search/replace tools).
  • Bonus once on the full engine: shipped rules can use field:/regex the napi subset couldn''t.

Acceptance (Phase 1)

  • ast-grep lsp registered role:"auxiliary", sgconfig-gated, with strategy (reopenOnResync if it ignores didChange like Opengrep)
  • auxiliary-lsp profile: source routing + semantic policy + blocking-on-curated-rules
  • rule fix: surfaces as a fixSuggestion/autofix
  • smoke-tools --lsp fixture validates end-to-end (auto-install, spawn, scan, sourced diagnostic alongside primary)
  • registry-consistency guard passes

(Origin: "utilize the ast-grep LSP" thread. Sibling LSP work: #235/#236/#238; auxiliary-LSP capability shipped with Opengrep #111.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:dispatchDispatch runners — linters/formatters/checkersarea:lspLSP servers & integrationfeatureNet-new capability: command/tool/runner/integration/config surface that didn't exist

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions