From 7f68bf79b6c4393d4a667fd672434ed9c39b2e2b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 23 Jan 2026 03:43:32 +0000 Subject: [PATCH] fix: prefer ~ for home paths in output --- .../register.files-downloads.ts | 5 +- src/cli/browser-cli-actions-observe.ts | 3 +- src/cli/browser-cli-debug.ts | 3 +- src/cli/browser-cli-extension.ts | 9 ++- src/cli/browser-cli-inspect.ts | 9 +-- src/cli/browser-cli-manage.ts | 5 +- src/cli/config-cli.ts | 3 +- src/cli/daemon-cli/status.print.ts | 19 ++++-- src/cli/gateway-cli/dev.ts | 6 +- src/cli/hooks-cli.ts | 8 +-- src/cli/memory-cli.ts | 40 +++++++---- src/cli/nodes-cli/register.camera.ts | 5 +- src/cli/nodes-cli/register.canvas.ts | 3 +- src/cli/nodes-cli/register.screen.ts | 3 +- src/cli/nodes-cli/register.status.ts | 6 +- src/cli/plugins-cli.ts | 13 ++-- src/cli/program/config-guard.ts | 3 +- src/cli/security-cli.ts | 17 +++-- src/cli/skills-cli.ts | 3 +- src/commands/agents.commands.add.ts | 10 +-- src/commands/agents.commands.delete.ts | 3 +- src/commands/agents.commands.identity.ts | 12 ++-- src/commands/agents.commands.list.ts | 5 +- src/commands/cleanup-utils.ts | 11 +-- src/commands/configure.channels.ts | 3 +- src/commands/configure.wizard.ts | 6 +- src/commands/doctor-platform-notes.ts | 6 +- src/commands/doctor-state-integrity.ts | 67 ++++++++++++------- src/commands/doctor-workspace.ts | 5 +- src/commands/doctor.ts | 5 +- src/commands/models/aliases.ts | 5 +- src/commands/models/auth.ts | 7 +- src/commands/models/fallbacks.ts | 7 +- src/commands/models/image-fallbacks.ts | 7 +- src/commands/models/list.status-command.ts | 2 +- src/commands/models/scan.ts | 3 +- src/commands/models/set-image.ts | 3 +- src/commands/models/set.ts | 3 +- src/commands/onboard-helpers.ts | 30 +++++---- src/commands/onboard-non-interactive/local.ts | 3 +- .../local/auth-choice.ts | 3 +- .../onboard-non-interactive/remote.ts | 3 +- src/commands/setup.ts | 11 +-- src/terminal/table.ts | 14 +++- 44 files changed, 245 insertions(+), 152 deletions(-) diff --git a/src/cli/browser-cli-actions-input/register.files-downloads.ts b/src/cli/browser-cli-actions-input/register.files-downloads.ts index f93451696..ffad3faa9 100644 --- a/src/cli/browser-cli-actions-input/register.files-downloads.ts +++ b/src/cli/browser-cli-actions-input/register.files-downloads.ts @@ -9,6 +9,7 @@ import { danger } from "../../globals.js"; import { defaultRuntime } from "../../runtime.js"; import type { BrowserParentOpts } from "../browser-cli-shared.js"; import { resolveBrowserActionContext } from "./shared.js"; +import { shortenHomePath } from "../../utils.js"; export function registerBrowserFilesAndDownloadsCommands( browser: Command, @@ -73,7 +74,7 @@ export function registerBrowserFilesAndDownloadsCommands( defaultRuntime.log(JSON.stringify(result, null, 2)); return; } - defaultRuntime.log(`downloaded: ${result.download.path}`); + defaultRuntime.log(`downloaded: ${shortenHomePath(result.download.path)}`); } catch (err) { defaultRuntime.error(danger(String(err))); defaultRuntime.exit(1); @@ -105,7 +106,7 @@ export function registerBrowserFilesAndDownloadsCommands( defaultRuntime.log(JSON.stringify(result, null, 2)); return; } - defaultRuntime.log(`downloaded: ${result.download.path}`); + defaultRuntime.log(`downloaded: ${shortenHomePath(result.download.path)}`); } catch (err) { defaultRuntime.error(danger(String(err))); defaultRuntime.exit(1); diff --git a/src/cli/browser-cli-actions-observe.ts b/src/cli/browser-cli-actions-observe.ts index c463bde37..66202e357 100644 --- a/src/cli/browser-cli-actions-observe.ts +++ b/src/cli/browser-cli-actions-observe.ts @@ -9,6 +9,7 @@ import { danger } from "../globals.js"; import { defaultRuntime } from "../runtime.js"; import type { BrowserParentOpts } from "./browser-cli-shared.js"; import { runCommandWithRuntime } from "./cli-utils.js"; +import { shortenHomePath } from "../utils.js"; function runBrowserObserve(action: () => Promise) { return runCommandWithRuntime(defaultRuntime, action, (err) => { @@ -61,7 +62,7 @@ export function registerBrowserActionObserveCommands( defaultRuntime.log(JSON.stringify(result, null, 2)); return; } - defaultRuntime.log(`PDF: ${result.path}`); + defaultRuntime.log(`PDF: ${shortenHomePath(result.path)}`); }); }); diff --git a/src/cli/browser-cli-debug.ts b/src/cli/browser-cli-debug.ts index 68bdb53b1..daa5ba284 100644 --- a/src/cli/browser-cli-debug.ts +++ b/src/cli/browser-cli-debug.ts @@ -12,6 +12,7 @@ import { danger } from "../globals.js"; import { defaultRuntime } from "../runtime.js"; import type { BrowserParentOpts } from "./browser-cli-shared.js"; import { runCommandWithRuntime } from "./cli-utils.js"; +import { shortenHomePath } from "../utils.js"; function runBrowserDebug(action: () => Promise) { return runCommandWithRuntime(defaultRuntime, action, (err) => { @@ -164,7 +165,7 @@ export function registerBrowserDebugCommands( defaultRuntime.log(JSON.stringify(result, null, 2)); return; } - defaultRuntime.log(`TRACE:${result.path}`); + defaultRuntime.log(`TRACE:${shortenHomePath(result.path)}`); }); }); } diff --git a/src/cli/browser-cli-extension.ts b/src/cli/browser-cli-extension.ts index ba4c62a65..3bd7c5b3a 100644 --- a/src/cli/browser-cli-extension.ts +++ b/src/cli/browser-cli-extension.ts @@ -11,6 +11,7 @@ import { defaultRuntime } from "../runtime.js"; import { movePathToTrash } from "../browser/trash.js"; import { formatDocsLink } from "../terminal/links.js"; import { theme } from "../terminal/theme.js"; +import { shortenHomePath } from "../utils.js"; import { formatCliCommand } from "./command-format.js"; function bundledExtensionRootDir() { @@ -77,7 +78,8 @@ export function registerBrowserExtensionCommands( defaultRuntime.log(JSON.stringify({ ok: true, path: installed.path }, null, 2)); return; } - defaultRuntime.log(installed.path); + const displayPath = shortenHomePath(installed.path); + defaultRuntime.log(displayPath); const copied = await copyToClipboard(installed.path).catch(() => false); defaultRuntime.error( info( @@ -85,7 +87,7 @@ export function registerBrowserExtensionCommands( copied ? "Copied to clipboard." : "Copy to clipboard unavailable.", "Next:", `- Chrome → chrome://extensions → enable “Developer mode”`, - `- “Load unpacked” → select: ${installed.path}`, + `- “Load unpacked” → select: ${displayPath}`, `- Pin “Clawdbot Browser Relay”, then click it on the tab (badge shows ON)`, "", `${theme.muted("Docs:")} ${formatDocsLink("/tools/chrome-extension", "docs.clawd.bot/tools/chrome-extension")}`, @@ -115,7 +117,8 @@ export function registerBrowserExtensionCommands( defaultRuntime.log(JSON.stringify({ path: dir }, null, 2)); return; } - defaultRuntime.log(dir); + const displayPath = shortenHomePath(dir); + defaultRuntime.log(displayPath); const copied = await copyToClipboard(dir).catch(() => false); if (copied) defaultRuntime.error(info("Copied to clipboard.")); }); diff --git a/src/cli/browser-cli-inspect.ts b/src/cli/browser-cli-inspect.ts index 111c888db..000d33be9 100644 --- a/src/cli/browser-cli-inspect.ts +++ b/src/cli/browser-cli-inspect.ts @@ -5,6 +5,7 @@ import { browserScreenshotAction } from "../browser/client-actions.js"; import { loadConfig } from "../config/config.js"; import { danger } from "../globals.js"; import { defaultRuntime } from "../runtime.js"; +import { shortenHomePath } from "../utils.js"; import type { BrowserParentOpts } from "./browser-cli-shared.js"; export function registerBrowserInspectCommands( @@ -36,7 +37,7 @@ export function registerBrowserInspectCommands( defaultRuntime.log(JSON.stringify(result, null, 2)); return; } - defaultRuntime.log(`MEDIA:${result.path}`); + defaultRuntime.log(`MEDIA:${shortenHomePath(result.path)}`); } catch (err) { defaultRuntime.error(danger(String(err))); defaultRuntime.exit(1); @@ -106,9 +107,9 @@ export function registerBrowserInspectCommands( ), ); } else { - defaultRuntime.log(opts.out); + defaultRuntime.log(shortenHomePath(opts.out)); if (result.format === "ai" && result.imagePath) { - defaultRuntime.log(`MEDIA:${result.imagePath}`); + defaultRuntime.log(`MEDIA:${shortenHomePath(result.imagePath)}`); } } return; @@ -122,7 +123,7 @@ export function registerBrowserInspectCommands( if (result.format === "ai") { defaultRuntime.log(result.snapshot); if (result.imagePath) { - defaultRuntime.log(`MEDIA:${result.imagePath}`); + defaultRuntime.log(`MEDIA:${shortenHomePath(result.imagePath)}`); } return; } diff --git a/src/cli/browser-cli-manage.ts b/src/cli/browser-cli-manage.ts index 9caa47cdf..612653616 100644 --- a/src/cli/browser-cli-manage.ts +++ b/src/cli/browser-cli-manage.ts @@ -18,6 +18,7 @@ import { import { browserAct } from "../browser/client-actions-core.js"; import { danger, info } from "../globals.js"; import { defaultRuntime } from "../runtime.js"; +import { shortenHomePath } from "../utils.js"; import type { BrowserParentOpts } from "./browser-cli-shared.js"; import { runCommandWithRuntime } from "./cli-utils.js"; @@ -46,6 +47,8 @@ export function registerBrowserManageCommands( defaultRuntime.log(JSON.stringify(status, null, 2)); return; } + const detectedPath = status.detectedExecutablePath ?? status.executablePath; + const detectedDisplay = detectedPath ? shortenHomePath(detectedPath) : "auto"; defaultRuntime.log( [ `profile: ${status.profile ?? "clawd"}`, @@ -56,7 +59,7 @@ export function registerBrowserManageCommands( `cdpUrl: ${status.cdpUrl ?? `http://127.0.0.1:${status.cdpPort}`}`, `browser: ${status.chosenBrowser ?? "unknown"}`, `detectedBrowser: ${status.detectedBrowser ?? "unknown"}`, - `detectedPath: ${status.detectedExecutablePath ?? status.executablePath ?? "auto"}`, + `detectedPath: ${detectedDisplay}`, `profileColor: ${status.color}`, ...(status.detectError ? [`detectError: ${status.detectError}`] : []), ].join("\n"), diff --git a/src/cli/config-cli.ts b/src/cli/config-cli.ts index 22fa7e7c3..605482251 100644 --- a/src/cli/config-cli.ts +++ b/src/cli/config-cli.ts @@ -7,6 +7,7 @@ import { defaultRuntime } from "../runtime.js"; import { formatDocsLink } from "../terminal/links.js"; import { formatCliCommand } from "./command-format.js"; import { theme } from "../terminal/theme.js"; +import { shortenHomePath } from "../utils.js"; type PathSegment = string; @@ -168,7 +169,7 @@ function unsetAtPath(root: Record, path: PathSegment[]): boolea async function loadValidConfig() { const snapshot = await readConfigFileSnapshot(); if (snapshot.valid) return snapshot; - defaultRuntime.error(`Config invalid at ${snapshot.path}.`); + defaultRuntime.error(`Config invalid at ${shortenHomePath(snapshot.path)}.`); for (const issue of snapshot.issues) { defaultRuntime.error(`- ${issue.path || ""}: ${issue.message}`); } diff --git a/src/cli/daemon-cli/status.print.ts b/src/cli/daemon-cli/status.print.ts index 931dc3f10..801980d24 100644 --- a/src/cli/daemon-cli/status.print.ts +++ b/src/cli/daemon-cli/status.print.ts @@ -13,6 +13,7 @@ import { isWSLEnv } from "../../infra/wsl.js"; import { getResolvedLoggerSettings } from "../../logging.js"; import { defaultRuntime } from "../../runtime.js"; import { colorize, isRich, theme } from "../../terminal/theme.js"; +import { shortenHomePath } from "../../utils.js"; import { formatCliCommand } from "../command-format.js"; import { filterDaemonEnv, @@ -66,7 +67,7 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean }) defaultRuntime.log(`${label("Service:")} ${accent(service.label)} (${serviceStatus})`); try { const logFile = getResolvedLoggerSettings().file; - defaultRuntime.log(`${label("File logs:")} ${infoText(logFile)}`); + defaultRuntime.log(`${label("File logs:")} ${infoText(shortenHomePath(logFile))}`); } catch { // ignore missing config/log resolution } @@ -76,10 +77,14 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean }) ); } if (service.command?.sourcePath) { - defaultRuntime.log(`${label("Service file:")} ${infoText(service.command.sourcePath)}`); + defaultRuntime.log( + `${label("Service file:")} ${infoText(shortenHomePath(service.command.sourcePath))}`, + ); } if (service.command?.workingDirectory) { - defaultRuntime.log(`${label("Working dir:")} ${infoText(service.command.workingDirectory)}`); + defaultRuntime.log( + `${label("Working dir:")} ${infoText(shortenHomePath(service.command.workingDirectory))}`, + ); } const daemonEnvLines = safeDaemonEnv(service.command?.environment); if (daemonEnvLines.length > 0) { @@ -101,7 +106,7 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean }) } if (status.config) { - const cliCfg = `${status.config.cli.path}${status.config.cli.exists ? "" : " (missing)"}${status.config.cli.valid ? "" : " (invalid)"}`; + const cliCfg = `${shortenHomePath(status.config.cli.path)}${status.config.cli.exists ? "" : " (missing)"}${status.config.cli.valid ? "" : " (invalid)"}`; defaultRuntime.log(`${label("Config (cli):")} ${infoText(cliCfg)}`); if (!status.config.cli.valid && status.config.cli.issues?.length) { for (const issue of status.config.cli.issues.slice(0, 5)) { @@ -111,7 +116,7 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean }) } } if (status.config.daemon) { - const daemonCfg = `${status.config.daemon.path}${status.config.daemon.exists ? "" : " (missing)"}${status.config.daemon.valid ? "" : " (invalid)"}`; + const daemonCfg = `${shortenHomePath(status.config.daemon.path)}${status.config.daemon.exists ? "" : " (missing)"}${status.config.daemon.valid ? "" : " (invalid)"}`; defaultRuntime.log(`${label("Config (service):")} ${infoText(daemonCfg)}`); if (!status.config.daemon.valid && status.config.daemon.issues?.length) { for (const issue of status.config.daemon.issues.slice(0, 5)) { @@ -276,8 +281,8 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean }) const logs = resolveGatewayLogPaths( (service.command?.environment ?? process.env) as NodeJS.ProcessEnv, ); - defaultRuntime.error(`${errorText("Logs:")} ${logs.stdoutPath}`); - defaultRuntime.error(`${errorText("Errors:")} ${logs.stderrPath}`); + defaultRuntime.error(`${errorText("Logs:")} ${shortenHomePath(logs.stdoutPath)}`); + defaultRuntime.error(`${errorText("Errors:")} ${shortenHomePath(logs.stderrPath)}`); } spacer(); } diff --git a/src/cli/gateway-cli/dev.ts b/src/cli/gateway-cli/dev.ts index d36acd21a..f851beec3 100644 --- a/src/cli/gateway-cli/dev.ts +++ b/src/cli/gateway-cli/dev.ts @@ -6,7 +6,7 @@ import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js"; import { handleReset } from "../../commands/onboard-helpers.js"; import { CONFIG_PATH_CLAWDBOT, writeConfigFile } from "../../config/config.js"; import { defaultRuntime } from "../../runtime.js"; -import { resolveUserPath } from "../../utils.js"; +import { resolveUserPath, shortenHomePath } from "../../utils.js"; const DEV_IDENTITY_NAME = "C3-PO"; const DEV_IDENTITY_THEME = "protocol droid"; @@ -117,6 +117,6 @@ export async function ensureDevGatewayConfig(opts: { reset?: boolean }) { }, }); await ensureDevWorkspace(workspace); - defaultRuntime.log(`Dev config ready: ${CONFIG_PATH_CLAWDBOT}`); - defaultRuntime.log(`Dev workspace ready: ${resolveUserPath(workspace)}`); + defaultRuntime.log(`Dev config ready: ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); + defaultRuntime.log(`Dev workspace ready: ${shortenHomePath(resolveUserPath(workspace))}`); } diff --git a/src/cli/hooks-cli.ts b/src/cli/hooks-cli.ts index e2cd504c3..022c7c295 100644 --- a/src/cli/hooks-cli.ts +++ b/src/cli/hooks-cli.ts @@ -25,7 +25,7 @@ import { formatDocsLink } from "../terminal/links.js"; import { renderTable } from "../terminal/table.js"; import { theme } from "../terminal/theme.js"; import { formatCliCommand } from "./command-format.js"; -import { resolveUserPath } from "../utils.js"; +import { resolveUserPath, shortenHomePath } from "../utils.js"; export type HooksListOptions = { json?: boolean; @@ -224,8 +224,8 @@ export function formatHookInfo( } else { lines.push(`${theme.muted(" Source:")} ${hook.source}`); } - lines.push(`${theme.muted(" Path:")} ${hook.filePath}`); - lines.push(`${theme.muted(" Handler:")} ${hook.handlerPath}`); + lines.push(`${theme.muted(" Path:")} ${shortenHomePath(hook.filePath)}`); + lines.push(`${theme.muted(" Handler:")} ${shortenHomePath(hook.handlerPath)}`); if (hook.homepage) { lines.push(`${theme.muted(" Homepage:")} ${hook.homepage}`); } @@ -577,7 +577,7 @@ export function registerHooksCli(program: Command): void { }); await writeConfigFile(next); - defaultRuntime.log(`Linked hook path: ${resolved}`); + defaultRuntime.log(`Linked hook path: ${shortenHomePath(resolved)}`); defaultRuntime.log(`Restart the gateway to load hooks.`); return; } diff --git a/src/cli/memory-cli.ts b/src/cli/memory-cli.ts index 70ef976d2..a184fa1b4 100644 --- a/src/cli/memory-cli.ts +++ b/src/cli/memory-cli.ts @@ -17,6 +17,7 @@ import { defaultRuntime } from "../runtime.js"; import { formatDocsLink } from "../terminal/links.js"; import { colorize, isRich, theme } from "../terminal/theme.js"; import { resolveStateDir } from "../config/paths.js"; +import { shortenHomeInString, shortenHomePath } from "../utils.js"; type MemoryCommandOptions = { agent?: string; @@ -44,11 +45,15 @@ type MemorySourceScan = { function formatSourceLabel(source: string, workspaceDir: string, agentId: string): string { if (source === "memory") { - return `memory (MEMORY.md + ${path.join(workspaceDir, "memory")}${path.sep}*.md)`; + return shortenHomeInString( + `memory (MEMORY.md + ${path.join(workspaceDir, "memory")}${path.sep}*.md)`, + ); } if (source === "sessions") { const stateDir = resolveStateDir(process.env, os.homedir); - return `sessions (${path.join(stateDir, "agents", agentId, "sessions")}${path.sep}*.jsonl)`; + return shortenHomeInString( + `sessions (${path.join(stateDir, "agents", agentId, "sessions")}${path.sep}*.jsonl)`, + ); } return source; } @@ -76,7 +81,10 @@ async function checkReadableFile(pathname: string): Promise<{ exists: boolean; i } catch (err) { const code = (err as NodeJS.ErrnoException).code; if (code === "ENOENT") return { exists: false }; - return { exists: true, issue: `${pathname} not readable (${code ?? "error"})` }; + return { + exists: true, + issue: `${shortenHomePath(pathname)} not readable (${code ?? "error"})`, + }; } } @@ -92,10 +100,12 @@ async function scanSessionFiles(agentId: string): Promise { } catch (err) { const code = (err as NodeJS.ErrnoException).code; if (code === "ENOENT") { - issues.push(`sessions directory missing (${sessionsDir})`); + issues.push(`sessions directory missing (${shortenHomePath(sessionsDir)})`); return { source: "sessions", totalFiles: 0, issues }; } - issues.push(`sessions directory not accessible (${sessionsDir}): ${code ?? "error"}`); + issues.push( + `sessions directory not accessible (${shortenHomePath(sessionsDir)}): ${code ?? "error"}`, + ); return { source: "sessions", totalFiles: null, issues }; } } @@ -118,10 +128,12 @@ async function scanMemoryFiles(workspaceDir: string): Promise { } catch (err) { const code = (err as NodeJS.ErrnoException).code; if (code === "ENOENT") { - issues.push(`memory directory missing (${memoryDir})`); + issues.push(`memory directory missing (${shortenHomePath(memoryDir)})`); dirReadable = false; } else { - issues.push(`memory directory not accessible (${memoryDir}): ${code ?? "error"}`); + issues.push( + `memory directory not accessible (${shortenHomePath(memoryDir)}): ${code ?? "error"}`, + ); dirReadable = null; } } @@ -134,7 +146,9 @@ async function scanMemoryFiles(workspaceDir: string): Promise { } catch (err) { const code = (err as NodeJS.ErrnoException).code; if (dirReadable !== null) { - issues.push(`memory directory scan failed (${memoryDir}): ${code ?? "error"}`); + issues.push( + `memory directory scan failed (${shortenHomePath(memoryDir)}): ${code ?? "error"}`, + ); dirReadable = null; } } @@ -152,7 +166,7 @@ async function scanMemoryFiles(workspaceDir: string): Promise { } if ((totalFiles ?? 0) === 0 && issues.length === 0) { - issues.push(`no memory files found in ${workspaceDir}`); + issues.push(`no memory files found in ${shortenHomePath(workspaceDir)}`); } return { source: "memory", totalFiles, issues }; @@ -294,8 +308,8 @@ export async function runMemoryStatus(opts: MemoryCommandOptions) { status.sources?.length ? `${label("Sources")} ${info(status.sources.join(", "))}` : null, `${label("Indexed")} ${success(indexedLabel)}`, `${label("Dirty")} ${status.dirty ? warn("yes") : muted("no")}`, - `${label("Store")} ${info(status.dbPath)}`, - `${label("Workspace")} ${info(status.workspaceDir)}`, + `${label("Store")} ${info(shortenHomePath(status.dbPath))}`, + `${label("Workspace")} ${info(shortenHomePath(status.workspaceDir))}`, ].filter(Boolean) as string[]; if (embeddingProbe) { const state = embeddingProbe.ok ? "ready" : "unavailable"; @@ -340,7 +354,7 @@ export async function runMemoryStatus(opts: MemoryCommandOptions) { lines.push(`${label("Vector dims")} ${info(String(status.vector.dims))}`); } if (status.vector.extensionPath) { - lines.push(`${label("Vector path")} ${info(status.vector.extensionPath)}`); + lines.push(`${label("Vector path")} ${info(shortenHomePath(status.vector.extensionPath))}`); } if (status.vector.loadError) { lines.push(`${label("Vector error")} ${warn(status.vector.loadError)}`); @@ -594,7 +608,7 @@ export function registerMemoryCli(program: Command) { `${colorize(rich, theme.success, result.score.toFixed(3))} ${colorize( rich, theme.accent, - `${result.path}:${result.startLine}-${result.endLine}`, + `${shortenHomePath(result.path)}:${result.startLine}-${result.endLine}`, )}`, ); lines.push(colorize(rich, theme.muted, result.snippet)); diff --git a/src/cli/nodes-cli/register.camera.ts b/src/cli/nodes-cli/register.camera.ts index d1c020f05..4d9e56cf0 100644 --- a/src/cli/nodes-cli/register.camera.ts +++ b/src/cli/nodes-cli/register.camera.ts @@ -13,6 +13,7 @@ import { getNodesTheme, runNodesCommand } from "./cli-utils.js"; import { callGatewayCli, nodesCallOpts, resolveNodeId } from "./rpc.js"; import type { NodesRpcOpts } from "./types.js"; import { renderTable } from "../../terminal/table.js"; +import { shortenHomePath } from "../../utils.js"; const parseFacing = (value: string): CameraFacing => { const v = String(value ?? "") @@ -165,7 +166,7 @@ export function registerNodesCameraCommands(nodes: Command) { defaultRuntime.log(JSON.stringify({ files: results }, null, 2)); return; } - defaultRuntime.log(results.map((r) => `MEDIA:${r.path}`).join("\n")); + defaultRuntime.log(results.map((r) => `MEDIA:${shortenHomePath(r.path)}`).join("\n")); }); }), { timeoutMs: 60_000 }, @@ -239,7 +240,7 @@ export function registerNodesCameraCommands(nodes: Command) { ); return; } - defaultRuntime.log(`MEDIA:${filePath}`); + defaultRuntime.log(`MEDIA:${shortenHomePath(filePath)}`); }); }), { timeoutMs: 90_000 }, diff --git a/src/cli/nodes-cli/register.canvas.ts b/src/cli/nodes-cli/register.canvas.ts index 2ed31bc40..214b92af1 100644 --- a/src/cli/nodes-cli/register.canvas.ts +++ b/src/cli/nodes-cli/register.canvas.ts @@ -9,6 +9,7 @@ import { buildA2UITextJsonl, validateA2UIJsonl } from "./a2ui-jsonl.js"; import { getNodesTheme, runNodesCommand } from "./cli-utils.js"; import { callGatewayCli, nodesCallOpts, resolveNodeId } from "./rpc.js"; import type { NodesRpcOpts } from "./types.js"; +import { shortenHomePath } from "../../utils.js"; async function invokeCanvas(opts: NodesRpcOpts, command: string, params?: Record) { const nodeId = await resolveNodeId(opts, String(opts.node ?? "")); @@ -85,7 +86,7 @@ export function registerNodesCanvasCommands(nodes: Command) { ); return; } - defaultRuntime.log(`MEDIA:${filePath}`); + defaultRuntime.log(`MEDIA:${shortenHomePath(filePath)}`); }); }), { timeoutMs: 60_000 }, diff --git a/src/cli/nodes-cli/register.screen.ts b/src/cli/nodes-cli/register.screen.ts index 4a50f539b..501e00fae 100644 --- a/src/cli/nodes-cli/register.screen.ts +++ b/src/cli/nodes-cli/register.screen.ts @@ -10,6 +10,7 @@ import { parseDurationMs } from "../parse-duration.js"; import { runNodesCommand } from "./cli-utils.js"; import { callGatewayCli, nodesCallOpts, resolveNodeId } from "./rpc.js"; import type { NodesRpcOpts } from "./types.js"; +import { shortenHomePath } from "../../utils.js"; export function registerNodesScreenCommands(nodes: Command) { const screen = nodes @@ -77,7 +78,7 @@ export function registerNodesScreenCommands(nodes: Command) { ); return; } - defaultRuntime.log(`MEDIA:${written.path}`); + defaultRuntime.log(`MEDIA:${shortenHomePath(written.path)}`); }); }), { timeoutMs: 180_000 }, diff --git a/src/cli/nodes-cli/register.status.ts b/src/cli/nodes-cli/register.status.ts index dd69234a3..6f916f23b 100644 --- a/src/cli/nodes-cli/register.status.ts +++ b/src/cli/nodes-cli/register.status.ts @@ -6,6 +6,7 @@ import { callGatewayCli, nodesCallOpts, resolveNodeId } from "./rpc.js"; import type { NodesRpcOpts } from "./types.js"; import { renderTable } from "../../terminal/table.js"; import { parseDurationMs } from "../parse-duration.js"; +import { shortenHomeInString } from "../../utils.js"; function formatVersionLabel(raw: string) { const trimmed = raw.trim(); @@ -49,8 +50,9 @@ function formatPathEnv(raw?: string): string | null { const trimmed = raw.trim(); if (!trimmed) return null; const parts = trimmed.split(":").filter(Boolean); - if (parts.length <= 3) return trimmed; - return `${parts.slice(0, 2).join(":")}:…:${parts.slice(-1)[0]}`; + const display = + parts.length <= 3 ? trimmed : `${parts.slice(0, 2).join(":")}:…:${parts.slice(-1)[0]}`; + return shortenHomeInString(display); } function parseSinceMs(raw: unknown, label: string): number | undefined { diff --git a/src/cli/plugins-cli.ts b/src/cli/plugins-cli.ts index 0f4468beb..62bc76889 100644 --- a/src/cli/plugins-cli.ts +++ b/src/cli/plugins-cli.ts @@ -15,7 +15,7 @@ import { defaultRuntime } from "../runtime.js"; import { formatDocsLink } from "../terminal/links.js"; import { renderTable } from "../terminal/table.js"; import { theme } from "../terminal/theme.js"; -import { resolveUserPath } from "../utils.js"; +import { resolveUserPath, shortenHomeInString, shortenHomePath } from "../utils.js"; export type PluginsListOptions = { json?: boolean; @@ -55,7 +55,7 @@ function formatPluginLine(plugin: PluginRecord, verbose = false): string { const parts = [ `${name}${idSuffix} ${status}`, - ` source: ${theme.muted(plugin.source)}`, + ` source: ${theme.muted(shortenHomeInString(plugin.source))}`, ` origin: ${plugin.origin}`, ]; if (plugin.version) parts.push(` version: ${plugin.version}`); @@ -201,7 +201,7 @@ export function registerPluginsCli(program: Command) { if (plugin.description) lines.push(plugin.description); lines.push(""); lines.push(`${theme.muted("Status:")} ${plugin.status}`); - lines.push(`${theme.muted("Source:")} ${plugin.source}`); + lines.push(`${theme.muted("Source:")} ${shortenHomeInString(plugin.source)}`); lines.push(`${theme.muted("Origin:")} ${plugin.origin}`); if (plugin.version) lines.push(`${theme.muted("Version:")} ${plugin.version}`); if (plugin.toolNames.length > 0) { @@ -227,9 +227,10 @@ export function registerPluginsCli(program: Command) { lines.push(""); lines.push(`${theme.muted("Install:")} ${install.source}`); if (install.spec) lines.push(`${theme.muted("Spec:")} ${install.spec}`); - if (install.sourcePath) lines.push(`${theme.muted("Source path:")} ${install.sourcePath}`); + if (install.sourcePath) + lines.push(`${theme.muted("Source path:")} ${shortenHomePath(install.sourcePath)}`); if (install.installPath) - lines.push(`${theme.muted("Install path:")} ${install.installPath}`); + lines.push(`${theme.muted("Install path:")} ${shortenHomePath(install.installPath)}`); if (install.version) lines.push(`${theme.muted("Recorded version:")} ${install.version}`); if (install.installedAt) lines.push(`${theme.muted("Installed at:")} ${install.installedAt}`); @@ -333,7 +334,7 @@ export function registerPluginsCli(program: Command) { next = slotResult.config; await writeConfigFile(next); logSlotWarnings(slotResult.warnings); - defaultRuntime.log(`Linked plugin path: ${resolved}`); + defaultRuntime.log(`Linked plugin path: ${shortenHomePath(resolved)}`); defaultRuntime.log(`Restart the gateway to load plugins.`); return; } diff --git a/src/cli/program/config-guard.ts b/src/cli/program/config-guard.ts index eef500c59..eeb3377f2 100644 --- a/src/cli/program/config-guard.ts +++ b/src/cli/program/config-guard.ts @@ -3,6 +3,7 @@ import { loadAndMaybeMigrateDoctorConfig } from "../../commands/doctor-config-fl import { colorize, isRich, theme } from "../../terminal/theme.js"; import type { RuntimeEnv } from "../../runtime.js"; import { formatCliCommand } from "../command-format.js"; +import { shortenHomePath } from "../../utils.js"; const ALLOWED_INVALID_COMMANDS = new Set(["doctor", "logs", "health", "help", "status"]); const ALLOWED_INVALID_GATEWAY_SUBCOMMANDS = new Set([ @@ -60,7 +61,7 @@ export async function ensureConfigReady(params: { const commandText = (value: string) => colorize(rich, theme.command, value); params.runtime.error(heading("Config invalid")); - params.runtime.error(`${muted("File:")} ${muted(snapshot.path)}`); + params.runtime.error(`${muted("File:")} ${muted(shortenHomePath(snapshot.path))}`); if (issues.length > 0) { params.runtime.error(muted("Problem:")); params.runtime.error(issues.map((issue) => ` ${error(issue)}`).join("\n")); diff --git a/src/cli/security-cli.ts b/src/cli/security-cli.ts index 726b5ba03..42bca4ca4 100644 --- a/src/cli/security-cli.ts +++ b/src/cli/security-cli.ts @@ -6,6 +6,7 @@ import { runSecurityAudit } from "../security/audit.js"; import { fixSecurityFootguns } from "../security/fix.js"; import { formatDocsLink } from "../terminal/links.js"; import { isRich, theme } from "../terminal/theme.js"; +import { shortenHomeInString, shortenHomePath } from "../utils.js"; import { formatCliCommand } from "./command-format.js"; type SecurityAuditOptions = { @@ -83,18 +84,24 @@ export function registerSecurityCli(program: Command) { lines.push(""); lines.push(heading("FIX")); for (const change of fixResult.changes) { - lines.push(muted(` ${change}`)); + lines.push(muted(` ${shortenHomeInString(change)}`)); } for (const action of fixResult.actions) { const mode = action.mode.toString(8).padStart(3, "0"); - if (action.ok) lines.push(muted(` chmod ${mode} ${action.path}`)); + if (action.ok) lines.push(muted(` chmod ${mode} ${shortenHomePath(action.path)}`)); else if (action.skipped) - lines.push(muted(` skip chmod ${mode} ${action.path} (${action.skipped})`)); + lines.push( + muted(` skip chmod ${mode} ${shortenHomePath(action.path)} (${action.skipped})`), + ); else if (action.error) - lines.push(muted(` chmod ${mode} ${action.path} failed: ${action.error}`)); + lines.push( + muted(` chmod ${mode} ${shortenHomePath(action.path)} failed: ${action.error}`), + ); } if (fixResult.errors.length > 0) { - for (const err of fixResult.errors) lines.push(muted(` error: ${err}`)); + for (const err of fixResult.errors) { + lines.push(muted(` error: ${shortenHomeInString(err)}`)); + } } } } diff --git a/src/cli/skills-cli.ts b/src/cli/skills-cli.ts index 21c934e17..7488aee83 100644 --- a/src/cli/skills-cli.ts +++ b/src/cli/skills-cli.ts @@ -10,6 +10,7 @@ import { defaultRuntime } from "../runtime.js"; import { formatDocsLink } from "../terminal/links.js"; import { renderTable } from "../terminal/table.js"; import { theme } from "../terminal/theme.js"; +import { shortenHomePath } from "../utils.js"; import { formatCliCommand } from "./command-format.js"; export type SkillsListOptions = { @@ -176,7 +177,7 @@ export function formatSkillInfo( // Details lines.push(theme.heading("Details:")); lines.push(`${theme.muted(" Source:")} ${skill.source}`); - lines.push(`${theme.muted(" Path:")} ${skill.filePath}`); + lines.push(`${theme.muted(" Path:")} ${shortenHomePath(skill.filePath)}`); if (skill.homepage) { lines.push(`${theme.muted(" Homepage:")} ${skill.homepage}`); } diff --git a/src/commands/agents.commands.add.ts b/src/commands/agents.commands.add.ts index 628d7d345..a815a2b87 100644 --- a/src/commands/agents.commands.add.ts +++ b/src/commands/agents.commands.add.ts @@ -12,7 +12,7 @@ import { CONFIG_PATH_CLAWDBOT, writeConfigFile } from "../config/config.js"; import { DEFAULT_AGENT_ID, normalizeAgentId } from "../routing/session-key.js"; import type { RuntimeEnv } from "../runtime.js"; import { defaultRuntime } from "../runtime.js"; -import { resolveUserPath } from "../utils.js"; +import { resolveUserPath, shortenHomePath } from "../utils.js"; import { createClackPrompter } from "../wizard/clack-prompter.js"; import { WizardCancelledError } from "../wizard/prompts.js"; import { @@ -126,7 +126,7 @@ export async function agentsAddCommand( : { config: nextConfig, added: [], skipped: [], conflicts: [] }; await writeConfigFile(bindingResult.config); - if (!opts.json) runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + if (!opts.json) runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); const quietRuntime = opts.json ? createQuietRuntime(runtime) : runtime; await ensureWorkspaceAndSessions(workspaceDir, quietRuntime, { skipBootstrap: Boolean(bindingResult.config.agents?.defaults?.skipBootstrap), @@ -151,8 +151,8 @@ export async function agentsAddCommand( runtime.log(JSON.stringify(payload, null, 2)); } else { runtime.log(`Agent: ${agentId}`); - runtime.log(`Workspace: ${workspaceDir}`); - runtime.log(`Agent dir: ${agentDir}`); + runtime.log(`Workspace: ${shortenHomePath(workspaceDir)}`); + runtime.log(`Agent dir: ${shortenHomePath(agentDir)}`); if (model) runtime.log(`Model: ${model}`); if (bindingResult.conflicts.length > 0) { runtime.error( @@ -334,7 +334,7 @@ export async function agentsAddCommand( } await writeConfigFile(nextConfig); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); await ensureWorkspaceAndSessions(workspaceDir, runtime, { skipBootstrap: Boolean(nextConfig.agents?.defaults?.skipBootstrap), agentId, diff --git a/src/commands/agents.commands.delete.ts b/src/commands/agents.commands.delete.ts index 7d138319c..758f2af36 100644 --- a/src/commands/agents.commands.delete.ts +++ b/src/commands/agents.commands.delete.ts @@ -4,6 +4,7 @@ import { resolveSessionTranscriptsDirForAgent } from "../config/sessions.js"; import { DEFAULT_AGENT_ID, normalizeAgentId } from "../routing/session-key.js"; import type { RuntimeEnv } from "../runtime.js"; import { defaultRuntime } from "../runtime.js"; +import { shortenHomePath } from "../utils.js"; import { createClackPrompter } from "../wizard/clack-prompter.js"; import { createQuietRuntime, requireValidConfig } from "./agents.command-shared.js"; @@ -69,7 +70,7 @@ export async function agentsDeleteCommand( const result = pruneAgentConfig(cfg, agentId); await writeConfigFile(result.config); - if (!opts.json) runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + if (!opts.json) runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); const quietRuntime = opts.json ? createQuietRuntime(runtime) : runtime; await moveToTrash(workspaceDir, quietRuntime); diff --git a/src/commands/agents.commands.identity.ts b/src/commands/agents.commands.identity.ts index 66cbdf7dd..410c3bf4b 100644 --- a/src/commands/agents.commands.identity.ts +++ b/src/commands/agents.commands.identity.ts @@ -9,7 +9,7 @@ import type { IdentityConfig } from "../config/types.js"; import { normalizeAgentId } from "../routing/session-key.js"; import type { RuntimeEnv } from "../runtime.js"; import { defaultRuntime } from "../runtime.js"; -import { resolveUserPath } from "../utils.js"; +import { resolveUserPath, shortenHomePath } from "../utils.js"; import { requireValidConfig } from "./agents.command-shared.js"; import { type AgentIdentity, @@ -105,14 +105,14 @@ export async function agentsSetIdentityCommand( const matches = resolveAgentIdByWorkspace(cfg, workspaceDir); if (matches.length === 0) { runtime.error( - `No agent workspace matches ${workspaceDir}. Pass --agent to target a specific agent.`, + `No agent workspace matches ${shortenHomePath(workspaceDir)}. Pass --agent to target a specific agent.`, ); runtime.exit(1); return; } if (matches.length > 1) { runtime.error( - `Multiple agents match ${workspaceDir}: ${matches.join(", ")}. Pass --agent to choose one.`, + `Multiple agents match ${shortenHomePath(workspaceDir)}: ${matches.join(", ")}. Pass --agent to choose one.`, ); runtime.exit(1); return; @@ -131,7 +131,7 @@ export async function agentsSetIdentityCommand( const targetPath = identityFilePath ?? (workspaceDir ? path.join(workspaceDir, DEFAULT_IDENTITY_FILENAME) : "IDENTITY.md"); - runtime.error(`No identity data found in ${targetPath}.`); + runtime.error(`No identity data found in ${shortenHomePath(targetPath)}.`); runtime.exit(1); return; } @@ -211,11 +211,11 @@ export async function agentsSetIdentityCommand( return; } - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); runtime.log(`Agent: ${agentId}`); if (nextIdentity.name) runtime.log(`Name: ${nextIdentity.name}`); if (nextIdentity.theme) runtime.log(`Theme: ${nextIdentity.theme}`); if (nextIdentity.emoji) runtime.log(`Emoji: ${nextIdentity.emoji}`); if (nextIdentity.avatar) runtime.log(`Avatar: ${nextIdentity.avatar}`); - if (workspaceDir) runtime.log(`Workspace: ${workspaceDir}`); + if (workspaceDir) runtime.log(`Workspace: ${shortenHomePath(workspaceDir)}`); } diff --git a/src/commands/agents.commands.list.ts b/src/commands/agents.commands.list.ts index 938663798..3a45256fe 100644 --- a/src/commands/agents.commands.list.ts +++ b/src/commands/agents.commands.list.ts @@ -3,6 +3,7 @@ import { normalizeAgentId } from "../routing/session-key.js"; import type { RuntimeEnv } from "../runtime.js"; import { defaultRuntime } from "../runtime.js"; import { formatCliCommand } from "../cli/command-format.js"; +import { shortenHomePath } from "../utils.js"; import { describeBinding } from "./agents.bindings.js"; import { requireValidConfig } from "./agents.command-shared.js"; import type { AgentSummary } from "./agents.config.js"; @@ -40,8 +41,8 @@ function formatSummary(summary: AgentSummary) { if (identityLine) { lines.push(` Identity: ${identityLine}${identitySource ? ` (${identitySource})` : ""}`); } - lines.push(` Workspace: ${summary.workspace}`); - lines.push(` Agent dir: ${summary.agentDir}`); + lines.push(` Workspace: ${shortenHomePath(summary.workspace)}`); + lines.push(` Agent dir: ${shortenHomePath(summary.agentDir)}`); if (summary.model) lines.push(` Model: ${summary.model}`); lines.push(` Routing rules: ${summary.bindings}`); diff --git a/src/commands/cleanup-utils.ts b/src/commands/cleanup-utils.ts index a90369f8a..d19d47f14 100644 --- a/src/commands/cleanup-utils.ts +++ b/src/commands/cleanup-utils.ts @@ -4,7 +4,7 @@ import path from "node:path"; import { resolveDefaultAgentWorkspaceDir } from "../agents/workspace.js"; import type { ClawdbotConfig } from "../config/config.js"; import type { RuntimeEnv } from "../runtime.js"; -import { resolveHomeDir, resolveUserPath } from "../utils.js"; +import { resolveHomeDir, resolveUserPath, shortenHomeInString } from "../utils.js"; export type RemovalResult = { ok: boolean; @@ -53,20 +53,21 @@ export async function removePath( if (!target?.trim()) return { ok: false, skipped: true }; const resolved = path.resolve(target); const label = opts?.label ?? resolved; + const displayLabel = shortenHomeInString(label); if (isUnsafeRemovalTarget(resolved)) { - runtime.error(`Refusing to remove unsafe path: ${label}`); + runtime.error(`Refusing to remove unsafe path: ${displayLabel}`); return { ok: false }; } if (opts?.dryRun) { - runtime.log(`[dry-run] remove ${label}`); + runtime.log(`[dry-run] remove ${displayLabel}`); return { ok: true, skipped: true }; } try { await fs.rm(resolved, { recursive: true, force: true }); - runtime.log(`Removed ${label}`); + runtime.log(`Removed ${displayLabel}`); return { ok: true }; } catch (err) { - runtime.error(`Failed to remove ${label}: ${String(err)}`); + runtime.error(`Failed to remove ${displayLabel}: ${String(err)}`); return { ok: false }; } } diff --git a/src/commands/configure.channels.ts b/src/commands/configure.channels.ts index 750df16e9..2eeba4aa2 100644 --- a/src/commands/configure.channels.ts +++ b/src/commands/configure.channels.ts @@ -4,6 +4,7 @@ import type { ClawdbotConfig } from "../config/config.js"; import { CONFIG_PATH_CLAWDBOT } from "../config/config.js"; import type { RuntimeEnv } from "../runtime.js"; import { note } from "../terminal/note.js"; +import { shortenHomePath } from "../utils.js"; import { confirm, select } from "./configure.shared.js"; import { guardCancel } from "./onboard-helpers.js"; @@ -51,7 +52,7 @@ export async function removeChannelConfigWizard( const label = getChannelPlugin(channel)?.meta.label ?? channel; const confirmed = guardCancel( await confirm({ - message: `Delete ${label} configuration from ${CONFIG_PATH_CLAWDBOT}?`, + message: `Delete ${label} configuration from ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}?`, initialValue: false, }), runtime, diff --git a/src/commands/configure.wizard.ts b/src/commands/configure.wizard.ts index e72ddb5a3..597768f70 100644 --- a/src/commands/configure.wizard.ts +++ b/src/commands/configure.wizard.ts @@ -10,7 +10,7 @@ import { ensureControlUiAssetsBuilt } from "../infra/control-ui-assets.js"; import type { RuntimeEnv } from "../runtime.js"; import { defaultRuntime } from "../runtime.js"; import { note } from "../terminal/note.js"; -import { resolveUserPath } from "../utils.js"; +import { resolveUserPath, shortenHomePath } from "../utils.js"; import { createClackPrompter } from "../wizard/clack-prompter.js"; import { WizardCancelledError } from "../wizard/prompts.js"; import { removeChannelConfigWizard } from "./configure.channels.js"; @@ -253,7 +253,7 @@ export async function runConfigureWizard( mode, }); await writeConfigFile(remoteConfig); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); outro("Remote gateway configured."); return; } @@ -286,7 +286,7 @@ export async function runConfigureWizard( mode, }); await writeConfigFile(nextConfig); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); }; if (opts.sections) { diff --git a/src/commands/doctor-platform-notes.ts b/src/commands/doctor-platform-notes.ts index 80634ee30..ac9edb8bc 100644 --- a/src/commands/doctor-platform-notes.ts +++ b/src/commands/doctor-platform-notes.ts @@ -6,6 +6,7 @@ import { promisify } from "node:util"; import type { ClawdbotConfig } from "../config/config.js"; import { note } from "../terminal/note.js"; +import { shortenHomePath } from "../utils.js"; const execFileAsync = promisify(execFile); @@ -19,10 +20,11 @@ export async function noteMacLaunchAgentOverrides() { const hasMarker = fs.existsSync(markerPath); if (!hasMarker) return; + const displayMarkerPath = shortenHomePath(markerPath); const lines = [ - `- LaunchAgent writes are disabled via ${markerPath}.`, + `- LaunchAgent writes are disabled via ${displayMarkerPath}.`, "- To restore default behavior:", - ` rm ${markerPath}`, + ` rm ${displayMarkerPath}`, ].filter((line): line is string => Boolean(line)); note(lines.join("\n"), "Gateway (macOS)"); } diff --git a/src/commands/doctor-state-integrity.ts b/src/commands/doctor-state-integrity.ts index e0820950a..814c99698 100644 --- a/src/commands/doctor-state-integrity.ts +++ b/src/commands/doctor-state-integrity.ts @@ -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); diff --git a/src/commands/doctor-workspace.ts b/src/commands/doctor-workspace.ts index 775498d07..c4b8fcee2 100644 --- a/src/commands/doctor-workspace.ts +++ b/src/commands/doctor-workspace.ts @@ -8,6 +8,7 @@ import { DEFAULT_SOUL_FILENAME, DEFAULT_USER_FILENAME, } from "../agents/workspace.js"; +import { shortenHomePath } from "../utils.js"; export const MEMORY_SYSTEM_PROMPT = [ "Memory system not found in workspace.", @@ -80,8 +81,8 @@ export function detectLegacyWorkspaceDirs(params: { export function formatLegacyWorkspaceWarning(detection: LegacyWorkspaceDetection): string { return [ "Extra workspace directories detected (may contain old agent files):", - ...detection.legacyDirs.map((dir) => `- ${dir}`), - `Active workspace: ${detection.activeWorkspace}`, + ...detection.legacyDirs.map((dir) => `- ${shortenHomePath(dir)}`), + `Active workspace: ${shortenHomePath(detection.activeWorkspace)}`, "If unused, archive or move to Trash (e.g. trash ~/clawdbot).", ].join("\n"); } diff --git a/src/commands/doctor.ts b/src/commands/doctor.ts index 980b7e4b3..9a68cbcfa 100644 --- a/src/commands/doctor.ts +++ b/src/commands/doctor.ts @@ -20,6 +20,7 @@ import type { RuntimeEnv } from "../runtime.js"; import { defaultRuntime } from "../runtime.js"; import { note } from "../terminal/note.js"; import { stylePromptTitle } from "../terminal/prompt-style.js"; +import { shortenHomePath } from "../utils.js"; import { maybeRepairAnthropicOAuthProfileId, noteAuthProfileHealth } from "./doctor-auth.js"; import { loadAndMaybeMigrateDoctorConfig } from "./doctor-config-flow.js"; import { maybeRepairGatewayDaemon } from "./doctor-gateway-daemon-flow.js"; @@ -269,10 +270,10 @@ export async function doctorCommand( if (shouldWriteConfig) { cfg = applyWizardMetadata(cfg, { command: "doctor", mode: resolveMode(cfg) }); await writeConfigFile(cfg); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); const backupPath = `${CONFIG_PATH_CLAWDBOT}.bak`; if (fs.existsSync(backupPath)) { - runtime.log(`Backup: ${backupPath}`); + runtime.log(`Backup: ${shortenHomePath(backupPath)}`); } } else { runtime.log(`Run "${formatCliCommand("clawdbot doctor --fix")}" to apply changes.`); diff --git a/src/commands/models/aliases.ts b/src/commands/models/aliases.ts index d37a52ec6..816a6f3da 100644 --- a/src/commands/models/aliases.ts +++ b/src/commands/models/aliases.ts @@ -1,5 +1,6 @@ import { CONFIG_PATH_CLAWDBOT, loadConfig } from "../../config/config.js"; import type { RuntimeEnv } from "../../runtime.js"; +import { shortenHomePath } from "../../utils.js"; import { ensureFlagCompatibility, normalizeAlias, @@ -74,7 +75,7 @@ export async function modelsAliasesAddCommand( }; }); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); runtime.log(`Alias ${alias} -> ${resolved.provider}/${resolved.model}`); } @@ -105,7 +106,7 @@ export async function modelsAliasesRemoveCommand(aliasRaw: string, runtime: Runt }; }); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); if ( !updated.agents?.defaults?.models || Object.values(updated.agents.defaults.models).every((entry) => !entry?.alias?.trim()) diff --git a/src/commands/models/auth.ts b/src/commands/models/auth.ts index 8a322d2cd..47528c2df 100644 --- a/src/commands/models/auth.ts +++ b/src/commands/models/auth.ts @@ -23,6 +23,7 @@ import { } from "../../config/config.js"; import type { RuntimeEnv } from "../../runtime.js"; import { stylePromptHint, stylePromptMessage } from "../../terminal/prompt-style.js"; +import { shortenHomePath } from "../../utils.js"; import { applyAuthProfileConfig } from "../onboard-auth.js"; import { isRemoteEnvironment } from "../oauth-env.js"; import { openUrl } from "../onboard-helpers.js"; @@ -117,7 +118,7 @@ export async function modelsAuthSetupTokenCommand( }), ); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); runtime.log(`Auth profile: ${CLAUDE_CLI_PROFILE_ID} (anthropic/oauth)`); } @@ -159,7 +160,7 @@ export async function modelsAuthPasteTokenCommand( await updateConfig((cfg) => applyAuthProfileConfig(cfg, { profileId, provider, mode: "token" })); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); runtime.log(`Auth profile: ${profileId} (${provider}/token)`); } @@ -425,7 +426,7 @@ export async function modelsAuthLoginCommand(opts: LoginOptions, runtime: Runtim return next; }); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); for (const profile of result.profiles) { runtime.log( `Auth profile: ${profile.profileId} (${profile.credential.provider}/${credentialMode(profile.credential)})`, diff --git a/src/commands/models/fallbacks.ts b/src/commands/models/fallbacks.ts index 6d239ab2e..ef0cbf152 100644 --- a/src/commands/models/fallbacks.ts +++ b/src/commands/models/fallbacks.ts @@ -1,6 +1,7 @@ import { buildModelAliasIndex, resolveModelRefFromString } from "../../agents/model-selection.js"; import { CONFIG_PATH_CLAWDBOT, loadConfig } from "../../config/config.js"; import type { RuntimeEnv } from "../../runtime.js"; +import { shortenHomePath } from "../../utils.js"; import { DEFAULT_PROVIDER, ensureFlagCompatibility, @@ -78,7 +79,7 @@ export async function modelsFallbacksAddCommand(modelRaw: string, runtime: Runti }; }); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); runtime.log(`Fallbacks: ${(updated.agents?.defaults?.model?.fallbacks ?? []).join(", ")}`); } @@ -124,7 +125,7 @@ export async function modelsFallbacksRemoveCommand(modelRaw: string, runtime: Ru }; }); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); runtime.log(`Fallbacks: ${(updated.agents?.defaults?.model?.fallbacks ?? []).join(", ")}`); } @@ -148,6 +149,6 @@ export async function modelsFallbacksClearCommand(runtime: RuntimeEnv) { }; }); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); runtime.log("Fallback list cleared."); } diff --git a/src/commands/models/image-fallbacks.ts b/src/commands/models/image-fallbacks.ts index 854f41e23..2d5b2520c 100644 --- a/src/commands/models/image-fallbacks.ts +++ b/src/commands/models/image-fallbacks.ts @@ -1,6 +1,7 @@ import { buildModelAliasIndex, resolveModelRefFromString } from "../../agents/model-selection.js"; import { CONFIG_PATH_CLAWDBOT, loadConfig } from "../../config/config.js"; import type { RuntimeEnv } from "../../runtime.js"; +import { shortenHomePath } from "../../utils.js"; import { DEFAULT_PROVIDER, ensureFlagCompatibility, @@ -78,7 +79,7 @@ export async function modelsImageFallbacksAddCommand(modelRaw: string, runtime: }; }); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); runtime.log( `Image fallbacks: ${(updated.agents?.defaults?.imageModel?.fallbacks ?? []).join(", ")}`, ); @@ -126,7 +127,7 @@ export async function modelsImageFallbacksRemoveCommand(modelRaw: string, runtim }; }); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); runtime.log( `Image fallbacks: ${(updated.agents?.defaults?.imageModel?.fallbacks ?? []).join(", ")}`, ); @@ -152,6 +153,6 @@ export async function modelsImageFallbacksClearCommand(runtime: RuntimeEnv) { }; }); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); runtime.log("Image fallback list cleared."); } diff --git a/src/commands/models/list.status-command.ts b/src/commands/models/list.status-command.ts index 7ba356771..0bd8f16e9 100644 --- a/src/commands/models/list.status-command.ts +++ b/src/commands/models/list.status-command.ts @@ -250,7 +250,7 @@ export async function modelsStatusCommand( rawModel && rawModel !== resolvedLabel ? `${resolvedLabel} (from ${rawModel})` : resolvedLabel; runtime.log( - `${label("Config")}${colorize(rich, theme.muted, ":")} ${colorize(rich, theme.info, CONFIG_PATH_CLAWDBOT)}`, + `${label("Config")}${colorize(rich, theme.muted, ":")} ${colorize(rich, theme.info, shortenHomePath(CONFIG_PATH_CLAWDBOT))}`, ); runtime.log( `${label("Agent dir")}${colorize(rich, theme.muted, ":")} ${colorize( diff --git a/src/commands/models/scan.ts b/src/commands/models/scan.ts index 5be4e1b54..f93e7e7c1 100644 --- a/src/commands/models/scan.ts +++ b/src/commands/models/scan.ts @@ -4,6 +4,7 @@ import { type ModelScanResult, scanOpenRouterModels } from "../../agents/model-s import { withProgressTotals } from "../../cli/progress.js"; import { CONFIG_PATH_CLAWDBOT, loadConfig } from "../../config/config.js"; import type { RuntimeEnv } from "../../runtime.js"; +import { shortenHomePath } from "../../utils.js"; import { stylePromptHint, stylePromptMessage, @@ -343,7 +344,7 @@ export async function modelsScanCommand( return; } - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); runtime.log(`Fallbacks: ${selected.join(", ")}`); if (selectedImages.length > 0) { runtime.log(`Image fallbacks: ${selectedImages.join(", ")}`); diff --git a/src/commands/models/set-image.ts b/src/commands/models/set-image.ts index b151af357..518e6c76b 100644 --- a/src/commands/models/set-image.ts +++ b/src/commands/models/set-image.ts @@ -1,6 +1,7 @@ import { CONFIG_PATH_CLAWDBOT } from "../../config/config.js"; import type { RuntimeEnv } from "../../runtime.js"; import { resolveModelTarget, updateConfig } from "./shared.js"; +import { shortenHomePath } from "../../utils.js"; export async function modelsSetImageCommand(modelRaw: string, runtime: RuntimeEnv) { const updated = await updateConfig((cfg) => { @@ -27,6 +28,6 @@ export async function modelsSetImageCommand(modelRaw: string, runtime: RuntimeEn }; }); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); runtime.log(`Image model: ${updated.agents?.defaults?.imageModel?.primary ?? modelRaw}`); } diff --git a/src/commands/models/set.ts b/src/commands/models/set.ts index 347d7fbd8..82d49c440 100644 --- a/src/commands/models/set.ts +++ b/src/commands/models/set.ts @@ -1,6 +1,7 @@ import { CONFIG_PATH_CLAWDBOT } from "../../config/config.js"; import type { RuntimeEnv } from "../../runtime.js"; import { resolveModelTarget, updateConfig } from "./shared.js"; +import { shortenHomePath } from "../../utils.js"; export async function modelsSetCommand(modelRaw: string, runtime: RuntimeEnv) { const updated = await updateConfig((cfg) => { @@ -27,6 +28,6 @@ export async function modelsSetCommand(modelRaw: string, runtime: RuntimeEnv) { }; }); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); runtime.log(`Default model: ${updated.agents?.defaults?.model?.primary ?? modelRaw}`); } diff --git a/src/commands/onboard-helpers.ts b/src/commands/onboard-helpers.ts index bd45bd755..b60511ba8 100644 --- a/src/commands/onboard-helpers.ts +++ b/src/commands/onboard-helpers.ts @@ -18,7 +18,13 @@ import { runCommandWithTimeout } from "../process/exec.js"; import type { RuntimeEnv } from "../runtime.js"; import { stylePromptTitle } from "../terminal/prompt-style.js"; import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js"; -import { CONFIG_DIR, resolveUserPath, sleep } from "../utils.js"; +import { + CONFIG_DIR, + resolveUserPath, + shortenHomeInString, + shortenHomePath, + sleep, +} from "../utils.js"; import { VERSION } from "../version.js"; import type { NodeManagerChoice, OnboardMode, ResetScope } from "./onboard-types.js"; @@ -33,21 +39,21 @@ export function guardCancel(value: T | symbol, runtime: RuntimeEnv): T { export function summarizeExistingConfig(config: ClawdbotConfig): string { const rows: string[] = []; const defaults = config.agents?.defaults; - if (defaults?.workspace) rows.push(`workspace: ${defaults.workspace}`); + if (defaults?.workspace) rows.push(shortenHomeInString(`workspace: ${defaults.workspace}`)); if (defaults?.model) { const model = typeof defaults.model === "string" ? defaults.model : defaults.model.primary; - if (model) rows.push(`model: ${model}`); + if (model) rows.push(shortenHomeInString(`model: ${model}`)); } - if (config.gateway?.mode) rows.push(`gateway.mode: ${config.gateway.mode}`); + if (config.gateway?.mode) rows.push(shortenHomeInString(`gateway.mode: ${config.gateway.mode}`)); if (typeof config.gateway?.port === "number") { - rows.push(`gateway.port: ${config.gateway.port}`); + rows.push(shortenHomeInString(`gateway.port: ${config.gateway.port}`)); } - if (config.gateway?.bind) rows.push(`gateway.bind: ${config.gateway.bind}`); + if (config.gateway?.bind) rows.push(shortenHomeInString(`gateway.bind: ${config.gateway.bind}`)); if (config.gateway?.remote?.url) { - rows.push(`gateway.remote.url: ${config.gateway.remote.url}`); + rows.push(shortenHomeInString(`gateway.remote.url: ${config.gateway.remote.url}`)); } if (config.skills?.install?.nodeManager) { - rows.push(`skills.nodeManager: ${config.skills.install.nodeManager}`); + rows.push(shortenHomeInString(`skills.nodeManager: ${config.skills.install.nodeManager}`)); } return rows.length ? rows.join("\n") : "No key settings detected."; } @@ -220,10 +226,10 @@ export async function ensureWorkspaceAndSessions( dir: workspaceDir, ensureBootstrapFiles: !options?.skipBootstrap, }); - runtime.log(`Workspace OK: ${ws.dir}`); + runtime.log(`Workspace OK: ${shortenHomePath(ws.dir)}`); const sessionsDir = resolveSessionTranscriptsDirForAgent(options?.agentId); await fs.mkdir(sessionsDir, { recursive: true }); - runtime.log(`Sessions OK: ${sessionsDir}`); + runtime.log(`Sessions OK: ${shortenHomePath(sessionsDir)}`); } export function resolveNodeManagerOptions(): Array<{ @@ -246,9 +252,9 @@ export async function moveToTrash(pathname: string, runtime: RuntimeEnv): Promis } try { await runCommandWithTimeout(["trash", pathname], { timeoutMs: 5000 }); - runtime.log(`Moved to Trash: ${pathname}`); + runtime.log(`Moved to Trash: ${shortenHomePath(pathname)}`); } catch { - runtime.log(`Failed to move to Trash (manual delete): ${pathname}`); + runtime.log(`Failed to move to Trash (manual delete): ${shortenHomePath(pathname)}`); } } diff --git a/src/commands/onboard-non-interactive/local.ts b/src/commands/onboard-non-interactive/local.ts index 73c0d1582..ad9ec9623 100644 --- a/src/commands/onboard-non-interactive/local.ts +++ b/src/commands/onboard-non-interactive/local.ts @@ -2,6 +2,7 @@ import type { ClawdbotConfig } from "../../config/config.js"; import { CONFIG_PATH_CLAWDBOT, resolveGatewayPort, writeConfigFile } from "../../config/config.js"; import type { RuntimeEnv } from "../../runtime.js"; import { formatCliCommand } from "../../cli/command-format.js"; +import { shortenHomePath } from "../../utils.js"; import { DEFAULT_GATEWAY_DAEMON_RUNTIME } from "../daemon-runtime.js"; import { healthCommand } from "../health.js"; import { @@ -74,7 +75,7 @@ export async function runNonInteractiveOnboardingLocal(params: { nextConfig = applyWizardMetadata(nextConfig, { command: "onboard", mode }); await writeConfigFile(nextConfig); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); await ensureWorkspaceAndSessions(workspaceDir, runtime, { skipBootstrap: Boolean(nextConfig.agents?.defaults?.skipBootstrap), diff --git a/src/commands/onboard-non-interactive/local/auth-choice.ts b/src/commands/onboard-non-interactive/local/auth-choice.ts index 0412d9c45..6762fb7d2 100644 --- a/src/commands/onboard-non-interactive/local/auth-choice.ts +++ b/src/commands/onboard-non-interactive/local/auth-choice.ts @@ -36,6 +36,7 @@ import { import type { AuthChoice, OnboardOptions } from "../../onboard-types.js"; import { applyOpenAICodexModelDefault } from "../../openai-codex-model-default.js"; import { resolveNonInteractiveApiKey } from "../api-keys.js"; +import { shortenHomePath } from "../../../utils.js"; export async function applyNonInteractiveAuthChoice(params: { nextConfig: ClawdbotConfig; @@ -172,7 +173,7 @@ export async function applyNonInteractiveAuthChoice(params: { const key = resolved.key; const result = upsertSharedEnvVar({ key: "OPENAI_API_KEY", value: key }); process.env.OPENAI_API_KEY = key; - runtime.log(`Saved OPENAI_API_KEY to ${result.path}`); + runtime.log(`Saved OPENAI_API_KEY to ${shortenHomePath(result.path)}`); return nextConfig; } diff --git a/src/commands/onboard-non-interactive/remote.ts b/src/commands/onboard-non-interactive/remote.ts index c28d96418..841fda46b 100644 --- a/src/commands/onboard-non-interactive/remote.ts +++ b/src/commands/onboard-non-interactive/remote.ts @@ -4,6 +4,7 @@ import type { RuntimeEnv } from "../../runtime.js"; import { formatCliCommand } from "../../cli/command-format.js"; import { applyWizardMetadata } from "../onboard-helpers.js"; import type { OnboardOptions } from "../onboard-types.js"; +import { shortenHomePath } from "../../utils.js"; export async function runNonInteractiveOnboardingRemote(params: { opts: OnboardOptions; @@ -33,7 +34,7 @@ export async function runNonInteractiveOnboardingRemote(params: { }; nextConfig = applyWizardMetadata(nextConfig, { command: "onboard", mode }); await writeConfigFile(nextConfig); - runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); const payload = { mode, diff --git a/src/commands/setup.ts b/src/commands/setup.ts index 4c853322e..b1b332e62 100644 --- a/src/commands/setup.ts +++ b/src/commands/setup.ts @@ -7,6 +7,7 @@ import { type ClawdbotConfig, CONFIG_PATH_CLAWDBOT, writeConfigFile } from "../c import { resolveSessionTranscriptsDir } from "../config/sessions.js"; import type { RuntimeEnv } from "../runtime.js"; import { defaultRuntime } from "../runtime.js"; +import { shortenHomePath } from "../utils.js"; async function readConfigFileRaw(): Promise<{ exists: boolean; @@ -54,20 +55,20 @@ export async function setupCommand( await writeConfigFile(next); runtime.log( !existingRaw.exists - ? `Wrote ${CONFIG_PATH_CLAWDBOT}` - : `Updated ${CONFIG_PATH_CLAWDBOT} (set agents.defaults.workspace)`, + ? `Wrote ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}` + : `Updated ${shortenHomePath(CONFIG_PATH_CLAWDBOT)} (set agents.defaults.workspace)`, ); } else { - runtime.log(`Config OK: ${CONFIG_PATH_CLAWDBOT}`); + runtime.log(`Config OK: ${shortenHomePath(CONFIG_PATH_CLAWDBOT)}`); } const ws = await ensureAgentWorkspace({ dir: workspace, ensureBootstrapFiles: !next.agents?.defaults?.skipBootstrap, }); - runtime.log(`Workspace OK: ${ws.dir}`); + runtime.log(`Workspace OK: ${shortenHomePath(ws.dir)}`); const sessionsDir = resolveSessionTranscriptsDir(); await fs.mkdir(sessionsDir, { recursive: true }); - runtime.log(`Sessions OK: ${sessionsDir}`); + runtime.log(`Sessions OK: ${shortenHomePath(sessionsDir)}`); } diff --git a/src/terminal/table.ts b/src/terminal/table.ts index 154e591e1..0a9f754c6 100644 --- a/src/terminal/table.ts +++ b/src/terminal/table.ts @@ -1,4 +1,5 @@ import { visibleWidth } from "./ansi.js"; +import { shortenHomeInString } from "../utils.js"; type Align = "left" | "right" | "center"; @@ -168,11 +169,18 @@ function normalizeWidth(n: number | undefined): number | undefined { } export function renderTable(opts: RenderTableOptions): string { + const rows = opts.rows.map((row) => { + const next: Record = {}; + for (const [key, value] of Object.entries(row)) { + next[key] = shortenHomeInString(value); + } + return next; + }); const border = opts.border ?? "unicode"; if (border === "none") { const columns = opts.columns; const header = columns.map((c) => c.header).join(" | "); - const lines = [header, ...opts.rows.map((r) => columns.map((c) => r[c.key] ?? "").join(" | "))]; + const lines = [header, ...rows.map((r) => columns.map((c) => r[c.key] ?? "").join(" | "))]; return `${lines.join("\n")}\n`; } @@ -181,7 +189,7 @@ export function renderTable(opts: RenderTableOptions): string { const metrics = columns.map((c) => { const headerW = visibleWidth(c.header); - const cellW = Math.max(0, ...opts.rows.map((r) => visibleWidth(r[c.key] ?? ""))); + const cellW = Math.max(0, ...rows.map((r) => visibleWidth(r[c.key] ?? ""))); return { headerW, cellW }; }); @@ -328,7 +336,7 @@ export function renderTable(opts: RenderTableOptions): string { lines.push(hLine(box.tl, box.t, box.tr)); lines.push(...renderRow({}, true)); lines.push(hLine(box.ml, box.m, box.mr)); - for (const row of opts.rows) { + for (const row of rows) { lines.push(...renderRow(row, false)); } lines.push(hLine(box.bl, box.b, box.br));