@nanocollective/nanocoder
Version:
A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter
142 lines • 5.34 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import { TaskListDisplay } from '../../components/task-list-display.js';
import { jsonSchema, tool } from '../../types/core.js';
import { loadTasks, saveTasks } from './storage.js';
const executeUpdateTask = async (args) => {
const tasks = await loadTasks();
const now = new Date().toISOString();
const results = [];
for (const update of args.updates) {
const taskIndex = tasks.findIndex(t => t.id === update.id);
if (taskIndex === -1) {
results.push(` ✗ Task not found: ${update.id}`);
continue;
}
const task = tasks[taskIndex];
if (update.status !== undefined) {
task.status = update.status;
if (update.status === 'completed') {
task.completedAt = now;
}
else {
task.completedAt = undefined;
}
}
if (update.title !== undefined) {
task.title = update.title;
}
if (update.description !== undefined) {
task.description = update.description;
}
task.updatedAt = now;
tasks[taskIndex] = task;
const statusIcon = task.status === 'completed'
? '✓'
: task.status === 'in_progress'
? '◐'
: '○';
results.push(` ${statusIcon} [${task.id}] ${task.title}`);
}
await saveTasks(tasks);
const counts = {
pending: tasks.filter(t => t.status === 'pending').length,
in_progress: tasks.filter(t => t.status === 'in_progress').length,
completed: tasks.filter(t => t.status === 'completed').length,
};
const allTasksList = tasks
.map(t => {
const icon = t.status === 'completed' ? '✓' : t.status === 'in_progress' ? '◐' : '○';
return ` ${icon} [${t.id}] ${t.title}`;
})
.join('\n');
return `Updated ${args.updates.length} task(s):\n${results.join('\n')}\n\nAll Tasks (${counts.pending} pending, ${counts.in_progress} in progress, ${counts.completed} completed):\n${allTasksList}`;
};
const updateTaskCoreTool = tool({
description: 'Update one or more tasks. Use this to mark tasks as in_progress when starting work, or completed when finished. Pass an array of updates.',
inputSchema: jsonSchema({
type: 'object',
properties: {
updates: {
type: 'array',
description: 'Array of task updates',
items: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'The ID of the task to update',
},
status: {
type: 'string',
enum: ['pending', 'in_progress', 'completed'],
description: 'New status for the task',
},
title: {
type: 'string',
description: 'New title for the task',
},
description: {
type: 'string',
description: 'New description for the task',
},
},
required: ['id'],
},
},
},
required: ['updates'],
}),
needsApproval: false,
execute: async (args, _options) => {
return await executeUpdateTask(args);
},
});
const updateTaskFormatter = async (_args, _result) => {
const allTasks = await loadTasks();
return _jsx(TaskListDisplay, { tasks: allTasks, title: "Tasks" });
};
const updateTaskValidator = async (args) => {
if (!args.updates || args.updates.length === 0) {
return {
valid: false,
error: '⚒ At least one update is required',
};
}
for (let i = 0; i < args.updates.length; i++) {
const update = args.updates[i];
if (!update?.id?.trim()) {
return {
valid: false,
error: `⚒ Update ${i + 1}: Task ID is required`,
};
}
if (update.status === undefined &&
update.title === undefined &&
update.description === undefined) {
return {
valid: false,
error: `⚒ Update ${i + 1}: At least one field (status, title, or description) must be provided`,
};
}
if (update.title !== undefined && update.title.trim().length === 0) {
return {
valid: false,
error: `⚒ Update ${i + 1}: Task title cannot be empty`,
};
}
if (update.title !== undefined && update.title.length > 200) {
return {
valid: false,
error: `⚒ Update ${i + 1}: Task title is too long (max 200 characters)`,
};
}
}
return { valid: true };
};
export const updateTaskTool = {
name: 'update_task',
tool: updateTaskCoreTool,
formatter: updateTaskFormatter,
validator: updateTaskValidator,
};
//# sourceMappingURL=update-task.js.map