@leing2021/super-pi 0.22.1 → 0.23.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/README.md +66 -293
- package/extensions/ce-core/index.ts +276 -48
- package/extensions/ce-core/tools/parallel-subagent.ts +3 -3
- package/extensions/ce-core/tools/subagent.ts +2 -2
- package/package.json +4 -20
- package/rules/common/naming.md +154 -0
- package/skills/01-brainstorm/SKILL.md +42 -79
- package/skills/01-brainstorm/references/ce-brainstorm-mode.md +47 -0
- package/skills/02-plan/SKILL.md +43 -61
- package/skills/02-plan/references/solution-search.md +46 -0
- package/skills/03-work/SKILL.md +34 -52
- package/skills/03-work/references/completion-report.md +51 -0
- package/skills/04-review/SKILL.md +44 -51
- package/skills/04-review/references/solution-search.md +46 -0
- package/skills/06-next/SKILL.md +35 -20
- package/skills/06-next/references/recommendation-logic.md +47 -2
- package/skills/07-worktree/SKILL.md +21 -20
- package/skills/08-help/SKILL.md +46 -0
- package/skills/08-help/references/workflow-sequence.md +135 -0
- package/skills/references/language-detection.md +44 -0
- package/LICENSE +0 -21
- package/THIRD-PARTY-NOTICES.md +0 -35
- package/extensions/subagent/__tests__/async-job-tracker.test.ts +0 -21
- package/extensions/subagent/__tests__/execution-activity.test.ts +0 -15
- package/extensions/subagent/__tests__/parallel-render-stress.test.ts +0 -82
- package/extensions/subagent/__tests__/render-dedup.test.ts +0 -98
- package/extensions/subagent/__tests__/render-widget.test.ts +0 -28
- package/extensions/subagent/__tests__/throttle.test.ts +0 -90
- package/extensions/subagent/agent-management.ts +0 -596
- package/extensions/subagent/agent-manager-chain-detail.ts +0 -163
- package/extensions/subagent/agent-manager-detail.ts +0 -232
- package/extensions/subagent/agent-manager-edit.ts +0 -391
- package/extensions/subagent/agent-manager-list.ts +0 -279
- package/extensions/subagent/agent-manager-parallel.ts +0 -305
- package/extensions/subagent/agent-manager.ts +0 -706
- package/extensions/subagent/agent-scope.ts +0 -9
- package/extensions/subagent/agent-selection.ts +0 -26
- package/extensions/subagent/agent-serializer.ts +0 -124
- package/extensions/subagent/agent-templates.ts +0 -63
- package/extensions/subagent/agents/context-builder.md +0 -37
- package/extensions/subagent/agents/delegate.md +0 -9
- package/extensions/subagent/agents/oracle.md +0 -73
- package/extensions/subagent/agents/planner.md +0 -52
- package/extensions/subagent/agents/researcher.md +0 -50
- package/extensions/subagent/agents/reviewer.md +0 -38
- package/extensions/subagent/agents/scout.md +0 -48
- package/extensions/subagent/agents/worker.md +0 -52
- package/extensions/subagent/agents.ts +0 -762
- package/extensions/subagent/artifacts.ts +0 -101
- package/extensions/subagent/async-execution.ts +0 -521
- package/extensions/subagent/async-job-tracker.ts +0 -240
- package/extensions/subagent/async-status.ts +0 -242
- package/extensions/subagent/chain-clarify.ts +0 -1365
- package/extensions/subagent/chain-execution.ts +0 -854
- package/extensions/subagent/chain-serializer.ts +0 -127
- package/extensions/subagent/completion-dedupe.ts +0 -66
- package/extensions/subagent/doctor.ts +0 -201
- package/extensions/subagent/execution.ts +0 -748
- package/extensions/subagent/file-coalescer.ts +0 -43
- package/extensions/subagent/fork-context.ts +0 -64
- package/extensions/subagent/formatters.ts +0 -123
- package/extensions/subagent/frontmatter.ts +0 -32
- package/extensions/subagent/index.ts +0 -586
- package/extensions/subagent/intercom-bridge.ts +0 -241
- package/extensions/subagent/jsonl-writer.ts +0 -84
- package/extensions/subagent/model-fallback.ts +0 -109
- package/extensions/subagent/notify.ts +0 -111
- package/extensions/subagent/parallel-utils.ts +0 -109
- package/extensions/subagent/pi-args.ts +0 -139
- package/extensions/subagent/pi-spawn.ts +0 -101
- package/extensions/subagent/post-exit-stdio-guard.ts +0 -88
- package/extensions/subagent/prompt-template-bridge.ts +0 -400
- package/extensions/subagent/prompts/gather-context-and-clarify.md +0 -13
- package/extensions/subagent/prompts/parallel-cleanup.md +0 -42
- package/extensions/subagent/prompts/parallel-research.md +0 -50
- package/extensions/subagent/prompts/parallel-review.md +0 -40
- package/extensions/subagent/render-helpers.ts +0 -83
- package/extensions/subagent/render.ts +0 -908
- package/extensions/subagent/result-intercom.ts +0 -238
- package/extensions/subagent/result-watcher.ts +0 -172
- package/extensions/subagent/run-history.ts +0 -58
- package/extensions/subagent/run-status.ts +0 -137
- package/extensions/subagent/schemas.ts +0 -165
- package/extensions/subagent/session-tokens.ts +0 -51
- package/extensions/subagent/settings.ts +0 -368
- package/extensions/subagent/single-output.ts +0 -98
- package/extensions/subagent/skills.ts +0 -627
- package/extensions/subagent/slash-bridge.ts +0 -177
- package/extensions/subagent/slash-commands.ts +0 -304
- package/extensions/subagent/slash-live-state.ts +0 -295
- package/extensions/subagent/subagent-control.ts +0 -151
- package/extensions/subagent/subagent-executor.ts +0 -1915
- package/extensions/subagent/subagent-prompt-runtime.ts +0 -76
- package/extensions/subagent/subagent-runner.ts +0 -1471
- package/extensions/subagent/subagents-status.ts +0 -473
- package/extensions/subagent/text-editor.ts +0 -273
- package/extensions/subagent/throttle.ts +0 -77
- package/extensions/subagent/top-level-async.ts +0 -16
- package/extensions/subagent/types.ts +0 -624
- package/extensions/subagent/utils.ts +0 -457
- package/extensions/subagent/worktree.ts +0 -580
- package/extensions/super-pi-extension/agents/ce-oracle.md +0 -109
- package/extensions/super-pi-extension/agents/ce-planner.md +0 -116
- package/extensions/super-pi-extension/agents/ce-reviewer.md +0 -99
- package/extensions/super-pi-extension/agents/ce-scout.md +0 -80
- package/extensions/super-pi-extension/agents/ce-worker.md +0 -78
- package/extensions/super-pi-extension/chains/ce-parallel-review.chain.md +0 -34
- package/extensions/super-pi-extension/chains/ce-review-only.chain.md +0 -12
- package/extensions/super-pi-extension/chains/ce-standard.chain.md +0 -28
- package/extensions/super-pi-extension/index.ts +0 -129
- package/extensions/super-pi-extension/model-sync.ts +0 -204
- package/skills/08-status/SKILL.md +0 -41
- package/skills/08-status/references/artifact-locations.md +0 -10
- package/skills/09-help/SKILL.md +0 -37
- package/skills/09-help/references/workflow-sequence.md +0 -9
- package/skills/10-rules/SKILL.md +0 -76
- package/skills/pi-subagents/SKILL.md +0 -566
|
@@ -1,596 +0,0 @@
|
|
|
1
|
-
// SPDX-FileCopyrightText: 2025 Nico Bailon
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
|
-
// Source: https://github.com/nicobailon/pi-subagents
|
|
4
|
-
import * as fs from "node:fs";
|
|
5
|
-
import * as path from "node:path";
|
|
6
|
-
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
|
7
|
-
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
8
|
-
import {
|
|
9
|
-
type AgentConfig,
|
|
10
|
-
type AgentScope,
|
|
11
|
-
type AgentSource,
|
|
12
|
-
type ChainConfig,
|
|
13
|
-
type ChainStepConfig,
|
|
14
|
-
defaultInheritProjectContext,
|
|
15
|
-
defaultInheritSkills,
|
|
16
|
-
defaultSystemPromptMode,
|
|
17
|
-
discoverAgentsAll,
|
|
18
|
-
} from "./agents.ts";
|
|
19
|
-
import { serializeAgent } from "./agent-serializer.ts";
|
|
20
|
-
import { serializeChain } from "./chain-serializer.ts";
|
|
21
|
-
import { discoverAvailableSkills } from "./skills.ts";
|
|
22
|
-
import type { Details } from "./types.ts";
|
|
23
|
-
|
|
24
|
-
type ManagementAction = "list" | "get" | "create" | "update" | "delete";
|
|
25
|
-
type ManagementScope = "user" | "project";
|
|
26
|
-
type ManagementContext = Pick<ExtensionContext, "cwd" | "modelRegistry">;
|
|
27
|
-
|
|
28
|
-
interface ManagementParams {
|
|
29
|
-
action?: string;
|
|
30
|
-
agent?: string;
|
|
31
|
-
chainName?: string;
|
|
32
|
-
agentScope?: string;
|
|
33
|
-
config?: unknown;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function result(text: string, isError = false): AgentToolResult<Details> {
|
|
37
|
-
return { content: [{ type: "text", text }], isError, details: { mode: "management", results: [] } };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function parseCsv(value: string): string[] {
|
|
41
|
-
return [...new Set(value.split(",").map((v) => v.trim()).filter(Boolean))];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function configObject(config: unknown): { value?: Record<string, unknown>; error?: string } {
|
|
45
|
-
let val = config;
|
|
46
|
-
if (typeof val === "string") {
|
|
47
|
-
try {
|
|
48
|
-
val = JSON.parse(val);
|
|
49
|
-
} catch (error) {
|
|
50
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
51
|
-
return { error: `config must be valid JSON: ${message}` };
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
if (!val || typeof val !== "object" || Array.isArray(val)) return {};
|
|
55
|
-
return { value: val as Record<string, unknown> };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function hasKey(obj: Record<string, unknown>, key: string): boolean {
|
|
59
|
-
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function asDisambiguationScope(scope: unknown): ManagementScope | undefined {
|
|
63
|
-
if (scope === "user" || scope === "project") return scope;
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function normalizeListScope(scope: unknown): AgentScope | undefined {
|
|
68
|
-
if (scope === undefined) return "both";
|
|
69
|
-
if (scope === "user" || scope === "project" || scope === "both") return scope;
|
|
70
|
-
return undefined;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function sanitizeName(name: string): string {
|
|
74
|
-
return name.toLowerCase().trim().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function allAgents(d: { builtin: AgentConfig[]; user: AgentConfig[]; project: AgentConfig[] }): AgentConfig[] {
|
|
78
|
-
return [...d.builtin, ...d.user, ...d.project];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function availableNames(cwd: string, kind: "agent" | "chain"): string[] {
|
|
82
|
-
const d = discoverAgentsAll(cwd);
|
|
83
|
-
const items = kind === "agent" ? allAgents(d) : d.chains;
|
|
84
|
-
return [...new Set(items.map((x) => x.name))].sort((a, b) => a.localeCompare(b));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function findAgents(name: string, cwd: string, scope: AgentScope = "both"): AgentConfig[] {
|
|
88
|
-
const d = discoverAgentsAll(cwd);
|
|
89
|
-
const raw = name.trim();
|
|
90
|
-
const sanitized = sanitizeName(raw);
|
|
91
|
-
return allAgents(d)
|
|
92
|
-
.filter((a) => (scope === "both" || a.source === scope) && (a.name === raw || a.name === sanitized))
|
|
93
|
-
.sort((a, b) => a.source.localeCompare(b.source));
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function findChains(name: string, cwd: string, scope: AgentScope = "both"): ChainConfig[] {
|
|
97
|
-
const raw = name.trim();
|
|
98
|
-
const sanitized = sanitizeName(raw);
|
|
99
|
-
return discoverAgentsAll(cwd).chains
|
|
100
|
-
.filter((c) => (scope === "both" || c.source === scope) && (c.name === raw || c.name === sanitized))
|
|
101
|
-
.sort((a, b) => a.source.localeCompare(b.source));
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function nameExistsInScope(cwd: string, scope: ManagementScope, name: string, excludePath?: string): boolean {
|
|
105
|
-
const d = discoverAgentsAll(cwd);
|
|
106
|
-
for (const a of scope === "user" ? d.user : d.project) {
|
|
107
|
-
if (a.name === name && a.filePath !== excludePath) return true;
|
|
108
|
-
}
|
|
109
|
-
for (const c of d.chains) {
|
|
110
|
-
if (c.source === scope && c.name === name && c.filePath !== excludePath) return true;
|
|
111
|
-
}
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function unknownChainAgents(cwd: string, steps: ChainStepConfig[]): string[] {
|
|
116
|
-
const d = discoverAgentsAll(cwd);
|
|
117
|
-
const known = new Set(allAgents(d).map((a) => a.name));
|
|
118
|
-
return [...new Set(steps.map((s) => s.agent).filter((a) => !known.has(a)))].sort((a, b) => a.localeCompare(b));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function chainStepWarnings(ctx: ManagementContext, steps: ChainStepConfig[]): string[] {
|
|
122
|
-
const warnings: string[] = [];
|
|
123
|
-
const available = new Set(discoverAvailableSkills(ctx.cwd).map((s) => s.name));
|
|
124
|
-
for (let i = 0; i < steps.length; i++) {
|
|
125
|
-
const s = steps[i]!;
|
|
126
|
-
if (s.model) {
|
|
127
|
-
const found = ctx.modelRegistry.getAvailable().some((m) => `${m.provider}/${m.id}` === s.model || m.id === s.model);
|
|
128
|
-
if (!found) warnings.push(`Warning: step ${i + 1} (${s.agent}): model '${s.model}' is not in the current model registry.`);
|
|
129
|
-
}
|
|
130
|
-
if (Array.isArray(s.skills) && s.skills.length > 0) {
|
|
131
|
-
const missing = s.skills.filter((sk) => !available.has(sk));
|
|
132
|
-
if (missing.length) warnings.push(`Warning: step ${i + 1} (${s.agent}): skills not found: ${missing.join(", ")}.`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return warnings;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function modelWarning(ctx: ManagementContext, model: string | undefined): string | undefined {
|
|
139
|
-
if (!model) return undefined;
|
|
140
|
-
const found = ctx.modelRegistry.getAvailable().some((m) => `${m.provider}/${m.id}` === model || m.id === model);
|
|
141
|
-
return found ? undefined : `Warning: model '${model}' is not in the current model registry.`;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function fallbackModelsWarning(ctx: ManagementContext, fallbackModels: string[] | undefined): string | undefined {
|
|
145
|
-
if (!fallbackModels || fallbackModels.length === 0) return undefined;
|
|
146
|
-
const available = new Set(ctx.modelRegistry.getAvailable().flatMap((m) => [`${m.provider}/${m.id}`, m.id]));
|
|
147
|
-
const missing = fallbackModels.filter((model) => !available.has(model));
|
|
148
|
-
return missing.length ? `Warning: fallback models not in the current model registry: ${missing.join(", ")}.` : undefined;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function skillsWarning(cwd: string, skills: string[] | undefined): string | undefined {
|
|
152
|
-
if (!skills || skills.length === 0) return undefined;
|
|
153
|
-
const available = new Set(discoverAvailableSkills(cwd).map((s) => s.name));
|
|
154
|
-
const missing = skills.filter((s) => !available.has(s));
|
|
155
|
-
return missing.length ? `Warning: skills not found: ${missing.join(", ")}.` : undefined;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function parseStepList(raw: unknown): { steps?: ChainStepConfig[]; error?: string } {
|
|
159
|
-
if (!Array.isArray(raw)) return { error: "config.steps must be an array." };
|
|
160
|
-
if (raw.length === 0) return { error: "config.steps must include at least one step." };
|
|
161
|
-
const steps: ChainStepConfig[] = [];
|
|
162
|
-
for (let i = 0; i < raw.length; i++) {
|
|
163
|
-
const item = raw[i];
|
|
164
|
-
if (!item || typeof item !== "object" || Array.isArray(item)) return { error: `config.steps[${i}] must be an object.` };
|
|
165
|
-
const s = item as Record<string, unknown>;
|
|
166
|
-
if (typeof s.agent !== "string" || !s.agent.trim()) return { error: `config.steps[${i}].agent must be a non-empty string.` };
|
|
167
|
-
const step: ChainStepConfig = { agent: s.agent.trim(), task: typeof s.task === "string" ? s.task : "" };
|
|
168
|
-
if (hasKey(s, "output")) {
|
|
169
|
-
if (s.output === false) step.output = false;
|
|
170
|
-
else if (typeof s.output === "string") step.output = s.output;
|
|
171
|
-
else return { error: `config.steps[${i}].output must be a string or false.` };
|
|
172
|
-
}
|
|
173
|
-
if (hasKey(s, "reads")) {
|
|
174
|
-
if (s.reads === false) step.reads = false;
|
|
175
|
-
else if (Array.isArray(s.reads)) step.reads = s.reads.filter((v): v is string => typeof v === "string").map((v) => v.trim()).filter(Boolean);
|
|
176
|
-
else return { error: `config.steps[${i}].reads must be an array or false.` };
|
|
177
|
-
}
|
|
178
|
-
if (hasKey(s, "model")) {
|
|
179
|
-
if (typeof s.model === "string") step.model = s.model;
|
|
180
|
-
else return { error: `config.steps[${i}].model must be a string.` };
|
|
181
|
-
}
|
|
182
|
-
if (hasKey(s, "skills")) {
|
|
183
|
-
if (s.skills === false) step.skills = false;
|
|
184
|
-
else if (Array.isArray(s.skills)) step.skills = s.skills.filter((v): v is string => typeof v === "string").map((v) => v.trim()).filter(Boolean);
|
|
185
|
-
else return { error: `config.steps[${i}].skills must be an array or false.` };
|
|
186
|
-
}
|
|
187
|
-
if (hasKey(s, "progress")) {
|
|
188
|
-
if (typeof s.progress === "boolean") step.progress = s.progress;
|
|
189
|
-
else return { error: `config.steps[${i}].progress must be a boolean.` };
|
|
190
|
-
}
|
|
191
|
-
steps.push(step);
|
|
192
|
-
}
|
|
193
|
-
return { steps };
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function parseTools(raw: string): { tools?: string[]; mcpDirectTools?: string[] } {
|
|
197
|
-
const tools: string[] = [];
|
|
198
|
-
const mcpDirectTools: string[] = [];
|
|
199
|
-
for (const item of parseCsv(raw)) {
|
|
200
|
-
if (item.startsWith("mcp:")) {
|
|
201
|
-
const direct = item.slice(4).trim();
|
|
202
|
-
if (direct) mcpDirectTools.push(direct);
|
|
203
|
-
} else tools.push(item);
|
|
204
|
-
}
|
|
205
|
-
return { tools: tools.length ? tools : undefined, mcpDirectTools: mcpDirectTools.length ? mcpDirectTools : undefined };
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): string | undefined {
|
|
209
|
-
if (hasKey(cfg, "systemPrompt")) {
|
|
210
|
-
if (cfg.systemPrompt === false || cfg.systemPrompt === "") target.systemPrompt = "";
|
|
211
|
-
else if (typeof cfg.systemPrompt === "string") target.systemPrompt = cfg.systemPrompt;
|
|
212
|
-
else return "config.systemPrompt must be a string or false when provided.";
|
|
213
|
-
}
|
|
214
|
-
if (hasKey(cfg, "model")) {
|
|
215
|
-
if (cfg.model === false || cfg.model === "") target.model = undefined;
|
|
216
|
-
else if (typeof cfg.model === "string") target.model = cfg.model.trim() || undefined;
|
|
217
|
-
else return "config.model must be a string or false when provided.";
|
|
218
|
-
}
|
|
219
|
-
if (hasKey(cfg, "fallbackModels")) {
|
|
220
|
-
if (cfg.fallbackModels === false || cfg.fallbackModels === "") target.fallbackModels = undefined;
|
|
221
|
-
else if (typeof cfg.fallbackModels === "string") {
|
|
222
|
-
const models = parseCsv(cfg.fallbackModels);
|
|
223
|
-
target.fallbackModels = models.length ? models : undefined;
|
|
224
|
-
} else if (Array.isArray(cfg.fallbackModels)) {
|
|
225
|
-
const models = cfg.fallbackModels
|
|
226
|
-
.filter((value): value is string => typeof value === "string")
|
|
227
|
-
.map((value) => value.trim())
|
|
228
|
-
.filter(Boolean);
|
|
229
|
-
target.fallbackModels = models.length ? [...new Set(models)] : undefined;
|
|
230
|
-
} else return "config.fallbackModels must be a comma-separated string, string array, or false when provided.";
|
|
231
|
-
}
|
|
232
|
-
if (hasKey(cfg, "tools")) {
|
|
233
|
-
if (cfg.tools === false || cfg.tools === "") { target.tools = undefined; target.mcpDirectTools = undefined; }
|
|
234
|
-
else if (typeof cfg.tools === "string") { const parsed = parseTools(cfg.tools); target.tools = parsed.tools; target.mcpDirectTools = parsed.mcpDirectTools; }
|
|
235
|
-
else return "config.tools must be a comma-separated string or false when provided.";
|
|
236
|
-
}
|
|
237
|
-
if (hasKey(cfg, "skills")) {
|
|
238
|
-
if (cfg.skills === false || cfg.skills === "") target.skills = undefined;
|
|
239
|
-
else if (typeof cfg.skills === "string") { const skills = parseCsv(cfg.skills); target.skills = skills.length ? skills : undefined; }
|
|
240
|
-
else return "config.skills must be a comma-separated string or false when provided.";
|
|
241
|
-
}
|
|
242
|
-
if (hasKey(cfg, "extensions")) {
|
|
243
|
-
if (cfg.extensions === false) target.extensions = undefined;
|
|
244
|
-
else if (cfg.extensions === "") target.extensions = [];
|
|
245
|
-
else if (typeof cfg.extensions === "string") target.extensions = parseCsv(cfg.extensions);
|
|
246
|
-
else return "config.extensions must be a comma-separated string, empty string, or false when provided.";
|
|
247
|
-
}
|
|
248
|
-
if (hasKey(cfg, "thinking")) {
|
|
249
|
-
if (cfg.thinking === false || cfg.thinking === "") target.thinking = undefined;
|
|
250
|
-
else if (typeof cfg.thinking === "string") target.thinking = cfg.thinking.trim() || undefined;
|
|
251
|
-
else return "config.thinking must be a string or false when provided.";
|
|
252
|
-
}
|
|
253
|
-
if (hasKey(cfg, "systemPromptMode")) {
|
|
254
|
-
if (cfg.systemPromptMode === "append" || cfg.systemPromptMode === "replace") target.systemPromptMode = cfg.systemPromptMode;
|
|
255
|
-
else return "config.systemPromptMode must be 'append' or 'replace' when provided.";
|
|
256
|
-
}
|
|
257
|
-
if (hasKey(cfg, "inheritProjectContext")) {
|
|
258
|
-
if (typeof cfg.inheritProjectContext !== "boolean") return "config.inheritProjectContext must be a boolean when provided.";
|
|
259
|
-
target.inheritProjectContext = cfg.inheritProjectContext;
|
|
260
|
-
}
|
|
261
|
-
if (hasKey(cfg, "inheritSkills")) {
|
|
262
|
-
if (typeof cfg.inheritSkills !== "boolean") return "config.inheritSkills must be a boolean when provided.";
|
|
263
|
-
target.inheritSkills = cfg.inheritSkills;
|
|
264
|
-
}
|
|
265
|
-
if (hasKey(cfg, "output")) {
|
|
266
|
-
if (cfg.output === false || cfg.output === "") target.output = undefined;
|
|
267
|
-
else if (typeof cfg.output === "string") target.output = cfg.output;
|
|
268
|
-
else return "config.output must be a string or false when provided.";
|
|
269
|
-
}
|
|
270
|
-
if (hasKey(cfg, "reads")) {
|
|
271
|
-
if (cfg.reads === false || cfg.reads === "") target.defaultReads = undefined;
|
|
272
|
-
else if (typeof cfg.reads === "string") {
|
|
273
|
-
const reads = parseCsv(cfg.reads);
|
|
274
|
-
target.defaultReads = reads.length ? reads : undefined;
|
|
275
|
-
} else return "config.reads must be a comma-separated string or false when provided.";
|
|
276
|
-
}
|
|
277
|
-
if (hasKey(cfg, "progress")) {
|
|
278
|
-
if (typeof cfg.progress !== "boolean") return "config.progress must be a boolean when provided.";
|
|
279
|
-
target.defaultProgress = cfg.progress;
|
|
280
|
-
}
|
|
281
|
-
if (hasKey(cfg, "maxSubagentDepth")) {
|
|
282
|
-
if (cfg.maxSubagentDepth === false || cfg.maxSubagentDepth === "") target.maxSubagentDepth = undefined;
|
|
283
|
-
else if (typeof cfg.maxSubagentDepth === "number" && Number.isInteger(cfg.maxSubagentDepth) && cfg.maxSubagentDepth >= 0) {
|
|
284
|
-
target.maxSubagentDepth = cfg.maxSubagentDepth;
|
|
285
|
-
} else return "config.maxSubagentDepth must be an integer >= 0 or false when provided.";
|
|
286
|
-
}
|
|
287
|
-
return undefined;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function resolveTarget<T extends { source: AgentSource; filePath: string }>(
|
|
291
|
-
kind: "agent" | "chain",
|
|
292
|
-
name: string,
|
|
293
|
-
matches: T[],
|
|
294
|
-
cwd: string,
|
|
295
|
-
scopeHint?: string,
|
|
296
|
-
): T | AgentToolResult<Details> {
|
|
297
|
-
const mutable = matches.filter((m) => m.source !== "builtin");
|
|
298
|
-
if (mutable.length === 0) {
|
|
299
|
-
if (matches.length > 0) {
|
|
300
|
-
return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' is builtin and cannot be modified. Create a same-named ${kind} in user or project scope to override it.`, true);
|
|
301
|
-
}
|
|
302
|
-
const available = availableNames(cwd, kind);
|
|
303
|
-
return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' not found. Available: ${available.join(", ") || "none"}.`, true);
|
|
304
|
-
}
|
|
305
|
-
if (mutable.length === 1) return mutable[0]!;
|
|
306
|
-
const scope = asDisambiguationScope(scopeHint);
|
|
307
|
-
if (!scope) {
|
|
308
|
-
const paths = mutable.map((m) => `${m.source}: ${m.filePath}`).join("\n");
|
|
309
|
-
return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' exists in both scopes. Specify agentScope: 'user' or 'project'.\n${paths}`, true);
|
|
310
|
-
}
|
|
311
|
-
const scoped = mutable.filter((m) => m.source === scope);
|
|
312
|
-
if (scoped.length === 0) return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' not found in scope '${scope}'.`, true);
|
|
313
|
-
if (scoped.length > 1) return result(`Multiple ${kind}s named '${name}' found in scope '${scope}': ${scoped.map((m) => m.filePath).join(", ")}`, true);
|
|
314
|
-
return scoped[0]!;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function renamePath(
|
|
318
|
-
kind: "agent" | "chain",
|
|
319
|
-
currentPath: string,
|
|
320
|
-
newName: string,
|
|
321
|
-
scope: ManagementScope,
|
|
322
|
-
cwd: string,
|
|
323
|
-
): { filePath?: string; error?: string } {
|
|
324
|
-
if (nameExistsInScope(cwd, scope, newName, currentPath)) return { error: `Name '${newName}' already exists in ${scope} scope.` };
|
|
325
|
-
const ext = kind === "agent" ? ".md" : ".chain.md";
|
|
326
|
-
const filePath = path.join(path.dirname(currentPath), `${newName}${ext}`);
|
|
327
|
-
if (fs.existsSync(filePath) && filePath !== currentPath) {
|
|
328
|
-
return { error: `File already exists at ${filePath} but is not a valid ${kind} definition. Remove or rename it first.` };
|
|
329
|
-
}
|
|
330
|
-
fs.renameSync(currentPath, filePath);
|
|
331
|
-
return { filePath };
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
export function formatAgentDetail(agent: AgentConfig): string {
|
|
335
|
-
const tools = [...(agent.tools ?? []), ...(agent.mcpDirectTools ?? []).map((t) => `mcp:${t}`)];
|
|
336
|
-
const lines: string[] = [`Agent: ${agent.name} (${agent.source})`, `Path: ${agent.filePath}`, `Description: ${agent.description}`];
|
|
337
|
-
if (agent.model) lines.push(`Model: ${agent.model}`);
|
|
338
|
-
if (agent.fallbackModels?.length) lines.push(`Fallback models: ${agent.fallbackModels.join(", ")}`);
|
|
339
|
-
if (tools.length) lines.push(`Tools: ${tools.join(", ")}`);
|
|
340
|
-
if (agent.skills?.length) lines.push(`Skills: ${agent.skills.join(", ")}`);
|
|
341
|
-
lines.push(`System prompt mode: ${agent.systemPromptMode}`);
|
|
342
|
-
lines.push(`Inherit project context: ${agent.inheritProjectContext ? "true" : "false"}`);
|
|
343
|
-
lines.push(`Inherit skills: ${agent.inheritSkills ? "true" : "false"}`);
|
|
344
|
-
if (agent.source === "builtin") lines.push(`Disabled: ${agent.disabled ? "true" : "false"}`);
|
|
345
|
-
if (agent.extensions !== undefined) lines.push(`Extensions: ${agent.extensions.length ? agent.extensions.join(", ") : "(none)"}`);
|
|
346
|
-
if (agent.thinking) lines.push(`Thinking: ${agent.thinking}`);
|
|
347
|
-
if (agent.output) lines.push(`Output: ${agent.output}`);
|
|
348
|
-
if (agent.defaultReads?.length) lines.push(`Reads: ${agent.defaultReads.join(", ")}`);
|
|
349
|
-
if (agent.defaultProgress) lines.push("Progress: true");
|
|
350
|
-
if (agent.maxSubagentDepth !== undefined) lines.push(`Max subagent depth: ${agent.maxSubagentDepth}`);
|
|
351
|
-
if (agent.systemPrompt.trim()) lines.push("", "System Prompt:", agent.systemPrompt);
|
|
352
|
-
return lines.join("\n");
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
export function formatChainDetail(chain: ChainConfig): string {
|
|
356
|
-
const lines: string[] = [`Chain: ${chain.name} (${chain.source})`, `Path: ${chain.filePath}`, `Description: ${chain.description}`, "", "Steps:"];
|
|
357
|
-
for (let i = 0; i < chain.steps.length; i++) {
|
|
358
|
-
const s = chain.steps[i]!;
|
|
359
|
-
lines.push(`${i + 1}. ${s.agent}`);
|
|
360
|
-
if (s.task.trim()) lines.push(` Task: ${s.task}`);
|
|
361
|
-
if (s.output === false) lines.push(" Output: false");
|
|
362
|
-
else if (s.output) lines.push(` Output: ${s.output}`);
|
|
363
|
-
if (s.reads === false) lines.push(" Reads: false");
|
|
364
|
-
else if (Array.isArray(s.reads) && s.reads.length > 0) lines.push(` Reads: ${s.reads.join(", ")}`);
|
|
365
|
-
if (s.model) lines.push(` Model: ${s.model}`);
|
|
366
|
-
if (s.skills === false) lines.push(" Skills: false");
|
|
367
|
-
else if (Array.isArray(s.skills) && s.skills.length > 0) lines.push(` Skills: ${s.skills.join(", ")}`);
|
|
368
|
-
if (s.progress !== undefined) lines.push(` Progress: ${s.progress ? "true" : "false"}`);
|
|
369
|
-
}
|
|
370
|
-
return lines.join("\n");
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
export function handleList(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
|
|
374
|
-
const scope = normalizeListScope(params.agentScope) ?? "both";
|
|
375
|
-
const d = discoverAgentsAll(ctx.cwd);
|
|
376
|
-
const scopedAgents = allAgents(d).filter((a) => scope === "both" || a.source === "builtin" || a.source === scope).sort((a, b) => a.name.localeCompare(b.name));
|
|
377
|
-
const agents = scopedAgents.filter((a) => !a.disabled);
|
|
378
|
-
const disabledBuiltins = scopedAgents.filter((a) => a.source === "builtin" && a.disabled);
|
|
379
|
-
const chains = d.chains.filter((c) => scope === "both" || c.source === scope).sort((a, b) => a.name.localeCompare(b.name));
|
|
380
|
-
const lines = [
|
|
381
|
-
"Executable agents:",
|
|
382
|
-
...(agents.length ? agents.map((a) => `- ${a.name} (${a.source}): ${a.description}`) : ["- (none)"]),
|
|
383
|
-
...(disabledBuiltins.length ? ["", "Disabled builtins:", ...disabledBuiltins.map((a) => `- ${a.name} (${a.source}, disabled): ${a.description}`)] : []),
|
|
384
|
-
"",
|
|
385
|
-
"Chains:",
|
|
386
|
-
...(chains.length ? chains.map((c) => `- ${c.name} (${c.source}): ${c.description}`) : ["- (none)"]),
|
|
387
|
-
];
|
|
388
|
-
return result(lines.join("\n"));
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
export function handleGet(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
|
|
392
|
-
if (!params.agent && !params.chainName) return result("Specify 'agent' or 'chainName' for get.", true);
|
|
393
|
-
const hasBoth = Boolean(params.agent && params.chainName);
|
|
394
|
-
const blocks: string[] = [];
|
|
395
|
-
let anyFound = false;
|
|
396
|
-
if (params.agent) {
|
|
397
|
-
const matches = findAgents(params.agent, ctx.cwd, "both");
|
|
398
|
-
if (!matches.length) {
|
|
399
|
-
const msg = `Agent '${params.agent}' not found. Available: ${availableNames(ctx.cwd, "agent").join(", ") || "none"}.`;
|
|
400
|
-
if (!hasBoth) return result(msg, true);
|
|
401
|
-
blocks.push(msg);
|
|
402
|
-
} else {
|
|
403
|
-
anyFound = true;
|
|
404
|
-
blocks.push(...matches.map(formatAgentDetail));
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
if (params.chainName) {
|
|
408
|
-
const matches = findChains(params.chainName, ctx.cwd, "both");
|
|
409
|
-
if (!matches.length) {
|
|
410
|
-
const msg = `Chain '${params.chainName}' not found. Available: ${availableNames(ctx.cwd, "chain").join(", ") || "none"}.`;
|
|
411
|
-
if (!hasBoth) return result(msg, true);
|
|
412
|
-
blocks.push(msg);
|
|
413
|
-
} else {
|
|
414
|
-
anyFound = true;
|
|
415
|
-
blocks.push(...matches.map(formatChainDetail));
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
return result(blocks.join("\n\n"), !anyFound);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
export function handleCreate(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
|
|
422
|
-
const parsedConfig = configObject(params.config);
|
|
423
|
-
if (parsedConfig.error) return result(parsedConfig.error, true);
|
|
424
|
-
const cfg = parsedConfig.value;
|
|
425
|
-
if (!cfg) return result("config required for create.", true);
|
|
426
|
-
if (typeof cfg.name !== "string" || !cfg.name.trim()) return result("config.name is required and must be a non-empty string.", true);
|
|
427
|
-
if (typeof cfg.description !== "string" || !cfg.description.trim()) return result("config.description is required and must be a non-empty string.", true);
|
|
428
|
-
const name = sanitizeName(cfg.name);
|
|
429
|
-
if (!name) return result("config.name is invalid after sanitization. Use letters, numbers, spaces, or hyphens.", true);
|
|
430
|
-
const scopeRaw = cfg.scope ?? "user";
|
|
431
|
-
if (scopeRaw !== "user" && scopeRaw !== "project") return result("config.scope must be 'user' or 'project'.", true);
|
|
432
|
-
const scope = scopeRaw as ManagementScope;
|
|
433
|
-
const isChain = hasKey(cfg, "steps");
|
|
434
|
-
const d = discoverAgentsAll(ctx.cwd);
|
|
435
|
-
const targetDir = scope === "user" ? d.userDir : d.projectDir ?? path.join(ctx.cwd, ".pi", "agents");
|
|
436
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
437
|
-
if (nameExistsInScope(ctx.cwd, scope, name)) return result(`Name '${name}' already exists in ${scope} scope. Use update instead.`, true);
|
|
438
|
-
const targetPath = path.join(targetDir, isChain ? `${name}.chain.md` : `${name}.md`);
|
|
439
|
-
if (fs.existsSync(targetPath)) return result(`File already exists at ${targetPath} but is not a valid ${isChain ? "chain" : "agent"} definition. Remove or rename it first.`, true);
|
|
440
|
-
const warnings: string[] = [];
|
|
441
|
-
if (!isChain && d.builtin.some((a) => a.name === name)) warnings.push(`Note: this shadows the builtin agent '${name}'.`);
|
|
442
|
-
if (isChain) {
|
|
443
|
-
const parsed = parseStepList(cfg.steps);
|
|
444
|
-
if (parsed.error) return result(parsed.error, true);
|
|
445
|
-
const chain: ChainConfig = { name, description: cfg.description.trim(), source: scope, filePath: targetPath, steps: parsed.steps! };
|
|
446
|
-
fs.writeFileSync(targetPath, serializeChain(chain), "utf-8");
|
|
447
|
-
const missing = unknownChainAgents(ctx.cwd, chain.steps);
|
|
448
|
-
if (missing.length) warnings.push(`Warning: chain steps reference unknown agents: ${missing.join(", ")}.`);
|
|
449
|
-
warnings.push(...chainStepWarnings(ctx, chain.steps));
|
|
450
|
-
return result([`Created chain '${name}' at ${targetPath}.`, ...warnings].join("\n"));
|
|
451
|
-
}
|
|
452
|
-
const agent: AgentConfig = {
|
|
453
|
-
name,
|
|
454
|
-
description: cfg.description.trim(),
|
|
455
|
-
source: scope,
|
|
456
|
-
filePath: targetPath,
|
|
457
|
-
systemPrompt: "",
|
|
458
|
-
systemPromptMode: defaultSystemPromptMode(name),
|
|
459
|
-
inheritProjectContext: defaultInheritProjectContext(name),
|
|
460
|
-
inheritSkills: defaultInheritSkills(),
|
|
461
|
-
};
|
|
462
|
-
const applyError = applyAgentConfig(agent, cfg);
|
|
463
|
-
if (applyError) return result(applyError, true);
|
|
464
|
-
const mw = modelWarning(ctx, agent.model);
|
|
465
|
-
if (mw) warnings.push(mw);
|
|
466
|
-
const fmw = fallbackModelsWarning(ctx, agent.fallbackModels);
|
|
467
|
-
if (fmw) warnings.push(fmw);
|
|
468
|
-
const sw = skillsWarning(ctx.cwd, agent.skills);
|
|
469
|
-
if (sw) warnings.push(sw);
|
|
470
|
-
fs.writeFileSync(targetPath, serializeAgent(agent), "utf-8");
|
|
471
|
-
return result([`Created agent '${name}' at ${targetPath}.`, ...warnings].join("\n"));
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
export function handleUpdate(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
|
|
475
|
-
if (!params.agent && !params.chainName) return result("Specify 'agent' or 'chainName' for update.", true);
|
|
476
|
-
if (params.agent && params.chainName) return result("Specify either 'agent' or 'chainName', not both.", true);
|
|
477
|
-
const parsedConfig = configObject(params.config);
|
|
478
|
-
if (parsedConfig.error) return result(parsedConfig.error, true);
|
|
479
|
-
const cfg = parsedConfig.value;
|
|
480
|
-
if (!cfg) return result("config required for update.", true);
|
|
481
|
-
const warnings: string[] = [];
|
|
482
|
-
if (params.agent) {
|
|
483
|
-
const scopeHint = asDisambiguationScope(params.agentScope);
|
|
484
|
-
const targetOrError = resolveTarget("agent", params.agent, findAgents(params.agent, ctx.cwd, scopeHint ?? "both"), ctx.cwd, params.agentScope);
|
|
485
|
-
if ("content" in targetOrError) return targetOrError;
|
|
486
|
-
const target = targetOrError;
|
|
487
|
-
const updated: AgentConfig = { ...target };
|
|
488
|
-
const oldName = target.name;
|
|
489
|
-
if (hasKey(cfg, "name") && (typeof cfg.name !== "string" || !cfg.name.trim())) return result("config.name must be a non-empty string when provided.", true);
|
|
490
|
-
if (hasKey(cfg, "description") && (typeof cfg.description !== "string" || !cfg.description.trim())) return result("config.description must be a non-empty string when provided.", true);
|
|
491
|
-
let newName: string | undefined;
|
|
492
|
-
if (hasKey(cfg, "name")) {
|
|
493
|
-
newName = sanitizeName(cfg.name as string);
|
|
494
|
-
if (!newName) return result("config.name is invalid after sanitization.", true);
|
|
495
|
-
}
|
|
496
|
-
const applyError = applyAgentConfig(updated, cfg);
|
|
497
|
-
if (applyError) return result(applyError, true);
|
|
498
|
-
if (newName !== undefined) updated.name = newName;
|
|
499
|
-
if (hasKey(cfg, "description")) updated.description = (cfg.description as string).trim();
|
|
500
|
-
if (hasKey(cfg, "model")) {
|
|
501
|
-
const mw = modelWarning(ctx, updated.model);
|
|
502
|
-
if (mw) warnings.push(mw);
|
|
503
|
-
}
|
|
504
|
-
if (hasKey(cfg, "fallbackModels")) {
|
|
505
|
-
const fmw = fallbackModelsWarning(ctx, updated.fallbackModels);
|
|
506
|
-
if (fmw) warnings.push(fmw);
|
|
507
|
-
}
|
|
508
|
-
if (hasKey(cfg, "skills")) {
|
|
509
|
-
const sw = skillsWarning(ctx.cwd, updated.skills);
|
|
510
|
-
if (sw) warnings.push(sw);
|
|
511
|
-
}
|
|
512
|
-
if (updated.name !== oldName) {
|
|
513
|
-
const renamed = renamePath("agent", target.filePath, updated.name, target.source, ctx.cwd);
|
|
514
|
-
if (renamed.error) return result(renamed.error, true);
|
|
515
|
-
updated.filePath = renamed.filePath!;
|
|
516
|
-
}
|
|
517
|
-
fs.writeFileSync(updated.filePath, serializeAgent(updated), "utf-8");
|
|
518
|
-
if (updated.name !== oldName) {
|
|
519
|
-
const refs = discoverAgentsAll(ctx.cwd).chains.filter((c) => c.steps.some((s) => s.agent === oldName)).map((c) => `${c.name} (${c.source})`);
|
|
520
|
-
if (refs.length) warnings.push(`Warning: chains still reference '${oldName}': ${refs.join(", ")}.`);
|
|
521
|
-
}
|
|
522
|
-
const headline = updated.name === oldName
|
|
523
|
-
? `Updated agent '${updated.name}' at ${updated.filePath}.`
|
|
524
|
-
: `Updated agent '${oldName}' to '${updated.name}' at ${updated.filePath}.`;
|
|
525
|
-
return result([headline, ...warnings].join("\n"));
|
|
526
|
-
}
|
|
527
|
-
const scopeHint = asDisambiguationScope(params.agentScope);
|
|
528
|
-
const targetOrError = resolveTarget("chain", params.chainName!, findChains(params.chainName!, ctx.cwd, scopeHint ?? "both"), ctx.cwd, params.agentScope);
|
|
529
|
-
if ("content" in targetOrError) return targetOrError;
|
|
530
|
-
const target = targetOrError;
|
|
531
|
-
const updated: ChainConfig = { ...target, steps: [...target.steps] };
|
|
532
|
-
const oldName = target.name;
|
|
533
|
-
if (hasKey(cfg, "name") && (typeof cfg.name !== "string" || !cfg.name.trim())) return result("config.name must be a non-empty string when provided.", true);
|
|
534
|
-
if (hasKey(cfg, "description") && (typeof cfg.description !== "string" || !cfg.description.trim())) return result("config.description must be a non-empty string when provided.", true);
|
|
535
|
-
let newName: string | undefined;
|
|
536
|
-
if (hasKey(cfg, "name")) {
|
|
537
|
-
newName = sanitizeName(cfg.name as string);
|
|
538
|
-
if (!newName) return result("config.name is invalid after sanitization.", true);
|
|
539
|
-
}
|
|
540
|
-
let parsedSteps: ChainStepConfig[] | undefined;
|
|
541
|
-
if (hasKey(cfg, "steps")) {
|
|
542
|
-
const parsed = parseStepList(cfg.steps);
|
|
543
|
-
if (parsed.error) return result(parsed.error, true);
|
|
544
|
-
parsedSteps = parsed.steps!;
|
|
545
|
-
}
|
|
546
|
-
if (newName !== undefined) updated.name = newName;
|
|
547
|
-
if (hasKey(cfg, "description")) updated.description = (cfg.description as string).trim();
|
|
548
|
-
if (parsedSteps) {
|
|
549
|
-
updated.steps = parsedSteps;
|
|
550
|
-
const missing = unknownChainAgents(ctx.cwd, updated.steps);
|
|
551
|
-
if (missing.length) warnings.push(`Warning: chain steps reference unknown agents: ${missing.join(", ")}.`);
|
|
552
|
-
warnings.push(...chainStepWarnings(ctx, updated.steps));
|
|
553
|
-
}
|
|
554
|
-
if (updated.name !== oldName) {
|
|
555
|
-
const renamed = renamePath("chain", target.filePath, updated.name, target.source, ctx.cwd);
|
|
556
|
-
if (renamed.error) return result(renamed.error, true);
|
|
557
|
-
updated.filePath = renamed.filePath!;
|
|
558
|
-
}
|
|
559
|
-
fs.writeFileSync(updated.filePath, serializeChain(updated), "utf-8");
|
|
560
|
-
const headline = updated.name === oldName
|
|
561
|
-
? `Updated chain '${updated.name}' at ${updated.filePath}.`
|
|
562
|
-
: `Updated chain '${oldName}' to '${updated.name}' at ${updated.filePath}.`;
|
|
563
|
-
return result([headline, ...warnings].join("\n"));
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
export function handleDelete(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
|
|
567
|
-
if (!params.agent && !params.chainName) return result("Specify 'agent' or 'chainName' for delete.", true);
|
|
568
|
-
if (params.agent && params.chainName) return result("Specify either 'agent' or 'chainName', not both.", true);
|
|
569
|
-
const scopeHint = asDisambiguationScope(params.agentScope);
|
|
570
|
-
if (params.agent) {
|
|
571
|
-
const targetOrError = resolveTarget("agent", params.agent, findAgents(params.agent, ctx.cwd, scopeHint ?? "both"), ctx.cwd, params.agentScope);
|
|
572
|
-
if ("content" in targetOrError) return targetOrError;
|
|
573
|
-
const target = targetOrError;
|
|
574
|
-
fs.unlinkSync(target.filePath);
|
|
575
|
-
const refs = discoverAgentsAll(ctx.cwd).chains.filter((c) => c.steps.some((s) => s.agent === target.name)).map((c) => `${c.name} (${c.source})`);
|
|
576
|
-
const lines = [`Deleted agent '${target.name}' at ${target.filePath}.`];
|
|
577
|
-
if (refs.length) lines.push(`Warning: chains reference deleted agent '${target.name}': ${refs.join(", ")}.`);
|
|
578
|
-
return result(lines.join("\n"));
|
|
579
|
-
}
|
|
580
|
-
const targetOrError = resolveTarget("chain", params.chainName!, findChains(params.chainName!, ctx.cwd, scopeHint ?? "both"), ctx.cwd, params.agentScope);
|
|
581
|
-
if ("content" in targetOrError) return targetOrError;
|
|
582
|
-
const target = targetOrError;
|
|
583
|
-
fs.unlinkSync(target.filePath);
|
|
584
|
-
return result(`Deleted chain '${target.name}' at ${target.filePath}.`);
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
export function handleManagementAction(action: string, params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
|
|
588
|
-
switch (action as ManagementAction) {
|
|
589
|
-
case "list": return handleList(params, ctx);
|
|
590
|
-
case "get": return handleGet(params, ctx);
|
|
591
|
-
case "create": return handleCreate(params, ctx);
|
|
592
|
-
case "update": return handleUpdate(params, ctx);
|
|
593
|
-
case "delete": return handleDelete(params, ctx);
|
|
594
|
-
default: return result(`Unknown action: ${action}`, true);
|
|
595
|
-
}
|
|
596
|
-
}
|