Linter
Run wippy lint to type-check and statically analyze Lua entries.
Usage
wippy lint # Check all Lua entries
wippy lint --level hint # Show all diagnostics including hints
wippy lint --json # Output in JSON format
wippy lint --ns app # Check only the app namespace
wippy lint --summary # Group results by error code
What Gets Checked
The linter validates all Lua entry kinds:
function.lua— Functionslibrary.lua— Librariesprocess.lua— Processesworkflow.lua— Workflows
Bytecode entries hold compiled bytecode (fs/path/hash), not source, so they cannot be parsed or type-checked; the linter only checks source-bearing Lua entries (their .bc variants are skipped, though they may still appear in the total entry count).
Each entry is parsed, type-checked, and analyzed for correctness issues.
Severity Levels
Diagnostics have three severity levels:
| Level | Description |
|---|---|
error |
Type errors and correctness issues that must be fixed |
warning |
Likely bugs or problematic patterns |
hint |
Style suggestions and informational notes |
Control which levels appear with --level:
wippy lint --level error # Errors only
wippy lint --level warning # Warnings and errors (default)
wippy lint --level hint # Everything
Error Codes
Parse Errors
| Code | Description |
|---|---|
P0001 |
Lua syntax error - source cannot be parsed |
Type Check Errors (E-series)
Type checker errors (E0001+) report issues found by the type system: type mismatches, undefined variables, invalid operations, and similar correctness problems. These are always reported as errors.
local x: number = "hello" -- E: string not assignable to number
local function add(a: number, b: number): number
return a + b
end
add("one", "two") -- E: string not assignable to number
Undeclared Requires
A string-literal require("name") whose module is neither in the entry's imports/modules declarations nor an ambient builtin fails with:
require("name") is not declared in _index.yaml imports or modules
This check always runs (it is not gated behind --rules) and reports as an error. Declare the module to satisfy it:
imports:
json: wippy.stdlib:json # alias -> registry id
modules:
- funcs # bare module name
Dynamic requires (require(variable)) are not inspected. The linter and runtime share the ambient module set, which includes modules available without declaration, such as process in executable kinds.
Lint Rule Warnings (W-series)
Lint rules provide style and quality checks. Enable them with --rules:
wippy lint --rules
| Code | Rule | Description |
|---|---|---|
W0001 |
no-empty-blocks | Empty block statements |
W0002 |
no-global-assign | Assignment to global variables |
W0003 |
no-self-compare | Comparison of a value with itself |
W0004 |
no-unused-vars | Unused local variables |
W0005 |
no-unused-params | Unused function parameters |
W0006 |
no-unused-imports | Unused import statements |
W0007 |
no-shadowed-vars | Variable shadowing outer scope |
Without --rules, only type checking (P and E codes) is performed.
Filtering
By Namespace
Check specific namespaces using --ns:
wippy lint --ns app # Exact namespace match
wippy lint --ns "app.*" # All under app
wippy lint --ns app --ns lib # Multiple namespaces
Dependencies of selected entries are loaded for type checking but their diagnostics are not reported.
By Error Code
Filter diagnostics by code:
wippy lint --code E0001
wippy lint --code E0001 --code E0004
By Count
Limit the number of diagnostics shown:
wippy lint --limit 10 # Show first 10 issues
Output Formats
Table Format (Default)
Each diagnostic is displayed with source context, file location, and the error message. Results are sorted by entry, severity, and line number.
A summary line shows totals:
Checked 42 entries: 5 errors, 12 warnings
Summary Format
Group diagnostics by namespace and error code:
wippy lint --summary
By namespace:
app 15 issues (5 errors, 10 warnings)
lib 2 issues (2 warnings)
By error code:
E0001 [error ] 5 occurrences
E0004 [error ] 3 occurrences
Checked 42 entries: 5 errors, 12 warnings
JSON Format
Machine-readable output for CI/CD processing:
wippy lint --json
{
"diagnostics": [
{
"entry_id": "app:handler",
"code": "E0001",
"severity": "error",
"message": "string not assignable to number",
"line": 10,
"column": 5
}
],
"total_entries": 42,
"error_count": 5,
"warning_count": 12,
"hint_count": 0
}
Caching
The linter caches results between runs. Cache keys include the source hash, method name, dependencies, and type-system configuration.
Clear the cache if results seem stale:
wippy lint --cache-reset
CI Integration
In table and summary modes, the command exits non-zero when the filtered result contains errors. Warnings and hints do not affect the exit code, even when --level warning or --level hint displays them.
JSON mode is different: after successfully encoding the result, wippy lint --json exits with code 0 even when error_count is non-zero. A CI job using JSON output must parse error_count itself. To use the command's exit status as the gate, run a non-JSON invocation:
wippy lint --level error
You can produce a report separately without treating its exit status as the lint result:
wippy lint --json --level error > lint-results.json
Example GitHub Actions step:
- name: Lint
run: wippy lint --level warning
Flags Reference
| Flag | Short | Default | Description |
|---|---|---|---|
--level |
warning | Minimum severity level (error, warning, hint) | |
--json |
false | Output in JSON format | |
--ns |
Filter by namespace patterns | ||
--code |
Filter by error codes | ||
--limit |
0 | Max diagnostics to show (0 = unlimited) | |
--summary |
false | Group by error code | |
--no-color |
false | Disable colored output | |
--rules |
false | Enable lint rules (W-series style/quality checks) | |
--cache-reset |
false | Clear cache before linting | |
--profile |
Apply a workspace profile from merged runtime configuration; repeat to apply profiles in order | ||
--set |
Override a merged configuration value as section.path=value; repeat for multiple overrides |
||
--lock-file |
-l |
wippy.lock | Path to lock file |
--profile |
Apply a workspace profile from the merged runtime config (repeatable, applied in order) | ||
--set |
Override a merged runtime config value (section.path=value, repeatable) |