robotcode-plugin 2.5.0__py3-none-any.whl → 2.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- robotcode/plugin/__init__.py +196 -69
- robotcode/plugin/__version__.py +1 -1
- robotcode/plugin/_agent_detection.py +102 -0
- robotcode/plugin/click_helper/types.py +3 -3
- {robotcode_plugin-2.5.0.dist-info → robotcode_plugin-2.6.0.dist-info}/METADATA +2 -1
- robotcode_plugin-2.6.0.dist-info/RECORD +12 -0
- {robotcode_plugin-2.5.0.dist-info → robotcode_plugin-2.6.0.dist-info}/WHEEL +1 -1
- robotcode_plugin-2.5.0.dist-info/RECORD +0 -11
robotcode/plugin/__init__.py
CHANGED
|
@@ -8,6 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
from types import TracebackType
|
|
9
9
|
from typing import (
|
|
10
10
|
IO,
|
|
11
|
+
TYPE_CHECKING,
|
|
11
12
|
Any,
|
|
12
13
|
AnyStr,
|
|
13
14
|
Callable,
|
|
@@ -29,6 +30,9 @@ import tomli_w
|
|
|
29
30
|
from robotcode.core.utils.dataclasses import as_dict, as_json
|
|
30
31
|
from robotcode.core.utils.path import same_file
|
|
31
32
|
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from rich.markdown import Markdown
|
|
35
|
+
|
|
32
36
|
__all__ = [
|
|
33
37
|
"Application",
|
|
34
38
|
"ColoredOutput",
|
|
@@ -101,6 +105,101 @@ class ProgressBar(Protocol[T]):
|
|
|
101
105
|
def __next__(self) -> T: ...
|
|
102
106
|
|
|
103
107
|
|
|
108
|
+
_deep_markdown_cls: "Optional[type[Markdown]]" = None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _get_deep_markdown_cls() -> "type[Markdown]":
|
|
112
|
+
"""Return a cached `rich.markdown.Markdown` subclass tuned for our
|
|
113
|
+
output, building it on first use.
|
|
114
|
+
|
|
115
|
+
`rich` (and its `markdown-it-py` dependency) are hard requirements
|
|
116
|
+
now, so there's no ImportError fallback — but the import is still
|
|
117
|
+
deferred to first use so the `--version` / `--help` fast paths don't
|
|
118
|
+
pay for it.
|
|
119
|
+
|
|
120
|
+
All customisation lives on the subclass's own `elements` mapping
|
|
121
|
+
and `__init__`, so we never mutate rich's global `Markdown.elements`
|
|
122
|
+
ClassVar or the shared element classes — any other `rich.Markdown`
|
|
123
|
+
user in the process keeps stock behaviour. Built once and cached."""
|
|
124
|
+
global _deep_markdown_cls
|
|
125
|
+
if _deep_markdown_cls is not None:
|
|
126
|
+
return _deep_markdown_cls
|
|
127
|
+
|
|
128
|
+
from markdown_it import MarkdownIt
|
|
129
|
+
from rich.console import Console, ConsoleOptions, RenderResult
|
|
130
|
+
from rich.markdown import (
|
|
131
|
+
Heading,
|
|
132
|
+
Markdown,
|
|
133
|
+
TableBodyElement,
|
|
134
|
+
TableDataElement,
|
|
135
|
+
TableElement,
|
|
136
|
+
TableHeaderElement,
|
|
137
|
+
TableRowElement,
|
|
138
|
+
)
|
|
139
|
+
from rich.text import Text
|
|
140
|
+
|
|
141
|
+
class LeftHeading(Heading):
|
|
142
|
+
"""Left-justify headings instead of rich's default centering."""
|
|
143
|
+
|
|
144
|
+
def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
|
|
145
|
+
for result in super().__rich_console__(console, options):
|
|
146
|
+
cast(Text, result).justify = "left"
|
|
147
|
+
yield result
|
|
148
|
+
|
|
149
|
+
# rich#3027: table rows render with spurious blank lines unless the
|
|
150
|
+
# table element classes carry `new_line = False`. Subclass rather
|
|
151
|
+
# than patch the originals so the change is scoped to our renderer.
|
|
152
|
+
class _Table(TableElement):
|
|
153
|
+
new_line = False
|
|
154
|
+
|
|
155
|
+
class _TableHeader(TableHeaderElement):
|
|
156
|
+
new_line = False
|
|
157
|
+
|
|
158
|
+
class _TableBody(TableBodyElement):
|
|
159
|
+
new_line = False
|
|
160
|
+
|
|
161
|
+
class _TableRow(TableRowElement):
|
|
162
|
+
new_line = False
|
|
163
|
+
|
|
164
|
+
class _TableData(TableDataElement):
|
|
165
|
+
new_line = False
|
|
166
|
+
|
|
167
|
+
class DeepMarkdown(Markdown):
|
|
168
|
+
"""`rich.markdown.Markdown` builds a `MarkdownIt()` whose
|
|
169
|
+
`maxNesting` defaults to 20 — and every nested ``list +
|
|
170
|
+
listitem`` eats two of that budget. A workspace-scale
|
|
171
|
+
`discover all` document with the typical Robot directory layout
|
|
172
|
+
(`tests/foo/bar/baz/…`) trips the limit around the 8th nested
|
|
173
|
+
level, and markdown-it-py silently discards the rest of the
|
|
174
|
+
document — including any footer such as the Statistics block.
|
|
175
|
+
Re-parse with a much higher limit so arbitrarily deep trees
|
|
176
|
+
render in full.
|
|
177
|
+
|
|
178
|
+
Customised token → element mappings (left-justified headings,
|
|
179
|
+
blank-line-free tables) are overridden on this subclass's own
|
|
180
|
+
`elements` map, leaving rich's global ClassVar untouched."""
|
|
181
|
+
|
|
182
|
+
elements = {
|
|
183
|
+
**Markdown.elements,
|
|
184
|
+
"heading_open": LeftHeading,
|
|
185
|
+
"table_open": _Table,
|
|
186
|
+
"thead_open": _TableHeader,
|
|
187
|
+
"tbody_open": _TableBody,
|
|
188
|
+
"tr_open": _TableRow,
|
|
189
|
+
"td_open": _TableData,
|
|
190
|
+
"th_open": _TableData,
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
def __init__(self, markup: str, **kwargs: Any) -> None:
|
|
194
|
+
super().__init__(markup, **kwargs)
|
|
195
|
+
parser = MarkdownIt().enable("strikethrough").enable("table")
|
|
196
|
+
parser.options.maxNesting = 1000
|
|
197
|
+
self.parsed = parser.parse(markup)
|
|
198
|
+
|
|
199
|
+
_deep_markdown_cls = DeepMarkdown
|
|
200
|
+
return DeepMarkdown
|
|
201
|
+
|
|
202
|
+
|
|
104
203
|
class Application:
|
|
105
204
|
def __init__(self) -> None:
|
|
106
205
|
self.config = CommonConfig()
|
|
@@ -115,12 +214,35 @@ class Application:
|
|
|
115
214
|
def show_diagnostics(self, value: bool) -> None:
|
|
116
215
|
self._show_diagnostics = value
|
|
117
216
|
|
|
217
|
+
def color_for(self, file: Optional[IO[Any]] = None, err: bool = False) -> bool:
|
|
218
|
+
"""Resolve the color decision for a specific output destination.
|
|
219
|
+
|
|
220
|
+
Honors the explicit ``--color`` / ``--no-color`` choice first, then the
|
|
221
|
+
``FORCE_COLOR`` / ``NO_COLOR`` conventions (https://no-color.org/), and
|
|
222
|
+
finally the TTY status of the relevant stream — which may be stdout,
|
|
223
|
+
stderr, or an explicit ``file=``. This matters because stdout and
|
|
224
|
+
stderr can be redirected independently: piping ``stdout`` into a file
|
|
225
|
+
should not disable colour on warnings written to ``stderr`` if that
|
|
226
|
+
is still attached to a terminal.
|
|
227
|
+
"""
|
|
228
|
+
pref = self.config.colored_output
|
|
229
|
+
if pref == ColoredOutput.NO:
|
|
230
|
+
return False
|
|
231
|
+
if pref == ColoredOutput.YES:
|
|
232
|
+
return True
|
|
233
|
+
if os.environ.get("FORCE_COLOR"):
|
|
234
|
+
return True
|
|
235
|
+
if os.environ.get("NO_COLOR"):
|
|
236
|
+
return False
|
|
237
|
+
stream = file if file is not None else (sys.stderr if err else sys.stdout)
|
|
238
|
+
isatty = getattr(stream, "isatty", None)
|
|
239
|
+
return bool(isatty()) if callable(isatty) else False
|
|
240
|
+
|
|
118
241
|
@property
|
|
119
242
|
def colored(self) -> bool:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
]
|
|
243
|
+
"""Shortcut for the color decision on stdout — used for branching
|
|
244
|
+
between rich and plain rendering paths that always target stdout."""
|
|
245
|
+
return self.color_for()
|
|
124
246
|
|
|
125
247
|
@property
|
|
126
248
|
def has_rich(self) -> bool:
|
|
@@ -139,12 +261,13 @@ class Application:
|
|
|
139
261
|
err: Optional[bool] = True,
|
|
140
262
|
) -> None:
|
|
141
263
|
if self.config.verbose:
|
|
264
|
+
err_resolved = err if err is not None else True
|
|
142
265
|
click.secho(
|
|
143
266
|
message() if callable(message) else message,
|
|
144
267
|
file=file,
|
|
145
268
|
nl=nl if nl is not None else True,
|
|
146
|
-
err=
|
|
147
|
-
color=self.
|
|
269
|
+
err=err_resolved,
|
|
270
|
+
color=self.color_for(file=file, err=err_resolved),
|
|
148
271
|
fg="bright_black",
|
|
149
272
|
)
|
|
150
273
|
|
|
@@ -167,7 +290,7 @@ class Application:
|
|
|
167
290
|
show_percent=show_percent,
|
|
168
291
|
show_pos=show_pos,
|
|
169
292
|
file=sys.stderr,
|
|
170
|
-
color=self.
|
|
293
|
+
color=self.color_for(file=sys.stderr),
|
|
171
294
|
)
|
|
172
295
|
|
|
173
296
|
def warning(
|
|
@@ -177,12 +300,13 @@ class Application:
|
|
|
177
300
|
nl: Optional[bool] = True,
|
|
178
301
|
err: Optional[bool] = True,
|
|
179
302
|
) -> None:
|
|
303
|
+
err_resolved = err if err is not None else True
|
|
180
304
|
click.secho(
|
|
181
305
|
f"[ {click.style('WARN', fg='yellow')} ] {message() if callable(message) else message}",
|
|
182
306
|
file=file,
|
|
183
307
|
nl=nl if nl is not None else True,
|
|
184
|
-
err=
|
|
185
|
-
color=self.
|
|
308
|
+
err=err_resolved,
|
|
309
|
+
color=self.color_for(file=file, err=err_resolved),
|
|
186
310
|
fg="bright_yellow",
|
|
187
311
|
)
|
|
188
312
|
|
|
@@ -193,12 +317,13 @@ class Application:
|
|
|
193
317
|
nl: Optional[bool] = True,
|
|
194
318
|
err: Optional[bool] = True,
|
|
195
319
|
) -> None:
|
|
320
|
+
err_resolved = err if err is not None else True
|
|
196
321
|
click.secho(
|
|
197
322
|
f"[ {click.style('ERROR', fg='red')} ] {message() if callable(message) else message}",
|
|
198
323
|
file=file,
|
|
199
324
|
nl=nl if nl is not None else True,
|
|
200
|
-
err=
|
|
201
|
-
color=self.
|
|
325
|
+
err=err_resolved,
|
|
326
|
+
color=self.color_for(file=file, err=err_resolved),
|
|
202
327
|
)
|
|
203
328
|
|
|
204
329
|
def print_data(
|
|
@@ -240,21 +365,19 @@ class Application:
|
|
|
240
365
|
if format == OutputFormat.JSON_INDENT:
|
|
241
366
|
format = OutputFormat.JSON
|
|
242
367
|
console = Console(soft_wrap=True)
|
|
243
|
-
|
|
368
|
+
syntax = Syntax(text, format, background_color="default")
|
|
369
|
+
if self._should_page(lambda: text.count("\n")):
|
|
244
370
|
with console.pager(styles=True, links=True):
|
|
245
|
-
console.print(
|
|
371
|
+
console.print(syntax)
|
|
246
372
|
else:
|
|
247
|
-
console.print(
|
|
373
|
+
console.print(syntax)
|
|
248
374
|
|
|
249
375
|
return
|
|
250
376
|
except ImportError:
|
|
251
377
|
if self.config.colored_output == ColoredOutput.YES:
|
|
252
378
|
self.warning('Package "rich" is required to use colored output.')
|
|
253
379
|
|
|
254
|
-
|
|
255
|
-
self.echo_via_pager(text)
|
|
256
|
-
else:
|
|
257
|
-
self.echo(text)
|
|
380
|
+
self.echo_via_pager(text)
|
|
258
381
|
|
|
259
382
|
return
|
|
260
383
|
|
|
@@ -269,55 +392,57 @@ class Application:
|
|
|
269
392
|
message() if callable(message) else message,
|
|
270
393
|
file=file,
|
|
271
394
|
nl=nl,
|
|
272
|
-
color=self.
|
|
395
|
+
color=self.color_for(file=file, err=err),
|
|
273
396
|
err=err,
|
|
274
397
|
)
|
|
275
398
|
|
|
276
|
-
def
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
TableBodyElement.new_line = False
|
|
295
|
-
TableRowElement.new_line = False
|
|
296
|
-
TableDataElement.new_line = False
|
|
399
|
+
def _should_page(self, measure_lines: Callable[[], int]) -> bool:
|
|
400
|
+
"""Tri-state pager decision.
|
|
401
|
+
|
|
402
|
+
- `config.pager` is True → always page.
|
|
403
|
+
- `config.pager` is False → never page.
|
|
404
|
+
- `config.pager` is None → auto: page only when stdout is a TTY AND
|
|
405
|
+
the rendered output exceeds the terminal
|
|
406
|
+
height (measured via the callback).
|
|
407
|
+
"""
|
|
408
|
+
pref = self.config.pager
|
|
409
|
+
if pref is True:
|
|
410
|
+
return True
|
|
411
|
+
if pref is False:
|
|
412
|
+
return False
|
|
413
|
+
if not sys.stdout.isatty():
|
|
414
|
+
return False
|
|
415
|
+
try:
|
|
416
|
+
from shutil import get_terminal_size
|
|
297
417
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
418
|
+
term_lines = get_terminal_size(fallback=(80, 24)).lines
|
|
419
|
+
return measure_lines() > term_lines - 2
|
|
420
|
+
except Exception:
|
|
421
|
+
return False
|
|
302
422
|
|
|
303
|
-
|
|
423
|
+
def echo_as_markdown(self, text: str) -> None:
|
|
424
|
+
# Plain / piped output just emits the raw markdown — it's
|
|
425
|
+
# readable as-is and pastable into PRs, Slack, or an LLM.
|
|
426
|
+
if not self.colored:
|
|
427
|
+
self.echo_via_pager(text)
|
|
428
|
+
return
|
|
304
429
|
|
|
305
|
-
|
|
430
|
+
from rich.console import Console
|
|
306
431
|
|
|
307
|
-
|
|
432
|
+
markdown = _get_deep_markdown_cls()(text, justify="left", code_theme="default")
|
|
433
|
+
console = Console()
|
|
308
434
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
console.print(markdown)
|
|
315
|
-
return
|
|
316
|
-
except ImportError:
|
|
317
|
-
if self.config.colored_output == ColoredOutput.YES:
|
|
318
|
-
self.warning('Package "rich" is required to use colored output.')
|
|
435
|
+
def _measure() -> int:
|
|
436
|
+
measure = Console(width=console.size.width, record=False, soft_wrap=True)
|
|
437
|
+
with measure.capture() as cap:
|
|
438
|
+
measure.print(markdown)
|
|
439
|
+
return cap.get().count("\n")
|
|
319
440
|
|
|
320
|
-
self.
|
|
441
|
+
if self._should_page(_measure):
|
|
442
|
+
with console.pager(styles=True, links=True):
|
|
443
|
+
console.print(markdown)
|
|
444
|
+
else:
|
|
445
|
+
console.print(markdown)
|
|
321
446
|
|
|
322
447
|
def echo_via_pager(
|
|
323
448
|
self,
|
|
@@ -325,18 +450,20 @@ class Application:
|
|
|
325
450
|
color: Optional[bool] = None,
|
|
326
451
|
) -> None:
|
|
327
452
|
try:
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
453
|
+
text = (
|
|
454
|
+
text_or_generator
|
|
455
|
+
if isinstance(text_or_generator, str)
|
|
456
|
+
else "".join(text_or_generator() if callable(text_or_generator) else text_or_generator)
|
|
457
|
+
)
|
|
458
|
+
use_color = color if color is not None else self.colored
|
|
459
|
+
if self._should_page(lambda: text.count("\n")):
|
|
460
|
+
if use_color:
|
|
461
|
+
# click only sets `LESS=-R` when color=None — set it here so
|
|
462
|
+
# less renders ANSI styles instead of showing raw ESC codes.
|
|
463
|
+
os.environ.setdefault("LESS", "-R")
|
|
464
|
+
click.echo_via_pager(text, color=use_color)
|
|
335
465
|
else:
|
|
336
|
-
click.
|
|
337
|
-
text_or_generator,
|
|
338
|
-
color=color if color is not None else self.colored,
|
|
339
|
-
)
|
|
466
|
+
click.echo(text, color=use_color)
|
|
340
467
|
except OSError:
|
|
341
468
|
pass
|
|
342
469
|
|
robotcode/plugin/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2.
|
|
1
|
+
__version__ = "2.6.0"
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Detect whether the current process runs inside an AI agent session.
|
|
2
|
+
|
|
3
|
+
There is no agreed-upon standard yet — we check a union of variables
|
|
4
|
+
known to be set by popular tools (Claude Code, Cursor, Copilot CLI,
|
|
5
|
+
OpenCode, Codex, …), plus the proposed generic conventions ``AI_AGENT``
|
|
6
|
+
and ``AGENT``. When any of them is set to a truthy value, callers use
|
|
7
|
+
the result to flip presentation defaults (colour, pager, REPL plain
|
|
8
|
+
backend) so agents get clean stdin/stdout without users having to pass
|
|
9
|
+
``--no-color`` / ``--no-pager`` / ``--plain`` on every invocation.
|
|
10
|
+
|
|
11
|
+
Override hatches:
|
|
12
|
+
|
|
13
|
+
- ``ROBOTCODE_FORCE_AI_AGENT=1`` — force the detection on (for testing
|
|
14
|
+
and for cases where a marker variable isn't recognised yet).
|
|
15
|
+
- ``ROBOTCODE_NO_AI_AGENT=1`` — force the detection off, regardless of
|
|
16
|
+
any other marker.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
from typing import Final, Tuple
|
|
21
|
+
|
|
22
|
+
_AGENT_ENV_VARS: Final[Tuple[str, ...]] = (
|
|
23
|
+
# Generic / proposed standards
|
|
24
|
+
"AI_AGENT",
|
|
25
|
+
"AGENT",
|
|
26
|
+
# Anthropic
|
|
27
|
+
"CLAUDECODE",
|
|
28
|
+
"CLAUDE_CODE",
|
|
29
|
+
# Cursor
|
|
30
|
+
"CURSOR_AGENT",
|
|
31
|
+
"CURSOR_TRACE_ID",
|
|
32
|
+
# OpenAI Codex (set on every subprocess Codex spawns)
|
|
33
|
+
"CODEX_CI",
|
|
34
|
+
"CODEX_THREAD_ID",
|
|
35
|
+
"CODEX_SANDBOX", # macOS-Seatbelt only; treated as a secondary signal
|
|
36
|
+
# Google
|
|
37
|
+
"GEMINI_CLI",
|
|
38
|
+
"ANTIGRAVITY_AGENT",
|
|
39
|
+
# GitHub Copilot — COPILOT_AGENT is set in terminals launched from
|
|
40
|
+
# VS Code Copilot Chat / agent mode (see microsoft/vscode#311734);
|
|
41
|
+
# COPILOT_AGENT_SESSION_ID is set on every shell command and MCP
|
|
42
|
+
# server the standalone Copilot CLI spawns (>= 0.0.429, April 2026).
|
|
43
|
+
"COPILOT_AGENT",
|
|
44
|
+
"COPILOT_AGENT_SESSION_ID",
|
|
45
|
+
# Microsoft VS Code (1.121+): set when a terminal command is launched
|
|
46
|
+
# by the VS Code agent flow (Copilot Chat) rather than a human.
|
|
47
|
+
"VSCODE_AGENT",
|
|
48
|
+
# opencode (sets both OPENCODE and AGENT)
|
|
49
|
+
"OPENCODE",
|
|
50
|
+
"OPENCODE_CLIENT", # legacy / Vercel-detection compat
|
|
51
|
+
# Misc agents
|
|
52
|
+
"AUGMENT_AGENT",
|
|
53
|
+
"CLINE_ACTIVE",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
_FORCE_ON_VAR: Final = "ROBOTCODE_FORCE_AI_AGENT"
|
|
57
|
+
_FORCE_OFF_VAR: Final = "ROBOTCODE_NO_AI_AGENT"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _is_active(var: str) -> bool:
|
|
61
|
+
"""A marker var is "active" when it is present in the environment
|
|
62
|
+
with any value other than the empty string or ``"0"``.
|
|
63
|
+
|
|
64
|
+
Different agents put very different things in their marker (``1``,
|
|
65
|
+
the agent's name, a session id, …), so anything we'd treat as
|
|
66
|
+
"no, you weren't serious" risks fighting the agent. Only the
|
|
67
|
+
explicit ``=0`` opt-out and absence count as off.
|
|
68
|
+
"""
|
|
69
|
+
value = os.environ.get(var)
|
|
70
|
+
if value is None:
|
|
71
|
+
return False
|
|
72
|
+
return value.strip() not in ("", "0")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def is_running_in_ai_agent() -> bool:
|
|
76
|
+
"""True when any known agent-marker env var is active.
|
|
77
|
+
|
|
78
|
+
`ROBOTCODE_FORCE_AI_AGENT` wins over everything; `ROBOTCODE_NO_AI_AGENT`
|
|
79
|
+
wins over the tool-specific markers but loses to `ROBOTCODE_FORCE_AI_AGENT`.
|
|
80
|
+
"""
|
|
81
|
+
if _is_active(_FORCE_ON_VAR):
|
|
82
|
+
return True
|
|
83
|
+
if _is_active(_FORCE_OFF_VAR):
|
|
84
|
+
return False
|
|
85
|
+
return any(_is_active(var) for var in _AGENT_ENV_VARS)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def detected_agent_marker() -> str:
|
|
89
|
+
"""Name of the first active env var, or ``""`` when none is set.
|
|
90
|
+
|
|
91
|
+
Returns the override-var name when one of them dictated the result;
|
|
92
|
+
otherwise the first tool-specific marker found, in the order listed
|
|
93
|
+
in `_AGENT_ENV_VARS`. For diagnostics / debug logging only.
|
|
94
|
+
"""
|
|
95
|
+
if _is_active(_FORCE_ON_VAR):
|
|
96
|
+
return _FORCE_ON_VAR
|
|
97
|
+
if _is_active(_FORCE_OFF_VAR):
|
|
98
|
+
return ""
|
|
99
|
+
for var in _AGENT_ENV_VARS:
|
|
100
|
+
if _is_active(var):
|
|
101
|
+
return var
|
|
102
|
+
return ""
|
|
@@ -30,7 +30,7 @@ class EnumChoice(click.Choice, Generic[T]): # type: ignore[type-arg]
|
|
|
30
30
|
excluded: Optional[Set[T]] = None,
|
|
31
31
|
) -> None:
|
|
32
32
|
super().__init__(
|
|
33
|
-
choices if excluded is None else
|
|
33
|
+
choices if excluded is None else [c for c in choices if c not in excluded],
|
|
34
34
|
case_sensitive,
|
|
35
35
|
)
|
|
36
36
|
|
|
@@ -76,7 +76,7 @@ class AddressesPort(NamedTuple):
|
|
|
76
76
|
port: Optional[int] = None
|
|
77
77
|
|
|
78
78
|
|
|
79
|
-
class AddressPortParamType(click.ParamType):
|
|
79
|
+
class AddressPortParamType(click.ParamType): # type: ignore[type-arg]
|
|
80
80
|
name = "[<address>:]<port>"
|
|
81
81
|
|
|
82
82
|
def convert(
|
|
@@ -113,7 +113,7 @@ class MutuallyExclusiveOption(click.Option):
|
|
|
113
113
|
self.mutually_exclusive = mutually_exclusive
|
|
114
114
|
help = kwargs.get("help", "")
|
|
115
115
|
if self.mutually_exclusive:
|
|
116
|
-
ex_str = ", ".join(self.mutually_exclusive)
|
|
116
|
+
ex_str = ", ".join(sorted(self.mutually_exclusive))
|
|
117
117
|
kwargs["help"] = help + ("\n*NOTE:* This option is mutually exclusive with options: " + ex_str + ".")
|
|
118
118
|
super(MutuallyExclusiveOption, self).__init__(*args, **kwargs)
|
|
119
119
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: robotcode-plugin
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.0
|
|
4
4
|
Summary: Some classes for RobotCode plugin management
|
|
5
5
|
Project-URL: Donate, https://opencollective.com/robotcode
|
|
6
6
|
Project-URL: Documentation, https://github.com/robotcodedev/robotcode#readme
|
|
@@ -27,6 +27,7 @@ Requires-Python: >=3.10
|
|
|
27
27
|
Requires-Dist: click>=8.2.0
|
|
28
28
|
Requires-Dist: colorama>=0.4.6
|
|
29
29
|
Requires-Dist: pluggy>=1.0.0
|
|
30
|
+
Requires-Dist: rich>=14.0.0
|
|
30
31
|
Requires-Dist: tomli-w>=1.0.0
|
|
31
32
|
Description-Content-Type: text/markdown
|
|
32
33
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
robotcode/plugin/__init__.py,sha256=DDk7J1ulkuIOzqIC41ATau9sbe7zbTkJHSG0G8OvMTM,16308
|
|
2
|
+
robotcode/plugin/__version__.py,sha256=OEib63e0yPEGlhEXyrWE1OwRnleR0cHI7KSX7oZEQLs,22
|
|
3
|
+
robotcode/plugin/_agent_detection.py,sha256=PVsBAxy_Xn_uefTBpBAGE-q3Z5CUxbhwp_z4Dcb_RL4,3606
|
|
4
|
+
robotcode/plugin/manager.py,sha256=ARNXtSbQTRktsbNQGLeU1fwtsK7GdstHa6OJaXuadrU,1098
|
|
5
|
+
robotcode/plugin/py.typed,sha256=bWew9mHgMy8LqMu7RuqQXFXLBxh2CRx0dUbSx-3wE48,27
|
|
6
|
+
robotcode/plugin/specs.py,sha256=_Ws6A9CZoCTHdxx_5D8ifpj4tfwin4zYUYJ8WLhSO3U,588
|
|
7
|
+
robotcode/plugin/click_helper/aliases.py,sha256=NJZKyF6GOro52fjp728OZfqZ9EptNhiJvQBhunqIpzc,1628
|
|
8
|
+
robotcode/plugin/click_helper/options.py,sha256=eAU05TfOq93wVWkiYqw7BV6RrXAp1xw9jT6DsPRdZ_Y,11430
|
|
9
|
+
robotcode/plugin/click_helper/types.py,sha256=UAno_G-0uNdPfapGCvECwd0yghdEZXMMv4DIlUAKKsw,3679
|
|
10
|
+
robotcode_plugin-2.6.0.dist-info/METADATA,sha256=5bekknuAIsTBNcZPEOhC5Nj2-RMkmegOGZGZmtjIH-o,2785
|
|
11
|
+
robotcode_plugin-2.6.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
12
|
+
robotcode_plugin-2.6.0.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
robotcode/plugin/__init__.py,sha256=4meRqy1rySlJl6yq1iadVJtRHaPVV-gJ05KjYkVwnKc,11183
|
|
2
|
-
robotcode/plugin/__version__.py,sha256=fMbNgIJqxiZEaSBLadLBt4rZpCHqarzb4Okt-aWsp2E,22
|
|
3
|
-
robotcode/plugin/manager.py,sha256=ARNXtSbQTRktsbNQGLeU1fwtsK7GdstHa6OJaXuadrU,1098
|
|
4
|
-
robotcode/plugin/py.typed,sha256=bWew9mHgMy8LqMu7RuqQXFXLBxh2CRx0dUbSx-3wE48,27
|
|
5
|
-
robotcode/plugin/specs.py,sha256=_Ws6A9CZoCTHdxx_5D8ifpj4tfwin4zYUYJ8WLhSO3U,588
|
|
6
|
-
robotcode/plugin/click_helper/aliases.py,sha256=NJZKyF6GOro52fjp728OZfqZ9EptNhiJvQBhunqIpzc,1628
|
|
7
|
-
robotcode/plugin/click_helper/options.py,sha256=eAU05TfOq93wVWkiYqw7BV6RrXAp1xw9jT6DsPRdZ_Y,11430
|
|
8
|
-
robotcode/plugin/click_helper/types.py,sha256=ZTFw37zZqw_1QLvqvrNMcp95bgYmVhyzi5lx4MB9-7Q,3639
|
|
9
|
-
robotcode_plugin-2.5.0.dist-info/METADATA,sha256=P92yXM-nLb8dnNCg89yozw5eQ2XXOG4vleph4Tu3fVs,2757
|
|
10
|
-
robotcode_plugin-2.5.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
11
|
-
robotcode_plugin-2.5.0.dist-info/RECORD,,
|