mirror of
https://github.com/stablyai/orca.git
synced 2026-10-17 16:02:55 +00:00
Track two-agent setup from agent hooks
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import type { JSX, ReactNode } from 'react'
|
||||
import { Briefcase, FolderGit2, House } from 'lucide-react'
|
||||
import { FolderGit2 } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { ClaudeIcon } from '../status-bar/icons'
|
||||
import { CodexInlineIcon, WorkingSpinner } from './feature-tour-preview-glyphs'
|
||||
@@ -9,29 +9,32 @@ type RepoFocus = 'personal' | 'work' | 'both'
|
||||
|
||||
type RepoVisual = {
|
||||
id: Exclude<RepoFocus, 'both'>
|
||||
label: string
|
||||
name: string
|
||||
worktree: string
|
||||
prompt: string
|
||||
action: string
|
||||
agent: 'Claude Code' | 'Codex'
|
||||
icon: ReactNode
|
||||
agentIcon: ReactNode
|
||||
}
|
||||
|
||||
const REPOS: readonly RepoVisual[] = [
|
||||
{
|
||||
id: 'personal',
|
||||
label: 'Personal',
|
||||
name: 'recipe-box',
|
||||
worktree: 'weekend polish',
|
||||
prompt: 'refine recipe search',
|
||||
action: 'Editing search filters',
|
||||
agent: 'Claude Code',
|
||||
icon: <House className="size-3.5" />
|
||||
agentIcon: <ClaudeIcon size={12} />
|
||||
},
|
||||
{
|
||||
id: 'work',
|
||||
label: 'Work',
|
||||
name: 'billing-app',
|
||||
worktree: 'checkout fix',
|
||||
prompt: 'fix checkout bug',
|
||||
action: 'Updating checkout tests',
|
||||
agent: 'Codex',
|
||||
icon: <Briefcase className="size-3.5" />
|
||||
agentIcon: <CodexInlineIcon />
|
||||
}
|
||||
]
|
||||
|
||||
@@ -62,59 +65,107 @@ function RepoProjectCard(props: {
|
||||
return (
|
||||
<section
|
||||
className={cn(
|
||||
'flex min-w-0 flex-col rounded-lg border bg-background transition-[border-color,box-shadow,background-color] duration-500',
|
||||
props.active ? 'border-primary/35 bg-primary/5 shadow-xs' : 'border-border'
|
||||
'flex min-w-0 flex-col gap-2 rounded-lg border border-sidebar-border bg-sidebar p-2 text-sidebar-foreground transition-[border-color,box-shadow] duration-500',
|
||||
props.active ? 'shadow-xs' : null
|
||||
)}
|
||||
>
|
||||
<div className="flex h-10 items-center gap-2 border-b border-border px-3">
|
||||
<span
|
||||
className={cn(
|
||||
'flex size-5 items-center justify-center rounded-md border border-border bg-card transition-colors',
|
||||
props.active ? 'text-primary' : 'text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{props.repo.icon}
|
||||
</span>
|
||||
<div className="min-w-0">
|
||||
<div className="truncate text-sm font-semibold leading-none text-foreground">
|
||||
{props.repo.name}
|
||||
</div>
|
||||
<div className="mt-1 text-[10px] font-medium uppercase tracking-[0.06em] text-muted-foreground">
|
||||
{props.repo.label}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ProjectSidebarRow name={props.repo.name} />
|
||||
|
||||
<div className="flex flex-1 flex-col gap-3 p-3">
|
||||
<div className="rounded-lg border border-border bg-card p-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="flex size-5 items-center justify-center rounded-md border border-border bg-background text-muted-foreground">
|
||||
<FolderGit2 className="size-3.5" />
|
||||
</span>
|
||||
<span className="min-w-0 truncate text-xs font-semibold text-foreground">
|
||||
{props.repo.worktree}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-3 rounded-md border border-border bg-background p-2.5 font-mono text-[11px]">
|
||||
<TerminalLine>
|
||||
<Prompt>></Prompt>
|
||||
{props.repo.id === 'personal' ? 'refine recipe search' : 'fix checkout bug'}
|
||||
</TerminalLine>
|
||||
<TerminalLine muted>
|
||||
<WorkingSpinner size="xs" reducedMotion={props.reducedMotion} />
|
||||
{props.repo.agent} working
|
||||
</TerminalLine>
|
||||
<TerminalLine muted>
|
||||
{props.repo.agent === 'Codex' ? <CodexInlineIcon /> : <ClaudeIcon size={12} />}
|
||||
{props.repo.id === 'personal' ? 'Updating UI' : 'Editing tests'}
|
||||
</TerminalLine>
|
||||
</div>
|
||||
<ProjectWorktreeRow
|
||||
repo={props.repo}
|
||||
active={props.active}
|
||||
reducedMotion={props.reducedMotion}
|
||||
/>
|
||||
|
||||
<div className="min-w-0 overflow-hidden rounded-lg border border-border bg-background text-foreground">
|
||||
<VisualTitlebar title={`${props.repo.worktree} - ${props.repo.agent}`} />
|
||||
<div className="space-y-1.5 p-2.5 font-mono text-[11px]">
|
||||
<TerminalLine>
|
||||
<Prompt>></Prompt>
|
||||
{props.repo.prompt}
|
||||
</TerminalLine>
|
||||
<TerminalLine muted>
|
||||
{props.repo.agentIcon}
|
||||
{props.repo.agent} session ready
|
||||
</TerminalLine>
|
||||
<TerminalLine muted>
|
||||
<WorkingSpinner size="xs" reducedMotion={props.reducedMotion} />
|
||||
{props.repo.action}
|
||||
</TerminalLine>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function ProjectSidebarRow(props: { name: string }): JSX.Element {
|
||||
return (
|
||||
<div
|
||||
aria-hidden
|
||||
className="relative flex h-8 min-w-0 items-center gap-1.5 rounded-md px-1.5 text-sidebar-foreground"
|
||||
>
|
||||
<span className="flex size-4 shrink-0 items-center justify-center text-muted-foreground">
|
||||
<FolderGit2 className="size-3.5" />
|
||||
</span>
|
||||
<span className="min-w-0 flex-1 truncate text-[13px] font-semibold leading-none">
|
||||
{props.name}
|
||||
</span>
|
||||
<span className="min-w-[3.75rem] rounded-full bg-sidebar-accent px-1.5 py-0.5 text-center text-[9px] font-medium leading-none text-muted-foreground">
|
||||
1 worktree
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ProjectWorktreeRow(props: {
|
||||
repo: RepoVisual
|
||||
active: boolean
|
||||
reducedMotion: boolean
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-md border border-sidebar-border px-2.5 py-2 transition-colors',
|
||||
props.active ? 'bg-sidebar-accent' : 'bg-sidebar'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className={cn(
|
||||
'size-1.5 shrink-0 rounded-full',
|
||||
props.active ? 'bg-emerald-500' : 'bg-muted-foreground/35'
|
||||
)}
|
||||
/>
|
||||
<span className="truncate text-xs font-medium text-sidebar-foreground">
|
||||
{props.repo.worktree}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-2 grid grid-cols-[8px_14px_minmax(0,1fr)] items-center gap-1.5">
|
||||
<WorkingSpinner size="xs" reducedMotion={props.reducedMotion} />
|
||||
<span className="flex size-3.5 items-center justify-center text-sidebar-foreground/65">
|
||||
{props.repo.agentIcon}
|
||||
</span>
|
||||
<span className="truncate font-mono text-[11px] text-sidebar-foreground/65">
|
||||
{props.repo.prompt}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function VisualTitlebar(props: { title: string }): JSX.Element {
|
||||
return (
|
||||
<div className="flex h-6 items-center gap-1.5 border-b border-border bg-muted/40 px-2">
|
||||
<span className="size-2 rounded-full bg-foreground/15" />
|
||||
<span className="size-2 rounded-full bg-foreground/15" />
|
||||
<span className="size-2 rounded-full bg-foreground/15" />
|
||||
<span className="ml-1 truncate font-mono text-[11px] text-muted-foreground">
|
||||
{props.title}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function TerminalLine(props: { children: ReactNode; muted?: boolean }): JSX.Element {
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useLayoutEffect, useRef, useState, type JSX } from 'react'
|
||||
import { Check, GitBranch } from 'lucide-react'
|
||||
import { GitBranch } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { ClaudeIcon } from '../status-bar/icons'
|
||||
import { FeatureWallClickRing } from './FeatureWallClickRing'
|
||||
@@ -166,20 +166,10 @@ function AgentWorktreeTerminal(props: {
|
||||
<span className="truncate">{WORKTREE_LABEL}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="space-y-2.5 p-3 font-mono text-[10.5px] leading-snug">
|
||||
<ClaudePrompt
|
||||
active={props.phase === 'inspect'}
|
||||
done={props.targetVisible}
|
||||
text="approve the note in my app"
|
||||
/>
|
||||
<div className="space-y-2 p-3 font-mono text-[10.5px] leading-snug">
|
||||
<TerminalRequest text="approve the note in my app" />
|
||||
<CliCommandBlock
|
||||
command={
|
||||
<>
|
||||
<span className="text-muted-foreground">orca computer</span>{' '}
|
||||
<span className="text-foreground">get-app-state</span>
|
||||
</>
|
||||
}
|
||||
arg='--app "Notes"'
|
||||
commandLines={['orca computer get-app-state', '--app "Notes"']}
|
||||
active={props.phase === 'inspect'}
|
||||
done={props.targetVisible}
|
||||
output={
|
||||
@@ -191,31 +181,19 @@ function AgentWorktreeTerminal(props: {
|
||||
}
|
||||
/>
|
||||
<CliCommandBlock
|
||||
command={
|
||||
<>
|
||||
<span className="text-muted-foreground">orca computer</span>{' '}
|
||||
<span className="text-foreground">click</span>
|
||||
</>
|
||||
}
|
||||
arg="--element-index 7"
|
||||
commandLines={['orca computer click', '--element-index 7']}
|
||||
active={props.phase === 'target' || props.phase === 'click'}
|
||||
done={props.clicked}
|
||||
output={props.clicked ? <>click sent</> : null}
|
||||
/>
|
||||
<CliCommandBlock
|
||||
command={
|
||||
<>
|
||||
<span className="text-muted-foreground">orca computer</span>{' '}
|
||||
<span className="text-foreground">get-app-state</span>
|
||||
</>
|
||||
}
|
||||
arg='--app "Notes" --json'
|
||||
commandLines={['orca computer get-app-state', '--app "Notes" --json']}
|
||||
active={props.phase === 'click'}
|
||||
done={props.verified}
|
||||
output={
|
||||
props.verified ? (
|
||||
<>
|
||||
state: <span className="text-foreground">Approved</span>
|
||||
<span aria-hidden>→</span> <span className="text-foreground">computer</span>
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
@@ -225,62 +203,38 @@ function AgentWorktreeTerminal(props: {
|
||||
)
|
||||
}
|
||||
|
||||
function ClaudePrompt(props: { text: string; active: boolean; done: boolean }): JSX.Element {
|
||||
function TerminalRequest(props: { text: string }): JSX.Element {
|
||||
return (
|
||||
<div className="flex min-w-0 items-start gap-1.5">
|
||||
<span className="shrink-0 text-amber-600">></span>
|
||||
<span
|
||||
className={cn(
|
||||
'truncate',
|
||||
props.active || props.done ? 'text-foreground' : 'text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{props.text}
|
||||
</span>
|
||||
<div className="min-w-0 truncate text-muted-foreground">
|
||||
<span>{props.text}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function CliCommandBlock(props: {
|
||||
command: JSX.Element
|
||||
arg: string
|
||||
commandLines: readonly string[]
|
||||
active: boolean
|
||||
done: boolean
|
||||
output: JSX.Element | null
|
||||
}): JSX.Element {
|
||||
const commandTone = props.active || props.done ? 'text-foreground' : 'text-muted-foreground/65'
|
||||
return (
|
||||
<div className="space-y-0.5">
|
||||
<div className="flex items-start gap-1.5">
|
||||
<span
|
||||
aria-hidden
|
||||
className={cn(
|
||||
'mt-px shrink-0',
|
||||
props.active || props.done ? 'text-foreground' : 'text-muted-foreground/60'
|
||||
)}
|
||||
>
|
||||
$
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div
|
||||
className={cn('truncate', props.active ? 'text-foreground' : 'text-muted-foreground')}
|
||||
>
|
||||
{props.command}
|
||||
<div className={cn('text-[11px]', commandTone)}>$</div>
|
||||
<div className={cn('min-w-0 space-y-0.5 pl-2', commandTone)}>
|
||||
{props.commandLines.map((line) => (
|
||||
<div key={line} className="truncate">
|
||||
{line}
|
||||
</div>
|
||||
<div className="truncate pl-3 text-muted-foreground">{props.arg}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'flex min-h-[14px] items-center gap-1.5 pl-3.5 text-muted-foreground transition-opacity duration-200',
|
||||
'min-h-[14px] truncate pl-2 text-muted-foreground transition-opacity duration-200',
|
||||
props.output ? 'opacity-100' : 'opacity-0'
|
||||
)}
|
||||
>
|
||||
{props.done ? (
|
||||
<Check className="size-2.5 text-primary" strokeWidth={3} />
|
||||
) : (
|
||||
<span aria-hidden>→</span>
|
||||
)}
|
||||
<span className="truncate">{props.output ?? ' '}</span>
|
||||
{props.output ?? ' '}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -18,7 +18,7 @@ function makeInput(
|
||||
gitRepoCount: 0,
|
||||
worktreesByRepo: {},
|
||||
tabsByWorktree: {},
|
||||
runtimePaneTitlesByTabId: {},
|
||||
agentStatusByPaneKey: {},
|
||||
hasSetupScript: false,
|
||||
...overrides
|
||||
}
|
||||
@@ -65,7 +65,7 @@ describe('getFeatureWallSetupProgress', () => {
|
||||
expect(progress.stepDone['two-agents']).toBe(false)
|
||||
})
|
||||
|
||||
it('marks two agents complete once two agent sessions exist in one worktree', () => {
|
||||
it('does not mark two agents complete from terminal titles alone', () => {
|
||||
const progress = getFeatureWallSetupProgress(
|
||||
makeInput({
|
||||
worktreesByRepo: { 'repo-1': [makeWorktree('worktree-1')] },
|
||||
@@ -78,9 +78,82 @@ describe('getFeatureWallSetupProgress', () => {
|
||||
})
|
||||
)
|
||||
|
||||
expect(progress.stepDone['two-agents']).toBe(false)
|
||||
})
|
||||
|
||||
it('marks two agents complete once two hook-reported agent sessions exist in one worktree', () => {
|
||||
const progress = getFeatureWallSetupProgress(
|
||||
makeInput({
|
||||
worktreesByRepo: { 'repo-1': [makeWorktree('worktree-1')] },
|
||||
tabsByWorktree: {
|
||||
'worktree-1': [
|
||||
{ id: 'tab-1', title: 'Terminal' },
|
||||
{ id: 'tab-2', title: 'Terminal' }
|
||||
] as never
|
||||
},
|
||||
agentStatusByPaneKey: {
|
||||
'tab-1:00000000-0000-4000-8000-000000000001': {
|
||||
paneKey: 'tab-1:00000000-0000-4000-8000-000000000001',
|
||||
state: 'working',
|
||||
prompt: 'first task',
|
||||
updatedAt: 1,
|
||||
stateStartedAt: 1,
|
||||
agentType: 'claude',
|
||||
stateHistory: []
|
||||
},
|
||||
'tab-2:00000000-0000-4000-8000-000000000002': {
|
||||
paneKey: 'tab-2:00000000-0000-4000-8000-000000000002',
|
||||
state: 'waiting',
|
||||
prompt: 'second task',
|
||||
updatedAt: 2,
|
||||
stateStartedAt: 2,
|
||||
agentType: 'codex',
|
||||
stateHistory: []
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
expect(progress.stepDone['two-agents']).toBe(true)
|
||||
})
|
||||
|
||||
it('does not mark two agents complete when hook-reported agents are in separate worktrees', () => {
|
||||
const progress = getFeatureWallSetupProgress(
|
||||
makeInput({
|
||||
worktreesByRepo: {
|
||||
'repo-1': [makeWorktree('worktree-1')],
|
||||
'repo-2': [makeWorktree('worktree-2')]
|
||||
},
|
||||
tabsByWorktree: {
|
||||
'worktree-1': [{ id: 'tab-1', title: 'Terminal' }] as never,
|
||||
'worktree-2': [{ id: 'tab-2', title: 'Terminal' }] as never
|
||||
},
|
||||
agentStatusByPaneKey: {
|
||||
'tab-1:00000000-0000-4000-8000-000000000001': {
|
||||
paneKey: 'tab-1:00000000-0000-4000-8000-000000000001',
|
||||
state: 'working',
|
||||
prompt: 'first task',
|
||||
updatedAt: 1,
|
||||
stateStartedAt: 1,
|
||||
agentType: 'claude',
|
||||
stateHistory: []
|
||||
},
|
||||
'tab-2:00000000-0000-4000-8000-000000000002': {
|
||||
paneKey: 'tab-2:00000000-0000-4000-8000-000000000002',
|
||||
state: 'working',
|
||||
prompt: 'second task',
|
||||
updatedAt: 2,
|
||||
stateStartedAt: 2,
|
||||
agentType: 'codex',
|
||||
stateHistory: []
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
expect(progress.stepDone['two-agents']).toBe(false)
|
||||
})
|
||||
|
||||
it('marks worktrees complete once two worktrees exist', () => {
|
||||
expect(
|
||||
getFeatureWallSetupProgress(
|
||||
|
||||
@@ -4,7 +4,8 @@ import {
|
||||
type FeatureWallSetupStepId
|
||||
} from '../../../../shared/feature-wall-setup-steps'
|
||||
import type { GlobalSettings, TerminalTab, Worktree } from '../../../../shared/types'
|
||||
import { getAgentLabel } from '../../lib/agent-status'
|
||||
import type { AgentStatusEntry } from '../../../../shared/agent-status-types'
|
||||
import { parsePaneKey } from '../../../../shared/stable-pane-id'
|
||||
|
||||
export type FeatureWallSetupProgressInput = {
|
||||
settings: GlobalSettings | null
|
||||
@@ -17,7 +18,7 @@ export type FeatureWallSetupProgressInput = {
|
||||
gitRepoCount: number
|
||||
worktreesByRepo: Record<string, Worktree[]>
|
||||
tabsByWorktree: Record<string, TerminalTab[]>
|
||||
runtimePaneTitlesByTabId: Record<string, Record<number, string>>
|
||||
agentStatusByPaneKey: Record<string, AgentStatusEntry>
|
||||
hasSetupScript: boolean
|
||||
}
|
||||
|
||||
@@ -27,30 +28,38 @@ export type FeatureWallSetupProgress = {
|
||||
coreTotal: number
|
||||
}
|
||||
|
||||
function hasTwoAgentSessionsInOneWorktree(input: FeatureWallSetupProgressInput): boolean {
|
||||
function hasTwoHookReportedAgentsInOneWorktree(input: FeatureWallSetupProgressInput): boolean {
|
||||
const validWorktreeIds = new Set(
|
||||
Object.values(input.worktreesByRepo)
|
||||
.flat()
|
||||
.map((worktree) => worktree.id)
|
||||
)
|
||||
const tabIdToWorktreeId = new Map<string, string>()
|
||||
for (const [worktreeId, tabs] of Object.entries(input.tabsByWorktree)) {
|
||||
if (
|
||||
!Object.values(input.worktreesByRepo).some((worktrees) =>
|
||||
worktrees.some((w) => w.id === worktreeId)
|
||||
)
|
||||
) {
|
||||
if (!validWorktreeIds.has(worktreeId)) {
|
||||
continue
|
||||
}
|
||||
let agentSessionCount = 0
|
||||
for (const tab of tabs) {
|
||||
const paneTitles = input.runtimePaneTitlesByTabId[tab.id]
|
||||
if (paneTitles && Object.keys(paneTitles).length > 0) {
|
||||
agentSessionCount += Object.values(paneTitles).filter((title) =>
|
||||
getAgentLabel(title)
|
||||
).length
|
||||
} else if (getAgentLabel(tab.title)) {
|
||||
agentSessionCount += 1
|
||||
}
|
||||
if (agentSessionCount >= 2) {
|
||||
return true
|
||||
}
|
||||
tabIdToWorktreeId.set(tab.id, worktreeId)
|
||||
}
|
||||
}
|
||||
|
||||
const agentCountsByWorktree = new Map<string, number>()
|
||||
for (const paneKey of Object.keys(input.agentStatusByPaneKey)) {
|
||||
const parsed = parsePaneKey(paneKey)
|
||||
if (!parsed) {
|
||||
continue
|
||||
}
|
||||
const worktreeId = tabIdToWorktreeId.get(parsed.tabId)
|
||||
if (!worktreeId) {
|
||||
continue
|
||||
}
|
||||
const count = (agentCountsByWorktree.get(worktreeId) ?? 0) + 1
|
||||
if (count >= 2) {
|
||||
return true
|
||||
}
|
||||
agentCountsByWorktree.set(worktreeId, count)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -73,7 +82,7 @@ export function getFeatureWallSetupProgress(
|
||||
notifications:
|
||||
input.settings?.notifications.enabled === true &&
|
||||
input.settings.notifications.agentTaskComplete === true,
|
||||
'two-agents': hasTwoAgentSessionsInOneWorktree(input),
|
||||
'two-agents': hasTwoHookReportedAgentsInOneWorktree(input),
|
||||
'three-workspaces': countWorkspaces(input.worktreesByRepo) >= 2,
|
||||
'task-sources': input.hasConnectedTaskSource,
|
||||
'agent-capabilities': agentCapabilitiesDone,
|
||||
|
||||
@@ -26,7 +26,7 @@ export function useSetupGuideProgress(
|
||||
const featureInteractions = useAppStore((s) => s.featureInteractions)
|
||||
const worktreesByRepo = useAppStore((s) => s.worktreesByRepo)
|
||||
const tabsByWorktree = useAppStore((s) => s.tabsByWorktree)
|
||||
const runtimePaneTitlesByTabId = useAppStore((s) => s.runtimePaneTitlesByTabId)
|
||||
const agentStatusByPaneKey = useAppStore((s) => s.agentStatusByPaneKey)
|
||||
const preflightStatus = useAppStore((s) => s.preflightStatus)
|
||||
const preflightStatusChecked = useAppStore((s) => s.preflightStatusChecked)
|
||||
const refreshPreflightStatus = useAppStore((s) => s.refreshPreflightStatus)
|
||||
@@ -168,7 +168,7 @@ export function useSetupGuideProgress(
|
||||
gitRepoCount,
|
||||
worktreesByRepo,
|
||||
tabsByWorktree,
|
||||
runtimePaneTitlesByTabId,
|
||||
agentStatusByPaneKey,
|
||||
hasSetupScript
|
||||
}),
|
||||
[
|
||||
@@ -178,11 +178,11 @@ export function useSetupGuideProgress(
|
||||
detectedBrowserUseSkillInstalled,
|
||||
detectedOrchestrationSkillInstalled,
|
||||
featureInteractions,
|
||||
agentStatusByPaneKey,
|
||||
gitRepoCount,
|
||||
hasConnectedTaskSource,
|
||||
hasSetupScript,
|
||||
orchestrationSkillInstalled,
|
||||
runtimePaneTitlesByTabId,
|
||||
settings,
|
||||
tabsByWorktree,
|
||||
worktreesByRepo
|
||||
|
||||
@@ -48,8 +48,8 @@ export const FEATURE_WALL_SETUP_STEPS: readonly FeatureWallSetupStep[] = [
|
||||
},
|
||||
{
|
||||
id: 'agent-capabilities',
|
||||
name: 'Enable advanced agent capabilities',
|
||||
subtitle: 'Enable advanced agent capabilities',
|
||||
name: 'Install CLI & Skills',
|
||||
subtitle: 'Install CLI & Skills',
|
||||
description: 'Enable the tools that let agents work more independently.'
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user