@hdwebsoft/hdcode-agent-team 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +85 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -117,6 +117,37 @@ function hasCycle(taskId, blockedBy, allTasks) {
|
|
|
117
117
|
}
|
|
118
118
|
return false;
|
|
119
119
|
}
|
|
120
|
+
function hasCycleViaBlocks(taskId, blockTargets, blockedBySources, allTasks) {
|
|
121
|
+
const upstream = new Set;
|
|
122
|
+
const queue = [...blockedBySources];
|
|
123
|
+
while (queue.length > 0) {
|
|
124
|
+
const current = queue.shift();
|
|
125
|
+
if (upstream.has(current))
|
|
126
|
+
continue;
|
|
127
|
+
upstream.add(current);
|
|
128
|
+
const task = allTasks.get(current);
|
|
129
|
+
if (task)
|
|
130
|
+
queue.push(...task.blockedBy);
|
|
131
|
+
}
|
|
132
|
+
for (const targetId of blockTargets) {
|
|
133
|
+
if (upstream.has(targetId))
|
|
134
|
+
return true;
|
|
135
|
+
const targetQueue = [targetId];
|
|
136
|
+
const visited = new Set;
|
|
137
|
+
while (targetQueue.length > 0) {
|
|
138
|
+
const cur = targetQueue.shift();
|
|
139
|
+
if (visited.has(cur))
|
|
140
|
+
continue;
|
|
141
|
+
visited.add(cur);
|
|
142
|
+
if (upstream.has(cur))
|
|
143
|
+
return true;
|
|
144
|
+
const t = allTasks.get(cur);
|
|
145
|
+
if (t)
|
|
146
|
+
targetQueue.push(...t.blockedBy);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
120
151
|
async function unblockDependents(base, teamName, completedTaskId) {
|
|
121
152
|
const unblocked = [];
|
|
122
153
|
const allTasks = await loadAllTasks(base, teamName);
|
|
@@ -312,6 +343,7 @@ function createTeamTools(directory) {
|
|
|
312
343
|
|
|
313
344
|
// src/tools/task-tools.ts
|
|
314
345
|
import { join as join5 } from "node:path";
|
|
346
|
+
import { unlink as unlink2 } from "node:fs/promises";
|
|
315
347
|
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
316
348
|
function parseStringArray(raw, label) {
|
|
317
349
|
if (!raw.trimStart().startsWith("[")) {
|
|
@@ -386,6 +418,9 @@ function createTaskTools(directory) {
|
|
|
386
418
|
if (addBlockedBy.length > 0 && hasCycle(id, addBlockedBy, taskMap)) {
|
|
387
419
|
return `Error: Circular dependency detected for task ${id}`;
|
|
388
420
|
}
|
|
421
|
+
if (addBlocks.length > 0 && hasCycleViaBlocks(id, addBlocks, addBlockedBy, taskMap)) {
|
|
422
|
+
return `Error: Circular dependency detected for task ${id}`;
|
|
423
|
+
}
|
|
389
424
|
const task = {
|
|
390
425
|
id,
|
|
391
426
|
subject: args.subject,
|
|
@@ -492,6 +527,11 @@ function createTaskTools(directory) {
|
|
|
492
527
|
const result = parseStringArray(args.addBlocks, "addBlocks");
|
|
493
528
|
if (typeof result === "string")
|
|
494
529
|
return result;
|
|
530
|
+
const allTasks = await loadAllTasks(directory, args.teamName);
|
|
531
|
+
const taskMap = new Map(allTasks.map((t) => [t.id, t]));
|
|
532
|
+
if (hasCycleViaBlocks(task.id, result, task.blockedBy, taskMap)) {
|
|
533
|
+
return `Error: Circular dependency detected — cannot add blocks`;
|
|
534
|
+
}
|
|
495
535
|
for (const targetId of result) {
|
|
496
536
|
if (!task.blocks.includes(targetId)) {
|
|
497
537
|
task.blocks.push(targetId);
|
|
@@ -510,6 +550,11 @@ function createTaskTools(directory) {
|
|
|
510
550
|
const result = parseStringArray(args.addBlockedBy, "addBlockedBy");
|
|
511
551
|
if (typeof result === "string")
|
|
512
552
|
return result;
|
|
553
|
+
const allTasks = await loadAllTasks(directory, args.teamName);
|
|
554
|
+
const taskMap = new Map(allTasks.map((t) => [t.id, t]));
|
|
555
|
+
if (hasCycle(task.id, result, taskMap)) {
|
|
556
|
+
return `Error: Circular dependency detected — cannot add blockedBy`;
|
|
557
|
+
}
|
|
513
558
|
for (const targetId of result) {
|
|
514
559
|
if (!task.blockedBy.includes(targetId)) {
|
|
515
560
|
task.blockedBy.push(targetId);
|
|
@@ -533,6 +578,46 @@ function createTaskTools(directory) {
|
|
|
533
578
|
});
|
|
534
579
|
}
|
|
535
580
|
}),
|
|
581
|
+
task_delete: tool2({
|
|
582
|
+
description: "Delete a task and clean up all bidirectional dependency links. Removes this task from blocks[] and blockedBy[] of all related tasks.",
|
|
583
|
+
args: {
|
|
584
|
+
teamName: tool2.schema.string().describe("Team name"),
|
|
585
|
+
taskId: tool2.schema.string().describe("Task ID to delete")
|
|
586
|
+
},
|
|
587
|
+
async execute(args) {
|
|
588
|
+
const dir = teamDir(directory, args.teamName);
|
|
589
|
+
if (!await exists(join5(dir, "config.json"))) {
|
|
590
|
+
return `Error: Team "${args.teamName}" not found`;
|
|
591
|
+
}
|
|
592
|
+
const tasksDir = join5(dir, "tasks");
|
|
593
|
+
const tasksLockPath = join5(tasksDir, ".lock");
|
|
594
|
+
return withLock(tasksLockPath, async () => {
|
|
595
|
+
const tPath = taskFilePath(directory, args.teamName, args.taskId);
|
|
596
|
+
if (!await exists(tPath)) {
|
|
597
|
+
return `Error: Task "${args.taskId}" not found in team "${args.teamName}"`;
|
|
598
|
+
}
|
|
599
|
+
const task = await readJson(tPath);
|
|
600
|
+
for (const blockerId of task.blockedBy) {
|
|
601
|
+
const blockerPath = taskFilePath(directory, args.teamName, blockerId);
|
|
602
|
+
if (await exists(blockerPath)) {
|
|
603
|
+
const blocker = await readJson(blockerPath);
|
|
604
|
+
blocker.blocks = blocker.blocks.filter((id) => id !== args.taskId);
|
|
605
|
+
await writeJsonAtomic(blockerPath, blocker);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
for (const blockedId of task.blocks) {
|
|
609
|
+
const blockedPath = taskFilePath(directory, args.teamName, blockedId);
|
|
610
|
+
if (await exists(blockedPath)) {
|
|
611
|
+
const blocked = await readJson(blockedPath);
|
|
612
|
+
blocked.blockedBy = blocked.blockedBy.filter((id) => id !== args.taskId);
|
|
613
|
+
await writeJsonAtomic(blockedPath, blocked);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
await unlink2(tPath);
|
|
617
|
+
return `Task "${args.taskId}" deleted and all dependency links cleaned up`;
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
}),
|
|
536
621
|
task_get: tool2({
|
|
537
622
|
description: "Get a single task by ID.",
|
|
538
623
|
args: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hdwebsoft/hdcode-agent-team",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "OpenCode plugin for multi-agent team coordination — per-agent inboxes, task management with dependency tracking, and file locking.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|