glaip-sdk 0.0.6a0__py3-none-any.whl → 0.0.8__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.
glaip_sdk/_version.py CHANGED
@@ -24,28 +24,51 @@ except Exception: # pragma: no cover
24
24
  _toml = None # type: ignore
25
25
 
26
26
 
27
- def _get_version() -> str:
27
+ def _try_get_installed_version() -> str | None:
28
+ """Try to get version from installed package."""
28
29
  try:
29
30
  return version("glaip-sdk")
30
31
  except PackageNotFoundError:
31
- # Not installed; try reading from local pyproject for dev
32
- if _toml is not None:
33
- try:
34
- from pathlib import Path
35
-
36
- here = Path(__file__).resolve()
37
- root = here.parent.parent # project root (contains pyproject.toml)
38
- pyproject = root / "pyproject.toml"
39
- if pyproject.is_file():
40
- data = _toml.loads(pyproject.read_text(encoding="utf-8"))
41
- ver = data.get("project", {}).get("version") or data.get(
42
- "tool", {}
43
- ).get("poetry", {}).get("version")
44
- if isinstance(ver, str) and ver:
45
- return ver
46
- except Exception:
47
- pass
48
- return "0.0.0.dev0"
32
+ return None
33
+
34
+
35
+ def _try_get_dev_version() -> str | None:
36
+ """Try to get version from local pyproject.toml for development."""
37
+ if _toml is None:
38
+ return None
39
+
40
+ try:
41
+ from pathlib import Path
42
+
43
+ here = Path(__file__).resolve()
44
+ root = here.parent.parent # project root (contains pyproject.toml)
45
+ pyproject = root / "pyproject.toml"
46
+ if not pyproject.is_file():
47
+ return None
48
+
49
+ data = _toml.loads(pyproject.read_text(encoding="utf-8"))
50
+ ver = data.get("project", {}).get("version") or data.get("tool", {}).get(
51
+ "poetry", {}
52
+ ).get("version")
53
+ if isinstance(ver, str) and ver:
54
+ return ver
55
+ except Exception:
56
+ pass
57
+ return None
58
+
59
+
60
+ def _get_version() -> str:
61
+ # Try installed version first
62
+ installed_version = _try_get_installed_version()
63
+ if installed_version:
64
+ return installed_version
65
+
66
+ # Try development version
67
+ dev_version = _try_get_dev_version()
68
+ if dev_version:
69
+ return dev_version
70
+
71
+ return "0.0.0.dev0"
49
72
 
50
73
 
51
74
  __version__ = _get_version()
glaip_sdk/branding.py CHANGED
@@ -31,8 +31,8 @@ except Exception: # pragma: no cover
31
31
 
32
32
 
33
33
  # ---- minimal, readable styles (light blue + white theme) -----------------
34
- PRIMARY = "bright_blue" # Light blue for main elements
35
- BORDER = "bright_blue" # Light blue borders
34
+ PRIMARY = "#15a2d8" # GDP Labs brand blue
35
+ BORDER = PRIMARY # Keep borders aligned with brand tone
36
36
  TITLE_STYLE = f"bold {PRIMARY}"
37
37
  LABEL = "bold"
38
38
 
@@ -95,7 +95,7 @@ GDP Labs AI Agents Package
95
95
  # ---- public API -----------------------------------------------------------
96
96
  def get_welcome_banner(self) -> str:
97
97
  """Get AIP banner with version info."""
98
- banner = self.AIP_LOGO
98
+ banner = f"[{PRIMARY}]{self.AIP_LOGO}[/{PRIMARY}]"
99
99
  line = f"Version: {self.version}"
100
100
  banner = f"{banner}\n{line}"
101
101
  return banner
@@ -6,6 +6,7 @@ Authors:
6
6
 
7
7
  import json
8
8
  import os
9
+ from collections.abc import Mapping
9
10
  from pathlib import Path
10
11
  from typing import Any
11
12
 
@@ -47,11 +48,13 @@ from glaip_sdk.cli.utils import (
47
48
  _fuzzy_pick_for_resources,
48
49
  build_renderer,
49
50
  coerce_to_row,
51
+ detect_export_format,
50
52
  get_client,
51
53
  get_ctx_value,
52
54
  output_flags,
53
55
  output_list,
54
56
  output_result,
57
+ spinner_context,
55
58
  )
56
59
  from glaip_sdk.cli.validators import (
57
60
  validate_agent_instruction_cli as validate_agent_instruction,
@@ -75,6 +78,91 @@ console = Console()
75
78
  AGENT_NOT_FOUND_ERROR = "Agent not found"
76
79
 
77
80
 
81
+ def _safe_agent_attribute(agent: Any, name: str) -> Any:
82
+ """Return attribute value for ``name`` while filtering Mock sentinels."""
83
+
84
+ try:
85
+ value = getattr(agent, name)
86
+ except Exception:
87
+ return None
88
+
89
+ if hasattr(value, "_mock_name"):
90
+ return None
91
+ return value
92
+
93
+
94
+ def _coerce_mapping_candidate(candidate: Any) -> dict[str, Any] | None:
95
+ """Convert a mapping-like candidate to a plain dict when possible."""
96
+
97
+ if candidate is None:
98
+ return None
99
+ if isinstance(candidate, Mapping):
100
+ return dict(candidate)
101
+ return None
102
+
103
+
104
+ def _call_agent_method(agent: Any, method_name: str) -> dict[str, Any] | None:
105
+ """Attempt to call the named method and coerce its output to a dict."""
106
+
107
+ method = getattr(agent, method_name, None)
108
+ if not callable(method):
109
+ return None
110
+ try:
111
+ candidate = method()
112
+ except Exception:
113
+ return None
114
+ return _coerce_mapping_candidate(candidate)
115
+
116
+
117
+ def _coerce_agent_via_methods(agent: Any) -> dict[str, Any] | None:
118
+ """Try standard serialisation helpers to produce a mapping."""
119
+
120
+ for attr in ("model_dump", "dict", "to_dict"):
121
+ mapping = _call_agent_method(agent, attr)
122
+ if mapping is not None:
123
+ return mapping
124
+ return None
125
+
126
+
127
+ def _build_fallback_agent_mapping(agent: Any) -> dict[str, Any]:
128
+ """Construct a minimal mapping from well-known agent attributes."""
129
+
130
+ fallback_fields = (
131
+ "id",
132
+ "name",
133
+ "instruction",
134
+ "description",
135
+ "model",
136
+ "agent_config",
137
+ "tools",
138
+ "agents",
139
+ "mcps",
140
+ "timeout",
141
+ )
142
+
143
+ fallback: dict[str, Any] = {}
144
+ for field in fallback_fields:
145
+ value = _safe_agent_attribute(agent, field)
146
+ if value is not None:
147
+ fallback[field] = value
148
+
149
+ return fallback or {"name": str(agent)}
150
+
151
+
152
+ def _prepare_agent_output(agent: Any) -> dict[str, Any]:
153
+ """Build a JSON-serialisable mapping for CLI output."""
154
+
155
+ method_mapping = _coerce_agent_via_methods(agent)
156
+ if method_mapping is not None:
157
+ return method_mapping
158
+
159
+ intrinsic = _coerce_mapping_candidate(agent)
160
+ if intrinsic is not None:
161
+ return intrinsic
162
+
163
+ return _build_fallback_agent_mapping(agent)
164
+
165
+
78
166
  def _fetch_full_agent_details(client: Any, agent: Any) -> Any | None:
79
167
  """Fetch full agent details by ID to ensure all fields are populated."""
80
168
  try:
@@ -180,7 +268,7 @@ def _format_fallback_agent_data(client: Any, agent: Any) -> dict:
180
268
  result_data = build_resource_result_data(full_agent, fields)
181
269
 
182
270
  # Handle missing instruction
183
- if not result_data.get("instruction"):
271
+ if result_data.get("instruction") in ["N/A", None, ""]:
184
272
  result_data["instruction"] = "-"
185
273
 
186
274
  # Format dates for better display
@@ -198,7 +286,12 @@ def _display_agent_details(ctx: Any, client: Any, agent: Any) -> None:
198
286
  return
199
287
 
200
288
  # Try to fetch and format raw agent data first
201
- formatted_data = _fetch_and_format_raw_agent_data(client, agent)
289
+ with spinner_context(
290
+ ctx,
291
+ "[bold blue]Loading agent details…[/bold blue]",
292
+ console_override=console,
293
+ ):
294
+ formatted_data = _fetch_and_format_raw_agent_data(client, agent)
202
295
 
203
296
  if formatted_data:
204
297
  # Use raw API data - this preserves ALL fields including account_id
@@ -215,7 +308,12 @@ def _display_agent_details(ctx: Any, client: Any, agent: Any) -> None:
215
308
  ctx, Text("[yellow]Falling back to Pydantic model data[/yellow]")
216
309
  )
217
310
 
218
- result_data = _format_fallback_agent_data(client, agent)
311
+ with spinner_context(
312
+ ctx,
313
+ "[bold blue]Preparing fallback agent details…[/bold blue]",
314
+ console_override=console,
315
+ ):
316
+ result_data = _format_fallback_agent_data(client, agent)
219
317
 
220
318
  # Display using output_result
221
319
  output_result(
@@ -288,13 +386,18 @@ def list_agents(
288
386
  """List agents with optional filtering."""
289
387
  try:
290
388
  client = get_client(ctx)
291
- agents = client.agents.list_agents(
292
- agent_type=agent_type,
293
- framework=framework,
294
- name=name,
295
- version=version,
296
- sync_langflow_agents=sync_langflow,
297
- )
389
+ with spinner_context(
390
+ ctx,
391
+ "[bold blue]Fetching agents…[/bold blue]",
392
+ console_override=console,
393
+ ):
394
+ agents = client.agents.list_agents(
395
+ agent_type=agent_type,
396
+ framework=framework,
397
+ name=name,
398
+ version=version,
399
+ sync_langflow_agents=sync_langflow,
400
+ )
298
401
 
299
402
  # Define table columns: (data_key, header, style, width)
300
403
  columns = [
@@ -356,18 +459,20 @@ def get(ctx: Any, agent_ref: str, select: int | None, export: str | None) -> Non
356
459
  )
357
460
 
358
461
  # Handle export option
359
- if export: # pragma: no cover - requires filesystem verification
462
+ if export:
360
463
  export_path = Path(export)
361
464
  # Auto-detect format from file extension
362
- if export_path.suffix.lower() in [".yaml", ".yml"]:
363
- detected_format = "yaml"
364
- else:
365
- detected_format = "json"
465
+ detected_format = detect_export_format(export_path)
366
466
 
367
467
  # Always export comprehensive data - re-fetch agent with full details
368
468
  try:
369
- agent = client.agents.get_agent_by_id(agent.id)
370
- except Exception as e: # pragma: no cover - best-effort fallback messaging
469
+ with spinner_context(
470
+ ctx,
471
+ "[bold blue]Fetching complete agent data…[/bold blue]",
472
+ console_override=console,
473
+ ):
474
+ agent = client.agents.get_agent_by_id(agent.id)
475
+ except Exception as e:
371
476
  handle_rich_output(
372
477
  ctx,
373
478
  Text(
@@ -584,8 +689,7 @@ def run(
584
689
  handle_json_output(ctx, error=Exception(error_msg))
585
690
  raise click.ClickException(error_msg)
586
691
  except Exception as e:
587
- handle_json_output(ctx, error=e)
588
- raise click.ClickException(str(e))
692
+ _handle_command_exception(ctx, e)
589
693
 
590
694
 
591
695
  def _handle_import_file_logic(
@@ -779,7 +883,7 @@ def _get_language_model_display_name(agent: Any, model: str | None) -> str:
779
883
 
780
884
  def _handle_successful_creation(ctx: Any, agent: Any, model: str | None) -> None:
781
885
  """Handle successful agent creation output."""
782
- handle_json_output(ctx, agent.model_dump())
886
+ handle_json_output(ctx, _prepare_agent_output(agent))
783
887
 
784
888
  lm_display = _get_language_model_display_name(agent, model)
785
889
 
@@ -798,8 +902,8 @@ def _handle_successful_creation(ctx: Any, agent: Any, model: str | None) -> None
798
902
  handle_rich_output(ctx, display_agent_run_suggestions(agent))
799
903
 
800
904
 
801
- def _handle_creation_exception(ctx: Any, e: Exception) -> None:
802
- """Handle exceptions during agent creation."""
905
+ def _handle_command_exception(ctx: Any, e: Exception) -> None:
906
+ """Handle exceptions during command execution with consistent error handling."""
803
907
  if isinstance(e, click.ClickException):
804
908
  if get_ctx_value(ctx, "view") == "json":
805
909
  handle_json_output(ctx, error=Exception(AGENT_NOT_FOUND_ERROR))
@@ -811,6 +915,11 @@ def _handle_creation_exception(ctx: Any, e: Exception) -> None:
811
915
  raise click.ClickException(str(e))
812
916
 
813
917
 
918
+ def _handle_creation_exception(ctx: Any, e: Exception) -> None:
919
+ """Handle exceptions during agent creation."""
920
+ _handle_command_exception(ctx, e)
921
+
922
+
814
923
  @agents_group.command()
815
924
  @click.option("--name", help="Agent name")
816
925
  @click.option("--instruction", help="Agent instruction (prompt)")
@@ -1078,7 +1187,7 @@ def update(
1078
1187
 
1079
1188
  updated_agent = client.agents.update_agent(agent.id, **update_data)
1080
1189
 
1081
- handle_json_output(ctx, updated_agent.model_dump())
1190
+ handle_json_output(ctx, _prepare_agent_output(updated_agent))
1082
1191
  handle_rich_output(ctx, display_update_success("Agent", updated_agent.name))
1083
1192
  handle_rich_output(ctx, display_agent_run_suggestions(updated_agent))
1084
1193
 
@@ -1089,10 +1198,7 @@ def update(
1089
1198
  # Re-raise ClickExceptions without additional processing
1090
1199
  raise
1091
1200
  except Exception as e:
1092
- handle_json_output(ctx, error=e)
1093
- if get_ctx_value(ctx, "view") != "json":
1094
- print_api_error(e)
1095
- raise click.ClickException(str(e))
1201
+ _handle_command_exception(ctx, e)
1096
1202
 
1097
1203
 
1098
1204
  @agents_group.command()
@@ -1133,10 +1239,7 @@ def delete(ctx: Any, agent_id: str, yes: bool) -> None:
1133
1239
  # Re-raise ClickExceptions without additional processing
1134
1240
  raise
1135
1241
  except Exception as e:
1136
- handle_json_output(ctx, error=e)
1137
- if get_ctx_value(ctx, "view") != "json":
1138
- print_api_error(e)
1139
- raise click.ClickException(str(e))
1242
+ _handle_command_exception(ctx, e)
1140
1243
 
1141
1244
 
1142
1245
  @agents_group.command()
@@ -1149,9 +1252,7 @@ def delete(ctx: Any, agent_id: str, yes: bool) -> None:
1149
1252
  )
1150
1253
  @output_flags()
1151
1254
  @click.pass_context
1152
- def sync_langflow(
1153
- ctx: Any, base_url: str | None, api_key: str | None
1154
- ) -> None: # pragma: no cover - integration-only path
1255
+ def sync_langflow(ctx: Any, base_url: str | None, api_key: str | None) -> None:
1155
1256
  """Sync agents with LangFlow server flows.
1156
1257
 
1157
1258
  This command fetches all flows from the configured LangFlow server and
@@ -1192,7 +1293,4 @@ def sync_langflow(
1192
1293
  )
1193
1294
 
1194
1295
  except Exception as e:
1195
- handle_json_output(ctx, error=e)
1196
- if get_ctx_value(ctx, "view") != "json":
1197
- print_api_error(e)
1198
- raise click.ClickException(str(e))
1296
+ _handle_command_exception(ctx, e)
@@ -47,8 +47,8 @@ def save_config(config: dict[str, Any]) -> None:
47
47
  # Set secure file permissions
48
48
  try:
49
49
  os.chmod(CONFIG_FILE, 0o600)
50
- except Exception:
51
- pass # Best effort
50
+ except Exception: # pragma: no cover - platform dependent best effort
51
+ pass
52
52
 
53
53
 
54
54
  @click.group()
@@ -30,6 +30,7 @@ from glaip_sdk.cli.io import (
30
30
  from glaip_sdk.cli.resolution import resolve_resource_reference
31
31
  from glaip_sdk.cli.utils import (
32
32
  coerce_to_row,
33
+ detect_export_format,
33
34
  get_client,
34
35
  get_ctx_value,
35
36
  output_flags,
@@ -178,10 +179,7 @@ def get(ctx: Any, mcp_ref: str, export: str | None) -> None:
178
179
  if export:
179
180
  export_path = Path(export)
180
181
  # Auto-detect format from file extension
181
- if export_path.suffix.lower() in [".yaml", ".yml"]:
182
- detected_format = "yaml"
183
- else:
184
- detected_format = "json"
182
+ detected_format = detect_export_format(export_path)
185
183
 
186
184
  # Always export comprehensive data - re-fetch MCP with full details if needed
187
185
  try:
@@ -34,6 +34,7 @@ from glaip_sdk.cli.io import (
34
34
  from glaip_sdk.cli.resolution import resolve_resource_reference
35
35
  from glaip_sdk.cli.utils import (
36
36
  coerce_to_row,
37
+ detect_export_format,
37
38
  get_client,
38
39
  get_ctx_value,
39
40
  output_flags,
@@ -328,10 +329,7 @@ def get(ctx: Any, tool_ref: str, select: int | None, export: str | None) -> None
328
329
  if export:
329
330
  export_path = Path(export)
330
331
  # Auto-detect format from file extension
331
- if export_path.suffix.lower() in [".yaml", ".yml"]:
332
- detected_format = "yaml"
333
- else:
334
- detected_format = "json"
332
+ detected_format = detect_export_format(export_path)
335
333
 
336
334
  # Always export comprehensive data - re-fetch tool with full details if needed
337
335
  try:
glaip_sdk/cli/display.py CHANGED
@@ -134,17 +134,18 @@ _MISSING = object()
134
134
  def build_resource_result_data(resource: Any, fields: list[str]) -> dict[str, Any]:
135
135
  """Return a normalized mapping of ``fields`` extracted from ``resource``."""
136
136
 
137
- return {
138
- field: _normalise_field_value(field, _safe_get_attr(resource, field))
139
- for field in fields
140
- }
137
+ result: dict[str, Any] = {}
138
+ for field in fields:
139
+ try:
140
+ value = getattr(resource, field)
141
+ except AttributeError:
142
+ value = _MISSING
143
+ except Exception:
144
+ value = _MISSING
141
145
 
146
+ result[field] = _normalise_field_value(field, value)
142
147
 
143
- def _safe_get_attr(resource: Any, field: str) -> Any:
144
- try:
145
- return getattr(resource, field, _MISSING)
146
- except Exception:
147
- return _MISSING
148
+ return result
148
149
 
149
150
 
150
151
  def _normalise_field_value(field: str, value: Any) -> Any:
@@ -188,9 +189,7 @@ def _build_success_output_data(data: Any) -> dict[str, Any]:
188
189
  return data if data is not None else {"success": True}
189
190
 
190
191
 
191
- def handle_json_output(
192
- ctx: Any, data: Any = None, error: Exception = None
193
- ) -> None: # pragma: no cover - formatting covered via integration tests
192
+ def handle_json_output(ctx: Any, data: Any = None, error: Exception = None) -> None:
194
193
  """Handle JSON output format for CLI commands.
195
194
 
196
195
  Args:
@@ -248,9 +247,10 @@ def display_agent_run_suggestions(agent: Any) -> Panel:
248
247
 
249
248
  return AIPPanel(
250
249
  f"[bold blue]💡 Next Steps:[/bold blue]\n\n"
251
- f"🚀 Run this agent:\n"
252
- f' [green]aip agents run {agent.id} "Your message here"[/green]\n\n'
253
- f"📋 Or use the agent name:\n"
250
+ f"🚀 Start chatting with [bold]{agent.name}[/bold] right here:\n"
251
+ f" Type your message below and press Enter to run it immediately.\n\n"
252
+ f"📋 Prefer the CLI instead?\n"
253
+ f' [green]aip agents run {agent.id} "Your message here"[/green]\n'
254
254
  f' [green]aip agents run "{agent.name}" "Your message here"[/green]\n\n'
255
255
  f"🔧 Available options:\n"
256
256
  f" [dim]--chat-history[/dim] Include previous conversation\n"
glaip_sdk/cli/main.py CHANGED
@@ -24,11 +24,21 @@ from glaip_sdk.cli.commands.configure import (
24
24
  from glaip_sdk.cli.commands.mcps import mcps_group
25
25
  from glaip_sdk.cli.commands.models import models_group
26
26
  from glaip_sdk.cli.commands.tools import tools_group
27
- from glaip_sdk.config.constants import DEFAULT_AGENT_RUN_TIMEOUT
27
+ from glaip_sdk.cli.utils import spinner_context
28
+ from glaip_sdk.config.constants import (
29
+ DEFAULT_AGENT_RUN_TIMEOUT,
30
+ )
28
31
  from glaip_sdk.rich_components import AIPPanel, AIPTable
29
32
 
33
+ # Import SlashSession for potential mocking in tests
34
+ try:
35
+ from glaip_sdk.cli.slash import SlashSession
36
+ except ImportError: # pragma: no cover - optional slash dependencies
37
+ # Slash dependencies might not be available in all environments
38
+ SlashSession = None
39
+
30
40
 
31
- @click.group()
41
+ @click.group(invoke_without_command=True)
32
42
  @click.version_option(version=_SDK_VERSION, prog_name="aip")
33
43
  @click.option("--api-url", envvar="AIP_API_URL", help="AIP API URL")
34
44
  @click.option("--api-key", envvar="AIP_API_KEY", help="AIP API Key")
@@ -72,6 +82,15 @@ def main(
72
82
 
73
83
  ctx.obj["tty"] = not no_tty
74
84
 
85
+ if ctx.invoked_subcommand is None and not ctx.resilient_parsing:
86
+ if _should_launch_slash(ctx) and SlashSession is not None:
87
+ session = SlashSession(ctx)
88
+ session.run()
89
+ ctx.exit()
90
+ else:
91
+ click.echo(ctx.get_help())
92
+ ctx.exit()
93
+
75
94
 
76
95
  # Add command groups
77
96
  main.add_command(agents_group)
@@ -87,6 +106,19 @@ main.add_command(configure_command)
87
106
  # Tip: `--version` is provided by click.version_option above.
88
107
 
89
108
 
109
+ def _should_launch_slash(ctx: click.Context) -> bool:
110
+ """Determine whether to open the command palette automatically."""
111
+
112
+ ctx_obj = ctx.obj or {}
113
+ if not bool(ctx_obj.get("tty", True)):
114
+ return False
115
+
116
+ if not (sys.stdin.isatty() and sys.stdout.isatty()):
117
+ return False
118
+
119
+ return True
120
+
121
+
90
122
  @main.command()
91
123
  @click.pass_context
92
124
  def status(ctx: Any) -> None:
@@ -152,9 +184,23 @@ def status(ctx: Any) -> None:
152
184
 
153
185
  # Test connection by listing resources
154
186
  try:
155
- agents = client.list_agents()
156
- tools = client.list_tools()
157
- mcps = client.list_mcps()
187
+ with spinner_context(
188
+ ctx,
189
+ "[bold blue]Checking GL AIP status…[/bold blue]",
190
+ console_override=console,
191
+ spinner_style="cyan",
192
+ ) as status_indicator:
193
+ if status_indicator is not None:
194
+ status_indicator.update("[bold blue]Fetching agents…[/bold blue]")
195
+ agents = client.list_agents()
196
+
197
+ if status_indicator is not None:
198
+ status_indicator.update("[bold blue]Fetching tools…[/bold blue]")
199
+ tools = client.list_tools()
200
+
201
+ if status_indicator is not None:
202
+ status_indicator.update("[bold blue]Fetching MCPs…[/bold blue]")
203
+ mcps = client.list_mcps()
158
204
 
159
205
  # Create status table
160
206
  table = AIPTable(title="🔗 GL AIP Status")
@@ -12,7 +12,7 @@ from typing import Any
12
12
 
13
13
  import click
14
14
 
15
- from glaip_sdk.cli.utils import resolve_resource
15
+ from glaip_sdk.cli.utils import resolve_resource, spinner_context
16
16
 
17
17
 
18
18
  def resolve_resource_reference(
@@ -25,6 +25,7 @@ def resolve_resource_reference(
25
25
  label: str,
26
26
  select: int | None = None,
27
27
  interface_preference: str | None = None,
28
+ spinner_message: str | None = None,
28
29
  ) -> Any | None:
29
30
  """Resolve resource reference (ID or name) with ambiguity handling.
30
31
 
@@ -47,14 +48,21 @@ def resolve_resource_reference(
47
48
  click.ClickException: If resolution fails
48
49
  """
49
50
  try:
50
- return resolve_resource(
51
- ctx,
52
- reference,
53
- get_by_id=get_by_id_func,
54
- find_by_name=find_by_name_func,
55
- label=label,
56
- select=select,
57
- interface_preference=interface_preference,
51
+ message = (
52
+ spinner_message
53
+ if spinner_message is not None
54
+ else f"[bold blue]Fetching {label}…[/bold blue]"
58
55
  )
56
+ with spinner_context(ctx, message, spinner_style="cyan") as status_indicator:
57
+ return resolve_resource(
58
+ ctx,
59
+ reference,
60
+ get_by_id=get_by_id_func,
61
+ find_by_name=find_by_name_func,
62
+ label=label,
63
+ select=select,
64
+ interface_preference=interface_preference,
65
+ status_indicator=status_indicator,
66
+ )
59
67
  except Exception as e:
60
68
  raise click.ClickException(f"Failed to resolve {resource_type.lower()}: {e}")
@@ -0,0 +1,25 @@
1
+ """Slash command palette entrypoints.
2
+
3
+ Authors:
4
+ Raymond Christopher (raymond.christopher@gdplabs.id)
5
+ """
6
+
7
+ from glaip_sdk.cli.commands.agents import get as agents_get_command
8
+ from glaip_sdk.cli.commands.agents import run as agents_run_command
9
+ from glaip_sdk.cli.commands.configure import configure_command, load_config
10
+ from glaip_sdk.cli.utils import get_client
11
+
12
+ from .agent_session import AgentRunSession
13
+ from .prompt import _HAS_PROMPT_TOOLKIT
14
+ from .session import SlashSession
15
+
16
+ __all__ = [
17
+ "AgentRunSession",
18
+ "SlashSession",
19
+ "_HAS_PROMPT_TOOLKIT",
20
+ "agents_get_command",
21
+ "agents_run_command",
22
+ "configure_command",
23
+ "get_client",
24
+ "load_config",
25
+ ]