fix(pi): discover ancestor project resources

This commit was merged in pull request #23.
This commit is contained in:
2026-09-07 22:46:14 -04:00
parent f940fc3fd2
commit db7da0e9c6
5 changed files with 146 additions and 11 deletions

View File

@@ -32,11 +32,11 @@
- Use produced literals for Herdr key names that include shifted punctuation. - Use produced literals for Herdr key names that include shifted punctuation.
For example, use `prefix+<` instead of `prefix+shift+comma`. For example, use `prefix+<` instead of `prefix+shift+comma`.
- Put global Pi extension sources in `configs/nixos/modules/shell/pi/extensions/`. - Put monorepo Pi extension sources in `.pi/extensions/`.
Home Manager deploys them to `~/.pi/agent/extensions/`. Pi discovers them from subdirectories through the local ancestor-resource patch.
- Treat Home Manager-managed Pi extension, prompt, and skill directories as generated output. - Treat Home Manager-managed Pi settings as generated output.
Do not edit those directories except for throwaway development or local experiments. Do not edit them except for throwaway development or local experiments.
Promote finished work into the NixOS configuration before treating it as deployed. Promote finished settings into the NixOS configuration before treating them as deployed.
## Gotchas ## Gotchas

View File

@@ -13,6 +13,7 @@ let
piDir = "${config.users.users.${user}.home}/.pi/agent"; piDir = "${config.users.users.${user}.home}/.pi/agent";
patchedPi = pkgs.pi-coding-agent.overrideAttrs (old: { patchedPi = pkgs.pi-coding-agent.overrideAttrs (old: {
patches = (old.patches or [ ]) ++ [ patches = (old.patches or [ ]) ++ [
./patches/pi-project-resource-ancestors.patch
./patches/pi-tool-lookup-validation.patch ./patches/pi-tool-lookup-validation.patch
]; ];
}); });
@@ -44,12 +45,6 @@ in
# The first declarative rollout replaces the interactive settings file. # The first declarative rollout replaces the interactive settings file.
# Login state stays in auth.json, which this module does not manage. # Login state stays in auth.json, which this module does not manage.
"${piDir}/settings.json".force = true; "${piDir}/settings.json".force = true;
"${piDir}/extensions" = {
source = ./extensions;
recursive = true;
force = true;
};
}; };
}; };
}; };

View File

@@ -0,0 +1,140 @@
diff --git a/packages/coding-agent/src/core/package-manager.ts b/packages/coding-agent/src/core/package-manager.ts
--- a/packages/coding-agent/src/core/package-manager.ts
+++ b/packages/coding-agent/src/core/package-manager.ts
@@ -459,14 +459,14 @@ function findGitRepoRoot(startDir: string): string | null {
}
}
-function collectAncestorAgentsSkillDirs(startDir: string): string[] {
- const skillDirs: string[] = [];
+function collectAncestorConfigDirs(startDir: string, configDirName: string): string[] {
+ const configDirs: string[] = [];
const resolvedStartDir = resolve(startDir);
const gitRepoRoot = findGitRepoRoot(resolvedStartDir);
let dir = resolvedStartDir;
while (true) {
- skillDirs.push(join(dir, ".agents", "skills"));
+ configDirs.push(join(dir, configDirName));
if (gitRepoRoot && dir === gitRepoRoot) {
break;
}
@@ -477,7 +477,7 @@ function collectAncestorAgentsSkillDirs(startDir: string): string[] {
dir = parent;
}
- return skillDirs;
+ return configDirs;
}
function collectAutoPromptEntries(dir: string): string[] {
@@ -2388,16 +2388,13 @@ export class DefaultPackageManager implements PackageManager {
prompts: join(globalBaseDir, "prompts"),
themes: join(globalBaseDir, "themes"),
};
- const projectDirs = {
- extensions: join(projectBaseDir, "extensions"),
- skills: join(projectBaseDir, "skills"),
- prompts: join(projectBaseDir, "prompts"),
- themes: join(projectBaseDir, "themes"),
- };
const userAgentsSkillsDir = join(getHomeDir(), ".agents", "skills");
const projectTrusted = this.settingsManager.isProjectTrusted();
- const projectAgentsSkillDirs = projectTrusted
- ? collectAncestorAgentsSkillDirs(this.cwd).filter((dir) => resolve(dir) !== resolve(userAgentsSkillsDir))
+ const projectConfigDirs = projectTrusted
+ ? collectAncestorConfigDirs(this.cwd, CONFIG_DIR_NAME).filter((dir) => resolve(dir) !== resolve(globalBaseDir))
+ : [];
+ const projectAgentsDirs = projectTrusted
+ ? collectAncestorConfigDirs(this.cwd, ".agents").filter((dir) => resolve(dir) !== resolve(dirname(userAgentsSkillsDir)))
: [];
const addResources = (
@@ -2414,56 +2411,54 @@ export class DefaultPackageManager implements PackageManager {
}
};
- if (projectTrusted) {
- // Project extensions from .pi/
+ // Project resources from .pi/ in cwd and ancestors.
+ for (const configDir of projectConfigDirs) {
+ const configMetadata: PathMetadata = {
+ ...projectMetadata,
+ baseDir: configDir,
+ };
addResources(
"extensions",
- collectAutoExtensionEntries(projectDirs.extensions),
- projectMetadata,
+ collectAutoExtensionEntries(join(configDir, "extensions")),
+ configMetadata,
projectOverrides.extensions,
- projectBaseDir,
+ configDir,
);
-
- // Project skills from .pi/
addResources(
"skills",
- collectAutoSkillEntries(projectDirs.skills, "pi"),
- projectMetadata,
+ collectAutoSkillEntries(join(configDir, "skills"), "pi"),
+ configMetadata,
projectOverrides.skills,
- projectBaseDir,
+ configDir,
+ );
+ addResources(
+ "prompts",
+ collectAutoPromptEntries(join(configDir, "prompts")),
+ configMetadata,
+ projectOverrides.prompts,
+ configDir,
+ );
+ addResources(
+ "themes",
+ collectAutoThemeEntries(join(configDir, "themes")),
+ configMetadata,
+ projectOverrides.themes,
+ configDir,
);
}
- // Project skills from .agents/ (each with its own baseDir)
- for (const agentsSkillsDir of projectAgentsSkillDirs) {
- const agentsBaseDir = dirname(agentsSkillsDir); // the .agents directory
+ // Project skills from .agents/ in cwd and ancestors.
+ for (const agentsDir of projectAgentsDirs) {
const agentsMetadata: PathMetadata = {
...projectMetadata,
- baseDir: agentsBaseDir,
+ baseDir: agentsDir,
};
addResources(
"skills",
- collectAutoSkillEntries(agentsSkillsDir, "agents"),
+ collectAutoSkillEntries(join(agentsDir, "skills"), "agents"),
agentsMetadata,
projectOverrides.skills,
- agentsBaseDir,
- );
- }
-
- if (projectTrusted) {
- addResources(
- "prompts",
- collectAutoPromptEntries(projectDirs.prompts),
- projectMetadata,
- projectOverrides.prompts,
- projectBaseDir,
- );
- addResources(
- "themes",
- collectAutoThemeEntries(projectDirs.themes),
- projectMetadata,
- projectOverrides.themes,
- projectBaseDir,
+ agentsDir,
);
}