Skip to content

feat: add /clear-screen slash command#647

Merged
bl-ue merged 6 commits into
Piebald-AI:mainfrom
VitalyOstanin:feature/clear-screen
Jun 6, 2026
Merged

feat: add /clear-screen slash command#647
bl-ue merged 6 commits into
Piebald-AI:mainfrom
VitalyOstanin:feature/clear-screen

Conversation

@VitalyOstanin

@VitalyOstanin VitalyOstanin commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

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

    • Adds a clear-screen slash command that clears the terminal, updates hidden-message tracking, and triggers a screen redraw without affecting hidden messages.
  • Tests

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

    • Registers the clear-screen patch in the patch application pipeline.

@coderabbitai

coderabbitai Bot commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Ready to act? Review this PR in Change Stack to turn feedback into patch suggestions you can inspect and refine.

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5f70f028-58b4-42f6-b745-fcb6e42f68e0

📥 Commits

Reviewing files that changed from the base of the PR and between b58ff14 and 64a82b2.

📒 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 (3)
  • src/patches/index.ts
  • src/patches/clearScreen.test.ts
  • src/patches/clearScreen.ts

📝 Walkthrough

Walkthrough

Adds a new "clear-screen" patch and tests: exposes an existing redraw callback as globalThis.__tweakccForceRedraw, injects a hidden-UUID guard into the render-filter to hide messages by UUID prefix, and registers an inline /clear-screen slash command that clears the terminal and triggers a redraw.

Changes

Clear Screen Patch

Layer / File(s) Summary
Render-filter rewrite
src/patches/clearScreen.ts
patchRenderFilter(oldFile) regex-matches a render-filter function and injects an early guard that returns !1 when globalThis.__tweakccHiddenUUIDs?.has(<arg>.uuid?.slice(0,24)), emitting a targeted diff or returning null if no match.
Redraw wiring and command registration
src/patches/clearScreen.ts, src/patches/index.ts
writeClearScreen(oldFile) locates the redraw/useCallback snippet, assigns it to globalThis.__tweakccForceRedraw, calls patchRenderFilter, composes an inline /clear-screen slash command that updates __tweakccHiddenUUIDs, clears the terminal, invokes the force-redraw hook, and registers the command. Registry wiring adds clear-screen to PATCH_DEFINITIONS and patchImplementations.
Unit tests
src/patches/clearScreen.test.ts
Vitest tests cover writeClearScreen and patchRenderFilter: successful patching assertions, idempotency when the command already exists, null returns for missing patterns, preservation of original callback snippets, delimiter robustness, and parameter-name variation handling.

Sequence Diagram(s)

sequenceDiagram
  participant writeClearScreen
  participant patchRenderFilter
  participant writeSlashCommandDefinition
  writeClearScreen->>patchRenderFilter: patch render-filter in file string
  patchRenderFilter-->>writeClearScreen: patched file | null
  writeClearScreen->>writeSlashCommandDefinition: register inline `clear-screen` command
  writeSlashCommandDefinition-->>writeClearScreen: registration result | null
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Suggested reviewers

  • bl-ue

Poem

🐇 I nibble through lines with whiskers bright,
I bind a redraw cord and hide the light.
A slash clears clutter, UUIDs take flight,
Terminals hush, and screens breathe new sight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature being added: a new /clear-screen slash command. It is concise, specific, and clearly summarizes the primary change introduced in this pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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

@VitalyOstanin VitalyOstanin force-pushed the feature/clear-screen branch from 43d31f2 to 4eea753 Compare April 4, 2026 21:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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
@bl-ue

bl-ue commented Jun 5, 2026

Copy link
Copy Markdown
Member

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@bl-ue

bl-ue commented Jun 5, 2026

Copy link
Copy Markdown
Member

Hi @VitalyOstanin! I'm sorry for the long delay on reviewing this PR! The code looks great, but I ran it on CC v2.1.158 and got an error:

patch: clearScreen: failed to find app:redraw callback

Would you mind please taking a look at that so that I can merge your PR? Thank you!

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).
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.
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].
Recent Claude Code moved the stdout force-redraw call out of a
useCallback into a standalone top-level function, so redrawPattern no
longer matched and the patch aborted with "failed to find app:redraw
callback".

Anchor redrawPattern on the standalone
`function NAME(){MAP.get(process.stdout)?.forceRedraw()}` form and
update the tests to the current minified shapes (forceRedraw function
and the memoized, spread-based slash command array). Verified end to
end on Claude Code 2.1.158 and 2.1.167.
@VitalyOstanin VitalyOstanin force-pushed the feature/clear-screen branch from b58ff14 to 64a82b2 Compare June 6, 2026 10:23
@VitalyOstanin

Copy link
Copy Markdown
Contributor Author

Hi @bl-ue, thanks for the review and for testing on 2.1.158!

The failure (failed to find app:redraw callback) came from redrawPattern: recent Claude Code moved the stdout force-redraw call out of a useCallback into a standalone top-level function, so the old pattern no longer matched.

I've rebased the branch onto current main and fixed it:

  • redrawPattern now anchors on the standalone function NAME(){MAP.get(process.stdout)?.forceRedraw()} form.
  • The slash-command array lookup needed no change — it's covered by the rewritten slashCommands.ts from Fix patches for Claude Code 2.1.146-2.1.158 #761, which the branch now sits on top of.

Verified end to end on 2.1.158 and 2.1.167: the patch applies, the binary launches, and /clear-screen is registered and works.

@bl-ue bl-ue merged commit 4e85d5d into Piebald-AI:main Jun 6, 2026
2 checks passed
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.

2 participants