pi-subagents-lite 0.2.0 → 0.3.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/README.md +222 -36
- package/package.json +3 -1
- package/src/agent-discovery.ts +36 -45
- package/src/agent-manager.ts +101 -87
- package/src/agent-runner.ts +40 -49
- package/src/agent-types.ts +15 -37
- package/src/config-io.ts +40 -0
- package/src/context.ts +80 -1
- package/src/index.ts +105 -1117
- package/src/menus.ts +866 -0
- package/src/model-precedence.ts +46 -36
- package/src/model-selector.ts +19 -19
- package/src/output-file.ts +123 -33
- package/src/prompts.ts +2 -2
- package/src/result-viewer.ts +166 -37
- package/src/skill-loader.ts +1 -1
- package/src/stop-agent-tool.ts +76 -0
- package/src/tool-execution.ts +361 -0
- package/src/types.ts +16 -1
- package/src/ui/agent-widget.ts +98 -91
- package/src/usage.ts +12 -4
- package/src/utils.ts +53 -4
package/README.md
CHANGED
|
@@ -3,74 +3,260 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/pi-subagents-lite)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
**Sub-agents for [pi](https://pi.dev) — schema-first, zero-fluff.**
|
|
7
|
+
|
|
8
|
+
Spawn specialized agents with isolated sessions, custom tools, and per-type models — all at minimal token cost.
|
|
9
|
+
|
|
10
|
+
## Schema-First Design
|
|
11
|
+
|
|
12
|
+
Every tool the LLM sees costs tokens — in the system prompt, and in every turn's context. Most extensions add description text, prompt snippets, and usage guidelines that compound across the session. This extension takes a **schema-first** approach: the tool name and parameter names **are** the schema. No bloated descriptions, no prose.
|
|
13
|
+
|
|
14
|
+
| Standard | Schema-first |
|
|
15
|
+
|---|---|
|
|
16
|
+
| `description: "Spawn a sub-agent"` | `description: "."` |
|
|
17
|
+
| `promptSnippet` with usage examples | _(none)_ |
|
|
18
|
+
| `promptGuidelines` with rules | _(none)_ |
|
|
19
|
+
| Parameters with `.description()` | Bare `Type.String()` |
|
|
20
|
+
|
|
21
|
+
Tool names like `Agent` and `StopAgent`, and parameter names like `prompt`, `description`, `run_in_background` are self-documenting. The LLM infers usage from the schema — no verbose descriptions needed. Tool results reinforce correct usage with clear success/error messages.
|
|
22
|
+
|
|
23
|
+
**Result:** foreground and background agents, custom agent types, per-model concurrency, cost tracking, steering, resume, model overrides — all with minimal token overhead.
|
|
7
24
|
|
|
8
25
|
## Features
|
|
9
26
|
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
27
|
+
- **Two tools** — `Agent` (spawn) and `StopAgent` (kill)
|
|
28
|
+
- **Foreground & background** — block or fire-and-forget with auto-delivered results
|
|
29
|
+
- **Custom agent types** — define via `.md` files with YAML frontmatter (tools, model, thinking, turn limits)
|
|
30
|
+
- **Smart model resolution** — 6-level precedence: session → config → frontmatter → parent. Set once, forget
|
|
31
|
+
- **Concurrency control** — per-model and per-provider slot limits with automatic queuing
|
|
32
|
+
- **Cost tracking** — input/output/cache tokens and dollar cost per agent
|
|
33
|
+
- **Live widget** — persistent status bar above the editor showing running/completed agents
|
|
34
|
+
- **Result viewer** — fullscreen markdown viewer with stats
|
|
35
|
+
- **Steer & resume** — inject mid-execution guidance or continue a previous conversation
|
|
36
|
+
- **Output logs** — human-readable, `tail -f` friendly
|
|
17
37
|
|
|
18
38
|
## Install
|
|
19
39
|
|
|
20
40
|
```bash
|
|
21
|
-
# Global
|
|
22
41
|
pi install npm:pi-subagents-lite
|
|
42
|
+
pi install -l npm:pi-subagents-lite # project-local
|
|
43
|
+
pi -e npm:pi-subagents-lite # try without installing
|
|
44
|
+
```
|
|
23
45
|
|
|
24
|
-
|
|
25
|
-
pi install -l npm:pi-subagents-lite
|
|
46
|
+
## Quick Start
|
|
26
47
|
|
|
27
|
-
|
|
28
|
-
pi -e npm:pi-subagents-lite
|
|
48
|
+
The LLM calls the `Agent` tool like any other tool. A foreground agent returns its result inline with stats; a background agent acknowledges immediately and auto-delivers the result when done.
|
|
29
49
|
|
|
30
|
-
# From git
|
|
31
|
-
pi install git:github.com/AlexParamonov/pi-subagents-lite
|
|
32
50
|
```
|
|
51
|
+
⠹ Working...
|
|
33
52
|
|
|
34
|
-
|
|
53
|
+
● Agents
|
|
54
|
+
├─ ⠙ Agent Write model precedence unit tests 6🛠 ·3⟳ ·8.1k(6%)·12s
|
|
55
|
+
│ │ tail -f /tmp/pi-agent-outputs/bb3382a9-1f7e-474.log
|
|
56
|
+
│ └ The file already exists but is ~175 lines. The user wants a …
|
|
57
|
+
├─ ⠙ Agent Code review of agent-runner.ts 4🛠 ·2⟳ ·8.7k(4%)·12s
|
|
58
|
+
│ │ tail -f /tmp/pi-agent-outputs/23689696-3cd3-400.log
|
|
59
|
+
│ └ Now let me check the types and related files for context on …
|
|
60
|
+
└─ ⠙ Explore Explore codebase architecture 13🛠 ·4⟳ ·19.0k(15%)·12s
|
|
61
|
+
│ tail -f /tmp/pi-agent-outputs/4f6b0f08-7a9a-419.log
|
|
62
|
+
└ ## Architecture Summary: pi-subagents-lite
|
|
63
|
+
```
|
|
64
|
+
Then you are notified like this for async (background) invocation:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
Subagent Result
|
|
68
|
+
|
|
69
|
+
✓ Explore (model-name)·13🛠 ·5⟳ ·30.8k(15%)·21s
|
|
70
|
+
Explore codebase architecture
|
|
71
|
+
tail -f /tmp/pi-agent-outputs/4f6b0f08-7a9a-419.log
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
or inline:
|
|
35
75
|
|
|
36
|
-
```ts
|
|
37
|
-
// Spawn a foreground agent
|
|
38
|
-
Agent({
|
|
39
|
-
agent: "Explore",
|
|
40
|
-
prompt: "Find all files that handle authentication",
|
|
41
|
-
description: "Find auth files",
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
// Spawn a background agent (result auto-delivered)
|
|
45
|
-
Agent({
|
|
46
|
-
agent: "Explore",
|
|
47
|
-
prompt: "Find all files that handle authentication",
|
|
48
|
-
description: "Find auth files",
|
|
49
|
-
run_in_background: true,
|
|
50
|
-
})
|
|
51
76
|
```
|
|
77
|
+
▸ Explore
|
|
78
|
+
✓ 31🛠 ·6⟳ ·57.3k(28%)·39s
|
|
79
|
+
Explore project directory structure
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Stop a running agent at any time via /agents command
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
○ Agents
|
|
86
|
+
└─ ■ Agent Code review of agent-runner.ts 12🛠 ·10⟳ ·39.0k(8%)·52s stopped
|
|
87
|
+
tail -f /tmp/pi-agent-outputs/23689696-3cd3-400.log
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Agent Tool Parameters
|
|
91
|
+
|
|
92
|
+
| Parameter | Required | Description |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| `prompt` | ✅ | The task for the sub-agent |
|
|
95
|
+
| `description` | ✅ | Brief description for the LLM caller |
|
|
96
|
+
| `agent` | | Type name — `general-purpose`, `Explore`, or any custom type you define (see [Custom Agent Types](#custom-agent-types)). The available values are **auto-populated** from `.md` files in your agent directories — drop a file, it appears in the enum. Set `enabled: false` in frontmatter to remove a type from this list. |
|
|
97
|
+
| `run_in_background` | | Fire-and-forget; result delivered automatically when done |
|
|
98
|
+
| `resume` | | Agent ID to continue a previous conversation |
|
|
99
|
+
|
|
100
|
+
> `model`, `max_turns`, `isolated`, and `thinking` are **not visible to the LLM** through tool introspection — the extension injects them at call time from agent config and frontmatter. `model` is resolved via the [Model Resolution](#model-resolution) chain; `max_turns`/`isolated`/`thinking` come from the agent's config. See [Custom Agent Types](#custom-agent-types) to set them.
|
|
52
101
|
|
|
53
102
|
## Custom Agent Types
|
|
54
103
|
|
|
55
|
-
|
|
104
|
+
Drop a `.md` file into `.pi/agents/` (project) or `~/.pi/agent/agents/` (global). The frontmatter configures the agent; the body is its system prompt.
|
|
105
|
+
|
|
106
|
+
The file's `name` frontmatter field (or the filename without extension) becomes the agent type name and **automatically populates the `agent` parameter's enum** in the tool schema. No registration step needed — the extension scans these directories at session start and makes every discovered agent available to the LLM.
|
|
107
|
+
|
|
108
|
+
Built-in types (`general-purpose`, `Explore`) are always present. User agents override built-ins with the same name; project agents override user agents (see [Merge precedence](#merge-precedence)).
|
|
56
109
|
|
|
57
110
|
```markdown
|
|
58
111
|
---
|
|
112
|
+
name: security-review
|
|
113
|
+
display_name: Security Review
|
|
59
114
|
description: Review code for security issues
|
|
60
|
-
tools: [read, bash, grep, find]
|
|
115
|
+
tools: [read, bash, grep, find, ls]
|
|
61
116
|
extensions: false
|
|
62
117
|
skills: false
|
|
63
|
-
|
|
118
|
+
model: anthropic/claude-sonnet-4-5-20250514
|
|
119
|
+
thinking: high
|
|
120
|
+
max_turns: 10
|
|
64
121
|
---
|
|
65
122
|
|
|
66
123
|
You are a security review specialist. Analyze code for vulnerabilities,
|
|
67
124
|
focusing on injection flaws, auth bypasses, and insecure defaults.
|
|
68
125
|
```
|
|
69
126
|
|
|
127
|
+
**Frontmatter quick reference:**
|
|
128
|
+
|
|
129
|
+
| Field | Type | Description |
|
|
130
|
+
|---|---|---|
|
|
131
|
+
| `name` | string | Agent type name. Used as the enum value in the `agent` parameter (defaults to filename). |
|
|
132
|
+
| `display_name` | string | Human-readable label shown in the UI. |
|
|
133
|
+
| `description` | string | Short description — displayed in the `/agents` type list and tool rendering. |
|
|
134
|
+
| `tools` | string[] | Built-in tool allowlist: `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`. If omitted, inherits all. |
|
|
135
|
+
| `disallowed_tools` | string[] | Tool denylist — removes these from the agent's toolset even if allowlisted by `tools` or extensions. |
|
|
136
|
+
| `extensions` | bool \| string[] | `false` = no extension tools; `true` = inherit all; `["ext-a"]` = allowlist. |
|
|
137
|
+
| `skills` | bool \| string[] | `false` = no skill prompts; `true` = inherit all; `["skill-a"]` = only these. |
|
|
138
|
+
| `model` | string | Default model as `"provider/model-id"`. Override via `/agents` or `subagents-lite.json`. |
|
|
139
|
+
| `thinking` | string | Default thinking level: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`. |
|
|
140
|
+
| `max_turns` | number | Turn limit (soft stop with steer). |
|
|
141
|
+
| `enabled` | bool | `false` removes the agent type from the tool schema's enum (LLM can't see or invoke it). Running agents unaffected. Default: `true`. |
|
|
142
|
+
| `isolated` | bool | Shorthand for `extensions: false` + `skills: false`. Reduces token footprint per turn. |
|
|
143
|
+
|
|
144
|
+
### Token-Saving Frontmatter Settings
|
|
145
|
+
|
|
146
|
+
Every tool schema and every skill snippet you inject costs tokens — in every turn. These frontmatter fields are your main levers:
|
|
147
|
+
|
|
148
|
+
| Setting | What it controls | Token impact |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `tools: [a, b, c]` | Which built-in tools the LLM sees | High — each tool has a schema (name, params, description) injected every turn. Fewer tools = fewer tokens. |
|
|
151
|
+
| `extensions: false` | Disables all extension-provided tools | Medium — extensions can register many tools (linters, browsers, etc.). Each adds schema tokens. |
|
|
152
|
+
| `extensions: ["my-ext"]` | Allowlist only specific extensions | Medium — pick only what the agent needs. |
|
|
153
|
+
| `skills: false` | Prevents skill content from being injected into the system prompt | **Highest** — skill prompts are prose, not schemas. A verbose skill can be 10-50x the token cost of a tool schema. |
|
|
154
|
+
| `skills: ["skill-a"]` | Inject only listed skills (preloaded inline) | Medium — you control exactly which skills appear. |
|
|
155
|
+
| `isolated: true` | Shorthand for `extensions: false` + `skills: false` | High — zero extension tools, zero skill prompts. Useful for fast, focused agents. |
|
|
156
|
+
|
|
157
|
+
**Practical example:** An `Explore` agent that only reads files doesn't need write tools, browser extensions, or git skills. Setting `tools: [read, bash, grep, find, ls]` + `extensions: false` + `skills: false` saves thousands of tokens per turn compared to inheriting everything.
|
|
158
|
+
|
|
159
|
+
### Merge precedence
|
|
160
|
+
|
|
161
|
+
Project agents override user agents, which override built-ins (`general-purpose`, `Explore`). Agent types discovered from `.md` files automatically appear in the `agent` parameter's dropdown — no registration required.
|
|
162
|
+
|
|
163
|
+
## Model Resolution
|
|
164
|
+
|
|
165
|
+
The extension picks the right model automatically. Precedence (highest first):
|
|
166
|
+
|
|
167
|
+
1. **Session per-type override** — `/agents > Model settings`, lasts the session
|
|
168
|
+
2. **Session global default** — temporary
|
|
169
|
+
3. **Config per-type override** — `~/.pi/agent/subagents-lite.json`
|
|
170
|
+
4. **Config global default**
|
|
171
|
+
5. **Agent frontmatter** — `model` in `.md` file
|
|
172
|
+
6. **Parent model** — inherit from the calling agent
|
|
173
|
+
|
|
174
|
+
The LLM never passes `model` — it's injected at call time. Set it once in config or frontmatter and forget about it.
|
|
175
|
+
|
|
70
176
|
## Commands
|
|
71
177
|
|
|
72
|
-
|
|
73
|
-
|
|
178
|
+
### `/agents`
|
|
179
|
+
|
|
180
|
+
Management menu with four sections:
|
|
181
|
+
|
|
182
|
+
- **Model settings** — global default, per-type overrides, force background mode
|
|
183
|
+
- **Concurrency** — default limit, per-provider and per-model slots
|
|
184
|
+
- **Running agents** — list, steer, stop, view snapshot, view result
|
|
185
|
+
- **Debug** — agent types, agent briefing (sends capabilities to the LLM)
|
|
186
|
+
|
|
187
|
+
## Interface
|
|
188
|
+
|
|
189
|
+
### Live Widget
|
|
190
|
+
|
|
191
|
+
Persistent bar above the editor showing running and completed agents. Updates live during execution.
|
|
192
|
+
|
|
193
|
+
- Running agents show a spinner, current tool activity, turn count, token usage (with optional context-fill percent), and elapsed time
|
|
194
|
+
- Completed agents show a check mark with final stats
|
|
195
|
+
- Click `tail -f` path to follow output logs in real time
|
|
196
|
+
|
|
197
|
+
Format (tree structure with branch connectors):
|
|
198
|
+
```
|
|
199
|
+
├─ ⠙ Explore description 3🛠 ·5≤30⟳ ·12.0k(45%)·1h 2m 3s
|
|
200
|
+
│ └ thinking…
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Turn format uses `≤` and `⟳` glyphs (`5≤30⟳` = 5 of 30 turns). Token count uses compact notation (`12.0k`) with optional context-fill percent in parentheses. No "tokens" label — the glyphs are self-explanatory.
|
|
204
|
+
|
|
205
|
+
### Result Viewer
|
|
206
|
+
|
|
207
|
+
Fullscreen markdown viewer for agent results. Opens automatically when viewing a completed agent's result from the `/agents` menu.
|
|
208
|
+
|
|
209
|
+
Key bindings: `↑↓` navigate · `PgUp/PgDn` · `g`/`G` top/bottom · `f` toggle fullscreen · `r` refresh · `q`/`Esc` close
|
|
210
|
+
|
|
211
|
+
Stats line: ` ↑12.0k · ↓8.0k · W3.0k · $0.024 · 15 turns · 47s`
|
|
212
|
+
|
|
213
|
+
## Configuration
|
|
214
|
+
|
|
215
|
+
`~/.pi/agent/subagents-lite.json` — managed via `/agents` menu, or edit directly:
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"agent": {
|
|
220
|
+
"default": null,
|
|
221
|
+
"forceBackground": false,
|
|
222
|
+
"Explore": "anthropic/claude-haiku-4-5-20251001"
|
|
223
|
+
},
|
|
224
|
+
"concurrency": {
|
|
225
|
+
"default": 4,
|
|
226
|
+
"providers": { "ollama": 2 },
|
|
227
|
+
"models": {
|
|
228
|
+
"anthropic/claude-sonnet-4-5-20250514": 3
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
> **Note:** `agent.default` (global fallback), `agent.forceBackground` (flag), and per-type overrides like `"Explore"` are peers in the same object. Agent type names become dynamic keys alongside the special fields.
|
|
235
|
+
|
|
236
|
+
## StopAgent Tool
|
|
237
|
+
|
|
238
|
+
Stop a running agent by ID. Returns a success message or an error if the agent isn't found.
|
|
239
|
+
|
|
240
|
+
| Parameter | Required | Description |
|
|
241
|
+
|---|---|---|
|
|
242
|
+
| `agent_id` | ✅ | The agent ID returned by the `Agent` tool when the agent was spawned |
|
|
243
|
+
|
|
244
|
+
Agent IDs can be discovered from:
|
|
245
|
+
- The `Agent` tool's result (shown on spawn)
|
|
246
|
+
- The `StopAgent` error message, which lists all running agent IDs
|
|
247
|
+
- The `/agents` menu's **Running agents** section
|
|
248
|
+
|
|
249
|
+
## Output Logs
|
|
250
|
+
|
|
251
|
+
`/tmp/pi-agent-outputs/<agentId>.log` — append-only, human-readable, `tail -f` friendly. Every line is prefixed with an ISO 8601 timestamp:
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
2026-05-27T12:00:00.000Z [USER] Find all authentication files
|
|
255
|
+
2026-05-27T12:00:02.000Z [TOOL] read("src/auth/index.ts")
|
|
256
|
+
2026-05-27T12:00:02.000Z [TOOL_RESULT] read: 234 chars
|
|
257
|
+
2026-05-27T12:00:15.000Z [ASSISTANT] I found the authentication module...
|
|
258
|
+
2026-05-27T12:00:45.000Z [DONE] 5 turns, 12 tool uses, 12.3k tokens, $0.024
|
|
259
|
+
```
|
|
74
260
|
|
|
75
261
|
## Requirements
|
|
76
262
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-subagents-lite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Lightweight sub-agents for pi — spawn specialized agents with isolated sessions, tools, and models.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
],
|
|
12
12
|
"author": "AlexParamonov",
|
|
13
13
|
"license": "MIT",
|
|
14
|
+
"packageManager": "bun@1",
|
|
14
15
|
"engines": {
|
|
16
|
+
"bun": ">=1.0.0",
|
|
15
17
|
"node": ">=18"
|
|
16
18
|
},
|
|
17
19
|
"repository": {
|
package/src/agent-discovery.ts
CHANGED
|
@@ -14,20 +14,7 @@
|
|
|
14
14
|
import * as fs from "node:fs";
|
|
15
15
|
import * as path from "node:path";
|
|
16
16
|
import type { AgentConfig, ThinkingLevel } from "./types.js";
|
|
17
|
-
|
|
18
|
-
/* ------------------------------------------------------------------ */
|
|
19
|
-
/* Validation helpers */
|
|
20
|
-
/* ------------------------------------------------------------------ */
|
|
21
|
-
|
|
22
|
-
const VALID_THINKING_LEVELS: readonly ThinkingLevel[] = [
|
|
23
|
-
"off", "minimal", "low", "medium", "high", "xhigh",
|
|
24
|
-
] as const;
|
|
25
|
-
|
|
26
|
-
/** Validate and narrow a raw thinking value to ThinkingLevel. */
|
|
27
|
-
function validateThinking(raw: string | undefined): ThinkingLevel | undefined {
|
|
28
|
-
if (raw === undefined) return undefined;
|
|
29
|
-
return VALID_THINKING_LEVELS.includes(raw as ThinkingLevel) ? (raw as ThinkingLevel) : undefined;
|
|
30
|
-
}
|
|
17
|
+
import { parseThinkingLevel } from "./utils.js";
|
|
31
18
|
|
|
32
19
|
/* ------------------------------------------------------------------ */
|
|
33
20
|
/* Types */
|
|
@@ -46,6 +33,7 @@ export interface AgentConfigFromMd {
|
|
|
46
33
|
max_turns?: number;
|
|
47
34
|
disallowed_tools?: string[];
|
|
48
35
|
enabled?: boolean;
|
|
36
|
+
isolated?: boolean;
|
|
49
37
|
systemPrompt: string;
|
|
50
38
|
source: "user" | "project";
|
|
51
39
|
}
|
|
@@ -180,7 +168,7 @@ export function parseExtensions(
|
|
|
180
168
|
/* ------------------------------------------------------------------ */
|
|
181
169
|
|
|
182
170
|
/** Extract a non-empty string value from frontmatter. */
|
|
183
|
-
|
|
171
|
+
function parseString(
|
|
184
172
|
frontmatter: Record<string, unknown>,
|
|
185
173
|
key: string,
|
|
186
174
|
): string | undefined {
|
|
@@ -189,7 +177,7 @@ export function parseString(
|
|
|
189
177
|
}
|
|
190
178
|
|
|
191
179
|
/** Extract a string array from frontmatter (array or comma-separated string). */
|
|
192
|
-
|
|
180
|
+
function parseStringArray(
|
|
193
181
|
frontmatter: Record<string, unknown>,
|
|
194
182
|
key: string,
|
|
195
183
|
): string[] | undefined {
|
|
@@ -203,6 +191,31 @@ export function parseStringArray(
|
|
|
203
191
|
return undefined;
|
|
204
192
|
}
|
|
205
193
|
|
|
194
|
+
/** Extract a boolean from frontmatter (true/false or "true"/"false"). */
|
|
195
|
+
function parseBoolean(
|
|
196
|
+
frontmatter: Record<string, unknown>,
|
|
197
|
+
key: string,
|
|
198
|
+
): boolean | undefined {
|
|
199
|
+
const v = frontmatter[key];
|
|
200
|
+
if (v === true || v === "true") return true;
|
|
201
|
+
if (v === false || v === "false") return false;
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Extract a number from frontmatter (number or numeric string). */
|
|
206
|
+
function parseNumber(
|
|
207
|
+
frontmatter: Record<string, unknown>,
|
|
208
|
+
key: string,
|
|
209
|
+
): number | undefined {
|
|
210
|
+
const v = frontmatter[key];
|
|
211
|
+
if (typeof v === "number") return v;
|
|
212
|
+
if (typeof v === "string" && v.length > 0) {
|
|
213
|
+
const n = Number(v);
|
|
214
|
+
if (!Number.isNaN(n)) return n;
|
|
215
|
+
}
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
|
|
206
219
|
/**
|
|
207
220
|
* Build an object containing only the entries whose value is not undefined.
|
|
208
221
|
* Used to transform AgentConfigFromMd fields into a Partial<AgentConfig>
|
|
@@ -233,42 +246,19 @@ export function parseAgentFile(
|
|
|
233
246
|
): AgentConfigFromMd {
|
|
234
247
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
235
248
|
|
|
236
|
-
const extensions = parseExtensions(frontmatter.extensions);
|
|
237
|
-
const skills = parseExtensions(frontmatter.skills);
|
|
238
|
-
|
|
239
|
-
// enabled field
|
|
240
|
-
const enabledRaw = frontmatter.enabled;
|
|
241
|
-
let enabled: boolean | undefined;
|
|
242
|
-
if (enabledRaw === "false" || enabledRaw === false) {
|
|
243
|
-
enabled = false;
|
|
244
|
-
} else if (enabledRaw === "true" || enabledRaw === true) {
|
|
245
|
-
enabled = true;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// max_turns field
|
|
249
|
-
const maxTurnsRaw = frontmatter.max_turns;
|
|
250
|
-
let maxTurns: number | undefined;
|
|
251
|
-
if (typeof maxTurnsRaw === "number") {
|
|
252
|
-
maxTurns = maxTurnsRaw;
|
|
253
|
-
} else if (typeof maxTurnsRaw === "string" && maxTurnsRaw.length > 0) {
|
|
254
|
-
const parsed = Number(maxTurnsRaw);
|
|
255
|
-
if (!Number.isNaN(parsed)) {
|
|
256
|
-
maxTurns = parsed;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
249
|
return {
|
|
261
250
|
name: parseString(frontmatter, "name"),
|
|
262
251
|
display_name: parseString(frontmatter, "display_name"),
|
|
263
252
|
description: parseString(frontmatter, "description"),
|
|
264
253
|
tools: parseStringArray(frontmatter, "tools"),
|
|
265
|
-
extensions,
|
|
266
|
-
skills,
|
|
254
|
+
extensions: parseExtensions(frontmatter.extensions),
|
|
255
|
+
skills: parseExtensions(frontmatter.skills),
|
|
267
256
|
model: parseString(frontmatter, "model"),
|
|
268
|
-
thinking:
|
|
269
|
-
max_turns:
|
|
257
|
+
thinking: parseThinkingLevel(parseString(frontmatter, "thinking")),
|
|
258
|
+
max_turns: parseNumber(frontmatter, "max_turns"),
|
|
270
259
|
disallowed_tools: parseStringArray(frontmatter, "disallowed_tools"),
|
|
271
|
-
enabled,
|
|
260
|
+
enabled: parseBoolean(frontmatter, "enabled"),
|
|
261
|
+
isolated: parseBoolean(frontmatter, "isolated"),
|
|
272
262
|
systemPrompt: body,
|
|
273
263
|
source: source,
|
|
274
264
|
};
|
|
@@ -392,6 +382,7 @@ function fromMd(md: AgentConfigFromMd): Partial<AgentConfig> {
|
|
|
392
382
|
maxTurns: md.max_turns,
|
|
393
383
|
disallowedTools: md.disallowed_tools,
|
|
394
384
|
enabled: md.enabled,
|
|
385
|
+
isolated: md.isolated,
|
|
395
386
|
systemPrompt: md.systemPrompt,
|
|
396
387
|
source: md.source === "project" ? "project" : "global",
|
|
397
388
|
};
|