-
Notifications
You must be signed in to change notification settings - Fork 0
Track session context changes during task execution #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
575690c
9131475
475f697
fddde88
9b49a03
8cdb876
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react'; | |
| import { Box, Text, useInput } from 'ink'; | ||
| import type { Plan, Task } from '../models/plan.js'; | ||
| import { executePlan } from '../services/executor.js'; | ||
| import type { ExecutionOptions, ExecutionHandle } from '../services/executor.js'; | ||
| import type { ExecutionOptions, ExecutionHandle, SessionEventWithTask } from '../services/executor.js'; | ||
| import { savePlan, summarizePlan } from '../services/persistence.js'; | ||
| import { computeBatches } from '../utils/dependency-graph.js'; | ||
| import Spinner from '../components/spinner.js'; | ||
|
|
@@ -46,6 +46,8 @@ export default function ExecuteScreen({ | |
| const [runCount, setRunCount] = useState(0); // incremented to re-trigger execution | ||
| const execHandleRef = useRef<ExecutionHandle | null>(null); | ||
| const [summarized, setSummarized] = useState(''); | ||
| const [sessionEvents, setSessionEvents] = useState<SessionEventWithTask[]>([]); | ||
| const [taskContexts, setTaskContexts] = useState<Record<string, { cwd?: string; repository?: string; branch?: string }>>({}); | ||
|
|
||
| const { batches } = computeBatches(plan.tasks); | ||
| // Total display batches: init batch (index 0) + real batches | ||
|
|
@@ -195,6 +197,32 @@ export default function ExecuteScreen({ | |
| } | ||
| // Otherwise stay on execute screen — user can press 'r' to retry | ||
| }, | ||
| onSessionEvent: (eventWithTask) => { | ||
| const { event, taskId } = eventWithTask; | ||
|
|
||
| // Only store session events that we actually render / use, and keep history bounded | ||
| if (event.type === 'session.context_changed') { | ||
| const { cwd, repository, branch } = event.data; | ||
| setSessionEvents((prev) => { | ||
| const updated = [...prev, eventWithTask]; | ||
| return updated.slice(-100); // Keep last 100 events | ||
| }); | ||
| setTaskContexts((prev) => ({ | ||
| ...prev, | ||
| [taskId]: { cwd, repository, branch }, | ||
| })); | ||
| } else if (event.type === 'session.start' && event.data.context) { | ||
| const { cwd, repository, branch } = event.data.context; | ||
| setSessionEvents((prev) => { | ||
| const updated = [...prev, eventWithTask]; | ||
| return updated.slice(-100); // Keep last 100 events | ||
| }); | ||
| setTaskContexts((prev) => ({ | ||
| ...prev, | ||
| [taskId]: { cwd, repository, branch }, | ||
| })); | ||
| } | ||
| }, | ||
| }, execOptions); | ||
|
|
||
| execHandleRef.current = handle; | ||
|
|
@@ -318,18 +346,29 @@ export default function ExecuteScreen({ | |
| const isSelected = i === selectedTaskIndex; | ||
| const icon = STATUS_ICON[task.status] ?? '?'; | ||
| const color = STATUS_COLOR[task.status] ?? 'gray'; | ||
| const context = taskContexts[task.id]; | ||
| return ( | ||
| <Box key={task.id}> | ||
| <Text color={isSelected ? 'white' : 'gray'}>{isSelected ? '❯ ' : ' '}</Text> | ||
| <Text color={color}>{icon} </Text> | ||
| <Text color={isSelected ? 'white' : color} bold={isSelected}> | ||
| {task.id} | ||
| </Text> | ||
| <Text color="gray"> — {task.title}</Text> | ||
| {task.status === 'in_progress' && ( | ||
| <Text color="yellow"> </Text> | ||
| <Box key={task.id} flexDirection="column"> | ||
| <Box> | ||
| <Text color={isSelected ? 'white' : 'gray'}>{isSelected ? '❯ ' : ' '}</Text> | ||
| <Text color={color}>{icon} </Text> | ||
| <Text color={isSelected ? 'white' : color} bold={isSelected}> | ||
| {task.id} | ||
| </Text> | ||
| <Text color="gray"> — {task.title}</Text> | ||
| {task.status === 'in_progress' && ( | ||
| <Text color="yellow"> </Text> | ||
| )} | ||
| {task.status === 'in_progress' && <Spinner />} | ||
| </Box> | ||
| {context?.cwd && ( | ||
| <Box marginLeft={4}> | ||
| <Text color="cyan" dimColor>📁 {context.cwd}</Text> | ||
| {context.repository && ( | ||
| <Text color="blue" dimColor> ({context.repository})</Text> | ||
| )} | ||
|
Comment on lines
+364
to
+369
|
||
| </Box> | ||
| )} | ||
| {task.status === 'in_progress' && <Spinner />} | ||
| </Box> | ||
| ); | ||
| })} | ||
|
|
@@ -376,6 +415,32 @@ export default function ExecuteScreen({ | |
| </Box> | ||
| )} | ||
|
|
||
| {/* Context change events for selected task */} | ||
| {started && selectedTask && (() => { | ||
| const taskEvents = sessionEvents.filter( | ||
| (e) => e.taskId === selectedTask.id && e.event.type === 'session.context_changed' | ||
| ); | ||
| if (taskEvents.length === 0) return null; | ||
|
|
||
| return ( | ||
| <Box flexDirection="column" marginLeft={1} marginBottom={1}> | ||
| <Text color="cyan" bold>Context Changes:</Text> | ||
| {taskEvents.slice(-3).map((e) => { | ||
| if (e.event.type !== 'session.context_changed') return null; | ||
| const time = new Date(e.event.timestamp).toLocaleTimeString(); | ||
| const { cwd, repository, branch } = e.event.data; | ||
| return ( | ||
| <Box key={e.event.id}> | ||
| <Text color="gray">{time} </Text> | ||
| <Text color="cyan">→ {cwd}</Text> | ||
| {repository && <Text color="blue"> ({repository}{branch ? `@${branch}` : ''})</Text>} | ||
| </Box> | ||
| ); | ||
| })} | ||
| </Box> | ||
| ); | ||
| })()} | ||
|
|
||
| {/* Retry prompt when there are failures */} | ||
| {started && !executing && failedCount > 0 && ( | ||
| <Box marginBottom={1}> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sessionEventsandtaskContextsare kept across retries / reruns (runCountchanges) and are not cleared when restarting execution. This can show stale cwd/repo/branch from a previous run or from before a task retry. Consider resetting these states when a new run starts, and clearing per-task context when retrying that task.