feat(server): improve task CLI with steering, execution order, and planner guidance

- Fix steering notes to check scope task instead of leaf task
- Fix NOT_DONE replan to use scope for bird's eye view
- Pass reason to planner prompt ("Why you were called" section)
- Add execution-order.ts with reusable computeExecutionOrder and buildSortedChildrenMap
- Make 'tree' an alias for 'plan' command
- Add comprehensive help text for run command and acceptance criteria
- Add planner guidance for propagating requirements to subtasks
This commit is contained in:
Mohamed Boudra
2026-01-17 11:46:38 +07:00
parent 420f25f1e9
commit d964415eba
33 changed files with 5205 additions and 3922 deletions
+4 -1
View File
@@ -1067,6 +1067,7 @@ interface ExpandableBadgeProps {
isError?: boolean;
isLastInSequence?: boolean;
disableOuterSpacing?: boolean;
testID?: string;
}
const ExpandableBadge = memo(function ExpandableBadge({
@@ -1081,6 +1082,7 @@ const ExpandableBadge = memo(function ExpandableBadge({
isError = false,
isLastInSequence = false,
disableOuterSpacing,
testID,
}: ExpandableBadgeProps) {
const { theme } = useUnistyles();
const resolvedDisableOuterSpacing =
@@ -1140,7 +1142,7 @@ const ExpandableBadge = memo(function ExpandableBadge({
}
return (
<View style={[expandableBadgeStylesheet.container, style]}>
<View style={[expandableBadgeStylesheet.container, style]} testID={testID}>
<Pressable
onPress={hasDetails ? onToggle : undefined}
disabled={!hasDetails}
@@ -1495,6 +1497,7 @@ export const ToolCall = memo(function ToolCall({
return (
<ExpandableBadge
testID="tool-call-badge"
label={toolName}
secondaryLabel={principalParam}
icon={IconComponent}
+1 -29
View File
@@ -65,19 +65,6 @@ function toolTimeline(
};
}
function permissionTimeline(id: string, status: string): AgentStreamEventPayload {
return {
type: "timeline",
provider: "claude",
item: {
type: "tool_call",
name: "permission_request",
status,
callId: id,
},
};
}
function todoTimeline(items: { text: string; completed: boolean }[]): AgentStreamEventPayload {
return {
type: "timeline",
@@ -643,21 +630,7 @@ function testHydratedUserMessagesPersist() {
});
}
// Test 8: Permission tool calls should not show in the timeline
function testPermissionToolCallFiltering() {
const timestamp = new Date('2025-01-01T12:00:00Z');
const updates = [
{ event: permissionTimeline('permission-1', 'pending'), timestamp },
{ event: permissionTimeline('permission-1', 'granted'), timestamp },
];
const state = hydrateStreamState(updates);
const permissionEntries = state.filter(isAgentToolCallItem);
assert.strictEqual(permissionEntries.length, 0);
}
// Test 9: Todo lists should consolidate into a single entry and update completions
// Test 8: Todo lists should consolidate into a single entry and update completions
function testTodoListConsolidation() {
const timestamp1 = new Date('2025-01-01T12:30:00Z');
const timestamp2 = new Date('2025-01-01T12:31:00Z');
@@ -1064,7 +1037,6 @@ describe('stream timeline reducers', () => {
it('preserves whitespace in assistant chunk concatenation', testAssistantWhitespacePreservation);
it('hydrates user messages and deduplicates optimistic/live entries', testUserMessageHydration);
it('retains hydrated user messages across providers', testHydratedUserMessagesPersist);
it('filters permission tool calls from the stream', testPermissionToolCallFiltering);
it('consolidates todo list updates', testTodoListConsolidation);
it('keeps timeline ids stable after list shrinkage', testTimelineIdStabilityAfterRemovals);
it('deduplicates live tool call entries', testToolCallDeduplicationLive);
-11
View File
@@ -480,14 +480,6 @@ function appendAgentToolCall(
return [...state, item];
}
function isPermissionToolCall(raw: unknown): boolean {
if (!raw || typeof raw !== "object") {
return false;
}
const candidate = raw as { name?: string };
return candidate.name === "permission_request";
}
const FAILED_STATUS_PATTERN =
/fail|error|deny|reject|cancel|abort|exception|refus/;
const COMPLETED_STATUS_PATTERN =
@@ -762,9 +754,6 @@ export function reduceStreamUpdate(
case "reasoning":
return appendThought(state, item.text, timestamp);
case "tool_call": {
if (isPermissionToolCall(item)) {
return state;
}
nextState = appendAgentToolCall(
state,
{