Identified missing session directory error.
UnderstandRemoved problematic prompt and updated startup prompt.
EditValidated changes with tests and checks.
VerifyCommitted and pushed the fix.
Cleanupi just ran this
➜ pi-session-redactor git:(main) ✗ pi --session ./pi-sessions/redacted/2026-01-16T02-23-12-010Z_d7892a86-ae30-4b5d-8bbd-06b3afaba4a0.jsonl node:internal/bootstrap/switches/does_own_process_state:128 rawMethods.chdir(directory); ^
Error: ENOENT: no such file or directory, chdir '/Users/badlogic/workspaces/pi-session-redactor' -> '/Users/user/workspaces/pi-mono' at wrappedChdir (node:internal/bootstrap/switches/does_own_process_state:128:14) at process.chdir (node:internal/worker:116:5) at process.chdir (/opt/homebrew/lib/node_modules/@mariozechner/pi-coding-agent/node_modules/graceful-fs/polyfills.js:22:11) at createAgentSessionRuntime (file:///opt/homebrew/lib/node_modules/@mariozechner/pi-coding-agent/dist/core/agent-session-runtime.js:227:17) at async main (file:///opt/homebrew/lib/node_modules/@mariozechner/pi-coding-agent/dist/main.js:442:21) { errno: -2, code: 'ENOENT', syscall: 'chdir', path: '/Users/badlogic/workspaces/pi-session-redactor', dest: '/Users/user/workspaces/pi-mono' }
Node.js v25.2.1
wtf is happening
Pi is restoring the session's original cwd from the JSONL header, then chdiring into it.
What happened:
pi from:
/Users/badlogic/workspaces/pi-session-redactorcwd: "/Users/user/workspaces/pi-mono"SessionManager.open(...) reads the session header and takes header.cwdcreateAgentSessionRuntime(...) calls process.chdir(sessionCwd)/Users/badlogic/workspaces/pi-session-redactor/Users/user/workspaces/pi-monoENOENTSo the key point is this:
--session does not mean "open this file but stay in my current directory".
It means "open this session and restore its original working directory".
Why this is especially likely here:
pi-sessions/redacted/.../Users/user/...Relevant code paths in packages/coding-agent:
src/core/session-manager.ts
SessionManager.open() reads header.cwdsrc/core/agent-session-runtime.ts
createAgentSessionRuntime() does process.chdir(result.services.cwd)So yes, the behavior is real, not your shell being weird.
Practical workarounds:
cwd to a real local path.--fork <session-file> from the directory you want. Forking rewrites the session header to the current cwd.What looks wrong in pi itself:
--session loadsIf you want, I can point you at the exact patch to make pi handle this gracefully.
Node.js v25.2.1 ➜ pi-session-redactor git:(main) ✗ pi --fork --session ./pi-sessions/redacted/2026-01-16T02-23-12-010Z_d7892a86-ae30-4b5d-8bbd-06b3afaba4a0.jsonl No session found matching '--session'
You invoked it wrong.
--fork takes an argument. It is not a boolean flag.
So this:
pi --fork --session ./pi-sessions/redacted/2026-01-16T02-23-12-010Z_d7892a86-ae30-4b5d-8bbd-06b3afaba4a0.jsonlgets parsed as:
--fork argument = --session--sessionNo session found matching '--session'Also, --fork and --session are mutually exclusive.
Use this instead, from the directory you want as the new cwd:
pi --fork ./pi-sessions/redacted/2026-01-16T02-23-12-010Z_d7892a86-ae30-4b5d-8bbd-06b3afaba4a0.jsonlWhat that should do:
/Users/user/workspaces/pi-mono chdirSo the two errors were different:
--session <file>
--fork --session <file>
--session got consumed as the argument to --forkIf pi --fork ./pi-sessions/redacted/...jsonl still fails, paste that output.
it shouldn't hard crash, i think it should show a simple tui that warns that the cwd doesn't exist, and whether the user wants to run in current cwd. but only for interactive mode. in print/json/rpc mode we probably want to error hard, but under our control.
Agreed.
Current behavior is wrong in two ways:
chdir path instead of a controlled pi errorYour proposed split makes sense:
If session header cwd does not exist:
process.chdir() yet<missing-cwd><process.cwd()>?Fail deterministically with a pi-managed error, for example:
Interactive mode can ask the user to resolve ambiguity. Non-interactive modes cannot safely guess, especially RPC.
The fix probably belongs before the runtime does process.chdir():
sessionManager.getCwd()--session currently means "resume this exact session", including its stored cwd semantics.
If interactive mode falls back to current cwd, it should be explicit to the user that this changes path resolution, context discovery, settings, and project-local resources.
So yes, this should be fixed, and your mode split is the right behavior. If you want, I can turn this into a concrete issue description or patch plan.
i want you to implement this
why do we have to modify session manager with overrideCwd?
Because SessionManager.open() is the point where pi turns a session file into runtime state.
Right now that method does this:
header.cwdEverything downstream uses sessionManager.getCwd():
For the fallback behavior, we need:
An override on SessionManager.open() is the smallest place to express that.
Without it, the alternatives are worse:
setCwd() mutator to SessionManageropen() logic somewhere elseSo the override is not about session persistence. It is about creating an in-memory SessionManager from an existing file while substituting the effective cwd.
If you want, I can switch it to a separate factory instead, for example:
SessionManager.open(path, sessionDir?)SessionManager.openWithCwd(path, cwd, sessionDir?)That is a bit clearer than adding a third optional parameter.
i thought we let sessionmanager open (reads header, or uses process.cwd), then main detects cwd doesn't exist, prompts users, if users says yes, we keep session manager as is, but we instantiate services with the current cwd. no?
Not safely with the current architecture.
What you describe would mean:
SessionManager.cwd = stored session cwd from headerservices.cwd = current cwd fallback chosen by userThat sounds nice, but pi currently assumes those are the same thing in a lot of places.
Problems if they diverge:
InteractiveMode uses this.sessionManager.getCwd() for:
/resume local session listingSessionManager.newSession() and branching/forking use this.cwd when writing new headersSo yes, main can detect the missing cwd and prompt the user. But after the user says yes, we still need one coherent effective cwd object graph for that session runtime.
That means one of these has to happen:
Option 2 is architecturally cleaner, but much larger.
Option 1 is why I added the override. It does not rewrite the file. It only says:
So the override is not to change persistence semantics. It is to keep the runtime internally coherent.
That said, I agree the current API shape is ugly. A third optional arg on open() is not ideal.
Better options:
SessionManager.openWithCwd(path, cwd, sessionDir?)SessionManager.open(path, { sessionDir, cwdOverride })That would make the intent explicit.
So the short answer is:
SessionManager on the old cwd while services use a different cwdoverrideCwd parameter on open()If you want, I’ll rework the partial change into SessionManager.openWithCwd(...) instead.
ok, then let's
Implemented.
Files changed:
packages/coding-agent/src/main.tspackages/coding-agent/src/core/agent-session-runtime.tspackages/coding-agent/src/core/session-manager.tspackages/coding-agent/src/modes/interactive/interactive-mode.tspackages/coding-agent/src/cli/missing-session-cwd-prompt.tspackages/coding-agent/src/core/session-cwd.tspackages/coding-agent/test/session-cwd.test.tsBehavior now:
cwd is missing, pi shows a small TUI promptprint, json, and rpc exit with a controlled errorchdir crash/resume and /import:
Implementation detail:
SessionManager.open(..., cwdOverride?) is used to create a coherent runtime with a fallback cwd while keeping the same session fileValidation:
packages/coding-agent/test/session-cwd.test.ts passednpm run check passedIf you want, I can also add a small changelog entry under packages/coding-agent/CHANGELOG.md.
───────────────────────────────────────────────────────────────────────────────────────────────────────
Session cwd not found
Session file:
/Users/badlogic/workspaces/pi-session-redactor/pi-sessions/redacted/2026-01-16T02-23-12-010Z_d7892a86 -ae30-4b5d-8bbd-06b3afaba4a0.jsonl
Stored cwd:
/Users/user/workspaces/pi-mono
That directory does not exist on this machine.
Current cwd:
/Users/badlogic/workspaces/pi-mono
Run the session in the current cwd?
enter continue · escape/ctrl+c cancel
───────────────────────────────────────────────────────────────────────────────────────────────────────
why is there so much fucking whitespace, this shouldn't be bigger than 5-6 lines
you can also use the confirm component
the info should jus tbe "cwd from session file does not exist. continue in current cwd?"
Fixed.
Changes:
Prompt text:
cwd from session file does not exist. Continue in current cwd?Updated files:
packages/coding-agent/src/core/session-cwd.tspackages/coding-agent/src/cli/missing-session-cwd-prompt.tsValidation:
npm run check passedIf you still want, I can replace the startup custom prompt with the shared selector-style confirm UI too. Right now it is compact, but still custom.
can you please output the cwd ...
yes, reuse as many components as we have, and rmove the @packages/coding-agent/src/cli/missing-session-cwd-prompt.ts shit. we can do this with exsiting components
Done.
Changes:
packages/coding-agent/src/cli/missing-session-cwd-prompt.tsExtensionSelectorComponentCurrent text:
cwd from session file does not exist<stored cwd>continue in current cwd<current cwd>Validation:
packages/coding-agent/test/session-cwd.test.ts passednpm run check passedIf you want, I can make the two path lines dim so the first line stands out more.
looks good, commit and push
Committed and pushed.
Commit:
080af6fc fix(coding-agent): handle missing session cwdpi --fork --session ./pi-sessions/redacted/2026-01-16T02-23-12-010Z_d7892a86-ae30-4b5d-8bbd-06b3afaba4a0.jsonlpi --fork ./pi-sessions/redacted/2026-01-16T02-23-12-010Z_d7892a86-ae30-4b5d-8bbd-06b3afaba4a0.jsonl