Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions core/util/paths.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as path from "path";
import { getDefaultContinueGlobalDir } from "./paths";

describe("getDefaultContinueGlobalDir", () => {
const HOME = "/home/user";

describe("on Linux", () => {
it("uses ~/.config/continue when no legacy dir exists and XDG_CONFIG_HOME is unset", () => {
const result = getDefaultContinueGlobalDir({
platform: "linux",
homedir: HOME,
xdgConfigHome: undefined,
legacyDirExists: false,
});
expect(result).toBe(path.join(HOME, ".config", "continue"));
});

it("uses $XDG_CONFIG_HOME/continue when XDG_CONFIG_HOME is a valid absolute path", () => {
const result = getDefaultContinueGlobalDir({
platform: "linux",
homedir: HOME,
xdgConfigHome: "/custom/config",
legacyDirExists: false,
});
expect(result).toBe("/custom/config/continue");
});

it("ignores XDG_CONFIG_HOME when it is an empty string", () => {
const result = getDefaultContinueGlobalDir({
platform: "linux",
homedir: HOME,
xdgConfigHome: "",
legacyDirExists: false,
});
expect(result).toBe(path.join(HOME, ".config", "continue"));
});

it("ignores XDG_CONFIG_HOME when it is a relative path", () => {
const result = getDefaultContinueGlobalDir({
platform: "linux",
homedir: HOME,
xdgConfigHome: "relative/path",
legacyDirExists: false,
});
expect(result).toBe(path.join(HOME, ".config", "continue"));
});

it("falls back to ~/.continue when the legacy dir already exists", () => {
const result = getDefaultContinueGlobalDir({
platform: "linux",
homedir: HOME,
xdgConfigHome: "/custom/config",
legacyDirExists: true,
});
expect(result).toBe(path.join(HOME, ".continue"));
});
});

describe("on macOS", () => {
it("always uses ~/.continue", () => {
const result = getDefaultContinueGlobalDir({
platform: "darwin",
homedir: HOME,
xdgConfigHome: "/custom/config",
legacyDirExists: false,
});
expect(result).toBe(path.join(HOME, ".continue"));
});
});

describe("on Windows", () => {
it("always uses ~/.continue", () => {
const result = getDefaultContinueGlobalDir({
platform: "win32",
homedir: "C:\\Users\\user",
xdgConfigHome: undefined,
legacyDirExists: false,
});
expect(result).toBe(path.join("C:\\Users\\user", ".continue"));
});
});
});
53 changes: 50 additions & 3 deletions core/util/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,44 @@ export function setConfigFilePermissions(filePath: string): void {
}
}

/**
* Resolves the default Continue global directory path.
*
* On Linux the XDG Base Directory Specification is followed:
* - $XDG_CONFIG_HOME/continue (when XDG_CONFIG_HOME is set)
* - ~/.config/continue (otherwise)
*
* The legacy ~/.continue location is returned as-is on Linux when it already
* exists so that existing installations are not silently relocated.
*
* On all other platforms ~/.continue is used.
*/
export function getDefaultContinueGlobalDir(opts: {
platform: NodeJS.Platform;
homedir: string;
xdgConfigHome: string | undefined;
legacyDirExists: boolean;
}): string {
const { platform, homedir, xdgConfigHome, legacyDirExists } = opts;
const legacyPath = path.join(homedir, ".continue");

if (platform === "linux") {
if (legacyDirExists) {
return legacyPath;
}
// Only accept XDG_CONFIG_HOME when it is a non-empty absolute path;
// an empty string or a relative path would place the config dir in an
// unexpected location (e.g. the current working directory).
const xdgBase =
xdgConfigHome && path.isAbsolute(xdgConfigHome)
? xdgConfigHome
: path.join(homedir, ".config");
return path.join(xdgBase, "continue");
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
}

return legacyPath;
}

const CONTINUE_GLOBAL_DIR = (() => {
const configPath = process.env.CONTINUE_GLOBAL_DIR;
if (configPath) {
Expand All @@ -32,7 +70,14 @@ const CONTINUE_GLOBAL_DIR = (() => {
? configPath
: path.resolve(process.cwd(), configPath);
}
return path.join(os.homedir(), ".continue");

const homedir = os.homedir();
return getDefaultContinueGlobalDir({
platform: os.platform(),
homedir,
xdgConfigHome: process.env.XDG_CONFIG_HOME,
legacyDirExists: fs.existsSync(path.join(homedir, ".continue")),
});
})();

// export const DEFAULT_CONFIG_TS_CONTENTS = `import { Config } from "./types"\n\nexport function modifyConfig(config: Config): Config {
Expand Down Expand Up @@ -67,10 +112,12 @@ export function getGlobalContinueIgnorePath(): string {
}

export function getContinueGlobalPath(): string {
// This is ~/.continue on mac/linux
// This is ~/.continue on mac/linux (or ~/.config/continue on Linux for new installs)
const continuePath = CONTINUE_GLOBAL_DIR;
if (!fs.existsSync(continuePath)) {
fs.mkdirSync(continuePath);
// Use recursive so intermediate directories (e.g. ~/.config) are created
// when the XDG path is used on Linux.
fs.mkdirSync(continuePath, { recursive: true });
}
return continuePath;
}
Expand Down
Loading