From e915dd2922ec0eb8bb3f04e516aa014253165b13 Mon Sep 17 00:00:00 2001 From: Joe Fleming Date: Fri, 13 Mar 2026 20:05:44 -0600 Subject: [PATCH] chore: better welcome messgae, throw on config parse error add instructions for gateway mode, change config order, and don't hard-code the config path, pull it from the default config --- src/cli/onboard.ts | 24 ++++++++++++++++-------- src/config/loader.ts | 4 ++-- src/config/types.ts | 4 ++-- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/cli/onboard.ts b/src/cli/onboard.ts index 68f0913..f90bd68 100644 --- a/src/cli/onboard.ts +++ b/src/cli/onboard.ts @@ -5,13 +5,19 @@ import pc from 'picocolors'; import { ConfigSchema, type Config } from '../config/types.ts'; import { ensureWorkspace, resolvePath, checkWorkspaceEmpty, syncTemplates } from './utils.ts'; +function logCreated(item: string) { + console.info(pc.green(` ✓ Created ${item}`)); +} + export function onboardCommand(program: Command): void { program .command('onboard [path]') .description('Initialize a new nanobot workspace with config and templates') .action(async (rawPath?: string) => { try { - const targetPath = resolvePath(rawPath ?? '~/.config/nanobot'); + const defaultConfig: Config = ConfigSchema.parse({}); + + const targetPath = resolvePath(rawPath ?? defaultConfig.agent.workspacePath); const configPath = join(targetPath, 'config.json'); console.info(pc.blue('Initializing nanobot workspace...')); @@ -21,18 +27,17 @@ export function onboardCommand(program: Command): void { checkWorkspaceEmpty(targetPath); // Create workspace directory - ensureWorkspace(targetPath); - console.info(pc.green('✓ Created workspace directory')); + ensureWorkspace(targetPath, true); + logCreated('workspace directory') // Write default config - const defaultConfig: Config = ConfigSchema.parse({}); writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2), 'utf8'); - console.info(pc.green('✓ Created config.json')); + logCreated('config.json') // Sync templates const createdFiles = syncTemplates(targetPath); for (const file of createdFiles) { - console.info(pc.dim(` Created ${file}`)); + logCreated(file) } console.info(); @@ -40,10 +45,13 @@ export function onboardCommand(program: Command): void { console.info(); console.info(pc.bold('Next steps:')); console.info(` 1. Edit ${pc.cyan(configPath)} to add your API keys`); - console.info(` 2. Customize ${pc.cyan(join(targetPath, 'USER.md'))} with your preferences`); + console.info(` 2. Customize ${pc.cyan(join(targetPath, 'USER.md'))} and ${pc.cyan(join(targetPath, 'SOUL.md'))} with your preferences`); console.info(` 3. Start chatting: ${pc.cyan('bun run nanobot agent')}`); console.info(); - console.info(pc.dim('For Mattermost integration, configure the channels.mattermost section in config.json')); + console.info(` -- For gateway mode:`); + console.info(` 1. Edit ${pc.cyan(configPath)} to add your channel config (Mattermost)`); + console.info(` 2. Connect your agent: ${pc.cyan('bun run nanobot gateway')}`); + console.info(); } catch (err) { console.error(pc.red(String(err))); process.exit(1); diff --git a/src/config/loader.ts b/src/config/loader.ts index 55f1cab..6ce3406 100644 --- a/src/config/loader.ts +++ b/src/config/loader.ts @@ -20,9 +20,9 @@ export function loadConfig(configPath?: string): Config { let json: unknown; try { json = JSON.parse(raw); - } catch { + } catch(error) { console.error(`Failed to parse config at ${path}`); - return ConfigSchema.parse({}); + throw error } // Apply NANOBOT_ env var overrides before validation diff --git a/src/config/types.ts b/src/config/types.ts index c40646f..efa51d3 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -112,6 +112,7 @@ export type HeartbeatConfig = z.infer; // --------------------------------------------------------------------------- export const ConfigSchema = z.object({ + providers: ProvidersConfigSchema.default(() => ({})), agent: AgentConfigSchema.default(() => ({ model: 'anthropic/claude-sonnet-4-5', workspacePath: '~/.config/nanobot', @@ -120,13 +121,12 @@ export const ConfigSchema = z.object({ temperature: 0.7, maxToolIterations: 40, })), - providers: ProvidersConfigSchema.default(() => ({})), + heartbeat: HeartbeatConfigSchema.default(() => ({ enabled: false, intervalMinutes: 30 })), channels: ChannelsConfigSchema.default(() => ({ sendProgress: true, sendToolHints: true })), tools: ToolsConfigSchema.default(() => ({ exec: { timeout: 120, denyPatterns: [], restrictToWorkspace: false }, web: {}, restrictToWorkspace: false, })), - heartbeat: HeartbeatConfigSchema.default(() => ({ enabled: false, intervalMinutes: 30 })), }); export type Config = z.infer;