Repository navigation
Expand file tree
/
Copy pathencode-project-path.js
More file actions
62 lines (53 loc) · 2.64 KB
/
Copy pathencode-project-path.js
File metadata and controls
62 lines (53 loc) · 2.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
const path = require('path');
// Mirror Claude CLI's project-folder naming so Switchboard-created folders
// match the ones the CLI writes for the same project path.
// Reverse-engineered from claude CLI 2.1.126.
const ENCODED_NAME_MAX = 200;
function sanitizeProjectPath(projectPath) {
return projectPath.replace(/[^a-zA-Z0-9]/g, '-');
}
function encodeProjectPath(projectPath) {
const sanitized = sanitizeProjectPath(projectPath);
if (sanitized.length <= ENCODED_NAME_MAX) return sanitized;
let h = 0;
for (let i = 0; i < projectPath.length; i++) {
h = (h << 5) - h + projectPath.charCodeAt(i) | 0;
}
return sanitized.slice(0, ENCODED_NAME_MAX) + '-' + Math.abs(h).toString(36);
}
// see .ai/contexts/session-cache.md ("A transcript moved into a worktree folder")
function encodedFolderMayExtend(folderName, pathPrefix) {
const prefix = sanitizeProjectPath(pathPrefix);
if (folderName.length <= ENCODED_NAME_MAX) {
return folderName.length > prefix.length && folderName.startsWith(prefix);
}
return folderName.startsWith(prefix.slice(0, ENCODED_NAME_MAX));
}
let remappedProjectReader = () => null;
function setRemappedProjectReader(reader) {
remappedProjectReader = typeof reader === 'function' ? reader : () => null;
}
// see .ai/contexts/session-cache.md ("Transcript cwd trust")
function verifiedTranscriptCwd(cwd, folderName) {
if (typeof cwd !== 'string' || !path.isAbsolute(cwd)) return null;
const resolved = path.resolve(cwd);
if (encodeProjectPath(resolved) === folderName) return resolved;
let remapped = null;
try { remapped = remappedProjectReader(folderName); } catch {}
if (typeof remapped === 'string' && path.isAbsolute(remapped) && path.resolve(remapped) === resolved) return resolved;
return null;
}
// Best-effort inverse of encodeProjectPath, for DISPLAY ONLY while the
// initial scan is still running and cache_meta has no real projectPath for a
// folder yet. The encoding is lossy (every non-alphanumeric became '-'), so
// this cannot distinguish a path separator from a dash, dot or underscore in
// the original path — '-home-jb-my-repo' decodes to '/home/jb/my/repo' even
// if the project was really /home/jb/my-repo. That approximation is
// acceptable: the sidebar entry is corrected as soon as the scan worker
// indexes the folder (per-folder projects-changed refresh). Callers must
// NEVER persist this guess (e.g. into cache_meta) — it would shadow the real
// derived path.
function decodeProjectFolderBestEffort(folder) {
return folder.replace(/-/g, '/');
}
module.exports = { encodeProjectPath, encodedFolderMayExtend, decodeProjectFolderBestEffort, verifiedTranscriptCwd, setRemappedProjectReader };