Skip to content

feat: configurable Claude home directory and runtime model detection#264

Open
pennn778 wants to merge 8 commits intoYishenTu:mainfrom
pennn778:main
Open

feat: configurable Claude home directory and runtime model detection#264
pennn778 wants to merge 8 commits intoYishenTu:mainfrom
pennn778:main

Conversation

@pennn778
Copy link

Summary

  • Configurable Claude home directory: Replace all hardcoded .claude path references with a centralized claudePaths module that reads a configurable directory name. This allows users of custom CLI builds (e.g., claude-internal) that store data in ~/.claude-internal/ to configure the plugin accordingly. Includes settings UI, i18n support for all 10 locales, and 15 unit tests.
  • Auto-detect available models from SDK at runtime: Replace the hardcoded model list with runtime detection via persistentQuery.supportedModels(), so claude-internal users see their actual available models. Priority: SDK models > env-var models > DEFAULT_CLAUDE_MODELS.
  • Documentation updates: Update CLAUDE.md, README, and add Chinese translation (README_CN.md).

Changes

  • Add src/utils/claudePaths.ts with get/set functions for the configurable dir name
  • Convert static path constants in all 8 storage modules to dynamic functions
  • Update PluginManager, AgentManager, sdkSession, and path utils to use dynamic paths
  • Add ClaudianService.getSupportedModels() to fetch models from SDK
  • Update ModelSelector to prefer SDK-detected models at runtime
  • Add settings UI for Claude home directory (persisted to data.json to avoid bootstrap circularity)
  • Bootstrap dir name in main.ts before loading settings
  • Add i18n keys to all 10 locales
  • Add unit tests for claudePaths, getSupportedModels, and InputToolbar SDK model integration

Test plan

  • Unit tests pass
  • Verify plugin loads with default .claude directory
  • Change Claude home directory in settings to .claude-internal and verify storage paths update
  • Verify model selector shows SDK-detected models when service is ready
  • Verify fallback to env-var/default models when SDK models unavailable

@YishenTu
Copy link
Owner

YishenTu commented Feb 10, 2026

PR review findings:

  1. Critical: setClaudeHomeDirName is called on every keystroke

    • File: src/features/settings/ClaudianSettings.ts:626
    • TextComponent.onChange fires per-keystroke, but this mutates global path resolution immediately. Intermediate values (e.g. .c, .cl) can become active while user is typing.
    • Suggested fix: in settings UI, only persist value to data.json and show restart notice; avoid calling setClaudeHomeDirName() here.
  2. Critical: bootstrap path applies persisted claudeHomeDirName without validation

    • File: src/main.ts:66 and src/main.ts:67
    • data.json is trusted directly; malformed/manual values can flow into global path resolution.
    • Suggested fix: validate before calling setter (or enforce validation inside setter).
  3. High: mutation boundary has no validation

    • File: src/utils/claudePaths.ts:6
    • setClaudeHomeDirName(dirName: string) currently accepts arbitrary strings.
    • Suggested fix: enforce validation in setter itself (startsWith('.'), no / or \\, reject . and .., reject empty).
  4. Important: current validation allows . and ..

    • File: src/features/settings/ClaudianSettings.ts:618
    • Current guard blocks separators but still allows . and ...
    • Suggested fix: reject both explicit values in the validation condition.
  5. High: hardcoded command path remains in delete flow

    • File: src/core/storage/SlashCommandStorage.ts:52
    • delete() still uses COMMANDS_PATH instead of getCommandsPath(), so configurable Claude dir migration is incomplete for this code path.
    • Suggested fix: replace with dynamic path helper for consistency with the rest of the migration.

pennn778 added a commit to pennn778/claudian that referenced this pull request Feb 28, 2026
…fix hardcoded path

- Add isValidClaudeHomeDirName() validator and enforce it in setter to
  reject empty, '.', '..', non-dot-prefixed, and separator-containing names
- Stop calling setClaudeHomeDirName() on every keystroke in settings UI;
  only persist to data.json so a restart is required for path changes
