@gotgenes/pi-subagents 6.16.1 → 6.16.3
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 +17 -0
- package/docs/architecture/architecture.md +22 -18
- package/docs/plans/0146-narrow-ui-context.md +319 -0
- package/docs/retro/0146-narrow-ui-context.md +70 -0
- package/docs/retro/0148-split-agent-widget-rendering.md +39 -0
- package/package.json +1 -1
- package/src/index.ts +19 -13
- package/src/tools/get-result-tool.ts +11 -14
- package/src/tools/steer-tool.ts +12 -15
- package/src/ui/agent-config-editor.ts +63 -67
- package/src/ui/agent-creation-wizard.ts +50 -39
- package/src/ui/agent-menu.ts +118 -74
|
@@ -6,16 +6,13 @@ import { formatDuration, getDisplayName } from "../ui/display.js";
|
|
|
6
6
|
import { getSessionContextPercent } from "../usage.js";
|
|
7
7
|
import { formatLifetimeTokens, textResult } from "./helpers.js";
|
|
8
8
|
|
|
9
|
-
/** Narrow deps — only the methods this tool's execute callback calls. */
|
|
10
|
-
export interface GetResultDeps {
|
|
11
|
-
getRecord: (id: string) => AgentRecord | undefined;
|
|
12
|
-
cancelNudge: (key: string) => void;
|
|
13
|
-
getConversation: (session: AgentSession) => string | undefined;
|
|
14
|
-
registry: AgentConfigLookup;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
9
|
/** Create the get_subagent_result tool definition (without Pi SDK wrapper). */
|
|
18
|
-
export function createGetResultTool(
|
|
10
|
+
export function createGetResultTool(
|
|
11
|
+
getRecord: (id: string) => AgentRecord | undefined,
|
|
12
|
+
cancelNudge: (key: string) => void,
|
|
13
|
+
getConversation: (session: AgentSession) => string | undefined,
|
|
14
|
+
registry: AgentConfigLookup,
|
|
15
|
+
) {
|
|
19
16
|
return {
|
|
20
17
|
name: "get_subagent_result" as const,
|
|
21
18
|
label: "Get Agent Result",
|
|
@@ -45,7 +42,7 @@ export function createGetResultTool(deps: GetResultDeps) {
|
|
|
45
42
|
_onUpdate: unknown,
|
|
46
43
|
_ctx: unknown,
|
|
47
44
|
) => {
|
|
48
|
-
const record =
|
|
45
|
+
const record = getRecord(params.agent_id);
|
|
49
46
|
if (!record) {
|
|
50
47
|
return textResult(`Agent not found: "${params.agent_id}". It may have been cleaned up.`);
|
|
51
48
|
}
|
|
@@ -58,11 +55,11 @@ export function createGetResultTool(deps: GetResultDeps) {
|
|
|
58
55
|
// Pre-mark consumed BEFORE awaiting — onComplete fires inside .then() and
|
|
59
56
|
// always runs before this await resumes. Prevents a redundant notification.
|
|
60
57
|
record.notification?.markConsumed();
|
|
61
|
-
|
|
58
|
+
cancelNudge(params.agent_id);
|
|
62
59
|
await record.promise;
|
|
63
60
|
}
|
|
64
61
|
|
|
65
|
-
const displayName = getDisplayName(record.type,
|
|
62
|
+
const displayName = getDisplayName(record.type, registry);
|
|
66
63
|
const duration = formatDuration(record.startedAt, record.completedAt);
|
|
67
64
|
const tokens = formatLifetimeTokens(record);
|
|
68
65
|
const contextPercent = getSessionContextPercent(record.session);
|
|
@@ -88,12 +85,12 @@ export function createGetResultTool(deps: GetResultDeps) {
|
|
|
88
85
|
// Mark result as consumed — suppresses the completion notification
|
|
89
86
|
if (record.status !== "running" && record.status !== "queued") {
|
|
90
87
|
record.notification?.markConsumed();
|
|
91
|
-
|
|
88
|
+
cancelNudge(params.agent_id);
|
|
92
89
|
}
|
|
93
90
|
|
|
94
91
|
// Verbose: include full conversation
|
|
95
92
|
if (params.verbose && record.session) {
|
|
96
|
-
const conversation =
|
|
93
|
+
const conversation = getConversation(record.session);
|
|
97
94
|
if (conversation) {
|
|
98
95
|
output += `\n\n--- Agent Conversation ---\n${conversation}`;
|
|
99
96
|
}
|
package/src/tools/steer-tool.ts
CHANGED
|
@@ -4,17 +4,14 @@ import type { AgentRecord } from "../types.js";
|
|
|
4
4
|
import { getSessionContextPercent } from "../usage.js";
|
|
5
5
|
import { formatLifetimeTokens, textResult } from "./helpers.js";
|
|
6
6
|
|
|
7
|
-
/** Narrow deps — only the methods this tool's execute callback calls. */
|
|
8
|
-
export interface SteerToolDeps {
|
|
9
|
-
getRecord: (id: string) => AgentRecord | undefined;
|
|
10
|
-
emitEvent: (name: string, data: unknown) => void;
|
|
11
|
-
steerAgent: (session: AgentSession, message: string) => Promise<void>;
|
|
12
|
-
/** Buffer a steer for an agent whose session isn't ready yet. */
|
|
13
|
-
queueSteer: (id: string, message: string) => boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
7
|
/** Create the steer_subagent tool definition (without Pi SDK wrapper). */
|
|
17
|
-
export function createSteerTool(
|
|
8
|
+
export function createSteerTool(
|
|
9
|
+
getRecord: (id: string) => AgentRecord | undefined,
|
|
10
|
+
emitEvent: (name: string, data: unknown) => void,
|
|
11
|
+
steerAgent: (session: AgentSession, message: string) => Promise<void>,
|
|
12
|
+
/** Buffer a steer for an agent whose session isn't ready yet. */
|
|
13
|
+
queueSteer: (id: string, message: string) => boolean,
|
|
14
|
+
) {
|
|
18
15
|
return {
|
|
19
16
|
name: "steer_subagent" as const,
|
|
20
17
|
label: "Steer Agent",
|
|
@@ -38,7 +35,7 @@ export function createSteerTool(deps: SteerToolDeps) {
|
|
|
38
35
|
_onUpdate: unknown,
|
|
39
36
|
_ctx: unknown,
|
|
40
37
|
) => {
|
|
41
|
-
const record =
|
|
38
|
+
const record = getRecord(params.agent_id);
|
|
42
39
|
if (!record) {
|
|
43
40
|
return textResult(
|
|
44
41
|
`Agent not found: "${params.agent_id}". It may have been cleaned up.`,
|
|
@@ -52,16 +49,16 @@ export function createSteerTool(deps: SteerToolDeps) {
|
|
|
52
49
|
const session = record.session;
|
|
53
50
|
if (!session) {
|
|
54
51
|
// Session not ready yet — queue via manager for delivery once initialized
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
queueSteer(record.id, params.message);
|
|
53
|
+
emitEvent("subagents:steered", { id: record.id, message: params.message });
|
|
57
54
|
return textResult(
|
|
58
55
|
`Steering message queued for agent ${record.id}. It will be delivered once the session initializes.`,
|
|
59
56
|
);
|
|
60
57
|
}
|
|
61
58
|
|
|
62
59
|
try {
|
|
63
|
-
await
|
|
64
|
-
|
|
60
|
+
await steerAgent(session, params.message);
|
|
61
|
+
emitEvent("subagents:steered", { id: record.id, message: params.message });
|
|
65
62
|
const tokens = formatLifetimeTokens(record);
|
|
66
63
|
const contextPercent = getSessionContextPercent(session);
|
|
67
64
|
const stateParts: string[] = [];
|
|
@@ -7,35 +7,31 @@
|
|
|
7
7
|
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
|
|
10
|
-
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
11
10
|
import type { AgentTypeRegistry } from "../agent-types.js";
|
|
12
11
|
import type { AgentConfig } from "../types.js";
|
|
13
12
|
import type { AgentFileOps } from "./agent-file-ops.js";
|
|
14
|
-
|
|
15
|
-
// ---- Deps interface ----
|
|
16
|
-
|
|
17
|
-
export interface AgentConfigEditorDeps {
|
|
18
|
-
fileOps: AgentFileOps;
|
|
19
|
-
registry: AgentTypeRegistry;
|
|
20
|
-
personalAgentsDir: string;
|
|
21
|
-
projectAgentsDir: string;
|
|
22
|
-
}
|
|
13
|
+
import type { MenuUI } from "./agent-menu.js";
|
|
23
14
|
|
|
24
15
|
// ---- Factory ----
|
|
25
16
|
|
|
26
|
-
export function createAgentConfigEditor(
|
|
17
|
+
export function createAgentConfigEditor(
|
|
18
|
+
fileOps: AgentFileOps,
|
|
19
|
+
registry: AgentTypeRegistry,
|
|
20
|
+
personalAgentsDir: string,
|
|
21
|
+
projectAgentsDir: string,
|
|
22
|
+
) {
|
|
27
23
|
function agentDirs(): string[] {
|
|
28
|
-
return [
|
|
24
|
+
return [projectAgentsDir, personalAgentsDir];
|
|
29
25
|
}
|
|
30
26
|
|
|
31
|
-
async function showAgentDetail(
|
|
32
|
-
if (
|
|
33
|
-
|
|
27
|
+
async function showAgentDetail(ui: MenuUI, name: string) {
|
|
28
|
+
if (registry.resolveType(name) == null) {
|
|
29
|
+
ui.notify(`Agent config not found for "${name}".`, "warning");
|
|
34
30
|
return;
|
|
35
31
|
}
|
|
36
|
-
const cfg =
|
|
32
|
+
const cfg = registry.resolveAgentConfig(name);
|
|
37
33
|
|
|
38
|
-
const file =
|
|
34
|
+
const file = fileOps.findAgentFile(name, agentDirs());
|
|
39
35
|
const isDefault = cfg.isDefault === true;
|
|
40
36
|
const disabled = cfg.enabled === false;
|
|
41
37
|
|
|
@@ -52,64 +48,64 @@ export function createAgentConfigEditor(deps: AgentConfigEditorDeps) {
|
|
|
52
48
|
menuOptions = ["Edit", "Disable", "Delete", "Back"];
|
|
53
49
|
}
|
|
54
50
|
|
|
55
|
-
const choice = await
|
|
51
|
+
const choice = await ui.select(name, menuOptions);
|
|
56
52
|
if (!choice || choice === "Back") return;
|
|
57
53
|
|
|
58
54
|
if (choice === "Edit" && file) {
|
|
59
|
-
const content =
|
|
55
|
+
const content = fileOps.read(file);
|
|
60
56
|
if (content !== undefined) {
|
|
61
|
-
const edited = await
|
|
57
|
+
const edited = await ui.editor(`Edit ${name}`, content);
|
|
62
58
|
if (edited !== undefined && edited !== content) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
fileOps.write(file, edited);
|
|
60
|
+
registry.reload();
|
|
61
|
+
ui.notify(`Updated ${file}`, "info");
|
|
66
62
|
}
|
|
67
63
|
}
|
|
68
64
|
} else if (choice === "Delete") {
|
|
69
65
|
if (file) {
|
|
70
|
-
const confirmed = await
|
|
66
|
+
const confirmed = await ui.confirm(
|
|
71
67
|
"Delete agent",
|
|
72
68
|
`Delete ${name} (${file})?`,
|
|
73
69
|
);
|
|
74
70
|
if (confirmed) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
fileOps.remove(file);
|
|
72
|
+
registry.reload();
|
|
73
|
+
ui.notify(`Deleted ${file}`, "info");
|
|
78
74
|
}
|
|
79
75
|
}
|
|
80
76
|
} else if (choice === "Reset to default" && file) {
|
|
81
|
-
const confirmed = await
|
|
77
|
+
const confirmed = await ui.confirm(
|
|
82
78
|
"Reset to default",
|
|
83
79
|
`Delete override ${file} and restore embedded default?`,
|
|
84
80
|
);
|
|
85
81
|
if (confirmed) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
fileOps.remove(file);
|
|
83
|
+
registry.reload();
|
|
84
|
+
ui.notify(`Restored default ${name}`, "info");
|
|
89
85
|
}
|
|
90
86
|
} else if (choice.startsWith("Eject")) {
|
|
91
|
-
await ejectAgent(
|
|
87
|
+
await ejectAgent(ui, name, cfg);
|
|
92
88
|
} else if (choice === "Disable") {
|
|
93
|
-
await disableAgent(
|
|
89
|
+
await disableAgent(ui, name);
|
|
94
90
|
} else if (choice === "Enable") {
|
|
95
|
-
await enableAgent(
|
|
91
|
+
await enableAgent(ui, name);
|
|
96
92
|
}
|
|
97
93
|
}
|
|
98
94
|
|
|
99
|
-
async function ejectAgent(
|
|
100
|
-
const location = await
|
|
95
|
+
async function ejectAgent(ui: MenuUI, name: string, cfg: AgentConfig) {
|
|
96
|
+
const location = await ui.select("Choose location", [
|
|
101
97
|
"Project (.pi/agents/)",
|
|
102
|
-
`Personal (${
|
|
98
|
+
`Personal (${personalAgentsDir})`,
|
|
103
99
|
]);
|
|
104
100
|
if (!location) return;
|
|
105
101
|
|
|
106
102
|
const targetDir = location.startsWith("Project")
|
|
107
|
-
?
|
|
108
|
-
:
|
|
103
|
+
? projectAgentsDir
|
|
104
|
+
: personalAgentsDir;
|
|
109
105
|
|
|
110
106
|
const targetPath = join(targetDir, `${name}.md`);
|
|
111
|
-
if (
|
|
112
|
-
const overwrite = await
|
|
107
|
+
if (fileOps.exists(targetPath)) {
|
|
108
|
+
const overwrite = await ui.confirm(
|
|
113
109
|
"Overwrite",
|
|
114
110
|
`${targetPath} already exists. Overwrite?`,
|
|
115
111
|
);
|
|
@@ -140,61 +136,61 @@ export function createAgentConfigEditor(deps: AgentConfigEditorDeps) {
|
|
|
140
136
|
|
|
141
137
|
const content = `---\n${fmFields.join("\n")}\n---\n\n${cfg.systemPrompt}\n`;
|
|
142
138
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
139
|
+
fileOps.write(targetPath, content);
|
|
140
|
+
registry.reload();
|
|
141
|
+
ui.notify(`Ejected ${name} to ${targetPath}`, "info");
|
|
146
142
|
}
|
|
147
143
|
|
|
148
|
-
async function disableAgent(
|
|
149
|
-
const file =
|
|
144
|
+
async function disableAgent(ui: MenuUI, name: string) {
|
|
145
|
+
const file = fileOps.findAgentFile(name, agentDirs());
|
|
150
146
|
if (file) {
|
|
151
|
-
const content =
|
|
147
|
+
const content = fileOps.read(file);
|
|
152
148
|
if (content?.includes("\nenabled: false\n")) {
|
|
153
|
-
|
|
149
|
+
ui.notify(`${name} is already disabled.`, "info");
|
|
154
150
|
return;
|
|
155
151
|
}
|
|
156
152
|
if (content) {
|
|
157
153
|
const updated = content.replace(/^---\n/, "---\nenabled: false\n");
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
154
|
+
fileOps.write(file, updated);
|
|
155
|
+
registry.reload();
|
|
156
|
+
ui.notify(`Disabled ${name} (${file})`, "info");
|
|
161
157
|
}
|
|
162
158
|
return;
|
|
163
159
|
}
|
|
164
160
|
|
|
165
|
-
const location = await
|
|
161
|
+
const location = await ui.select("Choose location", [
|
|
166
162
|
"Project (.pi/agents/)",
|
|
167
|
-
`Personal (${
|
|
163
|
+
`Personal (${personalAgentsDir})`,
|
|
168
164
|
]);
|
|
169
165
|
if (!location) return;
|
|
170
166
|
|
|
171
167
|
const targetDir = location.startsWith("Project")
|
|
172
|
-
?
|
|
173
|
-
:
|
|
168
|
+
? projectAgentsDir
|
|
169
|
+
: personalAgentsDir;
|
|
174
170
|
|
|
175
171
|
const targetPath = join(targetDir, `${name}.md`);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
172
|
+
fileOps.write(targetPath, "---\nenabled: false\n---\n");
|
|
173
|
+
registry.reload();
|
|
174
|
+
ui.notify(`Disabled ${name} (${targetPath})`, "info");
|
|
179
175
|
}
|
|
180
176
|
|
|
181
|
-
async function enableAgent(
|
|
182
|
-
const file =
|
|
177
|
+
async function enableAgent(ui: MenuUI, name: string) {
|
|
178
|
+
const file = fileOps.findAgentFile(name, agentDirs());
|
|
183
179
|
if (!file) return;
|
|
184
180
|
|
|
185
|
-
const content =
|
|
181
|
+
const content = fileOps.read(file);
|
|
186
182
|
if (!content) return;
|
|
187
183
|
|
|
188
184
|
const updated = content.replace(/^(---\n)enabled: false\n/, "$1");
|
|
189
185
|
|
|
190
186
|
if (updated.trim() === "---\n---" || updated.trim() === "---\n---\n") {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
187
|
+
fileOps.remove(file);
|
|
188
|
+
registry.reload();
|
|
189
|
+
ui.notify(`Enabled ${name} (removed ${file})`, "info");
|
|
194
190
|
} else {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
191
|
+
fileOps.write(file, updated);
|
|
192
|
+
registry.reload();
|
|
193
|
+
ui.notify(`Enabled ${name} (${file})`, "info");
|
|
198
194
|
}
|
|
199
195
|
}
|
|
200
196
|
|
|
@@ -7,17 +7,18 @@
|
|
|
7
7
|
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
|
|
10
|
-
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
11
10
|
import { BUILTIN_TOOL_NAMES } from "../agent-types.js";
|
|
11
|
+
import type { ParentSnapshot } from "../parent-snapshot.js";
|
|
12
12
|
import type { AgentRecord } from "../types.js";
|
|
13
13
|
import type { AgentFileOps } from "./agent-file-ops.js";
|
|
14
|
+
import type { MenuUI } from "./agent-menu.js";
|
|
14
15
|
|
|
15
16
|
// ---- Deps interface ----
|
|
16
17
|
|
|
17
18
|
/** Narrow manager interface for agent spawning (generate wizard). */
|
|
18
19
|
export interface WizardManager {
|
|
19
20
|
spawnAndWait: (
|
|
20
|
-
|
|
21
|
+
parentSnapshot: ParentSnapshot,
|
|
21
22
|
type: string,
|
|
22
23
|
prompt: string,
|
|
23
24
|
opts: { description: string; maxTurns: number },
|
|
@@ -39,50 +40,60 @@ export interface AgentCreationWizardDeps {
|
|
|
39
40
|
|
|
40
41
|
// ---- Factory ----
|
|
41
42
|
|
|
42
|
-
export function createAgentCreationWizard(
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
export function createAgentCreationWizard({
|
|
44
|
+
fileOps,
|
|
45
|
+
manager,
|
|
46
|
+
registry,
|
|
47
|
+
personalAgentsDir,
|
|
48
|
+
projectAgentsDir,
|
|
49
|
+
}: AgentCreationWizardDeps) {
|
|
50
|
+
async function showCreateWizard(ui: MenuUI, parentSnapshot: ParentSnapshot) {
|
|
51
|
+
const location = await ui.select("Choose location", [
|
|
45
52
|
"Project (.pi/agents/)",
|
|
46
|
-
`Personal (${
|
|
53
|
+
`Personal (${personalAgentsDir})`,
|
|
47
54
|
]);
|
|
48
55
|
if (!location) return;
|
|
49
56
|
|
|
50
57
|
const targetDir = location.startsWith("Project")
|
|
51
|
-
?
|
|
52
|
-
:
|
|
58
|
+
? projectAgentsDir
|
|
59
|
+
: personalAgentsDir;
|
|
53
60
|
|
|
54
|
-
const method = await
|
|
61
|
+
const method = await ui.select("Creation method", [
|
|
55
62
|
"Generate with Claude (recommended)",
|
|
56
63
|
"Manual configuration",
|
|
57
64
|
]);
|
|
58
65
|
if (!method) return;
|
|
59
66
|
|
|
60
67
|
if (method.startsWith("Generate")) {
|
|
61
|
-
await showGenerateWizard(
|
|
68
|
+
await showGenerateWizard(ui, parentSnapshot, targetDir);
|
|
62
69
|
} else {
|
|
63
|
-
await showManualWizard(
|
|
70
|
+
await showManualWizard(ui, targetDir);
|
|
64
71
|
}
|
|
65
72
|
}
|
|
66
73
|
|
|
67
|
-
async function showGenerateWizard(
|
|
68
|
-
|
|
74
|
+
async function showGenerateWizard(
|
|
75
|
+
ui: MenuUI,
|
|
76
|
+
parentSnapshot: ParentSnapshot,
|
|
77
|
+
targetDir: string,
|
|
78
|
+
) {
|
|
79
|
+
const description = await ui.input("Describe what this agent should do");
|
|
69
80
|
if (!description) return;
|
|
70
81
|
|
|
71
|
-
const name = await
|
|
82
|
+
const name = await ui.input("Agent name (filename, no spaces)");
|
|
72
83
|
if (!name) return;
|
|
73
84
|
|
|
74
|
-
|
|
85
|
+
fileOps.ensureDir(targetDir);
|
|
75
86
|
|
|
76
87
|
const targetPath = join(targetDir, `${name}.md`);
|
|
77
|
-
if (
|
|
78
|
-
const overwrite = await
|
|
88
|
+
if (fileOps.exists(targetPath)) {
|
|
89
|
+
const overwrite = await ui.confirm(
|
|
79
90
|
"Overwrite",
|
|
80
91
|
`${targetPath} already exists. Overwrite?`,
|
|
81
92
|
);
|
|
82
93
|
if (!overwrite) return;
|
|
83
94
|
}
|
|
84
95
|
|
|
85
|
-
|
|
96
|
+
ui.notify("Generating agent definition...", "info");
|
|
86
97
|
|
|
87
98
|
const generatePrompt = `Create a custom pi sub-agent definition file based on this description: "${description}"
|
|
88
99
|
|
|
@@ -122,8 +133,8 @@ Guidelines for choosing settings:
|
|
|
122
133
|
|
|
123
134
|
Write the file using the write tool. Only write the file, nothing else.`;
|
|
124
135
|
|
|
125
|
-
const record = await
|
|
126
|
-
|
|
136
|
+
const record = await manager.spawnAndWait(
|
|
137
|
+
parentSnapshot,
|
|
127
138
|
"general-purpose",
|
|
128
139
|
generatePrompt,
|
|
129
140
|
{
|
|
@@ -133,30 +144,30 @@ Write the file using the write tool. Only write the file, nothing else.`;
|
|
|
133
144
|
);
|
|
134
145
|
|
|
135
146
|
if (record.status === "error") {
|
|
136
|
-
|
|
147
|
+
ui.notify(`Generation failed: ${record.error}`, "warning");
|
|
137
148
|
return;
|
|
138
149
|
}
|
|
139
150
|
|
|
140
|
-
|
|
151
|
+
registry.reload();
|
|
141
152
|
|
|
142
|
-
if (
|
|
143
|
-
|
|
153
|
+
if (fileOps.exists(targetPath)) {
|
|
154
|
+
ui.notify(`Created ${targetPath}`, "info");
|
|
144
155
|
} else {
|
|
145
|
-
|
|
156
|
+
ui.notify(
|
|
146
157
|
"Agent generation completed but file was not created. Check the agent output.",
|
|
147
158
|
"warning",
|
|
148
159
|
);
|
|
149
160
|
}
|
|
150
161
|
}
|
|
151
162
|
|
|
152
|
-
async function showManualWizard(
|
|
153
|
-
const name = await
|
|
163
|
+
async function showManualWizard(ui: MenuUI, targetDir: string) {
|
|
164
|
+
const name = await ui.input("Agent name (filename, no spaces)");
|
|
154
165
|
if (!name) return;
|
|
155
166
|
|
|
156
|
-
const description = await
|
|
167
|
+
const description = await ui.input("Description (one line)");
|
|
157
168
|
if (!description) return;
|
|
158
169
|
|
|
159
|
-
const toolChoice = await
|
|
170
|
+
const toolChoice = await ui.select("Tools", [
|
|
160
171
|
"all",
|
|
161
172
|
"none",
|
|
162
173
|
"read-only (read, bash, grep, find, ls)",
|
|
@@ -172,7 +183,7 @@ Write the file using the write tool. Only write the file, nothing else.`;
|
|
|
172
183
|
} else if (toolChoice.startsWith("read-only")) {
|
|
173
184
|
tools = "read, bash, grep, find, ls";
|
|
174
185
|
} else {
|
|
175
|
-
const customTools = await
|
|
186
|
+
const customTools = await ui.input(
|
|
176
187
|
"Tools (comma-separated)",
|
|
177
188
|
BUILTIN_TOOL_NAMES.join(", "),
|
|
178
189
|
);
|
|
@@ -180,7 +191,7 @@ Write the file using the write tool. Only write the file, nothing else.`;
|
|
|
180
191
|
tools = customTools;
|
|
181
192
|
}
|
|
182
193
|
|
|
183
|
-
const modelChoice = await
|
|
194
|
+
const modelChoice = await ui.select("Model", [
|
|
184
195
|
"inherit (parent model)",
|
|
185
196
|
"haiku",
|
|
186
197
|
"sonnet",
|
|
@@ -197,11 +208,11 @@ Write the file using the write tool. Only write the file, nothing else.`;
|
|
|
197
208
|
else if (modelChoice === "opus")
|
|
198
209
|
modelLine = "\nmodel: anthropic/claude-opus-4-6";
|
|
199
210
|
else if (modelChoice === "custom...") {
|
|
200
|
-
const customModel = await
|
|
211
|
+
const customModel = await ui.input("Model (provider/modelId)");
|
|
201
212
|
if (customModel) modelLine = `\nmodel: ${customModel}`;
|
|
202
213
|
}
|
|
203
214
|
|
|
204
|
-
const thinkingChoice = await
|
|
215
|
+
const thinkingChoice = await ui.select("Thinking level", [
|
|
205
216
|
"inherit",
|
|
206
217
|
"off",
|
|
207
218
|
"minimal",
|
|
@@ -215,7 +226,7 @@ Write the file using the write tool. Only write the file, nothing else.`;
|
|
|
215
226
|
let thinkingLine = "";
|
|
216
227
|
if (thinkingChoice !== "inherit") thinkingLine = `\nthinking: ${thinkingChoice}`;
|
|
217
228
|
|
|
218
|
-
const systemPrompt = await
|
|
229
|
+
const systemPrompt = await ui.editor("System prompt", "");
|
|
219
230
|
if (systemPrompt === undefined) return;
|
|
220
231
|
|
|
221
232
|
const content = `---
|
|
@@ -229,17 +240,17 @@ ${systemPrompt}
|
|
|
229
240
|
|
|
230
241
|
const targetPath = join(targetDir, `${name}.md`);
|
|
231
242
|
|
|
232
|
-
if (
|
|
233
|
-
const overwrite = await
|
|
243
|
+
if (fileOps.exists(targetPath)) {
|
|
244
|
+
const overwrite = await ui.confirm(
|
|
234
245
|
"Overwrite",
|
|
235
246
|
`${targetPath} already exists. Overwrite?`,
|
|
236
247
|
);
|
|
237
248
|
if (!overwrite) return;
|
|
238
249
|
}
|
|
239
250
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
251
|
+
fileOps.write(targetPath, content);
|
|
252
|
+
registry.reload();
|
|
253
|
+
ui.notify(`Created ${targetPath}`, "info");
|
|
243
254
|
}
|
|
244
255
|
|
|
245
256
|
return { showCreateWizard };
|