@@ -24,6 +24,7 @@
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed git package installs with custom `npmCommand` values such as `pnpm` by avoiding npm-specific production flags in that compatibility path ([#3604](https://github.com/badlogic/pi-mono/issues/3604))
|
||||
- Fixed first user messages rendering without spacing after existing notices such as compaction summaries or status messages ([#3613](https://github.com/badlogic/pi-mono/issues/3613))
|
||||
- Fixed the handoff extension example to use the replacement-session context after creating a new session, avoiding stale `ctx` errors when it installs the generated prompt ([#3606](https://github.com/badlogic/pi-mono/issues/3606))
|
||||
- Fixed session replacement and `/quit` teardown ordering to run host-owned extension UI cleanup synchronously after `session_shutdown` handlers complete but before invalidating the old extension context, preventing stale extension UI from rendering against a disposed session.
|
||||
|
||||
@@ -384,7 +384,7 @@ pi update # skips pinned packages
|
||||
pi config # enable/disable extensions, skills, prompts, themes
|
||||
```
|
||||
|
||||
Packages install to `~/.pi/agent/git/` (git) or global npm. Use `-l` for project-local installs (`.pi/git/`, `.pi/npm/`). Git packages install dependencies with `npm install --omit=dev`, so runtime deps must be listed under `dependencies`. If you use a Node version manager and want package installs to reuse a stable npm context, set `npmCommand` in `settings.json`, for example `["mise", "exec", "node@20", "--", "npm"]`.
|
||||
Packages install to `~/.pi/agent/git/` (git) or global npm. Use `-l` for project-local installs (`.pi/git/`, `.pi/npm/`). Git packages install dependencies with `npm install --omit=dev` by default, so runtime deps must be listed under `dependencies`; when `npmCommand` is configured, git packages use plain `install` for compatibility with wrappers. If you use a Node version manager and want package installs to reuse a stable npm context, set `npmCommand` in `settings.json`, for example `["mise", "exec", "node@20", "--", "npm"]`.
|
||||
|
||||
Create a package by adding a `pi` key to `package.json`:
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ To share extensions via npm or git as pi packages, see [packages.md](packages.md
|
||||
|
||||
npm dependencies work too. Add a `package.json` next to your extension (or in a parent directory), run `npm install`, and imports from `node_modules/` are resolved automatically.
|
||||
|
||||
For distributed pi packages installed with `pi install` (npm or git), runtime deps must be in `dependencies`. Package installation uses production installs (`npm install --omit=dev`), so `devDependencies` are not available at runtime.
|
||||
For distributed pi packages installed with `pi install` (npm or git), runtime deps must be in `dependencies`. Package installation uses production installs (`npm install --omit=dev`) by default, so `devDependencies` are not available at runtime; when `npmCommand` is configured, git packages use plain `install` for compatibility with wrappers.
|
||||
|
||||
Node.js built-ins (`node:fs`, `node:path`, etc.) are also available.
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ When a provider requests a retry delay longer than `maxDelayMs` (e.g., Google's
|
||||
}
|
||||
```
|
||||
|
||||
`npmCommand` is used for all npm package-manager operations, including `npm root -g`, installs, uninstalls, and `npm install` inside git packages. Use argv-style entries exactly as the process should be launched.
|
||||
`npmCommand` is used for all npm package-manager operations, including `npm root -g`, installs, uninstalls, and dependency installs inside git packages. Use argv-style entries exactly as the process should be launched. When `npmCommand` is configured, git package dependency installs use plain `install` to avoid npm-specific flags in wrappers or alternate package managers.
|
||||
|
||||
### Sessions
|
||||
|
||||
|
||||
@@ -1661,6 +1661,14 @@ export class DefaultPackageManager implements PackageManager {
|
||||
await this.runCommand(npmCommand.command, [...npmCommand.args, ...args], options);
|
||||
}
|
||||
|
||||
private getGitDependencyInstallArgs(): string[] {
|
||||
const configuredCommand = this.settingsManager.getNpmCommand();
|
||||
if (configuredCommand && configuredCommand.length > 0) {
|
||||
return ["install"];
|
||||
}
|
||||
return ["install", "--omit=dev"];
|
||||
}
|
||||
|
||||
private runNpmCommandSync(args: string[]): string {
|
||||
const npmCommand = this.getNpmCommand();
|
||||
return this.runCommandSync(npmCommand.command, [...npmCommand.args, ...args]);
|
||||
@@ -1705,7 +1713,7 @@ export class DefaultPackageManager implements PackageManager {
|
||||
}
|
||||
const packageJsonPath = join(targetDir, "package.json");
|
||||
if (existsSync(packageJsonPath)) {
|
||||
await this.runNpmCommand(["install", "--omit=dev"], { cwd: targetDir });
|
||||
await this.runNpmCommand(this.getGitDependencyInstallArgs(), { cwd: targetDir });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1740,7 +1748,7 @@ export class DefaultPackageManager implements PackageManager {
|
||||
|
||||
const packageJsonPath = join(targetDir, "package.json");
|
||||
if (existsSync(packageJsonPath)) {
|
||||
await this.runNpmCommand(["install", "--omit=dev"], { cwd: targetDir });
|
||||
await this.runNpmCommand(this.getGitDependencyInstallArgs(), { cwd: targetDir });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -500,6 +500,33 @@ Content`,
|
||||
expect(runCommandSpy).toHaveBeenCalledWith("npm", ["install", "--omit=dev"], { cwd: targetDir });
|
||||
});
|
||||
|
||||
it("should use plain install for git package dependencies when npmCommand is configured", async () => {
|
||||
settingsManager = SettingsManager.inMemory({
|
||||
npmCommand: ["pnpm"],
|
||||
});
|
||||
packageManager = new DefaultPackageManager({
|
||||
cwd: tempDir,
|
||||
agentDir,
|
||||
settingsManager,
|
||||
});
|
||||
|
||||
const source = "git:github.com/user/repo";
|
||||
const targetDir = join(agentDir, "git", "github.com", "user", "repo");
|
||||
const runCommandSpy = vi
|
||||
.spyOn(packageManager as any, "runCommand")
|
||||
.mockImplementation(async (...callArgs: unknown[]) => {
|
||||
const [command, args] = callArgs as [string, string[]];
|
||||
if (command === "git" && args[0] === "clone") {
|
||||
mkdirSync(targetDir, { recursive: true });
|
||||
writeFileSync(join(targetDir, "package.json"), JSON.stringify({ name: "repo", version: "1.0.0" }));
|
||||
}
|
||||
});
|
||||
|
||||
await packageManager.install(source);
|
||||
|
||||
expect(runCommandSpy).toHaveBeenCalledWith("pnpm", ["install"], { cwd: targetDir });
|
||||
});
|
||||
|
||||
it("should update git package dependencies with --omit=dev", async () => {
|
||||
const source = "git:github.com/user/repo";
|
||||
const targetDir = join(tempDir, ".pi", "git", "github.com", "user", "repo");
|
||||
@@ -527,6 +554,44 @@ Content`,
|
||||
expect(runCommandSpy).toHaveBeenCalledWith("npm", ["install", "--omit=dev"], { cwd: targetDir });
|
||||
});
|
||||
|
||||
it("should use plain install through npmCommand argv when updating git package dependencies", async () => {
|
||||
settingsManager = SettingsManager.inMemory({
|
||||
npmCommand: ["mise", "exec", "node@20", "--", "pnpm"],
|
||||
});
|
||||
packageManager = new DefaultPackageManager({
|
||||
cwd: tempDir,
|
||||
agentDir,
|
||||
settingsManager,
|
||||
});
|
||||
|
||||
const source = "git:github.com/user/repo";
|
||||
const targetDir = join(tempDir, ".pi", "git", "github.com", "user", "repo");
|
||||
mkdirSync(targetDir, { recursive: true });
|
||||
writeFileSync(join(targetDir, "package.json"), JSON.stringify({ name: "repo", version: "1.0.0" }));
|
||||
settingsManager.setProjectPackages([source]);
|
||||
|
||||
vi.spyOn(packageManager as any, "runCommandCapture").mockImplementation(async (...callArgs: unknown[]) => {
|
||||
const [_command, args] = callArgs as [string, string[]];
|
||||
if (args[0] === "rev-parse" && args[1] === "--abbrev-ref" && args[2] === "@{upstream}") {
|
||||
return "origin/main";
|
||||
}
|
||||
if (args[0] === "rev-parse" && args[1] === "@{upstream}") {
|
||||
return "remote-head";
|
||||
}
|
||||
if (args[0] === "rev-parse" && args[1] === "HEAD") {
|
||||
return "local-head";
|
||||
}
|
||||
throw new Error(`Unexpected runCommandCapture args: ${args.join(" ")}`);
|
||||
});
|
||||
const runCommandSpy = vi.spyOn(packageManager as any, "runCommand").mockResolvedValue(undefined);
|
||||
|
||||
await packageManager.update(source);
|
||||
|
||||
expect(runCommandSpy).toHaveBeenCalledWith("mise", ["exec", "node@20", "--", "pnpm", "install"], {
|
||||
cwd: targetDir,
|
||||
});
|
||||
});
|
||||
|
||||
it("should use npmCommand argv for npm root lookup and invalidate cached root when npmCommand changes", () => {
|
||||
settingsManager = SettingsManager.inMemory({
|
||||
npmCommand: ["mise", "exec", "node@20", "--", "npm"],
|
||||
|
||||
Reference in New Issue
Block a user