feat(cli): add doctor check for team-mode dependencies

This commit is contained in:
YeonGyu-Kim
2026-04-28 10:48:16 +09:00
parent 9cf14792b2
commit fac5f95417
3 changed files with 71 additions and 0 deletions

View File

@@ -4,6 +4,7 @@ import { checkSystem, gatherSystemInfo } from "./system"
import { checkConfig } from "./config"
import { checkTools, gatherToolsSummary } from "./tools"
import { checkModels } from "./model-resolution"
import { checkTeamMode } from "./team-mode"
export type { CheckDefinition }
export * from "./model-resolution-types"
@@ -32,5 +33,10 @@ export function getAllCheckDefinitions(): CheckDefinition[] {
name: CHECK_NAMES[CHECK_IDS.MODELS],
check: checkModels,
},
{
id: CHECK_IDS.TEAM_MODE,
name: CHECK_NAMES[CHECK_IDS.TEAM_MODE],
check: checkTeamMode,
},
]
}

View File

@@ -0,0 +1,63 @@
import { checkTeamModeDependencies } from "../../../features/team-mode/deps"
import { resolveBaseDir } from "../../../features/team-mode/team-registry/paths"
import { TeamModeConfigSchema } from "../../../config/schema/team-mode"
import { CHECK_IDS, CHECK_NAMES } from "../constants"
import type { CheckResult } from "../types"
import { readFileSync, promises as fs } from "node:fs"
import path from "node:path"
import { detectPluginConfigFile, getOpenCodeConfigDir, parseJsonc } from "../../../shared"
export async function checkTeamMode(): Promise<CheckResult> {
const config = loadTeamModeConfig()
const teamModeConfig = TeamModeConfigSchema.parse(config.team_mode ?? {})
if (!teamModeConfig.enabled) {
return { name: CHECK_NAMES[CHECK_IDS.TEAM_MODE], status: "skip", message: "team_mode: disabled", issues: [] }
}
const deps = await checkTeamModeDependencies(teamModeConfig)
const baseDir = resolveBaseDir(teamModeConfig)
const [baseDirExists, teamCount, runtimeCount] = await Promise.all([
pathExists(baseDir),
safeCount(path.join(baseDir, "teams")),
safeCount(path.join(baseDir, "runtime")),
])
const baseDirMessage = baseDirExists ? `base dir: ok` : `base dir: missing (plugin init will create it on first use)`
return {
name: CHECK_NAMES[CHECK_IDS.TEAM_MODE],
status: deps.tmuxAvailable && deps.gitAvailable ? "pass" : "warn",
message: `team_mode: enabled | tmux: ${deps.tmuxAvailable ? "ok" : "missing"} | git: ${deps.gitAvailable ? "ok" : "missing"} | ${baseDirMessage} | declared: ${teamCount} | runtime dirs: ${runtimeCount}`,
details: undefined,
issues: [],
}
}
function loadTeamModeConfig() {
const projectConfig = detectPluginConfigFile(path.join(process.cwd(), ".opencode"))
const userConfig = detectPluginConfigFile(getOpenCodeConfigDir({ binary: "opencode" }))
const configPath = projectConfig.format !== "none" ? projectConfig.path : userConfig.path
if (!configPath) return { team_mode: undefined }
try {
return parseJsonc<{ team_mode?: { enabled?: boolean } }>(readFileSync(configPath, "utf-8"))
} catch {
return { team_mode: undefined }
}
}
async function safeCount(dir: string): Promise<number> {
try {
const entries = await fs.readdir(dir, { withFileTypes: true })
return entries.filter((entry) => entry.isDirectory()).length
} catch {
return 0
}
}
async function pathExists(dir: string): Promise<boolean> {
try {
const stats = await fs.stat(dir)
return stats.isDirectory()
} catch {
return false
}
}

View File

@@ -23,6 +23,7 @@ export const CHECK_IDS = {
CONFIG: "config",
TOOLS: "tools",
MODELS: "models",
TEAM_MODE: "team-mode",
} as const
export const CHECK_NAMES: Record<string, string> = {
@@ -30,6 +31,7 @@ export const CHECK_NAMES: Record<string, string> = {
[CHECK_IDS.CONFIG]: "Configuration",
[CHECK_IDS.TOOLS]: "Tools",
[CHECK_IDS.MODELS]: "Models",
[CHECK_IDS.TEAM_MODE]: "Team Mode",
} as const
export const EXIT_CODES = {