feat(v0.6): ctask agents check + doctor integration

ctask agents check [workspace] validates that a workspace's agent
configuration can be launched without launching it: agent type known,
command resolvable on PATH, launch_dir valid, AGENTS.md present,
CLAUDE.md shim present (WARN only, claude type only). agent.env keys
are displayed informationally, with a WARN line when any key shadows a
ctask-exported CTASK_* var. Returns non-zero when any check FAILs.

ctask doctor includes the same sweep when a workspace context is
resolvable — the most-recently-active workspace via
workspace.MostRecentActive. When no active workspace exists, doctor
shows "Agent check: skipped (no workspace context)" without bumping
the failure counter. runAgentsCheckOnWorkspace is shared between the
standalone command and the doctor integration.

TestCompletionSubcommandViaExecute is made order-independent: cobra's
default completion command captures the root output writer on the first
Execute() in the process, and the new agents-check tests now run an
Execute() earlier in the suite.
This commit is contained in:
2026-05-15 11:28:14 -04:00
parent 0c6ed0c0cf
commit 0f96d202c7
6 changed files with 501 additions and 0 deletions
+25
View File
@@ -213,6 +213,11 @@ func runDoctor(cmd *cobra.Command, args []string) error {
resolver := config.LoadResolver()
checkSettings(resolver, &passed, &failed)
// Check 11: v0.6 — agent check when a workspace context is resolvable.
// Uses the most-recently-active workspace (mirrors `ctask last`); skips
// with an INFO line when no active workspace exists anywhere.
checkAgentForDoctor(&passed, &failed)
// Summary
fmt.Println()
fmt.Printf("%d checks passed, %d failed\n", passed, failed)
@@ -364,6 +369,26 @@ func formatSettingSource(s config.ResolvedSetting) string {
return base
}
// checkAgentForDoctor runs the same validation as `ctask agents check`
// against the most-recently-active workspace. When no active workspace
// exists, prints a skip line. FAIL outcomes increment the failed counter
// (so doctor's overall exit code reflects agent breakage); WARN does not.
func checkAgentForDoctor(passed, failed *int) {
_ = passed // reserved for future symmetry with the other checks
fmt.Println()
roots := config.SearchRoots()
best, err := workspace.MostRecentActive(roots)
if err != nil || best == nil {
fmt.Println("Agent check: skipped (no workspace context)")
return
}
if checkErr := runAgentsCheckOnWorkspace(os.Stdout, best); checkErr != nil {
// runAgentsCheckOnWorkspace already printed the [FAIL] lines; just
// increment the counter so the summary reflects the breakage.
*failed++
}
}
// checkTmux reports the three-state tmux check (v0.5.3):
// - CTASK_SESSION_MODE != "persistent" -> INFO (direct mode, tmux optional)
// - persistent + tmux on PATH + version OK -> two INFO lines