-
-
Notifications
You must be signed in to change notification settings - Fork 964
fix(lint): handle forwardRef callbacks in useHookAtTopLevel #9648
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@biomejs/biome": patch | ||
| --- | ||
|
|
||
| Fixed [#9195](https://github.com/biomejs/biome/issues/9195): `useHookAtTopLevel` no longer reports false positives for component render functions passed to `forwardRef` or `React.forwardRef`. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,5 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::react::hooks::{is_react_hook_call, is_react_hook_name}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::react::{ReactLibrary, is_react_call_api}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::services::semantic::{SemanticModelBuilderVisitor, SemanticServices}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use biome_analyze::{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AddVisitor, FromServices, Phase, Phases, QueryMatch, Queryable, Rule, RuleDiagnostic, RuleKey, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -9,12 +10,13 @@ use biome_analyze::{RuleDomain, RuleSource}; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use biome_console::markup; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use biome_js_semantic::{CallsExtensions, SemanticModel}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use biome_js_syntax::{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AnyFunctionLike, AnyJsBinding, AnyJsClassMemberName, AnyJsExpression, AnyJsFunction, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AnyJsObjectMemberName, JsArrayAssignmentPatternElement, JsArrayBindingPatternElement, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JsCallExpression, JsConditionalExpression, JsGetterClassMember, JsGetterObjectMember, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JsIfStatement, JsLanguage, JsLogicalExpression, JsMethodClassMember, JsMethodObjectMember, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JsObjectBindingPatternShorthandProperty, JsReturnStatement, JsSetterClassMember, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JsSetterObjectMember, JsSyntaxKind, JsSyntaxNode, JsTryFinallyStatement, TextRange, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AnyFunctionLike, AnyJsBinding, AnyJsCallArgument, AnyJsClassMemberName, AnyJsExpression, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AnyJsFunction, AnyJsObjectMemberName, JsArrayAssignmentPatternElement, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JsArrayBindingPatternElement, JsCallExpression, JsConditionalExpression, JsGetterClassMember, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JsGetterObjectMember, JsIfStatement, JsLanguage, JsLogicalExpression, JsMethodClassMember, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JsMethodObjectMember, JsObjectBindingPatternShorthandProperty, JsReturnStatement, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JsSetterClassMember, JsSetterObjectMember, JsSyntaxKind, JsSyntaxNode, JsTryFinallyStatement, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TextRange, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use biome_rowan::{AstNode, Language, SyntaxNode, Text, WalkEvent, declare_node_union}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use rustc_hash::FxHashMap; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -89,10 +91,13 @@ declare_node_union! { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl AnyJsFunctionOrMethod { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn is_react_component_or_hook(&self) -> bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn is_react_component_or_hook(&self, model: &SemanticModel) -> bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ReactComponentInfo::from_function(self.syntax()).is_some() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if self.is_forward_ref_render_function(model) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(name) = self.name() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return is_react_hook_name(&name); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -128,6 +133,61 @@ impl AnyJsFunctionOrMethod { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map(AnyJsObjectMemberName::to_trimmed_text), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn is_forward_ref_render_function(&self, model: &SemanticModel) -> bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let Self::AnyJsFunction(function) = self else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let Ok(binding) = function.binding() else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let Some(binding) = binding.as_js_identifier_binding() else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model.as_binding(binding).all_references().any(|reference| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let Some(expression) = reference | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .syntax() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .ancestors() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .find_map(AnyJsExpression::cast) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map(AnyJsExpression::omit_parentheses) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !matches!(expression, AnyJsExpression::JsIdentifierExpression(_)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let Some(argument) = expression.syntax().parent::<AnyJsCallArgument>() else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let Some(argument_expression) = argument.as_any_js_expression() else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if argument_expression.omit_parentheses().syntax() != expression.syntax() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let Some(call_expression) = argument | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .syntax() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .ancestors() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .find_map(JsCallExpression::cast) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+162
to
+177
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Several API misuses causing compilation failures. The Additionally, once you fix the parent call, 🔧 Proposed fix- let Some(argument) = expression.syntax().parent::<AnyJsCallArgument>() else {
- return false;
- };
- let Some(argument_expression) = argument.as_any_js_expression() else {
- return false;
- };
- if argument_expression.omit_parentheses().syntax() != expression.syntax() {
- return false;
- }
-
- let Some(call_expression) = argument
- .syntax()
- .ancestors()
- .find_map(JsCallExpression::cast)
+ let Some(argument) = expression
+ .syntax()
+ .parent()
+ .and_then(AnyJsCallArgument::cast)
else {
return false;
};
+ let AnyJsCallArgument::AnyJsExpression(argument_expression) = &argument else {
+ return false;
+ };
+ if argument_expression.omit_parentheses().syntax() != expression.syntax() {
+ return false;
+ }
+
+ let Some(call_expression) = argument.syntax().ancestors().find_map(JsCallExpression::cast) else {
+ return false;
+ };📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Actions: autofix.ci[error] 162-162: Rust compile error E0107: method takes 0 generic arguments but 1 was supplied. Call [error] 165-165: Rust compile error E0599: no method named [error] 168-168: Rust compile error E0282: type annotations needed. Cannot infer type at [error] 173-173: Rust compile error E0599: no method named 🪛 GitHub Actions: Lint rule docs[error] 162-162: Rust compile error E0107: [error] 165-165: Rust compile error E0599: no method named [error] 168-168: Rust compile error E0282: type annotations needed at [error] 172-173: Rust compile error E0599: no method named 🪛 GitHub Actions: Pull request Node.js[error] 162-162: Rust compiler error E0107: method takes 0 generic arguments but 1 was supplied. [error] 165-165: Rust compiler error E0599: no method named [error] 168-168: Rust compiler error E0282: type annotations needed. Cannot infer type for [error] 172-173: Rust compiler error E0599: no method named 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let Ok(callee): Result<AnyJsExpression, _> = call_expression.callee() else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| is_react_call_api( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| &callee.omit_parentheses(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ReactLibrary::React, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "forwardRef", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub struct Suggestion { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -254,8 +314,11 @@ fn is_conditional_expression(parent_node: &JsSyntaxNode, node: &JsSyntaxNode) -> | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn is_nested_function_inside_component_or_hook(function: &AnyJsFunctionOrMethod) -> bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if function.is_react_component_or_hook() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn is_nested_function_inside_component_or_hook( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function: &AnyJsFunctionOrMethod, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model: &SemanticModel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if function.is_react_component_or_hook(model) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -265,7 +328,7 @@ fn is_nested_function_inside_component_or_hook(function: &AnyJsFunctionOrMethod) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parent.ancestors().any(|node| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AnyJsFunctionOrMethod::cast(node) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .is_some_and(|enclosing_function| enclosing_function.is_react_component_or_hook()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .is_some_and(|enclosing_function| enclosing_function.is_react_component_or_hook(model)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -541,7 +604,7 @@ impl Rule for UseHookAtTopLevel { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| path.push(range); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(enclosing_function) = enclosing_function_if_call_is_at_top_level(&call) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if is_nested_function_inside_component_or_hook(&enclosing_function) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if is_nested_function_inside_component_or_hook(&enclosing_function, model) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // We cannot allow nested functions inside hooks and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // components, since it would break the requirement for | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // hooks to be called from the top-level. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -561,7 +624,7 @@ impl Rule for UseHookAtTopLevel { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let enclosed = is_enclosed_in_component_or_hook | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| || enclosing_function.is_react_component_or_hook(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| || enclosing_function.is_react_component_or_hook(model); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let AnyJsFunctionOrMethod::AnyJsFunction(function) = enclosing_function | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && let Some(calls_iter) = function.all_calls(model) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -590,7 +653,7 @@ impl Rule for UseHookAtTopLevel { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if enclosing_function_if_call_is_at_top_level(call).is_some_and(|function| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !function.is_react_component_or_hook() && !function.is_function_expression() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !function.is_react_component_or_hook(model) && !function.is_function_expression() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Some(Suggestion { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hook_name_range: get_hook_name_range()?, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
function.binding()returnsOption, notResult.The pipeline failure confirms this: use
let Some(binding) = ...instead oflet Ok(binding) = ....🔧 Proposed fix
📝 Committable suggestion
🧰 Tools
🪛 GitHub Actions: autofix.ci
[error] 141-141: Rust compile error E0308: mismatched types.
function.binding()has typeOption<AnyJsBinding>, but code pattern expectsResultvialet Ok(binding) = ... else. ExpectedOption<AnyJsBinding>, foundResult<_, _>.🪛 GitHub Actions: Lint rule docs
[error] 141-141: Rust compile error E0308 (mismatched types):
function.binding()has typeOption<AnyJsBinding>but code useslet Ok(binding) = ... elseexpecting aResult. ExpectedOption<AnyJsBinding>, foundResult<_, _>.🪛 GitHub Actions: Pull request Node.js
[error] 141-141: Rust compiler error E0308: mismatched types in use_hook_at_top_level.rs.
let Ok(binding) = function.binding() else { ... }expectsOption<AnyJsBinding>but foundResult<_, _>.🤖 Prompt for AI Agents