feat(nixos): build pi from monorepo fork

This commit is contained in:
2026-09-13 07:06:49 -04:00
parent 0be3c0e5b4
commit 8ec9423db8
5 changed files with 54 additions and 224 deletions

View File

@@ -11,12 +11,10 @@ let
cfg = config.modules.shell.pi; cfg = config.modules.shell.pi;
user = config.user.name; user = config.user.name;
piDir = "${config.users.users.${user}.home}/.pi/agent"; piDir = "${config.users.users.${user}.home}/.pi/agent";
patchedPi = pkgs.pi-coding-agent.overrideAttrs (old: { forkedPi = pkgs.pi-coding-agent.overrideAttrs {
patches = (old.patches or [ ]) ++ [ src = ../../../../../src/pi;
./patches/pi-project-resource-ancestors.patch patches = [ ];
./patches/pi-tool-lookup-validation.patch };
];
});
in in
{ {
options.modules.shell.pi = { options.modules.shell.pi = {
@@ -28,7 +26,7 @@ in
home-manager.users.${user} = { home-manager.users.${user} = {
programs.pi-coding-agent = { programs.pi-coding-agent = {
enable = true; enable = true;
package = patchedPi; package = forkedPi;
settings = { settings = {
defaultProvider = "openai-codex"; defaultProvider = "openai-codex";

View File

@@ -1,140 +0,0 @@
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,
);
}

View File

@@ -1,23 +0,0 @@
diff --git a/packages/coding-agent/src/utils/tools-manager.ts b/packages/coding-agent/src/utils/tools-manager.ts
--- a/packages/coding-agent/src/utils/tools-manager.ts 2026-08-01 18:41:36.970496010 -0400
+++ b/packages/coding-agent/src/utils/tools-manager.ts 2026-08-01 18:41:37.028186009 -0400
@@ -74,8 +74,8 @@
function commandExists(cmd: string): boolean {
try {
const result = spawnSync(cmd, ["--version"], { stdio: "pipe" });
- // Check for ENOENT error (command not found)
- return result.error === undefined || result.error === null;
+ // Downloaded generic Linux binaries can exist but fail on NixOS before they print a version.
+ return (result.error === undefined || result.error === null) && result.status === 0;
} catch {
return false;
}
@@ -88,7 +87,7 @@
// Check our tools directory first
const localPath = join(TOOLS_DIR, config.binaryName + (platform() === "win32" ? ".exe" : ""));
- if (existsSync(localPath)) {
+ if (existsSync(localPath) && commandExists(localPath)) {
return localPath;
}

View File

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

View File

@@ -72,8 +72,8 @@ const TOOLS: Record<string, ToolConfig> = {
function commandExists(cmd: string): boolean { function commandExists(cmd: string): boolean {
try { try {
const result = spawnSync(cmd, ["--version"], { stdio: "pipe" }); const result = spawnSync(cmd, ["--version"], { stdio: "pipe" });
// Check for ENOENT error (command not found) // Downloaded generic Linux binaries can exist but fail on NixOS before they print a version.
return result.error === undefined || result.error === null; return (result.error === undefined || result.error === null) && result.status === 0;
} catch { } catch {
return false; return false;
} }
@@ -86,7 +86,7 @@ export function getToolPath(tool: "fd" | "rg"): string | null {
// Check our tools directory first // Check our tools directory first
const localPath = join(TOOLS_DIR, config.binaryName + (platform() === "win32" ? ".exe" : "")); const localPath = join(TOOLS_DIR, config.binaryName + (platform() === "win32" ? ".exe" : ""));
if (existsSync(localPath)) { if (existsSync(localPath) && commandExists(localPath)) {
return localPath; return localPath;
} }