Skip to content

Commit be3e3dc

Browse files
FrozenPandazclaude[bot]claude
authored andcommitted
fix(core): disable TUI on ai agents (#31480)
## Current Behavior The TUI (Terminal User Interface) is enabled for all environments, including AI agents like Claude Code, which can cause issues with AI-driven development workflows. ## Expected Behavior When an AI agent is detected through environment variables, the TUI should be automatically disabled to prevent interference with AI-driven interactions. ## Related Issue(s) This change improves the developer experience when using AI agents by automatically detecting their presence and adjusting the interface accordingly. --------- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: FrozenPandaz <FrozenPandaz@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0b143a7 commit be3e3dc

File tree

5 files changed

+191
-1
lines changed

5 files changed

+191
-1
lines changed

packages/nx/src/native/index.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,9 @@ export declare export declare function installNxConsole(): void
252252

253253
export const IS_WASM: boolean
254254

255+
/** Detects if the current process is being run by an AI agent */
256+
export declare export declare function isAiAgent(): boolean
257+
255258
export declare export declare function logDebug(message: string): void
256259

257260
/** Stripped version of the NxJson interface for use in rust */

packages/nx/src/native/native-bindings.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ module.exports.hashArray = nativeBinding.hashArray
392392
module.exports.hashFile = nativeBinding.hashFile
393393
module.exports.installNxConsole = nativeBinding.installNxConsole
394394
module.exports.IS_WASM = nativeBinding.IS_WASM
395+
module.exports.isAiAgent = nativeBinding.isAiAgent
395396
module.exports.logDebug = nativeBinding.logDebug
396397
module.exports.parseTaskStatus = nativeBinding.parseTaskStatus
397398
module.exports.remove = nativeBinding.remove

packages/nx/src/native/utils/ai.rs

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
use std::env;
2+
use tracing::debug;
3+
4+
/// Detects if the current process is being run by Claude Code
5+
fn is_claude_ai() -> bool {
6+
match env::var("CLAUDECODE") {
7+
Ok(_) => {
8+
debug!("Claude AI detected via CLAUDECODE environment variable");
9+
true
10+
}
11+
Err(_) => false,
12+
}
13+
}
14+
15+
/// Detects if the current process is being run by Repl.it
16+
fn is_replit_ai() -> bool {
17+
match env::var("REPL_ID") {
18+
Ok(_) => {
19+
debug!("Repl.it AI detected via REPL_ID environment variable");
20+
true
21+
}
22+
Err(_) => false,
23+
}
24+
}
25+
26+
/// Detects if the current process is being run by Cursor AI
27+
fn is_cursor_ai() -> bool {
28+
let pager_matches = env::var("PAGER")
29+
.map(|v| v == "head -n 10000 | cat")
30+
.unwrap_or(false);
31+
let has_cursor_trace_id = env::var("CURSOR_TRACE_ID").is_ok();
32+
let has_composer_no_interaction = env::var("COMPOSER_NO_INTERACTION").is_ok();
33+
34+
let is_cursor = pager_matches && has_cursor_trace_id && has_composer_no_interaction;
35+
36+
if is_cursor {
37+
debug!(
38+
"Cursor AI detected via PAGER, CURSOR_TRACE_ID, and COMPOSER_NO_INTERACTION environment variables"
39+
);
40+
}
41+
42+
is_cursor
43+
}
44+
45+
/// Detects if the current process is being run by an AI agent
46+
#[napi]
47+
pub fn is_ai_agent() -> bool {
48+
let is_ai = is_claude_ai() || is_replit_ai() || is_cursor_ai();
49+
50+
if is_ai {
51+
debug!("AI agent detected");
52+
}
53+
54+
is_ai
55+
}
56+
57+
#[cfg(test)]
58+
mod tests {
59+
use super::*;
60+
61+
fn clear_ai_env_vars() {
62+
let ai_vars = [
63+
"CLAUDECODE",
64+
"REPL_ID",
65+
"PAGER",
66+
"CURSOR_TRACE_ID",
67+
"COMPOSER_NO_INTERACTION",
68+
];
69+
for var in &ai_vars {
70+
unsafe {
71+
std::env::remove_var(var);
72+
}
73+
}
74+
}
75+
76+
#[test]
77+
fn test_ai_agent_detection() {
78+
// Save original environment state
79+
let original_claudecode = env::var("CLAUDECODE").ok();
80+
let original_repl_id = env::var("REPL_ID").ok();
81+
let original_pager = env::var("PAGER").ok();
82+
let original_cursor_trace_id = env::var("CURSOR_TRACE_ID").ok();
83+
let original_composer_no_interaction = env::var("COMPOSER_NO_INTERACTION").ok();
84+
85+
// Start with clean environment
86+
clear_ai_env_vars();
87+
88+
// Test no AI detection
89+
assert!(
90+
!is_claude_ai(),
91+
"Should not detect Claude AI without CLAUDECODE"
92+
);
93+
assert!(
94+
!is_replit_ai(),
95+
"Should not detect Repl.it AI without REPL_ID"
96+
);
97+
assert!(
98+
!is_cursor_ai(),
99+
"Should not detect Cursor AI without all variables"
100+
);
101+
assert!(!is_ai_agent(), "Should not detect any AI agent");
102+
103+
// Test Claude AI detection
104+
unsafe {
105+
env::set_var("CLAUDECODE", "1");
106+
}
107+
assert!(is_claude_ai(), "Should detect Claude AI with CLAUDECODE");
108+
assert!(is_ai_agent(), "Main function should detect Claude AI");
109+
unsafe {
110+
env::remove_var("CLAUDECODE");
111+
}
112+
113+
// Test Repl.it AI detection
114+
unsafe {
115+
env::set_var("REPL_ID", "some-repl-id");
116+
}
117+
assert!(is_replit_ai(), "Should detect Repl.it AI with REPL_ID");
118+
assert!(is_ai_agent(), "Main function should detect Repl.it AI");
119+
unsafe {
120+
env::remove_var("REPL_ID");
121+
}
122+
123+
// Test Cursor AI detection with wrong PAGER
124+
unsafe {
125+
env::set_var("PAGER", "wrong-value");
126+
env::set_var("CURSOR_TRACE_ID", "trace-123");
127+
env::set_var("COMPOSER_NO_INTERACTION", "1");
128+
}
129+
assert!(
130+
!is_cursor_ai(),
131+
"Should not detect Cursor AI with wrong PAGER value"
132+
);
133+
134+
// Test Cursor AI detection with correct PAGER
135+
unsafe {
136+
env::set_var("PAGER", "head -n 10000 | cat");
137+
}
138+
assert!(
139+
is_cursor_ai(),
140+
"Should detect Cursor AI with correct PAGER and all variables"
141+
);
142+
assert!(is_ai_agent(), "Main function should detect Cursor AI");
143+
clear_ai_env_vars();
144+
145+
// Test multiple AI agents
146+
unsafe {
147+
env::set_var("CLAUDECODE", "1");
148+
env::set_var("REPL_ID", "some-repl-id");
149+
}
150+
assert!(
151+
is_ai_agent(),
152+
"Should detect AI when multiple agents are present"
153+
);
154+
155+
// Restore original environment
156+
clear_ai_env_vars();
157+
if let Some(val) = original_claudecode {
158+
unsafe {
159+
env::set_var("CLAUDECODE", val);
160+
}
161+
}
162+
if let Some(val) = original_repl_id {
163+
unsafe {
164+
env::set_var("REPL_ID", val);
165+
}
166+
}
167+
if let Some(val) = original_pager {
168+
unsafe {
169+
env::set_var("PAGER", val);
170+
}
171+
}
172+
if let Some(val) = original_cursor_trace_id {
173+
unsafe {
174+
env::set_var("CURSOR_TRACE_ID", val);
175+
}
176+
}
177+
if let Some(val) = original_composer_no_interaction {
178+
unsafe {
179+
env::set_var("COMPOSER_NO_INTERACTION", val);
180+
}
181+
}
182+
}
183+
}

packages/nx/src/native/utils/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub use find_matching_projects::*;
1010
pub use get_mod_time::*;
1111
pub use normalize_trait::Normalize;
1212

13+
pub mod ai;
1314
#[cfg_attr(not(target_arch = "wasm32"), path = "atomics/default.rs")]
1415
#[cfg_attr(target_arch = "wasm32", path = "atomics/wasm.rs")]
1516
pub mod atomics;

packages/nx/src/tasks-runner/is-tui-enabled.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { NxJsonConfiguration } from '../config/nx-json';
2-
import { IS_WASM } from '../native';
2+
import { IS_WASM, isAiAgent } from '../native';
33
import { NxArgs } from '../utils/command-line-utils';
44
import { isCI } from '../utils/is-ci';
55
import { logger } from '../utils/logger';
@@ -70,6 +70,8 @@ export function shouldUseTui(
7070
if (
7171
// Interactive TUI doesn't make sense on CI
7272
isCI() ||
73+
// Interactive TUI doesn't make sense in an AI agent context
74+
isAiAgent() ||
7375
// TODO(@JamesHenry): Remove this check once Windows issues are fixed.
7476
// Windows is not working well right now, temporarily disable it on Windows even if it has been specified as enabled
7577
isWindows ||

0 commit comments

Comments
 (0)