@fickydev/pigent 0.1.21 → 0.1.22
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 +9 -0
- package/TODO.md +3 -0
- package/drizzle/migrations/0007_session_skills.sql +1 -0
- package/drizzle/migrations/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/src/agents/AgentRunner.ts +48 -10
- package/src/agents/BotCommandHandler.ts +110 -0
- package/src/config/skillRefs.ts +50 -0
- package/src/daemon/AgentDaemon.ts +1 -0
- package/src/db/client.ts +1 -0
- package/src/db/repositories/SessionRepository.ts +12 -0
- package/src/db/schema.ts +1 -0
- package/src/pi/PiAgentRunner.ts +7 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.22 - 2026-05-18
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Added `/skills` command to list all known skills with active/inactive flags and source labels for the current chat session.
|
|
8
|
+
- Added session-scoped dynamic skill loading with `/skills add <skill>`, `/skills remove <skill>`, and `/skills clear`.
|
|
9
|
+
- Added current skill inventory to each agent prompt so users can ask natural questions like "what skills do you have?".
|
|
10
|
+
- Added `agent_sessions.session_skills` persistence and migration.
|
|
11
|
+
|
|
3
12
|
## 0.1.21 - 2026-05-18
|
|
4
13
|
|
|
5
14
|
### Fixed
|
package/TODO.md
CHANGED
|
@@ -146,6 +146,9 @@
|
|
|
146
146
|
- [x] Make README more end-user facing
|
|
147
147
|
- [x] Confirm per-agent system prompt injection
|
|
148
148
|
- [x] Confirm per-agent skills loading
|
|
149
|
+
- [x] Add `/skills` command for current session skills with active/inactive flags
|
|
150
|
+
- [x] Add session-scoped dynamic skill loading via `/skills add/remove/clear`
|
|
151
|
+
- [x] Add skill inventory context so agents can answer natural-language skill questions
|
|
149
152
|
- [x] Confirm per-agent extensions loading
|
|
150
153
|
- [ ] Confirm per-agent custom tools loading
|
|
151
154
|
- [x] Prototype one prompt through Pi SDK
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE `agent_sessions` ADD `session_skills` text DEFAULT '[]' NOT NULL;
|
package/package.json
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { InboundMessage } from "../channels/types";
|
|
3
3
|
import type { LoadedAgentConfig } from "../config/schemas";
|
|
4
|
+
import { hasSkillRef, listBuiltInSkills, parseSessionSkills, skillDisplayName, uniqueSkillRefs } from "../config/skillRefs";
|
|
4
5
|
import type { Repositories } from "../db/repositories";
|
|
6
|
+
import type { AgentSessionRow } from "../db/schema";
|
|
5
7
|
import { logger } from "../logging/logger";
|
|
6
8
|
import { PiAgentRunner } from "../pi/PiAgentRunner";
|
|
7
9
|
import { createTaskTools, type TaskToolContext } from "../pi/tools/taskTools";
|
|
@@ -117,8 +119,8 @@ export class AgentRunner {
|
|
|
117
119
|
agent,
|
|
118
120
|
profile: this.registry.getProfile(agent.profile),
|
|
119
121
|
session,
|
|
120
|
-
prompt: this.composePrompt(input, chatInstructions),
|
|
121
|
-
customTools: this.buildCustomTools(agent, input.message),
|
|
122
|
+
prompt: this.composePrompt(input, session, chatInstructions),
|
|
123
|
+
customTools: this.buildCustomTools(agent, session, input.message),
|
|
122
124
|
});
|
|
123
125
|
|
|
124
126
|
if (session.piSessionId !== result.piSessionId || session.piSessionPath !== result.piSessionPath) {
|
|
@@ -145,10 +147,10 @@ export class AgentRunner {
|
|
|
145
147
|
}
|
|
146
148
|
}
|
|
147
149
|
|
|
148
|
-
private buildCustomTools(agent: LoadedAgentConfig, message: InboundMessage): ToolDefinition[] | undefined {
|
|
150
|
+
private buildCustomTools(agent: LoadedAgentConfig, session: AgentSessionRow, message: InboundMessage): ToolDefinition[] | undefined {
|
|
149
151
|
const tools: ToolDefinition[] = [];
|
|
150
152
|
|
|
151
|
-
if (this.hasSkill(agent, "task-management") && this.scheduler) {
|
|
153
|
+
if (this.hasSkill(agent, session, "task-management") && this.scheduler) {
|
|
152
154
|
const ctx: TaskToolContext = {
|
|
153
155
|
agentId: agent.id,
|
|
154
156
|
channel: message.channel,
|
|
@@ -173,18 +175,17 @@ export class AgentRunner {
|
|
|
173
175
|
return tools.length > 0 ? tools : undefined;
|
|
174
176
|
}
|
|
175
177
|
|
|
176
|
-
private hasSkill(agent: LoadedAgentConfig, skillName: string): boolean {
|
|
178
|
+
private hasSkill(agent: LoadedAgentConfig, session: AgentSessionRow, skillName: string): boolean {
|
|
177
179
|
const allSkills = [
|
|
178
180
|
...(agent.profile ? (this.registry.getProfile(agent.profile)?.defaultSkills ?? []) : []),
|
|
179
181
|
...agent.skills,
|
|
182
|
+
...parseSessionSkills(session.sessionSkills),
|
|
180
183
|
];
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return base === skillName || s === skillName;
|
|
184
|
-
});
|
|
184
|
+
|
|
185
|
+
return hasSkillRef(allSkills, skillName);
|
|
185
186
|
}
|
|
186
187
|
|
|
187
|
-
private composePrompt(input: AgentRunInput, chatInstructions: string): string {
|
|
188
|
+
private composePrompt(input: AgentRunInput, session: AgentSessionRow, chatInstructions: string): string {
|
|
188
189
|
const parts: string[] = [
|
|
189
190
|
`[Channel]\n${input.message.channel}`,
|
|
190
191
|
`[Chat]\n${input.message.chatId}`,
|
|
@@ -202,11 +203,48 @@ export class AgentRunner {
|
|
|
202
203
|
parts.push(`[Chat Instructions]\n${chatInstructions}`);
|
|
203
204
|
}
|
|
204
205
|
|
|
206
|
+
const skillsContext = this.skillsContext(input.agentId, session);
|
|
207
|
+
if (skillsContext) {
|
|
208
|
+
parts.push(`[Skills]\n${skillsContext}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
205
211
|
parts.push(`[Message]\n${input.text}`);
|
|
206
212
|
|
|
207
213
|
return parts.join("\n\n");
|
|
208
214
|
}
|
|
209
215
|
|
|
216
|
+
private skillsContext(agentId: string, session: AgentSessionRow): string {
|
|
217
|
+
const agent = this.registry.getAgent(agentId);
|
|
218
|
+
if (!agent) return "";
|
|
219
|
+
|
|
220
|
+
const profile = this.registry.getProfile(agent.profile);
|
|
221
|
+
const profileSkills = profile?.defaultSkills ?? [];
|
|
222
|
+
const agentSkills = agent.skills;
|
|
223
|
+
const sessionSkills = parseSessionSkills(session.sessionSkills);
|
|
224
|
+
const builtInSkills = listBuiltInSkills(import.meta.dir);
|
|
225
|
+
const activeSkills = uniqueSkillRefs([...profileSkills, ...agentSkills, ...sessionSkills]);
|
|
226
|
+
const knownSkills = uniqueSkillRefs([...builtInSkills, ...activeSkills]).sort((a, b) =>
|
|
227
|
+
skillDisplayName(a).localeCompare(skillDisplayName(b)),
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
if (knownSkills.length === 0) return "No skills are configured or available.";
|
|
231
|
+
|
|
232
|
+
return knownSkills
|
|
233
|
+
.map((skill) => {
|
|
234
|
+
const name = skillDisplayName(skill);
|
|
235
|
+
const active = hasSkillRef(activeSkills, name);
|
|
236
|
+
const sources = [];
|
|
237
|
+
|
|
238
|
+
if (hasSkillRef(profileSkills, name)) sources.push("profile");
|
|
239
|
+
if (hasSkillRef(agentSkills, name)) sources.push("agent");
|
|
240
|
+
if (hasSkillRef(sessionSkills, name)) sources.push("session");
|
|
241
|
+
if (hasSkillRef(builtInSkills, name)) sources.push("built-in");
|
|
242
|
+
|
|
243
|
+
return `- ${name}: ${active ? "active" : "available, inactive"}${sources.length > 0 ? ` (${sources.join(", ")})` : ""}`;
|
|
244
|
+
})
|
|
245
|
+
.join("\n");
|
|
246
|
+
}
|
|
247
|
+
|
|
210
248
|
private async chatInstructions(input: AgentRunInput): Promise<string> {
|
|
211
249
|
if (input.message.channel !== "telegram") return "";
|
|
212
250
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AuthStorage, ModelRegistry } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { InboundMessage, InlineKeyboardButton } from "../channels/types";
|
|
3
3
|
import type { LoadedAgentConfig, LoadedProfileConfig, ModelChoiceConfig } from "../config/schemas";
|
|
4
|
+
import { hasSkillRef, listBuiltInSkills, parseSessionSkills, skillDisplayName, uniqueSkillRefs } from "../config/skillRefs";
|
|
4
5
|
import type { AgentSessionRow } from "../db/schema";
|
|
5
6
|
import type { Repositories } from "../db/repositories";
|
|
6
7
|
import { PiAgentRunner, type PiContextUsage } from "../pi/PiAgentRunner";
|
|
@@ -47,6 +48,8 @@ export class BotCommandHandler {
|
|
|
47
48
|
return { handled: true, text: await this.newSessionText(message) };
|
|
48
49
|
case "/status":
|
|
49
50
|
return { handled: true, text: await this.statusText(message) };
|
|
51
|
+
case "/skills":
|
|
52
|
+
return { handled: true, text: await this.skillsText(message) };
|
|
50
53
|
case "/model":
|
|
51
54
|
return await this.modelResult(message);
|
|
52
55
|
case "/thinking":
|
|
@@ -71,6 +74,9 @@ export class BotCommandHandler {
|
|
|
71
74
|
"/agents - list agents for this chat",
|
|
72
75
|
"/new - start a fresh session for this chat",
|
|
73
76
|
"/status - show current session status",
|
|
77
|
+
"/skills - list skills for this chat session",
|
|
78
|
+
"/skills add <skill> - load a skill for this session",
|
|
79
|
+
"/skills remove <skill> - unload a session-loaded skill",
|
|
74
80
|
"/model - show model picker for this chat session",
|
|
75
81
|
"/model <provider/modelId> - set model for this chat session",
|
|
76
82
|
"/model default - clear model override for this chat session",
|
|
@@ -191,6 +197,83 @@ export class BotCommandHandler {
|
|
|
191
197
|
};
|
|
192
198
|
}
|
|
193
199
|
|
|
200
|
+
private async skillsText(message: InboundMessage): Promise<string> {
|
|
201
|
+
const sessionResult = await this.getDefaultSession(message);
|
|
202
|
+
if (!sessionResult.ok) return sessionResult.message;
|
|
203
|
+
|
|
204
|
+
const [, actionRaw, ...args] = message.text.trim().split(/\s+/);
|
|
205
|
+
const action = actionRaw?.toLowerCase() ?? "list";
|
|
206
|
+
const skillRef = args.join(" ").trim();
|
|
207
|
+
|
|
208
|
+
if (action === "add") {
|
|
209
|
+
if (!skillRef) return "Usage: /skills add <skill>";
|
|
210
|
+
|
|
211
|
+
const current = parseSessionSkills(sessionResult.session.sessionSkills);
|
|
212
|
+
const next = uniqueSkillRefs([...current, skillRef]);
|
|
213
|
+
await this.repositories.sessions.updateSessionSkills(sessionResult.session.id, next);
|
|
214
|
+
|
|
215
|
+
return [
|
|
216
|
+
"Skill loaded for current session: " + skillRef,
|
|
217
|
+
"",
|
|
218
|
+
"It will be included on the next agent response in this chat session.",
|
|
219
|
+
"Use /skills to see active skills.",
|
|
220
|
+
].join("\n");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (action === "remove") {
|
|
224
|
+
if (!skillRef) return "Usage: /skills remove <skill>";
|
|
225
|
+
|
|
226
|
+
const current = parseSessionSkills(sessionResult.session.sessionSkills);
|
|
227
|
+
const next = current.filter((item) => item !== skillRef);
|
|
228
|
+
await this.repositories.sessions.updateSessionSkills(sessionResult.session.id, next);
|
|
229
|
+
|
|
230
|
+
return next.length === current.length
|
|
231
|
+
? "Session skill not found: " + skillRef
|
|
232
|
+
: "Skill removed from current session: " + skillRef;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (action === "clear") {
|
|
236
|
+
await this.repositories.sessions.updateSessionSkills(sessionResult.session.id, []);
|
|
237
|
+
return "Session-loaded skills cleared.";
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (action !== "list") {
|
|
241
|
+
return [
|
|
242
|
+
"Usage:",
|
|
243
|
+
"/skills - list skills",
|
|
244
|
+
"/skills add <skill> - load skill for this session",
|
|
245
|
+
"/skills remove <skill> - unload session skill",
|
|
246
|
+
"/skills clear - clear session-loaded skills",
|
|
247
|
+
].join("\n");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const agent = sessionResult.agent;
|
|
251
|
+
const profile = this.registry.getProfile(agent.profile);
|
|
252
|
+
const profileSkills = profile?.defaultSkills ?? [];
|
|
253
|
+
const agentSkills = agent.skills;
|
|
254
|
+
const sessionSkills = parseSessionSkills(sessionResult.session.sessionSkills);
|
|
255
|
+
const builtInSkills = listBuiltInSkills(import.meta.dir);
|
|
256
|
+
const activeSkills = uniqueSkillRefs([...profileSkills, ...agentSkills, ...sessionSkills]);
|
|
257
|
+
const knownSkills = uniqueSkillRefs([...builtInSkills, ...activeSkills]).sort((a, b) =>
|
|
258
|
+
skillDisplayName(a).localeCompare(skillDisplayName(b)),
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
return [
|
|
262
|
+
"Skills for current session",
|
|
263
|
+
"",
|
|
264
|
+
"Agent: " + agent.id,
|
|
265
|
+
"Session: " + sessionResult.session.id,
|
|
266
|
+
"",
|
|
267
|
+
"Available skills:",
|
|
268
|
+
...formatSkillRows(knownSkills, { profileSkills, agentSkills, sessionSkills, builtInSkills }),
|
|
269
|
+
"",
|
|
270
|
+
"Legend: ✅ active, ⬜ available but inactive",
|
|
271
|
+
"Load on the fly: /skills add <skill>",
|
|
272
|
+
"Unload: /skills remove <skill>",
|
|
273
|
+
"Clear session-loaded: /skills clear",
|
|
274
|
+
].join("\n");
|
|
275
|
+
}
|
|
276
|
+
|
|
194
277
|
private async modelResult(message: InboundMessage): Promise<BotCommandResult> {
|
|
195
278
|
const sessionResult = await this.getDefaultSession(message);
|
|
196
279
|
if (!sessionResult.ok) return { handled: true, text: sessionResult.message };
|
|
@@ -511,6 +594,33 @@ function formatDuration(ms: number): string {
|
|
|
511
594
|
return Math.floor(hours / 24) + "d " + (hours % 24) + "h";
|
|
512
595
|
}
|
|
513
596
|
|
|
597
|
+
function formatSkillRows(
|
|
598
|
+
skills: string[],
|
|
599
|
+
sources: { profileSkills: string[]; agentSkills: string[]; sessionSkills: string[]; builtInSkills: string[] },
|
|
600
|
+
): string[] {
|
|
601
|
+
if (skills.length === 0) return ["none"];
|
|
602
|
+
|
|
603
|
+
return skills.map((skill) => {
|
|
604
|
+
const name = skillDisplayName(skill);
|
|
605
|
+
const sourceLabels = [];
|
|
606
|
+
|
|
607
|
+
if (hasSkillRef(sources.profileSkills, name)) sourceLabels.push("profile");
|
|
608
|
+
if (hasSkillRef(sources.agentSkills, name)) sourceLabels.push("agent");
|
|
609
|
+
if (hasSkillRef(sources.sessionSkills, name)) sourceLabels.push("session");
|
|
610
|
+
if (hasSkillRef(sources.builtInSkills, name)) sourceLabels.push("built-in");
|
|
611
|
+
|
|
612
|
+
const active = sourceLabels.some((source) => source !== "built-in");
|
|
613
|
+
const marker = active ? "✅" : "⬜";
|
|
614
|
+
const sourceText = sourceLabels.length > 0 ? sourceLabels.join(", ") : "configured";
|
|
615
|
+
|
|
616
|
+
return `${marker} ${name} (${active ? "active" : "inactive"}; ${sourceText})`;
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function formatList(items: string[]): string {
|
|
621
|
+
return items.length > 0 ? items.join(", ") : "none";
|
|
622
|
+
}
|
|
623
|
+
|
|
514
624
|
function formatPath(path: string): string {
|
|
515
625
|
const home = process.env.HOME || process.env.USERPROFILE;
|
|
516
626
|
if (home && path.startsWith(home)) {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
export function parseSessionSkills(raw: string | null | undefined): string[] {
|
|
5
|
+
if (!raw) return [];
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
const parsed = JSON.parse(raw);
|
|
9
|
+
if (!Array.isArray(parsed)) return [];
|
|
10
|
+
|
|
11
|
+
return parsed.filter((item): item is string => typeof item === "string" && item.trim().length > 0).map((item) => item.trim());
|
|
12
|
+
} catch {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function skillDisplayName(ref: string): string {
|
|
18
|
+
const normalized = ref.replace(/\\/g, "/").replace(/\/$/, "");
|
|
19
|
+
return normalized.split("/").pop() || ref;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function hasSkillRef(refs: string[], skillName: string): boolean {
|
|
23
|
+
return refs.some((ref) => ref === skillName || skillDisplayName(ref) === skillName);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function uniqueSkillRefs(refs: string[]): string[] {
|
|
27
|
+
const seen = new Set<string>();
|
|
28
|
+
const unique: string[] = [];
|
|
29
|
+
|
|
30
|
+
for (const ref of refs.map((item) => item.trim()).filter(Boolean)) {
|
|
31
|
+
if (seen.has(ref)) continue;
|
|
32
|
+
seen.add(ref);
|
|
33
|
+
unique.push(ref);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return unique;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function listBuiltInSkills(fromDir: string): string[] {
|
|
40
|
+
const skillsDir = resolve(fromDir, "../../skills");
|
|
41
|
+
|
|
42
|
+
if (!existsSync(skillsDir)) return [];
|
|
43
|
+
|
|
44
|
+
return readdirSync(skillsDir)
|
|
45
|
+
.filter((name) => {
|
|
46
|
+
const path = resolve(skillsDir, name);
|
|
47
|
+
return statSync(path).isDirectory() && existsSync(resolve(path, "SKILL.md"));
|
|
48
|
+
})
|
|
49
|
+
.sort();
|
|
50
|
+
}
|
|
@@ -181,6 +181,7 @@ function telegramCommands() {
|
|
|
181
181
|
{ command: "agents", description: "List agents available in this chat" },
|
|
182
182
|
{ command: "new", description: "Start a fresh chat session" },
|
|
183
183
|
{ command: "status", description: "Show current session status" },
|
|
184
|
+
{ command: "skills", description: "List or load skills for this session" },
|
|
184
185
|
{ command: "model", description: "Choose model for this chat session" },
|
|
185
186
|
{ command: "thinking", description: "Choose thinking level for this chat session" },
|
|
186
187
|
{ command: "agent", description: "Send message to a specific agent" },
|
package/src/db/client.ts
CHANGED
|
@@ -30,6 +30,7 @@ function ensureRuntimeSchema(): void {
|
|
|
30
30
|
ensureColumn("agent_sessions", "active", "ALTER TABLE `agent_sessions` ADD `active` integer DEFAULT true NOT NULL");
|
|
31
31
|
ensureColumn("agent_sessions", "ended_at", "ALTER TABLE `agent_sessions` ADD `ended_at` integer");
|
|
32
32
|
ensureColumn("agent_sessions", "pi_session_path", "ALTER TABLE `agent_sessions` ADD `pi_session_path` text");
|
|
33
|
+
ensureColumn("agent_sessions", "session_skills", "ALTER TABLE `agent_sessions` ADD `session_skills` text DEFAULT '[]' NOT NULL");
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
function ensureColumn(table: string, column: string, statement: string): void {
|
|
@@ -75,6 +75,18 @@ export class SessionRepository {
|
|
|
75
75
|
return this.requireById(id);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
async updateSessionSkills(id: string, skills: string[]): Promise<AgentSessionRow> {
|
|
79
|
+
await this.db
|
|
80
|
+
.update(agentSessions)
|
|
81
|
+
.set({
|
|
82
|
+
sessionSkills: JSON.stringify(skills),
|
|
83
|
+
updatedAt: Date.now(),
|
|
84
|
+
})
|
|
85
|
+
.where(eq(agentSessions.id, id));
|
|
86
|
+
|
|
87
|
+
return this.requireById(id);
|
|
88
|
+
}
|
|
89
|
+
|
|
78
90
|
async findById(id: string): Promise<AgentSessionRow | null> {
|
|
79
91
|
const row = await this.db.query.agentSessions.findFirst({
|
|
80
92
|
where: eq(agentSessions.id, id),
|
package/src/db/schema.ts
CHANGED
|
@@ -51,6 +51,7 @@ export const agentSessions = sqliteTable(
|
|
|
51
51
|
instructionsHash: text("instructions_hash"),
|
|
52
52
|
model: text("model"),
|
|
53
53
|
thinkingLevel: text("thinking_level", { enum: ["off", "low", "medium", "high"] }),
|
|
54
|
+
sessionSkills: text("session_skills").notNull().default("[]"),
|
|
54
55
|
active: integer("active", { mode: "boolean" }).notNull().default(true),
|
|
55
56
|
endedAt: integer("ended_at"),
|
|
56
57
|
createdAt: integer("created_at").notNull(),
|
package/src/pi/PiAgentRunner.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import { mkdir } from "node:fs/promises";
|
|
11
11
|
import { resolve } from "node:path";
|
|
12
12
|
import type { LoadedAgentConfig, LoadedProfileConfig } from "../config/schemas";
|
|
13
|
+
import { parseSessionSkills } from "../config/skillRefs";
|
|
13
14
|
import type { AgentSessionRow } from "../db/schema";
|
|
14
15
|
import { resolveModelSelection } from "./PiModelResolver";
|
|
15
16
|
import { loadOrCreatePiSession } from "./PiSessionFactory";
|
|
@@ -82,7 +83,7 @@ export class PiAgentRunner {
|
|
|
82
83
|
compaction: { enabled: false },
|
|
83
84
|
});
|
|
84
85
|
const agentDir = getAgentDir();
|
|
85
|
-
const skillPaths = resolveSkillPaths(input.agent, input.profile);
|
|
86
|
+
const skillPaths = resolveSkillPaths(input.agent, input.profile, input.session);
|
|
86
87
|
const resourceLoader = new DefaultResourceLoader({
|
|
87
88
|
cwd: workspace,
|
|
88
89
|
agentDir,
|
|
@@ -114,7 +115,7 @@ export class PiAgentRunner {
|
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
function resolveSkillPaths(agent: LoadedAgentConfig, profile: LoadedProfileConfig | null): string[] {
|
|
118
|
+
function resolveSkillPaths(agent: LoadedAgentConfig, profile: LoadedProfileConfig | null, session: AgentSessionRow): string[] {
|
|
118
119
|
const paths: string[] = [];
|
|
119
120
|
|
|
120
121
|
if (profile) {
|
|
@@ -127,6 +128,10 @@ function resolveSkillPaths(agent: LoadedAgentConfig, profile: LoadedProfileConfi
|
|
|
127
128
|
paths.push(resolveSkillPath(s, agent.baseDir));
|
|
128
129
|
}
|
|
129
130
|
|
|
131
|
+
for (const s of parseSessionSkills(session.sessionSkills)) {
|
|
132
|
+
paths.push(resolveSkillPath(s, agent.baseDir));
|
|
133
|
+
}
|
|
134
|
+
|
|
130
135
|
return paths;
|
|
131
136
|
}
|
|
132
137
|
|