Skip to content

Commit 2a8de44

Browse files
feat: Claude Code CLI provider and Conductor-style sidebar redesign (#3)
* feat: add Claude Code CLI as built-in provider Spawn claude CLI subprocess for claude-code/* models instead of AI SDK path. Supports text streaming, tool calls, thinking, multi-turn via --resume, session cleanup, timeout, and plugin hooks. Includes unit/integration/e2e tests (11 passing) and design specs. * fix: skip API key prompt for already-connected providers in web UI * feat: redesign sidebar with Conductor-style status groups, PR integration, and continue-on-new-branch - Sidebar shows 5 status groups: In progress / In review / Done / Backlog / Cancelled - 3-line session rows: title+diff / branch / provider·PR·time·shortcut - PR integration: open PR → In review, merged → Done (event-driven + 2min fallback) - Backend GET /experimental/pr: per-branch gh pr list with --head flag - Archive confirmation dialog before archiving sessions - Continue on new branch: fork Done sessions to new worktree with clean context - Fork strips snapshot refs and inits empty summary (no stale file changes) - Replaced History header with repo name + worktree path tooltip - Removed dummy All repos button and ReviewReadiness badge - SDK regenerated with Pr.list and Git.remote * fix: archive original session after continue-on-new-branch and remove 2min polling fallback - continueOnNewBranch now archives the original Done session after forking - Removed the session from the sidebar store to match archive behavior - Removed unnecessary 2-minute setInterval polling for PR detection - visibilitychange only fires fetchPrs when tab becomes visible * fix: add cwd, permission bypass, and AskUserQuestion routing for Claude Code CLI - Set cwd to Instance.directory so CLI runs in project dir - Add --permission-mode bypassPermissions for non-interactive tool execution - Remove release_date to fix model visibility in web UI - Route AskUserQuestion through OpenCode Question system: detect tool_use in stream, show in UI, pass answer on next --resume turn - Set finish to tool-calls on question so session loop continues * feat: show spinner on sidebar session row when agent is actively working * fix: refetch session diffs when cache is stale after fork * fix: add SessionSummary.summarize() to Claude Code CLI provider for real-time diff updates Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * fix: apply agent tint color to sidebar spinner indicator Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * fix: guard Snapshot.patch() call against undefined snap in Claude Code provider Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> --------- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1 parent 296250f commit 2a8de44

26 files changed

Lines changed: 2978 additions & 294 deletions

packages/app/src/components/dialog-connect-provider.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,13 @@ export function DialogConnectProvider(props: { provider: string }) {
173173
listRef?.onKeyDown(e)
174174
}
175175

176+
const connected = createMemo(() => new Set(globalSync.data.provider.connected))
177+
176178
onMount(() => {
179+
if (connected().has(props.provider)) {
180+
void complete()
181+
return
182+
}
177183
if (methods().length === 1) {
178184
selectMethod(0)
179185
}

packages/app/src/context/sync.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,14 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
276276
const directory = sdk.directory
277277
const client = sdk.client
278278
const [store, setStore] = globalSync.child(directory)
279-
if (store.session_diff[sessionID] !== undefined) return
279+
const cached = store.session_diff[sessionID]
280+
if (cached !== undefined) {
281+
if (cached.length > 0) return
282+
// Empty cache might be stale if summary says there are file changes
283+
// (session.diff SSE event can be missed during fork or reconnection)
284+
const match = Binary.search(store.session, sessionID, (s) => s.id)
285+
if (!match.found || (store.session[match.index]?.summary?.files ?? 0) === 0) return
286+
}
280287

281288
const key = keyFor(directory, sessionID)
282289
return runInflight(inflightDiff, key, () =>

packages/app/src/i18n/en.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,9 @@ export const dict = {
496496
"session.review.loadingChanges": "Loading changes...",
497497
"session.review.empty": "No changes in this session yet",
498498
"session.review.noChanges": "No changes",
499+
"session.stage.inProgress": "In progress",
500+
"session.stage.inReview": "In review",
501+
"session.stage.done": "Done",
499502

500503
"session.files.selectToOpen": "Select a file to open",
501504
"session.files.all": "All files",
@@ -795,6 +798,9 @@ export const dict = {
795798
"workspace.reset.success.title": "Workspace reset",
796799
"workspace.reset.success.description": "Workspace now matches the default branch.",
797800
"workspace.error.stillPreparing": "Workspace is still preparing",
801+
"workspace.stage.preparing": "Preparing",
802+
"workspace.stage.ready": "Ready",
803+
"workspace.stage.failed": "Failed",
798804
"workspace.status.checking": "Checking for unmerged changes...",
799805
"workspace.status.error": "Unable to verify git status.",
800806
"workspace.status.clean": "No unmerged changes detected.",
@@ -809,4 +815,13 @@ export const dict = {
809815
"workspace.reset.archived.one": "1 session will be archived.",
810816
"workspace.reset.archived.many": "{{count}} sessions will be archived.",
811817
"workspace.reset.note": "This will reset the workspace to match the default branch.",
818+
819+
"session.archive.title": "Archive session",
820+
"session.archive.confirm": "Archive this session?",
821+
"session.archive.description": "The session will be moved to Done.",
822+
"session.archive.button": "Archive",
823+
824+
"session.continue.title": "Continue on new branch",
825+
"session.continue.description": "Archive this session and continue on a new branch with the same context.",
826+
"session.continue.button": "Continue on new branch",
812827
}

packages/app/src/i18n/ko.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,9 @@ export const dict = {
445445
"session.review.loadingChanges": "변경 사항 로드 중...",
446446
"session.review.empty": "이 세션에 변경 사항이 아직 없습니다",
447447
"session.review.noChanges": "변경 없음",
448+
"session.stage.inProgress": "진행 중",
449+
"session.stage.inReview": "검토 중",
450+
"session.stage.done": "완료",
448451
"session.files.selectToOpen": "열 파일을 선택하세요",
449452
"session.files.all": "모든 파일",
450453
"session.files.binaryContent": "바이너리 파일 (내용을 표시할 수 없음)",
@@ -714,6 +717,9 @@ export const dict = {
714717
"workspace.reset.success.title": "작업 공간 재설정됨",
715718
"workspace.reset.success.description": "작업 공간이 이제 기본 브랜치와 일치합니다.",
716719
"workspace.error.stillPreparing": "작업 공간이 아직 준비 중입니다",
720+
"workspace.stage.preparing": "준비 중",
721+
"workspace.stage.ready": "준비 완료",
722+
"workspace.stage.failed": "실패",
717723
"workspace.status.checking": "병합되지 않은 변경 사항 확인 중...",
718724
"workspace.status.error": "Git 상태를 확인할 수 없습니다.",
719725
"workspace.status.clean": "병합되지 않은 변경 사항이 감지되지 않았습니다.",
@@ -728,4 +734,13 @@ export const dict = {
728734
"workspace.reset.archived.one": "1개의 세션이 보관됩니다.",
729735
"workspace.reset.archived.many": "{{count}}개의 세션이 보관됩니다.",
730736
"workspace.reset.note": "이 작업은 작업 공간을 기본 브랜치와 일치하도록 재설정합니다.",
737+
738+
"session.archive.title": "세션 보관",
739+
"session.archive.confirm": "이 세션을 보관하시겠습니까?",
740+
"session.archive.description": "세션이 완료 상태로 이동합니다.",
741+
"session.archive.button": "보관",
742+
743+
"session.continue.title": "새 브랜치에서 계속",
744+
"session.continue.description": "이 세션을 보관하고 동일한 컨텍스트로 새 브랜치에서 계속합니다.",
745+
"session.continue.button": "새 브랜치에서 계속",
731746
}

0 commit comments

Comments
 (0)