@narumitw/pi-subagents 0.38.0 → 0.40.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 CHANGED
@@ -390,13 +390,16 @@ Built-in agents inherit the active/default Pi model instead of forcing a provide
390
390
 
391
391
  Open `/subagents`, choose **Advanced settings**, then **Agent tool permissions** in an interactive
392
392
  Pi session to edit the tools each subagent may use. The standard bounded multi-select keeps a
393
- one-save draft: toggles do not write until **Save changes**, Escape discards the draft, and unavailable
394
- configured tool names remain visible and preserved. These are user settings stored in `~/.pi/agent/pi-subagents.json` and affect future sessions.
393
+ one-save draft: toggles do not write until **Save changes**, Escape leaves the draft without writing,
394
+ and unavailable configured tool names remain visible and preserved. In TUI mode, type to fuzzy-search
395
+ tool names and availability metadata; Save and Discard remain pinned below the matches. These are user
396
+ settings stored in `~/.pi/agent/pi-subagents.json` and affect future sessions.
395
397
 
396
398
  Compatibility: a valid legacy `pi-subagents-config.json` remains readable with a warning and is never modified automatically; rename it to `pi-subagents.json`. The first subsequent settings save writes the canonical file. If both files exist, the new filename takes precedence.
397
399
 
398
400
  - Select an agent, then press Enter or Space to toggle tools.
399
- - Press `S` to save, or Esc to cancel and return to agent selection.
401
+ - Choose **Save changes** to write the draft, choose **Discard draft** to abandon it, or press Esc to
402
+ return to agent selection without writing.
400
403
  - Save the default selection to remove a custom override and use the agent defaults again.
401
404
  - Deselect every tool and save to run that agent with no tools.
402
405
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-subagents",
3
- "version": "0.38.0",
3
+ "version": "0.40.0",
4
4
  "description": "Pi extension for delegating work to specialized isolated subagents.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -40,11 +40,11 @@
40
40
  "@earendil-works/pi-tui": "*"
41
41
  },
42
42
  "devDependencies": {
43
- "@biomejs/biome": "2.5.5",
44
- "@earendil-works/pi-agent-core": "0.82.1",
45
- "@earendil-works/pi-ai": "0.82.1",
46
- "@earendil-works/pi-coding-agent": "0.82.1",
47
- "@earendil-works/pi-tui": "0.82.1",
43
+ "@biomejs/biome": "2.5.6",
44
+ "@earendil-works/pi-agent-core": "0.83.0",
45
+ "@earendil-works/pi-ai": "0.83.0",
46
+ "@earendil-works/pi-coding-agent": "0.83.0",
47
+ "@earendil-works/pi-tui": "0.83.0",
48
48
  "typescript": "7.0.2"
49
49
  },
