fix: fold session manager blockers into one candidate

This commit is contained in:
Affaan Mustafa
2026-03-24 23:08:27 -04:00
parent 7726c25e46
commit 1d0aa5ac2a
30 changed files with 1126 additions and 288 deletions

View File

@@ -6,6 +6,7 @@
const fs = require('fs');
const path = require('path');
const os = require('os');
const crypto = require('crypto');
const { execSync, spawnSync } = require('child_process');
// Platform detection
@@ -31,9 +32,23 @@ function getClaudeDir() {
* Get the sessions directory
*/
function getSessionsDir() {
return path.join(getClaudeDir(), 'session-data');
}
/**
* Get the legacy sessions directory used by older ECC installs
*/
function getLegacySessionsDir() {
return path.join(getClaudeDir(), 'sessions');
}
/**
* Get all session directories to search, in canonical-first order
*/
function getSessionSearchDirs() {
return Array.from(new Set([getSessionsDir(), getLegacySessionsDir()]));
}
/**
* Get the learned skills directory
*/
@@ -107,16 +122,50 @@ function getProjectName() {
return path.basename(process.cwd()) || null;
}
/**
* Sanitize a string for use as a session filename segment.
* Replaces invalid characters with hyphens, collapses runs, strips
* leading/trailing hyphens, and removes leading dots so hidden-dir names
* like ".claude" map cleanly to "claude".
*
* Pure non-ASCII inputs get a stable 8-char hash so distinct names do not
* collapse to the same fallback session id. Mixed-script inputs retain their
* ASCII part and gain a short hash suffix for disambiguation.
*/
function sanitizeSessionId(raw) {
if (!raw || typeof raw !== 'string') return null;
const hasNonAscii = /[^\x00-\x7F]/.test(raw);
const normalized = raw.replace(/^\.+/, '');
const sanitized = normalized
.replace(/[^a-zA-Z0-9_-]/g, '-')
.replace(/-{2,}/g, '-')
.replace(/^-+|-+$/g, '');
if (sanitized.length > 0) {
if (!hasNonAscii) return sanitized;
const suffix = crypto.createHash('sha256').update(normalized).digest('hex').slice(0, 6);
return `${sanitized}-${suffix}`;
}
const meaningful = normalized.replace(/[\s\p{P}]/gu, '');
if (meaningful.length === 0) return null;
return crypto.createHash('sha256').update(normalized).digest('hex').slice(0, 8);
}
/**
* Get short session ID from CLAUDE_SESSION_ID environment variable
* Returns last 8 characters, falls back to project name then 'default'
* Returns last 8 characters, falls back to a sanitized project name then 'default'.
*/
function getSessionIdShort(fallback = 'default') {
const sessionId = process.env.CLAUDE_SESSION_ID;
if (sessionId && sessionId.length > 0) {
return sessionId.slice(-8);
const sanitized = sanitizeSessionId(sessionId.slice(-8));
if (sanitized) return sanitized;
}
return getProjectName() || fallback;
return sanitizeSessionId(getProjectName()) || sanitizeSessionId(fallback) || 'default';
}
/**
@@ -525,6 +574,8 @@ module.exports = {
getHomeDir,
getClaudeDir,
getSessionsDir,
getLegacySessionsDir,
getSessionSearchDirs,
getLearnedSkillsDir,
getTempDir,
ensureDir,
@@ -535,6 +586,7 @@ module.exports = {
getDateTimeString,
// Session/Project
sanitizeSessionId,
getSessionIdShort,
getGitRepoName,
getProjectName,