mirror of
http://10.0.2.1:3031/sauer/claude-code.git
synced 2026-06-30 20:46:58 +10:00
222 lines
25 KiB
TypeScript
222 lines
25 KiB
TypeScript
|
|
import { c as _c } from "react/compiler-runtime";
|
||
|
|
import * as React from 'react';
|
||
|
|
import { Markdown } from '../../components/Markdown.js';
|
||
|
|
import { Box, Text } from '../../ink.js';
|
||
|
|
import { jsonParse } from '../../utils/slowOperations.js';
|
||
|
|
import { type IdleNotificationMessage, isIdleNotification, isPlanApprovalRequest, isPlanApprovalResponse, type PlanApprovalRequestMessage, type PlanApprovalResponseMessage } from '../../utils/teammateMailbox.js';
|
||
|
|
import { getShutdownMessageSummary } from './ShutdownMessage.js';
|
||
|
|
import { getTaskAssignmentSummary } from './TaskAssignmentMessage.js';
|
||
|
|
type PlanApprovalRequestProps = {
|
||
|
|
request: PlanApprovalRequestMessage;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Renders a plan approval request with a planMode-colored border,
|
||
|
|
* showing the plan content and instructions for approving/rejecting.
|
||
|
|
*/
|
||
|
|
export function PlanApprovalRequestDisplay(t0) {
|
||
|
|
const $ = _c(10);
|
||
|
|
const {
|
||
|
|
request
|
||
|
|
} = t0;
|
||
|
|
let t1;
|
||
|
|
if ($[0] !== request.from) {
|
||
|
|
t1 = <Box marginBottom={1}><Text color="planMode" bold={true}>Plan Approval Request from {request.from}</Text></Box>;
|
||
|
|
$[0] = request.from;
|
||
|
|
$[1] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[1];
|
||
|
|
}
|
||
|
|
let t2;
|
||
|
|
if ($[2] !== request.planContent) {
|
||
|
|
t2 = <Box borderStyle="dashed" borderColor="subtle" borderLeft={false} borderRight={false} flexDirection="column" paddingX={1} marginBottom={1}><Markdown>{request.planContent}</Markdown></Box>;
|
||
|
|
$[2] = request.planContent;
|
||
|
|
$[3] = t2;
|
||
|
|
} else {
|
||
|
|
t2 = $[3];
|
||
|
|
}
|
||
|
|
let t3;
|
||
|
|
if ($[4] !== request.planFilePath) {
|
||
|
|
t3 = <Text dimColor={true}>Plan file: {request.planFilePath}</Text>;
|
||
|
|
$[4] = request.planFilePath;
|
||
|
|
$[5] = t3;
|
||
|
|
} else {
|
||
|
|
t3 = $[5];
|
||
|
|
}
|
||
|
|
let t4;
|
||
|
|
if ($[6] !== t1 || $[7] !== t2 || $[8] !== t3) {
|
||
|
|
t4 = <Box flexDirection="column" marginY={1}><Box borderStyle="round" borderColor="planMode" flexDirection="column" paddingX={1}>{t1}{t2}{t3}</Box></Box>;
|
||
|
|
$[6] = t1;
|
||
|
|
$[7] = t2;
|
||
|
|
$[8] = t3;
|
||
|
|
$[9] = t4;
|
||
|
|
} else {
|
||
|
|
t4 = $[9];
|
||
|
|
}
|
||
|
|
return t4;
|
||
|
|
}
|
||
|
|
type PlanApprovalResponseProps = {
|
||
|
|
response: PlanApprovalResponseMessage;
|
||
|
|
senderName: string;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Renders a plan approval response with a success (green) or error (red) border.
|
||
|
|
*/
|
||
|
|
export function PlanApprovalResponseDisplay(t0) {
|
||
|
|
const $ = _c(13);
|
||
|
|
const {
|
||
|
|
response,
|
||
|
|
senderName
|
||
|
|
} = t0;
|
||
|
|
if (response.approved) {
|
||
|
|
let t1;
|
||
|
|
if ($[0] !== senderName) {
|
||
|
|
t1 = <Box><Text color="success" bold={true}>✓ Plan Approved by {senderName}</Text></Box>;
|
||
|
|
$[0] = senderName;
|
||
|
|
$[1] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[1];
|
||
|
|
}
|
||
|
|
let t2;
|
||
|
|
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||
|
|
t2 = <Box marginTop={1}><Text>You can now proceed with implementation. Your plan mode restrictions have been lifted.</Text></Box>;
|
||
|
|
$[2] = t2;
|
||
|
|
} else {
|
||
|
|
t2 = $[2];
|
||
|
|
}
|
||
|
|
let t3;
|
||
|
|
if ($[3] !== t1) {
|
||
|
|
t3 = <Box flexDirection="column" marginY={1}><Box borderStyle="round" borderColor="success" flexDirection="column" paddingX={1} paddingY={1}>{t1}{t2}</Box></Box>;
|
||
|
|
$[3] = t1;
|
||
|
|
$[4] = t3;
|
||
|
|
} else {
|
||
|
|
t3 = $[4];
|
||
|
|
}
|
||
|
|
return t3;
|
||
|
|
}
|
||
|
|
let t1;
|
||
|
|
if ($[5] !== senderName) {
|
||
|
|
t1 = <Box><Text color="error" bold={true}>✗ Plan Rejected by {senderName}</Text></Box>;
|
||
|
|
$[5] = senderName;
|
||
|
|
$[6] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[6];
|
||
|
|
}
|
||
|
|
let t2;
|
||
|
|
if ($[7] !== response.feedback) {
|
||
|
|
t2 = response.feedback && <Box marginTop={1} borderStyle="dashed" borderColor="subtle" borderLeft={false} borderRight={false} paddingX={1}><Text>Feedback: {response.feedback}</Text></Box>;
|
||
|
|
$[7] = response.feedback;
|
||
|
|
$[8] = t2;
|
||
|
|
} else {
|
||
|
|
t2 = $[8];
|
||
|
|
}
|
||
|
|
let t3;
|
||
|
|
if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
|
||
|
|
t3 = <Box marginTop={1}><Text dimColor={true}>Please revise your plan based on the feedback and call ExitPlanMode again.</Text></Box>;
|
||
|
|
$[9] = t3;
|
||
|
|
} else {
|
||
|
|
t3 = $[9];
|
||
|
|
}
|
||
|
|
let t4;
|
||
|
|
if ($[10] !== t1 || $[11] !== t2) {
|
||
|
|
t4 = <Box flexDirection="column" marginY={1}><Box borderStyle="round" borderColor="error" flexDirection="column" paddingX={1} paddingY={1}>{t1}{t2}{t3}</Box></Box>;
|
||
|
|
$[10] = t1;
|
||
|
|
$[11] = t2;
|
||
|
|
$[12] = t4;
|
||
|
|
} else {
|
||
|
|
t4 = $[12];
|
||
|
|
}
|
||
|
|
return t4;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Try to parse and render a plan approval message from raw content.
|
||
|
|
* Returns the rendered component if it's a plan approval message, null otherwise.
|
||
|
|
*/
|
||
|
|
export function tryRenderPlanApprovalMessage(content: string, senderName: string): React.ReactNode | null {
|
||
|
|
const request = isPlanApprovalRequest(content);
|
||
|
|
if (request) {
|
||
|
|
return <PlanApprovalRequestDisplay request={request} />;
|
||
|
|
}
|
||
|
|
const response = isPlanApprovalResponse(content);
|
||
|
|
if (response) {
|
||
|
|
return <PlanApprovalResponseDisplay response={response} senderName={senderName} />;
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get a brief summary text for a plan approval message.
|
||
|
|
* Used in places like the inbox queue where we want a short description.
|
||
|
|
* Returns null if the content is not a plan approval message.
|
||
|
|
*/
|
||
|
|
function getPlanApprovalSummary(content: string): string | null {
|
||
|
|
const request = isPlanApprovalRequest(content);
|
||
|
|
if (request) {
|
||
|
|
return `[Plan Approval Request from ${request.from}]`;
|
||
|
|
}
|
||
|
|
const response = isPlanApprovalResponse(content);
|
||
|
|
if (response) {
|
||
|
|
if (response.approved) {
|
||
|
|
return '[Plan Approved] You can now proceed with implementation';
|
||
|
|
} else {
|
||
|
|
return `[Plan Rejected] ${response.feedback || 'Please revise your plan'}`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get a brief summary text for an idle notification.
|
||
|
|
*/
|
||
|
|
function getIdleNotificationSummary(msg: IdleNotificationMessage): string {
|
||
|
|
const parts: string[] = ['Agent idle'];
|
||
|
|
if (msg.completedTaskId) {
|
||
|
|
const status = msg.completedStatus || 'completed';
|
||
|
|
parts.push(`Task ${msg.completedTaskId} ${status}`);
|
||
|
|
}
|
||
|
|
if (msg.summary) {
|
||
|
|
parts.push(`Last DM: ${msg.summary}`);
|
||
|
|
}
|
||
|
|
return parts.join(' · ');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Format teammate message content for display.
|
||
|
|
* If it's a structured message (plan approval, shutdown, or idle), returns a formatted summary.
|
||
|
|
* Otherwise returns the original content.
|
||
|
|
*/
|
||
|
|
export function formatTeammateMessageContent(content: string): string {
|
||
|
|
const planSummary = getPlanApprovalSummary(content);
|
||
|
|
if (planSummary) {
|
||
|
|
return planSummary;
|
||
|
|
}
|
||
|
|
const shutdownSummary = getShutdownMessageSummary(content);
|
||
|
|
if (shutdownSummary) {
|
||
|
|
return shutdownSummary;
|
||
|
|
}
|
||
|
|
const idleMsg = isIdleNotification(content);
|
||
|
|
if (idleMsg) {
|
||
|
|
return getIdleNotificationSummary(idleMsg);
|
||
|
|
}
|
||
|
|
const taskAssignmentSummary = getTaskAssignmentSummary(content);
|
||
|
|
if (taskAssignmentSummary) {
|
||
|
|
return taskAssignmentSummary;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check for teammate_terminated message
|
||
|
|
try {
|
||
|
|
const parsed = jsonParse(content) as {
|
||
|
|
type?: string;
|
||
|
|
message?: string;
|
||
|
|
};
|
||
|
|
if (parsed?.type === 'teammate_terminated' && parsed.message) {
|
||
|
|
return parsed.message;
|
||
|
|
}
|
||
|
|
} catch {
|
||
|
|
// Not JSON
|
||
|
|
}
|
||
|
|
return content;
|
||
|
|
}
|
||
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIk1hcmtkb3duIiwiQm94IiwiVGV4dCIsImpzb25QYXJzZSIsIklkbGVOb3RpZmljYXRpb25NZXNzYWdlIiwiaXNJZGxlTm90aWZpY2F0aW9uIiwiaXNQbGFuQXBwcm92YWxSZXF1ZXN0IiwiaXNQbGFuQXBwcm92YWxSZXNwb25zZSIsIlBsYW5BcHByb3ZhbFJlcXVlc3RNZXNzYWdlIiwiUGxhbkFwcHJvdmFsUmVzcG9uc2VNZXNzYWdlIiwiZ2V0U2h1dGRvd25NZXNzYWdlU3VtbWFyeSIsImdldFRhc2tBc3NpZ25tZW50U3VtbWFyeSIsIlBsYW5BcHByb3ZhbFJlcXVlc3RQcm9wcyIsInJlcXVlc3QiLCJQbGFuQXBwcm92YWxSZXF1ZXN0RGlzcGxheSIsInQwIiwiJCIsIl9jIiwidDEiLCJmcm9tIiwidDIiLCJwbGFuQ29udGVudCIsInQzIiwicGxhbkZpbGVQYXRoIiwidDQiLCJQbGFuQXBwcm92YWxSZXNwb25zZVByb3BzIiwicmVzcG9uc2UiLCJzZW5kZXJOYW1lIiwiUGxhbkFwcHJvdmFsUmVzcG9uc2VEaXNwbGF5IiwiYXBwcm92ZWQiLCJTeW1ib2wiLCJmb3IiLCJmZWVkYmFjayIsInRyeVJlbmRlclBsYW5BcHByb3ZhbE1lc3NhZ2UiLCJjb250ZW50IiwiUmVhY3ROb2RlIiwiZ2V0UGxhbkFwcHJvdmFsU3VtbWFyeSIsImdldElkbGVOb3RpZmljYXRpb25TdW1tYXJ5IiwibXNnIiwicGFydHMiLCJjb21wbGV0ZWRUYXNrSWQiLCJzdGF0dXMiLCJjb21wbGV0ZWRTdGF0dXMiLCJwdXNoIiwic3VtbWFyeSIsImpvaW4iLCJmb3JtYXRUZWFtbWF0ZU1lc3NhZ2VDb250ZW50IiwicGxhblN1bW1hcnkiLCJzaHV0ZG93blN1bW1hcnkiLCJpZGxlTXNnIiwidGFza0Fzc2lnbm1lbnRTdW1tYXJ5IiwicGFyc2VkIiwidHlwZSIsIm1lc3NhZ2UiXSwic291cmNlcyI6WyJQbGFuQXBwcm92YWxNZXNzYWdlLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IE1hcmtkb3duIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9NYXJrZG93bi5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IGpzb25QYXJzZSB9IGZyb20gJy4uLy4uL3V0aWxzL3Nsb3dPcGVyYXRpb25zLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBJZGxlTm90aWZpY2F0aW9uTWVzc2FnZSxcbiAgaXNJZGxlTm90aWZpY2F0aW9uLFxuICBpc1BsYW5BcHByb3ZhbFJlcXVlc3QsXG4gIGlzUGxhbkFwcHJvdmFsUmVzcG9uc2UsXG4gIHR5cGUgUGxhbkFwcHJvdmFsUmVxdWVzdE1lc3NhZ2UsXG4gIHR5cGUgUGxhbkFwcHJvdmFsUmVzcG9uc2VNZXNzYWdlLFxufSBmcm9tICcuLi8uLi91dGlscy90ZWFtbWF0ZU1haWxib3guanMnXG5pbXBvcnQgeyBnZXRTaHV0ZG93bk1lc3NhZ2VTdW1tYXJ5IH0gZnJvbSAnLi9TaHV0ZG93bk1lc3NhZ2UuanMnXG5pbXBvcnQgeyBnZXRUYXNrQXNzaWdubWVudFN1bW1hcnkgfSBmcm9tICcuL1Rhc2tBc3NpZ25tZW50TWVzc2FnZS5qcydcblxudHlwZSBQbGFuQXBwcm92YWxSZXF1ZXN0UHJvcHMgPSB7XG4gIHJlcXVlc3Q6IFBsYW5BcHByb3ZhbFJlcXVlc3RNZXNzYWdlXG59XG5cbi8qKlxuICogUmVuZGVycyBhIHBsYW4gYXBwcm92YWwgcmVxdWVzdCB3aXRoIGEgcGxhbk1vZGUtY29sb3JlZCBib3JkZXIsXG4gKiBzaG93aW5nIHRoZSBwbGFuIGNvbnRlbnQgYW5kIGluc3RydWN0aW9ucyBmb3IgYXBwcm92aW5nL3JlamVjdGluZy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIFBsYW5BcHByb3ZhbFJlcXVlc3REaXNwbGF5KHtcbiAgcmVxdWVzdCxcbn06IFBsYW5BcHByb3ZhbFJlcXVlc3RQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luWT17MX0+XG4gICAgICA8Qm94XG4gICAgICAgIGJvcmRlclN0eWxlPVwicm91bmRcIlxuICAgICAgICBib3JkZXJDb2xvcj1cInBsYW5Nb2RlXCJcbiAgICAgICAgZmxleERpcmVjdGlvbj1cImNvbHVtblwiXG4gICAgICAgIHBhZGRpbmdYPXsxfVxuICAgICAgPlxuICAgICAgICA8Qm94IG1hcmdpbkJvdHRvbT17MX0+XG4gICAgICAgICAgPFRleHQgY29sb3I9XCJwbGFuTW9kZVwiIGJvbGQ+XG4gICAgICAgICAgICBQbGFuIEFwcHJvdmFsIFJlcXVlc3QgZnJvbSB7cmVxdWVzdC5mcm9tfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxCb3hcbiAgICAgICAgICBib3JkZXJTdHlsZT1cImRhc2hlZFwiXG4gICAgICAgICAgYm9yZGVyQ29sb3I9XCJzdWJ0bGVcIlxuICAgICAgICAgIGJvcmRlckxlZnQ9e2ZhbHNlfVxuICAgICAgICAgIGJvcmRlclJpZ2h0PXtmYWxzZX1cbiAgICAgICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgICAgICBwYWRkaW5nWD17MX1cbiAgICAgICAgICBtYXJnaW5Cb3R0b209ezF9XG4gICAgICAgID5cbiAgICAgICAgICA8TWFya2Rvd24+e3JlcXVlc3QucGxhbkNvbnRlbnR9PC9NYXJrZG93bj5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPlBsYW4gZmlsZToge3JlcXVlc3QucGxhbkZpbGVQYXRofTwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIDwvQm94PlxuICApXG59XG5cbnR5cGUgUGxhbkFwcHJvdmFsUmVzcG9uc2VQcm9wcyA9IHtcbiAgcmVzcG9uc2U6IFBsYW5BcHByb3ZhbFJlc3BvbnNlTWVzc2FnZVxuICBzZW5kZXJOYW1lOiBzdHJpbmdcbn1cblxuLyoqXG4gKiBSZW5kZXJzIGEgcGxhbiBhcHByb3ZhbCByZXNwb25zZSB3aXRoIGEgc3VjY2VzcyAoZ3JlZW4pIG9yIGVycm9yIChyZWQpIGJvcmRlci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIFBsYW5BcHByb3ZhbFJlc3BvbnNlRGlzcGxheSh7XG4gIHJlc3BvbnNlLFxuICBzZW5kZXJOYW1lLFxufTogUGxhbkFwcHJvdmFsUmVzcG9uc2VQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGlmIChyZXNwb25zZS5hcHByb3ZlZCkge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5ZPXsxfT5cbiAgICAgICAgPEJveFxuICAgICAgICAgIGJvcmRlclN
|