50
50
  "repository": {
package/src/config-ui.ts CHANGED
@@ -297,6 +297,7 @@ async function showSubagentManager(
297
297
  "tool-draft": () => ({
298
298
  kind: "multiSelect",
299
299
  title: toolDraft ? `${safeTerminalText(toolDraft.agentName)} tools` : "Agent tools",
300
+ enableSearch: true,
300
301
  lines: toolDraft
301
302
  ? [
302
303
  `Source: ${safeTerminalText(toolDraft.agentSource)}`,
@@ -311,6 +312,7 @@ async function showSubagentManager(
311
312
  id: name,
312
313
  label: safeTerminalText(name),
313
314
  description: available ? "Available tool" : "Configured tool is not currently loaded",
315
+ searchText: available ? "available tool" : "configured unavailable preserved",
314
316
  selected: toolDraft?.selected.has(name) ?? false,
315
317
  disabled: !available,
316
318
  disabledReason: available
@@ -73,6 +73,7 @@ export type ValidatedMailboxParams =
73
73
 
74
74
  export function validateManageParams(params: unknown): ValidatedManageParams {
75
75
  const values = parameterRecord(params, "subagent_manage");
76
+ assertKnownKeys("subagent_manage", values, ["action", "agentId", "includeClosed", "subtree"]);
76
77
  const action = values.action;
77
78
  if (
78
79
  typeof action !== "string" ||
@@ -81,18 +82,32 @@ export function validateManageParams(params: unknown): ValidatedManageParams {
81
82
  throw new Error(`subagent_manage action must be one of: ${MANAGE_ACTIONS.join(", ")}`);
82
83
  }
83
84
  if (action === "list") {
84
- assertOnlyActionKeys("subagent_manage", action, values, ["action", "includeClosed"]);
85
85
  assertOptionalBoolean("subagent_manage", action, values, "includeClosed");
86
- return values as ValidatedManageParams;
86
+ return {
87
+ action,
88
+ ...(values.includeClosed === undefined ? {} : { includeClosed: values.includeClosed }),
89
+ } as ValidatedManageParams;
87
90
  }
88
- assertOnlyActionKeys("subagent_manage", action, values, ["action", "agentId", "subtree"]);
89
91
  assertRequiredString("subagent_manage", action, values, "agentId");
90
92
  assertOptionalBoolean("subagent_manage", action, values, "subtree");
91
- return values as ValidatedManageParams;
93
+ return {
94
+ action,
95
+ agentId: values.agentId,
96
+ ...(values.subtree === undefined ? {} : { subtree: values.subtree }),
97
+ } as ValidatedManageParams;
92
98
  }
93
99
 
94
100
  export function validateMailboxParams(params: unknown): ValidatedMailboxParams {
95
101
  const values = parameterRecord(params, "subagent_mailbox");
102
+ assertKnownKeys("subagent_mailbox", values, [
103
+ "action",
104
+ "agentId",
105
+ "message",
106
+ "senderId",
107
+ "deduplicationKey",
108
+ "acknowledge",
109
+ "limit",
110
+ ]);
96
111
  const action = values.action;
97
112
  if (
98
113
  typeof action !== "string" ||
@@ -101,13 +116,6 @@ export function validateMailboxParams(params: unknown): ValidatedMailboxParams {
101
116
  throw new Error(`subagent_mailbox action must be one of: ${MAILBOX_ACTIONS.join(", ")}`);
102
117
  }
103
118
  if (action === "send") {
104
- assertOnlyActionKeys("subagent_mailbox", action, values, [
105
- "action",
106
- "agentId",
107
- "message",
108
- "senderId",
109
- "deduplicationKey",
110
- ]);
111
119
  assertRequiredString("subagent_mailbox", action, values, "agentId");
112
120
  assertRequiredString("subagent_mailbox", action, values, "message");
113
121
  const message = values.message as string;
@@ -123,14 +131,16 @@ export function validateMailboxParams(params: unknown): ValidatedMailboxParams {
123
131
  'subagent_mailbox action "send" requires deduplicationKey at most 256 characters',
124
132
  );
125
133
  }
126
- return values as ValidatedMailboxParams;
134
+ return {
135
+ action,
136
+ agentId: values.agentId,
137
+ message,
138
+ ...(values.senderId === undefined ? {} : { senderId: values.senderId }),
139
+ ...(values.deduplicationKey === undefined
140
+ ? {}
141
+ : { deduplicationKey: values.deduplicationKey }),
142
+ } as ValidatedMailboxParams;
127
143
  }
128
- assertOnlyActionKeys("subagent_mailbox", action, values, [
129
- "action",
130
- "agentId",
131
- "acknowledge",
132
- "limit",
133
- ]);
134
144
  assertRequiredString("subagent_mailbox", action, values, "agentId");
135
145
  assertOptionalBoolean("subagent_mailbox", action, values, "acknowledge");
136
146
  if (
@@ -142,7 +152,12 @@ export function validateMailboxParams(params: unknown): ValidatedMailboxParams {
142
152
  ) {
143
153
  throw new Error('subagent_mailbox action "read" requires limit between 1 and 20');
144
154
  }
145
- return values as ValidatedMailboxParams;
155
+ return {
156
+ action,
157
+ agentId: values.agentId,
158
+ ...(values.acknowledge === undefined ? {} : { acknowledge: values.acknowledge }),
159
+ ...(values.limit === undefined ? {} : { limit: values.limit }),
160
+ } as ValidatedMailboxParams;
146
161
  }
147
162
 
148
163
  function parameterRecord(params: unknown, toolName: string): Record<string, unknown> {
@@ -152,18 +167,15 @@ function parameterRecord(params: unknown, toolName: string): Record<string, unkn
152
167
  return params as Record<string, unknown>;
153
168
  }
154
169
 
155
- function assertOnlyActionKeys(
170
+ function assertKnownKeys(
156
171
  toolName: string,
157
- action: string,
158
172
  values: Record<string, unknown>,
159
173
  allowed: readonly string[],
160
174
  ): void {
161
175
  const unexpected = Object.keys(values).find(
162
176
  (key) => values[key] !== undefined && !allowed.includes(key),
163
177
  );
164
- if (unexpected) {
165
- throw new Error(`${toolName} action "${action}" does not accept ${unexpected}`);
166
- }
178
+ if (unexpected) throw new Error(`${toolName} does not accept ${unexpected}`);
167
179
  }
168
180
 
169
181
  function assertRequiredString(