aru-code 0.27.0__tar.gz → 0.28.0__tar.gz
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.
- {aru_code-0.27.0/aru_code.egg-info → aru_code-0.28.0}/PKG-INFO +1 -1
- aru_code-0.28.0/aru/__init__.py +1 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/cli.py +15 -0
- aru_code-0.28.0/aru/commands.py +244 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/config.py +7 -1
- aru_code-0.28.0/aru/plugin_cache.py +618 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/plugins/custom_tools.py +9 -1
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/plugins/manager.py +9 -1
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/runtime.py +3 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/registry.py +7 -1
- aru_code-0.28.0/aru/tools/skill.py +153 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/tasklist.py +13 -8
- {aru_code-0.27.0 → aru_code-0.28.0/aru_code.egg-info}/PKG-INFO +1 -1
- {aru_code-0.27.0 → aru_code-0.28.0}/aru_code.egg-info/SOURCES.txt +6 -1
- {aru_code-0.27.0 → aru_code-0.28.0}/pyproject.toml +1 -1
- aru_code-0.28.0/tests/test_invoke_skill.py +308 -0
- aru_code-0.28.0/tests/test_plugin_cache.py +354 -0
- aru_code-0.28.0/tests/test_tasklist.py +117 -0
- aru_code-0.27.0/aru/__init__.py +0 -1
- aru_code-0.27.0/aru/commands.py +0 -105
- {aru_code-0.27.0 → aru_code-0.28.0}/LICENSE +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/README.md +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/agent_factory.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/agents/__init__.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/agents/base.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/agents/catalog.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/agents/planner.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/cache_patch.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/checkpoints.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/completers.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/context.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/display.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/history_blocks.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/permissions.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/plugins/__init__.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/plugins/hooks.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/plugins/tool_api.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/providers.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/runner.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/select.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/session.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/__init__.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/_diff.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/_shared.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/ast_tools.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/codebase.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/delegate.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/file_ops.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/gitignore.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/mcp_client.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/plan_mode.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/ranker.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/search.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/shell.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru/tools/web.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru_code.egg-info/dependency_links.txt +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru_code.egg-info/entry_points.txt +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru_code.egg-info/requires.txt +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/aru_code.egg-info/top_level.txt +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/setup.cfg +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_agents_base.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_agents_md_coverage.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_cache_patch_metrics.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_catalog.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_checkpoints.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_cli.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_cli_advanced.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_cli_base.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_cli_completers.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_cli_new.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_cli_run_cli.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_cli_session.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_cli_shell.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_codebase.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_confabulation_regression.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_config.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_context.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_gitignore.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_guardrails_scenarios.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_main.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_mcp_client.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_permissions.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_plan_mode_refactor.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_plugins.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_providers.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_ranker.py +0 -0
- {aru_code-0.27.0 → aru_code-0.28.0}/tests/test_select.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.28.0"
|
|
@@ -136,6 +136,11 @@ async def run_cli(skip_permissions: bool = False, resume_id: str | None = None):
|
|
|
136
136
|
|
|
137
137
|
# Load project configuration
|
|
138
138
|
config = load_config()
|
|
139
|
+
ctx.config = config
|
|
140
|
+
# Populate invoke_skill's dynamic docstring so the LLM-facing schema lists
|
|
141
|
+
# the skills actually available on this machine.
|
|
142
|
+
from aru.tools.skill import _update_invoke_skill_docstring
|
|
143
|
+
_update_invoke_skill_docstring(config.skills)
|
|
139
144
|
if config.agents_md:
|
|
140
145
|
console.print("[dim]Loaded AGENTS.md[/dim]")
|
|
141
146
|
if config.commands:
|
|
@@ -546,6 +551,12 @@ async def run_cli(skip_permissions: bool = False, resume_id: str | None = None):
|
|
|
546
551
|
console.print(f" [bold cyan]{entry.name}[/bold cyan] [dim]{entry.description}[/dim]")
|
|
547
552
|
continue
|
|
548
553
|
|
|
554
|
+
if user_input.lower() == "/plugin" or user_input.lower().startswith("/plugin "):
|
|
555
|
+
from aru.commands import handle_plugin_command
|
|
556
|
+
rest = user_input[len("/plugin"):].strip()
|
|
557
|
+
handle_plugin_command(rest)
|
|
558
|
+
continue
|
|
559
|
+
|
|
549
560
|
if user_input.lower() == "/help":
|
|
550
561
|
_show_help(config)
|
|
551
562
|
continue
|
|
@@ -746,6 +757,10 @@ async def run_oneshot(prompt: str, print_only: bool = False, skip_permissions: b
|
|
|
746
757
|
ctx = init_ctx(console=console, skip_permissions=skip_permissions)
|
|
747
758
|
|
|
748
759
|
config = load_config()
|
|
760
|
+
ctx.config = config
|
|
761
|
+
# Populate invoke_skill's dynamic docstring (same as interactive path)
|
|
762
|
+
from aru.tools.skill import _update_invoke_skill_docstring
|
|
763
|
+
_update_invoke_skill_docstring(config.skills)
|
|
749
764
|
session = Session()
|
|
750
765
|
if config.default_model:
|
|
751
766
|
session.model_ref = config.default_model
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"""Slash command definitions, help display, shell execution, and user prompts."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.panel import Panel
|
|
10
|
+
from rich.syntax import Syntax
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
|
|
13
|
+
from aru.display import console
|
|
14
|
+
|
|
15
|
+
SLASH_COMMANDS = [
|
|
16
|
+
("/help", "Show help and available commands", "/help"),
|
|
17
|
+
("/plan", "Create an implementation plan", "/plan <task>"),
|
|
18
|
+
("/model", "Switch model/provider", "/model [provider/model]"),
|
|
19
|
+
("/sessions", "List recent sessions", "/sessions"),
|
|
20
|
+
("/commands", "List custom commands", "/commands"),
|
|
21
|
+
("/skills", "List available skills", "/skills"),
|
|
22
|
+
("/agents", "List custom agents", "/agents"),
|
|
23
|
+
("/mcp", "List loaded MCP tools", "/mcp"),
|
|
24
|
+
("/plugin", "Manage cached plugins (install/list/remove/update)", "/plugin <subcommand>"),
|
|
25
|
+
("/undo", "Undo last turn — restore files and/or conversation", "/undo"),
|
|
26
|
+
("/cost", "Show detailed token usage and cost", "/cost"),
|
|
27
|
+
("/quit", "Exit aru", "/quit"),
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def run_shell(command: str):
|
|
32
|
+
"""Run a shell command directly, streaming output to the terminal."""
|
|
33
|
+
console.print()
|
|
34
|
+
console.print(Panel(
|
|
35
|
+
Syntax(command, "bash", theme="monokai"),
|
|
36
|
+
title="[bold]Shell[/bold]",
|
|
37
|
+
border_style="dim",
|
|
38
|
+
expand=False,
|
|
39
|
+
))
|
|
40
|
+
try:
|
|
41
|
+
process = subprocess.Popen(
|
|
42
|
+
command,
|
|
43
|
+
shell=True,
|
|
44
|
+
stdout=subprocess.PIPE,
|
|
45
|
+
stderr=subprocess.STDOUT,
|
|
46
|
+
text=True,
|
|
47
|
+
cwd=os.getcwd(),
|
|
48
|
+
bufsize=1,
|
|
49
|
+
)
|
|
50
|
+
for line in process.stdout:
|
|
51
|
+
console.print(Text(line.rstrip()))
|
|
52
|
+
process.wait()
|
|
53
|
+
if process.returncode != 0:
|
|
54
|
+
console.print(f"[red]Exit code: {process.returncode}[/red]")
|
|
55
|
+
except KeyboardInterrupt:
|
|
56
|
+
process.kill()
|
|
57
|
+
console.print("\n[yellow]Interrupted.[/yellow]")
|
|
58
|
+
except Exception as e:
|
|
59
|
+
from rich.markup import escape
|
|
60
|
+
console.print(f"[red]Error: {escape(str(e))}[/red]")
|
|
61
|
+
console.print()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def ask_yes_no(prompt: str) -> bool:
|
|
65
|
+
"""Ask the user a yes/no question."""
|
|
66
|
+
try:
|
|
67
|
+
answer = console.input(f"[bold yellow]{prompt} (y/n):[/bold yellow] ").strip().lower()
|
|
68
|
+
return answer in ("y", "yes", "s", "sim")
|
|
69
|
+
except (EOFError, KeyboardInterrupt):
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def handle_plugin_command(args: str) -> None:
|
|
74
|
+
"""Handle /plugin <subcommand> [args] — install/list/remove/update/info."""
|
|
75
|
+
from rich.table import Table
|
|
76
|
+
from rich.markup import escape
|
|
77
|
+
|
|
78
|
+
parts = args.strip().split(None, 2)
|
|
79
|
+
if not parts:
|
|
80
|
+
_show_plugin_help()
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
subcmd = parts[0].lower()
|
|
84
|
+
|
|
85
|
+
if subcmd == "list":
|
|
86
|
+
from aru.plugin_cache import list_installed
|
|
87
|
+
entries = list_installed()
|
|
88
|
+
if not entries:
|
|
89
|
+
console.print("[dim]No plugins installed. Use /plugin install <spec> to add one.[/dim]")
|
|
90
|
+
return
|
|
91
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
92
|
+
table.add_column("Name", style="cyan")
|
|
93
|
+
table.add_column("Version", style="green")
|
|
94
|
+
table.add_column("Source")
|
|
95
|
+
table.add_column("Spec", style="dim")
|
|
96
|
+
for e in entries:
|
|
97
|
+
table.add_row(e.id, e.version or "-", e.source, e.spec)
|
|
98
|
+
console.print(table)
|
|
99
|
+
return
|
|
100
|
+
|
|
101
|
+
if subcmd == "install":
|
|
102
|
+
if len(parts) < 2:
|
|
103
|
+
console.print("[yellow]Usage: /plugin install <spec> [name][/yellow]")
|
|
104
|
+
return
|
|
105
|
+
spec = parts[1]
|
|
106
|
+
name = parts[2] if len(parts) >= 3 else None
|
|
107
|
+
from aru.plugin_cache import install
|
|
108
|
+
console.print(f"[dim]Installing {escape(spec)}...[/dim]")
|
|
109
|
+
result = install(spec, name=name)
|
|
110
|
+
if not result.ok:
|
|
111
|
+
console.print(f"[red]Install failed: {escape(result.error or 'unknown error')}[/red]")
|
|
112
|
+
return
|
|
113
|
+
provides = result.provides
|
|
114
|
+
provides_str = ", ".join(f"{c} {k}" for k, c in provides.items()) or "no resources"
|
|
115
|
+
console.print(
|
|
116
|
+
f"[green]Installed {escape(result.name or '')}"
|
|
117
|
+
f"{f'@{result.version}' if result.version else ''}[/green] "
|
|
118
|
+
f"([dim]{result.state}[/dim]) -> {escape(str(result.target))}"
|
|
119
|
+
)
|
|
120
|
+
console.print(f"[dim]Provides: {provides_str}[/dim]")
|
|
121
|
+
console.print(
|
|
122
|
+
"[dim]Discovery refreshes on next aru restart. "
|
|
123
|
+
"Skills/agents/tools from the plugin will then be available.[/dim]"
|
|
124
|
+
)
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
if subcmd == "remove":
|
|
128
|
+
if len(parts) < 2:
|
|
129
|
+
console.print("[yellow]Usage: /plugin remove <name>[/yellow]")
|
|
130
|
+
return
|
|
131
|
+
name = parts[1]
|
|
132
|
+
from aru.plugin_cache import remove
|
|
133
|
+
if remove(name):
|
|
134
|
+
console.print(f"[green]Removed plugin: {escape(name)}[/green]")
|
|
135
|
+
else:
|
|
136
|
+
console.print(f"[yellow]Plugin not found: {escape(name)}[/yellow]")
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
if subcmd == "update":
|
|
140
|
+
if len(parts) < 2:
|
|
141
|
+
console.print("[yellow]Usage: /plugin update <name>[/yellow]")
|
|
142
|
+
return
|
|
143
|
+
name = parts[1]
|
|
144
|
+
from aru.plugin_cache import update
|
|
145
|
+
console.print(f"[dim]Updating {escape(name)}...[/dim]")
|
|
146
|
+
result = update(name)
|
|
147
|
+
if not result.ok:
|
|
148
|
+
console.print(f"[red]Update failed: {escape(result.error or 'unknown error')}[/red]")
|
|
149
|
+
return
|
|
150
|
+
console.print(
|
|
151
|
+
f"[green]Updated {escape(result.name or '')}"
|
|
152
|
+
f"{f'@{result.version}' if result.version else ''}[/green] "
|
|
153
|
+
f"([dim]{result.state}[/dim])"
|
|
154
|
+
)
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
if subcmd == "info":
|
|
158
|
+
if len(parts) < 2:
|
|
159
|
+
console.print("[yellow]Usage: /plugin info <name>[/yellow]")
|
|
160
|
+
return
|
|
161
|
+
name = parts[1]
|
|
162
|
+
from aru.plugin_cache import list_installed, read_manifest
|
|
163
|
+
from pathlib import Path
|
|
164
|
+
entries = {e.id: e for e in list_installed()}
|
|
165
|
+
entry = entries.get(name)
|
|
166
|
+
if entry is None:
|
|
167
|
+
console.print(f"[yellow]Plugin not found: {escape(name)}[/yellow]")
|
|
168
|
+
return
|
|
169
|
+
manifest = read_manifest(Path(entry.target))
|
|
170
|
+
console.print(f"[bold cyan]{escape(entry.id)}[/bold cyan]")
|
|
171
|
+
console.print(f" [dim]version:[/dim] {entry.version or '-'}")
|
|
172
|
+
console.print(f" [dim]source:[/dim] {entry.source}")
|
|
173
|
+
console.print(f" [dim]spec:[/dim] {escape(entry.spec)}")
|
|
174
|
+
console.print(f" [dim]target:[/dim] {escape(entry.target)}")
|
|
175
|
+
console.print(f" [dim]fingerprint:[/dim] {entry.fingerprint}")
|
|
176
|
+
console.print(f" [dim]first_time:[/dim] {entry.first_time}")
|
|
177
|
+
console.print(f" [dim]last_time:[/dim] {entry.last_time}")
|
|
178
|
+
if manifest:
|
|
179
|
+
desc = manifest.get("description")
|
|
180
|
+
if desc:
|
|
181
|
+
console.print(f" [dim]description:[/dim] {escape(str(desc))}")
|
|
182
|
+
engines = manifest.get("engines") or {}
|
|
183
|
+
if isinstance(engines, dict) and engines.get("aru"):
|
|
184
|
+
console.print(f" [dim]engines.aru:[/dim] {escape(str(engines['aru']))}")
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
_show_plugin_help()
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _show_plugin_help() -> None:
|
|
191
|
+
"""Print /plugin command usage."""
|
|
192
|
+
from rich.table import Table
|
|
193
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
194
|
+
table.add_column("Subcommand", style="cyan")
|
|
195
|
+
table.add_column("Description", style="dim")
|
|
196
|
+
table.add_row("/plugin install <spec> [name]", "Install a plugin from git or local path")
|
|
197
|
+
table.add_row("/plugin list", "List installed plugins")
|
|
198
|
+
table.add_row("/plugin remove <name>", "Uninstall a plugin")
|
|
199
|
+
table.add_row("/plugin update <name>", "Update a plugin (git pull)")
|
|
200
|
+
table.add_row("/plugin info <name>", "Show plugin metadata")
|
|
201
|
+
console.print(table)
|
|
202
|
+
console.print()
|
|
203
|
+
console.print("[dim]Spec formats:[/dim]")
|
|
204
|
+
console.print("[dim] github:user/repo — shorthand for GitHub[/dim]")
|
|
205
|
+
console.print("[dim] github:user/repo@v1.0.0 — pin to tag/branch[/dim]")
|
|
206
|
+
console.print("[dim] git+https://host/path.git — any git URL[/dim]")
|
|
207
|
+
console.print("[dim] file:///abs/path or ./rel — local directory[/dim]")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _show_help(config) -> None:
|
|
211
|
+
"""Display help with available commands."""
|
|
212
|
+
from rich.table import Table
|
|
213
|
+
|
|
214
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
215
|
+
table.add_column("Command", style="cyan")
|
|
216
|
+
table.add_column("Description", style="dim")
|
|
217
|
+
|
|
218
|
+
table.add_row("/plan <task>", "Create detailed implementation plan")
|
|
219
|
+
table.add_row("/model [provider/model]", "Switch models (e.g., ollama/llama3.1, openai/gpt-4o)")
|
|
220
|
+
table.add_row("/sessions", "List recent sessions")
|
|
221
|
+
table.add_row("/commands", "List custom commands")
|
|
222
|
+
table.add_row("/skills", "List available skills")
|
|
223
|
+
table.add_row("/agents", "List custom agents")
|
|
224
|
+
table.add_row("/mcp", "List loaded MCP tools")
|
|
225
|
+
table.add_row("/plugin <subcmd>", "Manage plugins (install/list/remove/update/info)")
|
|
226
|
+
table.add_row("/undo", "Undo last turn (restore files and/or conversation)")
|
|
227
|
+
table.add_row("/help", "Show this help")
|
|
228
|
+
table.add_row("/quit", "Exit aru")
|
|
229
|
+
table.add_row("! <cmd>", "Run shell command")
|
|
230
|
+
|
|
231
|
+
if config and config.commands:
|
|
232
|
+
table.add_row("", "")
|
|
233
|
+
for name, cmd_def in config.commands.items():
|
|
234
|
+
table.add_row(f"/{name}", cmd_def.description)
|
|
235
|
+
|
|
236
|
+
if config and config.custom_agents:
|
|
237
|
+
primary = {k: v for k, v in config.custom_agents.items() if v.mode == "primary"}
|
|
238
|
+
if primary:
|
|
239
|
+
table.add_row("", "")
|
|
240
|
+
for name, agent_def in primary.items():
|
|
241
|
+
table.add_row(f"/{name}", f"[agent] {agent_def.description}")
|
|
242
|
+
|
|
243
|
+
console.print(table)
|
|
244
|
+
console.print()
|
|
@@ -535,9 +535,15 @@ def load_config(cwd: str | None = None) -> AgentConfig:
|
|
|
535
535
|
config.commands = _load_commands(agents_dir)
|
|
536
536
|
|
|
537
537
|
# Discover skills from multiple roots (agentskills.io convention)
|
|
538
|
-
# Order:
|
|
538
|
+
# Order: cached plugins first (lowest priority), then global, then project-local
|
|
539
|
+
# (local overrides global overrides cache — lets users shadow plugin skills).
|
|
539
540
|
home = Path.home()
|
|
540
541
|
skill_roots: list[Path] = []
|
|
542
|
+
try:
|
|
543
|
+
from aru.plugin_cache import get_cached_plugin_roots
|
|
544
|
+
skill_roots.extend(get_cached_plugin_roots())
|
|
545
|
+
except Exception as exc: # defensive: never fail config load over cache
|
|
546
|
+
logger.warning("Failed to load cached plugin roots: %s", exc)
|
|
541
547
|
for dirname in (".agents", ".claude"):
|
|
542
548
|
global_dir = home / dirname
|
|
543
549
|
if global_dir.is_dir():
|