From e60c3fc1b32e7898f1ee0fae43291c5f545b846c Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 10 Jan 2026 16:23:28 +0100 Subject: [PATCH] fix: doctor ignore install dir in legacy workspace check --- CHANGELOG.md | 1 + src/commands/doctor-workspace.test.ts | 37 +++++++++++++++++++++++++++ src/commands/doctor-workspace.ts | 32 +++++++++++++++++++---- src/commands/doctor.test.ts | 7 ++++- 4 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 src/commands/doctor-workspace.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cdaecb2f..8fb9d5d1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Onboarding/Gateway: persist non-interactive gateway token auth in config; add WS wizard + gateway tool-calling regression coverage. - CLI: `clawdbot sessions` now includes `elev:*` + `usage:*` flags in the table output. - Branding: normalize user-facing “ClawdBot”/“CLAWDBOT” → “Clawdbot” (CLI, status, docs). +- Doctor: avoid false legacy workspace warning when install dir is `~/clawdbot`. (#660) - iMessage: fix reasoning persistence across DMs; avoid partial/duplicate replies when reasoning is enabled. (#655) — thanks @antons. - Models/Auth: allow MiniMax API configs without `models.providers.minimax.apiKey` (auth profiles / `MINIMAX_API_KEY`). (#656) — thanks @mneves75. - Agents: avoid duplicate replies when the message tool sends. (#659) — thanks @mickahouan. diff --git a/src/commands/doctor-workspace.test.ts b/src/commands/doctor-workspace.test.ts new file mode 100644 index 000000000..424ff12b2 --- /dev/null +++ b/src/commands/doctor-workspace.test.ts @@ -0,0 +1,37 @@ +import path from "node:path"; + +import { describe, expect, it } from "vitest"; + +import { detectLegacyWorkspaceDirs } from "./doctor-workspace.js"; + +describe("detectLegacyWorkspaceDirs", () => { + it("ignores ~/clawdbot when it doesn't look like a workspace (e.g. install dir)", () => { + const home = "/home/user"; + const workspaceDir = "/home/user/clawd"; + const candidate = path.join(home, "clawdbot"); + + const detection = detectLegacyWorkspaceDirs({ + workspaceDir, + homedir: () => home, + exists: (value) => value === candidate, + }); + + expect(detection.activeWorkspace).toBe(path.resolve(workspaceDir)); + expect(detection.legacyDirs).toEqual([]); + }); + + it("flags ~/clawdis when it contains workspace markers", () => { + const home = "/home/user"; + const workspaceDir = "/home/user/clawd"; + const candidate = path.join(home, "clawdis"); + const agentsPath = path.join(candidate, "AGENTS.md"); + + const detection = detectLegacyWorkspaceDirs({ + workspaceDir, + homedir: () => home, + exists: (value) => value === candidate || value === agentsPath, + }); + + expect(detection.legacyDirs).toEqual([candidate]); + }); +}); diff --git a/src/commands/doctor-workspace.ts b/src/commands/doctor-workspace.ts index 0a3ea2ed0..3320e8e83 100644 --- a/src/commands/doctor-workspace.ts +++ b/src/commands/doctor-workspace.ts @@ -2,7 +2,12 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import { DEFAULT_AGENTS_FILENAME } from "../agents/workspace.js"; +import { + DEFAULT_AGENTS_FILENAME, + DEFAULT_IDENTITY_FILENAME, + DEFAULT_SOUL_FILENAME, + DEFAULT_USER_FILENAME, +} from "../agents/workspace.js"; export const MEMORY_SYSTEM_PROMPT = [ "Memory system not found in workspace.", @@ -46,6 +51,19 @@ export type LegacyWorkspaceDetection = { legacyDirs: string[]; }; +function looksLikeWorkspaceDir( + dir: string, + exists: (value: string) => boolean, +) { + const markers = [ + DEFAULT_AGENTS_FILENAME, + DEFAULT_SOUL_FILENAME, + DEFAULT_USER_FILENAME, + DEFAULT_IDENTITY_FILENAME, + ]; + return markers.some((name) => exists(path.join(dir, name))); +} + export function detectLegacyWorkspaceDirs(params: { workspaceDir: string; homedir?: () => string; @@ -56,10 +74,14 @@ export function detectLegacyWorkspaceDirs(params: { const home = homedir(); const activeWorkspace = path.resolve(params.workspaceDir); const candidates = [path.join(home, "clawdis"), path.join(home, "clawdbot")]; - const legacyDirs = candidates.filter((candidate) => { - if (!exists(candidate)) return false; - return path.resolve(candidate) !== activeWorkspace; - }); + const legacyDirs = candidates + .filter((candidate) => { + if (!exists(candidate)) return false; + return path.resolve(candidate) !== activeWorkspace; + }) + .filter((candidate) => { + return looksLikeWorkspaceDir(candidate, exists); + }); return { activeWorkspace, legacyDirs }; } diff --git a/src/commands/doctor.test.ts b/src/commands/doctor.test.ts index e6ef9905d..d649c810b 100644 --- a/src/commands/doctor.test.ts +++ b/src/commands/doctor.test.ts @@ -535,8 +535,13 @@ describe("doctor", () => { .mockReturnValue("/Users/steipete"); const realExists = fs.existsSync; const legacyPath = path.join("/Users/steipete", "clawdis"); + const legacyAgentsPath = path.join(legacyPath, "AGENTS.md"); const existsSpy = vi.spyOn(fs, "existsSync").mockImplementation((value) => { - if (value === "/Users/steipete/clawdis" || value === legacyPath) + if ( + value === "/Users/steipete/clawdis" || + value === legacyPath || + value === legacyAgentsPath + ) return true; return realExists(value as never); });