pi-crew 0.8.8 → 0.8.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-crew",
3
- "version": "0.8.8",
3
+ "version": "0.8.9",
4
4
  "description": "Pi extension for coordinated AI teams, workflows, worktrees, and async task orchestration",
5
5
  "author": "baphuongna",
6
6
  "license": "MIT",
@@ -29,6 +29,31 @@ import { appendEventAsync, readEvents } from "../../state/event-log.ts";
29
29
  import { resolveCrewRuntime, runtimeResolutionState } from "../../runtime/runtime-resolver.ts";
30
30
  import { normalizeSkillOverride } from "../../runtime/skill-instructions.ts";
31
31
  import { expandParallelResearchWorkflow } from "../../runtime/parallel-research.ts";
32
+
33
+ /**
34
+ * Module-scoped latch for the crew-init dynamic import.
35
+ *
36
+ * `crew-init.ts` is dynamically `await import()`'d from `handleRun` below, which
37
+ * N concurrent subagents hit simultaneously (every `team` tool call runs it).
38
+ * Under the tsx/jiti loader, concurrent first-imports race module-record
39
+ * instantiation → top-level `const` initializers (e.g. CREW_README) hit TDZ
40
+ * (`Cannot access 'CREW_README' before initialization`) and namespace bindings
41
+ * arrive as `undefined` (`reading 'existsSync'`). crew-init.ts's own header
42
+ * documents this for the `path` binding; the race persists for other top-level
43
+ * consts because module-body evaluation itself races.
44
+ *
45
+ * The latch makes concurrent callers share ONE in-flight import promise, so the
46
+ * module body evaluates exactly once regardless of fanout. Same pattern as
47
+ * runtime-warmup.ts / the v0.8.1 peer-dep latch, applied to this specific
48
+ * dynamic-import race site.
49
+ */
50
+ let crewInitPromise: Promise<typeof import("../../state/crew-init.ts")> | undefined;
51
+ function loadCrewInit(): Promise<typeof import("../../state/crew-init.ts")> {
52
+ if (!crewInitPromise) {
53
+ crewInitPromise = import("../../state/crew-init.ts");
54
+ }
55
+ return crewInitPromise;
56
+ }
32
57
  import { checkProcessLiveness, isActiveRunStatus } from "../../runtime/process-status.ts";
33
58
  import { waitForRun } from "../../runtime/run-tracker.ts";
34
59
  import { hasAsyncStartMarker } from "../../runtime/async-marker.ts";
@@ -81,9 +106,11 @@ export async function handleRun(params: TeamToolParamsValue, ctx: TeamContext):
81
106
  const intentPrefix = goal.length > 60 ? `${goal.slice(0, 57)}...` : goal;
82
107
 
83
108
  // P0: Ensure .crew directory structure exists before creating any manifests.
84
- // Dynamic import to avoid module binding issues in child-process contexts.
109
+ // Latched dynamic import (loadCrewInit) concurrent `team` tool calls from
110
+ // N subagents share ONE in-flight promise so crew-init.ts's module body
111
+ // evaluates exactly once (avoids the cold-start race on CREW_README / path / fs).
85
112
  const workingDir = ctx.cwd ?? process.cwd();
86
- const { ensureCrewDirectory } = await import("../../state/crew-init.ts");
113
+ const { ensureCrewDirectory } = await loadCrewInit();
87
114
  await ensureCrewDirectory(workingDir);
88
115
 
89
116
  // WORKTREE FIX: If worktree mode is needed but cwd is not a git repo,
@@ -54,6 +54,7 @@ const HOT_MODULE_SPECIFIERS = [
54
54
  "./task-runner.ts",
55
55
  "../extension/team-tool.ts",
56
56
  "../extension/validate-resources.ts",
57
+ "../state/crew-init.ts", // TDZ-prone top-level consts (CREW_README); dynamically imported by team-tool/run.ts
57
58
  ] as const;
58
59
 
59
60
  /** Additional bare-specifier peer deps to warm. */