Skip to content

feat(cli): improve validate command with JSON output and file filter#2790

Merged
davydkov merged 5 commits intomainfrom
feat/cli-validate-improvements
Mar 20, 2026
Merged

feat(cli): improve validate command with JSON output and file filter#2790
davydkov merged 5 commits intomainfrom
feat/cli-validate-improvements

Conversation

@davydkov
Copy link
Copy Markdown
Member

@davydkov davydkov commented Mar 20, 2026

Summary

  • Add --json flag for structured JSON output from likec4 validate
  • Add --file flag to filter validation errors to specific files
  • Add --no-layout flag to skip layout drift checks
  • Fix exit code to properly return 1 on validation failure
  • Add documentCount() method to language services
  • Improve CLI option grouping and descriptions

Split from #2782 (CLI changes only, SKILL work remains in #2782).

Test plan

  • likec4 validate returns exit code 0 on success, 1 on failure
  • likec4 validate --json outputs structured JSON
  • likec4 validate --file <path> filters to specific file
  • likec4 validate --no-layout skips layout drift checks

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Enhanced likec4 validate with options: --json, --file (filter), --no-layout, and --project.
    • Help output now groups and reveals global options for clearer CLI guidance.
    • Added success/failure summary messages and structured JSON output mode.
  • Bug Fixes / Behavior

    • Command now exits with status code 1 on validation failure.
    • Validation error messages are truncated to shorter, more concise summaries.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 20, 2026

🦋 Changeset detected

Latest commit: e325972

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 20 packages
Name Type
likec4 Patch
@likec4/language-services Patch
@likec4/docs-astro Patch
@likec4/mcp Patch
@likec4/vite-plugin Patch
likec4-vscode Patch
@likec4/playground Patch
@likec4/style-preset Patch
@likec4/styles Patch
@likec4/config Patch
@likec4/core Patch
@likec4/diagram Patch
@likec4/generators Patch
@likec4/language-server Patch
@likec4/layouts Patch
@likec4/leanix-bridge Patch
@likec4/log Patch
@likec4/react Patch
@likec4/tsconfig Patch
@likec4/vscode-preview Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

Extends the likec4 validate CLI with structured JSON output, file filtering, disable-able layout checks, and workspace/project support; refactors diagnostics to use user documents only; ensures process exits with code 1 on validation failure; reorganizes global CLI options/help.

Changes

Cohort / File(s) Summary
Validate CLI
packages/likec4/src/cli/validate/index.ts
Adds --json, --file (repeatable), --layout/--no-layout, and --project options; emits structured ValidateResult; collects model and layout diagnostics, applies file filtering, sets process.exitCode to 1 on failures; removed old boolean helpers and added typed diagnostics.
Language Services (diagnostics)
packages/language-services/src/common/LikeC4.ts
Switches diagnostic sources from LangiumDocuments.all to userDocuments; introduces isErrorDiagnostic() and firstFiveLines() helpers; truncates messages to 5 lines; refactors error printing flow and adds documentCount() method.
CLI entry & options
packages/likec4/src/cli/index.ts, packages/likec4/src/cli/options.ts
Reorganized global options into a "Globals" group, enabled showing hidden options, adjusted --color visibility, and added desc: 'force log level' to logLevel option.
Changeset metadata
.changeset/improve-validate-command.md
New changeset documenting patch-level CLI behavior and language-services updates for the validate command.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as Validate Command
    participant LS as Language Services
    participant Layout as Layout Validator
    participant Filter as File Filter
    participant Output as Output Handler

    CLI->>LS: init(printErrors based on --json, throwIfInvalid: false)
    LS->>LS: getErrors() from userDocuments
    LS-->>CLI: model diagnostics

    alt layout enabled
        CLI->>Layout: diagrams(project)
        Layout-->>CLI: layout diagnostics
    end

    CLI->>Filter: apply --file filters
    Filter-->>CLI: filtered diagnostics

    alt --json
        CLI->>Output: serialize ValidateResult (stats + diagnostics)
        Output-->>CLI: print JSON
    else
        CLI->>Output: log layout diagnostics (if any)
        CLI->>Output: log summary and success/failure
    end

    CLI->>CLI: set process.exitCode (0 or 1)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • PR #2667: Modifies packages/language-services/src/common/LikeC4.ts (adds formatting APIs) and is likely related at the Same-Class code level.

Poem

🐰 I hopped through docs and tracked each trace,

JSON crumbs and file-scope in place,
Layout checks optional, summaries bright,
Exit code now tells if the build's all right,
A small rabbit clap — validation takes flight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main feature additions: JSON output capability and file filtering for the validate command.
Description check ✅ Passed The PR description covers the key changes and objectives, though the test plan items are unchecked rather than marked as completed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cli-validate-improvements
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
packages/language-services/src/common/LikeC4.ts (3)

197-202: Consider iterating to count instead of spreading.

Spreading the iterator into an array allocates memory just to get the count. If userDocuments is large, this could be inefficient.

♻️ Alternative using iteration
   documentCount(): number {
-    return [...this.LangiumDocuments.userDocuments].length
+    let count = 0
+    for (const _ of this.LangiumDocuments.userDocuments) {
+      count++
+    }
+    return count
   }

Alternatively, if LangiumDocuments exposes a size or count property on the underlying collection, that would be even better.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/language-services/src/common/LikeC4.ts` around lines 197 - 202, The
documentCount() implementation currently spreads
this.LangiumDocuments.userDocuments into an array which allocates memory; change
it to count via iteration (or use an existing size/length property on the
underlying collection if available) to avoid allocation — iterate
this.LangiumDocuments.userDocuments with a simple counter and return the counter
in documentCount(), referencing the documentCount method and
this.LangiumDocuments.userDocuments when locating the change.

21-23: Consider documenting the magic number.

severity === 1 corresponds to DiagnosticSeverity.Error from the LSP specification. A brief comment would improve readability.

📝 Suggested documentation
+/** Checks if a diagnostic is an error (DiagnosticSeverity.Error === 1 in LSP). */
 const isErrorDiagnostic = (diagnostic: { severity?: number }): boolean => {
   return diagnostic.severity === 1
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/language-services/src/common/LikeC4.ts` around lines 21 - 23, The
check in isErrorDiagnostic uses the magic number diagnostic.severity === 1;
replace or document this by referencing the LSP enum (DiagnosticSeverity.Error)
— either import/define a named constant like DiagnosticSeverity.Error and use it
in the comparison, or add a one-line comment above isErrorDiagnostic explaining
that 1 corresponds to DiagnosticSeverity.Error from the LSP spec; update the
function isErrorDiagnostic to use the named constant or include the comment for
clarity.

157-161: Use optional chaining for cleaner code.

The static analysis tool correctly identifies that optional chaining would be more concise here.

