@maxiaochao/pi-toolkit 0.10.0 → 0.10.1
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/CHANGELOG.md +11 -0
- package/README.md +0 -1
- package/package.json +1 -1
- package/packages/herdr-subagents/CHANGELOG.md +9 -0
- package/packages/herdr-subagents/README.md +156 -28
- package/packages/herdr-subagents/TESTING.md +4 -3
- package/packages/herdr-subagents/docs/demo.webm +0 -0
- package/packages/herdr-subagents/docs/screenshot.png +0 -0
- package/packages/herdr-subagents/extensions/herdr-subagents.ts +1 -1
- package/packages/herdr-subagents/package.json +1 -1
- package/packages/herdr-subagents/src/config.ts +9 -5
- package/packages/herdr-subagents/tests/run-tests.mjs +11 -5
- package/packages/herdr-subagents/docs/demo.gif +0 -0
- package/skills/calldiff/LICENSE +0 -21
- package/skills/calldiff/NOTICE +0 -19
- package/skills/calldiff/SKILL.md +0 -121
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
## 2026-10-08 - v0.10.1
|
|
2
|
+
|
|
3
|
+
- bundle herdr-subagents v0.2.1: add default thinking configuration and inline Herdr workflow video
|
|
4
|
+
- remove the bundled `calldiff` skill
|
|
5
|
+
- add merged global/project `defaultThinking` for Herdr Subagents, below persona-specific thinking and above parent-session fallback
|
|
6
|
+
- embed the Herdr workflow video directly in the GitHub README, retain the reproducible WebM source, and remove the GIF preview
|
|
7
|
+
- document every Herdr Subagents settings and persona option, accepted values, defaults, configuration paths, examples, and exact precedence
|
|
8
|
+
- add a full real-time Herdr Subagents WebM recording that focuses every child Agent pane before returning to the parent summary
|
|
9
|
+
|
|
10
|
+
- replace the rendered Herdr transcript demo with a real UI recording of a parent Pi conversation opening and completing three concurrent interactive Agent panes
|
|
11
|
+
|
|
1
12
|
## 2026-10-08 - v0.10.0
|
|
2
13
|
|
|
3
14
|
- bundle herdr-subagents v0.2.0: replace Tiny Subagent with visible interactive Herdr agents and durable batch workflows
|
package/README.md
CHANGED
|
@@ -66,7 +66,6 @@ bash scripts/install.sh
|
|
|
66
66
|
| `herdr` | Control Herdr workspaces, tabs, panes, and agents |
|
|
67
67
|
| `agent-team` | Run cross-vendor adversarial review through Herdr |
|
|
68
68
|
| `tapd` | Use the TAPD CLI for requirements, defects, tasks, and wikis |
|
|
69
|
-
| `calldiff` | Diff call stacks across commits |
|
|
70
69
|
|
|
71
70
|
Package-specific skills, such as `pi-worktree`, are documented by their child package.
|
|
72
71
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maxiaochao/pi-toolkit",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Personal pi coding agent toolkit: /todos, /btw, /cache_export and apply_patch extensions, browser automation, and bundled theme/skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## 2026-10-08 - v0.2.1
|
|
2
|
+
|
|
3
|
+
- add default thinking configuration and inline Herdr workflow video
|
|
4
|
+
- Add merged global/project `defaultThinking`, below persona-specific thinking and above parent-session fallback, with effective source reporting.
|
|
5
|
+
- Embed the workflow video directly in the GitHub README, retain the reproducible WebM source, and remove the GIF preview.
|
|
6
|
+
- Expand the README with every settings and persona option, accepted values, defaults, paths, examples, and exact model, thinking, skills, and definition precedence.
|
|
7
|
+
- Add a full WebM recording that focuses all three live child Agent panes before returning to the parent summary.
|
|
8
|
+
- Replace the rendered transcript media with a real Herdr UI recording that follows a parent Pi conversation through a three-Agent batch and final compact response.
|
|
9
|
+
|
|
1
10
|
## 2026-10-08 - v0.2.0
|
|
2
11
|
|
|
3
12
|
- replace Tiny Subagent with visible interactive Herdr agents and durable batch workflows
|
|
@@ -2,11 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
A Pi extension that runs focused foreground or background work as normal interactive Pi Agents in visible Herdr panes. Full answers and sessions stay in project-local records while the parent receives only concise summaries and document paths.
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|
|
|
7
7
|
## Demo
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The recording shows a real Pi conversation invoking a three-task read-only batch, Herdr opening one interactive Agent pane per task, and the compact results returning to the parent.
|
|
10
|
+
|
|
11
|
+
https://github.com/user-attachments/assets/8ddf3ae6-a11a-492b-9cae-d9496ba65b67
|
|
12
|
+
|
|
13
|
+
The video starts a three-agent batch, focuses each live child pane in turn, then returns
|
|
14
|
+
to the parent summary. The reproducible source recording is retained as [`docs/demo.webm`](docs/demo.webm).
|
|
10
15
|
|
|
11
16
|
## Requirements and install
|
|
12
17
|
|
|
@@ -98,49 +103,172 @@ The complete result is not copied into the parent model context. Each run is ret
|
|
|
98
103
|
|
|
99
104
|
The task directory contains the full result, structured report, task metadata, system prompt, and persistent Pi session. Run-level metadata is stored in the parent run directory.
|
|
100
105
|
|
|
101
|
-
##
|
|
106
|
+
## Configuration and personas
|
|
102
107
|
|
|
103
|
-
|
|
108
|
+
Herdr Subagents loads settings and persona definitions from separate files:
|
|
104
109
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
110
|
+
| Scope | Settings | Persona definitions |
|
|
111
|
+
| --- | --- | --- |
|
|
112
|
+
| Project | `.pi/herdr-subagents.json` | `.pi/herdr-subagents/agents/*.md` |
|
|
113
|
+
| Global | `~/.pi/agent/herdr-subagents.json` | `~/.pi/agent/herdr-subagents/agents/*.md` |
|
|
114
|
+
| Package | built-in defaults | installed package `agents/*.md` |
|
|
108
115
|
|
|
109
|
-
|
|
116
|
+
Project JSON values recursively override global JSON values. The built-in setting defaults are `defaultConcurrency: 1`, `maxConcurrency: 4`, and `stalledWarningSeconds: 300`. `defaultModel` and `defaultThinking` are unset, so the parent Pi model and its thinking level are inherited unless another setting wins.
|
|
110
117
|
|
|
111
|
-
|
|
118
|
+
### Settings file option reference
|
|
112
119
|
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
name: worker
|
|
116
|
-
description: General-purpose worker for one focused task
|
|
117
|
-
tools: read, bash, edit, write, grep, find, ls
|
|
118
|
-
---
|
|
120
|
+
These are all supported top-level fields in `herdr-subagents.json`:
|
|
119
121
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
| Option | Accepted value | Default | Effect |
|
|
123
|
+
| --- | --- | --- | --- |
|
|
124
|
+
| `defaultConcurrency` | Positive integer | `1` | Number of read-only tasks allowed to run together when a batch call omits `concurrency`. It is capped to `maxConcurrency`. |
|
|
125
|
+
| `maxConcurrency` | Positive integer | `4` | Hard ceiling for requested/default batch concurrency. A batch containing any write-capable persona still runs serially. |
|
|
126
|
+
| `stalledWarningSeconds` | Positive integer | `300` | Seconds before a still-running child emits a warning. This is not a timeout and does not stop the child. |
|
|
127
|
+
| `defaultModel` | Non-empty Pi model string, normally `provider/model` | Unset | Model used when neither JSON persona settings nor persona Markdown select one. If unset, the parent model is inherited. Use `/herdr-subagents models` to list authenticated models. |
|
|
128
|
+
| `defaultThinking` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max` | Unset | Thinking level below persona-specific settings. Provider/model support can vary. If unset, parent thinking is inherited only when the parent model is also inherited. |
|
|
129
|
+
| `personas` | Object keyed by an existing persona name | `{}` | Per-persona JSON overrides described below. This does not create a persona; a matching Markdown definition must exist. |
|
|
130
|
+
|
|
131
|
+
Known fields with an invalid JSON type, an empty required string, or a non-positive integer are ignored with a diagnostic shown by `/herdr-subagents settings`. Model IDs, thinking-level compatibility, and skill availability are ultimately validated by Pi when the child starts. When `defaultConcurrency` exceeds `maxConcurrency`, the effective default is reduced to the maximum.
|
|
122
132
|
|
|
123
|
-
|
|
133
|
+
Each `personas.<name>` object supports exactly these overrides:
|
|
124
134
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
135
|
+
| Option | Accepted value | Effect |
|
|
136
|
+
| --- | --- | --- |
|
|
137
|
+
| `model` | Non-empty Pi model string | Overrides the model from the effective persona Markdown and `defaultModel`. |
|
|
138
|
+
| `thinking` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max` | Overrides persona Markdown and `defaultThinking`. The value is passed to Pi's `--thinking` option. |
|
|
139
|
+
| `skills` | Array of non-empty strings | Replaces the entire lower-precedence skill list. Use `[]` to clear inherited skills. Each item is passed to child Pi as `--skill`. |
|
|
130
140
|
|
|
131
|
-
|
|
141
|
+
JSON persona overrides cannot change `name`, `description`, `access`, `tools`, or the system prompt; define those in persona Markdown.
|
|
142
|
+
|
|
143
|
+
A global configuration can establish shared defaults:
|
|
132
144
|
|
|
133
145
|
```json
|
|
134
146
|
{
|
|
135
|
-
"
|
|
136
|
-
"maxConcurrency":
|
|
147
|
+
"defaultConcurrency": 2,
|
|
148
|
+
"maxConcurrency": 4,
|
|
149
|
+
"stalledWarningSeconds": 300,
|
|
150
|
+
"defaultModel": "anthropic/claude-sonnet-4-5",
|
|
151
|
+
"defaultThinking": "medium",
|
|
137
152
|
"personas": {
|
|
138
|
-
"
|
|
153
|
+
"explorer": {
|
|
154
|
+
"model": "anthropic/claude-haiku-4-5",
|
|
155
|
+
"thinking": "medium",
|
|
156
|
+
"skills": ["code-search"]
|
|
157
|
+
},
|
|
158
|
+
"reviewer": {
|
|
159
|
+
"thinking": "high",
|
|
160
|
+
"skills": ["code-review"]
|
|
161
|
+
}
|
|
139
162
|
}
|
|
140
163
|
}
|
|
141
164
|
```
|
|
142
165
|
|
|
143
|
-
|
|
166
|
+
The project file can override only the fields that differ. Persona fields are merged by persona name:
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"defaultConcurrency": 3,
|
|
171
|
+
"stalledWarningSeconds": 600,
|
|
172
|
+
"defaultThinking": "high",
|
|
173
|
+
"personas": {
|
|
174
|
+
"explorer": {
|
|
175
|
+
"model": "openai/gpt-5.2"
|
|
176
|
+
},
|
|
177
|
+
"reviewer": {
|
|
178
|
+
"skills": ["security-review", "code-review"]
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The package includes these personas:
|
|
185
|
+
|
|
186
|
+
| Persona | Access | Built-in tools | Intended use |
|
|
187
|
+
| --- | --- | --- | --- |
|
|
188
|
+
| `worker` | write | `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls` | Focused implementation work |
|
|
189
|
+
| `explorer` | read | `read`, `bash`, `grep`, `find`, `ls` | Fast codebase investigation |
|
|
190
|
+
| `reviewer` | read | `read`, `bash`, `grep`, `find`, `ls` | Correctness and maintainability review |
|
|
191
|
+
|
|
192
|
+
Persona definitions use Markdown frontmatter followed by the child system prompt. For example, save this as `.pi/herdr-subagents/agents/security-reviewer.md`:
|
|
193
|
+
|
|
194
|
+
```md
|
|
195
|
+
---
|
|
196
|
+
name: security-reviewer
|
|
197
|
+
description: Read-only reviewer for security risks
|
|
198
|
+
access: read
|
|
199
|
+
model: anthropic/claude-sonnet-4-5
|
|
200
|
+
thinking: high
|
|
201
|
+
tools: read, bash, grep, find, ls
|
|
202
|
+
skills: security-review, code-review
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
Review the assigned scope without modifying files. Report concrete risks and evidence.
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Persona Markdown supports these frontmatter fields:
|
|
209
|
+
|
|
210
|
+
| Field | Accepted value | Required/default | Effect |
|
|
211
|
+
| --- | --- | --- | --- |
|
|
212
|
+
| `name` | Non-empty string | Required | Persona identifier used by `agent`. Names must be unique within one persona directory. |
|
|
213
|
+
| `description` | Non-empty string | Required | Description shown to the parent when agents are listed. |
|
|
214
|
+
| `access` | `read` or `write` | `write` | Scheduling declaration. Any batch containing `write` is forced to concurrency one. `read` permits parallel scheduling but does not itself remove tools. |
|
|
215
|
+
| `model` | Non-empty Pi model string | Unset | Persona model default. |
|
|
216
|
+
| `thinking` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max` | Unset | Persona thinking default passed to Pi's `--thinking` option. |
|
|
217
|
+
| `tools` | Non-empty comma-separated list, optionally bracketed | Child Pi defaults | Restricts built-in tools. Supported names are `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`, and `powershell`. `subagent_report` is always added. |
|
|
218
|
+
| `skills` | Comma-separated list, optionally bracketed | None | Skills loaded explicitly into child Pi; automatic skill discovery is disabled. |
|
|
219
|
+
|
|
220
|
+
The Markdown body after the frontmatter is the child system prompt and may be empty. To make a custom persona operationally read-only, set `access: read` **and** choose an appropriate `tools` allowlist; `access` alone is not a tool sandbox. Model names and skill values are passed to Pi, so they must be available in the installed Pi environment.
|
|
221
|
+
|
|
222
|
+
Persona Markdown definitions use replacement precedence, not field merging. A same-name project definition replaces the entire global or package definition:
|
|
223
|
+
|
|
224
|
+
1. project `.pi/herdr-subagents/agents/*.md`
|
|
225
|
+
2. global `~/.pi/agent/herdr-subagents/agents/*.md`
|
|
226
|
+
3. package built-ins
|
|
227
|
+
|
|
228
|
+
### Model and persona-option precedence
|
|
229
|
+
|
|
230
|
+
The child model is selected in this exact order:
|
|
231
|
+
|
|
232
|
+
1. `model` on the task or call
|
|
233
|
+
2. project `personas.<name>.model` JSON setting
|
|
234
|
+
3. global `personas.<name>.model` JSON setting
|
|
235
|
+
4. `model` in the effective persona Markdown frontmatter
|
|
236
|
+
5. effective `defaultModel` JSON setting (project overrides global)
|
|
237
|
+
6. parent Pi model
|
|
238
|
+
|
|
239
|
+
The child thinking level is selected in this order:
|
|
240
|
+
|
|
241
|
+
1. project `personas.<name>.thinking` JSON setting
|
|
242
|
+
2. global `personas.<name>.thinking` JSON setting
|
|
243
|
+
3. `thinking` in the effective persona Markdown frontmatter
|
|
244
|
+
4. effective `defaultThinking` JSON setting (project overrides global)
|
|
245
|
+
5. parent Pi thinking level when the child also inherits the parent model
|
|
246
|
+
|
|
247
|
+
Thinking has no call-level override. `skills` uses the first three persona-level steps and has no global default; a project `skills` array replaces, rather than appends to, the global or Markdown list.
|
|
248
|
+
|
|
249
|
+
Override the model for one task:
|
|
250
|
+
|
|
251
|
+
```text
|
|
252
|
+
herdr_subagents({
|
|
253
|
+
agent: "reviewer",
|
|
254
|
+
model: "anthropic/claude-opus-4-1:high",
|
|
255
|
+
prompt: "Review the authorization boundary"
|
|
256
|
+
})
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Each task in a batch can select its own model:
|
|
260
|
+
|
|
261
|
+
```text
|
|
262
|
+
herdr_subagents({
|
|
263
|
+
concurrency: 2,
|
|
264
|
+
tasks: [
|
|
265
|
+
{ name: "map", agent: "explorer", model: "anthropic/claude-haiku-4-5", prompt: "Map the request flow" },
|
|
266
|
+
{ name: "review", agent: "reviewer", model: "anthropic/claude-sonnet-4-5", prompt: "Review input validation" }
|
|
267
|
+
]
|
|
268
|
+
})
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Run `/herdr-subagents agents`, `/herdr-subagents models`, or `/herdr-subagents settings` to inspect effective values and their sources.
|
|
144
272
|
|
|
145
273
|
## Recovery and failures
|
|
146
274
|
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
1. `npm --prefix packages/herdr-subagents test` runs deterministic scheduler, recovery, and persona tests.
|
|
6
6
|
2. `pi -ne -ns --no-session -e ./packages/herdr-subagents/extensions/herdr-subagents.ts` verifies extension loading.
|
|
7
|
-
3. `
|
|
8
|
-
4.
|
|
7
|
+
3. From a visible X11 Herdr client with the GStreamer X11 and VP8 plugins installed, run `HERDR_MEDIA_WINDOW_ID=<window-id> node scripts/capture-package-media.mjs`. The Herdr capture must show a parent Pi conversation, a three-task run tab, each of the three real interactive Agent panes receiving focus in turn, and the final compact parent response.
|
|
8
|
+
4. `npm pack ./packages/herdr-subagents --dry-run` verifies package contents.
|
|
9
|
+
5. Verify the README renders the GitHub attachment video directly and live Herdr UI media `docs/screenshot.png` and `docs/demo.webm` are present in the tarball; `docs/demo.gif` must be absent. Inspect the WebM metadata and play it through once to confirm the parent → three focused children → parent sequence.
|
|
9
10
|
|
|
10
11
|
## Cases
|
|
11
12
|
|
|
@@ -21,4 +22,4 @@
|
|
|
21
22
|
|
|
22
23
|
## Latest baseline
|
|
23
24
|
|
|
24
|
-
All
|
|
25
|
+
All 136 deterministic scheduler, recovery, process-boundary, schema-validation, navigation, and persona cases must pass. Release validation also requires extension loading and a real Herdr read-only batch smoke with distinct task panes and final tab cleanup. The latest smoke completed two concurrent tasks, retained its focused run tab through settlement, and closed it automatically after focus moved away.
|
|
Binary file
|
|
Binary file
|
|
@@ -92,7 +92,7 @@ async function handleHerdrCommand(args: string, ctx: ExtensionCommandContext, fl
|
|
|
92
92
|
}
|
|
93
93
|
if (section === "settings") {
|
|
94
94
|
const settings = configuration.settings;
|
|
95
|
-
const values: Record<string, string | number | undefined> = { defaultConcurrency: settings.defaultConcurrency, maxConcurrency: settings.maxConcurrency, stalledWarningSeconds: settings.stalledWarningSeconds, defaultModel: settings.defaultModel };
|
|
95
|
+
const values: Record<string, string | number | undefined> = { defaultConcurrency: settings.defaultConcurrency, maxConcurrency: settings.maxConcurrency, stalledWarningSeconds: settings.stalledWarningSeconds, defaultModel: settings.defaultModel, defaultThinking: settings.defaultThinking };
|
|
96
96
|
const lines = Object.entries(values).map(([key, value]) => `${key}=${String(value ?? "unset")} (${configuration.settingSources[key] ?? "built-in"})`);
|
|
97
97
|
if (configuration.diagnostics.length) lines.push("Diagnostics:", ...configuration.diagnostics);
|
|
98
98
|
ctx.ui.notify(lines.join("\n"), "info");
|
|
@@ -10,6 +10,7 @@ export interface SubagentConfig {
|
|
|
10
10
|
maxConcurrency: number;
|
|
11
11
|
stalledWarningSeconds: number;
|
|
12
12
|
defaultModel?: string;
|
|
13
|
+
defaultThinking?: string;
|
|
13
14
|
personas: Record<string, PersonaConfig>;
|
|
14
15
|
}
|
|
15
16
|
export interface EffectivePersona extends AgentDefinition {
|
|
@@ -33,6 +34,7 @@ interface ConfigFile {
|
|
|
33
34
|
maxConcurrency?: number;
|
|
34
35
|
stalledWarningSeconds?: number;
|
|
35
36
|
defaultModel?: string;
|
|
37
|
+
defaultThinking?: string;
|
|
36
38
|
personas?: Record<string, PersonaConfig>;
|
|
37
39
|
}
|
|
38
40
|
export interface ConfigurationPaths {
|
|
@@ -64,9 +66,11 @@ function mergeConfig(base: SubagentConfig, input: ConfigFile, source: string, so
|
|
|
64
66
|
result[key] = value as number;
|
|
65
67
|
sources[key] = source;
|
|
66
68
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
for (const key of ["defaultModel", "defaultThinking"] as const) {
|
|
70
|
+
const value = input[key];
|
|
71
|
+
if (value === undefined) continue;
|
|
72
|
+
if (typeof value === "string" && value.trim()) { result[key] = value.trim(); sources[key] = source; }
|
|
73
|
+
else diagnostics.push(`${source} ${key} must be a non-empty string`);
|
|
70
74
|
}
|
|
71
75
|
if (input.personas !== undefined) {
|
|
72
76
|
if (!input.personas || typeof input.personas !== "object" || Array.isArray(input.personas)) diagnostics.push(`${source} personas must be an object`);
|
|
@@ -115,8 +119,8 @@ export function loadSubagentConfiguration(cwd: string, packageAgentsDirectory: s
|
|
|
115
119
|
const projectPersona = projectInput.personas?.[definition.name] ?? {};
|
|
116
120
|
const model = projectPersona.model ?? globalPersona.model ?? definition.model ?? settings.defaultModel ?? parentModel;
|
|
117
121
|
const modelSource = projectPersona.model ? "project persona" : globalPersona.model ? "global persona" : definition.model ? `${source} persona` : settings.defaultModel ? settingSources.defaultModel : parentModel ? "parent session" : "unresolved";
|
|
118
|
-
const thinking = projectPersona.thinking ?? globalPersona.thinking ?? definition.thinking;
|
|
119
|
-
const thinkingSource = projectPersona.thinking ? "project persona" : globalPersona.thinking ? "global persona" : definition.thinking ? `${source} persona` : "default";
|
|
122
|
+
const thinking = projectPersona.thinking ?? globalPersona.thinking ?? definition.thinking ?? settings.defaultThinking;
|
|
123
|
+
const thinkingSource = projectPersona.thinking ? "project persona" : globalPersona.thinking ? "global persona" : definition.thinking ? `${source} persona` : settings.defaultThinking ? settingSources.defaultThinking : "default";
|
|
120
124
|
const skills = projectPersona.skills ?? globalPersona.skills ?? definition.skills ?? [];
|
|
121
125
|
const skillsSource = projectPersona.skills ? "project persona" : globalPersona.skills ? "global persona" : definition.skills ? `${source} persona` : "none";
|
|
122
126
|
return { ...definition, source, access: definition.access ?? "write", model, modelSource, thinking, thinkingSource, skills, skillsSource };
|
|
@@ -257,24 +257,30 @@ try {
|
|
|
257
257
|
mkdirSync(projectAgents, { recursive: true });
|
|
258
258
|
writeFileSync(join(packageAgents, "worker.md"), `---\nname: worker\ndescription: Package worker\naccess: write\nmodel: package/model\nskills: package-skill\n---\nPackage prompt.`);
|
|
259
259
|
writeFileSync(join(packageAgents, "explorer.md"), `---\nname: explorer\ndescription: Package explorer\naccess: read\n---\nExplore.`);
|
|
260
|
+
writeFileSync(join(packageAgents, "thinker.md"), `---\nname: thinker\ndescription: Package thinker\nthinking: minimal\n---\nThink.`);
|
|
260
261
|
writeFileSync(join(globalAgents, "worker.md"), `---\nname: worker\ndescription: Global worker\naccess: read\n---\nGlobal prompt.`);
|
|
261
262
|
writeFileSync(join(projectAgents, "worker.md"), `---\nname: worker\ndescription: Project worker\n---\nProject prompt.`);
|
|
262
263
|
const globalConfig = join(configRoot, "global.json");
|
|
263
264
|
const projectConfig = join(configRoot, "project.json");
|
|
264
|
-
writeFileSync(globalConfig, JSON.stringify({ maxConcurrency: 8, defaultModel: "global/default", personas: { worker: { model: "global/worker", thinking: "low", skills: ["global-skill"] } } }));
|
|
265
|
-
writeFileSync(projectConfig, JSON.stringify({ maxConcurrency: 3, personas: { worker: { model: "project/worker", thinking: "high", skills: ["project-skill"] } } }));
|
|
265
|
+
writeFileSync(globalConfig, JSON.stringify({ maxConcurrency: 8, defaultModel: "global/default", defaultThinking: "medium", personas: { worker: { model: "global/worker", thinking: "low", skills: ["global-skill"] } } }));
|
|
266
|
+
writeFileSync(projectConfig, JSON.stringify({ maxConcurrency: 3, defaultThinking: "high", personas: { worker: { model: "project/worker", thinking: "high", skills: ["project-skill"] } } }));
|
|
266
267
|
const configurationPaths = { globalConfig, projectConfig, globalAgents, projectAgents };
|
|
267
268
|
const configuration = loadSubagentConfiguration(configRoot, packageAgents, "parent/model", configurationPaths);
|
|
268
269
|
const configuredWorker = configuration.personas.find((persona) => persona.name === "worker");
|
|
269
270
|
const configuredExplorer = configuration.personas.find((persona) => persona.name === "explorer");
|
|
270
|
-
|
|
271
|
+
const configuredThinker = configuration.personas.find((persona) => persona.name === "thinker");
|
|
272
|
+
check("built-in settings recursively merge global then project", configuration.settings.defaultConcurrency === 1 && configuration.settings.maxConcurrency === 3 && configuration.settings.defaultModel === "global/default" && configuration.settings.defaultThinking === "high" && configuration.settingSources.maxConcurrency === "project" && configuration.settingSources.defaultThinking === "project");
|
|
271
273
|
check("persona definitions use project global package precedence", configuredWorker?.source === "project" && configuredWorker.description === "Project worker" && configuredWorker.systemPrompt === "Project prompt." && configuredWorker.access === "write" && configuredExplorer?.source === "package" && configuredExplorer.access === "read");
|
|
272
274
|
check("persona configuration resolves model thinking and skills", configuredWorker?.model === "project/worker" && configuredWorker.modelSource === "project persona" && configuredWorker.thinking === "high" && configuredWorker.skills.join(",") === "project-skill");
|
|
273
|
-
|
|
275
|
+
check("project default thinking applies below persona settings", configuredExplorer?.thinking === "high" && configuredExplorer.thinkingSource === "project");
|
|
276
|
+
check("persona Markdown thinking overrides default thinking", configuredThinker?.thinking === "minimal" && configuredThinker.thinkingSource === "package persona");
|
|
277
|
+
writeFileSync(projectConfig, JSON.stringify({ defaultThinking: [], personas: { worker: { model: 42, thinking: [], skills: "invalid" } } }));
|
|
274
278
|
const invalidConfiguration = loadSubagentConfiguration(configRoot, packageAgents, "parent/model", configurationPaths);
|
|
275
279
|
const invalidWorker = invalidConfiguration.personas.find((persona) => persona.name === "worker");
|
|
276
280
|
check("invalid persona fields are diagnosed and ignored", invalidWorker?.model === "global/worker" && invalidWorker.thinking === "low" && invalidWorker.skills.join(",") === "global-skill" && invalidConfiguration.diagnostics.filter((message) => message.includes("persona 'worker'")).length === 3);
|
|
277
|
-
|
|
281
|
+
const invalidExplorer = invalidConfiguration.personas.find((persona) => persona.name === "explorer");
|
|
282
|
+
check("invalid project default thinking is diagnosed and global default remains effective", invalidExplorer?.thinking === "medium" && invalidExplorer.thinkingSource === "global" && invalidConfiguration.diagnostics.some((message) => message === "project defaultThinking must be a non-empty string"));
|
|
283
|
+
writeFileSync(projectConfig, JSON.stringify({ maxConcurrency: 3, defaultThinking: "high", personas: { worker: { model: "project/worker", thinking: "high", skills: ["project-skill"] } } }));
|
|
278
284
|
const configuredHerdr = fakeHerdr();
|
|
279
285
|
const configuredRun = await executeHerdrSubagents({ agent: "worker", model: "task/model", prompt: "Use configured values" }, undefined, undefined, { cwd: configRoot, model: { provider: "parent", id: "model" }, thinkingLevel: "medium" }, { agentsDirectory: packageAgents, configurationPaths, herdr: configuredHerdr });
|
|
280
286
|
const configuredStart = configuredHerdr.calls.find(([name]) => name === "startAgent")[1];
|
|
Binary file
|
package/skills/calldiff/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Tanishq Kancharla
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/skills/calldiff/NOTICE
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
calldiff Skill
|
|
2
|
-
|
|
3
|
-
This directory is copied verbatim from the calldiff repository:
|
|
4
|
-
https://github.com/tanishqkancharla/calldiff
|
|
5
|
-
skills/calldiff/SKILL.md at commit 2086f6e4e9ed3c410bba7c94ca3dde77791b1956 (main)
|
|
6
|
-
|
|
7
|
-
The upstream repository is licensed under the MIT License. A copy of that
|
|
8
|
-
license is included in LICENSE.
|
|
9
|
-
|
|
10
|
-
Upstream publishes no release tags, so this file is pinned to a main commit
|
|
11
|
-
instead of a tag. The CLI can also write its own copy with `calldiff skills
|
|
12
|
-
add` (to ~/.agents/skills/calldiff/SKILL.md), but that generated file is terser
|
|
13
|
-
than the committed one — it leaves the --entry/--file descriptions empty and
|
|
14
|
-
says 22 languages — so pi-toolkit ships the committed file.
|
|
15
|
-
|
|
16
|
-
To refresh, copy skills/calldiff/SKILL.md and LICENSE from a newer upstream
|
|
17
|
-
commit and update the commit above. The bundled commands were smoke-tested
|
|
18
|
-
against calldiff 0.5.0 (`diff`, `tree`, `reach`, `--entry`, `--file`,
|
|
19
|
-
`--maxDepth`/`--max-depth`, `--locs`, `--format json`).
|
package/skills/calldiff/SKILL.md
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: calldiff
|
|
3
|
-
description: Diff call stacks across git commits — like git diff, but for who-calls-whom. Shows which callees appeared, disappeared, or moved under an entrypoint across 23 languages (diff, tree, and reach). Use for agentic code review when call flow changed and line diffs bury the shape of the change.
|
|
4
|
-
requires_bin: calldiff
|
|
5
|
-
command: calldiff
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# calldiff diff
|
|
9
|
-
|
|
10
|
-
Diff call stacks between two git trees
|
|
11
|
-
|
|
12
|
-
## Arguments
|
|
13
|
-
|
|
14
|
-
| Name | Type | Required | Description |
|
|
15
|
-
|------|------|----------|-------------|
|
|
16
|
-
| `from` | `string` | no | Before ref (default: HEAD) |
|
|
17
|
-
| `to` | `string` | no | After ref (default: working tree) |
|
|
18
|
-
| `paths` | `array` | no | Limit to these path prefixes |
|
|
19
|
-
|
|
20
|
-
## Options
|
|
21
|
-
|
|
22
|
-
| Flag | Type | Default | Description |
|
|
23
|
-
|------|------|---------|-------------|
|
|
24
|
-
| `--entry` | `unknown` | | Entrypoint symbol(s): functionName or ClassName.method |
|
|
25
|
-
| `--file` | `unknown` | | Entrypoint file(s): indexed source path; expands to that file's exports |
|
|
26
|
-
| `--maxDepth` | `number` | `12` | Max call-tree depth |
|
|
27
|
-
| `--locs` | `boolean` | `false` | Show call-site source locations (file:line) |
|
|
28
|
-
| `--from` | `string` | | Left / "before" tree |
|
|
29
|
-
| `--to` | `string` | | Right / "after" tree |
|
|
30
|
-
|
|
31
|
-
## Examples
|
|
32
|
-
|
|
33
|
-
```sh
|
|
34
|
-
# HEAD vs working tree
|
|
35
|
-
calldiff diff
|
|
36
|
-
|
|
37
|
-
# One ref vs working tree
|
|
38
|
-
calldiff diff main
|
|
39
|
-
|
|
40
|
-
# Two commits / branches
|
|
41
|
-
calldiff diff abc123 def456
|
|
42
|
-
|
|
43
|
-
# Force entrypoints
|
|
44
|
-
calldiff diff main feature --entry createAgentSession
|
|
45
|
-
|
|
46
|
-
# File expands to that file's exports
|
|
47
|
-
calldiff diff main feature --file src/routes.ts
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
> Semantics match git diff: no refs → HEAD vs worktree; one ref → that vs worktree; two refs → compare those trees.
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
# calldiff reach
|
|
55
|
-
|
|
56
|
-
Find all call paths from an entrypoint to a target symbol
|
|
57
|
-
|
|
58
|
-
## Arguments
|
|
59
|
-
|
|
60
|
-
| Name | Type | Required | Description |
|
|
61
|
-
|------|------|----------|-------------|
|
|
62
|
-
| `ref` | `string` | no | Git ref (default: working tree) |
|
|
63
|
-
| `paths` | `array` | no | Limit to these path prefixes |
|
|
64
|
-
|
|
65
|
-
## Options
|
|
66
|
-
|
|
67
|
-
| Flag | Type | Default | Description |
|
|
68
|
-
|------|------|---------|-------------|
|
|
69
|
-
| `--entry` | `unknown` | | Entrypoint symbol(s): functionName or ClassName.method |
|
|
70
|
-
| `--file` | `unknown` | | Entrypoint file(s): indexed source path; expands to that file's exports |
|
|
71
|
-
| `--to` | `string` | | Target symbol to reach (functionName or ClassName.method) |
|
|
72
|
-
| `--maxDepth` | `number` | `12` | Max call-tree depth |
|
|
73
|
-
| `--locs` | `boolean` | `false` | Show call-site source locations (file:line) |
|
|
74
|
-
|
|
75
|
-
## Examples
|
|
76
|
-
|
|
77
|
-
```sh
|
|
78
|
-
# Paths in the working tree
|
|
79
|
-
calldiff reach --entry runCheckout --to sendEmail
|
|
80
|
-
|
|
81
|
-
# Paths at a commit, limited to a directory
|
|
82
|
-
calldiff reach HEAD examples/checkout --entry runCheckout --to sendEmail
|
|
83
|
-
|
|
84
|
-
# Paths from every export in a file
|
|
85
|
-
calldiff reach --file packages/api/src/flow.ts --to notify
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
# calldiff tree
|
|
91
|
-
|
|
92
|
-
View a call tree (no diff) for one or more entrypoints
|
|
93
|
-
|
|
94
|
-
## Arguments
|
|
95
|
-
|
|
96
|
-
| Name | Type | Required | Description |
|
|
97
|
-
|------|------|----------|-------------|
|
|
98
|
-
| `ref` | `string` | no | Git ref (default: working tree) |
|
|
99
|
-
| `paths` | `array` | no | Limit to these path prefixes |
|
|
100
|
-
|
|
101
|
-
## Options
|
|
102
|
-
|
|
103
|
-
| Flag | Type | Default | Description |
|
|
104
|
-
|------|------|---------|-------------|
|
|
105
|
-
| `--entry` | `unknown` | | Entrypoint symbol(s): functionName or ClassName.method |
|
|
106
|
-
| `--file` | `unknown` | | Entrypoint file(s): indexed source path; expands to that file's exports |
|
|
107
|
-
| `--maxDepth` | `number` | `12` | Max call-tree depth |
|
|
108
|
-
| `--locs` | `boolean` | `false` | Show call-site source locations (file:line) |
|
|
109
|
-
|
|
110
|
-
## Examples
|
|
111
|
-
|
|
112
|
-
```sh
|
|
113
|
-
# Tree from working tree
|
|
114
|
-
calldiff tree --entry createAgentSession
|
|
115
|
-
|
|
116
|
-
# Tree from a commit
|
|
117
|
-
calldiff tree HEAD --entry PiService.createAgentSession
|
|
118
|
-
|
|
119
|
-
# Every export in a file
|
|
120
|
-
calldiff tree --file packages/api/src/routes.ts
|
|
121
|
-
```
|