Skip to content
7 changes: 6 additions & 1 deletion src/adapters/cli/customizations/tasks-customizations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export function getTasksCustomizations(): {
specPath: {
description: "Path to file containing task description",
},
dependencies: {
alias: "deps",
description: "Comma-separated task IDs this task depends on (e.g., mt#123,mt#124)",
},
},
},
"tasks.edit": {
Expand Down Expand Up @@ -126,7 +130,8 @@ export function getTasksCustomizations(): {
},
dependsOn: {
asArgument: false,
description: "Task that is the dependency",
description:
"Task that is the dependency, or comma-separated list (e.g., mt#123,mt#124)",
},
},
outputFormatter: (result: any) => {
Expand Down
8 changes: 4 additions & 4 deletions src/adapters/mcp/task-relationships-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function registerTaskRelationshipTools(commandMapper: CommandMapper): voi
description: "Add a dependency edge (task depends on prerequisite)",
parameters: AddSchema,
handler: async (args) => {
const db = await DatabaseConnectionManager.getInstance().getConnection();
const db = await new DatabaseConnectionManager().getConnection();
const service = new TaskGraphService(db);
const result = await service.addDependency(args.fromTaskId, args.toTaskId);
return { success: true, created: result.created };
Expand All @@ -28,7 +28,7 @@ export function registerTaskRelationshipTools(commandMapper: CommandMapper): voi
description: "Remove a dependency edge",
parameters: RemoveSchema,
handler: async (args) => {
const db = await DatabaseConnectionManager.getInstance().getConnection();
const db = await new DatabaseConnectionManager().getConnection();
const service = new TaskGraphService(db);
const result = await service.removeDependency(args.fromTaskId, args.toTaskId);
return { success: true, removed: result.removed };
Expand All @@ -40,7 +40,7 @@ export function registerTaskRelationshipTools(commandMapper: CommandMapper): voi
description: "List dependencies or dependents for a task",
parameters: ListSchema,
handler: async (args) => {
const db = await DatabaseConnectionManager.getInstance().getConnection();
const db = await new DatabaseConnectionManager().getConnection();
const service = new TaskGraphService(db);
if (args.direction === "dependents") {
const list = await service.listDependents(args.taskId);
Expand All @@ -50,4 +50,4 @@ export function registerTaskRelationshipTools(commandMapper: CommandMapper): voi
return { success: true, items: list };
},
});
}
}
55 changes: 55 additions & 0 deletions src/adapters/shared/commands/tasks/crud-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ interface TasksCreateParams extends BaseTaskParams {
title: string;
spec?: string;
specPath?: string;
dependencies?: string | string[];
force?: boolean;
githubRepo?: string;
}
Expand Down Expand Up @@ -202,6 +203,52 @@ export class TasksCreateCommand extends BaseTaskCommand {
});

this.debug("Task created successfully");
// Handle dependencies if provided
const dependencyResults: string[] = [];
if (params.dependencies) {
try {
const { TaskGraphService } = await import("../../../../domain/tasks/task-graph-service");
const { DatabaseConnectionManager } = await import(
"../../../../domain/database/connection-manager"
);

// Parse dependencies (handle both string and array formats)
const deps = Array.isArray(params.dependencies)
? params.dependencies
: params.dependencies.split(",").map((d) => d.trim());

if (deps.length > 0) {
this.debug(`Adding ${deps.length} dependencies to task ${result.id}`);
const db = await new DatabaseConnectionManager().getConnection();
const graphService = new TaskGraphService(db);

for (const dep of deps) {
try {
// Just use the dependency task ID directly - no type parsing needed
const depTaskId = dep.trim();

const addResult = await graphService.addDependency(result.id, depTaskId);

if (addResult.created) {
dependencyResults.push(
`✅ Added dependency: ${result.id} depends on ${depTaskId}`
);
} else {
dependencyResults.push(
`ℹ️ Dependency already exists: ${result.id} depends on ${depTaskId}`
);
}
} catch (depError) {
dependencyResults.push(`❌ Failed to add dependency ${dep}: ${depError.message}`);
this.debug(`Dependency addition failed for ${dep}: ${depError.message}`);
}
}
}
} catch (error) {
dependencyResults.push(`❌ Failed to process dependencies: ${error.message}`);
this.debug(`Dependencies processing failed: ${error.message}`);
}
}

// Build success message
let message = `Task ${result.taskId} created: "${result.title}"`;
Expand All @@ -214,6 +261,14 @@ export class TasksCreateCommand extends BaseTaskCommand {
}
message += `\n${chalk.gray(" Title: ")}${result.title}`;
message += `\n${chalk.gray(" ID: ")}${result.taskId}`;

// Add dependency results to message
if (dependencyResults.length > 0) {
message += `\n${chalk.gray(" Dependencies:")}`;
dependencyResults.forEach((depResult) => {
message += `\n ${depResult}`;
});
}
}

return this.formatResult(
Expand Down
46 changes: 35 additions & 11 deletions src/adapters/shared/commands/tasks/deps-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ const tasksDepsAddParams: CommandParameterMap = {
required: true,
},
dependsOn: {
schema: z.string(),
description: "Task that is the dependency",
schema: z.union([z.string(), z.array(z.string())]),
description: "Task that is the dependency, or comma-separated list of task IDs",
required: true,
},
};
Expand Down Expand Up @@ -51,15 +51,39 @@ export function createTasksDepsAddCommand() {
description: "Add a dependency edge (task depends on prerequisite)",
parameters: tasksDepsAddParams,
execute: async (params: any) => {
const db: PostgresJsDatabase = await DatabaseConnectionManager.getInstance().getConnection();
const db: PostgresJsDatabase = await new DatabaseConnectionManager().getConnection();
const service = new TaskGraphService(db);
const result = await service.addDependency(params.task, params.dependsOn);

const output = result.created
? `✅ Added dependency: ${params.task} depends on ${params.dependsOn}`
: `ℹ️ Dependency already exists: ${params.task} depends on ${params.dependsOn}`;
// Parse dependencies (handle both string and array formats)
const dependencies = Array.isArray(params.dependsOn)
? params.dependsOn
: typeof params.dependsOn === "string"
? params.dependsOn.split(",").map((d) => d.trim())
: [params.dependsOn];

const results: string[] = [];
let allSuccessful = true;

for (const dep of dependencies) {
try {
// Just use the dependency task ID directly - no type parsing needed
const depTaskId = dep.trim();

const result = await service.addDependency(params.task, depTaskId);

if (result.created) {
results.push(`✅ Added dependency: ${params.task} depends on ${depTaskId}`);
} else {
results.push(`ℹ️ Dependency already exists: ${params.task} depends on ${depTaskId}`);
}
} catch (error) {
results.push(`❌ Failed to add dependency ${dep}: ${error.message}`);
allSuccessful = false;
}
}

return { success: true, output };
const output = results.join("\n");
return { success: allSuccessful, output };
},
};
}
Expand All @@ -71,7 +95,7 @@ export function createTasksDepsRmCommand() {
description: "Remove a dependency edge",
parameters: tasksDepsRmParams,
execute: async (params: any) => {
const db: PostgresJsDatabase = await DatabaseConnectionManager.getInstance().getConnection();
const db: PostgresJsDatabase = await new DatabaseConnectionManager().getConnection();
const service = new TaskGraphService(db);
const result = await service.removeDependency(params.task, params.dependsOn);

Expand All @@ -91,7 +115,7 @@ export function createTasksDepsListCommand() {
description: "List dependencies for a task",
parameters: tasksDepsListParams,
execute: async (params: any) => {
const db: PostgresJsDatabase = await DatabaseConnectionManager.getInstance().getConnection();
const db: PostgresJsDatabase = await new DatabaseConnectionManager().getConnection();
const service = new TaskGraphService(db);
const dependencies = await service.listDependencies(params.task);
const dependents = await service.listDependents(params.task);
Expand Down Expand Up @@ -143,4 +167,4 @@ export function createTasksDepsListCommand() {
return { success: true, output: lines.join("\n") };
},
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export function createTasksDepsTreeCommand() {
description: "Show dependency tree for a specific task",
parameters: tasksDepsTreeParams,
execute: async (params: any) => {
const db: PostgresJsDatabase = await DatabaseConnectionManager.getInstance().getConnection();
const db: PostgresJsDatabase = await new DatabaseConnectionManager().getConnection();
const graphService = new TaskGraphService(db);
const taskService = await createConfiguredTaskService({
workspacePath: process.cwd(),
Expand Down Expand Up @@ -130,7 +130,7 @@ export function createTasksDepsGraphCommand() {
description: "Show ASCII graph of all task dependencies",
parameters: tasksDepsGraphParams,
execute: async (params: any) => {
const db: PostgresJsDatabase = await DatabaseConnectionManager.getInstance().getConnection();
const db: PostgresJsDatabase = await new DatabaseConnectionManager().getConnection();
const graphService = new TaskGraphService(db);
const taskService = await createConfiguredTaskService({
workspacePath: process.cwd(),
Expand Down Expand Up @@ -850,4 +850,4 @@ async function renderGraphvizFormat(
filePath: "",
};
}
}
}
12 changes: 12 additions & 0 deletions src/adapters/shared/commands/tasks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,15 @@ export { TasksSimilarCommand, TasksSearchCommand } from "./similarity-commands";

// Index embeddings command
export { TasksIndexEmbeddingsCommand } from "./index-embeddings-command";

// Dependency management commands
export {
createTasksDepsAddCommand,
createTasksDepsRmCommand,
createTasksDepsListCommand,
} from "./deps-commands";

export {
createTasksDepsTreeCommand,
createTasksDepsGraphCommand,
} from "./deps-visualization-commands";
6 changes: 3 additions & 3 deletions src/adapters/shared/commands/tasks/routing-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export function createTasksAvailableCommand() {
description: "Show tasks currently available to work on (unblocked by dependencies)",
parameters: tasksAvailableParams,
execute: async (params: any) => {
const db: PostgresJsDatabase = await DatabaseConnectionManager.getInstance().getConnection();
const db: PostgresJsDatabase = await new DatabaseConnectionManager().getConnection();
const graphService = new TaskGraphService(db);
const taskService = await createConfiguredTaskService({
workspacePath: process.cwd(),
Expand Down Expand Up @@ -176,7 +176,7 @@ export function createTasksRouteCommand() {
description: "Generate implementation route to target task",
parameters: tasksRouteParams,
execute: async (params: any) => {
const db: PostgresJsDatabase = await DatabaseConnectionManager.getInstance().getConnection();
const db: PostgresJsDatabase = await new DatabaseConnectionManager().getConnection();
const graphService = new TaskGraphService(db);
const taskService = await createConfiguredTaskService({
workspacePath: process.cwd(),
Expand Down Expand Up @@ -245,4 +245,4 @@ export function createTasksRouteCommand() {
return { success: true, output };
},
};
}
}
5 changes: 5 additions & 0 deletions src/adapters/shared/commands/tasks/task-parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ export const taskCreationParams = {
description: "Path to file containing task description",
required: false,
},
dependencies: {
schema: z.union([z.string(), z.array(z.string())]),
description: "Comma-separated list of task IDs this task depends on, or array of task IDs",
required: false,
},
githubRepo: {
schema: z.string(),
description:
Expand Down
4 changes: 4 additions & 0 deletions src/schemas/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ export const taskCreateParamsSchema = z
spec: z.string().optional().describe("Spec text for the task"),
description: z.string().optional().describe("Description text for the task (alias for spec)"),
specPath: z.string().optional().describe("Path to file containing task spec"),
dependencies: z
.union([z.string(), z.array(z.string())])
.optional()
.describe("Comma-separated list of task IDs this task depends on, or array of task IDs"),
force: flagSchema("Force creation even if task already exists"),
backend: z
.string()
Expand Down