pi-subagents 0.13.3 → 0.14.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/CHANGELOG.md +21 -0
- package/README.md +35 -12
- package/agent-management.ts +15 -6
- package/agent-manager-detail.ts +12 -2
- package/agent-manager-edit.ts +75 -23
- package/agent-manager-list.ts +9 -2
- package/agent-manager.ts +199 -11
- package/agents.ts +315 -20
- package/artifacts.ts +11 -5
- package/async-execution.ts +92 -71
- package/chain-clarify.ts +45 -156
- package/chain-execution.ts +23 -63
- package/execution.ts +54 -49
- package/index.ts +1 -1
- package/intercom-bridge.ts +8 -0
- package/model-fallback.ts +8 -2
- package/package.json +1 -1
- package/schemas.ts +1 -1
- package/settings.ts +6 -4
- package/skills.ts +259 -77
- package/subagent-executor.ts +45 -15
- package/subagent-runner.ts +176 -51
- package/types.ts +64 -13
- package/utils.ts +5 -10
- package/worktree.ts +27 -9
package/agents.ts
CHANGED
|
@@ -15,6 +15,31 @@ export type AgentScope = "user" | "project" | "both";
|
|
|
15
15
|
|
|
16
16
|
export type AgentSource = "builtin" | "user" | "project";
|
|
17
17
|
|
|
18
|
+
export interface BuiltinAgentOverrideBase {
|
|
19
|
+
model?: string;
|
|
20
|
+
fallbackModels?: string[];
|
|
21
|
+
thinking?: string;
|
|
22
|
+
systemPrompt: string;
|
|
23
|
+
skills?: string[];
|
|
24
|
+
tools?: string[];
|
|
25
|
+
mcpDirectTools?: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface BuiltinAgentOverrideConfig {
|
|
29
|
+
model?: string | false;
|
|
30
|
+
fallbackModels?: string[] | false;
|
|
31
|
+
thinking?: string | false;
|
|
32
|
+
systemPrompt?: string;
|
|
33
|
+
skills?: string[] | false;
|
|
34
|
+
tools?: string[] | false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface BuiltinAgentOverrideInfo {
|
|
38
|
+
scope: "user" | "project";
|
|
39
|
+
path: string;
|
|
40
|
+
base: BuiltinAgentOverrideBase;
|
|
41
|
+
}
|
|
42
|
+
|
|
18
43
|
export interface AgentConfig {
|
|
19
44
|
name: string;
|
|
20
45
|
description: string;
|
|
@@ -28,13 +53,13 @@ export interface AgentConfig {
|
|
|
28
53
|
filePath: string;
|
|
29
54
|
skills?: string[];
|
|
30
55
|
extensions?: string[];
|
|
31
|
-
// Chain behavior fields
|
|
32
56
|
output?: string;
|
|
33
57
|
defaultReads?: string[];
|
|
34
58
|
defaultProgress?: boolean;
|
|
35
59
|
interactive?: boolean;
|
|
36
60
|
maxSubagentDepth?: number;
|
|
37
61
|
extraFields?: Record<string, string>;
|
|
62
|
+
override?: BuiltinAgentOverrideInfo;
|
|
38
63
|
}
|
|
39
64
|
|
|
40
65
|
export interface ChainStepConfig {
|
|
@@ -61,6 +86,266 @@ export interface AgentDiscoveryResult {
|
|
|
61
86
|
projectAgentsDir: string | null;
|
|
62
87
|
}
|
|
63
88
|
|
|
89
|
+
function splitToolList(rawTools: string[] | undefined): { tools?: string[]; mcpDirectTools?: string[] } {
|
|
90
|
+
const mcpDirectTools: string[] = [];
|
|
91
|
+
const tools: string[] = [];
|
|
92
|
+
for (const tool of rawTools ?? []) {
|
|
93
|
+
if (tool.startsWith("mcp:")) {
|
|
94
|
+
mcpDirectTools.push(tool.slice(4));
|
|
95
|
+
} else {
|
|
96
|
+
tools.push(tool);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
...(tools.length > 0 ? { tools } : {}),
|
|
101
|
+
...(mcpDirectTools.length > 0 ? { mcpDirectTools } : {}),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function joinToolList(config: Pick<AgentConfig, "tools" | "mcpDirectTools">): string[] | undefined {
|
|
106
|
+
const joined = [
|
|
107
|
+
...(config.tools ?? []),
|
|
108
|
+
...(config.mcpDirectTools ?? []).map((tool) => `mcp:${tool}`),
|
|
109
|
+
];
|
|
110
|
+
return joined.length > 0 ? joined : undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function arraysEqual(a: string[] | undefined, b: string[] | undefined): boolean {
|
|
114
|
+
if (!a && !b) return true;
|
|
115
|
+
if (!a || !b) return false;
|
|
116
|
+
if (a.length !== b.length) return false;
|
|
117
|
+
for (let i = 0; i < a.length; i++) {
|
|
118
|
+
if (a[i] !== b[i]) return false;
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
124
|
+
return {
|
|
125
|
+
model: agent.model,
|
|
126
|
+
fallbackModels: agent.fallbackModels ? [...agent.fallbackModels] : undefined,
|
|
127
|
+
thinking: agent.thinking,
|
|
128
|
+
systemPrompt: agent.systemPrompt,
|
|
129
|
+
skills: agent.skills ? [...agent.skills] : undefined,
|
|
130
|
+
tools: agent.tools ? [...agent.tools] : undefined,
|
|
131
|
+
mcpDirectTools: agent.mcpDirectTools ? [...agent.mcpDirectTools] : undefined,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentOverrideConfig {
|
|
136
|
+
return {
|
|
137
|
+
...(override.model !== undefined ? { model: override.model } : {}),
|
|
138
|
+
...(override.fallbackModels !== undefined
|
|
139
|
+
? { fallbackModels: override.fallbackModels === false ? false : [...override.fallbackModels] }
|
|
140
|
+
: {}),
|
|
141
|
+
...(override.thinking !== undefined ? { thinking: override.thinking } : {}),
|
|
142
|
+
...(override.systemPrompt !== undefined ? { systemPrompt: override.systemPrompt } : {}),
|
|
143
|
+
...(override.skills !== undefined ? { skills: override.skills === false ? false : [...override.skills] } : {}),
|
|
144
|
+
...(override.tools !== undefined ? { tools: override.tools === false ? false : [...override.tools] } : {}),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function findNearestProjectRoot(cwd: string): string | null {
|
|
149
|
+
let currentDir = cwd;
|
|
150
|
+
while (true) {
|
|
151
|
+
if (isDirectory(path.join(currentDir, ".pi")) || isDirectory(path.join(currentDir, ".agents"))) {
|
|
152
|
+
return currentDir;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const parentDir = path.dirname(currentDir);
|
|
156
|
+
if (parentDir === currentDir) return null;
|
|
157
|
+
currentDir = parentDir;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function getUserAgentSettingsPath(): string {
|
|
162
|
+
return path.join(os.homedir(), ".pi", "agent", "settings.json");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function getProjectAgentSettingsPath(cwd: string): string | null {
|
|
166
|
+
const projectRoot = findNearestProjectRoot(cwd);
|
|
167
|
+
return projectRoot ? path.join(projectRoot, ".pi", "settings.json") : null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function readSettingsFileStrict(filePath: string): Record<string, unknown> {
|
|
171
|
+
if (!fs.existsSync(filePath)) return {};
|
|
172
|
+
let parsed: unknown;
|
|
173
|
+
try {
|
|
174
|
+
parsed = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
175
|
+
} catch (error) {
|
|
176
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
177
|
+
throw new Error(`Failed to parse settings file '${filePath}': ${message}`, { cause: error });
|
|
178
|
+
}
|
|
179
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
180
|
+
throw new Error(`Settings file '${filePath}' must contain a JSON object.`);
|
|
181
|
+
}
|
|
182
|
+
return parsed as Record<string, unknown>;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function writeSettingsFile(filePath: string, settings: Record<string, unknown>): void {
|
|
186
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
187
|
+
fs.writeFileSync(filePath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function parseStringArrayOrFalse(value: unknown): string[] | false | undefined {
|
|
191
|
+
if (value === false) return false;
|
|
192
|
+
if (!Array.isArray(value)) return undefined;
|
|
193
|
+
const items = value.filter((item): item is string => typeof item === "string").map((item) => item.trim()).filter(Boolean);
|
|
194
|
+
return items;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function parseBuiltinOverrideEntry(value: unknown): BuiltinAgentOverrideConfig | undefined {
|
|
198
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
199
|
+
const input = value as Record<string, unknown>;
|
|
200
|
+
const override: BuiltinAgentOverrideConfig = {};
|
|
201
|
+
|
|
202
|
+
if (typeof input.model === "string" || input.model === false) override.model = input.model;
|
|
203
|
+
if (typeof input.thinking === "string" || input.thinking === false) override.thinking = input.thinking;
|
|
204
|
+
if (typeof input.systemPrompt === "string") override.systemPrompt = input.systemPrompt;
|
|
205
|
+
|
|
206
|
+
const fallbackModels = parseStringArrayOrFalse(input.fallbackModels);
|
|
207
|
+
if (fallbackModels !== undefined) override.fallbackModels = fallbackModels;
|
|
208
|
+
|
|
209
|
+
const skills = parseStringArrayOrFalse(input.skills);
|
|
210
|
+
if (skills !== undefined) override.skills = skills;
|
|
211
|
+
|
|
212
|
+
const tools = parseStringArrayOrFalse(input.tools);
|
|
213
|
+
if (tools !== undefined) override.tools = tools;
|
|
214
|
+
|
|
215
|
+
return Object.keys(override).length > 0 ? override : undefined;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function readBuiltinOverrides(filePath: string | null): Record<string, BuiltinAgentOverrideConfig> {
|
|
219
|
+
if (!filePath || !fs.existsSync(filePath)) return {};
|
|
220
|
+
const settings = readSettingsFileStrict(filePath);
|
|
221
|
+
const subagents = settings.subagents;
|
|
222
|
+
if (!subagents || typeof subagents !== "object" || Array.isArray(subagents)) return {};
|
|
223
|
+
const agentOverrides = (subagents as Record<string, unknown>).agentOverrides;
|
|
224
|
+
if (!agentOverrides || typeof agentOverrides !== "object" || Array.isArray(agentOverrides)) return {};
|
|
225
|
+
|
|
226
|
+
const parsed: Record<string, BuiltinAgentOverrideConfig> = {};
|
|
227
|
+
for (const [name, value] of Object.entries(agentOverrides)) {
|
|
228
|
+
const override = parseBuiltinOverrideEntry(value);
|
|
229
|
+
if (override) parsed[name] = override;
|
|
230
|
+
}
|
|
231
|
+
return parsed;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function applyBuiltinOverride(
|
|
235
|
+
agent: AgentConfig,
|
|
236
|
+
override: BuiltinAgentOverrideConfig,
|
|
237
|
+
meta: { scope: "user" | "project"; path: string },
|
|
238
|
+
): AgentConfig {
|
|
239
|
+
const next: AgentConfig = {
|
|
240
|
+
...agent,
|
|
241
|
+
override: { ...meta, base: cloneOverrideBase(agent) },
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
if (override.model !== undefined) next.model = override.model === false ? undefined : override.model;
|
|
245
|
+
if (override.fallbackModels !== undefined) {
|
|
246
|
+
next.fallbackModels = override.fallbackModels === false ? undefined : [...override.fallbackModels];
|
|
247
|
+
}
|
|
248
|
+
if (override.thinking !== undefined) next.thinking = override.thinking === false ? undefined : override.thinking;
|
|
249
|
+
if (override.systemPrompt !== undefined) next.systemPrompt = override.systemPrompt;
|
|
250
|
+
if (override.skills !== undefined) next.skills = override.skills === false ? undefined : [...override.skills];
|
|
251
|
+
if (override.tools !== undefined) {
|
|
252
|
+
const { tools, mcpDirectTools } = splitToolList(override.tools === false ? [] : override.tools);
|
|
253
|
+
next.tools = tools;
|
|
254
|
+
next.mcpDirectTools = mcpDirectTools;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return next;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function applyBuiltinOverrides(
|
|
261
|
+
builtinAgents: AgentConfig[],
|
|
262
|
+
userOverrides: Record<string, BuiltinAgentOverrideConfig>,
|
|
263
|
+
projectOverrides: Record<string, BuiltinAgentOverrideConfig>,
|
|
264
|
+
userSettingsPath: string,
|
|
265
|
+
projectSettingsPath: string | null,
|
|
266
|
+
): AgentConfig[] {
|
|
267
|
+
return builtinAgents.map((agent) => {
|
|
268
|
+
const projectOverride = projectOverrides[agent.name];
|
|
269
|
+
if (projectOverride && projectSettingsPath) {
|
|
270
|
+
return applyBuiltinOverride(agent, projectOverride, { scope: "project", path: projectSettingsPath });
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const userOverride = userOverrides[agent.name];
|
|
274
|
+
if (userOverride) {
|
|
275
|
+
return applyBuiltinOverride(agent, userOverride, { scope: "user", path: userSettingsPath });
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return agent;
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export function buildBuiltinOverrideConfig(
|
|
283
|
+
base: BuiltinAgentOverrideBase,
|
|
284
|
+
draft: Pick<AgentConfig, "model" | "fallbackModels" | "thinking" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools">,
|
|
285
|
+
): BuiltinAgentOverrideConfig | undefined {
|
|
286
|
+
const override: BuiltinAgentOverrideConfig = {};
|
|
287
|
+
|
|
288
|
+
if (draft.model !== base.model) override.model = draft.model ?? false;
|
|
289
|
+
if (!arraysEqual(draft.fallbackModels, base.fallbackModels)) override.fallbackModels = draft.fallbackModels ? [...draft.fallbackModels] : false;
|
|
290
|
+
if (draft.thinking !== base.thinking) override.thinking = draft.thinking ?? false;
|
|
291
|
+
if (draft.systemPrompt !== base.systemPrompt) override.systemPrompt = draft.systemPrompt;
|
|
292
|
+
if (!arraysEqual(draft.skills, base.skills)) override.skills = draft.skills ? [...draft.skills] : false;
|
|
293
|
+
|
|
294
|
+
const baseTools = joinToolList(base);
|
|
295
|
+
const draftTools = joinToolList(draft);
|
|
296
|
+
if (!arraysEqual(draftTools, baseTools)) override.tools = draftTools ? [...draftTools] : false;
|
|
297
|
+
|
|
298
|
+
return Object.keys(override).length > 0 ? override : undefined;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function saveBuiltinAgentOverride(
|
|
302
|
+
cwd: string,
|
|
303
|
+
name: string,
|
|
304
|
+
scope: "user" | "project",
|
|
305
|
+
override: BuiltinAgentOverrideConfig,
|
|
306
|
+
): string {
|
|
307
|
+
const filePath = scope === "project" ? getProjectAgentSettingsPath(cwd) : getUserAgentSettingsPath();
|
|
308
|
+
if (!filePath) throw new Error("Project override is not available here. No project config root was found.");
|
|
309
|
+
|
|
310
|
+
const settings = readSettingsFileStrict(filePath);
|
|
311
|
+
const subagents = settings.subagents && typeof settings.subagents === "object" && !Array.isArray(settings.subagents)
|
|
312
|
+
? { ...(settings.subagents as Record<string, unknown>) }
|
|
313
|
+
: {};
|
|
314
|
+
const agentOverrides = subagents.agentOverrides && typeof subagents.agentOverrides === "object" && !Array.isArray(subagents.agentOverrides)
|
|
315
|
+
? { ...(subagents.agentOverrides as Record<string, unknown>) }
|
|
316
|
+
: {};
|
|
317
|
+
|
|
318
|
+
agentOverrides[name] = cloneOverrideValue(override);
|
|
319
|
+
subagents.agentOverrides = agentOverrides;
|
|
320
|
+
settings.subagents = subagents;
|
|
321
|
+
writeSettingsFile(filePath, settings);
|
|
322
|
+
return filePath;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export function removeBuiltinAgentOverride(cwd: string, name: string, scope: "user" | "project"): string {
|
|
326
|
+
const filePath = scope === "project" ? getProjectAgentSettingsPath(cwd) : getUserAgentSettingsPath();
|
|
327
|
+
if (!filePath) throw new Error("Project override is not available here. No project config root was found.");
|
|
328
|
+
if (!fs.existsSync(filePath)) return filePath;
|
|
329
|
+
|
|
330
|
+
const settings = readSettingsFileStrict(filePath);
|
|
331
|
+
const subagents = settings.subagents;
|
|
332
|
+
if (!subagents || typeof subagents !== "object" || Array.isArray(subagents)) return filePath;
|
|
333
|
+
const nextSubagents = { ...(subagents as Record<string, unknown>) };
|
|
334
|
+
const agentOverrides = nextSubagents.agentOverrides;
|
|
335
|
+
if (!agentOverrides || typeof agentOverrides !== "object" || Array.isArray(agentOverrides)) return filePath;
|
|
336
|
+
|
|
337
|
+
const nextOverrides = { ...(agentOverrides as Record<string, unknown>) };
|
|
338
|
+
delete nextOverrides[name];
|
|
339
|
+
if (Object.keys(nextOverrides).length > 0) nextSubagents.agentOverrides = nextOverrides;
|
|
340
|
+
else delete nextSubagents.agentOverrides;
|
|
341
|
+
|
|
342
|
+
if (Object.keys(nextSubagents).length > 0) settings.subagents = nextSubagents;
|
|
343
|
+
else delete settings.subagents;
|
|
344
|
+
|
|
345
|
+
writeSettingsFile(filePath, settings);
|
|
346
|
+
return filePath;
|
|
347
|
+
}
|
|
348
|
+
|
|
64
349
|
function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
|
|
65
350
|
const agents: AgentConfig[] = [];
|
|
66
351
|
|
|
@@ -111,7 +396,6 @@ function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
|
|
|
111
396
|
}
|
|
112
397
|
}
|
|
113
398
|
|
|
114
|
-
// Parse defaultReads as comma-separated list (like tools)
|
|
115
399
|
const defaultReads = frontmatter.defaultReads
|
|
116
400
|
?.split(",")
|
|
117
401
|
.map((f) => f.trim())
|
|
@@ -155,7 +439,6 @@ function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
|
|
|
155
439
|
filePath,
|
|
156
440
|
skills: skills && skills.length > 0 ? skills : undefined,
|
|
157
441
|
extensions,
|
|
158
|
-
// Chain behavior fields
|
|
159
442
|
output: frontmatter.output,
|
|
160
443
|
defaultReads: defaultReads && defaultReads.length > 0 ? defaultReads : undefined,
|
|
161
444
|
defaultProgress: frontmatter.defaultProgress === "true",
|
|
@@ -216,18 +499,12 @@ function isDirectory(p: string): boolean {
|
|
|
216
499
|
}
|
|
217
500
|
|
|
218
501
|
function findNearestProjectAgentsDir(cwd: string): string | null {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (isDirectory(candidate)) return candidate;
|
|
226
|
-
|
|
227
|
-
const parentDir = path.dirname(currentDir);
|
|
228
|
-
if (parentDir === currentDir) return null;
|
|
229
|
-
currentDir = parentDir;
|
|
230
|
-
}
|
|
502
|
+
const projectRoot = findNearestProjectRoot(cwd);
|
|
503
|
+
if (!projectRoot) return null;
|
|
504
|
+
const candidateAlt = path.join(projectRoot, ".agents");
|
|
505
|
+
if (isDirectory(candidateAlt)) return candidateAlt;
|
|
506
|
+
const candidate = path.join(projectRoot, ".pi", "agents");
|
|
507
|
+
return isDirectory(candidate) ? candidate : null;
|
|
231
508
|
}
|
|
232
509
|
|
|
233
510
|
const BUILTIN_AGENTS_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), "agents");
|
|
@@ -236,8 +513,16 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
|
|
|
236
513
|
const userDirOld = path.join(os.homedir(), ".pi", "agent", "agents");
|
|
237
514
|
const userDirNew = path.join(os.homedir(), ".agents");
|
|
238
515
|
const projectAgentsDir = findNearestProjectAgentsDir(cwd);
|
|
239
|
-
|
|
240
|
-
const
|
|
516
|
+
const userSettingsPath = getUserAgentSettingsPath();
|
|
517
|
+
const projectSettingsPath = getProjectAgentSettingsPath(cwd);
|
|
518
|
+
|
|
519
|
+
const builtinAgents = applyBuiltinOverrides(
|
|
520
|
+
loadAgentsFromDir(BUILTIN_AGENTS_DIR, "builtin"),
|
|
521
|
+
scope === "project" ? {} : readBuiltinOverrides(userSettingsPath),
|
|
522
|
+
scope === "user" ? {} : readBuiltinOverrides(projectSettingsPath),
|
|
523
|
+
userSettingsPath,
|
|
524
|
+
projectSettingsPath,
|
|
525
|
+
);
|
|
241
526
|
|
|
242
527
|
const userAgentsOld = scope === "project" ? [] : loadAgentsFromDir(userDirOld, "user");
|
|
243
528
|
const userAgentsNew = scope === "project" ? [] : loadAgentsFromDir(userDirNew, "user");
|
|
@@ -256,12 +541,22 @@ export function discoverAgentsAll(cwd: string): {
|
|
|
256
541
|
chains: ChainConfig[];
|
|
257
542
|
userDir: string;
|
|
258
543
|
projectDir: string | null;
|
|
544
|
+
userSettingsPath: string;
|
|
545
|
+
projectSettingsPath: string | null;
|
|
259
546
|
} {
|
|
260
547
|
const userDirOld = path.join(os.homedir(), ".pi", "agent", "agents");
|
|
261
548
|
const userDirNew = path.join(os.homedir(), ".agents");
|
|
262
549
|
const projectDir = findNearestProjectAgentsDir(cwd);
|
|
263
|
-
|
|
264
|
-
const
|
|
550
|
+
const userSettingsPath = getUserAgentSettingsPath();
|
|
551
|
+
const projectSettingsPath = getProjectAgentSettingsPath(cwd);
|
|
552
|
+
|
|
553
|
+
const builtin = applyBuiltinOverrides(
|
|
554
|
+
loadAgentsFromDir(BUILTIN_AGENTS_DIR, "builtin"),
|
|
555
|
+
readBuiltinOverrides(userSettingsPath),
|
|
556
|
+
readBuiltinOverrides(projectSettingsPath),
|
|
557
|
+
userSettingsPath,
|
|
558
|
+
projectSettingsPath,
|
|
559
|
+
);
|
|
265
560
|
const user = [
|
|
266
561
|
...loadAgentsFromDir(userDirOld, "user"),
|
|
267
562
|
...loadAgentsFromDir(userDirNew, "user"),
|
|
@@ -275,5 +570,5 @@ export function discoverAgentsAll(cwd: string): {
|
|
|
275
570
|
|
|
276
571
|
const userDir = fs.existsSync(userDirNew) ? userDirNew : userDirOld;
|
|
277
572
|
|
|
278
|
-
return { builtin, user, project, chains, userDir, projectDir };
|
|
573
|
+
return { builtin, user, project, chains, userDir, projectDir, userSettingsPath, projectSettingsPath };
|
|
279
574
|
}
|
package/artifacts.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import type
|
|
5
|
-
|
|
6
|
-
const TEMP_ARTIFACTS_DIR = path.join(os.tmpdir(), "pi-subagent-artifacts");
|
|
4
|
+
import { TEMP_ARTIFACTS_DIR, type ArtifactPaths } from "./types.ts";
|
|
7
5
|
const CLEANUP_MARKER_FILE = ".last-cleanup";
|
|
8
6
|
|
|
9
7
|
export function getArtifactsDir(sessionFile: string | null): string {
|
|
@@ -64,7 +62,10 @@ export function cleanupOldArtifacts(dir: string, maxAgeDays: number): void {
|
|
|
64
62
|
if (stat.mtimeMs < cutoff) {
|
|
65
63
|
fs.unlinkSync(filePath);
|
|
66
64
|
}
|
|
67
|
-
} catch {
|
|
65
|
+
} catch {
|
|
66
|
+
// Artifact cleanup is best-effort housekeeping. Skip files that disappear
|
|
67
|
+
// or become unreadable while scanning so one bad entry does not block the rest.
|
|
68
|
+
}
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
fs.writeFileSync(markerPath, String(now));
|
|
@@ -80,6 +81,8 @@ export function cleanupAllArtifactDirs(maxAgeDays: number): void {
|
|
|
80
81
|
try {
|
|
81
82
|
dirs = fs.readdirSync(sessionsBase);
|
|
82
83
|
} catch {
|
|
84
|
+
// Session artifact cleanup is best-effort. If the sessions root cannot be read,
|
|
85
|
+
// skip cleanup instead of failing extension startup.
|
|
83
86
|
return;
|
|
84
87
|
}
|
|
85
88
|
|
|
@@ -87,6 +90,9 @@ export function cleanupAllArtifactDirs(maxAgeDays: number): void {
|
|
|
87
90
|
const artifactsDir = path.join(sessionsBase, dir, "subagent-artifacts");
|
|
88
91
|
try {
|
|
89
92
|
cleanupOldArtifacts(artifactsDir, maxAgeDays);
|
|
90
|
-
} catch {
|
|
93
|
+
} catch {
|
|
94
|
+
// Session cleanup is best-effort. Keep going so one unreadable session dir
|
|
95
|
+
// does not block cleanup for the rest.
|
|
96
|
+
}
|
|
91
97
|
}
|
|
92
98
|
}
|