Merge PR #3354: fix: migrate coding-agent tests to removeWithRetries (@oldschoola)
This commit is contained in:
@@ -172,6 +172,7 @@
|
||||
- Fixed the `ask` tool's "Other (type your own)" free-text editor (prompt-style `HookEditorComponent`) ignoring Ctrl+Q and Ctrl+Enter, so Windows Terminal users who learned the `app.message.followUp` chord from the main editor (#1903 / fixed by #1905) got zero feedback on submit. The hook-style and main-editor surfaces honored `matchesAppFollowUp`; the prompt-style handler did not, leaving plain Enter as the sole submit path and Ctrl+Enter falling through to Editor as a newline (silently swallowed by WT). `#handlePromptStyleInput` now checks `matchesAppFollowUp` first — mirroring `#handleHookStyleInput` — and the hint reads `enter or ctrl+q submit` so the chord is discoverable. ([#3353](https://github.com/can1357/oh-my-pi/issues/3353))
|
||||
- Fixed the TUI freezing when a tool approval prompt fires while `/settings` (or the Extensions/Agents dashboard) is open. The fullscreen overlay's close handler restored focus to the editor it had captured at open time, but `ExtensionUiController` had since swapped the editor out of the editor slot for the approval prompt — so on exit the visible prompt sat unreachable while keystrokes routed to the now-unmounted editor (no Enter/Up/Down/Esc response, only Ctrl+C escaped). `SelectorController` now restores focus to whatever currently owns the editor slot via a `focusActiveEditorArea()` helper, applied to settings, extensions dashboard, and agents dashboard close paths. ([#3349](https://github.com/can1357/oh-my-pi/issues/3349))
|
||||
- Fixed `/settings` coercing enum/text values to display strings before handing them to the TUI list, preventing YAML numeric enum values from reaching native truncation ([#3338](https://github.com/can1357/oh-my-pi/issues/3338)).
|
||||
- Migrated 203 test files from `fs.rm`/`fs.rmSync` to `removeWithRetries`/`removeSyncWithRetries` (356 call sites) to reduce EBUSY test failures on Windows. `removeWithRetries` is now exported from `@oh-my-pi/pi-utils`
|
||||
- Fixed all extension loading silently failing on the cross-compiled `omp-darwin-arm64` release binary (downloaded directly or via a Homebrew tap wrapper) because `__computeBunfsPackageRoot` mis-handled `import.meta.dir = "//root/omp-darwin-arm64"`. Bun 1.3.14 reports `<bunfs-root>/<binary-name>` for the compiled entry's `import.meta.dir`, but the pre-fix function joined `metaDir + "packages"` and produced `/root/omp-darwin-arm64/packages` — the binary basename was baked into every bunfs path, so the TypeBox/legacy-pi shims and every `@oh-my-pi/pi-*` package-root override failed `existsSync` validation and `resolveCanonicalPiSpecifier` fell through to a bunfs `Bun.resolveSync` that also could not find the module. The function now detects the bunfs-root + binary-basename shape (`path.basename(path.dirname(metaDir)) === "root"`) and strips the trailing binary segment by slicing the original `metaDir`; the production bunfs shim join path also preserves Bun's bunfs-native `//root` / `B:\~BUN\root` prefix that `path.join` would otherwise collapse. ([#3329](https://github.com/can1357/oh-my-pi/issues/3329))
|
||||
- Fixed llama.cpp discovery to prefer per-model `/v1/models` `meta.n_ctx`/`meta.n_ctx_train` values, refresh selected models after lazy load, and bypass fresh-cache reuse so server restarts update context windows. ([#3310](https://github.com/can1357/oh-my-pi/issues/3310))
|
||||
- Fixed `task.maxConcurrency: 0` serializing subagent spawns instead of running them unbounded. The settings UI labels `0` as "Unlimited", but the session-scoped spawn `Semaphore` clamped `max` via `Math.max(1, max)`, so the second subagent body in a batch always waited for the first to release the seat. The constructor now treats `max <= 0` (and any non-finite input) as unbounded via `Number.POSITIVE_INFINITY`, matching the eval `parallel()`/`pipeline()` worker-pool semantics ([#3305](https://github.com/can1357/oh-my-pi/issues/3305)).
|
||||
@@ -196,6 +197,7 @@
|
||||
|
||||
- Reused shared TUI mouse-routing helpers across fullscreen overlay selectors.
|
||||
|
||||
|
||||
## [16.1.16] - 2026-06-23
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
||||
import type { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-session";
|
||||
import type { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
|
||||
import { executeAcpBuiltinSlashCommand } from "@oh-my-pi/pi-coding-agent/slash-commands/acp-builtins";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
interface FakeAcpBuiltinSession {
|
||||
fastMode: boolean;
|
||||
@@ -577,7 +578,7 @@ describe("wave 3 commands", () => {
|
||||
expect(output[0]).toBe(`Wrote todos to ${target}`);
|
||||
expect(await fs.readFile(target, "utf8")).toBe("# Work\n- [ ] Ship it\n");
|
||||
} finally {
|
||||
await fs.rm(tempRoot, { recursive: true, force: true });
|
||||
await removeWithRetries(tempRoot);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -594,7 +595,7 @@ describe("wave 3 commands", () => {
|
||||
expect(output[0]).toBe(`Wrote todos to ${target}`);
|
||||
expect(await fs.readFile(target, "utf8")).toBe("# Work\n- [ ] Ship it\n");
|
||||
} finally {
|
||||
await fs.rm(tempRoot, { recursive: true, force: true });
|
||||
await removeWithRetries(tempRoot);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -613,7 +614,7 @@ describe("wave 3 commands", () => {
|
||||
{ name: "Imported", tasks: [{ content: "Active task", status: "in_progress" }] },
|
||||
]);
|
||||
} finally {
|
||||
await fs.rm(tempRoot, { recursive: true, force: true });
|
||||
await removeWithRetries(tempRoot);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -633,7 +634,7 @@ describe("wave 3 commands", () => {
|
||||
{ name: "Default", tasks: [{ content: "From cwd", status: "in_progress" }] },
|
||||
]);
|
||||
} finally {
|
||||
await fs.rm(tempRoot, { recursive: true, force: true });
|
||||
await removeWithRetries(tempRoot);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -651,7 +652,7 @@ describe("wave 3 commands", () => {
|
||||
expect(output[0]).toContain(`Could not parse ${target}:`);
|
||||
expect(session._todoPhases).toEqual([]);
|
||||
} finally {
|
||||
await fs.rm(tempRoot, { recursive: true, force: true });
|
||||
await removeWithRetries(tempRoot);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
ADVISOR_TRANSCRIPT_FILENAME,
|
||||
AdvisorTranscriptRecorder,
|
||||
} from "@oh-my-pi/pi-coding-agent/advisor/transcript-recorder";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
interface AdvisorEntry {
|
||||
type?: string;
|
||||
@@ -38,7 +39,7 @@ async function withTempDir<T>(fn: (dir: string) => Promise<T>): Promise<T> {
|
||||
try {
|
||||
return await fn(dir);
|
||||
} finally {
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
await removeWithRetries(dir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
||||
import { AgentDashboard } from "@oh-my-pi/pi-coding-agent/modes/components/agent-dashboard";
|
||||
import { initTheme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
||||
import * as discovery from "@oh-my-pi/pi-coding-agent/task/discovery";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const ANSI_PATTERN = /\x1b\[[0-?]*[ -/]*[@-~]/g;
|
||||
const tempDirs: string[] = [];
|
||||
@@ -55,7 +56,7 @@ function stubStdoutGeometry(cols: number): { setRows(n: number): void; restore()
|
||||
|
||||
afterEach(async () => {
|
||||
vi.restoreAllMocks();
|
||||
await Promise.all(tempDirs.splice(0).map(dir => fs.rm(dir, { recursive: true, force: true })));
|
||||
await Promise.all(tempDirs.splice(0).map(dir => removeWithRetries(dir)));
|
||||
});
|
||||
|
||||
describe("AgentDashboard create editor", () => {
|
||||
|
||||
@@ -16,6 +16,7 @@ import { AgentTranscriptViewer } from "@oh-my-pi/pi-coding-agent/modes/component
|
||||
import { initTheme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
||||
import { AgentRegistry } from "@oh-my-pi/pi-coding-agent/registry/agent-registry";
|
||||
import { CURRENT_SESSION_VERSION } from "@oh-my-pi/pi-coding-agent/session/session-entries";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const TS = new Date().toISOString();
|
||||
|
||||
@@ -111,7 +112,7 @@ function withViewer(fn: (viewer: AgentTranscriptViewer) => void): void {
|
||||
try {
|
||||
fn(makeViewer(file));
|
||||
} finally {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(dir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +181,7 @@ describe("AgentTranscriptViewer", () => {
|
||||
viewer.handleInput("g");
|
||||
expect(body()).toContain("PROMPTMARKER");
|
||||
|
||||
fs.rmSync(file);
|
||||
removeSyncWithRetries(file);
|
||||
// Poll until the viewer's own poll timer re-stats and clears (deadline-bounded).
|
||||
const deadline = Date.now() + 5000;
|
||||
while (body().includes("PROMPTMARKER") && Date.now() < deadline) {
|
||||
@@ -189,7 +190,7 @@ describe("AgentTranscriptViewer", () => {
|
||||
expect(body()).not.toContain("PROMPTMARKER");
|
||||
} finally {
|
||||
viewer.dispose();
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(dir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
|
||||
import { convertToLlm } from "@oh-my-pi/pi-coding-agent/session/messages";
|
||||
import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
|
||||
import { BashTool, type ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { removeSyncWithRetries, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
|
||||
/** Scripted assistant turn that issues a single `bash` tool call. */
|
||||
function bashCall(command: string, callId: string): MockResponse {
|
||||
@@ -198,7 +198,7 @@ describe("BashTool through AgentSession runs children in their own session (e2e)
|
||||
authStorage?.close();
|
||||
authStorage = undefined;
|
||||
if (fs.existsSync(tempDir)) {
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-session";
|
||||
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
|
||||
import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
|
||||
import { createTools, type ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { removeSyncWithRetries, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { e2eApiKey } from "./utilities";
|
||||
|
||||
describe.skipIf(!e2eApiKey("ANTHROPIC_API_KEY"))("AgentSession branching", () => {
|
||||
@@ -41,7 +41,7 @@ describe.skipIf(!e2eApiKey("ANTHROPIC_API_KEY"))("AgentSession branching", () =>
|
||||
authStorage?.close();
|
||||
authStorage = undefined;
|
||||
if (tempDir && fs.existsSync(tempDir)) {
|
||||
fs.rmSync(tempDir, { recursive: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { AgentSession, type AgentSessionEvent } from "@oh-my-pi/pi-coding-agent/
|
||||
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
|
||||
import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
|
||||
import { createTools, type ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { removeSyncWithRetries, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { e2eApiKey } from "./utilities";
|
||||
|
||||
describe.skipIf(!e2eApiKey("ANTHROPIC_API_KEY"))("AgentSession compaction e2e", () => {
|
||||
@@ -45,7 +45,7 @@ describe.skipIf(!e2eApiKey("ANTHROPIC_API_KEY"))("AgentSession compaction e2e",
|
||||
authStorage?.close();
|
||||
authStorage = undefined;
|
||||
if (tempDir && fs.existsSync(tempDir)) {
|
||||
fs.rmSync(tempDir, { recursive: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-session";
|
||||
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
|
||||
import { convertToLlm } from "@oh-my-pi/pi-coding-agent/session/messages";
|
||||
import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
|
||||
import { Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { removeSyncWithRetries, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { type } from "arktype";
|
||||
import { createAssistantMessage } from "./helpers/agent-session-setup";
|
||||
|
||||
@@ -62,7 +62,7 @@ describe("AgentSession concurrent prompt guard", () => {
|
||||
authStorage.close();
|
||||
}
|
||||
if (tempDir && fs.existsSync(tempDir)) {
|
||||
fs.rmSync(tempDir, { recursive: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
}
|
||||
vi.restoreAllMocks();
|
||||
AsyncJobManager.resetForTests();
|
||||
@@ -982,7 +982,7 @@ describe("AgentSession TTSR resume gate", () => {
|
||||
authStorage.close();
|
||||
}
|
||||
if (tempDir && fs.existsSync(tempDir)) {
|
||||
fs.rmSync(tempDir, { recursive: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
}
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-session";
|
||||
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
|
||||
import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
|
||||
import { AUTO_THINKING } from "@oh-my-pi/pi-coding-agent/thinking";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
async function createMagicKeywordSession(root: string): Promise<{
|
||||
session: AgentSession;
|
||||
@@ -55,7 +56,7 @@ describe("AgentSession magic keyword settings", () => {
|
||||
vi.restoreAllMocks();
|
||||
if (session) await session.dispose();
|
||||
authStorage?.close();
|
||||
await fs.rm(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 }).catch(() => undefined);
|
||||
await removeWithRetries(root).catch(() => undefined);
|
||||
session = undefined;
|
||||
authStorage = undefined;
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@ import type { CustomTool } from "@oh-my-pi/pi-coding-agent/extensibility/custom-
|
||||
import { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-session";
|
||||
import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
|
||||
import type { OutputMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import { type } from "arktype";
|
||||
|
||||
function createModel(): Model<"openai-responses"> {
|
||||
@@ -149,7 +150,7 @@ describe("AgentSession MCP discovery", () => {
|
||||
await session.dispose();
|
||||
}
|
||||
for (const tempDir of tempDirs.splice(0)) {
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import type { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-sessi
|
||||
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
|
||||
import type { SessionEntry, SessionMessageEntry } from "@oh-my-pi/pi-coding-agent/session/session-entries";
|
||||
import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
|
||||
import { Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { removeSyncWithRetries, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
|
||||
function createUsage(): Usage {
|
||||
return {
|
||||
@@ -272,7 +272,7 @@ describe("AgentSession OpenAI Responses replay boundaries", () => {
|
||||
afterAll(() => {
|
||||
sharedModelRegistry?.authStorage.close();
|
||||
if (sharedRegistryDir && fs.existsSync(sharedRegistryDir)) {
|
||||
fs.rmSync(sharedRegistryDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(sharedRegistryDir);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -283,7 +283,7 @@ describe("AgentSession OpenAI Responses replay boundaries", () => {
|
||||
while (tempDirs.length > 0) {
|
||||
const tempDir = tempDirs.pop();
|
||||
if (tempDir && fs.existsSync(tempDir)) {
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ import { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-session";
|
||||
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
|
||||
import { USER_INTERRUPT_LABEL } from "@oh-my-pi/pi-coding-agent/session/messages";
|
||||
import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
|
||||
import { Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { removeSyncWithRetries, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const COLLAB_PROMPT_TYPE = "collab-prompt";
|
||||
|
||||
@@ -49,7 +49,7 @@ describe("AgentSession queued steer delivery", () => {
|
||||
for (const authStorage of authStorages.splice(0)) {
|
||||
authStorage.close();
|
||||
}
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
});
|
||||
|
||||
async function createSession(responses: MockResponse[]): Promise<SteerHarness> {
|
||||
|
||||
@@ -13,7 +13,7 @@ import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manage
|
||||
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { queueResolveHandler, ResolveTool } from "@oh-my-pi/pi-coding-agent/tools/resolve";
|
||||
import { buildNamedToolChoice } from "@oh-my-pi/pi-coding-agent/utils/tool-choice";
|
||||
import { Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { removeSyncWithRetries, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
|
||||
describe("AgentSession resolve reminder", () => {
|
||||
let session: AgentSession;
|
||||
@@ -65,7 +65,7 @@ describe("AgentSession resolve reminder", () => {
|
||||
authStorage?.close();
|
||||
authStorage = undefined;
|
||||
if (fs.existsSync(tempDir)) {
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { afterEach, describe, expect, it } from "bun:test";
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { ArtifactManager } from "@oh-my-pi/pi-coding-agent/session/artifacts";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
describe("ArtifactManager tool-type sanitization", () => {
|
||||
const dirs: string[] = [];
|
||||
@@ -15,7 +15,7 @@ describe("ArtifactManager tool-type sanitization", () => {
|
||||
|
||||
afterEach(() => {
|
||||
for (const dir of dirs.splice(0)) {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(dir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as path from "node:path";
|
||||
import { AuthStorage, SqliteAuthCredentialStore } from "@oh-my-pi/pi-ai";
|
||||
import { type AuthBrokerServerHandle, startAuthBroker } from "@oh-my-pi/pi-ai/auth-broker";
|
||||
import { runAuthBrokerCommand } from "@oh-my-pi/pi-coding-agent/cli/auth-broker-cli";
|
||||
import { getAgentDbPath, setAgentDir } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDbPath, removeWithRetries, setAgentDir } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const ORIGINAL_STDOUT_WRITE = process.stdout.write.bind(process.stdout);
|
||||
|
||||
@@ -34,8 +34,8 @@ describe("auth-broker import (CLIProxyAPI)", () => {
|
||||
process.stdout.write = ORIGINAL_STDOUT_WRITE;
|
||||
if (originalAgentDir === undefined) delete process.env.OMP_AGENT_DIR;
|
||||
else process.env.OMP_AGENT_DIR = originalAgentDir;
|
||||
await fs.rm(agentDir, { recursive: true, force: true });
|
||||
await fs.rm(cliproxyDir, { recursive: true, force: true });
|
||||
await removeWithRetries(agentDir);
|
||||
await removeWithRetries(cliproxyDir);
|
||||
});
|
||||
|
||||
async function writeCliProxyJson(name: string, body: Record<string, unknown>): Promise<string> {
|
||||
@@ -222,9 +222,9 @@ describe("auth-broker import (broker-routed)", () => {
|
||||
await handle?.close();
|
||||
brokerStorage?.close();
|
||||
brokerStore?.close();
|
||||
await fs.rm(agentDir, { recursive: true, force: true });
|
||||
await fs.rm(brokerAgentDir, { recursive: true, force: true });
|
||||
await fs.rm(cliproxyDir, { recursive: true, force: true });
|
||||
await removeWithRetries(agentDir);
|
||||
await removeWithRetries(brokerAgentDir);
|
||||
await removeWithRetries(cliproxyDir);
|
||||
for (const key of ["OMP_AUTH_BROKER_URL", "OMP_AUTH_BROKER_TOKEN"] as const) {
|
||||
if (savedEnv[key] === undefined) delete process.env[key];
|
||||
else process.env[key] = savedEnv[key];
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
writeAuthBrokerSnapshotCache,
|
||||
} from "@oh-my-pi/pi-ai/auth-broker";
|
||||
import { discoverAuthStorage } from "@oh-my-pi/pi-coding-agent/sdk";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const ENV_KEYS = [
|
||||
"OMP_AUTH_BROKER_URL",
|
||||
@@ -68,7 +69,7 @@ describe("discoverAuthStorage auth-broker snapshot cache", () => {
|
||||
if (savedEnv[key] === undefined) delete process.env[key];
|
||||
else process.env[key] = savedEnv[key];
|
||||
}
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempDir);
|
||||
});
|
||||
|
||||
test("boots from a fresh encrypted cache when the broker is down", async () => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import type { FetchImpl } from "@oh-my-pi/pi-ai";
|
||||
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
|
||||
import { Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { removeSyncWithRetries, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
|
||||
describe("AuthStorage MiniMax login", () => {
|
||||
let tempDir: string;
|
||||
@@ -37,7 +37,7 @@ describe("AuthStorage MiniMax login", () => {
|
||||
vi.restoreAllMocks();
|
||||
authStorage.close();
|
||||
if (tempDir && fs.existsSync(tempDir)) {
|
||||
fs.rmSync(tempDir, { recursive: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { UsageProvider } from "@oh-my-pi/pi-ai";
|
||||
import * as oauth from "@oh-my-pi/pi-ai/oauth";
|
||||
import type { OAuthCredentials } from "@oh-my-pi/pi-ai/oauth/types";
|
||||
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
|
||||
import { Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { removeSyncWithRetries, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
|
||||
describe("AuthStorage account rotation", () => {
|
||||
let tempDir: string;
|
||||
@@ -62,7 +62,7 @@ describe("AuthStorage account rotation", () => {
|
||||
vi.restoreAllMocks();
|
||||
authStorage.close();
|
||||
if (tempDir && fs.existsSync(tempDir)) {
|
||||
fs.rmSync(tempDir, { recursive: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
||||
import { getManagedSkillsDir } from "@oh-my-pi/pi-coding-agent/autolearn/managed-skills";
|
||||
import "@oh-my-pi/pi-coding-agent/discovery";
|
||||
import { loadSkills } from "@oh-my-pi/pi-coding-agent/extensibility/skills";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDir, setAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
async function writeSkill(dir: string, name: string, description: string): Promise<void> {
|
||||
@@ -37,7 +38,7 @@ describe("managed-skills discovery", () => {
|
||||
afterEach(async () => {
|
||||
spyOn(os, "homedir").mockRestore();
|
||||
setAgentDir(originalAgentDir);
|
||||
await fs.rm(tempHome, { recursive: true, force: true });
|
||||
await removeWithRetries(tempHome);
|
||||
});
|
||||
|
||||
it("surfaces a managed skill tagged with the omp-managed provider", async () => {
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { localBackend } from "@oh-my-pi/pi-coding-agent/memory-backend/local-backend";
|
||||
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { LearnTool } from "@oh-my-pi/pi-coding-agent/tools/learn";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
Bun.env.PI_PYTHON_SKIP_CHECK = "1";
|
||||
|
||||
@@ -27,7 +28,7 @@ describe("learned-lesson storage (local backend)", () => {
|
||||
learnedFile = path.join(getMemoryRoot(agentDir, projCwd), "learned.md");
|
||||
});
|
||||
afterEach(async () => {
|
||||
await fs.rm(tmp, { recursive: true, force: true });
|
||||
await removeWithRetries(tmp);
|
||||
});
|
||||
|
||||
it("appends a bullet, normalizes whitespace, and inlines context", async () => {
|
||||
@@ -153,7 +154,7 @@ describe("learned-lesson read-back", () => {
|
||||
agentDir = path.join(tmp, "agent");
|
||||
});
|
||||
afterEach(async () => {
|
||||
await fs.rm(tmp, { recursive: true, force: true });
|
||||
await removeWithRetries(tmp);
|
||||
});
|
||||
|
||||
it("injects lessons even when no consolidated summary exists", async () => {
|
||||
@@ -228,7 +229,7 @@ describe("learn tool (local backend)", () => {
|
||||
learnedFile = path.join(getMemoryRoot(agentDir, projCwd), "learned.md");
|
||||
});
|
||||
afterEach(async () => {
|
||||
await fs.rm(tmp, { recursive: true, force: true });
|
||||
await removeWithRetries(tmp);
|
||||
});
|
||||
|
||||
function localSession(): ToolSession {
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
toSkillFrontmatter,
|
||||
writeManagedSkill,
|
||||
} from "@oh-my-pi/pi-coding-agent/autolearn/managed-skills";
|
||||
import { parseFrontmatter } from "@oh-my-pi/pi-utils";
|
||||
import { parseFrontmatter, removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDir, setAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
describe("managed-skills primitives", () => {
|
||||
@@ -27,7 +27,7 @@ describe("managed-skills primitives", () => {
|
||||
afterEach(async () => {
|
||||
spyOn(os, "homedir").mockRestore();
|
||||
setAgentDir(originalAgentDir);
|
||||
await fs.rm(tempHome, { recursive: true, force: true });
|
||||
await removeWithRetries(tempHome);
|
||||
});
|
||||
|
||||
const skillFile = (name: string) => path.join(getManagedSkillsDir(), name, "SKILL.md");
|
||||
@@ -142,7 +142,7 @@ describe("managed-skills primitives", () => {
|
||||
// Nothing was written through the link.
|
||||
expect(await Bun.file(path.join(outside, "SKILL.md")).exists()).toBe(false);
|
||||
} finally {
|
||||
await fs.rm(outside, { recursive: true, force: true });
|
||||
await removeWithRetries(outside);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -170,7 +170,7 @@ describe("managed-skills primitives", () => {
|
||||
).rejects.toThrow(/managed-skills root is a symlink/);
|
||||
expect(await Bun.file(path.join(realRoot, "demo", "SKILL.md")).exists()).toBe(false);
|
||||
} finally {
|
||||
await fs.rm(realRoot, { recursive: true, force: true });
|
||||
await removeWithRetries(realRoot);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -203,14 +203,14 @@ describe("managed-skills primitives", () => {
|
||||
const target = path.join(outside, "target.md");
|
||||
await Bun.write(target, "outside content");
|
||||
try {
|
||||
await fs.rm(skillFile("linky"));
|
||||
await removeWithRetries(skillFile("linky"));
|
||||
await fs.symlink(target, skillFile("linky"));
|
||||
await expect(
|
||||
writeManagedSkill({ action: "update", name: "linky", description: "d", body: "hacked" }),
|
||||
).rejects.toThrow(/symlink/);
|
||||
expect(await Bun.file(target).text()).toBe("outside content");
|
||||
} finally {
|
||||
await fs.rm(outside, { recursive: true, force: true });
|
||||
await removeWithRetries(outside);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -218,7 +218,7 @@ describe("managed-skills primitives", () => {
|
||||
await writeManagedSkill({ action: "create", name: "hardlink", description: "d", body: "managed content" });
|
||||
const outside = path.join(tempHome, "authored-hardlink.md");
|
||||
await Bun.write(outside, "user-authored content");
|
||||
await fs.rm(skillFile("hardlink"));
|
||||
await removeWithRetries(skillFile("hardlink"));
|
||||
await fs.link(outside, skillFile("hardlink"));
|
||||
|
||||
await expect(
|
||||
@@ -248,7 +248,7 @@ describe("managed-skills primitives", () => {
|
||||
// The symlink target's contents are untouched.
|
||||
expect(await Bun.file(path.join(outside, "keep.txt")).exists()).toBe(true);
|
||||
} finally {
|
||||
await fs.rm(outside, { recursive: true, force: true });
|
||||
await removeWithRetries(outside);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import type { MnemopiSessionState } from "@oh-my-pi/pi-coding-agent/mnemopi/stat
|
||||
import { createTools, type ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { LearnTool } from "@oh-my-pi/pi-coding-agent/tools/learn";
|
||||
import { ManageSkillTool } from "@oh-my-pi/pi-coding-agent/tools/manage-skill";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDir, setAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { type } from "arktype";
|
||||
|
||||
@@ -119,7 +120,7 @@ describe("manage_skill execute", () => {
|
||||
spyOn(os, "homedir").mockRestore();
|
||||
setAgentDir(originalAgentDir);
|
||||
resetActiveSkillsForTests();
|
||||
await fs.rm(tempHome, { recursive: true, force: true });
|
||||
await removeWithRetries(tempHome);
|
||||
});
|
||||
|
||||
const tool = () => ManageSkillTool.createIf(makeSession({ "autolearn.enabled": true }))!;
|
||||
@@ -212,7 +213,7 @@ describe("learn execute", () => {
|
||||
afterEach(async () => {
|
||||
spyOn(os, "homedir").mockRestore();
|
||||
setAgentDir(originalAgentDir);
|
||||
await fs.rm(tempHome, { recursive: true, force: true });
|
||||
await removeWithRetries(tempHome);
|
||||
});
|
||||
|
||||
it("stores a lesson to memory without writing a skill when no skill payload", async () => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { afterEach, describe, expect, it } from "bun:test";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import { ensureAutoresearchBranch } from "../src/autoresearch/git";
|
||||
import type { ExtensionAPI } from "../src/extensibility/extensions";
|
||||
import * as jj from "../src/utils/jj";
|
||||
@@ -41,7 +42,7 @@ const stubApi = {} as unknown as ExtensionAPI;
|
||||
|
||||
afterEach(async () => {
|
||||
jj.repo.clearRootCache();
|
||||
await Promise.all(tempDirs.splice(0).map(dir => fs.rm(dir, { recursive: true, force: true })));
|
||||
await Promise.all(tempDirs.splice(0).map(dir => removeWithRetries(dir)));
|
||||
});
|
||||
|
||||
describe("ensureAutoresearchBranch jj guardrails", () => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { processFileArguments } from "@oh-my-pi/pi-coding-agent/cli/file-process
|
||||
import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
||||
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { ReadTool } from "@oh-my-pi/pi-coding-agent/tools/read";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
// 1x1 red PNG image as base64 (smallest valid PNG)
|
||||
const TINY_PNG_BASE64 =
|
||||
@@ -31,7 +32,7 @@ describe("blockImages setting", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(testDir);
|
||||
});
|
||||
|
||||
it("should include image blocks when inspect_image is disabled", async () => {
|
||||
@@ -74,7 +75,7 @@ describe("blockImages setting", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(testDir);
|
||||
});
|
||||
|
||||
it("should always process images (filtering happens at convertToLlm layer)", async () => {
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
type TtsrTestArgs,
|
||||
} from "@oh-my-pi/pi-coding-agent/cli/ttsr-cli";
|
||||
import { resetSettingsForTest } from "@oh-my-pi/pi-coding-agent/config/settings";
|
||||
import { getProjectAgentDir, getProjectDir, setProjectDir } from "@oh-my-pi/pi-utils";
|
||||
import { getProjectAgentDir, getProjectDir, removeSyncWithRetries, setProjectDir } from "@oh-my-pi/pi-utils";
|
||||
|
||||
let testTmpDir: string;
|
||||
|
||||
@@ -20,7 +20,7 @@ beforeAll(() => {
|
||||
|
||||
afterAll(() => {
|
||||
if (testTmpDir && fs.existsSync(testTmpDir)) {
|
||||
fs.rmSync(testTmpDir, { force: true, recursive: true });
|
||||
removeSyncWithRetries(testTmpDir);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -92,7 +92,7 @@ async function writeTempSnippet(content: string, ext: string): Promise<string> {
|
||||
function cleanupTmp(): void {
|
||||
if (!testTmpDir || !fs.existsSync(testTmpDir)) return;
|
||||
for (const entry of fs.readdirSync(testTmpDir)) {
|
||||
fs.rmSync(path.join(testTmpDir, entry), { force: true, recursive: true });
|
||||
removeSyncWithRetries(path.join(testTmpDir, entry));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { ApplyPatchError, applyPatch } from "@oh-my-pi/pi-coding-agent/edit";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
describe("applyPatch adversarial inputs", () => {
|
||||
let tempDir: string;
|
||||
@@ -14,7 +15,7 @@ describe("applyPatch adversarial inputs", () => {
|
||||
|
||||
afterEach(() => {
|
||||
try {
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
} catch {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
getFileSnapshotStore,
|
||||
} from "@oh-my-pi/pi-coding-agent/edit";
|
||||
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
beforeAll(async () => {
|
||||
resetSettingsForTest();
|
||||
@@ -21,7 +22,7 @@ async function withTempDir(fn: (tempDir: string) => Promise<void>): Promise<void
|
||||
try {
|
||||
await fn(tempDir);
|
||||
} finally {
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempDir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import { NOOP_HARD_LIMIT } from "@oh-my-pi/pi-coding-agent/edit/hashline/noop-loop-guard";
|
||||
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { ToolError } from "@oh-my-pi/pi-coding-agent/tools/tool-errors";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
beforeAll(async () => {
|
||||
resetSettingsForTest();
|
||||
@@ -43,7 +44,7 @@ async function withTempDir(fn: (tempDir: string) => Promise<void>): Promise<void
|
||||
try {
|
||||
await fn(tempDir);
|
||||
} finally {
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempDir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
} from "@oh-my-pi/pi-coding-agent/edit";
|
||||
import { resolveLocalUrlToPath } from "@oh-my-pi/pi-coding-agent/internal-urls";
|
||||
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import { type Type, type } from "arktype";
|
||||
|
||||
beforeAll(async () => {
|
||||
@@ -55,7 +56,7 @@ async function withTempDir(fn: (tempDir: string) => Promise<void>): Promise<void
|
||||
try {
|
||||
await fn(tempDir);
|
||||
} finally {
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempDir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
||||
import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
||||
import { CursorExecHandlers } from "@oh-my-pi/pi-coding-agent/cursor";
|
||||
import { GrepTool, type ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
function createTestSession(cwd: string, overrides: Partial<ToolSession> = {}): ToolSession {
|
||||
return {
|
||||
@@ -33,7 +34,7 @@ describe("CursorExecHandlers.grep bridge", () => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await fs.rm(cwd, { recursive: true, force: true });
|
||||
await removeWithRetries(cwd);
|
||||
});
|
||||
|
||||
it("maps caseInsensitive parameter correctly through the grep bridge", async () => {
|
||||
|
||||
@@ -14,6 +14,7 @@ import type {
|
||||
} from "@oh-my-pi/pi-coding-agent/dap/types";
|
||||
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { DebugTool } from "@oh-my-pi/pi-coding-agent/tools/debug";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const TEST_ADAPTER: DapResolvedAdapter = {
|
||||
name: "lldb-dap",
|
||||
@@ -360,7 +361,7 @@ describe("DAP launch failure handling", () => {
|
||||
expect(client.isAlive()).toBe(true);
|
||||
} finally {
|
||||
await client?.dispose();
|
||||
await fs.rm(cwd, { recursive: true, force: true });
|
||||
await removeWithRetries(cwd);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -385,7 +386,7 @@ describe("DebugTool launch validation", () => {
|
||||
/launch program resolves to a directory.*python/,
|
||||
);
|
||||
} finally {
|
||||
await fs.rm(cwd, { recursive: true, force: true });
|
||||
await removeWithRetries(cwd);
|
||||
}
|
||||
} finally {
|
||||
launchSpy.mockRestore();
|
||||
@@ -428,7 +429,7 @@ describe("DebugTool launch validation", () => {
|
||||
expect(opts.extraLaunchArguments).toEqual({ mode: "debug" });
|
||||
expect(opts.program).toBe(path.join(cwd, "cmd"));
|
||||
} finally {
|
||||
await fs.rm(cwd, { recursive: true, force: true });
|
||||
await removeWithRetries(cwd);
|
||||
}
|
||||
} finally {
|
||||
sessionLaunchSpy.mockRestore();
|
||||
@@ -465,7 +466,7 @@ describe("DebugTool launch validation", () => {
|
||||
expect(opts.adapter.name).toBe("dlv");
|
||||
expect(opts.extraLaunchArguments).toEqual({ mode: "debug" });
|
||||
} finally {
|
||||
await fs.rm(cwd, { recursive: true, force: true });
|
||||
await removeWithRetries(cwd);
|
||||
}
|
||||
} finally {
|
||||
sessionLaunchSpy.mockRestore();
|
||||
@@ -504,7 +505,7 @@ describe("DebugTool launch validation", () => {
|
||||
const [opts] = sessionLaunchSpy.mock.calls[0]!;
|
||||
expect(opts.extraLaunchArguments).toEqual({ mode: "exec" });
|
||||
} finally {
|
||||
await fs.rm(cwd, { recursive: true, force: true });
|
||||
await removeWithRetries(cwd);
|
||||
}
|
||||
} finally {
|
||||
sessionLaunchSpy.mockRestore();
|
||||
@@ -531,7 +532,7 @@ describe("DebugTool launch validation", () => {
|
||||
tool.execute("call", { action: "launch", program: "main.py", adapter: "debugpy" }),
|
||||
).rejects.toThrow(/debugpy.*python not found in PATH/);
|
||||
} finally {
|
||||
await fs.rm(cwd, { recursive: true, force: true });
|
||||
await removeWithRetries(cwd);
|
||||
}
|
||||
} finally {
|
||||
launchSpy.mockRestore();
|
||||
@@ -556,7 +557,7 @@ describe("DebugTool launch validation", () => {
|
||||
/debugpy.*python not found in PATH/,
|
||||
);
|
||||
} finally {
|
||||
await fs.rm(cwd, { recursive: true, force: true });
|
||||
await removeWithRetries(cwd);
|
||||
}
|
||||
} finally {
|
||||
attachSpy.mockRestore();
|
||||
@@ -582,7 +583,7 @@ describe("DebugTool launch validation", () => {
|
||||
/No debugger adapter available/,
|
||||
);
|
||||
} finally {
|
||||
await fs.rm(cwd, { recursive: true, force: true });
|
||||
await removeWithRetries(cwd);
|
||||
}
|
||||
} finally {
|
||||
launchSpy.mockRestore();
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { Model } from "@oh-my-pi/pi-ai";
|
||||
import { buildModel } from "@oh-my-pi/pi-catalog/build";
|
||||
import { RawSseDebugBuffer } from "@oh-my-pi/pi-coding-agent/debug/raw-sse-buffer";
|
||||
import { createReportBundle } from "@oh-my-pi/pi-coding-agent/debug/report-bundle";
|
||||
import { getConfigRootDir, setAgentDir } from "@oh-my-pi/pi-utils";
|
||||
import { getConfigRootDir, removeWithRetries, setAgentDir } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const model: Model<"anthropic-messages"> = buildModel({
|
||||
id: "claude-test",
|
||||
@@ -39,7 +39,7 @@ afterEach(async () => {
|
||||
delete process.env.PI_CODING_AGENT_DIR;
|
||||
}
|
||||
if (cleanupRoot) {
|
||||
await fs.rm(cleanupRoot, { recursive: true, force: true });
|
||||
await removeWithRetries(cleanupRoot);
|
||||
cleanupRoot = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import { disableProvider, enableProvider } from "@oh-my-pi/pi-coding-agent/capab
|
||||
import { clearCache as clearFsCache } from "@oh-my-pi/pi-coding-agent/capability/fs";
|
||||
import { clearClaudePluginRootsCache } from "@oh-my-pi/pi-coding-agent/discovery/helpers";
|
||||
import { discoverAgents } from "@oh-my-pi/pi-coding-agent/task/discovery";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const PLUGIN_AGENT_MD = [
|
||||
"---",
|
||||
@@ -59,7 +60,7 @@ describe("discoverAgents — claude-plugins disabled provider", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(tempHome, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tempHome);
|
||||
// Restore global state so other tests in the suite are not affected.
|
||||
enableProvider("claude-plugins");
|
||||
clearFsCache();
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
loadFilesFromDir,
|
||||
scanSkillsFromDir,
|
||||
} from "@oh-my-pi/pi-coding-agent/discovery/helpers";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const PROVIDER_ID = "agents";
|
||||
|
||||
@@ -55,7 +56,7 @@ describe("agents provider project-level discovery", () => {
|
||||
|
||||
afterEach(() => {
|
||||
clearCache();
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
});
|
||||
|
||||
// =========================================================================
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { clearCache as clearFsCache } from "@oh-my-pi/pi-coding-agent/capability/fs";
|
||||
import { expandAtImports, MAX_AT_IMPORT_DEPTH } from "@oh-my-pi/pi-coding-agent/discovery/at-imports";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
/**
|
||||
* Behavior contract for the @-import expander used by every AGENTS.md /
|
||||
@@ -19,7 +20,7 @@ describe("expandAtImports", () => {
|
||||
|
||||
afterEach(async () => {
|
||||
clearFsCache();
|
||||
await fs.rm(tmp, { recursive: true, force: true });
|
||||
await removeWithRetries(tmp);
|
||||
});
|
||||
|
||||
const writeFile = async (relPath: string, content: string): Promise<string> => {
|
||||
@@ -62,7 +63,7 @@ describe("expandAtImports", () => {
|
||||
});
|
||||
expect(expanded).toContain("See use 2 spaces");
|
||||
} finally {
|
||||
await fs.rm(fakeHome, { recursive: true, force: true });
|
||||
await removeWithRetries(fakeHome);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import { type Rule, ruleCapability } from "@oh-my-pi/pi-coding-agent/capability/
|
||||
import type { LoadContext } from "@oh-my-pi/pi-coding-agent/capability/types";
|
||||
// Register all discovery providers as a side effect.
|
||||
import "@oh-my-pi/pi-coding-agent/discovery";
|
||||
import { getConfigRootDir, setAgentDir } from "@oh-my-pi/pi-utils";
|
||||
import { getConfigRootDir, removeSyncWithRetries, setAgentDir } from "@oh-my-pi/pi-utils";
|
||||
|
||||
let tempDir: string;
|
||||
let home: string;
|
||||
@@ -59,7 +59,7 @@ afterEach(() => {
|
||||
setAgentDir(fallbackAgentDir);
|
||||
delete process.env.PI_CODING_AGENT_DIR;
|
||||
}
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
});
|
||||
|
||||
test("user ~/.omp/agent/RULES.md becomes an alwaysApply rule", async () => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { clearCache as clearFsCache } from "@oh-my-pi/pi-coding-agent/capability
|
||||
import { type SlashCommand, slashCommandCapability } from "@oh-my-pi/pi-coding-agent/capability/slash-command";
|
||||
import { resetSettingsForTest } from "@oh-my-pi/pi-coding-agent/config/settings";
|
||||
import { loadCapability } from "@oh-my-pi/pi-coding-agent/discovery";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
async function writeFile(filePath: string, content: string): Promise<void> {
|
||||
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
||||
@@ -39,7 +40,7 @@ describe("Claude Code slash command discovery", () => {
|
||||
} else {
|
||||
process.env.HOME = originalHome;
|
||||
}
|
||||
await fs.rm(root, { recursive: true, force: true });
|
||||
await removeWithRetries(root);
|
||||
});
|
||||
|
||||
test("loads subdirectory commands under both basename and namespace names", async () => {
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from "@oh-my-pi/pi-coding-agent/discovery/helpers";
|
||||
import { loadSlashCommands } from "@oh-my-pi/pi-coding-agent/extensibility/slash-commands";
|
||||
import { discoverAgents } from "@oh-my-pi/pi-coding-agent/task/discovery";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import "@oh-my-pi/pi-coding-agent/discovery/claude-plugins";
|
||||
import type { Skill } from "@oh-my-pi/pi-coding-agent/capability/skill";
|
||||
import type { SlashCommand } from "@oh-my-pi/pi-coding-agent/capability/slash-command";
|
||||
@@ -80,7 +81,7 @@ describe("listClaudePluginRoots", () => {
|
||||
} else {
|
||||
process.env.HOME = originalHome;
|
||||
}
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempDir);
|
||||
});
|
||||
|
||||
test("returns empty roots when no registry file exists", async () => {
|
||||
@@ -597,7 +598,7 @@ describe("discoverAgents plugin precedence", () => {
|
||||
|
||||
afterEach(async () => {
|
||||
clearClaudePluginRootsCache();
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempDir);
|
||||
});
|
||||
|
||||
test("prefers project-scoped plugin agent over user-scoped plugin agent", async () => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
||||
import { type ContextFile, contextFileCapability } from "@oh-my-pi/pi-coding-agent/capability/context-file";
|
||||
import { resetSettingsForTest, Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
||||
import { initializeWithSettings, loadCapability } from "@oh-my-pi/pi-coding-agent/discovery";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
describe("disabledExtensions runtime filtering", () => {
|
||||
let tempDir = "";
|
||||
@@ -39,8 +40,8 @@ describe("disabledExtensions runtime filtering", () => {
|
||||
} else {
|
||||
process.env.HOME = originalHome;
|
||||
}
|
||||
await fs.rm(tempHomeDir, { recursive: true, force: true });
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempHomeDir);
|
||||
await removeWithRetries(tempDir);
|
||||
});
|
||||
|
||||
test("hides disabled context files from runtime loads by default", async () => {
|
||||
|
||||
@@ -19,6 +19,7 @@ import type { Instruction } from "@oh-my-pi/pi-coding-agent/capability/instructi
|
||||
import type { Prompt } from "@oh-my-pi/pi-coding-agent/capability/prompt";
|
||||
import { type Rule, resetActiveRulesForTests, setActiveRules } from "@oh-my-pi/pi-coding-agent/capability/rule";
|
||||
import { RuleProtocolHandler } from "@oh-my-pi/pi-coding-agent/internal-urls/rule-protocol";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import "@oh-my-pi/pi-coding-agent/capability/context-file";
|
||||
import "@oh-my-pi/pi-coding-agent/capability/instruction";
|
||||
import "@oh-my-pi/pi-coding-agent/capability/prompt";
|
||||
@@ -57,7 +58,7 @@ describe("github discovery — Copilot user-global surface", () => {
|
||||
if (savedEnv[key] === undefined) delete process.env[key];
|
||||
else process.env[key] = savedEnv[key];
|
||||
}
|
||||
if (tempDir) fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
if (tempDir) removeSyncWithRetries(tempDir);
|
||||
});
|
||||
|
||||
test("loads user-global ~/.copilot/copilot-instructions.md via COPILOT_HOME (#1913)", async () => {
|
||||
|
||||
@@ -16,6 +16,7 @@ import * as path from "node:path";
|
||||
import { loadCapability } from "@oh-my-pi/pi-coding-agent/capability";
|
||||
import { clearCache } from "@oh-my-pi/pi-coding-agent/capability/fs";
|
||||
import type { Skill } from "@oh-my-pi/pi-coding-agent/capability/skill";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import "@oh-my-pi/pi-coding-agent/capability/skill";
|
||||
import "@oh-my-pi/pi-coding-agent/discovery/github";
|
||||
|
||||
@@ -37,7 +38,7 @@ describe("github discovery — skills", () => {
|
||||
|
||||
afterEach(() => {
|
||||
clearCache();
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
});
|
||||
|
||||
test("discovers .github/skills/<name>/SKILL.md via the github provider", async () => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { type MCPServer, mcpCapability } from "@oh-my-pi/pi-coding-agent/capability/mcp";
|
||||
import { loadCapability } from "@oh-my-pi/pi-coding-agent/discovery";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
async function loadStandaloneMcpConfig(cwd: string): Promise<MCPServer[]> {
|
||||
const result = await loadCapability<MCPServer>(mcpCapability.id, {
|
||||
@@ -43,7 +44,7 @@ describe("standalone mcp.json oauth env expansion", () => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempDir);
|
||||
for (const [key, value] of Object.entries(originalEnv)) {
|
||||
if (value === undefined) {
|
||||
delete process.env[key];
|
||||
|
||||
@@ -19,7 +19,7 @@ import * as path from "node:path";
|
||||
import { clearCache as clearFsCache } from "@oh-my-pi/pi-coding-agent/capability/fs";
|
||||
import { type MCPServer, mcpCapability } from "@oh-my-pi/pi-coding-agent/capability/mcp";
|
||||
import { loadCapability } from "@oh-my-pi/pi-coding-agent/discovery";
|
||||
import { getConfigRootDir, setAgentDir } from "@oh-my-pi/pi-utils";
|
||||
import { getConfigRootDir, removeWithRetries, setAgentDir } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const originalAgentDirEnv = process.env.PI_CODING_AGENT_DIR;
|
||||
const fallbackAgentDir = path.join(getConfigRootDir(), "agent");
|
||||
@@ -60,8 +60,8 @@ describe("native user-level MCP discovery follows the active profile", () => {
|
||||
}
|
||||
if (originalHome === undefined) delete process.env.HOME;
|
||||
else process.env.HOME = originalHome;
|
||||
await fs.rm(tempHome, { recursive: true, force: true });
|
||||
await fs.rm(projectDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempHome);
|
||||
await removeWithRetries(projectDir);
|
||||
});
|
||||
|
||||
test("active profile loads its own user server, not the default profile's", async () => {
|
||||
@@ -89,7 +89,7 @@ describe("native user-level MCP discovery follows the active profile", () => {
|
||||
expect(profileServer?._source.level).toBe("user");
|
||||
expect(profileServer?._source.path).toBe(path.join(profileAgentDir, "mcp.json"));
|
||||
|
||||
await fs.rm(profileAgentDir, { recursive: true, force: true });
|
||||
await removeWithRetries(profileAgentDir);
|
||||
});
|
||||
|
||||
test("default profile loads the user server from ~/.omp/agent", async () => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import { clearCache } from "@oh-my-pi/pi-coding-agent/capability/fs";
|
||||
import type { Skill } from "@oh-my-pi/pi-coding-agent/capability/skill";
|
||||
import type { LoadContext, LoadResult } from "@oh-my-pi/pi-coding-agent/capability/types";
|
||||
import { scanSkillsFromDir } from "@oh-my-pi/pi-coding-agent/discovery/helpers";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
function writeSkill(dir: string, name: string, description: string): void {
|
||||
const skillDir = path.join(dir, name);
|
||||
@@ -42,7 +43,7 @@ describe("monorepo skill discovery", () => {
|
||||
|
||||
afterEach(() => {
|
||||
clearCache();
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
});
|
||||
|
||||
test("finds skills in ancestor .omp/skills/ directories", async () => {
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
clearOmpExtensionCliRoots,
|
||||
injectOmpExtensionCliRoots,
|
||||
} from "@oh-my-pi/pi-coding-agent/discovery/omp-extension-roots";
|
||||
import { getConfigRootDir, setAgentDir } from "@oh-my-pi/pi-utils";
|
||||
import { getConfigRootDir, removeSyncWithRetries, setAgentDir } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const PROVIDER_ID = "omp-plugins";
|
||||
|
||||
@@ -108,7 +108,7 @@ afterEach(() => {
|
||||
setAgentDir(fallbackAgentDir);
|
||||
delete process.env.PI_CODING_AGENT_DIR;
|
||||
}
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
});
|
||||
|
||||
function ctx(): LoadContext {
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { type MCPServer, mcpCapability } from "@oh-my-pi/pi-coding-agent/capability/mcp";
|
||||
import { loadCapability } from "@oh-my-pi/pi-coding-agent/discovery";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
async function loadOpenCodeMcpConfig(cwd: string): Promise<MCPServer[]> {
|
||||
const result = await loadCapability<MCPServer>(mcpCapability.id, {
|
||||
@@ -21,7 +22,7 @@ describe("OpenCode MCP discovery", () => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempDir);
|
||||
});
|
||||
|
||||
test("normalizes array commands and OpenCode environment fields", async () => {
|
||||
|
||||
@@ -21,7 +21,7 @@ import { clearCache as clearFsCache } from "@oh-my-pi/pi-coding-agent/capability
|
||||
import { type Skill, skillCapability } from "@oh-my-pi/pi-coding-agent/capability/skill";
|
||||
import { type SlashCommand, slashCommandCapability } from "@oh-my-pi/pi-coding-agent/capability/slash-command";
|
||||
import { loadCapability } from "@oh-my-pi/pi-coding-agent/discovery";
|
||||
import { getConfigRootDir, setAgentDir } from "@oh-my-pi/pi-utils";
|
||||
import { getConfigRootDir, removeWithRetries, setAgentDir } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const originalAgentDirEnv = process.env.PI_CODING_AGENT_DIR;
|
||||
const fallbackAgentDir = path.join(getConfigRootDir(), "agent");
|
||||
@@ -74,9 +74,9 @@ describe("native user-level config discovery follows the active profile", () =>
|
||||
}
|
||||
if (originalHome === undefined) delete process.env.HOME;
|
||||
else process.env.HOME = originalHome;
|
||||
await fs.rm(tempHome, { recursive: true, force: true });
|
||||
await fs.rm(projectDir, { recursive: true, force: true });
|
||||
await fs.rm(profileAgentDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempHome);
|
||||
await removeWithRetries(projectDir);
|
||||
await removeWithRetries(profileAgentDir);
|
||||
});
|
||||
|
||||
test("slash commands resolve from the profile, not the default agent dir", async () => {
|
||||
|
||||
@@ -10,6 +10,7 @@ import type { WritethroughCallback } from "@oh-my-pi/pi-coding-agent/lsp";
|
||||
import type { PlanModeState } from "@oh-my-pi/pi-coding-agent/plan-mode/state";
|
||||
import type { ClientBridge } from "@oh-my-pi/pi-coding-agent/session/client-bridge";
|
||||
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
// ─── Shared helpers ───────────────────────────────────────────────────────────
|
||||
|
||||
@@ -80,7 +81,7 @@ describe("HashlineFilesystem ACP fs routing", () => {
|
||||
|
||||
afterEach(async () => {
|
||||
resetSettingsForTest();
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tmpDir);
|
||||
});
|
||||
|
||||
it("routes plain workspace writes through the bridge and skips writethrough", async () => {
|
||||
@@ -170,7 +171,7 @@ describe("executeReplaceSingle ACP fs routing", () => {
|
||||
|
||||
afterEach(async () => {
|
||||
resetSettingsForTest();
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tmpDir);
|
||||
});
|
||||
|
||||
it("routes plain workspace writes through the bridge and skips writethrough", async () => {
|
||||
@@ -243,7 +244,7 @@ describe("executePatchSingle ACP fs routing", () => {
|
||||
|
||||
afterEach(async () => {
|
||||
resetSettingsForTest();
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tmpDir);
|
||||
});
|
||||
|
||||
it("routes plain workspace writes through the bridge and skips writethrough", async () => {
|
||||
|
||||
@@ -29,7 +29,7 @@ import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manage
|
||||
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import * as autoGeneratedGuard from "@oh-my-pi/pi-coding-agent/tools/auto-generated-guard";
|
||||
import { ToolError } from "@oh-my-pi/pi-coding-agent/tools/tool-errors";
|
||||
import { Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { removeSyncWithRetries, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { type } from "arktype";
|
||||
|
||||
function createAssistantMessage(content: AssistantMessage["content"], stopReason: StopReason): AssistantMessage {
|
||||
@@ -187,7 +187,7 @@ beforeEach(() => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (tempDir) fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
if (tempDir) removeSyncWithRetries(tempDir);
|
||||
});
|
||||
|
||||
it("auto-generated streaming abort fires even when edit.streamingAbort is disabled", async () => {
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
DEFAULT_FUZZY_THRESHOLD,
|
||||
findMatch,
|
||||
} from "@oh-my-pi/pi-coding-agent/edit";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
describe("findMatch", () => {
|
||||
describe("exact matching", () => {
|
||||
@@ -227,7 +228,7 @@ describe("computeHashlineDiff", () => {
|
||||
|
||||
afterEach(async () => {
|
||||
if (tempDir) {
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempDir);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -308,7 +309,7 @@ describe("computeEditDiff", () => {
|
||||
|
||||
afterEach(async () => {
|
||||
if (tempDir) {
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempDir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { resetSettingsForTest, Settings } from "@oh-my-pi/pi-coding-agent/config
|
||||
import { DEFAULT_FUZZY_THRESHOLD, executePatchSingle } from "@oh-my-pi/pi-coding-agent/edit";
|
||||
import type { FileDiagnosticsResult } from "@oh-my-pi/pi-coding-agent/lsp";
|
||||
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
function makeSession(cwd: string): ToolSession {
|
||||
return {
|
||||
@@ -47,7 +48,7 @@ beforeEach(async () => {
|
||||
|
||||
afterEach(async () => {
|
||||
resetSettingsForTest();
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempDir);
|
||||
});
|
||||
|
||||
describe("executePatchSingle — post-write verification error path", () => {
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from "@oh-my-pi/pi-coding-agent/edit";
|
||||
import { writethroughNoop } from "@oh-my-pi/pi-coding-agent/lsp";
|
||||
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
// ─── Minimal ToolSession stub ────────────────────────────────────────────────
|
||||
|
||||
@@ -47,7 +48,7 @@ beforeEach(async () => {
|
||||
|
||||
afterEach(async () => {
|
||||
resetSettingsForTest();
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempDir);
|
||||
});
|
||||
|
||||
// ─── executePatchSingle ───────────────────────────────────────────────────────
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { computeFileHash, formatHashlineHeader, InMemorySnapshotStore } from "@oh-my-pi/hashline";
|
||||
import { dropIncompleteLastEdit, EDIT_MODE_STRATEGIES } from "@oh-my-pi/pi-coding-agent/edit";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
describe("dropIncompleteLastEdit", () => {
|
||||
test("keeps all entries when partialJson is undefined", () => {
|
||||
@@ -60,7 +61,7 @@ describe("hashline streaming preview (multi-section)", () => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tmpDir);
|
||||
});
|
||||
|
||||
const ctx = (cwd: string) => ({ cwd, signal: new AbortController().signal, snapshots });
|
||||
@@ -114,7 +115,7 @@ describe("hashline streaming preview (single-op trailing payload)", () => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tmpDir);
|
||||
});
|
||||
|
||||
const ctx = (cwd: string, isStreaming = true) => ({
|
||||
@@ -211,7 +212,7 @@ describe("hashline streaming preview (monotonic growth)", () => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tmpDir);
|
||||
});
|
||||
|
||||
const ctx = (cwd: string) => ({ cwd, signal: new AbortController().signal, snapshots, isStreaming: true });
|
||||
@@ -257,7 +258,7 @@ describe("apply_patch streaming preview (trailing partial line)", () => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tmpDir);
|
||||
});
|
||||
|
||||
const ctx = (cwd: string, isStreaming: boolean) => ({
|
||||
|
||||
@@ -7,6 +7,7 @@ import { type ExecuteHashlineSingleOptions, executeHashlineSingle } from "@oh-my
|
||||
import { canonicalSnapshotKey, getFileSnapshotStore } from "@oh-my-pi/pi-coding-agent/edit/file-snapshot-store";
|
||||
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { ReadTool } from "@oh-my-pi/pi-coding-agent/tools/read";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import { GrepTool } from "../../src/tools/grep";
|
||||
|
||||
function createSession(cwd: string): ToolSession {
|
||||
@@ -78,7 +79,7 @@ describe("read → edit seen-line guard", () => {
|
||||
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "seen-line-guard-"));
|
||||
});
|
||||
afterEach(async () => {
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tmpDir);
|
||||
});
|
||||
|
||||
it("records the displayed range as seen and excludes far lines", async () => {
|
||||
@@ -180,7 +181,7 @@ describe("search → edit seen-line guard", () => {
|
||||
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "seen-line-search-"));
|
||||
});
|
||||
afterEach(async () => {
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tmpDir);
|
||||
});
|
||||
|
||||
function searchSession(cwd: string): ToolSession {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import { collectSubSessions } from "../src/export/html";
|
||||
|
||||
/**
|
||||
@@ -41,7 +42,7 @@ describe("collectSubSessions", () => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await fs.rm(root, { recursive: true, force: true });
|
||||
await removeWithRetries(root);
|
||||
});
|
||||
|
||||
test("collects nested subagent sessions with parent links and leaf ids", async () => {
|
||||
|
||||
@@ -10,6 +10,7 @@ import type { PrDiffPayload, ViewLookupResult } from "@oh-my-pi/pi-coding-agent/
|
||||
import * as gh from "@oh-my-pi/pi-coding-agent/tools/gh";
|
||||
import * as git from "@oh-my-pi/pi-coding-agent/utils/git";
|
||||
import * as jj from "@oh-my-pi/pi-coding-agent/utils/jj";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const SAMPLE_JJ_DIFF = `diff --git a/src/workspace.ts b/src/workspace.ts
|
||||
--- a/src/workspace.ts
|
||||
@@ -86,7 +87,7 @@ describe("ReviewCommand", () => {
|
||||
afterEach(async () => {
|
||||
vi.restoreAllMocks();
|
||||
if (tmpDir) {
|
||||
await fs.rm(tmpDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tmpDir);
|
||||
tmpDir = undefined;
|
||||
}
|
||||
});
|
||||
@@ -183,7 +184,7 @@ describe("ReviewCommand", () => {
|
||||
const result = await command.execute([], ctx);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
await removeWithRetries(dir);
|
||||
tmpDir = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
loadLegacyPiModule,
|
||||
} from "@oh-my-pi/pi-coding-agent/extensibility/plugins/legacy-pi-compat";
|
||||
import { Type as TypeBoxShimType } from "@oh-my-pi/pi-coding-agent/extensibility/typebox";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
// pi-ai 15.1.0 removed the runtime `Type` export from `@oh-my-pi/pi-ai`'s
|
||||
// package root. Legacy extensions (and their aliased-scope variants such as
|
||||
@@ -29,7 +30,7 @@ afterEach(() => {
|
||||
|
||||
afterAll(async () => {
|
||||
for (const dir of tempRoots) {
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
await removeWithRetries(dir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
__rewriteLegacyExtensionSourceForTests,
|
||||
loadLegacyPiModule,
|
||||
} from "@oh-my-pi/pi-coding-agent/extensibility/plugins/legacy-pi-compat";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
// Issue #1674: legacy Pi extensions load browser-UI assets (HTML/CSS) at module
|
||||
// init via `readFileSync(join(__dirname, "ui.html"))`. The compat layer must run
|
||||
@@ -19,7 +20,7 @@ const tempRoots: string[] = [];
|
||||
|
||||
afterAll(async () => {
|
||||
for (const dir of tempRoots) {
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
await removeWithRetries(dir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
loadLegacyPiModule,
|
||||
} from "@oh-my-pi/pi-coding-agent/extensibility/plugins/legacy-pi-compat";
|
||||
import { Type as TypeBoxShimType } from "@oh-my-pi/pi-coding-agent/extensibility/typebox";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
// The remap installs a Bun.plugin onResolve hook plus an explicit
|
||||
// rewrite branch inside `rewriteBareImportsForLegacyExtension` that
|
||||
@@ -19,7 +20,7 @@ const tempRoots: string[] = [];
|
||||
|
||||
afterAll(async () => {
|
||||
for (const dir of tempRoots) {
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
await removeWithRetries(dir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { __internalsForTesting, withFileLock } from "@oh-my-pi/pi-coding-agent/config/file-lock";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const { tryAcquireLock, releaseLock, readLockInfo, isLockStale, getLockPath } = __internalsForTesting;
|
||||
|
||||
@@ -16,7 +17,7 @@ async function mkRoot(): Promise<string> {
|
||||
|
||||
afterAll(async () => {
|
||||
for (const root of ROOTS) {
|
||||
await fs.rm(root, { recursive: true, force: true }).catch(() => {});
|
||||
await removeWithRetries(root).catch(() => {});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -55,7 +56,7 @@ describe("file-lock token ownership (F1)", () => {
|
||||
const stale = await isLockStale(lockPath, 10_000);
|
||||
expect(stale).toBe(false);
|
||||
|
||||
await fs.rm(lockPath, { recursive: true });
|
||||
await removeWithRetries(lockPath);
|
||||
});
|
||||
|
||||
test("withFileLock serializes N concurrent writers without lost updates", async () => {
|
||||
|
||||
@@ -3,12 +3,13 @@ import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { extractFileMentions, generateFileMentionMessages } from "@oh-my-pi/pi-coding-agent/utils/file-mentions";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
afterEach(async () => {
|
||||
for (const dir of tempDirs.splice(0, tempDirs.length)) {
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
await removeWithRetries(dir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import { $ } from "bun";
|
||||
import * as git from "../src/utils/git";
|
||||
|
||||
@@ -55,9 +56,9 @@ describe.skipIf(!supportsReftable)("git reftable support", () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await $`git worktree remove ${worktreeDir} -f`.cwd(sharedRepoDir).quiet().nothrow();
|
||||
await fs.rm(worktreeDir, { recursive: true, force: true }).catch(() => {});
|
||||
await fs.rm(sharedRepoDir, { recursive: true, force: true }).catch(() => {});
|
||||
await fs.rm(configRepoDir, { recursive: true, force: true }).catch(() => {});
|
||||
await removeWithRetries(worktreeDir).catch(() => {});
|
||||
await removeWithRetries(sharedRepoDir).catch(() => {});
|
||||
await removeWithRetries(configRepoDir).catch(() => {});
|
||||
});
|
||||
|
||||
test("resolves references in a reftable repository", async () => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
||||
import { computeBankScope, deriveBankId, ensureBankExists } from "@oh-my-pi/pi-coding-agent/hindsight/bank";
|
||||
import { HindsightApi } from "@oh-my-pi/pi-coding-agent/hindsight/client";
|
||||
import type { HindsightConfig } from "@oh-my-pi/pi-coding-agent/hindsight/config";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
// Isolate `git` invocations in this file from the host's global config —
|
||||
// `~/.gitconfig` commit signing or template hooks would otherwise turn the
|
||||
@@ -178,7 +179,7 @@ describe("computeBankScope", () => {
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (baseDir) await fs.rm(baseDir, { recursive: true, force: true });
|
||||
if (baseDir) await removeWithRetries(baseDir);
|
||||
});
|
||||
|
||||
it("emits the same project tag from the primary checkout and a linked worktree", () => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { HistoryStorage } from "@oh-my-pi/pi-coding-agent/session/history-storage";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
let tempDir = "";
|
||||
|
||||
@@ -28,7 +29,7 @@ afterEach(async () => {
|
||||
HistoryStorage.resetInstance();
|
||||
vi.useRealTimers();
|
||||
if (tempDir) {
|
||||
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
||||
await removeWithRetries(tempDir).catch(() => {});
|
||||
tempDir = "";
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { readImageMetadata } from "@oh-my-pi/pi-utils";
|
||||
import { readImageMetadata, removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
describe("readImageMetadata", () => {
|
||||
let testDir: string;
|
||||
@@ -12,7 +12,7 @@ describe("readImageMetadata", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(testDir);
|
||||
});
|
||||
|
||||
it("reads PNG metadata from header", async () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
/**
|
||||
* Large-paste menu: when a paste reaches the configured `paste.largeMenuThreshold` line count,
|
||||
* the editor's `onLargePaste` hook routes through `InputController.handleLargePaste`, which offers
|
||||
@@ -121,7 +122,7 @@ describe("InputController.presentLargePasteMenu file attachment", () => {
|
||||
let dir: string | undefined;
|
||||
|
||||
afterEach(async () => {
|
||||
if (dir) await fs.rm(dir, { recursive: true, force: true });
|
||||
if (dir) await removeWithRetries(dir);
|
||||
dir = undefined;
|
||||
});
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { commands, isSubcommand, resolveCliArgv } from "@oh-my-pi/pi-coding-agent/cli-commands";
|
||||
import { looksLikeLocalPath } from "@oh-my-pi/pi-coding-agent/commands/install";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
describe("install command is registered as a top-level subcommand", () => {
|
||||
test("CLI runner sees `install` as a known command", () => {
|
||||
@@ -66,7 +67,7 @@ describe("looksLikeLocalPath", () => {
|
||||
expect(looksLikeLocalPath("vendored-ext", tempDir)).toBe(true);
|
||||
expect(looksLikeLocalPath("missing-pkg", tempDir)).toBe(false);
|
||||
} finally {
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,13 +17,14 @@ import { HistoryProtocolHandler } from "@oh-my-pi/pi-coding-agent/internal-urls/
|
||||
import { AgentRegistry } from "@oh-my-pi/pi-coding-agent/registry/agent-registry";
|
||||
import type { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-session";
|
||||
import { CURRENT_SESSION_VERSION } from "@oh-my-pi/pi-coding-agent/session/session-entries";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
async function withTempDir<T>(fn: (dir: string) => Promise<T>): Promise<T> {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "history-protocol-"));
|
||||
try {
|
||||
return await fn(dir);
|
||||
} finally {
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
await removeWithRetries(dir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
||||
import { InternalUrlRouter } from "@oh-my-pi/pi-coding-agent/internal-urls";
|
||||
import { resetForTests as resetCacheForTests } from "@oh-my-pi/pi-coding-agent/tools/github-cache";
|
||||
import * as git from "@oh-my-pi/pi-coding-agent/utils/git";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
let tempDir: string;
|
||||
let originalEnv: string | undefined;
|
||||
@@ -42,7 +43,7 @@ afterEach(async () => {
|
||||
process.env.GH_TOKEN = originalGhToken;
|
||||
}
|
||||
vi.restoreAllMocks();
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempDir);
|
||||
});
|
||||
|
||||
function issuePayload(number: number, body: string, commentBodies: string[] = []) {
|
||||
|
||||
@@ -8,13 +8,14 @@ import {
|
||||
resolveLocalRoot,
|
||||
resolveLocalUrlToPath,
|
||||
} from "@oh-my-pi/pi-coding-agent/internal-urls";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
async function withTempDir<T>(fn: (dir: string) => Promise<T>): Promise<T> {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "local-protocol-"));
|
||||
try {
|
||||
return await fn(dir);
|
||||
} finally {
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
await removeWithRetries(dir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { InternalUrlRouter } from "@oh-my-pi/pi-coding-agent/internal-urls";
|
||||
import { getMemoryRoot } from "@oh-my-pi/pi-coding-agent/memories";
|
||||
import { AgentRegistry } from "@oh-my-pi/pi-coding-agent/registry/agent-registry";
|
||||
import type { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-session";
|
||||
import { getAgentDir, setAgentDir } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDir, removeWithRetries, setAgentDir } from "@oh-my-pi/pi-utils";
|
||||
|
||||
interface MemoryFixture {
|
||||
cwd: string;
|
||||
@@ -42,7 +42,7 @@ async function withMemoryFixture(fn: (fixture: MemoryFixture) => Promise<void>):
|
||||
await fn({ cwd, memoryRoot, agentDir, cleanupRoot });
|
||||
} finally {
|
||||
setAgentDir(previousAgentDir);
|
||||
await fs.rm(cleanupRoot, { recursive: true, force: true });
|
||||
await removeWithRetries(cleanupRoot);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,13 +10,14 @@ import {
|
||||
VaultProtocolHandler,
|
||||
} from "@oh-my-pi/pi-coding-agent/internal-urls";
|
||||
import * as vaultProtocol from "@oh-my-pi/pi-coding-agent/internal-urls/vault-protocol";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
async function withTempDir<T>(fn: (dir: string) => Promise<T>): Promise<T> {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "vault-protocol-"));
|
||||
try {
|
||||
return await fn(dir);
|
||||
} finally {
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
await removeWithRetries(dir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { processFileArguments } from "@oh-my-pi/pi-coding-agent/cli/file-processor";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
function createPdfWithText(text: string): string {
|
||||
const chunks: string[] = [];
|
||||
@@ -47,7 +48,7 @@ describe("processFileArguments", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(testDir);
|
||||
});
|
||||
|
||||
it("converts PDF file arguments before adding them to the prompt", async () => {
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as path from "node:path";
|
||||
import type { FetchImpl } from "@oh-my-pi/pi-ai/types";
|
||||
import { ModelRegistry } from "@oh-my-pi/pi-coding-agent/config/model-registry";
|
||||
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
|
||||
import { Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { removeSyncWithRetries, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
|
||||
/**
|
||||
* Issue #1528: auto-discovered OpenAI-compatible models defaulted to
|
||||
@@ -31,7 +31,7 @@ describe("issue #1528 discovery maxTokens default", () => {
|
||||
afterEach(() => {
|
||||
authStorage.close();
|
||||
if (tempDir && fs.existsSync(tempDir)) {
|
||||
fs.rmSync(tempDir, { recursive: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { resolveUpdateMethodForTest } from "@oh-my-pi/pi-coding-agent/cli/update-cli";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
// Issue #845: on Windows with Bun installed via Scoop, ~/.bun is a junction
|
||||
// to scoop\persist\Oven-sh.Bun\.bun. `bun pm bin -g` and the omp path that
|
||||
@@ -34,7 +35,7 @@ describe("issue-845: resolveUpdateMethod follows symlinks/junctions", () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tmpRoot);
|
||||
});
|
||||
|
||||
it("classifies omp reached through a symlinked bin dir as bun-managed", () => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
||||
import { loadCapability } from "@oh-my-pi/pi-coding-agent/capability";
|
||||
import { clearCache as clearFsCache } from "@oh-my-pi/pi-coding-agent/capability/fs";
|
||||
import { clearClaudePluginRootsCache } from "@oh-my-pi/pi-coding-agent/discovery/helpers";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import "@oh-my-pi/pi-coding-agent/discovery/claude-plugins";
|
||||
import type { MCPServer } from "@oh-my-pi/pi-coding-agent/capability/mcp";
|
||||
|
||||
@@ -27,7 +28,7 @@ describe("issue-851: claude-plugins loads flat .mcp.json shape", () => {
|
||||
vi.restoreAllMocks();
|
||||
if (originalHome === undefined) delete process.env.HOME;
|
||||
else process.env.HOME = originalHome;
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempDir);
|
||||
});
|
||||
|
||||
async function setupPlugin(pluginId: string, mcpJson: unknown): Promise<void> {
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as path from "node:path";
|
||||
import * as mcpClient from "@oh-my-pi/pi-coding-agent/mcp/client";
|
||||
import { MCPCommandController } from "@oh-my-pi/pi-coding-agent/modes/controllers/mcp-command-controller";
|
||||
import { initTheme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
||||
import { getConfigRootDir, getProjectDir, setAgentDir, setProjectDir } from "@oh-my-pi/pi-utils";
|
||||
import { getConfigRootDir, getProjectDir, removeWithRetries, setAgentDir, setProjectDir } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const originalProjectDir = getProjectDir();
|
||||
const originalAgentDir = process.env.PI_CODING_AGENT_DIR;
|
||||
@@ -52,8 +52,8 @@ describe("issue #956: interactive /mcp test", () => {
|
||||
setAgentDir(fallbackAgentDir);
|
||||
delete process.env.PI_CODING_AGENT_DIR;
|
||||
}
|
||||
await fs.rm(projectDir, { recursive: true, force: true });
|
||||
await fs.rm(agentDir, { recursive: true, force: true });
|
||||
await removeWithRetries(projectDir);
|
||||
await removeWithRetries(agentDir);
|
||||
});
|
||||
|
||||
it("tests a connected server discovered from standalone .mcp.json", async () => {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ModelSelectorComponent } from "@oh-my-pi/pi-coding-agent/modes/componen
|
||||
import { getThemeByName, setThemeInstance } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
||||
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
|
||||
import type { TUI } from "@oh-my-pi/pi-tui";
|
||||
import { Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { removeSyncWithRetries, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
|
||||
function normalizeRenderedText(text: string): string {
|
||||
return stripVTControlCharacters(text).replace(/\s+/g, " ").trim();
|
||||
@@ -78,7 +78,7 @@ describe("issue #970 custom provider discovery", () => {
|
||||
afterEach(() => {
|
||||
authStorage.close();
|
||||
if (tempDir && fs.existsSync(tempDir)) {
|
||||
fs.rmSync(tempDir, { recursive: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { discoverAndLoadExtensions } from "@oh-my-pi/pi-coding-agent/extensibility/extensions/loader";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const TOOL_NAME = "legacy-multi-file-tool";
|
||||
|
||||
@@ -10,7 +11,7 @@ describe("issue #983: multi-file legacy Pi extensions", () => {
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(tempDirs.splice(0).map(dir => fs.rm(dir, { recursive: true, force: true })));
|
||||
await Promise.all(tempDirs.splice(0).map(dir => removeWithRetries(dir)));
|
||||
});
|
||||
|
||||
it("loads legacy Pi extensions whose sibling TypeScript files import each other via relative paths", async () => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
||||
import { KeybindingsManager } from "@oh-my-pi/pi-coding-agent/config/keybindings";
|
||||
import { matchesAppFollowUp } from "@oh-my-pi/pi-coding-agent/modes/utils/keybinding-matchers";
|
||||
import { setKeybindings } from "@oh-my-pi/pi-tui";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import { YAML } from "bun";
|
||||
|
||||
function ctrl(key: string): string {
|
||||
@@ -55,7 +56,7 @@ describe("KeybindingsManager.create", () => {
|
||||
expect(writtenConfig).not.toHaveProperty("selectModelTemporary");
|
||||
expect(await Bun.file(jsonPath).exists()).toBe(true);
|
||||
} finally {
|
||||
await fs.rm(agentDir, { recursive: true, force: true });
|
||||
await removeWithRetries(agentDir);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -92,7 +93,7 @@ describe("KeybindingsManager.create", () => {
|
||||
});
|
||||
expect(await Bun.file(jsonPath).exists()).toBe(true);
|
||||
} finally {
|
||||
await fs.rm(agentDir, { recursive: true, force: true });
|
||||
await removeWithRetries(agentDir);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -118,7 +119,7 @@ describe("KeybindingsManager.create", () => {
|
||||
expect(manager.getKeys("app.session.fork")).toEqual(["ctrl+f"]);
|
||||
expect(manager.getKeys("app.clipboard.copyPrompt")).toEqual(["alt+c", "ctrl+shift+c"]);
|
||||
} finally {
|
||||
await fs.rm(agentDir, { recursive: true, force: true });
|
||||
await removeWithRetries(agentDir);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -144,7 +145,7 @@ describe("KeybindingsManager.create", () => {
|
||||
expect(manager.getKeys("app.plan.toggle")).toEqual(["alt+shift+p"]);
|
||||
expect(await Bun.file(canonicalPath).exists()).toBe(false);
|
||||
} finally {
|
||||
await fs.rm(agentDir, { recursive: true, force: true });
|
||||
await removeWithRetries(agentDir);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -184,7 +185,7 @@ describe("KeybindingsManager.create", () => {
|
||||
// of the box, without breaking users on Kitty/iTerm2/WezTerm/Ghostty.
|
||||
expect(manager.getKeys("app.message.followUp")).toEqual(["ctrl+q", "ctrl+enter"]);
|
||||
} finally {
|
||||
await fs.rm(agentDir, { recursive: true, force: true });
|
||||
await removeWithRetries(agentDir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as path from "node:path";
|
||||
import type { FetchImpl } from "@oh-my-pi/pi-ai/types";
|
||||
import { ModelRegistry } from "@oh-my-pi/pi-coding-agent/config/model-registry";
|
||||
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
|
||||
import { Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { removeSyncWithRetries, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
|
||||
describe("ModelRegistry LM Studio Fixes", () => {
|
||||
let tempDir: string;
|
||||
@@ -22,7 +22,7 @@ describe("ModelRegistry LM Studio Fixes", () => {
|
||||
afterEach(() => {
|
||||
authStorage.close();
|
||||
if (tempDir && fs.existsSync(tempDir)) {
|
||||
fs.rmSync(tempDir, { recursive: true });
|
||||
removeSyncWithRetries(tempDir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { detectIndentFromContent, resolveFormatOptions } from "@oh-my-pi/pi-coding-agent/lsp/format-options";
|
||||
import { getProjectDir, Snowflake, setProjectDir } from "@oh-my-pi/pi-utils";
|
||||
import { getProjectDir, removeWithRetries, Snowflake, setProjectDir } from "@oh-my-pi/pi-utils";
|
||||
|
||||
/**
|
||||
* Regression coverage for issue #2329 — the LSP format-on-write path used to
|
||||
@@ -68,7 +68,7 @@ describe("resolveFormatOptions", () => {
|
||||
|
||||
afterEach(async () => {
|
||||
setProjectDir(previousProjectDir);
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
await removeWithRetries(tempDir);
|
||||
});
|
||||
|
||||
it("falls back to 2-space indent when no .editorconfig and no content signal exist", () => {
|
||||
|
||||
@@ -6,11 +6,12 @@ import { applyResolvedSystemPromptInputs, submitInteractiveInput } from "@oh-my-
|
||||
import type { SubmittedUserInput } from "@oh-my-pi/pi-coding-agent/modes/types";
|
||||
import type { CreateAgentSessionOptions } from "@oh-my-pi/pi-coding-agent/sdk";
|
||||
import { discoverTitleSystemPromptFile } from "@oh-my-pi/pi-coding-agent/system-prompt";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const cleanupDirs: string[] = [];
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(cleanupDirs.splice(0).map(dir => fs.rm(dir, { recursive: true, force: true })));
|
||||
await Promise.all(cleanupDirs.splice(0).map(dir => removeWithRetries(dir)));
|
||||
});
|
||||
|
||||
function createInput(overrides: Partial<SubmittedUserInput> = {}): SubmittedUserInput {
|
||||
|
||||
@@ -3,7 +3,6 @@ import * as fs from "node:fs";
|
||||
import * as fsp from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
|
||||
import {
|
||||
cachePlugin,
|
||||
cleanOrphanedCache,
|
||||
@@ -12,6 +11,7 @@ import {
|
||||
isValidVersionForCache,
|
||||
removeCachedPlugin,
|
||||
} from "@oh-my-pi/pi-coding-agent/extensibility/plugins/marketplace";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
// ── Helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -107,7 +107,7 @@ describe("cachePlugin, isCached, removeCachedPlugin", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tmpDir);
|
||||
});
|
||||
|
||||
it("isCached returns false before caching", async () => {
|
||||
@@ -174,7 +174,7 @@ describe("cleanOrphanedCache", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tmpDir);
|
||||
});
|
||||
|
||||
it("returns { removed: 0 } when cacheDir does not exist", async () => {
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
readInstalledPluginsRegistry,
|
||||
writeInstalledPluginsRegistry,
|
||||
} from "@oh-my-pi/pi-coding-agent/extensibility/plugins/marketplace";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
// ── Inline validator ───────────────────────────────────────────────────────────
|
||||
//
|
||||
@@ -79,7 +80,7 @@ beforeEach(() => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tmpHome);
|
||||
});
|
||||
|
||||
// ── Path contract ─────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -2,12 +2,12 @@ import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
|
||||
import {
|
||||
classifySource,
|
||||
fetchMarketplace,
|
||||
parseMarketplaceCatalog,
|
||||
} from "@oh-my-pi/pi-coding-agent/extensibility/plugins/marketplace";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
// Fixture lives at test/marketplace/fixtures/valid-marketplace/
|
||||
const FIXTURE_DIR = path.join(import.meta.dir, "fixtures", "valid-marketplace");
|
||||
@@ -156,7 +156,7 @@ describe("fetchMarketplace", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tmpDir);
|
||||
});
|
||||
|
||||
it("resolves catalog from fixture directory", async () => {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
MarketplaceManager,
|
||||
readInstalledPluginsRegistry,
|
||||
} from "@oh-my-pi/pi-coding-agent/extensibility/plugins/marketplace";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
// Minimal marketplace fixture, built once into a temp dir (see beforeAll). It carries only
|
||||
// what these tests assert — one plugin entry plus a plugin.json for the version-fallback path —
|
||||
@@ -99,7 +100,7 @@ describe("MarketplaceManager", () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
fs.rmSync(FIXTURE_DIR, { recursive: true, force: true });
|
||||
removeSyncWithRetries(FIXTURE_DIR);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -107,7 +108,7 @@ describe("MarketplaceManager", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(ctx.tmpDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(ctx.tmpDir);
|
||||
});
|
||||
|
||||
// ── Marketplace lifecycle ──────────────────────────────────────────────
|
||||
@@ -570,7 +571,7 @@ describe("MarketplaceManager", () => {
|
||||
noProjectManager.installPlugin("hello-plugin", "test-marketplace", { scope: "project" }),
|
||||
).rejects.toThrow(/project directory/);
|
||||
} finally {
|
||||
fs.rmSync(tmp, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tmp);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
readInstalledPluginsRegistry,
|
||||
writeInstalledPluginsRegistry,
|
||||
} from "@oh-my-pi/pi-coding-agent/extensibility/plugins/marketplace";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
// ── Fixtures ──────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -47,7 +48,7 @@ describe("resolveActiveProjectRegistryPath", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tmpDir);
|
||||
});
|
||||
|
||||
it("walk-up finds nearest .omp/ directory", async () => {
|
||||
@@ -158,8 +159,8 @@ describe("listClaudePluginRoots — project shadows user", () => {
|
||||
afterEach(() => {
|
||||
// Cache is keyed by home:projectPath — must clear between tests.
|
||||
clearClaudePluginRootsCache();
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpProject, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tmpHome);
|
||||
removeSyncWithRetries(tmpProject);
|
||||
});
|
||||
|
||||
it("project entry shadows user entry when plugin IDs match", async () => {
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
writeInstalledPluginsRegistry,
|
||||
writeMarketplacesRegistry,
|
||||
} from "@oh-my-pi/pi-coding-agent/extensibility/plugins/marketplace";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
// Inline the parseClaudePluginsRegistry validation logic to avoid pulling
|
||||
// in discovery/helpers.ts which transitively imports @oh-my-pi/pi-natives.
|
||||
@@ -207,7 +208,7 @@ describe("registry file I/O", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tmpDir);
|
||||
});
|
||||
|
||||
// ── Marketplaces registry ────────────────────────────────────────
|
||||
|
||||
@@ -2,9 +2,9 @@ import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
|
||||
import type { MarketplacePluginEntry } from "@oh-my-pi/pi-coding-agent/extensibility/plugins/marketplace";
|
||||
import { resolvePluginSource } from "@oh-my-pi/pi-coding-agent/extensibility/plugins/marketplace";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
// Fixture: a cloned marketplace with a single plugin at ./plugins/hello-plugin
|
||||
const FIXTURE_DIR = path.resolve(import.meta.dir, "fixtures/valid-marketplace");
|
||||
@@ -22,7 +22,7 @@ describe("resolvePluginSource", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(tmpDir);
|
||||
});
|
||||
|
||||
it("resolves relative source to absolute plugin directory", async () => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { convertBufferWithMarkit, convertFileWithMarkit } from "@oh-my-pi/pi-coding-agent/utils/markit";
|
||||
import { zip } from "@oh-my-pi/pi-coding-agent/utils/zip";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const enc = (s: string): Uint8Array => new TextEncoder().encode(s);
|
||||
const WML = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
||||
@@ -124,7 +125,7 @@ describe("markit converters", () => {
|
||||
expect(written).toHaveLength(1);
|
||||
expect(result.content).toContain(`](${path.join(imageDir, written[0]!)})`);
|
||||
} finally {
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
await removeWithRetries(dir);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -9,7 +9,14 @@ import * as oauthFlow from "@oh-my-pi/pi-coding-agent/mcp/oauth-flow";
|
||||
import type { MCPServerConfig } from "@oh-my-pi/pi-coding-agent/mcp/types";
|
||||
import { MCPCommandController } from "@oh-my-pi/pi-coding-agent/modes/controllers/mcp-command-controller";
|
||||
import { initTheme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
||||
import { getConfigRootDir, getMCPConfigPath, getProjectDir, setAgentDir, setProjectDir } from "@oh-my-pi/pi-utils";
|
||||
import {
|
||||
getConfigRootDir,
|
||||
getMCPConfigPath,
|
||||
getProjectDir,
|
||||
removeWithRetries,
|
||||
setAgentDir,
|
||||
setProjectDir,
|
||||
} from "@oh-my-pi/pi-utils";
|
||||
|
||||
const RAW_SERVER_URL = `https://\${MCP_HOST}/mcp`;
|
||||
const EXPANDED_SERVER_URL = "https://mcp.example.com/mcp";
|
||||
@@ -126,8 +133,8 @@ describe("/mcp auth commands", () => {
|
||||
setAgentDir(fallbackAgentDir);
|
||||
delete process.env.PI_CODING_AGENT_DIR;
|
||||
}
|
||||
await fs.rm(projectDir, { recursive: true, force: true });
|
||||
await fs.rm(agentDir, { recursive: true, force: true });
|
||||
await removeWithRetries(projectDir);
|
||||
await removeWithRetries(agentDir);
|
||||
});
|
||||
|
||||
test("stores definition-only OAuth credentials under the expanded URL key", async () => {
|
||||
|
||||
@@ -6,7 +6,14 @@ import type { SourceMeta } from "@oh-my-pi/pi-coding-agent/capability/types";
|
||||
import type { MCPServerConfig } from "@oh-my-pi/pi-coding-agent/mcp/types";
|
||||
import { MCPCommandController } from "@oh-my-pi/pi-coding-agent/modes/controllers/mcp-command-controller";
|
||||
import { initTheme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
||||
import { getConfigRootDir, getMCPConfigPath, getProjectDir, setAgentDir, setProjectDir } from "@oh-my-pi/pi-utils";
|
||||
import {
|
||||
getConfigRootDir,
|
||||
getMCPConfigPath,
|
||||
getProjectDir,
|
||||
removeWithRetries,
|
||||
setAgentDir,
|
||||
setProjectDir,
|
||||
} from "@oh-my-pi/pi-utils";
|
||||
|
||||
const originalProjectDir = getProjectDir();
|
||||
const originalAgentDir = process.env.PI_CODING_AGENT_DIR;
|
||||
@@ -97,8 +104,8 @@ describe("/mcp enable and disable", () => {
|
||||
vi.restoreAllMocks();
|
||||
setProjectDir(originalProjectDir);
|
||||
restoreAgentDir();
|
||||
await fs.rm(projectDir, { recursive: true, force: true });
|
||||
await fs.rm(agentDir, { recursive: true, force: true });
|
||||
await removeWithRetries(projectDir);
|
||||
await removeWithRetries(agentDir);
|
||||
});
|
||||
|
||||
test("disabling one configured server does not reload other MCP servers", async () => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
||||
import { MCPManager } from "@oh-my-pi/pi-coding-agent/mcp/manager";
|
||||
import type { McpConnectionStatusEvent } from "@oh-my-pi/pi-coding-agent/mcp/startup-events";
|
||||
import type { MCPServerConfig } from "@oh-my-pi/pi-coding-agent/mcp/types";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const FIXTURE_PATH = path.join(import.meta.dir, "fixtures", "many-tools-mcp.ts");
|
||||
const BUN_EXEC = process.execPath;
|
||||
@@ -17,7 +18,7 @@ describe("MCPManager connection status events", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(workDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(workDir);
|
||||
});
|
||||
|
||||
it("emits connecting, connected, and failed updates for startup status", async () => {
|
||||
|
||||
@@ -21,6 +21,7 @@ import { describe, expect, it } from "bun:test";
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import type { SourceMeta } from "../src/capability/types";
|
||||
import { readMCPConfigFile, updateMCPServer, validateServerName } from "../src/mcp/config-writer";
|
||||
import { MCPManager } from "../src/mcp/manager";
|
||||
@@ -63,7 +64,7 @@ describe("MCP discovered-server reauth", () => {
|
||||
expect(manager.getSource(NAMESPACED_NAME)).toEqual(source);
|
||||
} finally {
|
||||
await manager.disconnectAll();
|
||||
fs.rmSync(workDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(workDir);
|
||||
}
|
||||
}, 15_000);
|
||||
|
||||
@@ -101,7 +102,7 @@ describe("MCP discovered-server reauth", () => {
|
||||
const readBack = await readMCPConfigFile(filePath);
|
||||
expect(readBack.mcpServers?.[NAMESPACED_NAME]).toEqual(persisted);
|
||||
} finally {
|
||||
fs.rmSync(workDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(workDir);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { withTimeout } from "@oh-my-pi/pi-utils";
|
||||
import { removeSyncWithRetries, withTimeout } from "@oh-my-pi/pi-utils";
|
||||
import { MCPManager } from "../src/mcp/manager";
|
||||
import type { MCPStdioServerConfig } from "../src/mcp/types";
|
||||
|
||||
@@ -38,7 +38,7 @@ describe("owned-manager dispose disconnect is bounded (PR #2839)", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(workDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(workDir);
|
||||
});
|
||||
|
||||
it("bounds the owned disconnect when a transport close stalls", async () => {
|
||||
|
||||
@@ -18,6 +18,7 @@ import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { MCPManager } from "@oh-my-pi/pi-coding-agent/mcp/manager";
|
||||
import type { MCPStdioServerConfig } from "@oh-my-pi/pi-coding-agent/mcp/types";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const FIXTURE_PATH = path.join(import.meta.dir, "fixtures", "crash-after-init-mcp.ts");
|
||||
const BUN_EXEC = process.execPath;
|
||||
@@ -33,7 +34,7 @@ describe("MCP reconnect storm (issue #1592)", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(workDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(workDir);
|
||||
});
|
||||
|
||||
function countSpawns(): number {
|
||||
|
||||
@@ -14,6 +14,7 @@ import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import { listResourceTemplates } from "../src/mcp/client";
|
||||
import { MCPManager } from "../src/mcp/manager";
|
||||
import type { MCPServerConnection, MCPStdioServerConfig, MCPTransport } from "../src/mcp/types";
|
||||
@@ -79,7 +80,7 @@ describe("MCPManager loads resources for a templates-less server", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(workDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(workDir);
|
||||
});
|
||||
|
||||
it("keeps concrete resources when resources/templates/list is unimplemented", async () => {
|
||||
|
||||
@@ -20,6 +20,7 @@ import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
||||
import { MCPManager } from "../src/mcp/manager";
|
||||
import type { MCPStdioServerConfig } from "../src/mcp/types";
|
||||
|
||||
@@ -34,7 +35,7 @@ describe("MCP startup (issue #2100)", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(workDir, { recursive: true, force: true });
|
||||
removeSyncWithRetries(workDir);
|
||||
});
|
||||
|
||||
it("returns promptly when a configured MCP server stalls on initialize", async () => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user