@youngjurry/pi-agents 0.7.3 → 0.7.4

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.4 - 2026-09-05
4
+
5
+ - Match Pi's progressive Skill disclosure for sub-agent Roles instead of eagerly injecting complete `SKILL.md` files.
6
+ - Let an omitted `skills` field inherit all discoverable Skills, an explicit list expose only those Skills, and `skills: []` expose none.
7
+ - Preserve explicit unknown-Skill validation and report inherited Role skills as `*` through on-demand Role discovery.
8
+
3
9
  ## 0.7.3 - 2026-09-05
4
10
 
5
11
  - Fold the full `wait_agent` status tree by default and reuse Pi's `Ctrl+O` expansion state to reveal it on demand.
package/README.md CHANGED
@@ -134,7 +134,7 @@ nickname_candidates: [Ada, Grace]
134
134
  Review carefully and return findings with exact paths.
135
135
  ```
136
136
 
137
- `skills` is optional. Each named Skill must be discoverable by Pi. Its complete `SKILL.md` is loaded into that Role's child system prompt, including for tightly restricted Roles that do not expose `read` or `bash`. Relative references remain rooted at the Skill's base directory. Unknown Skill names fail explicitly instead of silently weakening the Role. Roles without `skills` retain Pi's normal progressive disclosure: the Skill catalog appears only when `read` or `bash` is active, and the child decides whether to load a matching Skill.
137
+ `skills` optionally filters Pi's normal progressive Skill disclosure. Omit the field to inherit all discoverable Skills, specify names such as `skills: [document]` to expose only those Skill names/descriptions/paths, or use `skills: []` to expose none. The child still reads a matching `SKILL.md` on demand rather than placing complete Skill instructions in every prompt. As in Pi's main Agent, the catalog is shown only when `read` or `bash` is active. Unknown explicitly selected Skill names fail clearly instead of being silently ignored.
138
138
 
139
139
  ## Model configuration
140
140
 
package/control.ts CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  rootAgentInstructions,
24
24
  sanitizeForkMessages,
25
25
  } from "./context.ts";
26
- import { discoverRoles, formatAssignedSkills, resolveRole } from "./roles.ts";
26
+ import { discoverRoles, resolveRole, selectRoleSkills } from "./roles.ts";
27
27
  import {
28
28
  DEFAULT_CHILD_THINKING_LEVEL,
29
29
  DEFAULT_MAX_CONCURRENT_SUBAGENTS,
@@ -594,8 +594,7 @@ export class AgentControl {
594
594
  assignedSkillNames?: readonly string[],
595
595
  ): Promise<DefaultResourceLoader> {
596
596
  const selfPath = path.resolve(this.selfExtensionPath);
597
- let loader: DefaultResourceLoader;
598
- loader = new DefaultResourceLoader({
597
+ const loader = new DefaultResourceLoader({
599
598
  cwd,
600
599
  agentDir: getAgentDir(),
601
600
  settingsManager,
@@ -603,11 +602,14 @@ export class AgentControl {
603
602
  ...base,
604
603
  extensions: base.extensions.filter((extension) => path.resolve(extension.resolvedPath) !== selfPath),
605
604
  }),
606
- systemPromptOverride: (base) => [
607
- base || "You are a coding agent.",
608
- instructions,
609
- formatAssignedSkills(assignedSkillNames, loader.getSkills().skills),
610
- ].filter((part) => part.trim()).join("\n\n"),
605
+ ...(assignedSkillNames === undefined ? {} : {
606
+ skillsOverride: (base: ReturnType<DefaultResourceLoader["getSkills"]>) => ({
607
+ ...base,
608
+ skills: selectRoleSkills(assignedSkillNames, base.skills),
609
+ }),
610
+ }),
611
+ systemPromptOverride: (base) => [base || "You are a coding agent.", instructions]
612
+ .filter((part) => part.trim()).join("\n\n"),
611
613
  });
612
614
  await loader.reload();
613
615
  this.captureTranscriptToolDefinitions(loader);
@@ -1151,7 +1153,7 @@ export class AgentControl {
1151
1153
  model: role.model || settings.defaultModel,
1152
1154
  thinkingLevel: role.thinkingLevel ?? settings.defaultThinkingLevel ?? DEFAULT_CHILD_THINKING_LEVEL,
1153
1155
  tools: role.tools,
1154
- skills: role.skills,
1156
+ skills: role.skills ?? ["*"],
1155
1157
  source: role.source,
1156
1158
  }));
1157
1159
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youngjurry/pi-agents",
3
- "version": "0.7.3",
3
+ "version": "0.7.4",
4
4
  "description": "Persistent in-process Codex-style multi-agent collaboration for Pi",
5
5
  "author": "youngjurry",
6
6
  "type": "module",
package/roles.ts CHANGED
@@ -45,6 +45,11 @@ function stringList(value: unknown): string[] | undefined {
45
45
  return result.length > 0 ? result : undefined;
46
46
  }
47
47
 
48
+ function optionalStringList(value: unknown): string[] | undefined {
49
+ if (value === undefined) return undefined;
50
+ return stringList(value) ?? [];
51
+ }
52
+
48
53
  function loadDirectory(directory: string, source: "user" | "project"): AgentRole[] {
49
54
  if (!fs.existsSync(directory)) return [];
50
55
  let entries: fs.Dirent[];
@@ -68,7 +73,7 @@ function loadDirectory(directory: string, source: "user" | "project"): AgentRole
68
73
  description: frontmatter.description.trim(),
69
74
  systemPrompt: body.trim(),
70
75
  tools: stringList(frontmatter.tools),
71
- skills: stringList(frontmatter.skills),
76
+ skills: optionalStringList(frontmatter.skills),
72
77
  model: typeof frontmatter.model === "string" ? frontmatter.model.trim() : undefined,
73
78
  thinkingLevel: thinking,
74
79
  nicknameCandidates: stringList(frontmatter.nickname_candidates),
@@ -117,18 +122,12 @@ export function resolveRole(cwd: string, projectTrusted: boolean, name?: string)
117
122
  return role;
118
123
  }
119
124
 
120
- export function formatAssignedSkills(requestedNames: readonly string[] | undefined, discoveredSkills: readonly Skill[]): string {
121
- if (!requestedNames?.length) return "";
125
+ export function selectRoleSkills(requestedNames: readonly string[], discoveredSkills: readonly Skill[]): Skill[] {
122
126
  const skillsByName = new Map(discoveredSkills.map((skill) => [skill.name, skill]));
123
127
  const missing = requestedNames.filter((name) => !skillsByName.has(name));
124
128
  if (missing.length > 0) {
125
129
  const available = [...skillsByName.keys()].sort().join(", ") || "none";
126
130
  throw new Error(`Role references unknown skill(s): ${missing.join(", ")}. Available skills: ${available}`);
127
131
  }
128
- const sections = requestedNames.map((name) => {
129
- const skill = skillsByName.get(name)!;
130
- const instructions = fs.readFileSync(skill.filePath, "utf8").trim();
131
- return `<skill>\nName: ${skill.name}\nLocation: ${skill.filePath}\nBase directory: ${skill.baseDir}\n\n${instructions}\n</skill>`;
132
- });
133
- return `<agent_skills>\nThe following Role-selected skills are fully loaded. Follow them when completing the task. Resolve relative paths against each skill's base directory.\n\n${sections.join("\n\n")}\n</agent_skills>`;
132
+ return [...new Set(requestedNames)].map((name) => skillsByName.get(name)!);
134
133
  }
package/tools.ts CHANGED
@@ -88,7 +88,7 @@ function renderCollaborationResult(
88
88
  role.model ? `model: ${role.model}` : undefined,
89
89
  role.thinkingLevel ? `thinking: ${role.thinkingLevel}` : undefined,
90
90
  `tools: ${role.tools?.join(", ") || "default set"}`,
91
- role.skills?.length ? `skills: ${role.skills.join(", ")}` : undefined,
91
+ `skills: ${role.skills.length > 0 ? role.skills.join(", ") : "none"}`,
92
92
  ].filter(Boolean).join(" · ");
93
93
  lines.push(` ${theme.fg("accent", role.name)} — ${role.description}`);
94
94
  lines.push(` ${theme.fg("dim", configuration)}`);
package/types.ts CHANGED
@@ -146,7 +146,7 @@ export interface AgentRoleView {
146
146
  model?: string;
147
147
  thinkingLevel?: ThinkingLevel;
148
148
  tools?: string[];
149
- skills?: string[];
149
+ skills: string[];
150
150
  source: "builtin" | "user" | "project";
151
151
  }
152
152