Skip to content

feat: add /clear-screen slash command#647

Open
VitalyOstanin wants to merge 5 commits intoPiebald-AI:mainfrom
VitalyOstanin:feature/clear-screen
Open

feat: add /clear-screen slash command#647
VitalyOstanin wants to merge 5 commits intoPiebald-AI:mainfrom
VitalyOstanin:feature/clear-screen

Conversation

@VitalyOstanin
Copy link
Copy Markdown

@VitalyOstanin VitalyOstanin commented Mar 31, 2026

Summary

Add /clear-screen slash command that clears the screen without resetting conversation context.

  • All messages remain in the messages array (full API context preserved)
  • Messages are hidden from ink rendering via UUID-based filter on g97
  • Terminal scrollback cleared via ANSI escape sequences
  • Ink repaint triggered via globalThis.__tweakccForceRedraw
  • Recommended keybinding: ctrl+k ctrl+l (Chat context)

How it works

Three-part patch:

  1. Expose ink's forceRedraw() — patches the app:redraw callback registration site to assign globalThis.__tweakccForceRedraw pointing to the ink instance's forceRedraw() method
  2. Patch render filter g97 — injects a check at the top of the render visibility function: if a message's UUID prefix is in globalThis.__tweakccHiddenUUIDs, return false (hide from UI). This works for both user and assistant messages, including multi-content assistant messages whose UUIDs are derived via m$ (first 24 chars stay the same)
  3. Register /clear-screen command — a type:"local" slash command that collects all current message UUID prefixes into a Set on globalThis.__tweakccHiddenUUIDs, clears terminal scrollback, and triggers ink repaint

Why UUID-based hiding instead of replacing messages

The initial approach replaced the messages array via setMessages, keeping only the last assistant message with content: [] to preserve ctx%. This caused Claude to lose conversation context — it would respond with "This is the first message in our session" because setMessages is the sole source for API messages. The UUID-based approach leaves all messages intact in the array (preserving full API context) and only hides them from ink rendering.

Test plan

  • /clear-screen clears visible messages while preserving full conversation context
  • After /clear-screen, Claude remembers prior conversation (no "first message in session")
  • ctrl+k ctrl+l keybinding triggers the command (Chat context)
  • Terminal scrollback is cleared
  • Ink repaints correctly after clear
  • Unit tests pass (12 tests covering both writeClearScreen and patchRenderFilter: success, already patched, pattern not found, context preservation, delimiter variants, different function/argument names)
  • Tested on Claude Code 2.1.87 and 2.1.88

Summary by CodeRabbit

  • New Features

    • Added a clear-screen slash command that clears the terminal and triggers a screen redraw, with wiring to message/UUID filtering to avoid affecting hidden items.
  • Tests

    • Added comprehensive tests covering command injection, render-filter patching, edge cases (already-present command, missing patterns, delimiter variations) and preservation of original redraw behavior.
  • Chores

    • Integrated the clear-screen patch into the patch application pipeline.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new "clear-screen" patch and tests: discovers an existing redraw callback that calls forceRedraw(), exposes it as globalThis.__tweakccForceRedraw, injects a guard into the render-filter to hide selected message UUIDs, and registers an inline /clear-screen slash command that clears the terminal and triggers a redraw.

Changes

Cohort / File(s) Summary
Clear Screen Patch
src/patches/clearScreen.ts
New exports `writeClearScreen(oldFile: string): string
Clear Screen Tests
src/patches/clearScreen.test.ts
New Vitest suite asserting writeClearScreen and patchRenderFilter behavior: injection of __tweakccForceRedraw, registration/contents of the /clear-screen command, hidden-UUID guard injection, idempotency when command already present, and null returns for missing patterns or differing delimiters.
Patch Registry Wiring
src/patches/index.ts
Adds clear-screen to PATCH_DEFINITIONS (always-applied) and wires patchImplementations['clear-screen'] to call writeClearScreen, enabling application via existing patch pipeline.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Suggested reviewers

  • bl-ue

Poem

🐇 I nibble through lines with whiskers bright,
I tie a redraw cord and hide the light.
A slash clears the clutter, UUIDs take flight,
Screens breathe anew beneath the moonlight.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add /clear-screen slash command' clearly and directly describes the main addition in the pull request—a new slash command for clearing the screen.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/patches/clearScreen.ts (1)

1-1: Drop the new file-level comment unless it was explicitly requested.

This adds a non-essential comment on Line 1.

As per coding guidelines: Do not add comments unless explicitly requested.

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

In `@src/patches/clearScreen.ts` at line 1, Remove the non-essential file-level
comment at the top of src/patches/clearScreen.ts (the Line 1 comment "// Please
see the note about writing patches in ./index") unless that comment was
explicitly requested; simply delete that comment so the file starts with real
code or the intended export/implementation.
src/patches/clearScreen.test.ts (1)

13-55: Add an edge-case test for assistant messages without usage.

Please add coverage for input where an assistant message exists but message.usage is absent, to lock in intended fallback behavior for /clear-screen.

As per coding guidelines: Test edge cases and error conditions in test files.

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

In `@src/patches/clearScreen.test.ts` around lines 13 - 55, Add a new test in
src/patches/clearScreen.test.ts that constructs an input via makeInput() but
includes an assistant message object that omits message.usage (i.e., assistant
message without a usage property), then call writeClearScreen(input) and assert
the call does not crash (result not null) and the patch still registers the
clear-screen behavior by checking for strings like 'name:"clear-screen"' and
'globalThis.__tweakccForceRedraw' in the result; this uses the existing helpers
writeClearScreen and makeInput to locate the proper insertion point and verifies
the intended fallback handling for assistant messages without usage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/patches/clearScreen.test.ts`:
- Line 1: Remove the redundant import line that pulls in Vitest functions and
rely on the configured globals instead: delete the import { describe, expect, it
} from 'vitest' statement and use the global describe, it, and expect
identifiers directly in the test file (ensure no other references to the
imported symbols remain).

In `@src/patches/clearScreen.ts`:
- Around line 45-49: The current $.setMessages(m=>{...}) callback drops all
messages when no assistant message has message.usage; change the logic so it
still preserves the last assistant message as a fallback: search m from the end
for an assistant with message.usage and if found return a single modified
message with its message.content cleared, otherwise locate the last message
where m[k].type === "assistant" (if any) and return that assistant message with
content emptied; only return [] if there are no assistant messages. Update the
anonymous function passed to $.setMessages (the m parameter and its message
handling) to implement this fallback.

---

Nitpick comments:
In `@src/patches/clearScreen.test.ts`:
- Around line 13-55: Add a new test in src/patches/clearScreen.test.ts that
constructs an input via makeInput() but includes an assistant message object
that omits message.usage (i.e., assistant message without a usage property),
then call writeClearScreen(input) and assert the call does not crash (result not
null) and the patch still registers the clear-screen behavior by checking for
strings like 'name:"clear-screen"' and 'globalThis.__tweakccForceRedraw' in the
result; this uses the existing helpers writeClearScreen and makeInput to locate
the proper insertion point and verifies the intended fallback handling for
assistant messages without usage.

In `@src/patches/clearScreen.ts`:
- Line 1: Remove the non-essential file-level comment at the top of
src/patches/clearScreen.ts (the Line 1 comment "// Please see the note about
writing patches in ./index") unless that comment was explicitly requested;
simply delete that comment so the file starts with real code or the intended
export/implementation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 23f51e3c-88cb-4759-be93-549271f35167

📥 Commits

Reviewing files that changed from the base of the PR and between 2bd68d9 and 6217161.

📒 Files selected for processing (3)
  • src/patches/clearScreen.test.ts
  • src/patches/clearScreen.ts
  • src/patches/index.ts

Comment thread src/patches/clearScreen.test.ts
Comment thread src/patches/clearScreen.ts Outdated
@VitalyOstanin VitalyOstanin force-pushed the feature/clear-screen branch from b5b9501 to 43d31f2 Compare April 2, 2026 13:06
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)
src/patches/clearScreen.ts (1)

68-74: Consider adding a delimiter prefix for regex performance.

The pattern starts with (function without a leading delimiter character. Per the coding guidelines for patches, starting with literal characters like ,;{} can significantly improve V8 regex matching performance.

That said, function as a keyword is specific enough that this is unlikely to cause issues in practice.

♻️ Optional: Add delimiter prefix
 export const patchRenderFilter = (oldFile: string): string | null => {
   const pattern =
-    /(function [$\w]+\([$\w]+,[$\w]+\)\{)if\([$\w]+\.type!=="user"\)return!0;if\([$\w]+\.isMeta\)/;
+    /([,;{}])(function [$\w]+\([$\w]+,[$\w]+\)\{)if\([$\w]+\.type!=="user"\)return!0;if\([$\w]+\.isMeta\)/;
   const match = oldFile.match(pattern);

Then adjust the replacement to include the delimiter and update capture group indices accordingly.

As per coding guidelines: "Use , ; } { literal characters at regex start instead of \\b for performance in patch patterns"

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

In `@src/patches/clearScreen.ts` around lines 68 - 74, The regex in
patchRenderFilter (variable pattern) should start with a literal delimiter like
',', ';', '{', or '}' before the "function" keyword to improve V8 regex
performance; update the pattern to include that leading delimiter and then
adjust any replacement logic that relies on capture groups (and their indices)
after oldFile.match so the correct groups are referenced in the
replacement/patch operation in patchRenderFilter.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/patches/clearScreen.ts`:
- Around line 68-74: The regex in patchRenderFilter (variable pattern) should
start with a literal delimiter like ',', ';', '{', or '}' before the "function"
keyword to improve V8 regex performance; update the pattern to include that
leading delimiter and then adjust any replacement logic that relies on capture
groups (and their indices) after oldFile.match so the correct groups are
referenced in the replacement/patch operation in patchRenderFilter.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e45c65ff-dae3-4680-af65-29909a7429f1

📥 Commits

Reviewing files that changed from the base of the PR and between b5b9501 and 43d31f2.

📒 Files selected for processing (3)
  • src/patches/clearScreen.test.ts
  • src/patches/clearScreen.ts
  • src/patches/index.ts
✅ Files skipped from review due to trivial changes (1)
  • src/patches/clearScreen.test.ts

Clear screen without resetting conversation context.
Recommended keybinding: ctrl+k ctrl+l (Chat context).

How it works:
1. Exposes ink's forceRedraw() via globalThis.__tweakccForceRedraw
   by patching the app:redraw callback registration site
2. Registers /clear-screen as a local slash command that:
   - Clears messages via setMessages, keeping last assistant message
     with empty content to preserve context window usage (ctx%)
   - Clears terminal scrollback with \x1b[3J
   - Triggers ink repaint via forceRedraw()
- setMessages now preserves the last assistant message as fallback when
  no assistant message has message.usage, instead of returning [].
- Add edge-case test for the fallback behavior.
- Keep vitest imports: coderabbitai suggested removing them since
  vitest.config.ts has globals:true, but tsconfig.json lacks
  "types": ["vitest/globals"], so tsc --noEmit (run by pre-commit hook
  via pnpm lint) fails without explicit imports. All other test files
  in the repo use explicit imports for the same reason.
The previous implementation replaced the messages array with a single
empty-content assistant message to preserve prompt cache. This caused
two problems:

1. Claude lost all conversation context and treated the next message
   as the start of a new session ("This is the first message...")
2. The prompt cache was not actually preserved — cache breakpoints
   are set via cache_control in the API request, and the usage field
   in the saved assistant message is a response from the previous
   call, not an instruction for the next one.

The new implementation hides messages from the UI without removing
them from the messages array:

- On /clear-screen, all current message UUIDs (first 24 chars) are
  collected into a globalThis.__tweakccHiddenUUIDs Set.
- The render filter function (g97) is patched to check this Set and
  skip messages whose UUID prefix matches.
- Messages remain in the array and are sent to the API as usual,
  preserving full conversation context.

UUID prefix (24 chars) is used because the render pipeline function
lP() splits multi-content assistant messages into separate objects
with modified UUIDs (original.slice(0,24) + hex index). Matching on
the prefix ensures both original and split messages are hidden.

The approach with a per-message flag (__tweakccHiddenFromUI) was
tried first but failed: lP() creates new objects for assistant
messages with a fixed set of fields, dropping custom properties.
User messages worked because lP() uses object spread for string
content, preserving extra fields — but assistant messages did not.

After session resume, globalThis is empty, so previously hidden
messages become visible again (acceptable trade-off).
@VitalyOstanin VitalyOstanin force-pushed the feature/clear-screen branch from 43d31f2 to 4eea753 Compare April 4, 2026 21:45
Anchor patchRenderFilter regex with [,;{}] literal delimiter before
"function" keyword, matching the convention used by redrawPattern
and other patches. Update tests to supply delimiter in test data
and add delimiter variation coverage.
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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/patches/clearScreen.ts (1)

1-1: Remove the file-level comment.

This repo’s TS guidelines say not to add comments unless they’re explicitly requested.

✂️ Suggested cleanup
-// Please see the note about writing patches in ./index
As per coding guidelines, "Do not add comments unless explicitly requested."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/clearScreen.ts` at line 1, Remove the file-level comment at the
top of clearScreen.ts ("// Please see the note about writing patches in
./index"); simply delete that comment line so the file contains only
implementation code and no repo-guideline comments per the TS guidelines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/patches/clearScreen.ts`:
- Around line 69-84: The current regex in pattern can match different
identifiers for the `type` and `isMeta` checks causing injections against the
wrong variable; update the regex so the first function parameter is captured and
reused (via a backreference) for both `type` and `isMeta` checks (i.e. capture
the first arg name in the function param group and reference that same group
where you currently match `.type` and `.isMeta`), so that funcPrefix, firstArg,
match and replacement all tie to the same identifier; adjust the pattern
variable and the logic that derives firstArg from match groups (instead of
re-parsing match[0]) so replacement uses the guaranteed-first-parameter
identifier.

---

Nitpick comments:
In `@src/patches/clearScreen.ts`:
- Line 1: Remove the file-level comment at the top of clearScreen.ts ("// Please
see the note about writing patches in ./index"); simply delete that comment line
so the file contains only implementation code and no repo-guideline comments per
the TS guidelines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b22168a5-3f55-4c15-bf05-d5727bc58a11

📥 Commits

Reviewing files that changed from the base of the PR and between 43d31f2 and 4eea753.

📒 Files selected for processing (3)
  • src/patches/clearScreen.test.ts
  • src/patches/clearScreen.ts
  • src/patches/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/patches/index.ts

Comment thread src/patches/clearScreen.ts Outdated
Use backreference \3 to ensure type/isMeta checks reference the
same identifier as the function's first parameter. Extract firstArg
directly from match[3] instead of re-parsing match[0].
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