Skip to content

Release: History検索機能 + perf最適化 + 不具合修正バンドル (#716, #708-711, #706, #704)#718

Merged
Kewton merged 9 commits into
mainfrom
develop
May 17, 2026
Merged

Release: History検索機能 + perf最適化 + 不具合修正バンドル (#716, #708-711, #706, #704)#718
Kewton merged 9 commits into
mainfrom
develop

Conversation

@Kewton
Copy link
Copy Markdown
Owner

@Kewton Kewton commented May 17, 2026

Summary

develop ブランチを main へマージするリリースPRです。Issue #701 のマージ(PR #703)以降に develop に積まれた 1機能追加 + 4パフォーマンス改善 + 2不具合修正 をまとめて本番ブランチへ反映します。

Included PRs (develop merge order)

develop PR 種別 Issue サマリー
#717 feat #716 HistoryPaneにメッセージテキスト検索(debounce/ハイライト/ナビ)
#715 perf #708 chat_messagesのrole列を含む複合インデックスでgetWorktrees高速化
#714 perf #709 useWorktreesCacheの二重インスタンス化解消(Provider一本化)
#713 perf #710 useWorktreesCacheポーリング間隔をactive/idle動的切替
#712 perf #711 scanMultipleRepositoriesのgit worktree list並列実行
#707 fix #706 Filesタブのツリー再描画でスクロール位置リセットを修正
#705 fix #704 Claude Code v2.1.142「Use skill "X"?」プロンプトのYes/No検出

Closes

主要変更

Feature

Performance

Bug Fix

Test plan

バージョン

現在: v0.5.7。本PRはバージョンバンプを含まないため、必要であれば main マージ後に /release で次バージョンタグ作成を推奨。

🤖 Generated with Claude Code

Kewton and others added 8 commits May 12, 2026 22:26
* feat(history): allow selecting history display limit (50-250)

Issue #701

- Add HistoryDisplayLimit selector (50/100/150/200/250) to HistoryPane header
- Persist selection in localStorage (commandmate:historyDisplayLimit)
- Raise messages API upper bound from 100 to MAX_MESSAGES_LIMIT (250)
- Centralize options/MAX/DEFAULT in src/config/history-display-config.ts
- Propagate selection through WorktreeDetailRefactored (PC + Mobile)
- worktreeApi.getMessages: optional limit argument
- useInfiniteMessages: default pageSize references DEFAULT_MESSAGES_LIMIT
- Tests: boundary (1/250/251/0/abc), DB getMessages limit=250,
  useInfiniteMessages hasMore at pageSize=250, config Single Source of Truth

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(history-display-config): derive MAX_MESSAGES_LIMIT from union type

- Replace brittle `as 250` literal cast with `HistoryDisplayLimit` type
  derivation so extending HISTORY_DISPLAY_LIMIT_OPTIONS no longer
  requires touching MAX_MESSAGES_LIMIT.
- Tighten comments and adjust wording for clarity.

Issue #701

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(claude-md): add history-display-config.ts to module reference (#701)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…line (#704) (#705)

Claude Code v2.1.142 renders trailing summary lines like "… +1 pending"
below the real options in skill approval prompts ("Use skill ...?").
NORMAL_OPTION_PATTERN was matching these as option N with label="pending",
which poisoned isValidPrecedingOption() and caused every real option
above (1./2./3.) to be rejected — collectedOptions ended up with length
< 2, returning no_prompt. As a result Yes/No UI never appeared and
Auto-Yes did not fire.

Defense-in-depth fix:
- S1: SUMMARY_LINE_PATTERN early-continue in Pass 2 loop (tight
  anchored pattern, no free `(.+)` capture, keyword whitelist
  pending|more) so legitimate labels mentioning these words are
  preserved.
- S2: CLAUDE_PROMPT_FOOTER_PATTERN trims effectiveEnd to the
  "Esc to cancel · Tab to amend" footer, putting the summary line
  outside the scan window entirely. Fallback-safe: no footer found =>
  effectiveEnd untouched, so Codex/Gemini/OpenCode/Copilot detection
  paths are unaffected.
- S3: 3-layer regression tests across prompt-detector, status-detector,
  and auto-yes-resolver covering the real Claude v2.1.142 fixture plus
  FP-suppression for option labels containing "pending"/"more".

Quality gates: lint 0/0, tsc 0, unit 6486 passed / 7 skipped, build OK.
- FileTreeView: limit full-screen loading/error to initial mount (rootItems empty)
- FileTreeView: add non-destructive refetch indicator (aria-live=polite) and error banner with retry button
- FileTreeView: extract reloadTreeWithExpandedDirs via useCallback + mountedRef guard
- WorktreeDetailRefactored: skip first-poll false-positive refresh by treating null prevTreeHashRef as baseline-only
- tests: add 5 new tests for refetch indicator / non-destructive error / retry path

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…led (#711) (#712)

Each `git worktree list` spawns an independent child process, so running
them sequentially via `for ... await` made the sync endpoint scale linearly
in the number of repositories. Switch to `Promise.allSettled(...map(...))`
so all repository scans run concurrently while preserving the prior "one
failure does not abort the rest" semantics. Logs continue to include
`repoPath` so the (now unordered) output can still be correlated.

The unit test mock for `child_process` had to be switched from auto-mock to
a factory mock: vitest's auto-mock preserves `util.promisify.custom` from
the real `exec`, which made `promisify(exec)` bypass `mockImplementation`
entirely. The factory returns a fresh `vi.fn()` without that symbol so
callback-style mocking works.

Closes #711

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…710) (#713)

useWorktreesCache のポーリング間隔(active=5s/idle=30s)が startPolling() 呼び出し時点で固定され、worktrees の active/idle 状態が遷移しても interval が更新されない問題を修正。

- currentIntervalRef: useRef<number | null> を追加し活動中の interval を追跡
- startPolling/stopPolling/hasActiveSession を useCallback でフック直下にリフトアップ
- worktrees-change useEffect で desired interval を再計算し差分時のみ startPolling を再実行
- document.hidden 中・初期化前(ref === null)はガードして no-op
- 既存の visibilitychange ハンドラの責務は維持

Closes #710

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…) (#714)

Sessions ページが useWorktreesCache() を直接呼び出していたため、
WorktreesCacheProvider と合わせて /api/worktrees ポーリングが2系統
並行で動いていた問題を解消。

- WorktreesCacheProvider に WorktreesCacheContext を新設し
  useWorktreesCacheContext() フックを export
- src/app/sessions/page.tsx を Context 経由参照に変更
- useWorktreesCache フック自体は不変 (テスト/単体利用は維持)
- 新規テスト tests/unit/components/providers/WorktreesCacheProvider.test.tsx

Closes #709

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…708) (#715)

chat_messages の相関サブクエリ (role='assistant'/'user' AND archived=0 の
MAX(timestamp)/ORDER BY timestamp DESC LIMIT 1) が、既存
idx_messages_archived(worktree_id, archived, timestamp DESC) では role 列を
含まないため行スキャンに陥り、件数増加に伴い線形劣化していた。

Migration v32 で複合インデックス
  idx_messages_worktree_role_archived_time(worktree_id, role, archived, timestamp DESC)
を新設し、既存 idx_messages_archived を DROP。
他の role なしクエリ (getMessages, getLastMessage, deleteAllMessages) は
idx_messages_worktree_time(worktree_id, timestamp DESC) で従来同等に動作する。

- src/lib/db/migrations/v32-add-messages-role-composite-index.ts: 新規
- src/lib/db/migrations/index.ts / runner.ts: v32 登録、CURRENT_SCHEMA_VERSION = 32
- src/lib/db/init-db.ts: 初期化パスを新インデックスへ置換
- tests/unit/lib/db-migrations.test.ts: v32 describe ブロック追加、
  v22 旧インデックスアサーションを rollback to 31 経由に修正
- tests/unit/lib/db/chat-db-explain-plan.test.ts: EXPLAIN QUERY PLAN
  で対象4クエリが新インデックスを使うことを検証

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(history): add text search to HistoryPane (#716)

Adds a per-pane text search bar to Worktree HistoryPane with namespace-isolated
CSS Custom Highlight API rendering so it can coexist with the TerminalPane
search (Issue #47).

Key design choices (3-stage reviewed):
- Preserve OCP: existing applyTerminalHighlights / clearTerminalHighlights
  signatures unchanged. New applyHistoryHighlights / clearHistoryHighlights
  share the internal engine via HighlightNamespace.
- ConversationPairCard receives no new props; only data-message-id is added so
  memo is preserved. useConversationHistory is untouched.
- HistoryPane manages autoExpandedIds internally to force-expand truncated
  assistant messages that contain hits, then applies highlights with a strict
  useLayoutEffect order (save scroll → restore scroll (skipped during search)
  → autoExpandedIds → applyHistoryHighlights) so textContent stays aligned
  with message.content.
- Debounce (300ms), min-query length (2), and max-matches (500) are shared
  via newly exported SEARCH_DEBOUNCE_MS / SEARCH_MIN_QUERY_LENGTH /
  TERMINAL_SEARCH_MAX_MATCHES from useTerminalSearch.

Tests: 47 new cases across useHistorySearch, HistorySearchBar, terminal-
highlight, and HistoryPane. Full unit suite: 6558 passed / 0 failed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(history-search): DRY debounce timer + unify selector escape (#716)

Small post-implementation cleanups identified during the refactoring phase.
No behavior changes; all 6558 unit tests still pass.

useHistorySearch.ts:
- Extract clearDebounceTimer() helper (DRY: clearTimeout/null pattern was
  duplicated across scheduleSearch / closeSearch / onCompositionStart /
  unmount cleanup).

HistoryPane.tsx:
- Replace custom escapeAttrValue() with native CSS.escape() via a small
  findMessageElement() helper. Removes a homemade escape function that
  duplicated standards-track functionality and unifies the two
  inconsistent selector-building sites (one used CSS.escape, the other
  the custom helper).
- Capture the current match element during the highlight loop so the
  scrollIntoView path no longer issues a second querySelector for the
  same node.

Constraints preserved:
- terminal-highlight.ts public API untouched (OCP).
- ConversationPairCard props unchanged (memo preserved).
- Effect declaration order (1)->(2)->(3)->(4) and HISTORY_SEARCH_NAMESPACE
  isolation unchanged (design policy v1.2 core).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(history): update CLAUDE.md and implementation-history for #716

Add HistoryPane text search feature entry and document new modules:
useHistorySearch hook, HistorySearchBar component, terminal-highlight
namespace separation (HISTORY_SEARCH_NAMESPACE), and data-message-id
attribution in ConversationPairCard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Kewton Kewton added the feature label May 17, 2026
main側に既に取り込まれた #701 (PR #703) と、develop側の #716 等の変更を統合。
phantom conflict は develop 側を保持 (上位互換)。
@Kewton Kewton merged commit 4e6d56c into main May 17, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment