refactor(team-mode): remove AI slop from team-mode feature files

Bulk cleanup of AI-generated code smells across src/features/team-mode. Ran the ai-slop-remover skill on 35 added source files in this feature; this commit captures the subset that actually had safe slop to remove. Every change is behavior-preserving (typecheck + full test suite green: 4861 pass). Net removal: -28 lines.

Categories cleaned: redundant wrapper abstractions (resolveHomeDir, getMemberName, cloneJsonRecord, serializeResult, resolveProjectRoot), redundant defensive guards (typed-status runtime re-check, nullish-vs-nullish-or-undefined, === true on optional chains, schema-already-validated array holes), duplication (identical switch cases, dup lead-only branches consolidated via template literal, repeated Set-has+find into Map-has), dead payload fields, and needless async wrappers (return await with no try/catch, spread before new Set).

Skipped preserved: concurrency-sensitive lock files (locks.ts), input-normalization boundaries, explicit per-tool factory signatures that keep pattern consistency, and fixture helpers where micro-refactor would add abstraction. Anything ambiguous left untouched per minimal-diff rule.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-04-18 19:22:32 +09:00
parent e74156c8cf
commit 123729880b
15 changed files with 63 additions and 91 deletions

View File

@@ -9,17 +9,13 @@ export class MemberValidationError extends Error {
}
}
function getMemberName(input: Record<string, unknown>): string {
return typeof input.name === "string" ? input.name : "<unnamed>"
}
function translateMemberError(
input: Record<string, unknown>,
agentEligibilityRegistry: Readonly<Record<string, { verdict: "eligible" | "conditional" | "hard-reject"; rejectionMessage?: string }>>,
): MemberValidationError {
const name = getMemberName(input)
const hasCategory = "category" in input && input.category != null
const hasSubagentType = "subagent_type" in input && input.subagent_type != null
const name = typeof input.name === "string" ? input.name : "<unnamed>"
const hasCategory = input.category != null
const hasSubagentType = input.subagent_type != null
const hasKind = input.kind === "category" || input.kind === "subagent_type"
if (hasCategory && hasSubagentType) {
@@ -66,21 +62,21 @@ export function createParseMember<TMember>(
agentEligibilityRegistry: Readonly<Record<string, { verdict: "eligible" | "conditional" | "hard-reject"; rejectionMessage?: string }>>,
): (input: unknown) => TMember {
return function parseMember(input: unknown) {
if (input == null || typeof input !== "object") {
throw new MemberValidationError("Member must be an object")
}
if (input == null || typeof input !== "object") {
throw new MemberValidationError("Member must be an object")
}
const raw = input as Record<string, unknown>
const result = memberSchema.safeParse(
raw.kind === undefined && (raw.category !== undefined || raw.subagent_type !== undefined)
? { ...raw, kind: raw.category !== undefined ? "category" : "subagent_type" }
: raw,
)
const raw = input as Record<string, unknown>
const result = memberSchema.safeParse(
raw.kind === undefined && (raw.category !== undefined || raw.subagent_type !== undefined)
? { ...raw, kind: raw.category !== undefined ? "category" : "subagent_type" }
: raw,
)
if (!result.success) {
throw translateMemberError(raw, agentEligibilityRegistry)
}
if (!result.success) {
throw translateMemberError(raw, agentEligibilityRegistry)
}
return result.data
return result.data
}
}

View File

@@ -11,10 +11,6 @@ type TeamSpecEntry = {
path: string
}
function resolveHomeDir(): string {
return homedir()
}
function getTeamDirectory(baseDir: string, teamName: string, scope: "user" | "project", projectRoot?: string): string {
if (scope === "project") {
return path.join(projectRoot ?? "", ".omo", "teams", teamName)
@@ -24,7 +20,7 @@ function getTeamDirectory(baseDir: string, teamName: string, scope: "user" | "pr
}
export function resolveBaseDir(config: TeamModeConfig): string {
return config.base_dir ?? path.join(resolveHomeDir(), ".omo")
return config.base_dir ?? path.join(homedir(), ".omo")
}
export function getTeamSpecPath(

View File

@@ -95,7 +95,7 @@ export function validateDualSupport(member: Member): void {
)
}
if (member.kind === "category" && trimmedPrompt !== undefined && trimmedPrompt.length < 8) {
if (member.kind === "category" && member.prompt.trim().length < 8) {
throw new TeamSpecValidationError(
`Member '${member.name}' category prompt must be at least 8 characters long.`,
"CATEGORY_PROMPT_TOO_SHORT",

View File

@@ -108,8 +108,8 @@ export async function createTeamRun(
const baseDir = resolveBaseDir(config)
await ensureBaseDirs(baseDir)
const runtimeState = await createRuntimeState(spec, leadSessionId, await resolveSpecSource(spec, ctx, config), config)
await Promise.all(spec.members.map(async (member) => mkdir(getInboxDir(baseDir, runtimeState.teamRunId, member.name), { recursive: true })))
await Promise.all(spec.members.map(async (member) => ensureTeamMemberFifo(runtimeState.teamRunId, member.name)))
await Promise.all(spec.members.map((member) => mkdir(getInboxDir(baseDir, runtimeState.teamRunId, member.name), { recursive: true })))
await Promise.all(spec.members.map((member) => ensureTeamMemberFifo(runtimeState.teamRunId, member.name)))
const deadlineAt = Date.now() + (config.max_wall_clock_minutes * 60_000)
const resources: SpawnedMemberResource[] = spec.members.map(() => ({}))
@@ -127,8 +127,7 @@ export async function createTeamRun(
failure = new Error("team creation exceeded max_wall_clock_minutes")
return
}
const memberIndex = nextMemberIndex
nextMemberIndex += 1
const memberIndex = nextMemberIndex++
const member = spec.members[memberIndex]
if (!member) return
const resource = resources[memberIndex]

View File

@@ -57,7 +57,6 @@ export function findLatestShutdownRequestIndex(
): number {
for (let index = runtimeState.shutdownRequests.length - 1; index >= 0; index -= 1) {
const shutdownRequest = runtimeState.shutdownRequests[index]
if (!shutdownRequest) continue
if (shutdownRequest.memberId !== memberName) continue
if (requesterName !== undefined && shutdownRequest.requesterName !== requesterName) continue
return index
@@ -69,7 +68,7 @@ export function findLatestShutdownRequestIndex(
export async function removeWorktrees(memberPaths: Array<string | undefined>): Promise<string[]> {
const removedWorktrees: string[] = []
for (const memberPath of [...new Set(memberPaths)]) {
for (const memberPath of new Set(memberPaths)) {
if (!memberPath) continue
await rm(memberPath, { recursive: true, force: true })
removedWorktrees.push(memberPath)

View File

@@ -128,7 +128,7 @@ export async function updateMemberStatuses(
export async function readInboxMessages(teamRunId: string, memberName: string, config: TeamModeConfig) {
const inboxDir = getInboxDir(resolveBaseDir(config), teamRunId, memberName)
const fileNames = (await readdir(inboxDir)).filter((entry) => entry.endsWith(".json")).sort()
return await Promise.all(fileNames.map(async (fileName) => {
return Promise.all(fileNames.map(async (fileName) => {
const content = await readFile(path.join(inboxDir, fileName), "utf8")
return MessageSchema.parse(JSON.parse(content))
}))

View File

@@ -21,11 +21,11 @@ export async function requestShutdownOfMember(
getRuntimeMember(runtimeState, requesterName)
const existingRequestIndex = findLatestShutdownRequestIndex(runtimeState, targetMemberName, requesterName)
if (existingRequestIndex >= 0) {
const existingRequest = runtimeState.shutdownRequests[existingRequestIndex]
if (existingRequest?.approvedAt === undefined && existingRequest?.rejectedAt === undefined) {
return
}
const existingRequest = existingRequestIndex >= 0
? runtimeState.shutdownRequests[existingRequestIndex]
: undefined
if (existingRequest && existingRequest.approvedAt === undefined && existingRequest.rejectedAt === undefined) {
return
}
await sendMessage(
@@ -37,11 +37,11 @@ export async function requestShutdownOfMember(
await transitionRuntimeState(teamRunId, (currentRuntimeState) => {
const duplicateRequestIndex = findLatestShutdownRequestIndex(currentRuntimeState, targetMemberName, requesterName)
if (duplicateRequestIndex >= 0) {
const duplicateRequest = currentRuntimeState.shutdownRequests[duplicateRequestIndex]
if (duplicateRequest?.approvedAt === undefined && duplicateRequest?.rejectedAt === undefined) {
return currentRuntimeState
}
const duplicateRequest = duplicateRequestIndex >= 0
? currentRuntimeState.shutdownRequests[duplicateRequestIndex]
: undefined
if (duplicateRequest && duplicateRequest.approvedAt === undefined && duplicateRequest.rejectedAt === undefined) {
return currentRuntimeState
}
return {

View File

@@ -64,10 +64,6 @@ function getPrimaryModelKey(bgMgr: TeamBackgroundManager | undefined, leadSessio
return `${firstModel.providerID}/${firstModel.modelID}`
}
function isTaskStatus(status: string): status is Task["status"] {
return status === "pending" || status === "claimed" || status === "in_progress" || status === "completed" || status === "deleted"
}
function countTasks(tasks: Task[]): TeamStatus["tasks"] {
const counts = {
pending: 0,
@@ -79,8 +75,6 @@ function countTasks(tasks: Task[]): TeamStatus["tasks"] {
}
for (const task of tasks) {
if (!isTaskStatus(task.status)) continue
counts[task.status] += 1
counts.total += 1
}

View File

@@ -36,7 +36,8 @@ function extractErrorStatus(error: unknown): number | undefined {
function isSessionNotFoundError(error: unknown): boolean {
if (extractErrorStatus(error) === 404) return true
const message = extractErrorMessage(error)?.toLowerCase()
return message?.includes("not found") === true || message?.includes("missing") === true
if (!message) return false
return message.includes("not found") || message.includes("missing")
}
async function runtimeDirectoryExists(teamRunId: string, config: TeamModeConfig): Promise<boolean> {
@@ -70,7 +71,7 @@ async function leadSessionExists(
try {
const response = await ctx.client.session.get({ path: { id: leadSessionId } })
if (response.error !== undefined && response.error !== null) {
if (response.error != null) {
if (isSessionNotFoundError(response.error)) return false
throw toError(response.error)
}
@@ -130,10 +131,6 @@ export async function resumeAllTeams(
break
}
case "shutdown_requested": {
break
}
case "deleting": {
await cleanupMemberWorktrees(runtimeState)
await transitionRuntimeState(runtimeState.teamRunId, (currentRuntimeState) => ({
@@ -154,6 +151,7 @@ export async function resumeAllTeams(
break
}
case "shutdown_requested":
case "orphaned": {
break
}

View File

@@ -43,10 +43,6 @@ function serializeRuntimeState(runtimeState: RuntimeState): string {
return `${JSON.stringify(parsedRuntimeState, null, 2)}\n`
}
function determineAgentType(spec: TeamSpec, memberName: string): "leader" | "general-purpose" {
return spec.leadAgentId === memberName ? "leader" : "general-purpose"
}
function validateRuntimeState(rawState: unknown, teamRunId: string): RuntimeState {
const parsedRuntimeState = RuntimeStateSchema.safeParse(rawState)
if (!parsedRuntimeState.success) {
@@ -84,7 +80,7 @@ export async function createRuntimeState(
leadSessionId,
members: spec.members.map((member) => ({
name: member.name,
agentType: determineAgentType(spec, member.name),
agentType: spec.leadAgentId === member.name ? "leader" : "general-purpose",
status: "pending",
color: member.color,
worktreePath: member.worktreePath,

View File

@@ -29,9 +29,7 @@ export async function listTasks(
const parsedTasks: Task[] = []
for (const entry of entries) {
if (entry.isDirectory()) continue
if (entry.name.startsWith(".")) continue
if (!entry.name.endsWith(".json")) continue
if (entry.isDirectory() || entry.name.startsWith(".") || !entry.name.endsWith(".json")) continue
const taskPath = path.join(tasksDirectory, entry.name)
try {
@@ -56,7 +54,12 @@ export async function listTasks(
}
return parsedTasks
.filter((task) => filter?.status === undefined || task.status === filter.status)
.filter((task) => filter?.owner === undefined || task.owner === filter.owner)
.filter((task) => {
if (filter?.status !== undefined && task.status !== filter.status) {
return false
}
return filter?.owner === undefined || task.owner === filter.owner
})
.sort((leftTask, rightTask) => Number.parseInt(leftTask.id, 10) - Number.parseInt(rightTask.id, 10))
}

View File

@@ -29,7 +29,7 @@ export async function createTask(
await mkdir(tasksDirectory, { recursive: true, mode: 0o700 })
await mkdir(path.join(tasksDirectory, "claims"), { recursive: true, mode: 0o700 })
return await withLock(path.join(tasksDirectory, ".lock"), async () => {
return withLock(path.join(tasksDirectory, ".lock"), async () => {
const watermarkPath = path.join(tasksDirectory, HIGH_WATERMARK_FILE)
const nextTaskId = (await readHighWatermark(watermarkPath)) + 1
await atomicWrite(watermarkPath, String(nextTaskId))

View File

@@ -57,14 +57,6 @@ function sanitizeRuntimeState(runtimeState: RuntimeState): Omit<RuntimeState, "m
}
}
function resolveProjectRoot(toolContext: TeamLifecycleToolContext): string {
return typeof toolContext.directory === "string" ? toolContext.directory : process.cwd()
}
function serializeResult(result: Record<string, unknown>): string {
return JSON.stringify(result)
}
function parseInlineTeamSpec(rawSpec: unknown): TeamSpec {
let specObject: unknown = rawSpec
if (typeof rawSpec === "string") {
@@ -113,14 +105,14 @@ export function createTeamCreateTool(
const runtimeContext = toolContext as TeamLifecycleToolContext
const leadSessionId = args.leadSessionId ?? runtimeContext.sessionID
if (!leadSessionId) throw new Error("team_create requires leadSessionId or tool context sessionID")
const projectRoot = resolveProjectRoot(runtimeContext)
const projectRoot = typeof runtimeContext.directory === "string" ? runtimeContext.directory : process.cwd()
const spec = args.teamName ? await loadTeamSpec(args.teamName, config, projectRoot) : parseInlineTeamSpec(args.inline_spec)
const participantRuntime = await findParticipantRuntime(runtimeContext.sessionID, config)
if (participantRuntime && (participantRuntime.teamName !== spec.name || participantRuntime.leadSessionId !== leadSessionId)) {
throw new Error(`team_create denied: session is already a participant of team ${participantRuntime.teamRunId}`)
}
const runtimeState = await createTeamRun(spec, leadSessionId, { client, manager: bgMgr, directory: projectRoot }, config, bgMgr, tmuxMgr)
return serializeResult({ teamRunId: runtimeState.teamRunId, runtimeState: sanitizeRuntimeState(runtimeState) })
return JSON.stringify({ teamRunId: runtimeState.teamRunId, runtimeState: sanitizeRuntimeState(runtimeState) })
},
})
}
@@ -141,7 +133,7 @@ export function createTeamDeleteTool(
const runtimeContext = toolContext as TeamLifecycleToolContext
const { runtimeState, participant } = await resolveParticipant(args.teamRunId, runtimeContext.sessionID, config)
if (participant?.role !== "lead") throw new Error("team_delete is lead-only")
return serializeResult({ teamRunId: args.teamRunId, teamName: runtimeState.teamName, deleted: true, ...(await deleteTeam(args.teamRunId, config, tmuxMgr, backgroundManager)) })
return JSON.stringify({ teamRunId: args.teamRunId, teamName: runtimeState.teamName, deleted: true, ...(await deleteTeam(args.teamRunId, config, tmuxMgr, backgroundManager)) })
},
})
}
@@ -158,7 +150,7 @@ export function createTeamShutdownRequestTool(config: TeamModeConfig, client: Op
const { participant } = await resolveParticipant(args.teamRunId, runtimeContext.sessionID, config)
if (participant?.role !== "lead") throw new Error("team_shutdown_request is lead-only")
await requestShutdownOfMember(args.teamRunId, args.targetMemberName, participant.memberName, config)
return serializeResult({ teamRunId: args.teamRunId, targetMemberName: args.targetMemberName, requesterName: participant.memberName, status: "shutdown_requested" })
return JSON.stringify({ teamRunId: args.teamRunId, targetMemberName: args.targetMemberName, requesterName: participant.memberName, status: "shutdown_requested" })
},
})
}
@@ -175,7 +167,7 @@ export function createTeamApproveShutdownTool(config: TeamModeConfig, client: Op
const { participant } = await resolveParticipant(args.teamRunId, runtimeContext.sessionID, config)
if (!participant || (participant.role !== "lead" && participant.memberName !== args.memberName)) throw new Error("team_approve_shutdown: caller must be target member or team lead")
await approveShutdown(args.teamRunId, args.memberName, participant.memberName, config)
return serializeResult({ teamRunId: args.teamRunId, memberName: args.memberName, approverName: participant.memberName, status: "shutdown_approved" })
return JSON.stringify({ teamRunId: args.teamRunId, memberName: args.memberName, approverName: participant.memberName, status: "shutdown_approved" })
},
})
}
@@ -192,7 +184,7 @@ export function createTeamRejectShutdownTool(config: TeamModeConfig, client: Ope
const { participant } = await resolveParticipant(args.teamRunId, runtimeContext.sessionID, config)
if (!participant || (participant.role !== "lead" && participant.memberName !== args.memberName)) throw new Error("team_reject_shutdown: caller must be target member or team lead")
await rejectShutdown(args.teamRunId, args.memberName, args.reason, config)
return serializeResult({ teamRunId: args.teamRunId, memberName: args.memberName, rejectedBy: participant.memberName, reason: args.reason, status: "shutdown_rejected" })
return JSON.stringify({ teamRunId: args.teamRunId, memberName: args.memberName, rejectedBy: participant.memberName, reason: args.reason, status: "shutdown_rejected" })
},
})
}

View File

@@ -25,12 +25,15 @@ async function resolveTeamRuntimeDetails(teamRunId: string, sessionID: string, c
if (team.teamRunId !== teamRunId) continue
const runtimeState = await loadRuntimeState(team.teamRunId, config)
const leadMember = runtimeState.members.find((member) => member.agentType === "leader" && runtimeState.leadSessionId === sessionID)
const isLead = runtimeState.leadSessionId === sessionID
const leadMember = isLead
? runtimeState.members.find((member) => member.agentType === "leader")
: undefined
const member = runtimeState.members.find((entry) => entry.sessionId === sessionID)
return {
teamRunId: runtimeState.teamRunId,
isLead: Boolean(leadMember),
isLead,
senderName: leadMember?.name ?? member?.name ?? "unknown",
activeMembers: runtimeState.members
.filter((entry) => entry.sessionId !== undefined)

View File

@@ -17,10 +17,6 @@ type TeamListEntry = {
memberCount: number
}
function getProjectRoot(): string {
return process.cwd()
}
export function createTeamStatusTool(
config: TeamModeConfig,
client: OpencodeClient,
@@ -51,7 +47,7 @@ export function createTeamListTool(config: TeamModeConfig, client: OpencodeClien
},
execute: async (args: { scope?: TeamListScope }) => {
const scope = args.scope ?? "all"
const projectRoot = getProjectRoot()
const projectRoot = process.cwd()
const declaredTeamSpecs = await discoverTeamSpecs(config, projectRoot)
const activeTeams = await listActiveTeams(config)
@@ -62,7 +58,7 @@ export function createTeamListTool(config: TeamModeConfig, client: OpencodeClien
const declaredTeamSpecsByName = new Map(
await Promise.all(filteredDeclaredTeamSpecs.map(async (teamSpec) => {
const loadedTeamSpec = await loadTeamSpec(teamSpec.name, config, projectRoot)
return [teamSpec.name, { scope: teamSpec.scope, memberCount: loadedTeamSpec.members.length }] as const
return [teamSpec.name, loadedTeamSpec.members.length] as const
})),
)
@@ -72,18 +68,18 @@ export function createTeamListTool(config: TeamModeConfig, client: OpencodeClien
for (const declaredTeamSpec of filteredDeclaredTeamSpecs) {
const activeTeam = activeTeamsByName.get(declaredTeamSpec.name)
const declaredTeamSpecDetails = declaredTeamSpecsByName.get(declaredTeamSpec.name)
const declaredTeamSpecMemberCount = declaredTeamSpecsByName.get(declaredTeamSpec.name)
teamEntries.push({
name: declaredTeamSpec.name,
scope: declaredTeamSpec.scope,
status: activeTeam?.status ?? "not-started",
teamRunId: activeTeam?.teamRunId,
memberCount: activeTeam?.memberCount ?? declaredTeamSpecDetails?.memberCount ?? 0,
memberCount: activeTeam?.memberCount ?? declaredTeamSpecMemberCount ?? 0,
})
}
for (const activeTeam of activeTeams) {
if (filteredDeclaredTeamSpecs.some((teamSpec) => teamSpec.name === activeTeam.teamName)) continue
if (declaredTeamSpecsByName.has(activeTeam.teamName)) continue
teamEntries.push({
name: activeTeam.teamName,