Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions src/tmux-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,49 @@ function setOpenCodeEnvVars(muxName: string): void {
}
}

/**
* Stamp a per-session git identity onto a tmux session via setenv, derived from
* the session's display name. Every commit from this session is then attributable
* to the session — and it overrides any repo-local user.name, because the
* GIT_AUTHOR and GIT_COMMITTER env vars beat all git config files. This is the
* only mechanism that distinguishes sessions sharing one working dir.
*
* The session env is inherited by panes and persists across respawns, so a
* rename re-runs this and the next (re)spawn — resume, clear, pane respawn —
* commits under the new name. A currently-running process keeps its spawn-time
* identity until it respawns.
*/
function setGitIdentityEnvVars(muxName: string, name: string | undefined): void {
if (IS_TEST_MODE) return;
const author = (name ?? '').trim();
if (!author) return;
// Slug the name into a stable email local-part (e.g. "w1-axon" -> w1-axon@codeman.local)
const local = author
.toLowerCase()
.replace(/[^a-z0-9._-]+/g, '-')
.replace(/^-+|-+$/g, '');
const email = `${local || 'session'}@codeman.local`;
const identity: Record<string, string> = {
GIT_AUTHOR_NAME: author,
GIT_AUTHOR_EMAIL: email,
GIT_COMMITTER_NAME: author,
GIT_COMMITTER_EMAIL: email,
};
for (const [key, val] of Object.entries(identity)) {
// Shell-escape: wrap in single quotes, escape any inner single quotes
const escaped = val.replace(/'/g, "'\\''");
try {
execSync(`tmux setenv -t '${muxName}' ${key} '${escaped}'`, {
encoding: 'utf8',
timeout: EXEC_TIMEOUT_MS,
stdio: ['pipe', 'pipe', 'pipe'],
});
} catch {
/* Non-critical — git identity is best-effort */
}
}
}

/**
* Set OPENCODE_CONFIG_CONTENT on a tmux session via setenv.
* Uses tmux setenv to avoid shell metacharacter injection from user-supplied JSON.
Expand Down Expand Up @@ -494,6 +537,10 @@ export class TmuxManager extends EventEmitter implements TerminalMultiplexer {
setOpenCodeConfigContent(muxName, openCodeConfig);
}

// Per-session git identity so commits are attributable to this session
// (and override any repo-local user.name). Inherited by the pane below.
setGitIdentityEnvVars(muxName, name);

// Replace the shell with the actual command (no echo in terminal)
execSync(`tmux respawn-pane -k -t "${muxName}" bash -c ${JSON.stringify(fullCmd)}`, {
timeout: EXEC_TIMEOUT_MS,
Expand Down Expand Up @@ -877,6 +924,9 @@ export class TmuxManager extends EventEmitter implements TerminalMultiplexer {
}
session.name = name;
this.saveSessions();
// Update the live session's git identity so the next (re)spawn commits
// under the new name. The running process keeps its identity until respawn.
setGitIdentityEnvVars(session.muxName, name);
return true;
}

Expand Down