pi-agents-switch 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -53,7 +53,6 @@ The Builder will design the agent, write the AGENTS.md, and set everything up.
53
53
  ```
54
54
  ~/.pi/agents/<name>/
55
55
  ├── AGENTS.md ← YAML frontmatter + system prompt (all-in-one)
56
- ├── extensions/ ← agent-specific extensions (optional)
57
56
  ├── skills/ ← agent-specific skills (optional)
58
57
  └── prompts/ ← agent-specific prompt templates (optional)
59
58
  ```
@@ -80,14 +79,10 @@ excluded_tools:
80
79
  - edit
81
80
  - write
82
81
 
83
- # Extensions / skills (same inheritance pattern)
84
- extensions:
85
- - my-custom-ext
86
- noextensions:
87
- - annoying-ext
82
+ # Skills (same inheritance pattern)
88
83
  skills:
89
84
  - my-skill
90
- noskills:
85
+ excluded_skills:
91
86
  - noisy-skill
92
87
  ---
93
88
 
@@ -101,11 +96,11 @@ Your role and specific instructions go here.
101
96
 
102
97
  Each agent starts with PI's current config, then applies:
103
98
 
104
- 1. **Remove** items listed in `excluded_tools` / `noextensions` / `noskills`
105
- 2. **Add** items listed in `tools` / `extensions` / `skills`
99
+ 1. **Remove** items listed in `excluded_tools` / `excluded_skills`
100
+ 2. **Add** items listed in `tools` / `skills`
106
101
  3. If an item appears in **both** `tools` and `excluded_tools`, `tools` wins (explicit include beats exclude)
107
102
 
108
- Agent-specific folders (`extensions/`, `skills/`, `prompts/`) are always included in addition to inherited items.
103
+ Agent-specific folders (`skills/`, `prompts/`) are always included in addition to inherited items.
109
104
 
110
105
  ### Fallback Chain
111
106
 
@@ -113,7 +108,7 @@ When resolving an agent's config:
113
108
 
114
109
  1. **Agent-level**: `~/.pi/agents/<name>/AGENTS.md`
115
110
  2. **Project-level**: `<cwd>/.pi/agents/<name>/AGENTS.md` — overrides agent-level fields
116
- 3. **PI defaults**: tools/extensions/skills from your running PI config
111
+ 3. **PI defaults**: tools/skills from your running PI config
117
112
 
118
113
  ## Creating agents
119
114
 
@@ -172,10 +167,8 @@ Agents are auto-discovered from the filesystem — no registration needed.
172
167
  | `topP` | number | Nucleus sampling threshold (0-1) |
173
168
  | `tools` | string[] | Tools to ADD after inheritance |
174
169
  | `excluded_tools` | string[] | Tools to REMOVE from PI's inherited set |
175
- | `extensions` | string[] | Extensions to ADD |
176
- | `noextensions` | string[] | Extensions to REMOVE |
177
170
  | `skills` | string[] | Skills to ADD |
178
- | `noskills` | string[] | Skills to REMOVE |
171
+ | `excluded_skills` | string[] | Skills to REMOVE |
179
172
 
180
173
  The Markdown body (everything after the last `---`) is the **system prompt** injected before each agent turn.
181
174
 
package/frontmatter.ts CHANGED
@@ -170,8 +170,7 @@ export function serializeFrontmatter(fm: AgentFrontmatter): string {
170
170
 
171
171
  writeArray("tools", fm.tools);
172
172
  writeArray("excluded_tools", fm.excluded_tools);
173
- writeArray("extensions", fm.extensions);
174
- writeArray("excluded_extensions", fm.excluded_extensions ?? fm.noextensions);
173
+
175
174
  writeArray("skills", fm.skills);
176
175
  writeArray("excluded_skills", fm.excluded_skills ?? fm.noskills);
177
176
 
package/index.ts CHANGED
@@ -109,12 +109,6 @@ excluded_tools: # Tools to REMOVE from PI's default set
109
109
  tools: # Tools to ADD
110
110
  -what you want
111
111
 
112
- excluded_extensions: # Extensions to REMOVE
113
- - annoying-ext
114
-
115
- extensions: # Extensions to ADD
116
- - my-extension
117
-
118
112
  excluded_skills: # Skills to REMOVE
119
113
  - noisy-skill
120
114
 
@@ -132,14 +126,14 @@ Your behavior instructions...
132
126
 
133
127
  Every agent starts with what PI already has, then:
134
128
 
135
- 1. **Remove** items in \`excluded_tools\` / \`excluded_extensions\` / \`excluded_skills\`
136
- 2. **Add** items in \`tools\` / \`extensions\` / \`skills\`
129
+ 1. **Remove** items in \`excluded_tools\` / \`excluded_skills\`
130
+ 2. **Add** items in \`tools\` / \`skills\`
137
131
  3. - **Order matters.** If the same item appears in both an exclude field and an add field, the later declaration wins.
138
132
  - \`excluded_tools: [read]\` then \`tools: [read]\` → \`read\` is added.
139
133
  - \`tools: [read]\` then \`excluded_tools: [read]\` → \`read\` is removed.
140
134
  - \`*\` means "all" for that category.
141
135
 
142
- If you want to keep only a small set of tools/skills/extensions, exclude everything with \`*\` and add back what you need.
136
+ If you want to keep only a small set of tools/skills, exclude everything with \`*\` and add back what you need.
143
137
  If you want to exclude just a few items, add \`*\` to the include list and use the exclude to remove the unwanted ones.
144
138
 
145
139
  ### Agent Directory Structure
@@ -147,7 +141,6 @@ If you want to exclude just a few items, add \`*\` to the include list and use t
147
141
  \`\`\`
148
142
  ~/.pi/agents/<name>/
149
143
  ├── AGENTS.md ← YAML frontmatter + system prompt (THE ONLY REQUIRED FILE)
150
- ├── extensions/ ← Agent-specific extensions (mkdir)
151
144
  ├── skills/ ← Agent-specific skills (mkdir)
152
145
  └── prompts/ ← Agent-specific prompt templates (mkdir)
153
146
  \`\`\`
@@ -166,7 +159,7 @@ Inventory what's available so you can make grounded recommendations:
166
159
  4. **Skills currently available**: check \`<available_skills>\` block in your context, or \`ls ~/.pi/agent/skills/\` and \`ls ~/.agents/skills/\`
167
160
  5. **Extensions installed**: \`ls ~/.pi/agent/npm/node_modules/\` for pi-* packages
168
161
 
169
- **Never** recommend models, tools, skills, or extensions the user doesn't have installed.
162
+ **Never** recommend models, tools, or skills the user doesn't have installed.
170
163
 
171
164
  ### Phase 1 — Gather requirements (single \`ask_user_question\` call, 4-6 questions)
172
165
 
@@ -181,7 +174,7 @@ Cover every parameter the agent needs. Skip this phase only if the user's reques
181
174
  | 3 | **Model** | Provider + model (from Phase 0 catalog); \`thinkingLevel\`: off / minimal / low / medium / high / xhigh |
182
175
  | 4 | **Tools** | Which to add/remove? Start from a preset, let user customize |
183
176
  | 5 | **Skills** | Which to add (\`skills\`) or remove (\`excluded_skills\`)? Reference Phase 0 catalog |
184
- | 6 | **Extensions** | Which to add (\`extensions\`) or remove (\`excluded_extensions\`)? Reference Phase 0 catalog |
177
+
185
178
 
186
179
  **Tool presets** (offer as starting points):
187
180
 
@@ -192,7 +185,7 @@ Cover every parameter the agent needs. Skip this phase only if the user's reques
192
185
  | 🛠️ Full-access | (inherit all PI defaults) | — | Building, refactoring, full-stack work |
193
186
  | 📦 Minimal | \`[bash, read, write]\` | everything else | Shell scripting, simple file ops |
194
187
 
195
- If user picks a preset, still confirm skills/extensions/model choices.
188
+ If user picks a preset, still confirm skills/model choices.
196
189
 
197
190
  ### Phase 2 — Build
198
191
 
@@ -204,7 +197,7 @@ If user picks a preset, still confirm skills/extensions/model choices.
204
197
  ### Phase 3 — Verify & deliver
205
198
 
206
199
  - Confirm file exists; spot-check YAML frontmatter
207
- - Report: display name, switch command (\`/switch <name>\`), provider/model, tool preset, skills/extensions delta
200
+ - Report: display name, switch command (\`/switch <name>\`), provider/model, tool preset, skills delta
208
201
  - Offer: "Switch now with \`/switch <name>\` to test."
209
202
  `;
210
203
 
@@ -268,11 +261,48 @@ export default function agentsSwitch(pi: ExtensionAPI) {
268
261
  function getPIState(): PIState {
269
262
  return {
270
263
  tools: pi.getActiveTools(),
271
- extensions: [],
272
264
  skills: [],
273
265
  };
274
266
  }
275
267
 
268
+ // ─── Skill formatting helpers ────────────────────────
269
+
270
+ function escapeXml(str: string): string {
271
+ return str
272
+ .replace(/&/g, "&amp;")
273
+ .replace(/</g, "&lt;")
274
+ .replace(/>/g, "&gt;")
275
+ .replace(/"/g, "&quot;")
276
+ .replace(/'/g, "&apos;");
277
+ }
278
+
279
+ function formatSkillsBlock(
280
+ skills: Array<{
281
+ name: string;
282
+ description: string;
283
+ filePath: string;
284
+ }>,
285
+ ): string {
286
+ const lines = [
287
+ "\n\nThe following skills provide specialized instructions for specific tasks.",
288
+ "Use the read tool to load a skill's file when the task matches its description.",
289
+ "When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.",
290
+ "",
291
+ "<available_skills>",
292
+ ];
293
+ for (const skill of skills) {
294
+ lines.push(" <skill>");
295
+ lines.push(` <name>${escapeXml(skill.name)}</name>`);
296
+ lines.push(
297
+ ` <description>${escapeXml(skill.description)}</description>`,
298
+ );
299
+ lines.push(` <location>${escapeXml(skill.filePath)}</location>`);
300
+ lines.push(" </skill>");
301
+ }
302
+ lines.push("</available_skills>");
303
+ return lines.join("\n");
304
+ }
305
+
276
306
  // ─── State snapshot / restore ───────────────────────
277
307
 
278
308
  function snapshotPIState(): void {
@@ -699,35 +729,86 @@ export default function agentsSwitch(pi: ExtensionAPI) {
699
729
  pi.on("before_agent_start", async (event, ctx) => {
700
730
  initPM(ctx.cwd);
701
731
  loadCurrentAgent(pm.loadConfig());
702
- // Tools/model/thinking are applied at session_start and switch time,
703
- // so here we only handle text injection into the already-correct prompt.
704
732
  updateStatus(ctx);
733
+
734
+ if (currentAgent === PI_AGENT_NAME) return;
735
+
705
736
  const section = getAgentSystemPromptSection();
706
- if (!section) return;
707
-
708
- // Replace the default role line with the agent's identity banner.
709
- // Everything else (tools, guidelines, skills, etc.) stays intact.
710
- const roleStart = event.systemPrompt.indexOf(DEFAULT_ROLE_LINE);
711
- if (roleStart >= 0) {
712
- const roleEnd = roleStart + DEFAULT_ROLE_LINE.length;
713
- const newPrompt =
714
- event.systemPrompt.substring(0, roleStart) +
715
- section +
716
- event.systemPrompt.substring(roleEnd);
717
- return { systemPrompt: newPrompt };
737
+ let newPrompt = event.systemPrompt;
738
+
739
+ // Strip Pi documentation section for non-PI agents
740
+ const piDocsMarker =
741
+ "Pi documentation (read only when the user asks about pi itself";
742
+ const piDocsStart = newPrompt.indexOf(piDocsMarker);
743
+ if (piDocsStart >= 0) {
744
+ const currentDateMarker = "\nCurrent date:";
745
+ const currentDateStart = newPrompt.indexOf(
746
+ currentDateMarker,
747
+ piDocsStart,
748
+ );
749
+ if (currentDateStart >= 0) {
750
+ newPrompt =
751
+ newPrompt.substring(0, piDocsStart) +
752
+ newPrompt.substring(currentDateStart);
753
+ }
754
+ }
755
+
756
+ // Replace or prepend agent identity
757
+ if (section) {
758
+ const roleStart = newPrompt.indexOf(DEFAULT_ROLE_LINE);
759
+ if (roleStart >= 0) {
760
+ const roleEnd = roleStart + DEFAULT_ROLE_LINE.length;
761
+ newPrompt =
762
+ newPrompt.substring(0, roleStart) +
763
+ section +
764
+ newPrompt.substring(roleEnd);
765
+ } else {
766
+ newPrompt = section + "\n\n" + newPrompt;
767
+ }
768
+ }
769
+
770
+ // Append skills section if agent has resolved skills
771
+ if (currentResolved && currentResolved.skillNames.length > 0) {
772
+ const loadedSkills = (event.systemPromptOptions as { skills?: any[] })
773
+ .skills;
774
+ let selectedSkills: Array<{
775
+ name: string;
776
+ description: string;
777
+ filePath: string;
778
+ }>;
779
+
780
+ if (loadedSkills && loadedSkills.length > 0) {
781
+ selectedSkills = loadedSkills.filter((s) =>
782
+ currentResolved!.skillNames.includes(s.name),
783
+ );
784
+ } else {
785
+ selectedSkills = pm.getSkillObjects(currentResolved.skillNames);
786
+ }
787
+
788
+ if (selectedSkills.length > 0) {
789
+ const skillsBlock = formatSkillsBlock(selectedSkills);
790
+ const currentDateMarker = "\nCurrent date:";
791
+ const idx = newPrompt.indexOf(currentDateMarker);
792
+ if (idx >= 0) {
793
+ newPrompt =
794
+ newPrompt.substring(0, idx) +
795
+ skillsBlock +
796
+ newPrompt.substring(idx);
797
+ }
798
+ }
718
799
  }
719
800
 
720
- // Fallback: if the role line wasn't found (e.g. custom SYSTEM.md),
721
- // prepend the agent section at the top instead of appending.
722
- return { systemPrompt: section + "\n\n" + event.systemPrompt };
801
+ return { systemPrompt: newPrompt };
723
802
  });
724
803
 
725
- // ─── before_provider_request ────────────────────────
804
+ // ─── before_provider_request: enforce tool set + sanitize ─
726
805
 
727
806
  pi.on("before_provider_request", (event) => {
728
- if (!currentResolved) return;
807
+ if (!currentResolved || currentAgent === PI_AGENT_NAME) return;
729
808
  const payload = event.payload as Record<string, unknown>;
730
809
  let changed = false;
810
+
811
+ // Temperature / topP
731
812
  if (currentResolved.temperature !== undefined) {
732
813
  payload.temperature = currentResolved.temperature;
733
814
  changed = true;
@@ -736,6 +817,111 @@ export default function agentsSwitch(pi: ExtensionAPI) {
736
817
  payload.top_p = currentResolved.topP;
737
818
  changed = true;
738
819
  }
820
+
821
+ // ---- Sanitization: prevent other extensions from contaminating ----
822
+ // Other extensions (e.g. pi-lens) may override our tools and prompt
823
+ // in before_agent_start (last-runner-wins). We re-apply our config
824
+ // here as the final defence before the API call.
825
+
826
+ // 1. Filter tools — only allow resolved tools
827
+ const allowedTools = new Set(currentResolved.tools);
828
+ const rawTools = payload.tools as
829
+ | Array<{ function?: { name?: string } }>
830
+ | undefined;
831
+ if (rawTools && rawTools.length > 0) {
832
+ const before = rawTools.length;
833
+ payload.tools = rawTools.filter((t) =>
834
+ allowedTools.has(t?.function?.name ?? ""),
835
+ );
836
+ if ((payload.tools as Array<any>).length !== before) changed = true;
837
+ }
838
+
839
+ // 2. Clean system message — replace contaminated sections
840
+ const messages = payload.messages as
841
+ | Array<{ role: string; content: string }>
842
+ | undefined;
843
+ if (messages && messages.length > 0 && messages[0]?.role === "system") {
844
+ let content: string = messages[0].content;
845
+ const originalLen = content.length;
846
+
847
+ // Strip the "Available tools:" block (other extensions may inject their own)
848
+ // Format: "Available tools:\n- name: desc\n..."
849
+ // We replace it with our clean version.
850
+ const toolsMarker = "\nAvailable tools:";
851
+ const toolsIdx = content.indexOf(toolsMarker);
852
+ if (toolsIdx >= 0) {
853
+ // Find where the tools block ends (next section or empty line before next section)
854
+ const nextSection = content.indexOf(
855
+ "\nIn addition to the tools above",
856
+ toolsIdx,
857
+ );
858
+ if (nextSection >= 0) {
859
+ // Build clean tools list
860
+ if (currentResolved.tools.length === 0) {
861
+ content =
862
+ content.substring(0, toolsIdx) +
863
+ "\nAvailable tools:\n(none)\n" +
864
+ content.substring(nextSection);
865
+ } else {
866
+ // Rebuild clean tools from tool snippets if available
867
+ const snippets = pi.getAllTools();
868
+ const cleanLines = currentResolved.tools
869
+ .map((name) => {
870
+ const tool = snippets.find(
871
+ (t) => t.name === name,
872
+ );
873
+ return tool
874
+ ? `- ${tool.name}: ${tool.description ?? ""}`
875
+ : `- ${name}`;
876
+ })
877
+ .join("\n");
878
+ content =
879
+ content.substring(0, toolsIdx) +
880
+ `\nAvailable tools:\n${cleanLines}\n` +
881
+ content.substring(nextSection);
882
+ }
883
+ }
884
+ }
885
+
886
+ // Strip the <available_skills> block (other extensions might add extra skills)
887
+ // Our before_agent_start handler manages this — any extras are contamination.
888
+ const skillsOpen = "\n<available_skills>";
889
+ const skillsIdx = content.indexOf(skillsOpen);
890
+ if (skillsIdx >= 0) {
891
+ const skillsEnd = content.indexOf("\n</available_skills>", skillsIdx);
892
+ if (skillsEnd >= 0) {
893
+ content =
894
+ content.substring(0, skillsIdx) +
895
+ content.substring(
896
+ skillsEnd + "\n</available_skills>".length,
897
+ );
898
+ }
899
+ }
900
+
901
+ // Strip "Pi documentation" block (pi-lens may re-add after our handler strips it)
902
+ const piDocsMarker =
903
+ "Pi documentation (read only when the user asks about pi itself";
904
+ const piDocsStart = content.indexOf(piDocsMarker);
905
+ if (piDocsStart >= 0) {
906
+ const currentDateMarker = "\nCurrent date:";
907
+ const currentDateStart = content.indexOf(
908
+ currentDateMarker,
909
+ piDocsStart,
910
+ );
911
+ if (currentDateStart >= 0) {
912
+ content =
913
+ content.substring(0, piDocsStart) +
914
+ content.substring(currentDateStart);
915
+ }
916
+ }
917
+
918
+ if (content.length !== originalLen) {
919
+ messages[0].content = content;
920
+ changed = true;
921
+ }
922
+ }
923
+
924
+ // Return payload only if we changed something (otherwise Pi uses default)
739
925
  if (changed) return payload;
740
926
  });
741
927
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-agents-switch",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Tab to switch primary agents in Pi — like OpenCode's agent switching. Each agent gets an isolated profile with its own AGENTS.md, extensions, skills, and settings.",
5
5
  "type": "module",
6
6
  "pi": {
@@ -56,8 +56,6 @@ const MAX_NAME_LENGTH = 32;
56
56
  export interface PIState {
57
57
  /** Active tool names in PI */
58
58
  tools: string[];
59
- /** Active extension names in PI */
60
- extensions: string[];
61
59
  /** Active skill names in PI */
62
60
  skills: string[];
63
61
  }
@@ -341,7 +339,7 @@ export class ProfileManager {
341
339
  agentPath?: string,
342
340
  projectPath?: string,
343
341
  ): ResolvedAgentConfig {
344
- const pi = piState ?? { tools: [], extensions: [], skills: [] };
342
+ const pi = piState ?? { tools: [], skills: [] };
345
343
 
346
344
  // Apply inheritance: start with PI's → respect last-write-wins
347
345
  const tools = this.resolveList(
@@ -353,25 +351,6 @@ export class ProfileManager {
353
351
  keyOrder,
354
352
  );
355
353
 
356
- // Extensions: prefer excluded_extensions, fall back to deprecated noextensions
357
- const extExclude = fm.excluded_extensions ?? fm.noextensions;
358
- const extExcludeKey =
359
- fm.excluded_extensions !== undefined
360
- ? "excluded_extensions"
361
- : fm.noextensions !== undefined
362
- ? "noextensions"
363
- : undefined;
364
- const extensionPaths = extExcludeKey
365
- ? this.resolveList(
366
- pi.extensions,
367
- extExclude,
368
- fm.extensions,
369
- "extensions",
370
- extExcludeKey,
371
- keyOrder,
372
- )
373
- : [...pi.extensions, ...(fm.extensions ?? [])];
374
-
375
354
  // Skills: prefer excluded_skills, fall back to deprecated noskills
376
355
  const skillExclude = fm.excluded_skills ?? fm.noskills;
377
356
  const skillExcludeKey =
@@ -391,22 +370,6 @@ export class ProfileManager {
391
370
  )
392
371
  : [...pi.skills, ...(fm.skills ?? [])];
393
372
 
394
- // Add agent-specific folders
395
- const agentExtensions: string[] = [];
396
- const agentSkills: string[] = [];
397
-
398
- const effectiveAgentDir = projectPath ?? agentPath;
399
- if (effectiveAgentDir) {
400
- const extDir = join(effectiveAgentDir, "extensions");
401
- if (existsSync(extDir)) {
402
- agentExtensions.push(extDir);
403
- }
404
- const skillDir = join(effectiveAgentDir, "skills");
405
- if (existsSync(skillDir)) {
406
- agentSkills.push(skillDir);
407
- }
408
- }
409
-
410
373
  const sourcePath = projectPath ?? agentPath ?? "PI (default)";
411
374
 
412
375
  const modelStr =
@@ -422,8 +385,7 @@ export class ProfileManager {
422
385
  topP: fm.topP,
423
386
  systemPrompt: "", // filled in later by caller
424
387
  tools,
425
- extensionPaths: [...extensionPaths, ...agentExtensions],
426
- skillPaths: [...skillPaths, ...agentSkills],
388
+ skillNames: skillPaths,
427
389
  sourcePath,
428
390
  exists: true,
429
391
  };
@@ -494,6 +456,54 @@ export class ProfileManager {
494
456
  return result;
495
457
  }
496
458
 
459
+ /**
460
+ * Look up full skill objects by name from Pi's standard skill directories.
461
+ * Returns name, description, and filePath for each skill found.
462
+ */
463
+ getSkillObjects(
464
+ skillNames: string[],
465
+ ): Array<{ name: string; description: string; filePath: string }> {
466
+ const result: Array<{
467
+ name: string;
468
+ description: string;
469
+ filePath: string;
470
+ }> = [];
471
+
472
+ for (const name of skillNames) {
473
+ const skill = this.findSkillOnDisk(name);
474
+ if (skill) result.push(skill);
475
+ }
476
+
477
+ return result;
478
+ }
479
+
480
+ private findSkillOnDisk(
481
+ name: string,
482
+ ): { name: string; description: string; filePath: string } | undefined {
483
+ const locations = [join(this.piAgentDir, "skills", name, "SKILL.md")];
484
+
485
+ if (this.cwd) {
486
+ locations.push(join(this.cwd, ".pi", "skills", name, "SKILL.md"));
487
+ }
488
+
489
+ for (const loc of locations) {
490
+ if (!existsSync(loc)) continue;
491
+ try {
492
+ const content = readFileSync(loc, "utf8");
493
+ const { frontmatter } = parseFrontmatter(content);
494
+ return {
495
+ name: frontmatter.name ?? name,
496
+ description: frontmatter.description ?? "",
497
+ filePath: loc,
498
+ };
499
+ } catch {
500
+ // Skip unreadable files
501
+ }
502
+ }
503
+
504
+ return undefined;
505
+ }
506
+
497
507
  // ─── Agent creation / deletion ───────────────────────
498
508
 
499
509
  validateName(name: string): void {
package/types.ts CHANGED
@@ -46,17 +46,6 @@ export interface AgentFrontmatter {
46
46
  */
47
47
  excluded_tools?: string[];
48
48
 
49
- // ── Extension lists ──
50
- /** Extensions to ADD. */
51
- extensions?: string[];
52
- /**
53
- * Extensions to REMOVE (from PI's inherited set). Supports `"*"` wildcard.
54
- * Preferred over deprecated `noextensions`.
55
- */
56
- excluded_extensions?: string[];
57
- /** @deprecated Use `excluded_extensions` instead. Still recognized for backward compat. */
58
- noextensions?: string[];
59
-
60
49
  // ── Skill lists ──
61
50
  /** Skills to ADD. */
62
51
  skills?: string[];
@@ -107,10 +96,8 @@ export interface ResolvedAgentConfig {
107
96
  systemPrompt: string;
108
97
  /** Resolved tools for this agent */
109
98
  tools: string[];
110
- /** Resolved extensions for this agent (from agent-specific folder) */
111
- extensionPaths: string[];
112
- /** Resolved skills for this agent (from agent-specific folder) */
113
- skillPaths: string[];
99
+ /** Resolved skill names for this agent */
100
+ skillNames: string[];
114
101
  /** Source of this agent (path to AGENTS.md used) */
115
102
  sourcePath: string;
116
103
  /** Whether this agent exists on disk */