Skip to content

feat: add session-color patch for env-based prompt bar color#657

Open
VitalyOstanin wants to merge 3 commits intoPiebald-AI:mainfrom
VitalyOstanin:feature/session-color
Open

feat: add session-color patch for env-based prompt bar color#657
VitalyOstanin wants to merge 3 commits intoPiebald-AI:mainfrom
VitalyOstanin:feature/session-color

Conversation

@VitalyOstanin
Copy link
Copy Markdown

@VitalyOstanin VitalyOstanin commented Apr 2, 2026

Summary

Add an always-applied patch that reads TWEAKCC_SESSION_COLOR env var at startup
and sets the session prompt bar color accordingly.

  • Designed for wrapper scripts (e.g. ccw) that determine color based on the
    working directory before launching Claude Code
  • Valid values: red, blue, green, yellow, purple, orange, pink, cyan
  • Invalid or unset values are silently ignored (no color applied)
  • On session resume, Claude Code restores color from the session file, so the
    env var only affects new sessions

How it works

Claude Code has two app state initialization sites — the CLI bootstrap (builds
initialState inline with dynamic values like effortValue, fastMode) and
the default state factory (used as fallback). Neither includes
standaloneAgentContext, so it defaults to undefined.

The patch injects a standaloneAgentContext IIFE into both sites that reads
TWEAKCC_SESSION_COLOR, validates it against the hardcoded allowed color list,
and returns {name:"",color:value} or void 0. This is the same state shape
that /color sets interactively.

Test plan

  • Unit tests pass (6 tests: CLI state injection, default state injection,
    both locations, color validation, idempotency, pattern-not-found)
  • pnpm lint clean (tsc + eslint)
  • Manual test: TWEAKCC_SESSION_COLOR=cyan ccw-test shows cyan prompt bar
  • Verify color persists after /clear
  • Verify session resume restores color without env var

Summary by CodeRabbit

  • New Features

    • Session color can be set via an environment variable. Values are validated against an allowlist and ignored if invalid. The setting is injected automatically into session initialization points and reported when applied.
  • Tests

    • Added tests covering injection points, validation behavior, idempotency, multiple-insertion handling, and no-op/error cases.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

📝 Walkthrough

Walkthrough

Adds a new patch "session-color" that injects an environment-driven session color snippet into serialized state initializers and registers the implementation; includes tests verifying injection, validation, idempotency, multi-location insertion, and no-op behavior when patterns are absent.

Changes

Cohort / File(s) Summary
Patch registration
src/patches/index.ts
Registered new patch session-color (ID session-color, group PatchGroup.ALWAYS_APPLIED) and added implementation mapping to call writeSessionColor.
Patch implementation
src/patches/sessionColor.ts
Added `writeSessionColor(oldFile: string): string
Tests
src/patches/sessionColor.test.ts
New Vitest suite asserting correct token injections for CLI/app states and combined input, presence of validation logic and allowed color literals, idempotency on repeated application, dual-location insertion counts, and null result when no target patterns match.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • bl-ue
  • georpar

Poem

🐇 I nibble code with careful paws,
I tuck a color into startup laws.
Env whispers green or cyan bright,
I plant the token just right.
Two patches set — I hop into the night.

🚥 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 clearly and accurately describes the main change: adding a session-color patch that reads an environment variable to set the prompt bar color. It is concise, specific, and directly related to the core functionality introduced in the changeset.
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.

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

426-432: Place session-color in the existing “Always Applied” block for ordering consistency.

PATCH_DEFINITIONS order controls both apply order and listing. Keeping this ALWAYS_APPLIED item with the other always-applied entries will make ordering expectations clearer.

♻️ Suggested reordering
   {
     id: 'statusline-update-throttle',
     name: `Statusline update throttling correction`,
     group: PatchGroup.ALWAYS_APPLIED,
     description: `Statusline updates will be properly throttled instead of queued (or debounced)`,
   },
+  {
+    id: 'session-color',
+    name: 'Session color from env',
+    group: PatchGroup.ALWAYS_APPLIED,
+    description:
+      'Set session prompt bar color via TWEAKCC_SESSION_COLOR env var',
+  },
   // Misc Configurable
@@
-  {
-    id: 'session-color',
-    name: 'Session color from env',
-    group: PatchGroup.ALWAYS_APPLIED,
-    description:
-      'Set session prompt bar color via TWEAKCC_SESSION_COLOR env var',
-  },
 ] as const;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/index.ts` around lines 426 - 432, The 'session-color' patch
