Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -898,8 +898,21 @@ async function handleCliOnly(command: string, args: string[]) {
await runDoctor(eng, args);
await eng.disconnect();
} catch {
// DB unavailable — still run filesystem checks
await runDoctor(null, args, getDbUrlSource());
// DB unavailable — still run filesystem checks. Detect the common
// case where PGLite is locked by an attached `gbrain serve` MCP
// (co-existence is expected, not an error) so doctor can demote
// the connection warning to an info-level ok message.
const fs = await import('fs');
const path = await import('path');
const { gbrainPath } = await import('./core/config.ts');
let lockHeldByMcp = false;
try {
const pidFile = path.join(gbrainPath('brain.pglite'), 'postmaster.pid');
lockHeldByMcp = fs.existsSync(pidFile);
} catch {
// best-effort detection
}
await runDoctor(null, args, getDbUrlSource(), { lockHeldByMcp });
}
}
return;
Expand Down
14 changes: 12 additions & 2 deletions src/commands/doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ export async function checkSyncFreshness(engine: BrainEngine): Promise<Check> {
* user has no DB configured anywhere; otherwise the caller chose --fast or
* we failed to connect despite a configured URL.
*/
export async function runDoctor(engine: BrainEngine | null, args: string[], dbSource?: DbUrlSource) {
export async function runDoctor(engine: BrainEngine | null, args: string[], dbSource?: DbUrlSource, opts?: { lockHeldByMcp?: boolean }) {
const jsonOutput = args.includes('--json');
const fastMode = args.includes('--fast');
const doFix = args.includes('--fix');
Expand Down Expand Up @@ -1260,14 +1260,24 @@ export async function runDoctor(engine: BrainEngine | null, args: string[], dbSo
// skipped the connection. When null, there really is no config
// anywhere.
let msg: string;
let status: 'ok' | 'warn' | 'fail' = 'warn';
if (fastMode && dbSource) {
msg = `Skipping DB checks (--fast mode, URL present from ${dbSource})`;
} else if (!fastMode && dbSource && opts?.lockHeldByMcp) {
// PGLite single-writer lock held by another process (typically
// `gbrain serve` MCP). Co-existence with MCP is expected, not an
// error: filesystem checks still ran; DB-backed health is reachable
// via the MCP surface (mcp__gbrain__get_health). Demoting to `ok`
// with an info-prefixed message so this stops surfacing as a warning
// every time a user runs `gbrain doctor` alongside an attached MCP.
msg = `info: DB owned by another process (likely 'gbrain serve' MCP holding the PGLite single-writer lock). Filesystem checks ran; use 'mcp__gbrain__get_health' for DB-backed checks.`;
status = 'ok';
} else if (!fastMode && dbSource) {
msg = `Could not connect to configured DB (URL from ${dbSource}); filesystem checks only`;
} else {
msg = 'No database configured (filesystem checks only). Set GBRAIN_DATABASE_URL or run `gbrain init`.';
}
checks.push({ name: 'connection', status: 'warn', message: msg });
checks.push({ name: 'connection', status, message: msg });
}
const earlyFail1 = outputResults(checks, jsonOutput);
process.exit(earlyFail1 ? 1 : 0);
Expand Down
2 changes: 1 addition & 1 deletion src/core/check-resolvable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export function parseResolverEntries(resolverContent: string): ResolverEntry[] {

/** Simple YAML frontmatter parser — extracts triggers array if present. */
function extractTriggers(skillContent: string): string[] {
const fmMatch = skillContent.match(/^---\n([\s\S]*?)\n---/);
const fmMatch = skillContent.match(/^---\r?\n([\s\S]*?)\r?\n---/);
if (!fmMatch) return [];
const fm = fmMatch[1];
const triggersMatch = fm.match(/^triggers:\s*\n((?:\s+-\s+.+\n?)*)/m);
Expand Down