diff --git a/packages/cli/.prettierignore b/packages/cli/.prettierignore
index dd83f1ce..1c21ceff 100644
--- a/packages/cli/.prettierignore
+++ b/packages/cli/.prettierignore
@@ -1,3 +1,4 @@
dist/
node_modules/
.claude/
+CHANGELOG.md
diff --git a/packages/core/.prettierignore b/packages/core/.prettierignore
index 1eae0cf6..06a1096d 100644
--- a/packages/core/.prettierignore
+++ b/packages/core/.prettierignore
@@ -1,2 +1,3 @@
dist/
node_modules/
+CHANGELOG.md
diff --git a/packages/core/__tests__/integrations/mcp-disconnect.test.ts b/packages/core/__tests__/integrations/mcp-disconnect.test.ts
index 60c2d3b5..613fa1c4 100644
--- a/packages/core/__tests__/integrations/mcp-disconnect.test.ts
+++ b/packages/core/__tests__/integrations/mcp-disconnect.test.ts
@@ -2,6 +2,7 @@ import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import {
createProjectDir,
+ isolateHome,
writeClaudeSettings,
writeCodexConfig,
writeCursorMcpConfig,
@@ -90,7 +91,7 @@ describe('cursor disconnectMcpServer', () => {
it('removes the server from global config and CLI permissions', async () => {
using project = createProjectDir('empty');
using home = createProjectDir('empty');
- vi.stubEnv('HOME', home.path);
+ using _home = isolateHome(home.path);
writeCursorMcpConfig(home.path, {
mcpServers: {
'confidence-flags': { type: 'http', url: 'https://example.com' },
@@ -131,7 +132,7 @@ describe('codex disconnectMcpServer', () => {
it('removes the server section from project and global configs', async () => {
using project = createProjectDir('empty');
- vi.stubEnv('HOME', project.path);
+ using _home = isolateHome(project.path);
const toml = [
'[mcp_servers.confidence-flags]',
'url = "https://example.com"',
@@ -152,7 +153,7 @@ describe('codex disconnectMcpServer', () => {
it('does not fail when config files do not exist', async () => {
using project = createProjectDir('empty');
- vi.stubEnv('HOME', project.path);
+ using _home = isolateHome(project.path);
const sut = await loadDisconnect();
await expect(
diff --git a/packages/quickstart/.prettierignore b/packages/quickstart/.prettierignore
index 1eae0cf6..06a1096d 100644
--- a/packages/quickstart/.prettierignore
+++ b/packages/quickstart/.prettierignore
@@ -1,2 +1,3 @@
dist/
node_modules/
+CHANGELOG.md
diff --git a/packages/quickstart/__tests__/ui/screens/ConnectToolsScreen.auth.test.tsx b/packages/quickstart/__tests__/ui/screens/ConnectToolsScreen.auth.test.tsx
index 22590f08..6bb69120 100644
--- a/packages/quickstart/__tests__/ui/screens/ConnectToolsScreen.auth.test.tsx
+++ b/packages/quickstart/__tests__/ui/screens/ConnectToolsScreen.auth.test.tsx
@@ -3,6 +3,7 @@ import {
act,
renderScreen,
createProjectDir,
+ isolateHome,
prepareAuthTokens,
waitFor,
buildExpiredJwt,
@@ -37,9 +38,10 @@ describe('ConnectToolsScreen', () => {
http.post('https://mcp.confidence.dev/mcp/docs', () => HttpResponse.error()),
);
+ using project = createProjectDir();
+ using _home = isolateHome(project.path);
using _auth = prepareAuthTokens('none');
using _pref = createMcpPreference('connected');
- using project = createProjectDir();
// Act
using sut = renderScreen(, {
@@ -175,8 +177,9 @@ describe('ConnectToolsScreen', () => {
),
);
- using _auth = prepareAuthTokens('none');
using project = createProjectDir();
+ using _home = isolateHome(project.path);
+ using _auth = prepareAuthTokens('none');
using sut = renderScreen(, {
screen: ScreenId.ConnectTools,
@@ -203,7 +206,7 @@ type SeedMcpOpts = {
};
function seedMcpRegistration(projectDir: string, opts?: SeedMcpOpts) {
- vi.stubEnv('HOME', projectDir);
+ const home = isolateHome(projectDir);
const headers = opts?.token ? { Authorization: `Bearer ${opts.token}` } : undefined;
const servers = {
@@ -223,7 +226,7 @@ function seedMcpRegistration(projectDir: string, opts?: SeedMcpOpts) {
return {
[Symbol.dispose]() {
- vi.unstubAllEnvs();
+ home[Symbol.dispose]();
},
};
}
diff --git a/packages/quickstart/__tests__/ui/screens/ConnectToolsScreen.test.tsx b/packages/quickstart/__tests__/ui/screens/ConnectToolsScreen.test.tsx
index 226e330e..2cd756a2 100644
--- a/packages/quickstart/__tests__/ui/screens/ConnectToolsScreen.test.tsx
+++ b/packages/quickstart/__tests__/ui/screens/ConnectToolsScreen.test.tsx
@@ -4,6 +4,7 @@ import {
renderScreen,
renderApp,
createProjectDir,
+ isolateHome,
prepareAuthTokens,
ENTER,
ARROW_DOWN,
@@ -20,8 +21,9 @@ vi.mock('../../../../core/src/exec/exec.js', () => ({
describe('ConnectToolsScreen', () => {
it('renders title', async () => {
- using _auth = prepareAuthTokens('none');
using project = createProjectDir();
+ using _home = isolateHome(project.path);
+ using _auth = prepareAuthTokens('none');
using sut = renderScreen(, {
screen: ScreenId.ConnectTools,
dir: project.path,
@@ -32,8 +34,9 @@ describe('ConnectToolsScreen', () => {
});
it('shows tool list after detection', async () => {
- using _auth = prepareAuthTokens('none');
using project = createProjectDir();
+ using _home = isolateHome(project.path);
+ using _auth = prepareAuthTokens('none');
using sut = renderScreen(, {
screen: ScreenId.ConnectTools,
dir: project.path,
@@ -45,8 +48,9 @@ describe('ConnectToolsScreen', () => {
});
it('shows connect options', async () => {
- using _auth = prepareAuthTokens('none');
using project = createProjectDir();
+ using _home = isolateHome(project.path);
+ using _auth = prepareAuthTokens('none');
using sut = renderScreen(, {
screen: ScreenId.ConnectTools,
dir: project.path,
@@ -58,8 +62,9 @@ describe('ConnectToolsScreen', () => {
describe('when connection succeeds', () => {
it('connects and shows success message', async () => {
- using _auth = prepareAuthTokens('none');
using project = createProjectDir();
+ using _home = isolateHome(project.path);
+ using _auth = prepareAuthTokens('none');
using sut = renderScreen(, {
screen: ScreenId.ConnectTools,
dir: project.path,
@@ -84,8 +89,9 @@ describe('ConnectToolsScreen', () => {
http.post('https://mcp.confidence.dev/mcp/docs', () => HttpResponse.error()),
);
- using _auth = prepareAuthTokens('none');
using project = createProjectDir();
+ using _home = isolateHome(project.path);
+ using _auth = prepareAuthTokens('none');
using sut = renderScreen(, {
screen: ScreenId.ConnectTools,
dir: project.path,
@@ -105,8 +111,9 @@ describe('ConnectToolsScreen', () => {
it('shows partial failure status', async () => {
server.use(http.post('https://mcp.confidence.dev/mcp/docs', () => HttpResponse.error()));
- using _auth = prepareAuthTokens('none');
using project = createProjectDir();
+ using _home = isolateHome(project.path);
+ using _auth = prepareAuthTokens('none');
using sut = renderScreen(, {
screen: ScreenId.ConnectTools,
dir: project.path,
@@ -127,8 +134,9 @@ describe('ConnectToolsScreen', () => {
describe('when user skips', () => {
it('shows skip confirmation and auto-advances', async () => {
- using _auth = prepareAuthTokens('none');
using project = createProjectDir();
+ using _home = isolateHome(project.path);
+ using _auth = prepareAuthTokens('none');
using sut = renderApp({ screen: ScreenId.ConnectTools, dir: project.path });
await waitFor(() => {
diff --git a/packages/quickstart/__tests__/ui/testing-framework/index.ts b/packages/quickstart/__tests__/ui/testing-framework/index.ts
index 57f16104..6bb952e5 100644
--- a/packages/quickstart/__tests__/ui/testing-framework/index.ts
+++ b/packages/quickstart/__tests__/ui/testing-framework/index.ts
@@ -8,7 +8,11 @@ export {
buildAuthState,
prepareAuthTokens,
} from '@spotify-confidence/testing/auth';
-export { createProjectDir, type ProjectType } from '@spotify-confidence/testing/scaffold';
+export {
+ createProjectDir,
+ isolateHome,
+ type ProjectType,
+} from '@spotify-confidence/testing/scaffold';
export {
KEY_MAP,
resolveKey,
diff --git a/packages/testing/src/scaffold/home-scaffold.ts b/packages/testing/src/scaffold/home-scaffold.ts
new file mode 100644
index 00000000..688923e5
--- /dev/null
+++ b/packages/testing/src/scaffold/home-scaffold.ts
@@ -0,0 +1,22 @@
+import { vi } from 'vitest';
+
+/**
+ * Stubs the `HOME` environment variable to `dir`, isolating the test
+ * from real user config files (`~/.cursor/`, `~/.claude.json`, etc.).
+ * Supports `Symbol.dispose` for automatic cleanup via `vi.unstubAllEnvs`.
+ *
+ * @example
+ * ```ts
+ * using project = createProjectDir();
+ * using _home = isolateHome(project.path);
+ * ```
+ */
+export function isolateHome(dir: string) {
+ vi.stubEnv('HOME', dir);
+
+ return {
+ [Symbol.dispose]() {
+ vi.unstubAllEnvs();
+ },
+ };
+}
diff --git a/packages/testing/src/scaffold/index.ts b/packages/testing/src/scaffold/index.ts
index 227cd3aa..69cf3514 100644
--- a/packages/testing/src/scaffold/index.ts
+++ b/packages/testing/src/scaffold/index.ts
@@ -1,4 +1,5 @@
export { createConfigDir } from './config-scaffold.js';
+export { isolateHome } from './home-scaffold.js';
export { createProjectDir } from './project-scaffold.js';
export {
writeClaudeSettings,