♻️ Suggested refactor
   hasErrors(): boolean {
-    return this.LangiumDocuments.userDocuments.some(doc => {
-      return doc.diagnostics && doc.diagnostics.some(isErrorDiagnostic)
-    })
+    return this.LangiumDocuments.userDocuments.some(doc =>
+      doc.diagnostics?.some(isErrorDiagnostic) ?? false
+    )
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/language-services/src/common/LikeC4.ts` around lines 157 - 161, The
hasErrors() method is verbose; simplify by using optional chaining on
doc.diagnostics when checking for error diagnostics. Update the implementation
in LikeC4.hasErrors to use this.LangiumDocuments.userDocuments.some(doc =>
doc.diagnostics?.some(isErrorDiagnostic)) (or equivalent) so you avoid explicit
&& checks and make the code more concise and readable.
packages/likec4/src/cli/validate/index.ts (1)

145-154: Clarify filteredFiles semantics in stats.

When no file filter is applied, filteredFiles is set to totalFiles (total documents parsed). However, when a filter is applied, filteredFiles is the count of unique files with errors. This asymmetry may confuse consumers of the JSON output.

Consider whether filteredFiles should consistently represent "files with errors" or "files considered in validation."

📝 Option: Consistent "files with errors" semantics
+        const allFileSet = new Set(allErrors.map(e => e.file))
         const filteredFileSet = new Set(filteredErrors.map(e => e.file))

         const result: ValidateResult = {
           valid,
           errors: filteredErrors,
           stats: {
             totalFiles,
             totalErrors,
-            filteredFiles: fileFilter ? filteredFileSet.size : totalFiles,
+            filteredFiles: fileFilter ? filteredFileSet.size : allFileSet.size,
             filteredErrors: filteredErrors.length,
           },
         }

Alternatively, document the current behavior clearly in a JSDoc comment on ValidateResult.stats.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/likec4/src/cli/validate/index.ts` around lines 145 - 154, The stats
field is inconsistent: filteredFiles currently equals totalFiles when no
fileFilter is set but equals the count of unique error files when a filter is
applied; make it consistent by defining filteredFiles to always mean "files with
errors." Update the construction of result.stats in the block that builds
ValidateResult so filteredFiles is always set to filteredFileSet.size (ensure
filteredFileSet is populated with files that have errors regardless of
fileFilter), and add or update a JSDoc on ValidateResult.stats to state that
filteredFiles represents the number of files with validation errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/language-services/src/common/LikeC4.ts`:
- Around line 197-202: The documentCount() implementation currently spreads
this.LangiumDocuments.userDocuments into an array which allocates memory; change
it to count via iteration (or use an existing size/length property on the
underlying collection if available) to avoid allocation — iterate
this.LangiumDocuments.userDocuments with a simple counter and return the counter
in documentCount(), referencing the documentCount method and
this.LangiumDocuments.userDocuments when locating the change.
- Around line 21-23: The check in isErrorDiagnostic uses the magic number
diagnostic.severity === 1; replace or document this by referencing the LSP enum
(DiagnosticSeverity.Error) — either import/define a named constant like
DiagnosticSeverity.Error and use it in the comparison, or add a one-line comment
above isErrorDiagnostic explaining that 1 corresponds to
DiagnosticSeverity.Error from the LSP spec; update the function
isErrorDiagnostic to use the named constant or include the comment for clarity.
- Around line 157-161: The hasErrors() method is verbose; simplify by using
optional chaining on doc.diagnostics when checking for error diagnostics. Update
the implementation in LikeC4.hasErrors to use
this.LangiumDocuments.userDocuments.some(doc =>
doc.diagnostics?.some(isErrorDiagnostic)) (or equivalent) so you avoid explicit
&& checks and make the code more concise and readable.

In `@packages/likec4/src/cli/validate/index.ts`:
- Around line 145-154: The stats field is inconsistent: filteredFiles currently
equals totalFiles when no fileFilter is set but equals the count of unique error
files when a filter is applied; make it consistent by defining filteredFiles to
always mean "files with errors." Update the construction of result.stats in the
block that builds ValidateResult so filteredFiles is always set to
filteredFileSet.size (ensure filteredFileSet is populated with files that have
errors regardless of fileFilter), and add or update a JSDoc on
ValidateResult.stats to state that filteredFiles represents the number of files
with validation errors.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c0d4fe39-f10e-49e0-826b-43fd2c040baf

📥 Commits

Reviewing files that changed from the base of the PR and between a0976a8 and 9cf1e8e.

📒 Files selected for processing (6)
  • .changeset/improve-validate-command.md
  • packages/language-services/src/common/LikeC4.ts
  • packages/likec4/src/cli/check-update/utils.ts
  • packages/likec4/src/cli/index.ts
  • packages/likec4/src/cli/options.ts
  • packages/likec4/src/cli/validate/index.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/language-services/src/common/LikeC4.ts (1)

167-169: Use optional chaining in hasErrors() for cleaner boolean logic.

The current expression works, but optional chaining + nullish coalescing is more idiomatic. Note that the same method (printErrors()) on line 177 already uses this pattern (doc.diagnostics?.filter(isErrorDiagnostic) ?? []).

Proposed diff
   hasErrors(): boolean {
-    return this.LangiumDocuments.userDocuments.some(doc => {
-      return doc.diagnostics && doc.diagnostics.some(isErrorDiagnostic)
-    })
+    return this.LangiumDocuments.userDocuments.some(doc => {
+      return doc.diagnostics?.some(isErrorDiagnostic) ?? false
+    })
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/language-services/src/common/LikeC4.ts` around lines 167 - 169, The
hasErrors() method currently uses explicit truthiness checks for
doc.diagnostics; update it to use optional chaining and nullish coalescing like
printErrors() does: inside the arrow passed to
this.LangiumDocuments.userDocuments.some, replace the existing doc.diagnostics
&& doc.diagnostics.some(...) logic with an expression that calls
doc.diagnostics?.some(isErrorDiagnostic) ?? false so missing diagnostics are
treated as false; this keeps behavior identical but uses the more idiomatic
optional chaining pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/language-services/src/common/LikeC4.ts`:
- Around line 167-169: The hasErrors() method currently uses explicit truthiness
checks for doc.diagnostics; update it to use optional chaining and nullish
coalescing like printErrors() does: inside the arrow passed to
this.LangiumDocuments.userDocuments.some, replace the existing doc.diagnostics
&& doc.diagnostics.some(...) logic with an expression that calls
doc.diagnostics?.some(isErrorDiagnostic) ?? false so missing diagnostics are
treated as false; this keeps behavior identical but uses the more idiomatic
optional chaining pattern.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ac80998f-0c75-41af-ac20-059a11451f77

📥 Commits

Reviewing files that changed from the base of the PR and between b63f491 and e325972.

📒 Files selected for processing (1)
  • packages/language-services/src/common/LikeC4.ts

@davydkov davydkov merged commit 9a3fa0b into main Mar 20, 2026
16 checks passed
@davydkov davydkov deleted the feat/cli-validate-improvements branch March 20, 2026 15:14
@likec4-ci likec4-ci bot mentioned this pull request Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant