Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion extensions/agent-chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { Type } from "@sinclair/typebox";
import { Text, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
import { spawn } from "child_process";
import { realpathSync } from "fs";
import { readFileSync, existsSync, readdirSync, mkdirSync, unlinkSync } from "fs";
import { join, resolve } from "path";
import { applyExtensionDefaults } from "./themeMap.ts";
Expand Down Expand Up @@ -364,7 +365,10 @@ export default function (pi: ExtensionAPI) {
const state = stepStates[stepIndex];

return new Promise((resolve) => {
const proc = spawn("pi", args, {
// Use process.execPath (real node) + pi script to bypass snap AppArmor confinement.
const nodeBin = process.execPath;
const piScript = (() => { try { return realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
const proc = spawn(nodeBin, [piScript, ...args], {
stdio: ["ignore", "pipe", "pipe"],
env: { ...process.env },
});
Expand Down
7 changes: 6 additions & 1 deletion extensions/agent-team.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { Type } from "@sinclair/typebox";
import { Text, type AutocompleteItem, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
import { spawn } from "child_process";
import { realpathSync } from "fs";
import { readdirSync, readFileSync, existsSync, mkdirSync, unlinkSync } from "fs";
import { join, resolve } from "path";
import { applyExtensionDefaults } from "./themeMap.ts";
Expand Down Expand Up @@ -365,7 +366,11 @@ export default function (pi: ExtensionAPI) {
const textChunks: string[] = [];

return new Promise((resolve) => {
const proc = spawn("pi", args, {
// Use process.execPath (real node binary) + resolved pi script to bypass
// snap AppArmor confinement that breaks pipes on snap-to-snap spawns.
const nodeBin = process.execPath;
const piScript = (() => { try { return realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
const proc = spawn(nodeBin, [piScript, ...args], {
stdio: ["ignore", "pipe", "pipe"],
env: { ...process.env },
});
Expand Down
6 changes: 5 additions & 1 deletion extensions/pi-pi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { Type } from "@sinclair/typebox";
import { Text, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
import { spawn } from "child_process";
import { realpathSync } from "fs";
import { readdirSync, readFileSync, existsSync, mkdirSync } from "fs";
import { join, resolve } from "path";
import { applyExtensionDefaults } from "./themeMap.ts";
Expand Down Expand Up @@ -291,7 +292,10 @@ export default function (pi: ExtensionAPI) {
const textChunks: string[] = [];

return new Promise((resolve) => {
const proc = spawn("pi", args, {
// Use process.execPath (real node) + pi script to bypass snap AppArmor confinement.
const nodeBin = process.execPath;
const piScript = (() => { try { return realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
const proc = spawn(nodeBin, [piScript, ...args], {
stdio: ["ignore", "pipe", "pipe"],
env: { ...process.env },
});
Expand Down
10 changes: 8 additions & 2 deletions extensions/subagent-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { DynamicBorder } from "@mariozechner/pi-coding-agent";
import { Container, Text } from "@mariozechner/pi-tui";
import { Type } from "@sinclair/typebox";
const { spawn } = require("child_process") as any;
import { spawn } from "child_process";
import { realpathSync } from "fs";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
Expand Down Expand Up @@ -139,7 +140,12 @@ export default function (pi: ExtensionAPI) {
: "openrouter/google/gemini-3-flash-preview";

return new Promise<void>((resolve) => {
const proc = spawn("pi", [
// Use process.execPath (real node binary, not snap wrapper) + resolved pi script
// to bypass snap AppArmor confinement that breaks pipes on snap-to-snap spawns.
const nodeBin = process.execPath;
const piScript = (() => { try { return realpathSync(process.argv[1]); } catch { return process.argv[1]; } })();
const proc = spawn(nodeBin, [
piScript,
"--mode", "json",
"-p",
"--session", state.sessionFile, // persistent session for /subcont resumption
Expand Down