- Replace hardcoded COMMANDS_PATH with getCommandsPath() in delete() and
  remove the deprecated constant
- Add unit tests for validation logic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@pennn778
Copy link
Author

Review feedback addressed

Pushed a fix commit (7ca68e1) addressing all 5 review comments:

Changes

  1. Critical — setter called on every keystroke (comment Coming to Obsidian Community Plugins #1)

    • Settings UI no longer calls setClaudeHomeDirName() in onChange. Only persists the value to data.json; a restart is required for path changes to take effect.
  2. Critical — bootstrap without validation (comment Fix Windows path comparisons and environment variable expansion #2)

    • setClaudeHomeDirName() now internally validates via isValidClaudeHomeDirName(). Invalid values from data.json are silently ignored (default .claude remains).
  3. High — setter has no validation (comment feat: support concurrent title generation per conversation #3)

    • Added isValidClaudeHomeDirName() that rejects: empty strings, ., .., names without leading ., and names containing path separators (/, \).
  4. Important — validation allows . and .. (comment Refactor test layout into unit/integration #4)

    • Settings UI validation now uses the shared isValidClaudeHomeDirName() which rejects both . and ...
  5. High — hardcoded COMMANDS_PATH in delete flow (comment Show title model only when auto titles enabled and guard AI generation #5)

    • SlashCommandStorage.delete() now uses getCommandsPath(). The deprecated COMMANDS_PATH constant has been removed.

Verification

  • All 4454 tests pass
  • TypeScript type check passes
  • Lint passes
  • Build succeeds

pennn778 and others added 5 commits February 28, 2026 23:51
Replace all hardcoded `.claude` path references with a centralized
`claudePaths` module that reads a configurable directory name. This
allows users of custom CLI builds (e.g., `claude-internal`) that store
data in `~/.claude-internal/` to configure the plugin accordingly.

- Add `src/utils/claudePaths.ts` with get/set functions for the dir name
- Convert static path constants in all 8 storage modules to dynamic functions
- Update PluginManager, AgentManager, sdkSession, and path utils
- Dynamize plan file path matching in StreamController and InlineExitPlanMode
- Add settings UI (persisted to data.json to avoid bootstrap circularity)
- Bootstrap dir name in main.ts before loading settings
- Add i18n keys to all 10 locales
- Add 15 unit tests for the new module

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hardcoded model list with runtime detection via
persistentQuery.supportedModels() so claude-internal users
see their actual available models (e.g. Claude-4.5-Sonnet,
Opus 4.6, Opus 4.6 1M, Haiku 4.5) instead of the default
haiku/sonnet/opus shortcuts.

Priority: SDK models > env-var models > DEFAULT_CLAUDE_MODELS.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- CLAUDE.md: add entry point, key dependencies, path aliases, and
  single test file command
- README.md: update Advanced Model Control to mention SDK runtime
  model detection
- README_CN.md: add full Chinese translation of README

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
onload() had no error handling — any failure in settings loading,
storage migration, or manager initialization would prevent
registerView/addRibbonIcon/addCommand from executing, causing the
plugin to silently fail on Obsidian startup.

- Restructure onload() into phased initialization with per-phase
  try-catch, ensuring critical registrations always execute
- Make storage migration non-fatal (replace throw with graceful
  return + Notice)
- Wrap runMigrations() in try-catch within initialize()
- Extract conversation loading into separate method with try-catch
- Add try-catch safety net to ClaudianView.onOpen() with fallback UI
- Add Electron AbortSignal compatibility patch for SDK

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…fix hardcoded path

- Add isValidClaudeHomeDirName() validator and enforce it in setter to
  reject empty, '.', '..', non-dot-prefixed, and separator-containing names
- Stop calling setClaudeHomeDirName() on every keystroke in settings UI;
  only persist to data.json so a restart is required for path changes
- Replace hardcoded COMMANDS_PATH with getCommandsPath() in delete() and
  remove the deprecated constant
- Add unit tests for validation logic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants