mirror of
https://mirror.skon.top/github.com/code-yeongyu/oh-my-opencode
synced 2026-04-21 07:50:31 +08:00
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:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
}))
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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" })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user