@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.
Files changed (2) hide show
  1. package/dist/index.js +85 -0
  2. 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.2",
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",