@phi-code-admin/phi-code 0.56.5 → 0.57.0
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.
- package/extensions/phi/README.md +34 -214
- package/extensions/phi/agents.ts +202 -0
- package/extensions/phi/benchmark.ts +611 -398
- package/extensions/phi/init.ts +447 -312
- package/extensions/phi/memory.ts +36 -18
- package/package.json +2 -2
package/extensions/phi/README.md
CHANGED
|
@@ -1,228 +1,48 @@
|
|
|
1
1
|
# Phi Code Extensions
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
9 TypeScript extensions automatically loaded at startup.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Extensions
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
| Extension | File | Tools | Commands | Events |
|
|
8
|
+
|-----------|------|-------|----------|--------|
|
|
9
|
+
| **Memory** | `memory.ts` | `memory_search`, `memory_write`, `memory_read`, `memory_status` | — | `session_start` (auto-load AGENTS.md) |
|
|
10
|
+
| **Benchmark** | `benchmark.ts` | — | `/benchmark` | `session_start` (results count) |
|
|
11
|
+
| **Smart Router** | `smart-router.ts` | — | `/routing` | `input` (model suggestion), `session_start` |
|
|
12
|
+
| **Orchestrator** | `orchestrator.ts` | `orchestrate` | `/plan`, `/plans` | — |
|
|
13
|
+
| **Skill Loader** | `skill-loader.ts` | — | `/skills` | `input` (skill matching), `session_start` |
|
|
14
|
+
| **Web Search** | `web-search.ts` | `web_search` | `/search` | `session_start` (key detection) |
|
|
15
|
+
| **Agents** | `agents.ts` | — | `/agents` | `session_start` (agent count) |
|
|
16
|
+
| **Init** | `init.ts` | — | `/phi-init` | — |
|
|
8
17
|
|
|
9
|
-
|
|
18
|
+
## Benchmark Categories
|
|
10
19
|
|
|
11
|
-
|
|
12
|
-
- `memory_search(query)` - Full-text search across memory files
|
|
13
|
-
- `memory_write(content, file?)` - Write to memory files (defaults to today's date)
|
|
14
|
-
- `memory_read(file?)` - Read specific files or list all available
|
|
15
|
-
- Auto-loads `AGENTS.md` on session start
|
|
16
|
-
- Creates memory directories automatically (`~/.phi/memory/`, `.phi/memory/`)
|
|
20
|
+
The `/benchmark` command tests models across 6 weighted categories:
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
| Category | Weight | Test |
|
|
23
|
+
|----------|--------|------|
|
|
24
|
+
| Code Generation | ×2 | Write a TypeScript function from spec |
|
|
25
|
+
| Debugging | ×2 | Find and fix a mutation bug |
|
|
26
|
+
| Planning | ×2 | Create JWT auth implementation plan |
|
|
27
|
+
| Tool Calling | ×1 | Parse natural language to structured JSON |
|
|
28
|
+
| Speed | ×1 | Response latency (simple instruction following) |
|
|
29
|
+
| Orchestration | ×2 | Multi-step memory leak analysis |
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
Scoring: S (80+), A (65+), B (50+), C (35+), D (<35)
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
## Memory Auto-Recall
|
|
29
34
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- `/routing` command for configuration management
|
|
35
|
+
The memory extension adds prompt guidelines that instruct the model to:
|
|
36
|
+
1. Search memory before answering questions about prior work or decisions
|
|
37
|
+
2. Search memory when starting work on a topic
|
|
38
|
+
3. Write to memory after completing important work
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
- **Code tasks** (implement, create, refactor) → Coder model
|
|
38
|
-
- **Debug tasks** (fix, bug, error) → Reasoning model
|
|
39
|
-
- **Exploration** (read, analyze, explain) → Fast model
|
|
40
|
-
- **Planning** (plan, design, architect) → Reasoning model
|
|
40
|
+
This is not forced via code — it's a prompt guideline that well-trained models follow naturally.
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
- `/routing` - Show current configuration
|
|
44
|
-
- `/routing enable|disable` - Toggle smart routing
|
|
45
|
-
- `/routing notify-on|notify-off` - Toggle notifications
|
|
46
|
-
- `/routing test` - Test routing on sample inputs
|
|
42
|
+
## Setup Wizard Modes
|
|
47
43
|
|
|
48
|
-
|
|
44
|
+
`/phi-init` offers 3 configuration modes:
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
**
|
|
53
|
-
- `/plan <description>` - Create structured project plans
|
|
54
|
-
- `orchestrate(description)` - LLM-callable planning tool
|
|
55
|
-
- Generates spec files and TODO lists with timestamps
|
|
56
|
-
- Automatic task breakdown and status tracking
|
|
57
|
-
|
|
58
|
-
**File Output:**
|
|
59
|
-
- `.phi/plans/spec-TIMESTAMP.md` - Project specifications
|
|
60
|
-
- `.phi/plans/todo-TIMESTAMP.md` - Task lists with checkboxes
|
|
61
|
-
|
|
62
|
-
**Commands:**
|
|
63
|
-
- `/plan <description>` - Create a new project plan
|
|
64
|
-
- `/plans` - List existing project plans
|
|
65
|
-
|
|
66
|
-
### 4. 🧩 Skill Loader Extension (`skill-loader.ts`)
|
|
67
|
-
|
|
68
|
-
Dynamic loading and injection of specialized skills.
|
|
69
|
-
|
|
70
|
-
**Features:**
|
|
71
|
-
- Scans `~/.phi/agent/skills/` and `.phi/skills/` for skills
|
|
72
|
-
- Keyword-based skill detection and auto-loading
|
|
73
|
-
- `/skills` command to browse available skills
|
|
74
|
-
- Automatic context injection when skills are relevant
|
|
75
|
-
|
|
76
|
-
**Skill Structure:**
|
|
77
|
-
```
|
|
78
|
-
skill-name/
|
|
79
|
-
├── SKILL.md # Main skill content
|
|
80
|
-
└── (other files) # Optional supporting files
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
**Commands:**
|
|
84
|
-
- `/skills` - List all available skills
|
|
85
|
-
- `/skills <name>` - View specific skill details
|
|
86
|
-
|
|
87
|
-
### 5. 🌐 Web Search Extension (`web-search.ts`)
|
|
88
|
-
|
|
89
|
-
Internet search capabilities with multiple providers.
|
|
90
|
-
|
|
91
|
-
**Features:**
|
|
92
|
-
- `web_search(query, count?)` - LLM-accessible web search
|
|
93
|
-
- Brave Search API integration (with `BRAVE_API_KEY`)
|
|
94
|
-
- DuckDuckGo HTML fallback when API unavailable
|
|
95
|
-
- `/search` command for quick searches
|
|
96
|
-
|
|
97
|
-
**Setup:**
|
|
98
|
-
```bash
|
|
99
|
-
# Optional: Set Brave Search API key for better results
|
|
100
|
-
export BRAVE_API_KEY="your-api-key"
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
**Commands:**
|
|
104
|
-
- `/search <query>` - Quick web search from chat
|
|
105
|
-
|
|
106
|
-
### 6. 🏆 Benchmark Extension (`benchmark.ts`)
|
|
107
|
-
|
|
108
|
-
Integrated AI model performance testing and comparison.
|
|
109
|
-
|
|
110
|
-
**Features:**
|
|
111
|
-
- `/benchmark` command for interactive testing
|
|
112
|
-
- Fibonacci code generation test (more categories planned)
|
|
113
|
-
- Performance metrics: time, quality, token usage
|
|
114
|
-
- Results persistence in `~/.phi/benchmark/results.json`
|
|
115
|
-
- Model ranking and comparison
|
|
116
|
-
|
|
117
|
-
**Test Categories (V1):**
|
|
118
|
-
- **Fibonacci** - Iterative function implementation with test cases
|
|
119
|
-
|
|
120
|
-
**Commands:**
|
|
121
|
-
- `/benchmark` - Show available options
|
|
122
|
-
- `/benchmark <model>` - Test specific model
|
|
123
|
-
- `/benchmark results` - View benchmark report
|
|
124
|
-
- `/benchmark clear` - Clear all results
|
|
125
|
-
|
|
126
|
-
## Installation
|
|
127
|
-
|
|
128
|
-
1. Copy the desired extensions to your extensions directory:
|
|
129
|
-
```bash
|
|
130
|
-
# Global installation
|
|
131
|
-
cp -r packages/coding-agent/extensions/phi ~/.pi/agent/extensions/
|
|
132
|
-
|
|
133
|
-
# Project-specific installation
|
|
134
|
-
cp -r packages/coding-agent/extensions/phi .pi/extensions/
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
2. Extensions will automatically load on next Pi session start.
|
|
138
|
-
|
|
139
|
-
## Configuration
|
|
140
|
-
|
|
141
|
-
### Memory Extension
|
|
142
|
-
- Memory files stored in `~/.phi/memory/` (global) and `.phi/memory/` (local)
|
|
143
|
-
- No configuration required - creates directories automatically
|
|
144
|
-
|
|
145
|
-
### Smart Router Extension
|
|
146
|
-
- Configuration: `~/.phi/agent/routing.json`
|
|
147
|
-
- Auto-creates default config on first run
|
|
148
|
-
- Modify patterns and model assignments as needed
|
|
149
|
-
|
|
150
|
-
### Skill Loader Extension
|
|
151
|
-
- Skills directory: `~/.phi/agent/skills/` (global) and `.phi/skills/` (local)
|
|
152
|
-
- Each skill is a folder containing `SKILL.md`
|
|
153
|
-
|
|
154
|
-
### Web Search Extension
|
|
155
|
-
- Optional: Set `BRAVE_API_KEY` environment variable
|
|
156
|
-
- Falls back to DuckDuckGo if no API key provided
|
|
157
|
-
|
|
158
|
-
### Benchmark Extension
|
|
159
|
-
- Results saved to `~/.phi/benchmark/results.json`
|
|
160
|
-
- No configuration required
|
|
161
|
-
|
|
162
|
-
## Development
|
|
163
|
-
|
|
164
|
-
Each extension follows these conventions:
|
|
165
|
-
|
|
166
|
-
```typescript
|
|
167
|
-
import { Type } from "@sinclair/typebox";
|
|
168
|
-
import type { ExtensionAPI, ExtensionContext } from "phi-code";
|
|
169
|
-
|
|
170
|
-
export default function extensionName(pi: ExtensionAPI) {
|
|
171
|
-
// Register tools
|
|
172
|
-
pi.registerTool({
|
|
173
|
-
name: "tool_name",
|
|
174
|
-
description: "Tool description",
|
|
175
|
-
parameters: Type.Object({
|
|
176
|
-
param: Type.String({ description: "Parameter description" })
|
|
177
|
-
}),
|
|
178
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
179
|
-
// Implementation
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
// Register commands
|
|
184
|
-
pi.registerCommand("command", {
|
|
185
|
-
description: "Command description",
|
|
186
|
-
handler: async (args, ctx) => {
|
|
187
|
-
// Implementation
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
// Event listeners
|
|
192
|
-
pi.on("session_start", async (event, ctx) => {
|
|
193
|
-
// Session initialization
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
### Guidelines
|
|
199
|
-
- Use `@sinclair/typebox` for parameter schemas
|
|
200
|
-
- Prefer Node.js built-in modules over external dependencies
|
|
201
|
-
- Include JSDoc comments for all functions
|
|
202
|
-
- Handle errors gracefully with user-friendly messages
|
|
203
|
-
- Use `ctx.ui.notify()` for user feedback
|
|
204
|
-
|
|
205
|
-
## Future Extensions
|
|
206
|
-
|
|
207
|
-
Planned extensions for future releases:
|
|
208
|
-
|
|
209
|
-
- **Git Integration** - Advanced Git operations and workflow automation
|
|
210
|
-
- **Code Review** - Automated code review and quality checking
|
|
211
|
-
- **Documentation** - Auto-generation of docs from code
|
|
212
|
-
- **Testing** - Test generation and coverage analysis
|
|
213
|
-
- **Deployment** - CI/CD pipeline integration
|
|
214
|
-
- **Monitoring** - Real-time system monitoring and alerting
|
|
215
|
-
|
|
216
|
-
## Contributing
|
|
217
|
-
|
|
218
|
-
To add new extensions:
|
|
219
|
-
|
|
220
|
-
1. Create a new `.ts` file in this directory
|
|
221
|
-
2. Follow the existing pattern and conventions
|
|
222
|
-
3. Add comprehensive JSDoc documentation
|
|
223
|
-
4. Update this README with the new extension details
|
|
224
|
-
5. Test thoroughly before submitting
|
|
225
|
-
|
|
226
|
-
## License
|
|
227
|
-
|
|
228
|
-
Same license as the main Phi Code project.
|
|
46
|
+
- **auto**: Assigns models based on public rankings and specializations (instant)
|
|
47
|
+
- **benchmark**: Tests available models with `/benchmark all`, then assigns best-per-category
|
|
48
|
+
- **manual**: Interactive prompts to choose each model assignment
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agents Extension - Sub-agent management and visibility
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - /agents command to list all configured sub-agents
|
|
6
|
+
* - /agents <name> to show detailed agent info
|
|
7
|
+
* - Agent definitions loaded from ~/.phi/agent/agents/ and .phi/agents/
|
|
8
|
+
* - Model assignment visibility
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ExtensionAPI } from "phi-code";
|
|
12
|
+
import { readFileSync, readdirSync, existsSync, statSync } from "node:fs";
|
|
13
|
+
import { join, basename } from "node:path";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
|
|
16
|
+
interface AgentDefinition {
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
tools: string[];
|
|
20
|
+
model: string;
|
|
21
|
+
source: string; // "global", "project", "bundled"
|
|
22
|
+
filePath: string;
|
|
23
|
+
systemPrompt: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Parse YAML frontmatter from agent .md file
|
|
28
|
+
*/
|
|
29
|
+
function parseAgentFile(filePath: string): AgentDefinition | null {
|
|
30
|
+
try {
|
|
31
|
+
const content = readFileSync(filePath, "utf-8");
|
|
32
|
+
const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
|
|
33
|
+
|
|
34
|
+
if (!fmMatch) return null;
|
|
35
|
+
|
|
36
|
+
const frontmatter = fmMatch[1];
|
|
37
|
+
const body = fmMatch[2].trim();
|
|
38
|
+
|
|
39
|
+
// Simple YAML parser for our frontmatter
|
|
40
|
+
const fields: Record<string, string> = {};
|
|
41
|
+
for (const line of frontmatter.split("\n")) {
|
|
42
|
+
const match = line.match(/^(\w+):\s*(.*)$/);
|
|
43
|
+
if (match) {
|
|
44
|
+
fields[match[1]] = match[2].trim();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!fields.name) return null;
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
name: fields.name,
|
|
52
|
+
description: fields.description || "No description",
|
|
53
|
+
tools: (fields.tools || "").split(",").map(t => t.trim()).filter(Boolean),
|
|
54
|
+
model: fields.model || "default",
|
|
55
|
+
source: "unknown",
|
|
56
|
+
filePath,
|
|
57
|
+
systemPrompt: body,
|
|
58
|
+
};
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Scan a directory for agent .md files
|
|
66
|
+
*/
|
|
67
|
+
function scanAgentDir(dir: string, source: string): AgentDefinition[] {
|
|
68
|
+
const agents: AgentDefinition[] = [];
|
|
69
|
+
|
|
70
|
+
if (!existsSync(dir)) return agents;
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const entries = readdirSync(dir);
|
|
74
|
+
for (const entry of entries) {
|
|
75
|
+
if (!entry.endsWith(".md")) continue;
|
|
76
|
+
const fullPath = join(dir, entry);
|
|
77
|
+
if (!statSync(fullPath).isFile()) continue;
|
|
78
|
+
|
|
79
|
+
const agent = parseAgentFile(fullPath);
|
|
80
|
+
if (agent) {
|
|
81
|
+
agent.source = source;
|
|
82
|
+
agents.push(agent);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
// Directory not readable
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return agents;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export default function agentsExtension(pi: ExtensionAPI) {
|
|
93
|
+
/**
|
|
94
|
+
* Discover all agent definitions from all sources
|
|
95
|
+
*/
|
|
96
|
+
function discoverAgents(): AgentDefinition[] {
|
|
97
|
+
const seen = new Set<string>();
|
|
98
|
+
const allAgents: AgentDefinition[] = [];
|
|
99
|
+
|
|
100
|
+
const addAgents = (agents: AgentDefinition[]) => {
|
|
101
|
+
for (const agent of agents) {
|
|
102
|
+
if (!seen.has(agent.name)) {
|
|
103
|
+
seen.add(agent.name);
|
|
104
|
+
allAgents.push(agent);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// 1. Project-local agents
|
|
110
|
+
addAgents(scanAgentDir(join(process.cwd(), ".phi", "agents"), "project"));
|
|
111
|
+
|
|
112
|
+
// 2. Global agents
|
|
113
|
+
addAgents(scanAgentDir(join(homedir(), ".phi", "agent", "agents"), "global"));
|
|
114
|
+
|
|
115
|
+
// 3. Bundled agents (shipped with package)
|
|
116
|
+
const bundledDir = join(__dirname, "..", "..", "..", "agents");
|
|
117
|
+
if (existsSync(bundledDir)) {
|
|
118
|
+
addAgents(scanAgentDir(bundledDir, "bundled"));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return allAgents;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* /agents command
|
|
126
|
+
*/
|
|
127
|
+
pi.registerCommand("agents", {
|
|
128
|
+
description: "List and inspect sub-agent definitions",
|
|
129
|
+
handler: async (args, ctx) => {
|
|
130
|
+
const agents = discoverAgents();
|
|
131
|
+
const arg = args.trim().toLowerCase();
|
|
132
|
+
|
|
133
|
+
if (agents.length === 0) {
|
|
134
|
+
ctx.ui.notify("No agent definitions found.\n\nCreate agent files in:\n- `.phi/agents/` (project)\n- `~/.phi/agent/agents/` (global)\n\nFormat: Markdown with YAML frontmatter (name, description, tools, model).", "info");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Show specific agent details
|
|
139
|
+
if (arg && arg !== "list") {
|
|
140
|
+
const agent = agents.find(a => a.name.toLowerCase() === arg);
|
|
141
|
+
if (!agent) {
|
|
142
|
+
ctx.ui.notify(`Agent "${arg}" not found. Available: ${agents.map(a => a.name).join(", ")}`, "warning");
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const detail = `**Agent: ${agent.name}**
|
|
147
|
+
|
|
148
|
+
📝 ${agent.description}
|
|
149
|
+
🤖 Model: \`${agent.model}\`
|
|
150
|
+
🔧 Tools: ${agent.tools.map(t => `\`${t}\``).join(", ")}
|
|
151
|
+
📁 Source: ${agent.source} (\`${agent.filePath}\`)
|
|
152
|
+
|
|
153
|
+
**System Prompt:**
|
|
154
|
+
\`\`\`
|
|
155
|
+
${agent.systemPrompt.substring(0, 800)}${agent.systemPrompt.length > 800 ? "\n..." : ""}
|
|
156
|
+
\`\`\``;
|
|
157
|
+
|
|
158
|
+
ctx.ui.notify(detail, "info");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// List all agents
|
|
163
|
+
let output = `**🤖 Sub-Agents (${agents.length})**\n\n`;
|
|
164
|
+
|
|
165
|
+
// Group by source
|
|
166
|
+
const bySource: Record<string, AgentDefinition[]> = {};
|
|
167
|
+
for (const agent of agents) {
|
|
168
|
+
const key = agent.source;
|
|
169
|
+
if (!bySource[key]) bySource[key] = [];
|
|
170
|
+
bySource[key].push(agent);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const sourceLabels: Record<string, string> = {
|
|
174
|
+
project: "📁 Project (.phi/agents/)",
|
|
175
|
+
global: "🏠 Global (~/.phi/agent/agents/)",
|
|
176
|
+
bundled: "📦 Bundled (shipped with Phi Code)",
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
for (const [source, sourceAgents] of Object.entries(bySource)) {
|
|
180
|
+
output += `**${sourceLabels[source] || source}**\n`;
|
|
181
|
+
for (const agent of sourceAgents) {
|
|
182
|
+
output += ` • **${agent.name}** → \`${agent.model}\`\n`;
|
|
183
|
+
output += ` ${agent.description}\n`;
|
|
184
|
+
output += ` Tools: ${agent.tools.join(", ")}\n`;
|
|
185
|
+
}
|
|
186
|
+
output += "\n";
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
output += `Use \`/agents <name>\` for detailed info on a specific agent.`;
|
|
190
|
+
|
|
191
|
+
ctx.ui.notify(output, "info");
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Session start: show agent count
|
|
196
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
197
|
+
const agents = discoverAgents();
|
|
198
|
+
if (agents.length > 0) {
|
|
199
|
+
ctx.ui.notify(`🤖 ${agents.length} sub-agents available. /agents to list.`, "info");
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|