fix: prefer ~ for home paths in output

This commit is contained in:
Peter Steinberger
2026-01-23 03:43:32 +00:00
parent 34bb7250f8
commit 7f68bf79b6
44 changed files with 245 additions and 152 deletions

View File

@@ -13,6 +13,7 @@ import {
resolveStorePath,
} from "../config/sessions.js";
import { note } from "../terminal/note.js";
import { shortenHomePath } from "../utils.js";
type DoctorPrompterLike = {
confirmSkipInNonInteractive: (params: {
@@ -131,11 +132,16 @@ export async function noteStateIntegrity(
const sessionsDir = resolveSessionTranscriptsDirForAgent(agentId, env, homedir);
const storePath = resolveStorePath(cfg.session?.store, { agentId });
const storeDir = path.dirname(storePath);
const displayStateDir = shortenHomePath(stateDir);
const displayOauthDir = shortenHomePath(oauthDir);
const displaySessionsDir = shortenHomePath(sessionsDir);
const displayStoreDir = shortenHomePath(storeDir);
const displayConfigPath = configPath ? shortenHomePath(configPath) : undefined;
let stateDirExists = existsDir(stateDir);
if (!stateDirExists) {
warnings.push(
`- CRITICAL: state directory missing (${stateDir}). Sessions, credentials, logs, and config are stored there.`,
`- CRITICAL: state directory missing (${displayStateDir}). Sessions, credentials, logs, and config are stored there.`,
);
if (cfg.gateway?.mode === "remote") {
warnings.push(
@@ -143,26 +149,26 @@ export async function noteStateIntegrity(
);
}
const create = await prompter.confirmSkipInNonInteractive({
message: `Create ${stateDir} now?`,
message: `Create ${displayStateDir} now?`,
initialValue: false,
});
if (create) {
const created = ensureDir(stateDir);
if (created.ok) {
changes.push(`- Created ${stateDir}`);
changes.push(`- Created ${displayStateDir}`);
stateDirExists = true;
} else {
warnings.push(`- Failed to create ${stateDir}: ${created.error}`);
warnings.push(`- Failed to create ${displayStateDir}: ${created.error}`);
}
}
}
if (stateDirExists && !canWriteDir(stateDir)) {
warnings.push(`- State directory not writable (${stateDir}).`);
warnings.push(`- State directory not writable (${displayStateDir}).`);
const hint = dirPermissionHint(stateDir);
if (hint) warnings.push(` ${hint}`);
const repair = await prompter.confirmSkipInNonInteractive({
message: `Repair permissions on ${stateDir}?`,
message: `Repair permissions on ${displayStateDir}?`,
initialValue: true,
});
if (repair) {
@@ -170,9 +176,9 @@ export async function noteStateIntegrity(
const stat = fs.statSync(stateDir);
const target = addUserRwx(stat.mode);
fs.chmodSync(stateDir, target);
changes.push(`- Repaired permissions on ${stateDir}`);
changes.push(`- Repaired permissions on ${displayStateDir}`);
} catch (err) {
warnings.push(`- Failed to repair ${stateDir}: ${String(err)}`);
warnings.push(`- Failed to repair ${displayStateDir}: ${String(err)}`);
}
}
}
@@ -181,19 +187,19 @@ export async function noteStateIntegrity(
const stat = fs.statSync(stateDir);
if ((stat.mode & 0o077) !== 0) {
warnings.push(
`- State directory permissions are too open (${stateDir}). Recommend chmod 700.`,
`- State directory permissions are too open (${displayStateDir}). Recommend chmod 700.`,
);
const tighten = await prompter.confirmSkipInNonInteractive({
message: `Tighten permissions on ${stateDir} to 700?`,
message: `Tighten permissions on ${displayStateDir} to 700?`,
initialValue: true,
});
if (tighten) {
fs.chmodSync(stateDir, 0o700);
changes.push(`- Tightened permissions on ${stateDir} to 700`);
changes.push(`- Tightened permissions on ${displayStateDir} to 700`);
}
}
} catch (err) {
warnings.push(`- Failed to read ${stateDir} permissions: ${String(err)}`);
warnings.push(`- Failed to read ${displayStateDir} permissions: ${String(err)}`);
}
}
@@ -202,19 +208,21 @@ export async function noteStateIntegrity(
const stat = fs.statSync(configPath);
if ((stat.mode & 0o077) !== 0) {
warnings.push(
`- Config file is group/world readable (${configPath}). Recommend chmod 600.`,
`- Config file is group/world readable (${displayConfigPath ?? configPath}). Recommend chmod 600.`,
);
const tighten = await prompter.confirmSkipInNonInteractive({
message: `Tighten permissions on ${configPath} to 600?`,
message: `Tighten permissions on ${displayConfigPath ?? configPath} to 600?`,
initialValue: true,
});
if (tighten) {
fs.chmodSync(configPath, 0o600);
changes.push(`- Tightened permissions on ${configPath} to 600`);
changes.push(`- Tightened permissions on ${displayConfigPath ?? configPath} to 600`);
}
}
} catch (err) {
warnings.push(`- Failed to read config permissions (${configPath}): ${String(err)}`);
warnings.push(
`- Failed to read config permissions (${displayConfigPath ?? configPath}): ${String(err)}`,
);
}
}
@@ -223,26 +231,33 @@ export async function noteStateIntegrity(
dirCandidates.set(sessionsDir, "Sessions dir");
dirCandidates.set(storeDir, "Session store dir");
dirCandidates.set(oauthDir, "OAuth dir");
const displayDirFor = (dir: string) => {
if (dir === sessionsDir) return displaySessionsDir;
if (dir === storeDir) return displayStoreDir;
if (dir === oauthDir) return displayOauthDir;
return shortenHomePath(dir);
};
for (const [dir, label] of dirCandidates) {
const displayDir = displayDirFor(dir);
if (!existsDir(dir)) {
warnings.push(`- CRITICAL: ${label} missing (${dir}).`);
warnings.push(`- CRITICAL: ${label} missing (${displayDir}).`);
const create = await prompter.confirmSkipInNonInteractive({
message: `Create ${label} at ${dir}?`,
message: `Create ${label} at ${displayDir}?`,
initialValue: true,
});
if (create) {
const created = ensureDir(dir);
if (created.ok) {
changes.push(`- Created ${label}: ${dir}`);
changes.push(`- Created ${label}: ${displayDir}`);
} else {
warnings.push(`- Failed to create ${dir}: ${created.error}`);
warnings.push(`- Failed to create ${displayDir}: ${created.error}`);
}
}
continue;
}
if (!canWriteDir(dir)) {
warnings.push(`- ${label} not writable (${dir}).`);
warnings.push(`- ${label} not writable (${displayDir}).`);
const hint = dirPermissionHint(dir);
if (hint) warnings.push(` ${hint}`);
const repair = await prompter.confirmSkipInNonInteractive({
@@ -254,9 +269,9 @@ export async function noteStateIntegrity(
const stat = fs.statSync(dir);
const target = addUserRwx(stat.mode);
fs.chmodSync(dir, target);
changes.push(`- Repaired permissions on ${label}: ${dir}`);
changes.push(`- Repaired permissions on ${label}: ${displayDir}`);
} catch (err) {
warnings.push(`- Failed to repair ${dir}: ${String(err)}`);
warnings.push(`- Failed to repair ${displayDir}: ${String(err)}`);
}
}
}
@@ -274,8 +289,8 @@ export async function noteStateIntegrity(
warnings.push(
[
"- Multiple state directories detected. This can split session history.",
...Array.from(extraStateDirs).map((dir) => ` - ${dir}`),
` Active state dir: ${stateDir}`,
...Array.from(extraStateDirs).map((dir) => ` - ${shortenHomePath(dir)}`),
` Active state dir: ${displayStateDir}`,
].join("\n"),
);
}
@@ -311,7 +326,7 @@ export async function noteStateIntegrity(
const transcriptPath = resolveSessionFilePath(mainEntry.sessionId, mainEntry, { agentId });
if (!existsFile(transcriptPath)) {
warnings.push(
`- Main session transcript missing (${transcriptPath}). History will appear to reset.`,
`- Main session transcript missing (${shortenHomePath(transcriptPath)}). History will appear to reset.`,
);
} else {
const lineCount = countJsonlLines(transcriptPath);