object is declared separately; move the object with id 'session-color' (name
'Session color from env', group PatchGroup.ALWAYS_APPLIED) into the existing
ALWAYS_APPLIED block inside the PATCH_DEFINITIONS array so it sits alongside the
other always-applied entries to preserve apply/listing order; locate the current
standalone object (id 'session-color') and cut-and-paste it into the existing
block where other PatchGroup.ALWAYS_APPLIED items are defined, ensuring no
duplicate IDs and keeping the same properties.
src/patches/sessionColor.ts (1)

25-27: Tighten the idempotency sentinel to this patch’s actual injection marker.

A plain includes('TWEAKCC_SESSION_COLOR') can false-positive if that token appears elsewhere, causing an unintended early return.

🔧 Suggested guard refinement
 export const writeSessionColor = (oldFile: string): string | null => {
-  if (oldFile.includes('TWEAKCC_SESSION_COLOR')) {
+  if (
+    oldFile.includes(
+      'standaloneAgentContext:(()=>{let __c=process.env.TWEAKCC_SESSION_COLOR;'
+    )
+  ) {
     return oldFile;
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/sessionColor.ts` around lines 25 - 27, The idempotency check
using oldFile.includes('TWEAKCC_SESSION_COLOR') is too broad and can
false-positive; tighten it by checking for the exact injected sentinel (e.g., a
unique marker string you insert with the patch like '/*
TWEAKCC_SESSION_COLOR_INJECTED */' or the full block snippet) or use a precise
regex that matches the exact injection start/end, replacing the current includes
call on oldFile with that exact-marker check so the function only returns early
when the patch’s real injection is present.
🤖 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/index.ts`:
- Around line 426-432: The 'session-color' patch object is declared separately;
move the object with id 'session-color' (name 'Session color from env', group
PatchGroup.ALWAYS_APPLIED) into the existing ALWAYS_APPLIED block inside the
PATCH_DEFINITIONS array so it sits alongside the other always-applied entries to
preserve apply/listing order; locate the current standalone object (id
'session-color') and cut-and-paste it into the existing block where other
PatchGroup.ALWAYS_APPLIED items are defined, ensuring no duplicate IDs and
keeping the same properties.

In `@src/patches/sessionColor.ts`:
- Around line 25-27: The idempotency check using
oldFile.includes('TWEAKCC_SESSION_COLOR') is too broad and can false-positive;
tighten it by checking for the exact injected sentinel (e.g., a unique marker
string you insert with the patch like '/* TWEAKCC_SESSION_COLOR_INJECTED */' or
the full block snippet) or use a precise regex that matches the exact injection
start/end, replacing the current includes call on oldFile with that exact-marker
check so the function only returns early when the patch’s real injection is
present.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6c0a9a3a-d57c-41eb-9844-8219b96cb972

📥 Commits

Reviewing files that changed from the base of the PR and between 88b8ed0 and 6f71f32.

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

@VitalyOstanin VitalyOstanin force-pushed the feature/session-color branch from 6f71f32 to 6f25c6b Compare April 2, 2026 07:28
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

🤖 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/sessionColor.ts`:
- Around line 51-57: The current showDiff call uses the post-patch `result` and
the full `replacement`, which can produce misleading diffs after the first
patch; update the call to use the pre-patch buffer (the buffer state before
applying the current replacement) for the index calculations and pass only the
INJECTION text as the injectedText argument to showDiff (keep the same start/end
as match.index and match.index + match[0].length but computed against the
pre-patch buffer), so replace the showDiff(oldFile, result, replacement, ...)
invocation with one that uses the pre-patch buffer for indices and INJECTION for
the injectedText parameter.
🪄 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: 13605414-8faf-4988-b1d2-660eda55da2a

📥 Commits

Reviewing files that changed from the base of the PR and between 6f71f32 and 6f25c6b.

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

Comment on lines +51 to +57
showDiff(
oldFile,
result,
replacement,
match.index,
match.index + match[0].length
);
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Apr 2, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

showDiff args are inconsistent after the first patch.

Use the pre-patch buffer for indices and pass only INJECTION as injectedText; current call can produce misleading diffs when both patterns are patched.

Suggested fix
   for (const pattern of patterns) {
-    const match = result.match(pattern);
+    const beforePatch = result;
+    const match = beforePatch.match(pattern);
     if (!match || match.index === undefined) continue;

     const replacement = INJECTION + match[0];
     result =
-      result.slice(0, match.index) +
+      beforePatch.slice(0, match.index) +
       replacement +
-      result.slice(match.index + match[0].length);
+      beforePatch.slice(match.index + match[0].length);

     showDiff(
-      oldFile,
+      beforePatch,
       result,
-      replacement,
+      INJECTION,
       match.index,
       match.index + match[0].length
     );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/patches/sessionColor.ts` around lines 51 - 57, The current showDiff call
uses the post-patch `result` and the full `replacement`, which can produce
misleading diffs after the first patch; update the call to use the pre-patch
buffer (the buffer state before applying the current replacement) for the index
calculations and pass only the INJECTION text as the injectedText argument to
showDiff (keep the same start/end as match.index and match.index +
match[0].length but computed against the pre-patch buffer), so replace the
showDiff(oldFile, result, replacement, ...) invocation with one that uses the
pre-patch buffer for indices and INJECTION for the injectedText parameter.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@coderabbitai Fixed in 35e30f0. showDiff now uses prePatch (pre-patch buffer) for both slicing and diff indices, ensuring consistent results when multiple patterns are patched.

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.

@VitalyOstanin, thanks for the update! Using prePatch as the base for both slicing and the showDiff indices ensures that each iteration operates on a stable snapshot, so the displayed diffs remain accurate even when both patterns are matched and patched in the same run. The fix looks correct. ✅


🧠 Learnings used
Learnt from: VitalyOstanin
Repo: Piebald-AI/tweakcc PR: 634
File: src/patches/inputChevronColor.ts:8-9
Timestamp: 2026-03-30T16:29:19.451Z
Learning: In src/patches/inputChevronColor.ts, the Claude Code minified binary uses `\xA0` (U+00A0 non-breaking space) rather than a regular ASCII space `" "` in the chevron component child tail (e.g., after `s6.pointer`). Any regex anchor using `," "` (regular space) to uniquely identify the chevron element will never match the minified output. The pattern ending at `{color:VAR,dimColor:VAR}` is already unique due to the `{isLoading, themeColor}` destructuring context.

Learnt from: bl-ue
Repo: Piebald-AI/tweakcc PR: 421
File: src/patches/thinkerSymbolSpeed.ts:46-58
Timestamp: 2026-01-27T21:16:01.318Z
Learning: In patches under src/patches/, assume code works with minified, original Claude Code installations. Patch regexes must be strict and match the minified format exactly (ignore formatting differences like whitespace/newlines). This guidance applies to all .ts files in src/patches and should be followed when adding or reviewing patches.

Learnt from: ljepson
Repo: Piebald-AI/tweakcc PR: 437
File: src/patches/toolsets.ts:811-833
Timestamp: 2026-01-29T21:19:04.770Z
Learning: In src/patches/toolsets.ts, when matching mode change patterns like `let X=Y(Z,{type:"setMode",mode:vv(TA),destination:"session"});`, the regex should capture the inner variable name (TA) not the entire expression (vv(TA)), because the variable holds the raw mode string value (e.g., "plan", "default", "acceptEdits") and is used in direct string comparisons like `if(${modeVar}==="plan")`. The wrapping function vv() is only for dispatch transformation.

Learnt from: CR
Repo: Piebald-AI/tweakcc PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-06T21:30:11.641Z
Learning: Applies to **/*.{ts,tsx} : Use `chalk` for terminal colors

Learnt from: CR
Repo: Piebald-AI/tweakcc PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-06T21:30:11.641Z
Learning: Applies to **/patches/**/*.{ts,tsx} : Use `,` `;` `}` `{` literal characters at regex start instead of `\\b` for performance in patch patterns

Learnt from: CR
Repo: Piebald-AI/tweakcc PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-06T21:30:11.641Z
Learning: Applies to **/patches/**/*.{ts,tsx} : Avoid `\\b` in regex patterns due to V8 performance issues; use literal character alternatives

Learnt from: CR
Repo: Piebald-AI/tweakcc PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-06T21:30:11.641Z
Learning: Applies to **/patches/**/*.{ts,tsx} : Use `[\$\\w]+` instead of `\\w+` for identifier matching in regex patterns to include `$` for React refs

Learnt from: VitalyOstanin
Repo: Piebald-AI/tweakcc PR: 647
File: src/patches/clearScreen.test.ts:1-1
Timestamp: 2026-03-31T12:24:56.066Z
Learning: In Piebald-AI/tweakcc, test files (`**/*.test.{ts,tsx}`) must explicitly import Vitest functions (`describe`, `it`, `expect`, `beforeEach`, `vi`) from `'vitest'` rather than relying on globals. Although `vitest.config.ts` sets `globals: true`, the `tsconfig.json` does NOT include `"types": ["vitest/globals"]`, so `tsc --noEmit` (run by the pre-commit hook via `pnpm lint`) fails if globals are used without explicit imports. All test files in the repo follow this explicit-import pattern.

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

Set the session prompt bar color via TWEAKCC_SESSION_COLOR env var.
Intended for use in wrapper scripts (e.g. ccw) that determine color
based on the working directory before launching Claude Code.

Valid values: red, blue, green, yellow, purple, orange, pink, cyan.
Invalid or unset values are silently ignored (no color applied).

How it works:
Claude Code has two app state initialization sites — the CLI
bootstrap (which builds initialState inline with dynamic values like
effortValue, fastMode) and the default state factory u3H() used as
fallback. Neither includes standaloneAgentContext, so it defaults to
undefined and the prompt bar shows no color.

The patch injects a standaloneAgentContext IIFE into both sites that
reads TWEAKCC_SESSION_COLOR at startup, validates it against the
hardcoded allowed list, and returns {name:"",color:value} or void 0.
This is the same state shape that /color sets interactively.

On session resume, Claude Code restores the color from the session
file, so the env var only affects new sessions.
1. Move session-color definition into the ALWAYS_APPLIED block
   (next to statusline-update-throttle) for ordering consistency.
2. Tighten idempotency check: match the full injection sentinel
   'standaloneAgentContext:(()=>{let __c=process.env.TWEAKCC_SESSION_COLOR;'
   instead of the broad 'TWEAKCC_SESSION_COLOR' substring to avoid
   false-positive early returns.
3. Rebase onto upstream/main to resolve conflict with channels-mode
   patch (Piebald-AI#653).
@VitalyOstanin VitalyOstanin force-pushed the feature/session-color branch from 35e30f0 to 5620d5b Compare April 4, 2026 21:45
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 (2)
src/patches/index.ts (1)

899-901: Place session-color with the Always Applied implementation block.

Functionally correct, but moving this entry near the other Always Applied implementations will reduce maintenance drift between definition and implementation sections.

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

In `@src/patches/index.ts` around lines 899 - 901, The 'session-color' patch entry
should be relocated into the "Always Applied" implementation block to match its
implementation; move the object with key 'session-color' (fn: c =>
writeSessionColor(c)) so that it sits with the other Always Applied entries next
to the implementation of writeSessionColor, ensuring the definition and
implementation remain adjacent and reduce maintenance drift.
src/patches/sessionColor.test.ts (1)

15-55: Add tests for remaining session lifecycle edge cases.

Please add coverage for the two still-unverified behaviors (/clear persistence and resume restoring session color without env var) to lock in intended semantics.

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/sessionColor.test.ts` around lines 15 - 55, Add two new unit
tests inside the existing describe('writeSessionColor') block to cover the
missing edge cases: one test that exercises the "/clear" persistence behavior by
feeding an appropriate fixture into writeSessionColor (use or extend
makeCLIState/makeBoth to include a clear action) and assert the returned patch
reflects the expected persisted-clear semantics; and a second test that
simulates a resume restore path without the TWEAKCC_SESSION_COLOR env var
(create a fixture representing resumed state and call writeSessionColor or the
resume helper) and assert the function injects/restores the session color
correctly in the output. Reference writeSessionColor, makeCLIState,
makeDefaultState and makeBoth to locate where to add these tests.
🤖 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/index.ts`:
- Around line 899-901: The 'session-color' patch entry should be relocated into
the "Always Applied" implementation block to match its implementation; move the
object with key 'session-color' (fn: c => writeSessionColor(c)) so that it sits
with the other Always Applied entries next to the implementation of
writeSessionColor, ensuring the definition and implementation remain adjacent
and reduce maintenance drift.

In `@src/patches/sessionColor.test.ts`:
- Around line 15-55: Add two new unit tests inside the existing
describe('writeSessionColor') block to cover the missing edge cases: one test
that exercises the "/clear" persistence behavior by feeding an appropriate
fixture into writeSessionColor (use or extend makeCLIState/makeBoth to include a
clear action) and assert the returned patch reflects the expected
persisted-clear semantics; and a second test that simulates a resume restore
path without the TWEAKCC_SESSION_COLOR env var (create a fixture representing
resumed state and call writeSessionColor or the resume helper) and assert the
function injects/restores the session color correctly in the output. Reference
writeSessionColor, makeCLIState, makeDefaultState and makeBoth to locate where
to add these tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c34df35d-b2fa-43f1-bac8-be987e40b8b5

📥 Commits

Reviewing files that changed from the base of the PR and between 35e30f0 and 5620d5b.

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

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