refactor: update comment formatting and notification handling in maintenance and tasks
- Modified comment notification formatting in tasks.js to enhance clarity by using bold text for task references. - Updated the automated comment notification detection in maintenance.js to align with the new formatting. - Adjusted test cases to reflect the updated comment structure for consistency in notifications.
This commit is contained in:
parent
92dd8f445f
commit
a29d8403d6
8 changed files with 37 additions and 24 deletions
|
|
@ -82,7 +82,7 @@ function isAutomatedCommentNotification(message) {
|
|||
if (!text) return false;
|
||||
|
||||
if (text.includes('Reply to this comment using:')) return true;
|
||||
if (text.startsWith('Comment on task #')) return true;
|
||||
if (text.startsWith('**Comment on task')) return true;
|
||||
if (text.startsWith('New comment from user on your task #')) return true;
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,8 @@ function buildAssignmentMessage(context, task, options = {}) {
|
|||
function buildCommentNotificationMessage(context, task, comment) {
|
||||
const taskLabel = `#${task.displayId || task.id}`;
|
||||
return [
|
||||
`Comment on task ${taskLabel} "${task.subject}":`,
|
||||
`**Comment on task ${taskLabel}**`,
|
||||
`> ${task.subject}`,
|
||||
``,
|
||||
comment.text,
|
||||
``,
|
||||
|
|
|
|||
|
|
@ -416,7 +416,7 @@ describe('agent-teams-controller API', () => {
|
|||
timestamp: '2026-02-23T11:00:00.000Z',
|
||||
read: false,
|
||||
text:
|
||||
`Comment on task #${task.displayId} "Ship migration":\n\nHeads up\n\n` +
|
||||
`**Comment on task #${task.displayId}**\n> Ship migration\n\nHeads up\n\n` +
|
||||
'<agent-block>\nReply to this comment using:\nnode "tool.js" --team my-team task comment 1 --text "..." --from "bob"\n</agent-block>',
|
||||
},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -259,6 +259,7 @@ export class TeamMemberLogsFinder {
|
|||
if (s) results.push(s);
|
||||
}
|
||||
const totalFiles = candidates.length;
|
||||
const step2Count = results.length; // count before step 3 (owner fallback)
|
||||
const tScan = performance.now();
|
||||
|
||||
const normalizedOwner =
|
||||
|
|
@ -350,13 +351,11 @@ export class TeamMemberLogsFinder {
|
|||
const tTotal = performance.now();
|
||||
|
||||
console.log(
|
||||
`[perf] findLogsForTask(${taskId}@${teamName}) ` +
|
||||
`total=${(tTotal - t0).toFixed(0)}ms | ` +
|
||||
`discovery=${(tDiscovery - t0).toFixed(0)}ms | ` +
|
||||
`lead=${(tLead - tDiscovery).toFixed(0)}ms | ` +
|
||||
`scan=${(tScan - tLead).toFixed(0)}ms (${totalFiles} files, ${mentionHits} hits) | ` +
|
||||
`owner=${(tOwner - tScan).toFixed(0)}ms | ` +
|
||||
`sessions=${sessionIds.length} | results=${sorted.length}`
|
||||
`[findLogsForTask] task=${taskId}@${teamName} | ` +
|
||||
`step2=${step2Count} (scan ${mentionHits}/${totalFiles} files) | ` +
|
||||
`step3=${sorted.length - step2Count} (owner=${normalizedOwner ?? 'none'}, includeOwner=${includeOwnerSessions}) | ` +
|
||||
`total=${sorted.length} | ` +
|
||||
`${(tTotal - t0).toFixed(0)}ms`
|
||||
);
|
||||
|
||||
return sorted;
|
||||
|
|
@ -975,6 +974,12 @@ export class TeamMemberLogsFinder {
|
|||
const b = block as Record<string, unknown>;
|
||||
if (b.type !== 'tool_use') continue;
|
||||
|
||||
// Skip read-only task tools — they reference taskId but don't indicate
|
||||
// that this session actually WORKED on the task. Agents commonly call
|
||||
// task_get to check dependencies from other tasks, producing false matches.
|
||||
const toolName = typeof b.name === 'string' ? b.name : '';
|
||||
if (toolName === 'task_get' || toolName === 'mcp__agent-teams__task_get') continue;
|
||||
|
||||
const input = b.input as Record<string, unknown> | undefined;
|
||||
if (!input) continue;
|
||||
|
||||
|
|
|
|||
|
|
@ -812,6 +812,7 @@ export const MarkdownViewer: React.FC<MarkdownViewerProps> = ({
|
|||
components={components}
|
||||
urlTransform={allowCustomProtocols}
|
||||
allowElement={isAllowedElement}
|
||||
unwrapDisallowed
|
||||
>
|
||||
{content}
|
||||
</ReactMarkdown>
|
||||
|
|
|
|||
|
|
@ -104,8 +104,8 @@ type KanbanViewMode = 'grid' | 'columns';
|
|||
const COLUMNS: { id: KanbanColumnId; title: string }[] = [
|
||||
{ id: 'todo', title: 'TODO' },
|
||||
{ id: 'in_progress', title: 'IN PROGRESS' },
|
||||
{ id: 'done', title: 'DONE' },
|
||||
{ id: 'review', title: 'REVIEW' },
|
||||
{ id: 'done', title: 'DONE' },
|
||||
{ id: 'approved', title: 'APPROVED' },
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ const DEFAULT_ITEM_HEIGHT = Math.max(
|
|||
);
|
||||
const DEFAULT_MIN_HEIGHT = 10;
|
||||
const DEFAULT_MIN_WIDTH = 3;
|
||||
const GRID_SCOPE_KEY = 'kanban-grid-layout:global';
|
||||
const GRID_SCOPE_KEY = 'kanban-grid-layout:global:v2';
|
||||
const SKELETON_HIDE_DELAY_MS = 500;
|
||||
const RESIZE_HANDLES: ResizeHandleAxis[] = ['s', 'w', 'e', 'n', 'sw', 'nw', 'se', 'ne'];
|
||||
const WidthAwareGridLayout = WidthProvider(ReactGridLayout);
|
||||
|
|
@ -54,16 +54,19 @@ interface LoadingKanbanGridLayoutProps {
|
|||
readonly visibleItems: PersistedGridLayoutItem[];
|
||||
}
|
||||
|
||||
const ITEMS_PER_FIRST_ROW = 3;
|
||||
const SECOND_ROW_ITEM_WIDTH = 6;
|
||||
|
||||
function buildDefaultItems(itemIds: string[]): PersistedGridLayoutItem[] {
|
||||
return itemIds.map((id, index) => ({
|
||||
id,
|
||||
x: (index % 3) * DEFAULT_ITEM_WIDTH,
|
||||
y: Math.floor(index / 3) * DEFAULT_ITEM_HEIGHT,
|
||||
w: DEFAULT_ITEM_WIDTH,
|
||||
h: DEFAULT_ITEM_HEIGHT,
|
||||
minW: DEFAULT_MIN_WIDTH,
|
||||
minH: DEFAULT_MIN_HEIGHT,
|
||||
}));
|
||||
return itemIds.map((id, index) => {
|
||||
const isSecondRow = index >= ITEMS_PER_FIRST_ROW;
|
||||
const w = isSecondRow ? SECOND_ROW_ITEM_WIDTH : DEFAULT_ITEM_WIDTH;
|
||||
const x = isSecondRow
|
||||
? (index - ITEMS_PER_FIRST_ROW) * SECOND_ROW_ITEM_WIDTH
|
||||
: index * DEFAULT_ITEM_WIDTH;
|
||||
const y = isSecondRow ? DEFAULT_ITEM_HEIGHT : 0;
|
||||
return { id, x, y, w, h: DEFAULT_ITEM_HEIGHT, minW: DEFAULT_MIN_WIDTH, minH: DEFAULT_MIN_HEIGHT };
|
||||
});
|
||||
}
|
||||
|
||||
function toReactGridLayoutItem(item: PersistedGridLayoutItem): LayoutItem {
|
||||
|
|
|
|||
|
|
@ -188,6 +188,9 @@ export const MentionSuggestionList = ({
|
|||
>
|
||||
<HighlightedName name={isTask ? `#${s.name}` : s.name} query={query} />
|
||||
</span>
|
||||
{!isTask && !isFileOrFolder && s.subtitle ? (
|
||||
<span className="truncate text-[var(--color-text-muted)]">{s.subtitle}</span>
|
||||
) : null}
|
||||
{isTask && s.ownerName ? (
|
||||
<MemberBadge name={s.ownerName} color={s.ownerColor} size="xs" disableHoverCard />
|
||||
) : null}
|
||||
|
|
@ -213,12 +216,12 @@ export const MentionSuggestionList = ({
|
|||
title={s.isOnline ? 'Online' : 'Offline'}
|
||||
/>
|
||||
) : null}
|
||||
{s.subtitle && !isTask ? (
|
||||
{s.subtitle && isFileOrFolder ? (
|
||||
<span
|
||||
className="truncate text-[var(--color-text-muted)]"
|
||||
style={isFileOrFolder ? { direction: 'rtl', textAlign: 'left' } : undefined}
|
||||
style={{ direction: 'rtl', textAlign: 'left' }}
|
||||
>
|
||||
{isFileOrFolder ? '\u200E' + s.subtitle : s.subtitle}
|
||||
{'\u200E' + s.subtitle}
|
||||
</span>
|
||||
) : null}
|
||||
</li>
|
||||
|
|
|
|||
Loading…
Reference in a new issue