mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
fix(lsp): typescript SDK install + tsc-missing skip + shellcheck warning (#24630)
Three follow-ups to PR #24168 found during live E2E testing on TS/bash files: 1. typescript-language-server now installs the typescript SDK (tsserver) alongside it. Without that sibling install, initialize() failed with "Could not find a valid TypeScript installation" and the server was marked broken — no diagnostics ever reached the agent. New extra_pkgs field on INSTALL_RECIPES makes that explicit and reusable for future peer-dep cases. 2. _check_lint now treats "linter command exists on PATH but cannot actually run" as skipped instead of error. The motivating case is npx tsc when typescript is not in node_modules — npx prints its "This is not the tsc command you are looking for" banner and exits non-zero, which previously blocked the LSP semantic tier (gated on success or skipped). Pattern-matched per base command (npx, rustfmt, go) so genuine lint errors still flow through normally. 3. hermes lsp status now surfaces a Backend warnings section when bash-language-server is installed but shellcheck is missing. The server itself spawns fine but bash-language-server delegates diagnostics to shellcheck — without it on PATH the integration looks alive but never reports any problems. Same warning is logged once at server spawn time. Validation: - 12 new tests in tests/agent/lsp/test_install_and_lint_fixes.py: * recipe carries typescript SDK * _install_npm passes both pkg + extras to npm CLI * backwards compat: recipes without extras still work * _backend_warnings quiet when bash absent / both present * _backend_warnings fires when bash installed without shellcheck * status output includes the Backend warnings section * _looks_like_linter_unusable catches the npx tsc banner * real TS type errors not misclassified as unusable * unfamiliar linters fall through normally * _check_lint returns skipped on npx tsc unusable * _check_lint returns error on real tsc type errors - Full lsp + file_operations test suite: 245/245 pass - Live E2E: * try_install("typescript-language-server") installs both packages into node_modules * write_file(bad.ts, ...) returns lint=skipped + lsp_diagnostics with two real TS errors (was lint=error, no lsp_diagnostics) * hermes lsp status renders the shellcheck warning when bash is installed but shellcheck is not on PATH
This commit is contained in:
parent
6f285efb80
commit
29c9ff9ba5
5 changed files with 434 additions and 6 deletions
|
|
@ -327,6 +327,55 @@ LINTERS = {
|
|||
}
|
||||
|
||||
|
||||
# Patterns that indicate the linter base command exists on PATH but
|
||||
# couldn't actually run — e.g. ``npx tsc`` when tsc isn't installed in
|
||||
# node_modules, or rustfmt complaining there's no Cargo project. When
|
||||
# any of these substrings appears in the linter output, ``_check_lint``
|
||||
# returns ``skipped`` instead of ``error`` so:
|
||||
#
|
||||
# 1. The write isn't flagged for a tooling problem the agent can't fix.
|
||||
# 2. The LSP semantic tier still runs (it gates on success/skipped).
|
||||
#
|
||||
# Patterns are matched case-insensitively against linter stdout.
|
||||
_LINTER_UNUSABLE_PATTERNS = {
|
||||
'npx': (
|
||||
# npx prints this banner when the package isn't installed locally
|
||||
# AND it can't auto-install (no internet, registry off, etc.) or
|
||||
# when the binary it tried to run is the wrong one.
|
||||
'this is not the tsc command you are looking for',
|
||||
# npx with --no-install resolution failures
|
||||
'could not determine executable to run',
|
||||
'not found in npm registry',
|
||||
),
|
||||
'rustfmt': (
|
||||
# rustfmt outside a Cargo project
|
||||
'no input filename given',
|
||||
'error: not a workspace',
|
||||
),
|
||||
'go': (
|
||||
# ``go vet`` on a file outside a module / GOPATH
|
||||
'cannot find package',
|
||||
'go: cannot find main module',
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _looks_like_linter_unusable(base_cmd: str, output: str) -> bool:
|
||||
"""Return True iff ``output`` from ``base_cmd`` indicates the linter
|
||||
itself couldn't run (a tooling gap), as opposed to a real lint error
|
||||
in the file being checked.
|
||||
|
||||
``base_cmd`` is the first word of the linter command line (``npx``,
|
||||
``rustfmt``, ``go``, ...). ``output`` is the stdout/stderr captured
|
||||
from running it.
|
||||
"""
|
||||
patterns = _LINTER_UNUSABLE_PATTERNS.get(base_cmd)
|
||||
if not patterns:
|
||||
return False
|
||||
lower = output.lower()
|
||||
return any(p in lower for p in patterns)
|
||||
|
||||
|
||||
def _lint_json_inproc(content: str) -> tuple[bool, str]:
|
||||
"""In-process JSON syntax check. Returns (ok, error_message)."""
|
||||
import json as _json
|
||||
|
|
@ -1117,6 +1166,24 @@ class ShellFileOperations(FileOperations):
|
|||
cmd = linter_cmd.replace("{file}", self._escape_shell_arg(path))
|
||||
result = self._exec(cmd, timeout=30)
|
||||
|
||||
if result.exit_code != 0 and _looks_like_linter_unusable(base_cmd, result.stdout):
|
||||
# The linter command exists on PATH but couldn't actually run
|
||||
# (e.g. ``npx tsc`` when tsc isn't in node_modules; ``rustfmt
|
||||
# --check`` without a Cargo project). This is a tooling gap,
|
||||
# not a real lint failure — surface it as ``skipped`` so the
|
||||
# write doesn't get flagged AND so the LSP tier still runs.
|
||||
from tools.ansi_strip import strip_ansi
|
||||
cleaned = strip_ansi(result.stdout).strip()
|
||||
# Collapse to a single line — the npx banner is multi-line ASCII.
|
||||
first_line = next(
|
||||
(ln.strip() for ln in cleaned.splitlines() if ln.strip()),
|
||||
cleaned[:120],
|
||||
)
|
||||
return LintResult(
|
||||
skipped=True,
|
||||
message=f"{base_cmd} not usable: {first_line[:200]}",
|
||||
)
|
||||
|
||||
return LintResult(
|
||||
success=result.exit_code == 0,
|
||||
output=result.stdout.strip() if result.stdout.strip() else ""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue