mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-08-03 21:51:11 -07:00
Merge branch 'u/anj/task-tracker-phase-1' into u/anj/task-tracker-phase-2
This commit is contained in:
@@ -6,7 +6,8 @@
|
||||
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import type { TrackerTask } from './trackerTypes.js';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import { TrackerTaskSchema, type TrackerTask } from './trackerTypes.js';
|
||||
|
||||
export class TrackerService {
|
||||
private readonly trackerDir: string;
|
||||
@@ -28,7 +29,7 @@ export class TrackerService {
|
||||
* Generates a 6-character hex ID.
|
||||
*/
|
||||
private generateId(): string {
|
||||
return Math.random().toString(16).substring(2, 8).padEnd(6, '0');
|
||||
return randomBytes(3).toString('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,8 +54,8 @@ export class TrackerService {
|
||||
const taskPath = path.join(this.tasksDir, `${id}.json`);
|
||||
try {
|
||||
const content = await fs.readFile(taskPath, 'utf8');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return JSON.parse(content) as TrackerTask;
|
||||
const data: unknown = JSON.parse(content);
|
||||
return TrackerTaskSchema.parse(data);
|
||||
} catch (error) {
|
||||
if (
|
||||
error &&
|
||||
@@ -82,8 +83,8 @@ export class TrackerService {
|
||||
path.join(this.tasksDir, f),
|
||||
'utf8',
|
||||
);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return JSON.parse(content) as TrackerTask;
|
||||
const data: unknown = JSON.parse(content);
|
||||
return TrackerTaskSchema.parse(data);
|
||||
}),
|
||||
);
|
||||
return tasks;
|
||||
@@ -159,10 +160,17 @@ export class TrackerService {
|
||||
private async validateNoCircularDependencies(
|
||||
task: TrackerTask,
|
||||
): Promise<void> {
|
||||
const allTasks = await this.listTasks();
|
||||
const taskMap = new Map<string, TrackerTask>(
|
||||
allTasks.map((t) => [t.id, t]),
|
||||
);
|
||||
// Ensure the current (possibly unsaved) task state is used
|
||||
taskMap.set(task.id, task);
|
||||
|
||||
const visited = new Set<string>();
|
||||
const stack = new Set<string>();
|
||||
|
||||
const check = async (currentId: string) => {
|
||||
const check = (currentId: string) => {
|
||||
if (stack.has(currentId)) {
|
||||
throw new Error(
|
||||
`Circular dependency detected involving task ${currentId}.`,
|
||||
@@ -175,17 +183,16 @@ export class TrackerService {
|
||||
visited.add(currentId);
|
||||
stack.add(currentId);
|
||||
|
||||
const currentTask =
|
||||
currentId === task.id ? task : await this.getTask(currentId);
|
||||
const currentTask = taskMap.get(currentId);
|
||||
if (currentTask) {
|
||||
for (const depId of currentTask.dependencies) {
|
||||
await check(depId);
|
||||
check(depId);
|
||||
}
|
||||
}
|
||||
|
||||
stack.delete(currentId);
|
||||
};
|
||||
|
||||
await check(task.id);
|
||||
check(task.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,18 +4,29 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
export type TaskType = 'epic' | 'task' | 'bug';
|
||||
import { z } from 'zod';
|
||||
|
||||
export type TaskStatus = 'open' | 'in_progress' | 'blocked' | 'closed';
|
||||
export const TaskTypeSchema = z.enum(['epic', 'task', 'bug']);
|
||||
export type TaskType = z.infer<typeof TaskTypeSchema>;
|
||||
|
||||
export interface TrackerTask {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
type: TaskType;
|
||||
status: TaskStatus;
|
||||
parentId?: string;
|
||||
dependencies: string[];
|
||||
subagentSessionId?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
export const TaskStatusSchema = z.enum([
|
||||
'open',
|
||||
'in_progress',
|
||||
'blocked',
|
||||
'closed',
|
||||
]);
|
||||
export type TaskStatus = z.infer<typeof TaskStatusSchema>;
|
||||
|
||||
export const TrackerTaskSchema = z.object({
|
||||
id: z.string().length(6),
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
type: TaskTypeSchema,
|
||||
status: TaskStatusSchema,
|
||||
parentId: z.string().optional(),
|
||||
dependencies: z.array(z.string()),
|
||||
subagentSessionId: z.string().optional(),
|
||||
metadata: z.record(z.unknown()).optional(),
|
||||
});
|
||||
|
||||
export type TrackerTask = z.infer<typeof TrackerTaskSchema>;
|
||||
|
||||
Reference in New Issue
Block a user