diff --git a/package.json b/package.json index 65f7fef0..75bd7e70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "better-clawd", - "version": "0.1.1", + "version": "0.1.4", "description": "Claude Code, but better.", "type": "module", "bin": { diff --git a/scripts/build.ts b/scripts/build.ts index 99d92490..d9cab81d 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -13,7 +13,7 @@ const result = await Bun.build({ minify: false, naming: 'cli.mjs', define: { - 'MACRO.VERSION': JSON.stringify('99.0.0'), + 'MACRO.VERSION': JSON.stringify(version), 'MACRO.DISPLAY_VERSION': JSON.stringify(version), 'MACRO.BUILD_TIME': JSON.stringify(new Date().toISOString()), 'MACRO.ISSUES_EXPLAINER': JSON.stringify( diff --git a/src/components/Onboarding.tsx b/src/components/Onboarding.tsx index d4b62663..5bbb83cb 100644 --- a/src/components/Onboarding.tsx +++ b/src/components/Onboarding.tsx @@ -2,6 +2,7 @@ import { c as _c } from "react/compiler-runtime"; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; import { setupTerminal, shouldOfferTerminalSetup } from '../commands/terminalSetup/terminalSetup.js'; +import { Login } from '../commands/login/login.js'; import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js'; import { Box, Link, Newline, Text, useTheme } from '../ink.js'; import { useKeybindings } from '../keybindings/useKeybinding.js'; @@ -13,7 +14,6 @@ import { isRunningOnHomespace } from '../utils/envUtils.js'; import { PreflightStep } from '../utils/preflightChecks.js'; import type { ThemeSetting } from '../utils/theme.js'; import { ApproveApiKey } from './ApproveApiKey.js'; -import { ConsoleOAuthFlow } from './ConsoleOAuthFlow.js'; import { Select } from './CustomSelect/select.js'; import { WelcomeV2 } from './LogoV2/WelcomeV2.js'; import { PressEnterToContinue } from './PressEnterToContinue.js'; @@ -134,7 +134,13 @@ export function Onboarding({ steps.push({ id: 'oauth', component: - + { + if (success) { + goToNextStep(); + } + }} /> }); } diff --git a/src/constants/product.ts b/src/constants/product.ts index bdc4f608..26043879 100644 --- a/src/constants/product.ts +++ b/src/constants/product.ts @@ -25,10 +25,7 @@ export const CLAUDE_AI_STAGING_BASE_URL = ANTHROPIC_APP_STAGING_BASE_URL export const CLAUDE_AI_LOCAL_BASE_URL = ANTHROPIC_APP_LOCAL_BASE_URL export function getConfiguredProductConfigDir(): string | undefined { - return ( - process.env[PRODUCT_CONFIG_ENV_VAR] ?? - process.env[LEGACY_PRODUCT_CONFIG_ENV_VAR] - ) + return process.env[PRODUCT_CONFIG_ENV_VAR] } /** diff --git a/src/main.tsx b/src/main.tsx index 32119111..ca7faf72 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -27,7 +27,11 @@ import pickBy from 'lodash-es/pickBy.js'; import uniqBy from 'lodash-es/uniqBy.js'; import React from 'react'; import { getOauthConfig } from './constants/oauth.js'; -import { getRemoteSessionUrl } from './constants/product.js'; +import { + CLI_BINARY_NAME, + getRemoteSessionUrl, + PRODUCT_NAME, +} from './constants/product.js'; import { getSystemContext, getUserContext } from './context.js'; import { init, initializeTelemetryAfterTrust } from './entrypoints/init.js'; import { addToHistory } from './history.js'; @@ -952,7 +956,7 @@ async function run(): Promise { // terminal shell integration may mirror the process name to the tab. // After init() so settings.json env can also gate this (gh-4765). if (!isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_TERMINAL_TITLE)) { - process.title = 'claude'; + process.title = CLI_BINARY_NAME; } // Attach logging sinks so subcommand handlers can use logEvent/logError. @@ -997,7 +1001,7 @@ async function run(): Promise { } profileCheckpoint('preAction_after_settings_sync'); }); - program.name('claude').description(`Claude Code - starts an interactive session by default, use -p/--print for non-interactive output`).argument('[prompt]', 'Your prompt', String) + program.name(CLI_BINARY_NAME).description(`${PRODUCT_NAME} starts an interactive session by default. Use -p/--print for non-interactive output.`).argument('[prompt]', 'Your prompt', String) // Subcommands inherit helpOption via commander's copyInheritedSettings — // setting it once here covers mcp, plugin, auth, and all other subcommands. .helpOption('-h, --help', 'Display help for command').option('-d, --debug [filter]', 'Enable debug mode with optional category filtering (e.g., "api,hooks" or "!1p,!file")', (_value: string | true) => { @@ -1051,7 +1055,7 @@ async function run(): Promise { if (prompt === 'code') { logEvent('tengu_code_prompt_ignored', {}); // biome-ignore lint/suspicious/noConsole:: intentional console output - console.warn(chalk.yellow('Tip: You can launch Claude Code with just `claude`')); + console.warn(chalk.yellow(`Tip: You can launch ${PRODUCT_NAME} with just \`${CLI_BINARY_NAME}\``)); prompt = undefined; } @@ -3835,7 +3839,7 @@ async function run(): Promise { pendingHookMessages }, renderAndRun); } - }).version(`${MACRO.VERSION} (Claude Code)`, '-v, --version', 'Output the version number'); + }).version(`${MACRO.VERSION} (${PRODUCT_NAME})`, '-v, --version', 'Output the version number'); // Worktree flags program.option('-w, --worktree [name]', 'Create a new git worktree for this session (optionally specify a name)'); diff --git a/src/screens/REPL.tsx b/src/screens/REPL.tsx index 1fdf1e8e..2c9a55ca 100644 --- a/src/screens/REPL.tsx +++ b/src/screens/REPL.tsx @@ -68,6 +68,7 @@ import { useSkillImprovementSurvey } from '../hooks/useSkillImprovementSurvey.js import { useMoreRight } from '../moreright/useMoreRight.js'; import { SpinnerWithVerb, BriefIdleStatus, type SpinnerMode } from '../components/Spinner.js'; import { getSystemPrompt } from '../constants/prompts.js'; +import { PRODUCT_CONFIG_DIRNAME, PRODUCT_NAME } from '../constants/product.js'; import { buildEffectiveSystemPrompt } from '../utils/systemPrompt.js'; import { getSystemContext, getUserContext } from '../context.js'; import { getMemoryFiles } from '../utils/claudemd.js'; @@ -1176,7 +1177,7 @@ export function REPL({ // session from mid-conversation context. const haikuTitleAttemptedRef = useRef((initialMessages?.length ?? 0) > 0); const agentTitle = mainThreadAgentDefinition?.agentType; - const terminalTitle = sessionTitle ?? agentTitle ?? haikuTitle ?? 'Claude Code'; + const terminalTitle = sessionTitle ?? agentTitle ?? haikuTitle ?? PRODUCT_NAME; const isWaitingForApproval = toolUseConfirmQueue.length > 0 || promptQueue.length > 0 || pendingWorkerRequest || pendingSandboxRequest; // Local-jsx commands (like /plugin, /config) show user-facing dialogs that // wait for input. Require jsx != null — if the flag is stuck true but jsx @@ -1692,7 +1693,7 @@ export function REPL({ if (wt.creationDurationMs < 15_000) return; worktreeTipShownRef.current = true; const secs = Math.round(wt.creationDurationMs / 1000); - setMessages(prev => [...prev, createSystemMessage(`Worktree creation took ${secs}s. For large repos, set \`worktree.sparsePaths\` in .claude/settings.json to check out only the directories you need — e.g. \`{"worktree": {"sparsePaths": ["src", "packages/foo"]}}\`.`, 'info')]); + setMessages(prev => [...prev, createSystemMessage(`Worktree creation took ${secs}s. For large repos, set \`worktree.sparsePaths\` in \`${PRODUCT_CONFIG_DIRNAME}/settings.json\` to check out only the directories you need — e.g. \`{"worktree": {"sparsePaths": ["src", "packages/foo"]}}\`.`, 'info')]); }, [setMessages]); // Hide spinner when the only in-progress tool is Sleep @@ -4193,7 +4194,7 @@ export function REPL({ useEffect(() => { const handleSuspend = () => { // Print suspension instructions - process.stdout.write(`\nClaude Code has been suspended. Run \`fg\` to bring Claude Code back.\nNote: ctrl + z now suspends Claude Code, ctrl + _ undoes input.\n`); + process.stdout.write(`\n${PRODUCT_NAME} has been suspended. Run \`fg\` to bring ${PRODUCT_NAME} back.\nNote: ctrl + z now suspends ${PRODUCT_NAME}, ctrl + _ undoes input.\n`); }; const handleResume = () => { // Force complete component tree replacement instead of terminal clear diff --git a/src/utils/env.ts b/src/utils/env.ts index bfaa40fe..3d038919 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -3,12 +3,11 @@ import { homedir } from 'os' import { join } from 'path' import { getConfiguredProductConfigDir, - LEGACY_PRODUCT_SLUG, PRODUCT_SLUG, } from '../constants/product.js' import { fileSuffixForOauthConfig } from '../constants/oauth.js' import { isRunningWithBun } from './bundledMode.js' -import { getClaudeConfigHomeDir, isEnvTruthy } from './envUtils.js' +import { isEnvTruthy } from './envUtils.js' import { findExecutable } from './findExecutable.js' import { getFsImplementation } from './fsOperations.js' import { which } from './which.js' @@ -17,27 +16,15 @@ type Platform = 'win32' | 'darwin' | 'linux' // Config and data paths export const getGlobalClaudeFile = memoize((): string => { - const configHomeDir = getClaudeConfigHomeDir() const configDirOverride = getConfiguredProductConfigDir() - // Legacy fallback for backwards compatibility - const legacyConfigPath = join(configHomeDir, '.config.json') - if (getFsImplementation().existsSync(legacyConfigPath)) { - return legacyConfigPath - } - const suffix = fileSuffixForOauthConfig() const preferredFilename = `.${PRODUCT_SLUG}${suffix}.json` - const legacyFilename = `.${LEGACY_PRODUCT_SLUG}${suffix}.json` const preferredPath = join(configDirOverride || homedir(), preferredFilename) - const legacyPath = join(configDirOverride || homedir(), legacyFilename) if (getFsImplementation().existsSync(preferredPath)) { return preferredPath } - if (getFsImplementation().existsSync(legacyPath)) { - return legacyPath - } return preferredPath }) diff --git a/src/utils/envUtils.ts b/src/utils/envUtils.ts index 3f65f52f..94cf248e 100644 --- a/src/utils/envUtils.ts +++ b/src/utils/envUtils.ts @@ -4,12 +4,11 @@ import { homedir } from 'os' import { join } from 'path' import { getConfiguredProductConfigDir, - LEGACY_PRODUCT_CONFIG_DIRNAME, PRODUCT_CONFIG_DIRNAME, } from '../constants/product.js' -// Memoized: 150+ callers, many on hot paths. Keyed off CLAUDE_CONFIG_DIR so -// tests that change the env var get a fresh value without explicit cache.clear. +// Memoized: 150+ callers, many on hot paths. Keyed off BETTER_CLAWD_CONFIG_DIR +// so tests that change the env var get a fresh value without explicit cache.clear. export const getClaudeConfigHomeDir = memoize( (): string => { const configuredDir = getConfiguredProductConfigDir() @@ -18,14 +17,10 @@ export const getClaudeConfigHomeDir = memoize( } const betterClawdDir = join(homedir(), PRODUCT_CONFIG_DIRNAME) - const legacyClaudeDir = join(homedir(), LEGACY_PRODUCT_CONFIG_DIRNAME) if (existsSync(betterClawdDir)) { return betterClawdDir.normalize('NFC') } - if (existsSync(legacyClaudeDir)) { - return legacyClaudeDir.normalize('NFC') - } return betterClawdDir.normalize('NFC') }, diff --git a/src/utils/secureStorage/macOsKeychainHelpers.ts b/src/utils/secureStorage/macOsKeychainHelpers.ts index cd07550a..3397bcbd 100644 --- a/src/utils/secureStorage/macOsKeychainHelpers.ts +++ b/src/utils/secureStorage/macOsKeychainHelpers.ts @@ -19,7 +19,6 @@ import { userInfo } from 'os' import { getOauthConfig } from 'src/constants/oauth.js' import { getConfiguredProductConfigDir, - LEGACY_PRODUCT_NAME, PRODUCT_NAME, } from 'src/constants/product.js' import { getClaudeConfigHomeDir } from '../envUtils.js' @@ -33,7 +32,6 @@ export const CREDENTIALS_SERVICE_SUFFIX = '-credentials' export function getMacOsKeychainStorageServiceName( serviceSuffix: string = '', - opts: { legacy?: boolean } = {}, ): string { const configDir = getClaudeConfigHomeDir() const isDefaultDir = !getConfiguredProductConfigDir() @@ -43,18 +41,13 @@ export function getMacOsKeychainStorageServiceName( const dirHash = isDefaultDir ? '' : `-${createHash('sha256').update(configDir).digest('hex').substring(0, 8)}` - const baseName = opts.legacy ? LEGACY_PRODUCT_NAME : PRODUCT_NAME - return `${baseName}${getOauthConfig().OAUTH_FILE_SUFFIX}${serviceSuffix}${dirHash}` + return `${PRODUCT_NAME}${getOauthConfig().OAUTH_FILE_SUFFIX}${serviceSuffix}${dirHash}` } export function getMacOsKeychainStorageServiceNames( serviceSuffix: string = '', ): string[] { - const names = [ - getMacOsKeychainStorageServiceName(serviceSuffix), - getMacOsKeychainStorageServiceName(serviceSuffix, { legacy: true }), - ] - return Array.from(new Set(names)) + return [getMacOsKeychainStorageServiceName(serviceSuffix)] } export function getUsername(): string {