fix: deduplicate config/session lookups in send, hoist sm.list() in batch-spawn
1. Send command: merge resolveTmuxTarget() and resolveAgentForSession() into a single resolveSessionContext() that loads config and calls sm.get() once instead of twice. 2. Batch-spawn: move sm.list() call before the loop and build a Map for O(1) duplicate lookups, avoiding repeated metadata reads + runtime enrichment on every iteration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a5a98cd7aa
commit
09d631f511
|
|
@ -9,41 +9,26 @@ import { getAgentByName } from "../lib/plugins.js";
|
|||
import { getSessionManager } from "../lib/create-session-manager.js";
|
||||
|
||||
/**
|
||||
* Resolve the tmux session name for a user-facing session ID.
|
||||
* Returns the runtimeHandle.id (which is the tmux session name) or falls back
|
||||
* to the session name itself for backwards compatibility with pre-hash sessions.
|
||||
* Resolve session context: tmux target name and Agent plugin.
|
||||
* Loads config and looks up the session once, avoiding duplicate work.
|
||||
*/
|
||||
async function resolveTmuxTarget(sessionName: string): Promise<string> {
|
||||
try {
|
||||
const config = loadConfig();
|
||||
const sm = await getSessionManager(config);
|
||||
const session = await sm.get(sessionName);
|
||||
if (session?.runtimeHandle?.id) {
|
||||
return session.runtimeHandle.id;
|
||||
}
|
||||
} catch {
|
||||
// No config or session not found — fall back to session name as tmux name
|
||||
}
|
||||
return sessionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the Agent plugin for a session.
|
||||
*/
|
||||
async function resolveAgentForSession(sessionName: string): Promise<{ agent: Agent; session: Session | null }> {
|
||||
async function resolveSessionContext(
|
||||
sessionName: string,
|
||||
): Promise<{ tmuxTarget: string; agent: Agent; session: Session | null }> {
|
||||
try {
|
||||
const config = loadConfig();
|
||||
const sm = await getSessionManager(config);
|
||||
const session = await sm.get(sessionName);
|
||||
if (session) {
|
||||
const tmuxTarget = session.runtimeHandle?.id ?? sessionName;
|
||||
const project = config.projects[session.projectId];
|
||||
const agentName = project?.agent ?? config.defaults.agent;
|
||||
return { agent: getAgentByName(agentName), session };
|
||||
return { tmuxTarget, agent: getAgentByName(agentName), session };
|
||||
}
|
||||
} catch {
|
||||
// Fall through to default
|
||||
// No config or session not found — fall back to defaults
|
||||
}
|
||||
return { agent: getAgentByName("claude-code"), session: null };
|
||||
return { tmuxTarget: sessionName, agent: getAgentByName("claude-code"), session: null };
|
||||
}
|
||||
|
||||
function isActive(agent: Agent, terminalOutput: string): boolean {
|
||||
|
|
@ -73,8 +58,8 @@ export function registerSend(program: Command): void {
|
|||
messageParts: string[],
|
||||
opts: { file?: string; wait?: boolean; timeout?: string },
|
||||
) => {
|
||||
// Resolve tmux target name (hash-based or legacy)
|
||||
const tmuxTarget = await resolveTmuxTarget(session);
|
||||
// Resolve session context once: tmux target, agent plugin, session data
|
||||
const { tmuxTarget, agent } = await resolveSessionContext(session);
|
||||
|
||||
const exists = await tmux("has-session", "-t", tmuxTarget);
|
||||
if (exists === null) {
|
||||
|
|
@ -89,8 +74,6 @@ export function registerSend(program: Command): void {
|
|||
process.exit(1);
|
||||
}
|
||||
|
||||
const { agent } = await resolveAgentForSession(session);
|
||||
|
||||
const parsedTimeout = parseInt(opts.timeout || "600", 10);
|
||||
const timeoutMs = (isNaN(parsedTimeout) || parsedTimeout <= 0 ? 600 : parsedTimeout) * 1000;
|
||||
|
||||
|
|
|
|||
|
|
@ -111,6 +111,14 @@ export function registerBatchSpawn(program: Command): void {
|
|||
const failed: Array<{ issue: string; error: string }> = [];
|
||||
const spawnedIssues = new Set<string>();
|
||||
|
||||
// Load existing sessions once before the loop to avoid repeated reads + enrichment
|
||||
const existingSessions = await sm.list(projectId);
|
||||
const existingIssueMap = new Map(
|
||||
existingSessions
|
||||
.filter((s) => s.issueId)
|
||||
.map((s) => [s.issueId!.toLowerCase(), s.id]),
|
||||
);
|
||||
|
||||
for (const issue of issues) {
|
||||
// Duplicate detection — check both existing sessions and same-run duplicates
|
||||
if (spawnedIssues.has(issue.toLowerCase())) {
|
||||
|
|
@ -119,14 +127,11 @@ export function registerBatchSpawn(program: Command): void {
|
|||
continue;
|
||||
}
|
||||
|
||||
// Check existing sessions via session manager (works with hash-based naming)
|
||||
const existingSessions = await sm.list(projectId);
|
||||
const existingSession = existingSessions.find(
|
||||
(s) => s.issueId?.toLowerCase() === issue.toLowerCase(),
|
||||
);
|
||||
if (existingSession) {
|
||||
console.log(chalk.yellow(` Skip ${issue} — already has session: ${existingSession.id}`));
|
||||
skipped.push({ issue, existing: existingSession.id });
|
||||
// Check existing sessions (pre-loaded before loop)
|
||||
const existingSessionId = existingIssueMap.get(issue.toLowerCase());
|
||||
if (existingSessionId) {
|
||||
console.log(chalk.yellow(` Skip ${issue} — already has session: ${existingSessionId}`));
|
||||
skipped.push({ issue, existing: existingSessionId });
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue