feat(nixos): build pi from monorepo fork
This commit is contained in:
@@ -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";
|
||||||
|
|||||||
@@ -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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user