CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
todoWrite.ts616 linesDownload Raw Back to tools
1/**2 * @license3 * Copyright 2025 Google LLC4 * SPDX-License-Identifier: Apache-2.05 */6 7import type { ToolResult } from './tools.js';8import { BaseDeclarativeTool, BaseToolInvocation, Kind } from './tools.js';9import type { FunctionDeclaration } from '@google/genai';10import * as fs from 'fs/promises';11import * as fsSync from 'fs';12import * as path from 'path';13 14import type { Config } from '../config/config.js';15import { Storage } from '../config/storage.js';16import { ToolDisplayNames, ToolNames } from './tool-names.js';17import { atomicWriteFile } from '../utils/atomicFileWrite.js';18import { createDebugLogger } from '../utils/debugLogger.js';19import { detectTodoChanges, HookPhase, type TodoItem } from '../hooks/types.js';20export type { TodoItem } from '../hooks/types.js';21 22const debugLogger = createDebugLogger('TODO_WRITE');23 24export interface TodoWriteParams {25  todos: TodoItem[];26  modified_by_user?: boolean;27  modified_content?: string;28}29 30const todoWriteToolSchemaData: FunctionDeclaration = {31  name: 'todo_write',32  description:33    'Creates and manages a structured task list for your current coding session. This helps track progress, organize complex tasks, and demonstrate thoroughness.',34  parametersJsonSchema: {35    type: 'object',36    properties: {37      todos: {38        type: 'array',39        items: {40          type: 'object',41          properties: {42            content: {43              type: 'string',44              minLength: 1,45            },46            status: {47              type: 'string',48              enum: ['pending', 'in_progress', 'completed'],49            },50            id: {51              type: 'string',52            },53          },54          required: ['content', 'status', 'id'],55          additionalProperties: false,56        },57        description: 'The updated todo list',58      },59    },60    required: ['todos'],61    $schema: 'http://json-schema.org/draft-07/schema#',62  },63};64 65const todoWriteToolDescription = `66Use this tool to create and manage a structured task list for your current coding session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.67It also helps the user understand the progress of the task and overall progress of their requests.68 69## When to Use This Tool70Use this tool proactively in these scenarios:71 721. Complex multi-step tasks - When a task requires 3 or more distinct steps or actions732. Non-trivial and complex tasks - Tasks that require careful planning or multiple operations743. User explicitly requests todo list - When the user directly asks you to use the todo list754. User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)765. After receiving new instructions - Immediately capture user requirements as todos776. When you start working on a task - Mark it as in_progress BEFORE beginning work. Ideally you should only have one todo as in_progress at a time787. After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation79 80## When NOT to Use This Tool81 82Skip using this tool when:831. There is only a single, straightforward task842. The task is trivial and tracking it provides no organizational benefit853. The task can be completed in less than 3 trivial steps864. The task is purely conversational or informational87 88NOTE that you should not use this tool if there is only one trivial task to do. In this case you are better off just doing the task directly.89 90## Examples of When to Use the Todo List91 92<example>93User: I want to add a dark mode toggle to the application settings. Make sure you run the tests and build when you're done!94Assistant: I'll help add a dark mode toggle to your application settings. Let me create a todo list to track this implementation.95*Creates todo list with the following items:*961. Create dark mode toggle component in Settings page972. Add dark mode state management (context/store)983. Implement CSS-in-JS styles for dark theme994. Update existing components to support theme switching1005. Run tests and build process, addressing any failures or errors that occur101*Begins working on the first task*102 103<reasoning>104The assistant used the todo list because:1051. Adding dark mode is a multi-step feature requiring UI, state management, and styling changes1062. The user explicitly requested tests and build be run afterward1073. The assistant inferred that tests and build need to pass by adding "Ensure tests and build succeed" as the final task108</reasoning>109</example>110 111<example>112User: Help me rename the function getCwd to getCurrentWorkingDirectory across my project113Assistant: Let me first search through your codebase to find all occurrences of 'getCwd'.114*Uses grep or search tools to locate all instances of getCwd in the codebase*115Assistant: I've found 15 instances of 'getCwd' across 8 different files. Let me create a todo list to track these changes.116*Creates todo list with specific items for each file that needs updating*117 118<reasoning>119The assistant used the todo list because:1201. First, the assistant searched to understand the scope of the task1212. Upon finding multiple occurrences across different files, it determined this was a complex task with multiple steps1223. The todo list helps ensure every instance is tracked and updated systematically1234. This approach prevents missing any occurrences and maintains code consistency124</reasoning>125</example>126 127 128<example>129User: I need to implement these features for my e-commerce site: user registration, product catalog, shopping cart, and checkout flow.130Assistant: I'll help implement these features. First, let's add all the features to the todo list.131*Creates a todo list breaking down each feature into specific tasks based on the project architecture*132Assistant: Let's start with user registration. This will involve creating database models, API endpoints, and frontend forms that integrate with your existing authentication system.133 134<reasoning>135The assistant used the todo list because:1361. The user provided multiple complex features to implement in a comma separated list1372. The todo list helps organize these large features into manageable tasks1383. This approach allows for tracking progress across the entire implementation139</reasoning>140</example>141 142<example>143User: Can you help optimize my React application? It's rendering slowly and has performance issues.</user>144Assistant: I'll help optimize your React application. First, let me examine your codebase to identify potential performance bottlenecks.145*Reviews component structure, render patterns, state management, and data fetching*146Assistant: After analyzing your codebase, I've identified several performance issues. Let me create a todo list to track our optimization efforts.147*Creates todo list with items like: 1) Implement memoization for expensive calculations in ProductList, 2) Add virtualization for long lists in Dashboard, 3) Optimize image loading in Gallery component, 4) Fix state update loops in ShoppingCart, 5) Review bundle size and implement code splitting*148Let's start by implementing memoization for the expensive calculations in your ProductList component.</assistant>149 150<reasoning>151The assistant used the todo list because:1521. First, the assistant examined the codebase to identify specific performance issues1532. Based on this analysis, it identified multiple optimization opportunities1543. Performance optimization is a non-trivial task requiring multiple steps1554. The todo list helps methodically track improvements across different components1565. This systematic approach ensures all performance bottlenecks are addressed157</reasoning>158</example>159 160## Examples of When NOT to Use the Todo List161 162<example>163User: How do I print 'Hello World' in Python?164Assistant: In Python, you can print "Hello World" with this simple code:165 166python167print("Hello World")168 169This will output the text "Hello World" to the console when executed.</assistant>170 171<reasoning>172The assistant did not use the todo list because this is a single, trivial task that can be completed in one step. There's no need to track multiple tasks or steps for such a straightforward request.173</reasoning>174</example>175 176<example>177User: What does the git status command do?178Assistant: The git status command shows the current state of your working directory and staging area. It displays which changes have been staged, which haven't, and which files aren't being tracked by Git. This is a fundamental command for understanding the current state of your repository before making commits.179 180<reasoning>181The assistant did not use the todo list because this is an informational request with no actual coding task to complete. The user is simply asking for an explanation, not for the assistant to perform multiple steps or tasks.182</reasoning>183</example>184 185<example>186User: Can you add a comment to the calculateTotal function to explain what it does?187Assistant: Sure, let me add a comment to the calculateTotal function to explain what it does.188* Uses the Edit tool to add a comment to the calculateTotal function *189 190<reasoning>191The assistant did not use the todo list because this is a single, straightforward task confined to one location in the code. Adding a comment doesn't require tracking multiple steps or systematic organization.192</reasoning>193</example>194 195<example>196User: Run npm install for me and tell me what happens.197Assistant: I'll run the npm install command for you.198 199*Executes: npm install*200 201The command completed successfully. Here's the output:202[Output of npm install command]203 204All dependencies have been installed according to your package.json file.205 206<reasoning>207The assistant did not use the todo list because this is a single command execution with immediate results. There are no multiple steps to track or organize, making the todo list unnecessary for this straightforward task.208</reasoning>209</example>210 211## Planning with Todos212 213Before breaking work into low-level edits, use the todo list to reflect the overall approach at a meaningful level (for example: investigate, design, implement, verify). This helps maintain a global view of the task instead of jumping between isolated local changes.214 215When new information changes your understanding of the task, update the todo structure to reflect the revised plan rather than only appending isolated follow-up items. The todo list should continue to represent the current overall strategy.216 217## Task States and Management218 2191. **Task States**: Use these states to track progress:220   - pending: Task not yet started221   - in_progress: Currently working on (limit to ONE task at a time)222   - completed: Task finished successfully223 2242. **Task Management**:225   - Update task status in real-time as you work226   - Mark tasks complete IMMEDIATELY after finishing (don't batch completions)227   - Only have ONE task in_progress at any time228   - Complete current tasks before starting new ones229   - Remove tasks that are no longer relevant from the list entirely230 2313. **Task Completion Requirements**:232   - ONLY mark a task as completed when you have FULLY accomplished it233   - If you encounter errors, blockers, or cannot finish, keep the task as in_progress234   - When blocked, create a new task describing what needs to be resolved235   - Never mark a task as completed if:236     - Tests are failing237     - Implementation is partial238     - You encountered unresolved errors239     - You couldn't find necessary files or dependencies240 2414. **Task Breakdown**:242   - Create specific, actionable items243   - Break complex tasks into smaller, manageable steps244   - Use clear, descriptive task names245 246When in doubt, use this tool. Being proactive with task management demonstrates attentiveness and ensures you complete all requirements successfully.247`;248 249const TODO_SUBDIR = 'todos';250 251function getTodoFilePath(sessionId?: string): string {252  const todoDir = path.join(Storage.getRuntimeBaseDir(), TODO_SUBDIR);253 254  // Use sessionId if provided, otherwise fall back to 'default'255  const filename = `${sessionId || 'default'}.json`;256  return path.join(todoDir, filename);257}258 259/**260 * Reads the current todos from the file system261 */262async function readTodosFromFile(sessionId?: string): Promise<TodoItem[]> {263  try {264    const todoFilePath = getTodoFilePath(sessionId);265    const content = await fs.readFile(todoFilePath, 'utf-8');266    const data = JSON.parse(content);267    return Array.isArray(data.todos) ? data.todos : [];268  } catch (err) {269    const error = err as Error & { code?: string };270    if (!(error instanceof Error) || error.code !== 'ENOENT') {271      throw err;272    }273    return [];274  }275}276 277/**278 * Writes todos to the file system279 */280async function writeTodosToFile(281  todos: TodoItem[],282  sessionId?: string,283): Promise<void> {284  const todoFilePath = getTodoFilePath(sessionId);285  const todoDir = path.dirname(todoFilePath);286 287  await fs.mkdir(todoDir, { recursive: true });288 289  const data = {290    todos,291    sessionId: sessionId || 'default',292  };293 294  await atomicWriteFile(todoFilePath, JSON.stringify(data, null, 2), {295    encoding: 'utf-8',296  });297}298 299function createBlockedTodoResult(300  message: string,301  systemMessage: string,302): ToolResult {303  return {304    llmContent: `${message}305 306<system-reminder>307${systemMessage}308</system-reminder>`,309    returnDisplay: message,310  };311}312 313class TodoWriteToolInvocation extends BaseToolInvocation<314  TodoWriteParams,315  ToolResult316> {317  private operationType: 'create' | 'update';318 319  constructor(320    private readonly config: Config,321    params: TodoWriteParams,322    operationType: 'create' | 'update' = 'update',323  ) {324    super(params);325    this.operationType = operationType;326  }327 328  getDescription(): string {329    return this.operationType === 'create' ? 'Create todos' : 'Update todos';330  }331 332  async execute(_signal: AbortSignal): Promise<ToolResult> {333    const { todos, modified_by_user, modified_content } = this.params;334    const sessionId = this.config.getSessionId();335 336    try {337      // 1. Read current todos (for change detection)338      const oldTodos = await readTodosFromFile(sessionId);339 340      let finalTodos: TodoItem[];341 342      if (modified_by_user && modified_content !== undefined) {343        // User modified the content in external editor, parse it directly344        const data = JSON.parse(modified_content);345        finalTodos = Array.isArray(data.todos) ? data.todos : [];346      } else {347        // Use the normal todo logic - simply replace with new todos348        finalTodos = todos;349      }350 351      // 2. Detect changes352      const changes = detectTodoChanges(oldTodos, finalTodos);353      const oldTodosMap = new Map(oldTodos.map((t) => [t.id, t]));354 355      // 3. VALIDATION PHASE: Execute all hooks with Validation phase356      // Hooks should only check and return block/approve decisions, no side effects357      const hookSystem = this.config.getHookSystem();358 359      // Validate TodoCreated hooks360      if (hookSystem && changes.created.length > 0) {361        const createdResults = await Promise.all(362          changes.created.map((todo) =>363            hookSystem.fireTodoCreatedEvent(364              todo.id,365              todo.content,366              todo.status,367              finalTodos,368              HookPhase.Validation,369              _signal,370            ),371          ),372        );373 374        const blockedCreatedResult = createdResults.find(375          (result) => result.finalOutput?.decision === 'block',376        );377        if (blockedCreatedResult?.finalOutput) {378          const reason =379            blockedCreatedResult.finalOutput.reason ||380            'Hook blocked todo creation';381          return createBlockedTodoResult(382            `Todo creation blocked: ${reason}`,383            `Todo list was not modified because a TodoCreated hook blocked the operation: ${reason}`,384          );385        }386      }387 388      // Validate TodoCompleted hooks389      if (hookSystem && changes.completed.length > 0) {390        const completedResults = await Promise.all(391          changes.completed.map((todo) => {392            const oldTodo = oldTodosMap.get(todo.id);393            const previousStatus = oldTodo?.status ?? 'pending';394 395            return hookSystem.fireTodoCompletedEvent(396              todo.id,397              todo.content,398              previousStatus as 'pending' | 'in_progress',399              finalTodos,400              HookPhase.Validation,401              _signal,402            );403          }),404        );405 406        const blockedCompletedResult = completedResults.find(407          (result) => result.finalOutput?.decision === 'block',408        );409        if (blockedCompletedResult?.finalOutput) {410          const reason =411            blockedCompletedResult.finalOutput.reason ||412            'Hook blocked todo completion';413          return createBlockedTodoResult(414            `Todo completion blocked: ${reason}`,415            `Todo list was not modified because a TodoCompleted hook blocked the operation: ${reason}`,416          );417        }418      }419 420      // 4. Write new todos AFTER all validation passes421      await writeTodosToFile(finalTodos, sessionId);422 423      // 5. POST-WRITE PHASE: Execute hooks for side effects (logging, HTTP sync, etc.)424      // These hooks can now safely perform side effects knowing data is persisted425      // We don't check for blocking here since validation already passed426      let postWriteError: Error | undefined;427      try {428        if (hookSystem && changes.created.length > 0) {429          await Promise.all(430            changes.created.map((todo) =>431              hookSystem.fireTodoCreatedEvent(432                todo.id,433                todo.content,434                todo.status,435                finalTodos,436                HookPhase.PostWrite,437                _signal,438              ),439            ),440          );441        }442 443        if (hookSystem && changes.completed.length > 0) {444          await Promise.all(445            changes.completed.map((todo) => {446              const oldTodo = oldTodosMap.get(todo.id);447              const previousStatus = oldTodo?.status ?? 'pending';448 449              return hookSystem.fireTodoCompletedEvent(450                todo.id,451                todo.content,452                previousStatus as 'pending' | 'in_progress',453                finalTodos,454                HookPhase.PostWrite,455                _signal,456              );457            }),458          );459        }460      } catch (error) {461        postWriteError =462          error instanceof Error ? error : new Error(String(error));463        debugLogger.error(464          `[TodoWriteTool] Post-write hooks failed after todos were persisted: ${postWriteError.message}`,465        );466      }467 468      // 6. Create structured display object for rich UI rendering469      const todoResultDisplay = {470        type: 'todo_list' as const,471        todos: finalTodos,472        changes,473      };474 475      // Create plain string format with system reminder476      const todosJson = JSON.stringify(finalTodos);477      let llmContent: string;478      const postWriteReminder = postWriteError479        ? `480 481<system-reminder>482Todos were persisted successfully, but post-write hooks failed with error: ${postWriteError.message}. Do not tell the user the write failed; only handle any follow-up hook issues if needed.483</system-reminder>`484        : '';485 486      if (finalTodos.length === 0) {487        // Special message for empty todos488        llmContent = `Todo list has been cleared.489 490<system-reminder>491Your todo list is now empty. DO NOT mention this explicitly to the user. You have no pending tasks in your todo list.492</system-reminder>${postWriteReminder}`;493      } else {494        // Normal message for todos with items495        llmContent = `Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable496 497<system-reminder>498Your todo list has changed. DO NOT mention this explicitly to the user. Here are the latest contents of your todo list:499 500${todosJson}. Continue on with the tasks at hand if applicable.501</system-reminder>${postWriteReminder}`;502      }503 504      return {505        llmContent,506        returnDisplay: todoResultDisplay,507      };508    } catch (error) {509      const errorMessage =510        error instanceof Error ? error.message : String(error);511      debugLogger.error(512        `[TodoWriteTool] Error executing todo_write: ${errorMessage}`,513      );514 515      // Create plain string format for error with system reminder516      const errorLlmContent = `Failed to modify todos. An error occurred during the operation.517 518<system-reminder>519Todo list modification failed with error: ${errorMessage}. You may need to retry or handle this error appropriately.520</system-reminder>`;521 522      return {523        llmContent: errorLlmContent,524        returnDisplay: `Error writing todos: ${errorMessage}`,525      };526    }527  }528}529 530/**531 * Utility function to read todos for a specific session (useful for session recovery)532 */533export async function readTodosForSession(534  sessionId?: string,535): Promise<TodoItem[]> {536  return readTodosFromFile(sessionId);537}538 539/**540 * Utility function to list all todo files in the todos directory541 */542export async function listTodoSessions(): Promise<string[]> {543  try {544    const todoDir = path.join(Storage.getRuntimeBaseDir(), TODO_SUBDIR);545    const files = await fs.readdir(todoDir);546    return files547      .filter((file: string) => file.endsWith('.json'))548      .map((file: string) => file.replace('.json', ''));549  } catch (err) {550    const error = err as Error & { code?: string };551    if (!(error instanceof Error) || error.code !== 'ENOENT') {552      throw err;553    }554    return [];555  }556}557 558export class TodoWriteTool extends BaseDeclarativeTool<559  TodoWriteParams,560  ToolResult561> {562  static readonly Name: string = ToolNames.TODO_WRITE;563 564  constructor(private readonly config: Config) {565    super(566      TodoWriteTool.Name,567      ToolDisplayNames.TODO_WRITE,568      todoWriteToolDescription,569      Kind.Think,570      todoWriteToolSchemaData.parametersJsonSchema as Record<string, unknown>,571    );572  }573 574  override validateToolParams(params: TodoWriteParams): string | null {575    // Validate todos array576    if (!Array.isArray(params.todos)) {577      return 'Parameter "todos" must be an array.';578    }579 580    // Validate individual todos581    for (const todo of params.todos) {582      if (!todo.id || typeof todo.id !== 'string' || todo.id.trim() === '') {583        return 'Each todo must have a non-empty "id" string.';584      }585      if (586        !todo.content ||587        typeof todo.content !== 'string' ||588        todo.content.trim() === ''589      ) {590        return 'Each todo must have a non-empty "content" string.';591      }592      if (!['pending', 'in_progress', 'completed'].includes(todo.status)) {593        return 'Each todo must have a valid "status" (pending, in_progress, completed).';594      }595    }596 597    // Check for duplicate IDs598    const ids = params.todos.map((todo) => todo.id);599    const uniqueIds = new Set(ids);600    if (ids.length !== uniqueIds.size) {601      return 'Todo IDs must be unique within the array.';602    }603 604    return null;605  }606 607  protected createInvocation(params: TodoWriteParams) {608    // Determine if this is a create or update operation by checking if todos file exists609    const sessionId = this.config.getSessionId();610    const todoFilePath = getTodoFilePath(sessionId);611    const operationType = fsSync.existsSync(todoFilePath) ? 'update' : 'create';612 613    return new TodoWriteToolInvocation(this.config, params, operationType);614  }615}616 
basant307/AI_Governance_Project · CoolFace