Skip to content

Commit 3fb8c05

Browse files
igorcostaAutohand Evolve
andcommitted
Show all todo task states in progress output
Co-authored-by: Autohand Evolve <code-noreply@autohand.ai>
1 parent 72f0386 commit 3fb8c05

2 files changed

Lines changed: 59 additions & 8 deletions

File tree

src/core/actionExecutor.ts

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1582,26 +1582,49 @@ export class ActionExecutor {
15821582
return 'Task list cleared (0 tasks)';
15831583
}
15841584

1585-
const completed = allTodos.filter((t: any) => t.status === 'completed').length;
1585+
const completedTasks = allTodos.filter((t: any) => t.status === 'completed');
15861586
const inProgress = allTodos.filter((t: any) => t.status === 'in_progress');
1587-
const pending = allTodos.filter((t: any) => t.status === 'pending').length;
1587+
const pendingTasks = allTodos.filter((t: any) => t.status === 'pending');
1588+
const completed = completedTasks.length;
1589+
const pending = pendingTasks.length;
15881590

15891591
const percent = Math.round((completed / total) * 100);
15901592
const barWidth = 20;
15911593
const filled = Math.round((barWidth * percent) / 100);
15921594
const bar = '█'.repeat(filled) + '░'.repeat(barWidth - filled);
15931595

1594-
console.log(chalk.cyan('\n📋 Task Progress:'));
1595-
console.log(` ${chalk.green(bar)} ${percent}%`);
1596-
console.log(chalk.gray(` ${completed} done · ${inProgress.length} in progress · ${pending} pending`));
1596+
const titleOf = (task: Record<string, unknown>): string => {
1597+
const title = task.title ?? task.content;
1598+
return typeof title === 'string' && title.trim().length > 0 ? title : 'Untitled task';
1599+
};
1600+
const outputLines = [
1601+
chalk.cyan('\n📋 Task Progress:'),
1602+
` ${chalk.green(bar)} ${percent}%`,
1603+
chalk.gray(` ${completed} done · ${inProgress.length} in progress · ${pending} pending`)
1604+
];
1605+
1606+
if (completedTasks.length > 0) {
1607+
outputLines.push('', chalk.green(' ✅ Completed Tasks:'));
1608+
for (const task of completedTasks) {
1609+
outputLines.push(chalk.green(` ✓ ${titleOf(task)}`));
1610+
}
1611+
}
15971612

15981613
if (inProgress.length > 0) {
1599-
console.log(chalk.yellow('\n 🔄 Active Tasks:'));
1614+
outputLines.push('', chalk.yellow(' 🔄 Active Tasks:'));
16001615
for (const task of inProgress) {
1601-
console.log(` • ${(task as any).title || (task as any).content}`);
1616+
outputLines.push(chalk.yellow(` • ${titleOf(task)}`));
16021617
}
16031618
}
1604-
console.log();
1619+
1620+
if (pendingTasks.length > 0) {
1621+
outputLines.push('', chalk.cyan(' ⏳ Pending Tasks:'));
1622+
for (const task of pendingTasks) {
1623+
outputLines.push(chalk.dim(` ○ ${titleOf(task)}`));
1624+
}
1625+
}
1626+
1627+
console.log(`${outputLines.join('\n')}\n`);
16051628

16061629
return `Updated task list: ${percent}% complete (${completed}/${total})`;
16071630
}

tests/actionExecutor.spec.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66
import { describe, it, expect, vi, beforeEach } from 'vitest';
7+
import stripAnsi from 'strip-ansi';
78
import type { AgentRuntime } from '../src/types.js';
89
import type { FileActionManager } from '../src/actions/filesystem.js';
910
import { ActionExecutor } from '../src/core/actionExecutor.js';
@@ -957,6 +958,33 @@ describe('ActionExecutor', () => {
957958
expect(result).toContain('0%'); // in_progress doesn't count as completed
958959
});
959960

961+
it('prints completed, active, and pending tasks in the progress output', async () => {
962+
const readFile = vi.fn().mockRejectedValue(new Error('not found'));
963+
const writeFile = vi.fn().mockResolvedValue(undefined);
964+
const executor = createExecutor({ readFile, writeFile });
965+
const log = vi.spyOn(console, 'log').mockImplementation(() => undefined);
966+
967+
try {
968+
await executor.execute({
969+
type: 'todo_write',
970+
tasks: [
971+
{ id: '1', title: 'Set up project shell', status: 'completed' },
972+
{ id: '2', title: 'Wire game state', status: 'in_progress' },
973+
{ id: '3', title: 'Persist high score', status: 'pending' }
974+
]
975+
} as any);
976+
const output = stripAnsi(log.mock.calls.map(([message]) => String(message)).join('\n'));
977+
expect(output).toContain('✅ Completed Tasks:');
978+
expect(output).toContain('✓ Set up project shell');
979+
expect(output).toContain('🔄 Active Tasks:');
980+
expect(output).toContain('• Wire game state');
981+
expect(output).toContain('⏳ Pending Tasks:');
982+
expect(output).toContain('○ Persist high score');
983+
} finally {
984+
log.mockRestore();
985+
}
986+
});
987+
960988
it('auto-generates ids for tasks without id', async () => {
961989
const readFile = vi.fn().mockRejectedValue(new Error('not found'));
962990
const writeFile = vi.fn().mockResolvedValue(undefined);

0 commit comments

Comments
 (0)