mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-07 22:20:18 +00:00
fix(memory): use async fs APIs to satisfy maintainability gate
This commit is contained in:
@@ -42,7 +42,7 @@ export async function handleCreate(ctx: CommandContext, args: string[]): Promise
|
|||||||
try {
|
try {
|
||||||
// Create instance directory
|
// Create instance directory
|
||||||
console.log(info(`Creating profile: ${profileName}`));
|
console.log(info(`Creating profile: ${profileName}`));
|
||||||
const instancePath = ctx.instanceMgr.ensureInstance(profileName);
|
const instancePath = await ctx.instanceMgr.ensureInstance(profileName);
|
||||||
|
|
||||||
// Create/update profile entry based on config mode
|
// Create/update profile entry based on config mode
|
||||||
if (isUnifiedMode()) {
|
if (isUnifiedMode()) {
|
||||||
|
|||||||
+1
-1
@@ -884,7 +884,7 @@ async function main(): Promise<void> {
|
|||||||
const instanceMgr = new InstanceManager();
|
const instanceMgr = new InstanceManager();
|
||||||
|
|
||||||
// Ensure instance exists (lazy init if needed)
|
// Ensure instance exists (lazy init if needed)
|
||||||
const instancePath = instanceMgr.ensureInstance(profileInfo.name);
|
const instancePath = await instanceMgr.ensureInstance(profileInfo.name);
|
||||||
|
|
||||||
// Update last_used timestamp (check unified config first, fallback to legacy)
|
// Update last_used timestamp (check unified config first, fallback to legacy)
|
||||||
if (registry.hasAccountUnified(profileInfo.name)) {
|
if (registry.hasAccountUnified(profileInfo.name)) {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class InstanceManager {
|
|||||||
/**
|
/**
|
||||||
* Ensure instance exists for profile (lazy init only)
|
* Ensure instance exists for profile (lazy init only)
|
||||||
*/
|
*/
|
||||||
ensureInstance(profileName: string): string {
|
async ensureInstance(profileName: string): Promise<string> {
|
||||||
const instancePath = this.getInstancePath(profileName);
|
const instancePath = this.getInstancePath(profileName);
|
||||||
|
|
||||||
// Lazy initialization
|
// Lazy initialization
|
||||||
@@ -38,7 +38,7 @@ class InstanceManager {
|
|||||||
this.validateInstance(instancePath);
|
this.validateInstance(instancePath);
|
||||||
|
|
||||||
// Keep project memory shared across instances.
|
// Keep project memory shared across instances.
|
||||||
this.sharedManager.syncProjectMemories(instancePath);
|
await this.sharedManager.syncProjectMemories(instancePath);
|
||||||
|
|
||||||
return instancePath;
|
return instancePath;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,25 +210,25 @@ class SharedManager {
|
|||||||
* Shared layout (canonical):
|
* Shared layout (canonical):
|
||||||
* ~/.ccs/shared/memory/<project>/
|
* ~/.ccs/shared/memory/<project>/
|
||||||
*/
|
*/
|
||||||
syncProjectMemories(instancePath: string): void {
|
async syncProjectMemories(instancePath: string): Promise<void> {
|
||||||
const projectsDir = path.join(instancePath, 'projects');
|
const projectsDir = path.join(instancePath, 'projects');
|
||||||
if (!fs.existsSync(projectsDir)) {
|
if (!(await this.pathExists(projectsDir))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(this.sharedDir)) {
|
await this.ensureDirectory(this.sharedDir);
|
||||||
fs.mkdirSync(this.sharedDir, { recursive: true, mode: 0o700 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const sharedMemoryRoot = path.join(this.sharedDir, 'memory');
|
const sharedMemoryRoot = path.join(this.sharedDir, 'memory');
|
||||||
if (!fs.existsSync(sharedMemoryRoot)) {
|
await this.ensureDirectory(sharedMemoryRoot);
|
||||||
fs.mkdirSync(sharedMemoryRoot, { recursive: true, mode: 0o700 });
|
|
||||||
|
let projectEntries: fs.Dirent[] = [];
|
||||||
|
try {
|
||||||
|
projectEntries = await fs.promises.readdir(projectsDir, { withFileTypes: true });
|
||||||
|
} catch (_err) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const projects = fs.readdirSync(projectsDir, { withFileTypes: true }).filter((entry) => {
|
const projects = projectEntries.filter((entry) => entry.isDirectory());
|
||||||
return entry.isDirectory();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (projects.length === 0) {
|
if (projects.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -243,22 +243,21 @@ class SharedManager {
|
|||||||
const projectMemoryPath = path.join(projectDir, 'memory');
|
const projectMemoryPath = path.join(projectDir, 'memory');
|
||||||
const sharedProjectMemoryPath = path.join(sharedMemoryRoot, project.name);
|
const sharedProjectMemoryPath = path.join(sharedMemoryRoot, project.name);
|
||||||
|
|
||||||
if (!fs.existsSync(projectMemoryPath)) {
|
const projectMemoryStats = await this.getLstat(projectMemoryPath);
|
||||||
if (this.ensureProjectMemoryLink(projectMemoryPath, sharedProjectMemoryPath)) {
|
if (!projectMemoryStats) {
|
||||||
|
if (await this.ensureProjectMemoryLink(projectMemoryPath, sharedProjectMemoryPath)) {
|
||||||
linked++;
|
linked++;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const projectMemoryStats = fs.lstatSync(projectMemoryPath);
|
|
||||||
|
|
||||||
if (projectMemoryStats.isSymbolicLink()) {
|
if (projectMemoryStats.isSymbolicLink()) {
|
||||||
if (this.isSymlinkTarget(projectMemoryPath, sharedProjectMemoryPath)) {
|
if (await this.isSymlinkTarget(projectMemoryPath, sharedProjectMemoryPath)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.unlinkSync(projectMemoryPath);
|
await fs.promises.unlink(projectMemoryPath);
|
||||||
if (this.ensureProjectMemoryLink(projectMemoryPath, sharedProjectMemoryPath)) {
|
if (await this.ensureProjectMemoryLink(projectMemoryPath, sharedProjectMemoryPath)) {
|
||||||
linked++;
|
linked++;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -268,19 +267,19 @@ class SharedManager {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(sharedProjectMemoryPath)) {
|
if (!(await this.pathExists(sharedProjectMemoryPath))) {
|
||||||
this.moveDirectory(projectMemoryPath, sharedProjectMemoryPath);
|
await this.moveDirectory(projectMemoryPath, sharedProjectMemoryPath);
|
||||||
migrated++;
|
migrated++;
|
||||||
} else {
|
} else {
|
||||||
merged += this.mergeDirectoryWithConflictCopies(
|
merged += await this.mergeDirectoryWithConflictCopies(
|
||||||
projectMemoryPath,
|
projectMemoryPath,
|
||||||
sharedProjectMemoryPath,
|
sharedProjectMemoryPath,
|
||||||
instanceName
|
instanceName
|
||||||
);
|
);
|
||||||
fs.rmSync(projectMemoryPath, { recursive: true, force: true });
|
await fs.promises.rm(projectMemoryPath, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.ensureProjectMemoryLink(projectMemoryPath, sharedProjectMemoryPath)) {
|
if (await this.ensureProjectMemoryLink(projectMemoryPath, sharedProjectMemoryPath)) {
|
||||||
linked++;
|
linked++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -517,21 +516,19 @@ class SharedManager {
|
|||||||
* Ensure memory path is linked to shared memory root.
|
* Ensure memory path is linked to shared memory root.
|
||||||
* Returns true when a link/copy was created or updated.
|
* Returns true when a link/copy was created or updated.
|
||||||
*/
|
*/
|
||||||
private ensureProjectMemoryLink(linkPath: string, targetPath: string): boolean {
|
private async ensureProjectMemoryLink(linkPath: string, targetPath: string): Promise<boolean> {
|
||||||
if (!fs.existsSync(targetPath)) {
|
await this.ensureDirectory(targetPath);
|
||||||
fs.mkdirSync(targetPath, { recursive: true, mode: 0o700 });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fs.existsSync(linkPath)) {
|
const linkStats = await this.getLstat(linkPath);
|
||||||
const stats = fs.lstatSync(linkPath);
|
if (linkStats) {
|
||||||
if (stats.isSymbolicLink() && this.isSymlinkTarget(linkPath, targetPath)) {
|
if (linkStats.isSymbolicLink() && (await this.isSymlinkTarget(linkPath, targetPath))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stats.isDirectory()) {
|
if (linkStats.isDirectory()) {
|
||||||
fs.rmSync(linkPath, { recursive: true, force: true });
|
await fs.promises.rm(linkPath, { recursive: true, force: true });
|
||||||
} else {
|
} else {
|
||||||
fs.unlinkSync(linkPath);
|
await fs.promises.unlink(linkPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,7 +536,7 @@ class SharedManager {
|
|||||||
const linkTarget = process.platform === 'win32' ? path.resolve(targetPath) : targetPath;
|
const linkTarget = process.platform === 'win32' ? path.resolve(targetPath) : targetPath;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fs.symlinkSync(linkTarget, linkPath, symlinkType);
|
await fs.promises.symlink(linkTarget, linkPath, symlinkType);
|
||||||
return true;
|
return true;
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
@@ -556,14 +553,14 @@ class SharedManager {
|
|||||||
/**
|
/**
|
||||||
* Check whether symlink points to expected target.
|
* Check whether symlink points to expected target.
|
||||||
*/
|
*/
|
||||||
private isSymlinkTarget(linkPath: string, expectedTarget: string): boolean {
|
private async isSymlinkTarget(linkPath: string, expectedTarget: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const stats = fs.lstatSync(linkPath);
|
const stats = await fs.promises.lstat(linkPath);
|
||||||
if (!stats.isSymbolicLink()) {
|
if (!stats.isSymbolicLink()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentTarget = fs.readlinkSync(linkPath);
|
const currentTarget = await fs.promises.readlink(linkPath);
|
||||||
const resolvedCurrentTarget = path.resolve(path.dirname(linkPath), currentTarget);
|
const resolvedCurrentTarget = path.resolve(path.dirname(linkPath), currentTarget);
|
||||||
const resolvedExpectedTarget = path.resolve(expectedTarget);
|
const resolvedExpectedTarget = path.resolve(expectedTarget);
|
||||||
return resolvedCurrentTarget === resolvedExpectedTarget;
|
return resolvedCurrentTarget === resolvedExpectedTarget;
|
||||||
@@ -575,17 +572,17 @@ class SharedManager {
|
|||||||
/**
|
/**
|
||||||
* Move directory, with cross-device fallback.
|
* Move directory, with cross-device fallback.
|
||||||
*/
|
*/
|
||||||
private moveDirectory(src: string, dest: string): void {
|
private async moveDirectory(src: string, dest: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
fs.renameSync(src, dest);
|
await fs.promises.rename(src, dest);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const error = err as NodeJS.ErrnoException;
|
const error = err as NodeJS.ErrnoException;
|
||||||
if (error.code !== 'EXDEV') {
|
if (error.code !== 'EXDEV') {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.cpSync(src, dest, { recursive: true });
|
await fs.promises.cp(src, dest, { recursive: true });
|
||||||
fs.rmSync(src, { recursive: true, force: true });
|
await fs.promises.rm(src, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,39 +590,41 @@ class SharedManager {
|
|||||||
* Merge source into target. On file conflicts, keep target and copy source
|
* Merge source into target. On file conflicts, keep target and copy source
|
||||||
* as "<name>.migrated-from-<instance>[-N]" to avoid data loss.
|
* as "<name>.migrated-from-<instance>[-N]" to avoid data loss.
|
||||||
*/
|
*/
|
||||||
private mergeDirectoryWithConflictCopies(
|
private async mergeDirectoryWithConflictCopies(
|
||||||
sourceDir: string,
|
sourceDir: string,
|
||||||
targetDir: string,
|
targetDir: string,
|
||||||
instanceName: string
|
instanceName: string
|
||||||
): number {
|
): Promise<number> {
|
||||||
if (!fs.existsSync(targetDir)) {
|
await this.ensureDirectory(targetDir);
|
||||||
fs.mkdirSync(targetDir, { recursive: true, mode: 0o700 });
|
|
||||||
}
|
|
||||||
|
|
||||||
let conflicts = 0;
|
let conflicts = 0;
|
||||||
const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
|
const entries = await fs.promises.readdir(sourceDir, { withFileTypes: true });
|
||||||
|
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
const sourcePath = path.join(sourceDir, entry.name);
|
const sourcePath = path.join(sourceDir, entry.name);
|
||||||
const targetPath = path.join(targetDir, entry.name);
|
const targetPath = path.join(targetDir, entry.name);
|
||||||
|
|
||||||
if (entry.isDirectory()) {
|
if (entry.isDirectory()) {
|
||||||
conflicts += this.mergeDirectoryWithConflictCopies(sourcePath, targetPath, instanceName);
|
conflicts += await this.mergeDirectoryWithConflictCopies(
|
||||||
|
sourcePath,
|
||||||
|
targetPath,
|
||||||
|
instanceName
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.isFile()) {
|
if (entry.isFile()) {
|
||||||
if (!fs.existsSync(targetPath)) {
|
if (!(await this.pathExists(targetPath))) {
|
||||||
fs.copyFileSync(sourcePath, targetPath);
|
await fs.promises.copyFile(sourcePath, targetPath);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.fileContentsEqual(sourcePath, targetPath)) {
|
if (await this.fileContentsEqual(sourcePath, targetPath)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const conflictPath = this.getConflictCopyPath(targetPath, instanceName);
|
const conflictPath = await this.getConflictCopyPath(targetPath, instanceName);
|
||||||
fs.copyFileSync(sourcePath, conflictPath);
|
await fs.promises.copyFile(sourcePath, conflictPath);
|
||||||
conflicts++;
|
conflicts++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -636,16 +635,17 @@ class SharedManager {
|
|||||||
/**
|
/**
|
||||||
* Compare two files byte-for-byte.
|
* Compare two files byte-for-byte.
|
||||||
*/
|
*/
|
||||||
private fileContentsEqual(fileA: string, fileB: string): boolean {
|
private async fileContentsEqual(fileA: string, fileB: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const statA = fs.statSync(fileA);
|
const [statA, statB] = await Promise.all([fs.promises.stat(fileA), fs.promises.stat(fileB)]);
|
||||||
const statB = fs.statSync(fileB);
|
|
||||||
if (statA.size !== statB.size) {
|
if (statA.size !== statB.size) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const contentA = fs.readFileSync(fileA);
|
const [contentA, contentB] = await Promise.all([
|
||||||
const contentB = fs.readFileSync(fileB);
|
fs.promises.readFile(fileA),
|
||||||
|
fs.promises.readFile(fileB),
|
||||||
|
]);
|
||||||
return contentA.equals(contentB);
|
return contentA.equals(contentB);
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
return false;
|
return false;
|
||||||
@@ -655,13 +655,16 @@ class SharedManager {
|
|||||||
/**
|
/**
|
||||||
* Build a non-destructive conflict copy path.
|
* Build a non-destructive conflict copy path.
|
||||||
*/
|
*/
|
||||||
private getConflictCopyPath(existingTargetPath: string, instanceName: string): string {
|
private async getConflictCopyPath(
|
||||||
|
existingTargetPath: string,
|
||||||
|
instanceName: string
|
||||||
|
): Promise<string> {
|
||||||
const safeInstanceName = instanceName.replace(/[^a-zA-Z0-9_-]/g, '-').toLowerCase();
|
const safeInstanceName = instanceName.replace(/[^a-zA-Z0-9_-]/g, '-').toLowerCase();
|
||||||
const baseSuffix = `.migrated-from-${safeInstanceName}`;
|
const baseSuffix = `.migrated-from-${safeInstanceName}`;
|
||||||
|
|
||||||
let candidate = `${existingTargetPath}${baseSuffix}`;
|
let candidate = `${existingTargetPath}${baseSuffix}`;
|
||||||
let sequence = 1;
|
let sequence = 1;
|
||||||
while (fs.existsSync(candidate)) {
|
while (await this.pathExists(candidate)) {
|
||||||
candidate = `${existingTargetPath}${baseSuffix}-${sequence}`;
|
candidate = `${existingTargetPath}${baseSuffix}-${sequence}`;
|
||||||
sequence++;
|
sequence++;
|
||||||
}
|
}
|
||||||
@@ -669,6 +672,30 @@ class SharedManager {
|
|||||||
return candidate;
|
return candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async pathExists(targetPath: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await fs.promises.access(targetPath);
|
||||||
|
return true;
|
||||||
|
} catch (_err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async ensureDirectory(targetPath: string): Promise<void> {
|
||||||
|
await fs.promises.mkdir(targetPath, { recursive: true, mode: 0o700 });
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getLstat(targetPath: string): Promise<fs.Stats | null> {
|
||||||
|
try {
|
||||||
|
return await fs.promises.lstat(targetPath);
|
||||||
|
} catch (err) {
|
||||||
|
if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy directory as fallback (Windows without Developer Mode)
|
* Copy directory as fallback (Windows without Developer Mode)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ describe('SharedManager project memory sync', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('migrates existing project memory and replaces it with a shared symlink', () => {
|
it('migrates existing project memory and replaces it with a shared symlink', async () => {
|
||||||
const ccsDir = getTestCcsDir();
|
const ccsDir = getTestCcsDir();
|
||||||
const instancePath = path.join(ccsDir, 'instances', 'work');
|
const instancePath = path.join(ccsDir, 'instances', 'work');
|
||||||
const projectName = '-tmp-my-project';
|
const projectName = '-tmp-my-project';
|
||||||
@@ -54,7 +54,7 @@ describe('SharedManager project memory sync', () => {
|
|||||||
fs.writeFileSync(path.join(projectMemoryPath, 'MEMORY.md'), 'instance knowledge', 'utf8');
|
fs.writeFileSync(path.join(projectMemoryPath, 'MEMORY.md'), 'instance knowledge', 'utf8');
|
||||||
|
|
||||||
const manager = new SharedManager();
|
const manager = new SharedManager();
|
||||||
manager.syncProjectMemories(instancePath);
|
await manager.syncProjectMemories(instancePath);
|
||||||
|
|
||||||
const sharedMemoryFile = path.join(ccsDir, 'shared', 'memory', projectName, 'MEMORY.md');
|
const sharedMemoryFile = path.join(ccsDir, 'shared', 'memory', projectName, 'MEMORY.md');
|
||||||
expect(fs.existsSync(sharedMemoryFile)).toBe(true);
|
expect(fs.existsSync(sharedMemoryFile)).toBe(true);
|
||||||
@@ -67,7 +67,7 @@ describe('SharedManager project memory sync', () => {
|
|||||||
expect(resolvedTarget).toBe(path.join(ccsDir, 'shared', 'memory', projectName));
|
expect(resolvedTarget).toBe(path.join(ccsDir, 'shared', 'memory', projectName));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('preserves canonical memory and writes conflict copy when contents differ', () => {
|
it('preserves canonical memory and writes conflict copy when contents differ', async () => {
|
||||||
const ccsDir = getTestCcsDir();
|
const ccsDir = getTestCcsDir();
|
||||||
const instancePath = path.join(ccsDir, 'instances', 'work');
|
const instancePath = path.join(ccsDir, 'instances', 'work');
|
||||||
const projectName = '-tmp-shared-project';
|
const projectName = '-tmp-shared-project';
|
||||||
@@ -80,7 +80,7 @@ describe('SharedManager project memory sync', () => {
|
|||||||
fs.writeFileSync(path.join(sharedProjectMemoryPath, 'MEMORY.md'), 'shared memory', 'utf8');
|
fs.writeFileSync(path.join(sharedProjectMemoryPath, 'MEMORY.md'), 'shared memory', 'utf8');
|
||||||
|
|
||||||
const manager = new SharedManager();
|
const manager = new SharedManager();
|
||||||
manager.syncProjectMemories(instancePath);
|
await manager.syncProjectMemories(instancePath);
|
||||||
|
|
||||||
const canonicalFile = path.join(sharedProjectMemoryPath, 'MEMORY.md');
|
const canonicalFile = path.join(sharedProjectMemoryPath, 'MEMORY.md');
|
||||||
expect(fs.readFileSync(canonicalFile, 'utf8')).toBe('shared memory');
|
expect(fs.readFileSync(canonicalFile, 'utf8')).toBe('shared memory');
|
||||||
@@ -93,7 +93,7 @@ describe('SharedManager project memory sync', () => {
|
|||||||
expect(linkStats.isSymbolicLink()).toBe(true);
|
expect(linkStats.isSymbolicLink()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates shared memory link for projects that do not have memory directory yet', () => {
|
it('creates shared memory link for projects that do not have memory directory yet', async () => {
|
||||||
const ccsDir = getTestCcsDir();
|
const ccsDir = getTestCcsDir();
|
||||||
const instancePath = path.join(ccsDir, 'instances', 'work');
|
const instancePath = path.join(ccsDir, 'instances', 'work');
|
||||||
const projectName = '-tmp-new-project';
|
const projectName = '-tmp-new-project';
|
||||||
@@ -102,7 +102,7 @@ describe('SharedManager project memory sync', () => {
|
|||||||
fs.mkdirSync(projectPath, { recursive: true });
|
fs.mkdirSync(projectPath, { recursive: true });
|
||||||
|
|
||||||
const manager = new SharedManager();
|
const manager = new SharedManager();
|
||||||
manager.syncProjectMemories(instancePath);
|
await manager.syncProjectMemories(instancePath);
|
||||||
|
|
||||||
const linkStats = fs.lstatSync(projectMemoryPath);
|
const linkStats = fs.lstatSync(projectMemoryPath);
|
||||||
expect(linkStats.isSymbolicLink()).toBe(true);
|
expect(linkStats.isSymbolicLink()).toBe(true);
|
||||||
|
|||||||
Reference in New Issue
Block a user