Skip to content

Commit d3c991c

Browse files
committed
feat: implement project code management for Windows compatibility and enhance debug logger tests
1 parent 39b48e1 commit d3c991c

3 files changed

Lines changed: 64 additions & 26 deletions

File tree

src/session.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ export type {
5757
} from "./common/permissions";
5858

5959
const MAX_SESSION_ENTRIES = 50;
60+
const MAX_PROJECT_CODE_LENGTH = 64;
61+
const PROJECT_CODE_HASH_LENGTH = 16;
6062
const DEFAULT_NEW_PROMPT_API_URL = "https://deepcode.vegamo.cn/api/plugin/new";
6163
const NEW_PROMPT_REPORT_TIMEOUT_MS = 3000;
6264
const DEFAULT_COMPACT_PROMPT_TOKEN_THRESHOLD = 128 * 1024;
@@ -75,6 +77,36 @@ export function getCompactPromptTokenThreshold(model: string): number {
7577
: DEFAULT_COMPACT_PROMPT_TOKEN_THRESHOLD;
7678
}
7779

80+
// Keep project storage paths short enough for Git's internal files on Windows.
81+
export function getProjectCode(projectRoot: string): string {
82+
const legacyCode = getLegacyProjectCode(projectRoot);
83+
if (legacyCode.length <= MAX_PROJECT_CODE_LENGTH) {
84+
return legacyCode;
85+
}
86+
87+
const normalizedRoot = path.resolve(projectRoot);
88+
const hashInput = process.platform === "win32" ? normalizedRoot.toLowerCase() : normalizedRoot;
89+
const hash = crypto.createHash("sha256").update(hashInput).digest("hex").slice(0, PROJECT_CODE_HASH_LENGTH);
90+
const prefixLimit = MAX_PROJECT_CODE_LENGTH - PROJECT_CODE_HASH_LENGTH - 1;
91+
const basename = path.basename(normalizedRoot);
92+
const prefix =
93+
sanitizeProjectCodePart(basename)
94+
.slice(0, prefixLimit)
95+
.replace(/[-.]+$/g, "") || "project";
96+
return `${prefix}-${hash}`;
97+
}
98+
99+
function getLegacyProjectCode(projectRoot: string): string {
100+
return projectRoot.replace(/[\\/]/g, "-").replace(/:/g, "");
101+
}
102+
103+
function sanitizeProjectCodePart(value: string): string {
104+
return value
105+
.replace(/[^A-Za-z0-9._-]/g, "-")
106+
.replace(/-+/g, "-")
107+
.replace(/^[-.]+|[-.]+$/g, "");
108+
}
109+
78110
function isUsageRecord(value: unknown): value is Record<string, unknown> {
79111
return value !== null && typeof value === "object" && !Array.isArray(value);
80112
}
@@ -1726,16 +1758,12 @@ ${skillMd}
17261758
};
17271759
}
17281760

