feat(agent-teams): add team path resolution utilities

- Implement user-global paths (~/.sisyphus/teams/, ~/.sisyphus/tasks/)
- Reuse sanitizePathSegment for team name sanitization
- Cross-platform home directory resolution
- Comprehensive test coverage with sanitization tests

Task 2/25 complete
This commit is contained in:
YeonGyu-Kim 2026-02-11 19:13:07 +09:00
parent 5e06db0c60
commit 3e2e4e29df
2 changed files with 29 additions and 26 deletions

View File

@ -1,7 +1,6 @@
/// <reference types="bun-types" />
import { afterEach, beforeEach, describe, expect, test } from "bun:test"
import { mkdtempSync, rmSync } from "node:fs"
import { tmpdir } from "node:os"
import { describe, expect, test } from "bun:test"
import { homedir } from "node:os"
import { join } from "node:path"
import {
getAgentTeamsRootDir,
@ -16,23 +15,9 @@ import {
} from "./paths"
describe("agent-teams paths", () => {
let originalCwd: string
let tempProjectDir: string
beforeEach(() => {
originalCwd = process.cwd()
tempProjectDir = mkdtempSync(join(tmpdir(), "agent-teams-paths-"))
process.chdir(tempProjectDir)
})
afterEach(() => {
process.chdir(originalCwd)
rmSync(tempProjectDir, { recursive: true, force: true })
})
test("uses project-local .sisyphus directory as storage root", () => {
test("uses user-global .sisyphus directory as storage root", () => {
//#given
const expectedRoot = join(tempProjectDir, ".sisyphus", "agent-teams")
const expectedRoot = join(homedir(), ".sisyphus", "agent-teams")
//#when
const root = getAgentTeamsRootDir()
@ -43,7 +28,7 @@ describe("agent-teams paths", () => {
test("builds expected teams and tasks root directories", () => {
//#given
const expectedRoot = join(tempProjectDir, ".sisyphus", "agent-teams")
const expectedRoot = join(homedir(), ".sisyphus", "agent-teams")
//#when
const teamsRoot = getTeamsRootDir()
@ -59,7 +44,7 @@ describe("agent-teams paths", () => {
const teamName = "alpha_team"
const agentName = "worker_1"
const taskId = "T-123"
const expectedTeamDir = join(getTeamsRootDir(), teamName)
const expectedTeamDir = join(getTeamsRootDir(), "alpha_team")
//#when
const teamDir = getTeamDir(teamName)
@ -74,7 +59,23 @@ describe("agent-teams paths", () => {
expect(configPath).toBe(join(expectedTeamDir, "config.json"))
expect(inboxDir).toBe(join(expectedTeamDir, "inboxes"))
expect(inboxPath).toBe(join(expectedTeamDir, "inboxes", `${agentName}.json`))
expect(taskDir).toBe(join(getTeamTasksRootDir(), teamName))
expect(taskPath).toBe(join(getTeamTasksRootDir(), teamName, `${taskId}.json`))
expect(taskDir).toBe(join(getTeamTasksRootDir(), "alpha_team"))
expect(taskPath).toBe(join(getTeamTasksRootDir(), "alpha_team", `${taskId}.json`))
})
test("sanitizes team names with invalid characters", () => {
//#given
const invalidTeamName = "team space/with@special#chars"
const expectedSanitized = "team-space-with-special-chars"
//#when
const teamDir = getTeamDir(invalidTeamName)
const configPath = getTeamConfigPath(invalidTeamName)
const taskDir = getTeamTaskDir(invalidTeamName)
//#then
expect(teamDir).toBe(join(getTeamsRootDir(), expectedSanitized))
expect(configPath).toBe(join(getTeamsRootDir(), expectedSanitized, "config.json"))
expect(taskDir).toBe(join(getTeamTasksRootDir(), expectedSanitized))
})
})

View File

@ -1,10 +1,12 @@
import { join } from "node:path"
import { homedir } from "node:os"
import { sanitizePathSegment } from "../../features/claude-tasks/storage"
const SISYPHUS_DIR = ".sisyphus"
const AGENT_TEAMS_DIR = "agent-teams"
export function getAgentTeamsRootDir(): string {
return join(process.cwd(), SISYPHUS_DIR, AGENT_TEAMS_DIR)
return join(homedir(), SISYPHUS_DIR, AGENT_TEAMS_DIR)
}
export function getTeamsRootDir(): string {
@ -16,7 +18,7 @@ export function getTeamTasksRootDir(): string {
}
export function getTeamDir(teamName: string): string {
return join(getTeamsRootDir(), teamName)
return join(getTeamsRootDir(), sanitizePathSegment(teamName))
}
export function getTeamConfigPath(teamName: string): string {
@ -32,7 +34,7 @@ export function getTeamInboxPath(teamName: string, agentName: string): string {
}
export function getTeamTaskDir(teamName: string): string {
return join(getTeamTasksRootDir(), teamName)
return join(getTeamTasksRootDir(), sanitizePathSegment(teamName))
}
export function getTeamTaskPath(teamName: string, taskId: string): string {