Merge origin/main into feature/shared-packages

Resolved conflicts:
- list.ts: Keep @automaker/git-utils import, add worktree-metadata import
- feature-loader.ts: Use Feature type from @automaker/types
- automaker-paths.test.ts: Import from @automaker/platform
- kanban-card.tsx: Accept deletion (split into components/)
- subprocess.test.ts: Keep libs/platform location

Added missing exports to @automaker/platform:
- getGlobalSettingsPath, getCredentialsPath, getProjectSettingsPath, ensureDataDir

Added title and titleGenerating fields to @automaker/types Feature interface.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Kacper
2025-12-20 22:20:17 +01:00
108 changed files with 10834 additions and 3489 deletions

View File

@@ -65,6 +65,47 @@ describe("fs-utils.ts", () => {
// Should not throw
await expect(mkdirSafe(symlinkPath)).resolves.toBeUndefined();
});
it("should handle ELOOP error gracefully when checking path", async () => {
// Mock lstat to throw ELOOP error
const originalLstat = fs.lstat;
const mkdirSafePath = path.join(testDir, "eloop-path");
vi.spyOn(fs, "lstat").mockRejectedValueOnce({ code: "ELOOP" });
// Should not throw, should return gracefully
await expect(mkdirSafe(mkdirSafePath)).resolves.toBeUndefined();
vi.restoreAllMocks();
});
it("should handle EEXIST error gracefully when creating directory", async () => {
const newDir = path.join(testDir, "race-condition-dir");
// Mock lstat to return ENOENT (path doesn't exist)
// Then mock mkdir to throw EEXIST (race condition)
vi.spyOn(fs, "lstat").mockRejectedValueOnce({ code: "ENOENT" });
vi.spyOn(fs, "mkdir").mockRejectedValueOnce({ code: "EEXIST" });
// Should not throw, should return gracefully
await expect(mkdirSafe(newDir)).resolves.toBeUndefined();
vi.restoreAllMocks();
});
it("should handle ELOOP error gracefully when creating directory", async () => {
const newDir = path.join(testDir, "eloop-create-dir");
// Mock lstat to return ENOENT (path doesn't exist)
// Then mock mkdir to throw ELOOP
vi.spyOn(fs, "lstat").mockRejectedValueOnce({ code: "ENOENT" });
vi.spyOn(fs, "mkdir").mockRejectedValueOnce({ code: "ELOOP" });
// Should not throw, should return gracefully
await expect(mkdirSafe(newDir)).resolves.toBeUndefined();
vi.restoreAllMocks();
});
});
describe("existsSafe", () => {
@@ -109,5 +150,24 @@ describe("fs-utils.ts", () => {
const exists = await existsSafe(symlinkPath);
expect(exists).toBe(true);
});
it("should return true for ELOOP error (symlink loop)", async () => {
// Mock lstat to throw ELOOP error
vi.spyOn(fs, "lstat").mockRejectedValueOnce({ code: "ELOOP" });
const exists = await existsSafe("/some/path/with/loop");
expect(exists).toBe(true);
vi.restoreAllMocks();
});
it("should throw for other errors", async () => {
// Mock lstat to throw a non-ENOENT, non-ELOOP error
vi.spyOn(fs, "lstat").mockRejectedValueOnce({ code: "EACCES" });
await expect(existsSafe("/some/path")).rejects.toMatchObject({ code: "EACCES" });
vi.restoreAllMocks();
});
});
});