From 66fde1b0f865484678ef4001387e184023920c4e Mon Sep 17 00:00:00 2001 From: Maciej Krajowski-Kukiel Date: Thu, 12 Mar 2026 10:40:21 +0100 Subject: [PATCH] add check option to pos-cli check --- CHANGELOG.md | 2 +- README.md | 9 +++++++ bin/pos-cli-check-run.js | 6 +++++ lib/check.js | 19 +++++++++++++-- test/integration/check.test.js | 43 ++++++++++++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58481592..df2e0c60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ * `pos-cli mcp-config` — display and inspect the MCP server tool configuration (which tools are enabled/disabled). Supports `--json` for raw output. Configurable via `MCP_TOOLS_CONFIG` env var. * `pos-cli lsp` — start the platformOS Language Server Protocol server for Liquid, GraphQL, and JSON files. Provides autocompletion, diagnostics, hover docs, and go-to-definition in LSP-compatible editors. * `pos-cli check init [path]` — generate a `.platformos-check.yml` configuration file for the Liquid code quality checker. -* `pos-cli check run [path]` — run the platformos-check Node.js linter on Liquid/JSON files. Supports `-a` (auto-fix), `-f json` (JSON output), `-s` (silent). No Ruby required. +* `pos-cli check run [path]` — run the platformos-check Node.js linter on Liquid/JSON files. Supports `-a` (auto-fix), `-c, --check ` (filter by check name, repeatable), `-f json` (JSON output), `-s` (silent). No Ruby required. * `pos-cli check update-docs` — download the latest platformOS Liquid documentation used by the linter, keeping checks like `UndefinedObject` and `UndefinedFilter` up to date. * `pos-cli fetch-logs [environment]` — fetch recent logs as NDJSON (newline-delimited JSON), designed for machine consumption and used internally by the MCP server. Supports `--last-log-id` for incremental polling. * `pos-cli exec liquid` — execute Liquid code directly on an instance (supports `-f` flag to load from file, requires confirmation on production) diff --git a/README.md b/README.md index 82739b58..0b856345 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ The output includes: #### Options - `-a` - Enable automatic fixing of issues where possible +- `-c, --check ` - Only show offenses from the named check (repeatable, e.g. `-c UndefinedObject -c UnusedAssign`) - `-f ` - Output format: `text` (default) or `json` - `-s, --silent` - Only show errors, no success messages @@ -162,6 +163,14 @@ Silent mode (no success message): pos-cli check run -s +Filter by specific check: + + pos-cli check run --check UndefinedObject + +Filter by multiple checks: + + pos-cli check run -c UndefinedObject -c UnusedAssign + Combined options: pos-cli check run -a -f json diff --git a/bin/pos-cli-check-run.js b/bin/pos-cli-check-run.js index 3b439da0..c17fe682 100755 --- a/bin/pos-cli-check-run.js +++ b/bin/pos-cli-check-run.js @@ -8,6 +8,7 @@ program .description('check Liquid code quality with platformos-check linter') .argument('[path]', 'path to check (defaults to current directory)', process.cwd()) .option('-a', 'enable automatic fixing') + .option('-c, --check ', 'only show offenses from the named check (repeatable)', collect, []) .option('-f ', 'output format: text or json', 'text') .option('-s, --silent', 'only show errors, no success messages') .action(async (checkPath, options) => { @@ -16,9 +17,14 @@ program await run({ path: absolutePath, autoFix: options.a || false, + checks: options.check.length > 0 ? options.check : undefined, format: options.f || 'text', silent: options.silent || false }); }); +function collect(value, previous) { + return previous.concat([value]); +} + program.parse(process.argv); diff --git a/lib/check.js b/lib/check.js index d9dec135..a554c7c6 100644 --- a/lib/check.js +++ b/lib/check.js @@ -350,12 +350,25 @@ const initConfig = async (rootPath) => { }; const run = async (opts) => { - const { path: checkPath, autoFix, format, silent } = opts; + const { path: checkPath, autoFix, checks, format, silent } = opts; await validatePath(checkPath); const platformosCheck = await loadPlatformosCheck(); + if (checks && checks.length > 0) { + const validNames = new Set(platformosCheck.allChecks.map((c) => c.meta.code)); + const unknown = checks.filter((name) => !validNames.has(name)); + if (unknown.length > 0) { + const available = Array.from(validNames).sort().join(', '); + await logger.Error( + `Unknown check${unknown.length > 1 ? 's' : ''}: ${unknown.join(', ')}\n` + + `Available checks: ${available}` + ); + return; + } + } + let offenses = []; let spinner; let app; @@ -374,7 +387,9 @@ const run = async (opts) => { } }); - offenses = result.offenses; + offenses = checks + ? result.offenses.filter((o) => checks.includes(o.check)) + : result.offenses; app = result.app; // Update spinner with completion info if it's still running diff --git a/test/integration/check.test.js b/test/integration/check.test.js index 231c8037..1b09847b 100644 --- a/test/integration/check.test.js +++ b/test/integration/check.test.js @@ -98,6 +98,47 @@ describe('pos-cli check run', () => { expect(json.files[0].offenses[0].severity).toEqual('warning'); }); + test('Filter by check name with --check', async () => { + const { stdout, code } = await run('with-issues', '--check UndefinedObject'); + + expect(code).toEqual(1); + expect(stdout).toMatch('3 offenses found in 1 file'); + expect(stdout).toMatch('UndefinedObject'); + }); + + test('Errors on unknown check name', async () => { + const { stderr, code } = await run('with-issues', '--check NonExistentCheck'); + + expect(code).toEqual(1); + expect(stderr).toMatch('Unknown check: NonExistentCheck'); + expect(stderr).toMatch('Available checks:'); + }); + + test('Filter by check name with -c shorthand', async () => { + const { stdout, code } = await run('with-issues', '-c UndefinedObject'); + + expect(code).toEqual(1); + expect(stdout).toMatch('3 offenses found in 1 file'); + expect(stdout).toMatch('UndefinedObject'); + }); + + test('JSON output with --check filter', async () => { + const { stdout, code } = await run('with-issues', '-f json --check UndefinedObject'); + + expect(code).toEqual(1); + + const json = JSON.parse(stdout); + expect(json.offenseCount).toEqual(3); + expect(json.files[0].offenses.every(o => o.check === 'UndefinedObject')).toBe(true); + }); + + test('JSON output errors on unknown check name', async () => { + const { stderr, code } = await run('with-issues', '-f json --check NonExistentCheck'); + + expect(code).toEqual(1); + expect(stderr).toMatch('Unknown check: NonExistentCheck'); + }); + test('Specific path argument', async () => { const cwdPath = cwd('with-issues'); const specificPath = path.join(cwdPath, 'app/views/pages'); @@ -199,6 +240,8 @@ describe('pos-cli check run', () => { expect(stdout).toMatch('Usage: pos-cli check run'); expect(stdout).toMatch('-a'); expect(stdout).toMatch('enable automatic fixing'); + expect(stdout).toMatch('-c, --check '); + expect(stdout).toMatch('only show offenses from the named check'); expect(stdout).toMatch('-f '); expect(stdout).toMatch('output format'); expect(stdout).toMatch('-s, --silent');