Skip to content

Commit 22e638b

Browse files
authored
refactor(repl): remove /list command and adopt per-domain list subcommands (Tracer-Cloud#2713)
* refactor(repl): remove /list command and adopt per-domain list subcommands * resolving the greptile feed back * refactor(command_registry): remove 'list' subcommands and associated completions * chore(session_cmds): add a blank line for improved readability * refactor(command_registry): implement a new root handler for the /tools command * refactor(planner): remove informational question detection and simplify fallback logic * refactor(command_registry): update references to slash_invoke tool description in documentation and tests * refactor(planner): streamline context length overflow handling and improve error classification * refactor(failure_explain): add blank lines for improved readability and maintainability
1 parent 83d5a1f commit 22e638b

38 files changed

Lines changed: 497 additions & 264 deletions

app/cli/interactive_shell/chat/cli_agent.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def _command_selection_response() -> str:
125125
'`{"action":"switch_toolcall_model","model":"claude-opus-4-7"}` '
126126
"to change ONLY the toolcall model on the currently active provider; "
127127
'`{"action":"slash","command":"/model show"}` where command is one of '
128-
"/model show, /list models, /health, /doctor, /version; "
128+
"/model show, /health, /doctor, /version; "
129129
'`{"action":"run_cli_command","args":"<subcommand> <flags>"}` '
130130
"to run any opensre subcommand (agent is blocked). For ordinary "
131131
"questions, return normal Markdown. Do not return action JSON for vague "
@@ -136,7 +136,6 @@ def _command_selection_response() -> str:
136136
_ALLOWED_SLASH_ACTIONS = frozenset(
137137
{
138138
"/model show",
139-
"/list models",
140139
"/health",
141140
"/doctor",
142141
"/version",

app/cli/interactive_shell/command_registry/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from app.cli.interactive_shell.command_registry.suggestions import closest_choice
3838
from app.cli.interactive_shell.command_registry.system import COMMANDS as SYSTEM_COMMANDS
3939
from app.cli.interactive_shell.command_registry.tasks_cmds import COMMANDS as TASK_COMMANDS
40+
from app.cli.interactive_shell.command_registry.tools_cmds import COMMANDS as TOOLS_COMMANDS
4041
from app.cli.interactive_shell.command_registry.types import SlashCommand
4142
from app.cli.interactive_shell.command_registry.watch_cmds import COMMANDS as WATCH_COMMANDS
4243
from app.cli.interactive_shell.routing.handle_message_with_agent.orchestration.execution_policy import (
@@ -53,6 +54,7 @@
5354
SESSION_COMMANDS,
5455
INTEGRATIONS_COMMANDS,
5556
MODEL_COMMANDS,
57+
TOOLS_COMMANDS,
5658
INVESTIGATION_COMMANDS,
5759
TASK_COMMANDS,
5860
WATCH_COMMANDS,

app/cli/interactive_shell/command_registry/agents.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,6 @@ def _cmd_agents(session: ReplSession, console: Console, args: list[str]) -> bool
653653
return _cmd_agents_list(console)
654654

655655
sub = args[0].lower().strip()
656-
657656
if sub == "budget":
658657
return _cmd_agents_budget(session, console, args[1:])
659658
if sub == "bus":

app/cli/interactive_shell/command_registry/help.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,14 @@ def _raw_help_sections() -> list[HelpSection]:
5050
from app.cli.interactive_shell.command_registry.session_cmds import COMMANDS as SESSION_CMDS
5151
from app.cli.interactive_shell.command_registry.system import COMMANDS as SYS_CMDS
5252
from app.cli.interactive_shell.command_registry.tasks_cmds import COMMANDS as TASK_CMDS
53+
from app.cli.interactive_shell.command_registry.tools_cmds import COMMANDS as TOOLS_CMDS
5354
from app.cli.interactive_shell.command_registry.watch_cmds import COMMANDS as WATCH_CMDS
5455

5556
return [
5657
_quick_access_section(),
5758
("Help", list(COMMANDS)),
5859
("Session", list(SESSION_CMDS)),
59-
("Integrations & Models", list(INT_CMDS) + list(MODEL_CMDS)),
60+
("Integrations, Models & Tools", list(INT_CMDS) + list(MODEL_CMDS) + list(TOOLS_CMDS)),
6061
("Investigation", list(INV_CMDS)),
6162
("Privacy", list(PRIVACY_CMDS)),
6263
("Tasks", list(TASK_CMDS) + list(WATCH_CMDS)),

app/cli/interactive_shell/command_registry/integrations.py

Lines changed: 1 addition & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Slash commands for /list, /integrations, and /mcp."""
1+
"""Slash commands for /integrations and /mcp."""
22

33
from __future__ import annotations
44

@@ -8,7 +8,6 @@
88
from app.cli.interactive_shell.command_registry import repl_data
99
from app.cli.interactive_shell.command_registry.cli_parity import run_cli_command
1010
from app.cli.interactive_shell.command_registry.types import ExecutionTier, SlashCommand
11-
from app.cli.interactive_shell.config.tool_catalog import build_tool_catalog
1211
from app.cli.interactive_shell.runtime import ReplSession
1312
from app.cli.interactive_shell.ui import (
1413
BOLD_BRAND,
@@ -19,8 +18,6 @@
1918
WARNING,
2019
render_integrations_table,
2120
render_mcp_table,
22-
render_models_table,
23-
render_tools_table,
2421
repl_table,
2522
)
2623
from app.cli.interactive_shell.ui.choice_menu import (
@@ -36,7 +33,6 @@
3633
repl_print,
3734
)
3835

39-
_ROOT_LIST = "/list"
4036
_ROOT_INTEGRATIONS = "/integrations"
4137
_ROOT_MCP = "/mcp"
4238

@@ -89,40 +85,6 @@ def _render_integration_show(console: Console, service: str) -> bool:
8985
return True
9086

9187

92-
def _interactive_list_menu(_session: ReplSession, console: Console) -> bool:
93-
while True:
94-
sub = repl_choose_one(
95-
title="list",
96-
breadcrumb=_ROOT_LIST,
97-
choices=[
98-
("integrations", "/list integrations"),
99-
("models", "/list models"),
100-
("mcp", "/list mcp"),
101-
("tools", "/list tools"),
102-
("all", "all"),
103-
("done", "done"),
104-
],
105-
)
106-
if sub is None or sub == "done":
107-
return True
108-
if sub == "integrations":
109-
results = repl_data.load_verified_integrations()
110-
render_integrations_table(console, results)
111-
elif sub == "mcp":
112-
results = repl_data.load_verified_integrations()
113-
render_mcp_table(console, results)
114-
elif sub == "models":
115-
render_models_table(console, repl_data.load_llm_settings())
116-
elif sub == "all":
117-
results = repl_data.load_verified_integrations()
118-
render_integrations_table(console, results)
119-
render_mcp_table(console, results)
120-
render_models_table(console, repl_data.load_llm_settings())
121-
elif sub == "tools":
122-
render_tools_table(console, build_tool_catalog())
123-
repl_section_break(console)
124-
125-
12688
def _cmd_integrations(session: ReplSession, console: Console, args: list[str]) -> bool:
12789
if not args and repl_tty_interactive():
12890
return _interactive_integrations_menu(session, console)
@@ -294,50 +256,6 @@ def _interactive_mcp_menu(session: ReplSession, console: Console) -> bool:
294256
repl_section_break(console)
295257

296258

297-
def _cmd_list(session: ReplSession, console: Console, args: list[str]) -> bool:
298-
if not args and repl_tty_interactive():
299-
return _interactive_list_menu(session, console)
300-
301-
sub = (args[0].lower() if args else "").strip()
302-
303-
if sub in ("integrations", "integration", "int"):
304-
render_integrations_table(console, repl_data.load_verified_integrations())
305-
return True
306-
307-
if sub in ("mcp", "mcps"):
308-
render_mcp_table(console, repl_data.load_verified_integrations())
309-
return True
310-
311-
if sub in ("models", "model", "llm", "llms"):
312-
render_models_table(console, repl_data.load_llm_settings())
313-
return True
314-
315-
if sub in ("tools", "tool"):
316-
render_tools_table(console, build_tool_catalog())
317-
return True
318-
319-
if sub and sub not in ("", "all"):
320-
console.print(
321-
f"[{ERROR}]unknown list target:[/] {escape(sub)} "
322-
"(try [bold]/list integrations[/bold], [bold]/list models[/bold], "
323-
"[bold]/list mcp[/bold], or [bold]/list tools[/bold])"
324-
)
325-
return True
326-
327-
results = repl_data.load_verified_integrations()
328-
render_integrations_table(console, results)
329-
render_mcp_table(console, results)
330-
render_models_table(console, repl_data.load_llm_settings())
331-
return True
332-
333-
334-
_LIST_FIRST_ARGS: tuple[tuple[str, str], ...] = (
335-
("integrations", "alert-source integrations"),
336-
("models", "active LLM models"),
337-
("mcp", "connected MCP servers"),
338-
("tools", "registered tools (investigation + chat surfaces)"),
339-
)
340-
341259
_INTEGRATIONS_FIRST_ARGS: tuple[tuple[str, str], ...] = (
342260
("list", "list all configured integrations"),
343261
("verify", "run health checks on all integrations"),
@@ -351,15 +269,6 @@ def _cmd_list(session: ReplSession, console: Console, args: list[str]) -> bool:
351269
)
352270

353271
COMMANDS: list[SlashCommand] = [
354-
SlashCommand(
355-
"/list",
356-
"Browse integrations, MCP servers, models, and tools.",
357-
_cmd_list,
358-
usage=("/list", "/list integrations", "/list models", "/list mcp", "/list tools"),
359-
notes=("In a TTY, bare /list opens an interactive menu.",),
360-
first_arg_completions=_LIST_FIRST_ARGS,
361-
execution_tier=ExecutionTier.SAFE,
362-
),
363272
SlashCommand(
364273
"/integrations",
365274
"Manage integrations.",

app/cli/interactive_shell/command_registry/session_cmds.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
from rich.markup import escape
1111

1212
import app.cli.interactive_shell.command_registry.repl_data as repl_data
13-
from app.cli.interactive_shell.command_registry.types import ExecutionTier, SlashCommand
13+
from app.cli.interactive_shell.command_registry.types import (
14+
ExecutionTier,
15+
SlashCommand,
16+
)
1417
from app.cli.interactive_shell.runtime import ReplSession
1518
from app.cli.interactive_shell.ui import (
1619
BOLD_BRAND,
@@ -658,7 +661,12 @@ def _cmd_resume(session: ReplSession, console: Console, args: list[str]) -> bool
658661
first_arg_completions=_VERBOSE_FIRST_ARGS,
659662
),
660663
SlashCommand("/compact", "Trim old session history to free memory.", _cmd_compact),
661-
SlashCommand("/sessions", "List recent REPL sessions.", _cmd_sessions),
664+
SlashCommand(
665+
"/sessions",
666+
"List recent REPL sessions.",
667+
_cmd_sessions,
668+
usage=("/sessions",),
669+
),
662670
SlashCommand(
663671
"/resume",
664672
"Resume a previous session by restoring its conversation context.",

app/cli/interactive_shell/command_registry/slash_catalog.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
string_property,
1616
)
1717

18-
_MAX_COMPACT_DESC_CHARS = 180
18+
_MAX_COMPACT_DESC_CHARS = 120
1919

2020

2121
@dataclass(frozen=True)
@@ -125,7 +125,7 @@ def _mcp(
125125
"User asks if OpenSRE is healthy, working, or connected",
126126
anti_examples=(
127127
"User asks what integrations OpenSRE supports in general (docs → assistant_handoff)",
128-
"User asks to list connected integrations (use /list integrations)",
128+
"User asks to list connected integrations (use /integrations list)",
129129
),
130130
),
131131
"/help": _mcp(
@@ -147,7 +147,7 @@ def _mcp(
147147
"User asks to show details for a configured integration",
148148
anti_examples=(
149149
"User asks which integrations OpenSRE supports without configuring (assistant_handoff)",
150-
"User asks to list connected integrations (prefer /list integrations)",
150+
"User asks to list connected integrations (prefer /integrations list)",
151151
),
152152
),
153153
"/investigate": _mcp(
@@ -163,14 +163,6 @@ def _mcp(
163163
"Reprint the most recent investigation report from this session.",
164164
"User asks to show the last investigation result or report again",
165165
),
166-
"/list": _mcp(
167-
"Browse connected integrations, MCP servers, active LLM models, or registered tools.",
168-
"User asks to show or list connected/configured integrations or services",
169-
"User asks to list MCP servers (/list mcp)",
170-
anti_examples=(
171-
"User asks what integrations OpenSRE supports in general (assistant_handoff)",
172-
),
173-
),
174166
"/mcp": _mcp(
175167
"Manage connected MCP servers. Subcommands: list, connect, disconnect.",
176168
"User asks to list, connect, or disconnect MCP servers",
@@ -259,6 +251,11 @@ def _mcp(
259251
"Print a starter alert JSON template (generic, datadog, grafana, honeycomb, coralogix, splunk).",
260252
"User asks for an alert template or example payload format",
261253
),
254+
"/tools": _mcp(
255+
"List registered investigation/chat tools wired into this OpenSRE build.",
256+
"User asks what tools the REPL can use",
257+
"User asks to list investigation or chat tools",
258+
),
262259
"/tests": _mcp(
263260
"Browse and run inventoried tests from the terminal. Subcommands: list, run, synthetic.",
264261
"User asks to list or run bundled tests via /tests",
@@ -383,7 +380,7 @@ def format_slash_catalog_text(
383380
if compact and len(desc) > _MAX_COMPACT_DESC_CHARS:
384381
desc = desc[: _MAX_COMPACT_DESC_CHARS - 1].rstrip() + "…"
385382
lines.append(f"- **{spec.name}** — {desc}")
386-
if spec.use_cases:
383+
if spec.use_cases and not compact:
387384
lines.append(f" - use when: {spec.use_cases[0]}")
388385
if spec.anti_examples and not compact:
389386
lines.append(f" - not for: {spec.anti_examples[0]}")
@@ -399,7 +396,10 @@ def slash_invoke_tool_description(specs: list[SlashCommandSpec] | None = None) -
399396
"Pick the command whose use-case best matches the user request, then supply "
400397
"positional args in the args array."
401398
)
402-
body = format_slash_catalog_text(entries, compact=True)
399+
# Keep planner payload intentionally tiny for live LLM runs with strict
400+
# prompt budgets. The full rich catalog remains available via
401+
# format_slash_catalog_text(..., compact=False).
402+
body = "\n".join(f"- `{spec.name}`" for spec in entries)
403403
return f"{header}\n\n{body}"
404404

405405

@@ -410,8 +410,8 @@ def slash_invoke_input_schema(
410410
command_names = tuple(spec.name for spec in entries)
411411
args_description = (
412412
"Positional arguments after the command name. Valid values depend on the "
413-
"chosen command — see the tool description catalog. Examples: "
414-
'["integrations"] for /list, ["verify", "datadog"] for /integrations.'
413+
"chosen command — see the slash_invoke tool description. Examples: "
414+
'["list"] for /tools, ["verify", "datadog"] for /integrations.'
415415
)
416416
return object_schema(
417417
properties={

app/cli/interactive_shell/command_registry/tasks_cmds.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
from rich.console import Console
88
from rich.markup import escape
99

10-
from app.cli.interactive_shell.command_registry.types import ExecutionTier, SlashCommand
10+
from app.cli.interactive_shell.command_registry.types import (
11+
ExecutionTier,
12+
SlashCommand,
13+
)
1114
from app.cli.interactive_shell.history import load_command_history_entries
1215
from app.cli.interactive_shell.runtime import ReplSession, TaskKind, TaskRecord, TaskStatus
1316
from app.cli.interactive_shell.ui import (
@@ -217,7 +220,12 @@ def _cmd_cancel(session: ReplSession, console: Console, args: list[str]) -> bool
217220

218221
COMMANDS: list[SlashCommand] = [
219222
SlashCommand("/history", "Show persisted command history.", _cmd_history),
220-
SlashCommand("/tasks", "List recent and in-flight shell tasks.", _cmd_tasks),
223+
SlashCommand(
224+
"/tasks",
225+
"List recent and in-flight shell tasks.",
226+
_cmd_tasks,
227+
usage=("/tasks",),
228+
),
221229
SlashCommand(
222230
"/cancel",
223231
"Cancel a running task by id.",
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""Slash command /tools."""
2+
3+
from __future__ import annotations
4+
5+
from rich.console import Console
6+
7+
from app.cli.interactive_shell.command_registry.types import (
8+
ExecutionTier,
9+
SlashCommand,
10+
make_list_root_handler,
11+
)
12+
from app.cli.interactive_shell.config.tool_catalog import build_tool_catalog
13+
from app.cli.interactive_shell.runtime import ReplSession
14+
from app.cli.interactive_shell.ui import render_tools_table
15+
16+
17+
def _list_tools(_session: ReplSession, console: Console, _args: list[str]) -> bool:
18+
render_tools_table(console, build_tool_catalog())
19+
return True
20+
21+
22+
_cmd_tools = make_list_root_handler(
23+
"/tools",
24+
_list_tools,
25+
list_aliases=("list", "ls", "tool", "tools"),
26+
)
27+
28+
_TOOLS_FIRST_ARGS: tuple[tuple[str, str], ...] = (
29+
("list", "list registered tools (investigation + chat surfaces)"),
30+
)
31+
32+
COMMANDS: list[SlashCommand] = [
33+
SlashCommand(
34+
"/tools",
35+
"List registered tools.",
36+
_cmd_tools,
37+
usage=("/tools", "/tools list"),
38+
first_arg_completions=_TOOLS_FIRST_ARGS,
39+
execution_tier=ExecutionTier.SAFE,
40+
)
41+
]
42+
43+
__all__ = ["COMMANDS", "_TOOLS_FIRST_ARGS", "_cmd_tools"]

0 commit comments

Comments
 (0)