Create projectless chat sessions under a managed, date-scoped Chats directory and clean abandoned or deleted session folders. Add Chats to sidebar state, startup cache, shared context, and Electron Mini Chat while keeping VS Code project-only. Resolve managed chat directories to one server-side memory owner and document the runtime contracts.
66 lines
2.3 KiB
JavaScript
66 lines
2.3 KiB
JavaScript
/**
|
|
* Which project's memory a session directory belongs to.
|
|
*
|
|
* A session often runs in a worktree, whose path is not the project's path.
|
|
* Keying memory by the session directory filed a worktree's memories under a
|
|
* project the panel never reads, so the agent stored them and the user never
|
|
* saw them. Every worktree of a repository shares one project memory, which is
|
|
* also what the user means by "this project".
|
|
*
|
|
* A directory that is itself a configured project is taken as-is; anything else
|
|
* resolves to its primary worktree. The configured check comes first because a
|
|
* user may register a worktree as a project in its own right, and that choice
|
|
* has to win over the git topology.
|
|
*/
|
|
|
|
import path from 'node:path';
|
|
|
|
import { createProjectIdFromPath } from '../projects/project-id.js';
|
|
|
|
const normalize = (value) => {
|
|
if (typeof value !== 'string') return '';
|
|
const trimmed = value.trim();
|
|
return trimmed ? path.resolve(trimmed) : '';
|
|
};
|
|
|
|
export const createMemoryProjectResolver = (dependencies) => {
|
|
const { listProjectPaths, resolvePrimaryWorktreeRoot, managedProjectRoots = [] } = dependencies;
|
|
const managedRoots = managedProjectRoots.map(normalize).filter(Boolean);
|
|
|
|
return async (directory) => {
|
|
const resolved = normalize(directory);
|
|
if (!resolved) {
|
|
return '';
|
|
}
|
|
|
|
const managedRoot = managedRoots.find((root) => {
|
|
const relative = path.relative(root, resolved);
|
|
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
});
|
|
if (managedRoot) {
|
|
return createProjectIdFromPath(managedRoot);
|
|
}
|
|
|
|
let configured = [];
|
|
try {
|
|
configured = ((await listProjectPaths()) || []).map(normalize).filter(Boolean);
|
|
} catch {
|
|
// An unreadable project list must not lose the memory: the git-derived
|
|
// root below still converges every worktree of the repository on one
|
|
// store rather than scattering one per checkout.
|
|
}
|
|
if (configured.includes(resolved)) {
|
|
return createProjectIdFromPath(resolved);
|
|
}
|
|
|
|
let primaryRoot = '';
|
|
try {
|
|
primaryRoot = normalize((await resolvePrimaryWorktreeRoot(resolved))?.root);
|
|
} catch {
|
|
// Not a git checkout, or git is unavailable.
|
|
}
|
|
|
|
return createProjectIdFromPath(primaryRoot || resolved);
|
|
};
|
|
};
|