1729-
private getProjectCode(projectRoot: string): string {
1730-
return projectRoot.replace(/[\\/]/g, "-").replace(/:/g, "");
1731-
}
1732-
17331761
private getProjectStorage(): {
17341762
projectCode: string;
17351763
projectDir: string;
17361764
sessionsIndexPath: string;
17371765
} {
1738-
const projectCode = this.getProjectCode(this.projectRoot);
1766+
const projectCode = getProjectCode(this.projectRoot);
17391767
const projectDir = path.join(os.homedir(), ".deepcode", "projects", projectCode);
17401768
const sessionsIndexPath = path.join(projectDir, "sessions-index.json");
17411769
return { projectCode, projectDir, sessionsIndexPath };

src/tests/debug-logger.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ import { getDebugLogPath, logOpenAIChatCompletionDebug } from "../common/debug-l
77

88
test("debug logger appends full entries without rotation", () => {
99
const originalHome = process.env.HOME;
10+
const originalUserProfile = process.env.USERPROFILE;
1011
const home = fs.mkdtempSync(path.join(os.tmpdir(), "deepcode-debug-log-home-"));
1112
process.env.HOME = home;
13+
if (process.platform === "win32") {
14+
process.env.USERPROFILE = home;
15+
}
1216
try {
1317
for (let index = 0; index < 25; index += 1) {
1418
logOpenAIChatCompletionDebug({
@@ -42,5 +46,10 @@ test("debug logger appends full entries without rotation", () => {
4246
} else {
4347
process.env.HOME = originalHome;
4448
}
49+
if (originalUserProfile === undefined) {
50+
delete process.env.USERPROFILE;
51+
} else {
52+
process.env.USERPROFILE = originalUserProfile;
53+
}
4554
}
4655
});

src/tests/session.test.ts

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ import * as os from "os";
66
import * as path from "path";
77
import { GitFileHistory } from "../common/file-history";
88
import { clearSessionState } from "../common/state";
9-
import { type SessionMessage } from "../session";
10-
import { SessionManager } from "../session";
9+
import { getProjectCode, SessionManager, type SessionMessage } from "../session";
1110

1211
const originalFetch = globalThis.fetch;
1312
const originalConsoleWarn = console.warn;
@@ -45,6 +44,21 @@ afterEach(() => {
4544
}
4645
});
4746

47+
test("getProjectCode shortens long project roots for Windows-compatible storage paths", () => {
48+
const shortRoot = "short-project";
49+
assert.equal(getProjectCode(shortRoot), shortRoot.replace(/[\\/]/g, "-").replace(/:/g, ""));
50+
51+
const longRoot = path.join(
52+
os.tmpdir(),
53+
"deepcode-project-code-workspace-with-a-long-name-that-would-create-long-git-internal-paths"
54+
);
55+
const projectCode = getProjectCode(longRoot);
56+
57+
assert.ok(projectCode.length <= 64);
58+
assert.match(projectCode, /^[A-Za-z0-9._-]+$/);
59+
assert.notEqual(projectCode, longRoot.replace(/[\\/]/g, "-").replace(/:/g, ""));
60+
});
61+
4862
test("SessionManager preserves structured system content when building OpenAI messages", () => {
4963
const manager = new SessionManager({
5064
projectRoot: process.cwd(),
@@ -271,7 +285,7 @@ test("SessionManager normalizes legacy sessions without activeTokens to zero", (
271285
const home = createTempDir("deepcode-legacy-active-tokens-home-");
272286
setHomeDir(home);
273287

274-
const projectCode = workspace.replace(/[\\/]/g, "-").replace(/:/g, "");
288+
const projectCode = getProjectCode(workspace);
275289
const projectDir = path.join(home, ".deepcode", "projects", projectCode);
276290
fs.mkdirSync(projectDir, { recursive: true });
277291
fs.writeFileSync(
@@ -324,7 +338,7 @@ test("SessionManager marks skills loaded from existing session messages", async
324338
"utf8"
325339
);
326340

327-
const projectCode = workspace.replace(/[\\/]/g, "-").replace(/:/g, "");
341+
const projectCode = getProjectCode(workspace);
328342
const projectDir = path.join(home, ".deepcode", "projects", projectCode);
329343
fs.mkdirSync(projectDir, { recursive: true });
330344
fs.writeFileSync(
@@ -982,14 +996,7 @@ test("createSession initializes file-history repo and session branch", async (t)
982996

983997
const sessionId = await manager.createSession({ text: "first prompt" });
984998
const userMessage = manager.listSessionMessages(sessionId).find((message) => message.role === "user");
985-
const gitDir = path.join(
986-
home,
987-
".deepcode",
988-
"projects",
989-
workspace.replace(/[\\/]/g, "-").replace(/:/g, ""),
990-
"file-history",
991-
".git"
992-
);
999+
const gitDir = path.join(home, ".deepcode", "projects", getProjectCode(workspace), "file-history", ".git");
9931000

9941001
assert.ok(fs.existsSync(gitDir));
9951002
assert.ok(userMessage?.checkpointHash);
@@ -2850,13 +2857,7 @@ test("SessionManager.deleteSession removes the messages file", () => {
28502857
(manager as any).activateSession = async () => {};
28512858

28522859
const sessionId = createSessionAndMessages(manager, "session-delete-msg", "Test session");
2853-
const messagePath = path.join(
2854-
home,
2855-
".deepcode",
2856-
"projects",
2857-
workspace.replace(/[\\\\/]/g, "-").replace(/:/g, ""),
2858-
`${sessionId}.jsonl`
2859-
);
2860+
const messagePath = path.join(home, ".deepcode", "projects", getProjectCode(workspace), `${sessionId}.jsonl`);
28602861

28612862
// Verify messages file exists
28622863
assert.ok(fs.existsSync(messagePath));
@@ -2962,7 +2963,7 @@ function createFileHistoryCommit(
29622963
sessionId: string,
29632964
files: Record<string, string>
29642965
): string {
2965-
const projectCode = workspace.replace(/[\\/]/g, "-").replace(/:/g, "");
2966+
const projectCode = getProjectCode(workspace);
29662967
const gitDir = path.join(home, ".deepcode", "projects", projectCode, "file-history", ".git");
29672968
const fileHistory = new GitFileHistory(workspace, gitDir);
29682969
fileHistory.ensureSession(sessionId);
@@ -2980,7 +2981,7 @@ function createFileHistoryCommit(
29802981
}
29812982

29822983
function getFileHistoryGitDir(home: string, workspace: string): string {
2983-
const projectCode = workspace.replace(/[\\/]/g, "-").replace(/:/g, "");
2984+
const projectCode = getProjectCode(workspace);
29842985
return path.join(home, ".deepcode", "projects", projectCode, "file-history", ".git");
29852986
}
29862987

0 commit comments

Comments
 (0)