@hdwebsoft/hdcode-agent-team 0.2.0 → 0.2.1

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 +89 -0
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/index.ts
2
+ import { relative, isAbsolute } from "node:path";
3
+
1
4
  // src/teams.ts
2
5
  import { readdir as readdir2 } from "node:fs/promises";
3
6
  import { join as join3 } from "node:path";
@@ -360,6 +363,7 @@ import { tool as tool2 } from "@opencode-ai/plugin";
360
363
 
361
364
  // src/reservations.ts
362
365
  import { join as join5 } from "node:path";
366
+ import { readdir as readdir4 } from "node:fs/promises";
363
367
  import { randomUUID } from "node:crypto";
364
368
  var DEFAULT_TTL_SECONDS = 1800;
365
369
  var MAX_TTL_SECONDS = 14400;
@@ -590,6 +594,70 @@ async function checkFile(base, teamName, path, agentId) {
590
594
  const canEdit = !reserved || !!agentId && holders.every((h) => h.agentId === agentId);
591
595
  return { reserved, canEdit, holders };
592
596
  }
597
+ async function checkFileAcrossTeams(base, filePath, sessionId) {
598
+ const root = join5(base, TEAM_ROOT);
599
+ if (!await exists(root))
600
+ return null;
601
+ let normalized;
602
+ try {
603
+ normalized = normalizeRepoPath(filePath);
604
+ } catch {
605
+ return null;
606
+ }
607
+ let entries;
608
+ try {
609
+ entries = await readdir4(root, { withFileTypes: true });
610
+ } catch {
611
+ return null;
612
+ }
613
+ for (const entry of entries) {
614
+ if (!entry.isDirectory() || entry.name.startsWith("."))
615
+ continue;
616
+ const storePath = reservationStorePath(base, entry.name);
617
+ if (!await exists(storePath))
618
+ continue;
619
+ let store;
620
+ try {
621
+ store = await readJson(storePath);
622
+ } catch {
623
+ continue;
624
+ }
625
+ pruneExpired(store);
626
+ for (const r of store.reservations) {
627
+ if (r.files.includes(normalized)) {
628
+ return {
629
+ teamName: entry.name,
630
+ holder: {
631
+ reservationId: r.id,
632
+ agentId: r.agentId,
633
+ taskId: r.taskId,
634
+ reason: r.reason,
635
+ expiresAt: r.expiresAt,
636
+ matchedBy: "file",
637
+ value: normalized
638
+ }
639
+ };
640
+ }
641
+ for (const p of r.prefixes) {
642
+ if (isPathInsidePrefix(normalized, p)) {
643
+ return {
644
+ teamName: entry.name,
645
+ holder: {
646
+ reservationId: r.id,
647
+ agentId: r.agentId,
648
+ taskId: r.taskId,
649
+ reason: r.reason,
650
+ expiresAt: r.expiresAt,
651
+ matchedBy: "prefix",
652
+ value: p
653
+ }
654
+ };
655
+ }
656
+ }
657
+ }
658
+ }
659
+ return null;
660
+ }
593
661
 
594
662
  // src/tools/task-tools.ts
595
663
  function parseStringArray(raw, label) {
@@ -1143,6 +1211,7 @@ function createReservationTools(directory) {
1143
1211
  }
1144
1212
 
1145
1213
  // src/index.ts
1214
+ var WRITE_TOOLS = new Set(["edit", "write", "create"]);
1146
1215
  var HDTeamPlugin = async ({ directory }) => {
1147
1216
  return {
1148
1217
  "experimental.session.compacting": async (_input, output) => {
@@ -1159,6 +1228,26 @@ Use team_status(teamName) to get full details. Continue any in-progress tasks.
1159
1228
  `);
1160
1229
  }
1161
1230
  },
1231
+ "tool.execute.before": async (input, output) => {
1232
+ if (!WRITE_TOOLS.has(input.tool))
1233
+ return;
1234
+ const filePath = output.args.filePath;
1235
+ if (!filePath)
1236
+ return;
1237
+ let repoPath = filePath;
1238
+ if (isAbsolute(filePath)) {
1239
+ repoPath = relative(directory, filePath);
1240
+ if (repoPath.startsWith(".."))
1241
+ return;
1242
+ }
1243
+ const match = await checkFileAcrossTeams(directory, repoPath);
1244
+ if (match) {
1245
+ const h = match.holder;
1246
+ const who = h.taskId ? `${h.agentId} (task ${h.taskId})` : h.agentId;
1247
+ const reason = h.reason ? ` — ${h.reason}` : "";
1248
+ throw new Error(`\uD83D\uDD12 File "${repoPath}" is reserved by ${who} in team "${match.teamName}"${reason}. ` + `Use file_check to see details or wait for the reservation to expire (${h.expiresAt}).`);
1249
+ }
1250
+ },
1162
1251
  tool: {
1163
1252
  ...createTeamTools(directory),
1164
1253
  ...createTaskTools(directory),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hdwebsoft/hdcode-agent-team",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "OpenCode plugin for multi-agent team coordination — per-agent inboxes, task management with dependency tracking, file reservation/locking, and atomic mkdir-based concurrency control.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",