mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-08-24 10:14:19 -05:00
fix plan mode branding and plan path (#1062)
This commit is contained in:
@@ -13,7 +13,7 @@ export function RejectedPlanMessage(t0) {
|
||||
} = t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = <Text color="subtle">User rejected Claude's plan:</Text>;
|
||||
t1 = <Text color="subtle">User rejected the plan:</Text>;
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
|
||||
+9
-5
@@ -23,7 +23,7 @@ import { toIDEDisplayName } from '../../../utils/ide.js';
|
||||
import { logError } from '../../../utils/log.js';
|
||||
import { enqueuePendingNotification } from '../../../utils/messageQueueManager.js';
|
||||
import { createUserMessage } from '../../../utils/messages.js';
|
||||
import { getMainLoopModel, getRuntimeMainLoopModel } from '../../../utils/model/model.js';
|
||||
import { getMainLoopModel, getRuntimeMainLoopModel, modelDisplayString } from '../../../utils/model/model.js';
|
||||
import { createPromptRuleContent, isClassifierPermissionsEnabled, PROMPT_PREFIX } from '../../../utils/permissions/bashClassifier.js';
|
||||
import { type PermissionMode, toExternalPermissionMode } from '../../../utils/permissions/PermissionMode.js';
|
||||
import type { PermissionUpdate } from '../../../utils/permissions/PermissionUpdateSchema.js';
|
||||
@@ -148,14 +148,16 @@ export function ExitPlanModePermissionRequest({
|
||||
isAutoModeAvailable,
|
||||
isBypassPermissionsModeAvailable
|
||||
} = toolPermissionContext;
|
||||
const planAuthorName = modelDisplayString(toolUseConfirm.assistantMessage.message.model);
|
||||
const options = useMemo(() => buildPlanApprovalOptions({
|
||||
showClearContext,
|
||||
showUltraplan,
|
||||
usedPercent: showClearContext ? getContextUsedPercent(usage, mode) : null,
|
||||
isAutoModeAvailable,
|
||||
isBypassPermissionsModeAvailable,
|
||||
planAuthorName,
|
||||
onFeedbackChange: setPlanFeedback
|
||||
}), [showClearContext, showUltraplan, usage, mode, isAutoModeAvailable, isBypassPermissionsModeAvailable]);
|
||||
}), [showClearContext, showUltraplan, usage, mode, isAutoModeAvailable, isBypassPermissionsModeAvailable, planAuthorName]);
|
||||
function onImagePaste(base64Image: string, mediaType?: string, filename?: string, dimensions?: ImageDimensions, _sourcePath?: string) {
|
||||
const pasteId = nextPasteIdRef.current++;
|
||||
const newContent: PastedContent = {
|
||||
@@ -627,7 +629,7 @@ export function ExitPlanModePermissionRequest({
|
||||
<PermissionDialog color="planMode" title="Ready to code?" innerPaddingX={0} workerBadge={workerBadge}>
|
||||
<Box flexDirection="column" marginTop={1}>
|
||||
<Box paddingX={1} flexDirection="column">
|
||||
<Text>Here is Claude's plan:</Text>
|
||||
<Text>Here is {planAuthorName}'s plan:</Text>
|
||||
</Box>
|
||||
<Box borderColor="subtle" borderStyle="dashed" flexDirection="column" borderLeft={false} borderRight={false} paddingX={1} marginBottom={1}
|
||||
// Necessary for Windows Terminal to render properly
|
||||
@@ -644,7 +646,7 @@ export function ExitPlanModePermissionRequest({
|
||||
</Box>}
|
||||
{!useStickyFooter && <>
|
||||
<Text dimColor>
|
||||
Claude has written up a plan and is ready to execute. Would
|
||||
{planAuthorName} has written up a plan and is ready to execute. Would
|
||||
you like to proceed?
|
||||
</Text>
|
||||
<Box marginTop={1}>
|
||||
@@ -677,6 +679,7 @@ export function buildPlanApprovalOptions({
|
||||
usedPercent,
|
||||
isAutoModeAvailable,
|
||||
isBypassPermissionsModeAvailable,
|
||||
planAuthorName,
|
||||
onFeedbackChange
|
||||
}: {
|
||||
showClearContext: boolean;
|
||||
@@ -684,6 +687,7 @@ export function buildPlanApprovalOptions({
|
||||
usedPercent: number | null;
|
||||
isAutoModeAvailable: boolean | undefined;
|
||||
isBypassPermissionsModeAvailable: boolean | undefined;
|
||||
planAuthorName: string;
|
||||
onFeedbackChange: (v: string) => void;
|
||||
}): OptionWithDescription<ResponseValue>[] {
|
||||
const options: OptionWithDescription<ResponseValue>[] = [];
|
||||
@@ -738,7 +742,7 @@ export function buildPlanApprovalOptions({
|
||||
type: 'input',
|
||||
label: 'No, keep planning',
|
||||
value: 'no',
|
||||
placeholder: 'Tell Claude what to change',
|
||||
placeholder: `Tell ${planAuthorName} what to change`,
|
||||
description: 'shift+tab to approve with this feedback',
|
||||
onChange: onFeedbackChange
|
||||
});
|
||||
|
||||
@@ -55,7 +55,7 @@ export function renderToolResultMessage(output: Output, _progressMessagesForMess
|
||||
return <Box flexDirection="column" marginTop={1}>
|
||||
<Box flexDirection="row">
|
||||
<Text color={getModeColor('plan')}>{BLACK_CIRCLE}</Text>
|
||||
<Text> User approved Claude's plan</Text>
|
||||
<Text> User approved the plan</Text>
|
||||
</Box>
|
||||
<MessageResponse>
|
||||
<Box flexDirection="column">
|
||||
|
||||
@@ -11,6 +11,7 @@ import * as lockfile from './lockfile.js'
|
||||
import { logError } from './log.js'
|
||||
import { cleanupOldVersions } from './nativeInstaller/index.js'
|
||||
import { cleanupOldPastes } from './pasteStore.js'
|
||||
import { getDefaultPlansDirectory } from './plans.js'
|
||||
import { getProjectsDir } from './sessionStorage.js'
|
||||
import { getSettingsWithAllErrors } from './settings/allErrors.js'
|
||||
import {
|
||||
@@ -298,8 +299,7 @@ async function cleanupSingleDirectory(
|
||||
}
|
||||
|
||||
export function cleanupOldPlanFiles(): Promise<CleanupResult> {
|
||||
const plansDir = join(getClaudeConfigHomeDir(), 'plans')
|
||||
return cleanupSingleDirectory(plansDir, '.md')
|
||||
return cleanupSingleDirectory(getDefaultPlansDirectory(), '.md')
|
||||
}
|
||||
|
||||
export async function cleanupOldFileHistoryBackups(): Promise<CleanupResult> {
|
||||
|
||||
@@ -18,6 +18,10 @@ async function importFreshLocalInstaller() {
|
||||
return import(`./localInstaller.ts?ts=${Date.now()}-${Math.random()}`)
|
||||
}
|
||||
|
||||
async function importFreshPlans() {
|
||||
return import(`./plans.ts?ts=${Date.now()}-${Math.random()}`)
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
process.env = { ...originalEnv }
|
||||
process.argv = [...originalArgv]
|
||||
@@ -51,6 +55,39 @@ describe('OpenClaude paths', () => {
|
||||
).toBe(join(homedir(), '.claude'))
|
||||
})
|
||||
|
||||
test('default plans directory uses ~/.openclaude/plans', async () => {
|
||||
delete process.env.CLAUDE_CONFIG_DIR
|
||||
const { getDefaultPlansDirectory } = await importFreshPlans()
|
||||
|
||||
expect(getDefaultPlansDirectory({ homeDir: homedir() })).toBe(
|
||||
join(homedir(), '.openclaude', 'plans'),
|
||||
)
|
||||
})
|
||||
|
||||
test('default plans directory respects explicit CLAUDE_CONFIG_DIR', async () => {
|
||||
const { getDefaultPlansDirectory } = await importFreshPlans()
|
||||
|
||||
expect(
|
||||
getDefaultPlansDirectory({ configDirEnv: '/tmp/custom-openclaude' }),
|
||||
).toBe(join('/tmp/custom-openclaude', 'plans'))
|
||||
})
|
||||
|
||||
test('default plans directory normalizes generated path to NFC', async () => {
|
||||
const { getDefaultPlansDirectory } = await importFreshPlans()
|
||||
|
||||
expect(
|
||||
getDefaultPlansDirectory({ homeDir: '/tmp/cafe\u0301' }),
|
||||
).toBe(join('/tmp/caf\u00e9', '.openclaude', 'plans'))
|
||||
})
|
||||
|
||||
test('default plans directory normalizes explicit CLAUDE_CONFIG_DIR to NFC', async () => {
|
||||
const { getDefaultPlansDirectory } = await importFreshPlans()
|
||||
|
||||
expect(
|
||||
getDefaultPlansDirectory({ configDirEnv: '/tmp/cafe\u0301-openclaude' }),
|
||||
).toBe(join('/tmp/caf\u00e9-openclaude', 'plans'))
|
||||
})
|
||||
|
||||
test('uses CLAUDE_CONFIG_DIR override when provided', async () => {
|
||||
process.env.CLAUDE_CONFIG_DIR = '/tmp/custom-openclaude'
|
||||
const { getClaudeConfigHomeDir, resolveClaudeConfigHomeDir } =
|
||||
|
||||
+16
-3
@@ -1,6 +1,7 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import { copyFile, writeFile } from 'fs/promises'
|
||||
import memoize from 'lodash-es/memoize.js'
|
||||
import { homedir } from 'os'
|
||||
import { join, resolve, sep } from 'path'
|
||||
import type { AgentId, SessionId } from 'src/types/ids.js'
|
||||
import type { LogOption } from 'src/types/logs.js'
|
||||
@@ -14,7 +15,6 @@ import { getPlanSlugCache, getSessionId } from '../bootstrap/state.js'
|
||||
import { EXIT_PLAN_MODE_V2_TOOL_NAME } from '../tools/ExitPlanModeTool/constants.js'
|
||||
import { getCwd } from './cwd.js'
|
||||
import { logForDebugging } from './debug.js'
|
||||
import { getClaudeConfigHomeDir } from './envUtils.js'
|
||||
import { isENOENT } from './errors.js'
|
||||
import { getEnvironmentKind } from './filePersistence/outputsScanner.js'
|
||||
import { getFsImplementation } from './fsOperations.js'
|
||||
@@ -24,6 +24,19 @@ import { generateWordSlug } from './words.js'
|
||||
|
||||
const MAX_SLUG_RETRIES = 10
|
||||
|
||||
export function getDefaultPlansDirectory({
|
||||
configDirEnv = process.env.CLAUDE_CONFIG_DIR,
|
||||
homeDir = homedir(),
|
||||
}: {
|
||||
configDirEnv?: string
|
||||
homeDir?: string
|
||||
} = {}): string {
|
||||
if (configDirEnv) {
|
||||
return join(configDirEnv.normalize('NFC'), 'plans')
|
||||
}
|
||||
return join(homeDir, '.openclaude', 'plans').normalize('NFC')
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or generate a word slug for the current session's plan.
|
||||
* The slug is generated lazily on first access and cached for the session.
|
||||
@@ -91,13 +104,13 @@ export const getPlansDirectory = memoize(function getPlansDirectory(): string {
|
||||
logError(
|
||||
new Error(`plansDirectory must be within project root: ${settingsDir}`),
|
||||
)
|
||||
plansPath = join(getClaudeConfigHomeDir(), 'plans')
|
||||
plansPath = getDefaultPlansDirectory()
|
||||
} else {
|
||||
plansPath = resolved
|
||||
}
|
||||
} else {
|
||||
// Default
|
||||
plansPath = join(getClaudeConfigHomeDir(), 'plans')
|
||||
plansPath = getDefaultPlansDirectory()
|
||||
}
|
||||
|
||||
// Ensure directory exists (mkdirSync with recursive: true is a no-op if it exists)
|
||||
|
||||
@@ -856,7 +856,7 @@ export const SettingsSchema = lazySchema(() =>
|
||||
.optional()
|
||||
.describe(
|
||||
'Custom directory for plan files, relative to project root. ' +
|
||||
'If not set, defaults to ~/.claude/plans/',
|
||||
'If not set, defaults to ~/.openclaude/plans/',
|
||||
),
|
||||
...(process.env.USER_TYPE === 'ant'
|
||||
? {
|
||||
|
||||
Reference in New Issue
Block a user