Skip to content

Conversation

kpavlov
Copy link
Collaborator

@kpavlov kpavlov commented Sep 25, 2025

Improve response logging via HttpFormatter

  • Colorize JSON string response logs, where possible (näive approach)

@kpavlov kpavlov added the enhancement New feature or request label Sep 25, 2025
Copy link

codacy-production bot commented Sep 25, 2025

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
+0.33% (target: -1.00%) 90.48%
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (0245814) 7007 4824 68.85%
Head commit (47e0d11) 7102 (+95) 4913 (+89) 69.18% (+0.33%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#390) 126 114 90.48%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

Copy link
Contributor

coderabbitai bot commented Sep 25, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Rich, formatted verbose logging for HTTP responses and streaming chunks.
    • Streaming responses now support chunk-specific content types and improved SSE handling/encoding.
  • Bug Fixes
    • Responses consistently set Content-Type: application/json across OpenAI, Anthropic, Gemini, Ollama, and A2A mocks.
  • Refactor
    • Response API streamlined: non-null content type; constructors accept headers, delay, and optional body.
  • Chores
    • Added SSE dependency to enable server-sent events features.
  • Tests
    • Updated to use the new formatter-aware APIs.

Walkthrough

Propagates a new HttpFormatter through stub-building and response layers, adds formatted verbose logging for responses and streamed chunks, makes AbstractResponseDefinition.contentType non‑nullable and adds headerList/delay/responseBody constructor properties, introduces SSE dependency and encoding changes, and updates tests and call sites.

Changes

Cohort / File(s) Summary
Building step & server wiring
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/BuildingStep.kt, mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/MokksyServer.kt, mokksy/src/commonTest/kotlin/me/kpavlov/mokksy/BuildingStepTest.kt
Add HttpFormatter parameter to BuildingStep (constructors/property); propagate httpFormatter from MokksyServer; update tests/call sites to pass formatter.
Response builders
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinitionBuilders.kt
Add formatter: HttpFormatter to ResponseDefinitionBuilder and StreamingResponseDefinitionBuilder; store/forward formatter when building responses; switch to headers.toList() and add chunkContentType for streaming builder.
Response definitions (standard & streaming/SSE)
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/AbstractResponseDefinition.kt, .../ResponseDefinition.kt, .../StreamResponseDefinition.kt, .../SseStreamResponseDefinition.kt
Constructors accept formatter: HttpFormatter; AbstractResponseDefinition.contentType becomes non‑nullable and gains headerList, delay, responseBody ctor properties; streaming class gains chunkContentType and logging changes; verbose logging now uses formatter and logger-based chunk/error handling.
Formatter implementation
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/utils/logger/HttpFormatter.kt
Add public responseLine(httpVersion, status) and helpers formatResponseHeader, formatResponse, formatResponseChunk to produce formatted status line, headers, full response blocks, and per‑chunk formatting.
SSE dependency & encoding
ai-mocks-gemini/build.gradle.kts, gradle/libs.versions.toml, ai-mocks-gemini/src/commonMain/kotlin/.../GeminiStreamingContentBuildingStep.kt
Add Ktor SSE dependency (ktor-sse) and update SSE encoding to use TypedServerSentEvent with Json.encodeToString (annotated @OptIn where required).
AI mock response content-type defaults
ai-mocks-*/src/.../*BuildingStep.kt (multiple files)
Set contentType = ContentType.Application.Json in various AI mock BuildingStep respond flows (OpenAI, Gemini, Anthropic, Ollama, etc.).
Misc (formatting-only)
mokksy/src/jvmMain/kotlin/me/kpavlov/mokksy/utils/Base64Urls.jvm.kt
Reflow function signature formatting only (no behavioral change).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Developer
  participant Server as MokksyServer
  participant Step as BuildingStep
  participant Builder as ResponseDefinitionBuilder / StreamingResponseDefinitionBuilder
  participant Resp as ResponseDefinition / StreamResponseDefinition
  participant Call as Ktor ApplicationCall
  participant Fmt as HttpFormatter

  Dev->>Server: register stub / define route
  Server->>Step: create BuildingStep(..., formatter = httpFormatter)
  Step->>Builder: create builder(..., formatter)
  Builder->>Resp: build(..., formatter)

  Call->>Resp: writeResponse(call, verbose, chunkContentType?)
  alt verbose
    Resp->>Fmt: formatResponse(...) / formatResponseChunk(...)
    Fmt-->>Resp: formatted text
    Resp->>Call: send headers/body/chunks (and log formatted output)
  else non-verbose
    Resp->>Call: send headers/body/chunks (no formatted log)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

refactoring

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly captures the primary change of the pull request by indicating the addition of enhanced response logging support via HttpFormatter, directly reflecting the introduction and propagation of HttpFormatter throughout the response-building and logging flow.
Description Check ✅ Passed The description clearly states the goal of improving response logging using HttpFormatter and colorizing JSON response logs, which directly corresponds to the changes throughout the codebase that add and integrate HttpFormatter for enhanced log formatting.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch colorize-output

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9435326 and 47e0d11.

📒 Files selected for processing (4)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinition.kt (3 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinitionBuilders.kt (7 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/SseStreamResponseDefinition.kt (4 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/StreamResponseDefinition.kt (7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build

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
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.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (8)
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinition.kt (1)

55-75: Honor runtime body overrides in ResponseDefinition.writeResponse.

AbstractResponseDefinition.withResponseBody now allows call sites to mutate the payload by setting responseBody, but this override is never read here—we still log and send body. As a result, any dynamic response shaping is silently ignored. Please resolve the effective payload once (e.g., val effectiveBody = responseBody ?: body) and use it for both logging and call.respond.

-        if (verbose) {
+        val effectiveBody = responseBody ?: body
+        if (verbose) {
             call.application.log.debug(
                 "Sending:\n---\n${
                     formatter.formatResponse(
                         httpVersion = call.request.httpVersion,
                         headers = call.response.headers,
                         contentType = this.contentType,
                         status = httpStatus,
-                        body = body?.toString(),
+                        body = effectiveBody?.toString(),
                     )
                 }---\n",
             )
         }
         try {
-            call.respond(
+            val payload: Any = effectiveBody ?: ""
+            call.respond(
                 status = httpStatus,
-                message = body ?: "" as Any,
+                message = payload,
             )
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/AbstractResponseDefinition.kt (1)

32-32: Avoid exposing mutable responseBody as public.

Make it protected to restrict mutation to subclasses. External mutation should go through withResponseBody.

Apply this diff:

-    public var responseBody: T? = null,
+    protected var responseBody: T? = null,
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinitionBuilders.kt (1)

53-55: Keep httpStatusCode and httpStatus consistent.

httpStatus(status: Int) updates only httpStatus, leaving httpStatusCode stale.

Apply this diff:

-    public fun httpStatus(status: Int) {
-        this.httpStatus = HttpStatusCode.fromValue(status)
-    }
+    public fun httpStatus(status: Int) {
+        this.httpStatusCode = status
+        this.httpStatus = HttpStatusCode.fromValue(status)
+    }
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/SseStreamResponseDefinition.kt (2)

41-44: Invalid logger usage: Ktor Logger doesn’t support SLF4J-style {} placeholders.

This won’t compile; debug accepts a single String.

Apply this diff:

-                        if (verbose) {
-                            call.application.log.debug("Sending {}: {}", httpStatus, it)
-                        }
+                        if (verbose) {
+                            call.application.log.debug("Sending $httpStatus: $it")
+                        }

64-66: Honor configured status for SSE responses.

Use httpStatus instead of hardcoded HttpStatusCode.OK.

Apply this diff:

-        call.response.status(HttpStatusCode.OK)
+        call.response.status(httpStatus)
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/StreamResponseDefinition.kt (3)

170-199: Apply headers before streaming from Flow.

headersLambda and headerList are not applied; responses may miss configured headers.

Apply this diff:

     override suspend fun writeResponse(
         call: ApplicationCall,
         verbose: Boolean,
         chunkContentType: ContentType?,
     ) {
-        when {
+        // Apply configured headers
+        headers?.invoke(call.response.headers)
+        for ((name, value) in headerList) {
+            call.response.headers.append(name, value)
+        }
+        when {
             chunkFlow != null -> {
                 call.response.cacheControl(CacheControl.NoCache(null))
                 call.respondBytesWriter(
                     status = this.httpStatus,
                     contentType = this.contentType,
                 ) {

202-215: Parity: log formatted headers for list-based chunks too.

Add the same verbose header log as in the Flow branch.

Apply this diff:

                 call.respondBytesWriter(
                     status = this.httpStatus,
                     contentType = this.contentType,
                 ) {
+                    if (verbose) {
+                        call.application.log.debug(
+                            "Sending:\n---\n${
+                                formatter.formatResponseHeader(
+                                    httpVersion = call.request.httpVersion,
+                                    headers = call.response.headers,
+                                    status = httpStatus,
+                                )
+                            }",
+                        )
+                    }
                     writeChunksFromList(
                         writer = this,
                         verbose = verbose,
                         logger = call.application.log,
                         chunkContentType = chunkContentType,
                     )

137-138: Remove unused overload of writeChunksFromFlow(session: ServerSSESession)
This method (and its internal print call) in StreamResponseDefinition.kt isn’t referenced—remove it (including the @Suppress("unused") annotation) to eliminate dead code and avoid print in library code.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f3c3ad and 4e7ea11.

📒 Files selected for processing (10)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/BuildingStep.kt (5 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/MokksyServer.kt (1 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/AbstractResponseDefinition.kt (2 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinition.kt (3 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinitionBuilders.kt (5 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/SseStreamResponseDefinition.kt (2 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/StreamResponseDefinition.kt (8 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/utils/logger/HttpFormatter.kt (5 hunks)
  • mokksy/src/commonTest/kotlin/me/kpavlov/mokksy/BuildingStepTest.kt (2 hunks)
  • mokksy/src/jvmMain/kotlin/me/kpavlov/mokksy/utils/Base64Urls.jvm.kt (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/utils/logger/HttpFormatter.kt (2)
ai-mocks-a2a/src/jvmTest/kotlin/me/kpavlov/mokksy/utils/logger/HighlightingTest.kt (1)
  • colorize (73-76)
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/utils/logger/Highlighting.kt (1)
  • highlightBody (15-23)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build
🔇 Additional comments (8)
mokksy/src/jvmMain/kotlin/me/kpavlov/mokksy/utils/Base64Urls.jvm.kt (1)

11-13: Signature reflow keeps API intact

The multiline signature with trailing comma is idiomatic Kotlin and preserves behavior. Looks good.

mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/BuildingStep.kt (1)

70-108: Formatter plumbing is consistent

Forwarding the injected HttpFormatter into both the regular and streaming response builders ensures all response paths share the same formatting logic. Nice work.

mokksy/src/commonTest/kotlin/me/kpavlov/mokksy/BuildingStepTest.kt (1)

49-56: Test setup updated correctly

Passing a concrete HttpFormatter() into the subject mirrors the production wiring and keeps the fixture aligned with the new constructor signature. 👍

mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/MokksyServer.kt (1)

195-201: Formatter propagates from server entry point

Injecting the shared httpFormatter into each BuildingStep maintains consistent formatting across all stub builders. Looks good.

mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinitionBuilders.kt (1)

87-88: Formatter wiring LGTM.

Passing HttpFormatter through the builders to concrete definitions is correct.

mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/SseStreamResponseDefinition.kt (1)

24-26: Formatter injection LGTM.

Constructor change correctly forwards formatter to the superclass.

mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/StreamResponseDefinition.kt (1)

60-61: Formatter injection LGTM.

Passing formatter into the streaming response definition is correct.

mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/AbstractResponseDefinition.kt (1)

37-38: All writeResponse overrides updated with chunkContentType.

@kpavlov kpavlov force-pushed the colorize-output branch 2 times, most recently from 457a003 to cdd06e2 Compare September 25, 2025 07:21
- Enhanced response lifecycle methods across multiple classes to support verbose logging with chunk details.
- Updated `ResponseDefinition` and its builders to integrate `HttpFormatter` for better structured output.
@kpavlov
Copy link
Collaborator Author

kpavlov commented Sep 25, 2025

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Sep 25, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/AbstractResponseDefinition.kt (1)

18-31: Document the new delay and responseBody properties.

The constructor now exposes delay and responseBody, but the KDoc still omits them. Updating the doc keeps the contract clear for consumers.

Apply this diff to extend the property list:

  * @property headers A lambda function for configuring the response headers. Defaults to `null`.
  * @property headerList A list of header key-value pairs to populate the response headers. Defaults to an empty list.
+ * @property delay A delay applied before sending the response. Defaults to [Duration.ZERO].
+ * @property responseBody The optional response payload associated with this definition.
 */
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e7ea11 and ab97da3.

📒 Files selected for processing (13)
  • ai-mocks-gemini/build.gradle.kts (1 hunks)
  • ai-mocks-gemini/src/commonMain/kotlin/me/kpavlov/aimocks/gemini/content/GeminiStreamingContentBuildingStep.kt (2 hunks)
  • gradle/libs.versions.toml (1 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/BuildingStep.kt (5 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/MokksyServer.kt (1 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/AbstractResponseDefinition.kt (1 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinition.kt (3 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinitionBuilders.kt (6 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/SseStreamResponseDefinition.kt (2 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/StreamResponseDefinition.kt (8 hunks)
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/utils/logger/HttpFormatter.kt (5 hunks)
  • mokksy/src/commonTest/kotlin/me/kpavlov/mokksy/BuildingStepTest.kt (2 hunks)
  • mokksy/src/jvmMain/kotlin/me/kpavlov/mokksy/utils/Base64Urls.jvm.kt (1 hunks)
👮 Files not reviewed due to content moderation or server errors (6)
  • ai-mocks-gemini/build.gradle.kts
  • gradle/libs.versions.toml
  • mokksy/src/commonTest/kotlin/me/kpavlov/mokksy/BuildingStepTest.kt
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/MokksyServer.kt
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinition.kt
  • mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/BuildingStep.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (4)
mokksy/src/jvmMain/kotlin/me/kpavlov/mokksy/utils/Base64Urls.jvm.kt (1)

11-13: Formatting-only change looks good

Expanding the parameter list for readability keeps behavior intact. No concerns.

mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/utils/logger/HttpFormatter.kt (1)

189-193: Compile error: iterate allValues() without calling entries()

Line 190 currently invokes headers.allValues().entries() but allValues() already returns a Map<String, List<String>>. Calling entries() on that map won’t compile. Iterate the map directly so the formatter can build header lines.

Apply this diff:

-            headers.allValues().entries().forEach { (key, value) ->
-                appendLine(header(key, value))
-            }
+            headers
+                .allValues()
+                .forEach { (key, values) ->
+                    appendLine(header(key, values))
+                }
mokksy/src/commonMain/kotlin/me/kpavlov/mokksy/response/ResponseDefinitionBuilders.kt (1)

9-9: Remove JVM-only Collections usage in commonMain.

java.util.Collections isn’t available to commonMain, so this won’t compile for non-JVM targets. Copy the headers with toList() (or another multiplatform-safe option) and drop the import.

-import me.kpavlov.mokksy.utils.logger.HttpFormatter
-import java.util.Collections
+import me.kpavlov.mokksy.utils.logger.HttpFormatter
@@
-            headerList = Collections.unmodifiableList(headers),
+            headerList = headers.toList(),

Also applies to: 149-154

ai-mocks-gemini/src/commonMain/kotlin/me/kpavlov/aimocks/gemini/content/GeminiStreamingContentBuildingStep.kt (1)

95-100: TypedServerSentEvent serialization looks solid.

Line 96: Switching to TypedServerSentEvent and delegating the payload encoding to Json.encodeToString removes the brittle, hand-crafted SSE formatting and yields well-formed events. Nicely done.

Copy link
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.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab97da3 and 792487d.

📒 Files selected for processing (1)
  • gradle/libs.versions.toml (1 hunks)

ktor-server-double-receive = { module = "io.ktor:ktor-server-double-receive" }
ktor-server-netty = { module = "io.ktor:ktor-server-netty" }
ktor-server-sse = { module = "io.ktor:ktor-server-sse" }
ktor-sse = { module = "io.ktor:ktor-sse" }
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

io.ktor:ktor-sse artifact doesn’t exist in Ktor 3.3.0
Ktor only publishes ktor-server-sse / ktor-client-sse. This alias will break dependency resolution as Maven can’t find io.ktor:ktor-sse. Please point the alias to the actual module you need (e.g., ktor-client-sse) or drop it.

🤖 Prompt for AI Agents
In gradle/libs.versions.toml around line 67, the alias ktor-sse is pointing to a
non-existent artifact "io.ktor:ktor-sse"; replace it with the correct module
published by Ktor 3.3.0 (for example "io.ktor:ktor-client-sse" or
"io.ktor:ktor-server-sse" depending on whether you need client or server SSE) or
remove the alias entirely so dependency resolution won't fail.

@kpavlov kpavlov marked this pull request as ready for review October 2, 2025 19:41
- Update `ResponseDefinition` to utilize `effectiveBody` for improved logging and response payload handling.
- Allow application of custom headers in `StreamResponseDefinition`.
- Fix logging format in `SseStreamResponseDefinition` and ensure proper usage of `httpStatus`.
- Add support for configuring `httpStatusCode` in response builders.
@kpavlov kpavlov merged commit 0ec5d33 into main Oct 2, 2025
9 checks passed
@kpavlov kpavlov deleted the colorize-output branch October 2, 2025 21:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant