pi-crew 0.1.30 → 0.1.32

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 (159) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +80 -27
  3. package/agents/analyst.md +11 -11
  4. package/agents/critic.md +11 -11
  5. package/agents/executor.md +11 -11
  6. package/agents/explorer.md +11 -11
  7. package/agents/planner.md +11 -11
  8. package/agents/reviewer.md +11 -11
  9. package/agents/security-reviewer.md +11 -11
  10. package/agents/test-engineer.md +11 -11
  11. package/agents/verifier.md +11 -11
  12. package/agents/writer.md +11 -11
  13. package/docs/architecture.md +173 -164
  14. package/docs/refactor-tasks-phase3.md +394 -394
  15. package/docs/refactor-tasks-phase4.md +564 -564
  16. package/docs/refactor-tasks-phase5.md +402 -402
  17. package/docs/refactor-tasks-phase6.md +662 -662
  18. package/docs/research-extension-examples.md +297 -0
  19. package/docs/research-extension-system.md +324 -0
  20. package/docs/research-optimization-plan.md +548 -0
  21. package/docs/research-pi-coding-agent.md +357 -0
  22. package/docs/resource-formats.md +4 -3
  23. package/docs/runtime-flow.md +148 -148
  24. package/docs/source-runtime-refactor-map.md +83 -83
  25. package/docs/usage.md +3 -3
  26. package/index.ts +6 -6
  27. package/package.json +1 -1
  28. package/schema.json +53 -1
  29. package/skills/git-master/SKILL.md +24 -19
  30. package/skills/read-only-explorer/SKILL.md +26 -21
  31. package/skills/safe-bash/SKILL.md +21 -16
  32. package/skills/task-packet/SKILL.md +28 -23
  33. package/skills/verify-evidence/SKILL.md +27 -22
  34. package/src/agents/agent-serializer.ts +34 -34
  35. package/src/agents/discover-agents.ts +102 -102
  36. package/src/config/config.ts +14 -1
  37. package/src/config/defaults.ts +3 -2
  38. package/src/extension/cross-extension-rpc.ts +82 -82
  39. package/src/extension/import-index.ts +4 -3
  40. package/src/extension/management.ts +2 -2
  41. package/src/extension/project-init.ts +9 -7
  42. package/src/extension/register.ts +63 -0
  43. package/src/extension/registration/artifact-cleanup.ts +15 -14
  44. package/src/extension/registration/commands.ts +208 -208
  45. package/src/extension/registration/compaction-guard.ts +125 -0
  46. package/src/extension/registration/subagent-tools.ts +27 -8
  47. package/src/extension/registration/team-tool.ts +61 -44
  48. package/src/extension/result-watcher.ts +98 -98
  49. package/src/extension/run-import.ts +4 -4
  50. package/src/extension/run-index.ts +14 -14
  51. package/src/extension/run-maintenance.ts +24 -24
  52. package/src/extension/team-tool/api.ts +3 -0
  53. package/src/extension/team-tool/cancel.ts +31 -31
  54. package/src/extension/team-tool/doctor.ts +179 -178
  55. package/src/extension/team-tool/inspect.ts +41 -41
  56. package/src/extension/team-tool/lifecycle-actions.ts +79 -79
  57. package/src/extension/team-tool/plan.ts +19 -19
  58. package/src/extension/team-tool/status.ts +73 -73
  59. package/src/prompt/prompt-runtime.ts +68 -68
  60. package/src/runtime/agent-control.ts +64 -64
  61. package/src/runtime/agent-memory.ts +72 -72
  62. package/src/runtime/agent-observability.ts +113 -113
  63. package/src/runtime/async-marker.ts +26 -26
  64. package/src/runtime/background-runner.ts +53 -53
  65. package/src/runtime/crew-agent-runtime.ts +58 -58
  66. package/src/runtime/direct-run.ts +35 -35
  67. package/src/runtime/foreground-control.ts +82 -82
  68. package/src/runtime/green-contract.ts +46 -46
  69. package/src/runtime/group-join.ts +88 -88
  70. package/src/runtime/live-agent-control.ts +78 -78
  71. package/src/runtime/live-agent-manager.ts +85 -85
  72. package/src/runtime/live-control-realtime.ts +36 -36
  73. package/src/runtime/live-session-runtime.ts +299 -299
  74. package/src/runtime/manifest-cache.ts +212 -214
  75. package/src/runtime/model-fallback.ts +261 -261
  76. package/src/runtime/parallel-research.ts +44 -44
  77. package/src/runtime/parallel-utils.ts +99 -99
  78. package/src/runtime/pi-json-output.ts +111 -111
  79. package/src/runtime/pi-spawn.ts +96 -96
  80. package/src/runtime/policy-engine.ts +78 -78
  81. package/src/runtime/post-exit-stdio-guard.ts +86 -86
  82. package/src/runtime/process-status.ts +56 -56
  83. package/src/runtime/progress-event-coalescer.ts +43 -43
  84. package/src/runtime/recovery-recipes.ts +74 -74
  85. package/src/runtime/role-permission.ts +39 -39
  86. package/src/runtime/session-usage.ts +79 -79
  87. package/src/runtime/sidechain-output.ts +28 -28
  88. package/src/runtime/subagent-manager.ts +29 -2
  89. package/src/runtime/task-display.ts +38 -38
  90. package/src/runtime/task-output-context.ts +106 -106
  91. package/src/runtime/task-packet.ts +84 -84
  92. package/src/runtime/task-runner/live-executor.ts +98 -98
  93. package/src/runtime/task-runner/progress.ts +111 -111
  94. package/src/runtime/task-runner/prompt-builder.ts +72 -72
  95. package/src/runtime/task-runner/result-utils.ts +14 -14
  96. package/src/runtime/task-runner/state-helpers.ts +22 -22
  97. package/src/runtime/worker-heartbeat.ts +21 -21
  98. package/src/runtime/worker-startup.ts +57 -57
  99. package/src/schema/config-schema.ts +12 -0
  100. package/src/schema/team-tool-schema.ts +100 -100
  101. package/src/state/artifact-store.ts +108 -108
  102. package/src/state/contracts.ts +105 -105
  103. package/src/state/jsonl-writer.ts +77 -77
  104. package/src/state/state-store.ts +8 -9
  105. package/src/state/task-claims.ts +42 -42
  106. package/src/state/types.ts +180 -180
  107. package/src/state/usage.ts +29 -29
  108. package/src/subagents/async-entry.ts +1 -1
  109. package/src/subagents/index.ts +3 -3
  110. package/src/subagents/live/control.ts +1 -1
  111. package/src/subagents/live/manager.ts +1 -1
  112. package/src/subagents/live/realtime.ts +1 -1
  113. package/src/subagents/live/session-runtime.ts +1 -1
  114. package/src/subagents/manager.ts +1 -1
  115. package/src/subagents/spawn.ts +1 -1
  116. package/src/teams/discover-teams.ts +2 -2
  117. package/src/teams/team-serializer.ts +36 -36
  118. package/src/types/diff.d.ts +18 -0
  119. package/src/ui/crew-footer.ts +101 -101
  120. package/src/ui/crew-select-list.ts +111 -111
  121. package/src/ui/crew-widget.ts +285 -285
  122. package/src/ui/dynamic-border.ts +25 -25
  123. package/src/ui/layout-primitives.ts +106 -106
  124. package/src/ui/loaders.ts +158 -158
  125. package/src/ui/mascot.ts +441 -441
  126. package/src/ui/powerbar-publisher.ts +94 -94
  127. package/src/ui/render-diff.ts +119 -119
  128. package/src/ui/run-dashboard.ts +372 -372
  129. package/src/ui/status-colors.ts +54 -54
  130. package/src/ui/syntax-highlight.ts +116 -116
  131. package/src/ui/transcript-viewer.ts +302 -302
  132. package/src/utils/completion-dedupe.ts +63 -63
  133. package/src/utils/file-coalescer.ts +84 -84
  134. package/src/utils/frontmatter.ts +36 -36
  135. package/src/utils/fs-watch.ts +31 -31
  136. package/src/utils/git.ts +262 -262
  137. package/src/utils/ids.ts +12 -12
  138. package/src/utils/names.ts +26 -26
  139. package/src/utils/paths.ts +34 -7
  140. package/src/utils/sleep.ts +32 -32
  141. package/src/utils/timings.ts +31 -31
  142. package/src/utils/visual.ts +159 -159
  143. package/src/workflows/discover-workflows.ts +2 -2
  144. package/src/workflows/validate-workflow.ts +40 -40
  145. package/src/worktree/branch-freshness.ts +45 -45
  146. package/src/worktree/cleanup.ts +71 -69
  147. package/src/worktree/worktree-manager.ts +3 -1
  148. package/teams/default.team.md +12 -12
  149. package/teams/fast-fix.team.md +11 -11
  150. package/teams/implementation.team.md +18 -18
  151. package/teams/parallel-research.team.md +14 -14
  152. package/teams/research.team.md +11 -11
  153. package/teams/review.team.md +12 -12
  154. package/workflows/default.workflow.md +29 -29
  155. package/workflows/fast-fix.workflow.md +22 -22
  156. package/workflows/implementation.workflow.md +38 -38
  157. package/workflows/parallel-research.workflow.md +46 -46
  158. package/workflows/research.workflow.md +22 -22
  159. package/workflows/review.workflow.md +30 -30
@@ -1,214 +1,212 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { closeWatcher, watchWithErrorHandler } from "../utils/fs-watch.ts";
4
- import { projectPiRoot, userPiRoot } from "../utils/paths.ts";
5
- import type { TeamRunManifest } from "../state/types.ts";
6
- import { DEFAULT_CACHE, DEFAULT_PATHS } from "../config/defaults.ts";
7
-
8
- export interface ManifestCache {
9
- list(limit?: number): TeamRunManifest[];
10
- get(runId: string): TeamRunManifest | undefined;
11
- clear(runId?: string): void;
12
- dispose(): void;
13
- }
14
-
15
- interface CachedManifest {
16
- path: string;
17
- manifest: TeamRunManifest;
18
- mtimeMs: number;
19
- size: number;
20
- loadedAtMs: number;
21
- }
22
-
23
- interface CachedList {
24
- runs: TeamRunManifest[];
25
- limit?: number;
26
- expireAtMs: number;
27
- }
28
-
29
- export interface ManifestCacheOptions {
30
- debounceMs?: number;
31
- watch?: boolean;
32
- maxEntries?: number;
33
- }
34
-
35
- const DEFAULT_TTL_MS = 500;
36
-
37
- interface ParsedEntry {
38
- runId: string;
39
- path: string;
40
- manifest?: TeamRunManifest;
41
- }
42
-
43
- function manifestPathForRun(root: string, runId: string): string {
44
- return path.join(root, runId, DEFAULT_PATHS.state.manifestFile);
45
- }
46
-
47
- function parseManifest(filePath: string): TeamRunManifest | undefined {
48
- try {
49
- return JSON.parse(fs.readFileSync(filePath, "utf-8")) as TeamRunManifest;
50
- } catch {
51
- return undefined;
52
- }
53
- }
54
-
55
- function parseManifestIfChanged(filePath: string, previous?: CachedManifest): CachedManifest | undefined {
56
- let stat: fs.Stats;
57
- try {
58
- stat = fs.statSync(filePath);
59
- } catch {
60
- return undefined;
61
- }
62
- if (previous && previous.mtimeMs === stat.mtimeMs && previous.size === stat.size) {
63
- return previous;
64
- }
65
- const manifest = parseManifest(filePath);
66
- if (!manifest) return undefined;
67
- return {
68
- path: filePath,
69
- manifest,
70
- mtimeMs: stat.mtimeMs,
71
- size: stat.size,
72
- loadedAtMs: Date.now(),
73
- };
74
- }
75
-
76
- function listRunRoots(cwd: string): string[] {
77
- return [
78
- path.join(userPiRoot(), "extensions", "pi-crew", DEFAULT_PATHS.state.userBase),
79
- path.join(projectPiRoot(cwd), DEFAULT_PATHS.state.projectBase),
80
- ].map((root) => path.join(root, DEFAULT_PATHS.state.runsSubdir));
81
- }
82
-
83
- function collectRoots(root: string): ParsedEntry[] {
84
- if (!fs.existsSync(root)) return [];
85
- let entries: string[];
86
- try {
87
- entries = fs.readdirSync(root);
88
- } catch {
89
- return [];
90
- }
91
- return entries
92
- .filter((entry) => entry.length > 0)
93
- .map((entry) => ({ runId: entry, path: manifestPathForRun(root, entry) }));
94
- }
95
-
96
- export function createManifestCache(cwd: string, options: ManifestCacheOptions = {}): ManifestCache {
97
- const ttlMs = options.debounceMs ?? DEFAULT_TTL_MS;
98
- const maxEntries = options.maxEntries ?? DEFAULT_CACHE.manifestMaxEntries;
99
- const roots = listRunRoots(cwd);
100
- const manifestIndex = new Map<string, CachedManifest>();
101
- const listCache = new Map<number, CachedList>();
102
- let listTimer: ReturnType<typeof setTimeout> | undefined;
103
- let watchers: fs.FSWatcher[] = [];
104
-
105
- function invalidate(runId?: string): void {
106
- if (runId) {
107
- manifestIndex.delete(runId);
108
- } else {
109
- manifestIndex.clear();
110
- }
111
- listCache.clear();
112
- }
113
-
114
- function scheduleListRefresh(): void {
115
- if (listTimer) {
116
- clearTimeout(listTimer);
117
- }
118
- listTimer = setTimeout(() => {
119
- listTimer = undefined;
120
- listCache.clear();
121
- }, ttlMs);
122
- listTimer.unref?.();
123
- }
124
-
125
- function loadManifest(runId: string, rootsToCheck: string[]): CachedManifest | undefined {
126
- let cached = manifestIndex.get(runId);
127
- for (const root of rootsToCheck) {
128
- const manifestPath = manifestPathForRun(root, runId);
129
- const parsed = parseManifestIfChanged(manifestPath, cached);
130
- if (parsed) {
131
- if (!cached || parsed.mtimeMs !== cached.mtimeMs || parsed.size !== cached.size) {
132
- manifestIndex.set(runId, parsed);
133
- if (manifestIndex.size > maxEntries) {
134
- const oldest = [...manifestIndex.values()].sort((a, b) => a.loadedAtMs - b.loadedAtMs)[0];
135
- if (oldest) manifestIndex.delete(oldest.manifest.runId);
136
- }
137
- }
138
- return manifestIndex.get(runId);
139
- }
140
- }
141
- return undefined;
142
- }
143
-
144
- function list(limit = DEFAULT_CACHE.manifestMaxEntries): TeamRunManifest[] {
145
- const now = Date.now();
146
- const cached = listCache.get(limit);
147
- if (cached && cached.expireAtMs > now) {
148
- return cached.runs;
149
- }
150
- const parsedEntries = roots.flatMap((root) => collectRoots(root));
151
- const unique = new Map<string, CachedManifest | undefined>();
152
- for (const entry of parsedEntries) {
153
- if (entry.runId.length === 0) continue;
154
- let cached = manifestIndex.get(entry.runId);
155
- const parsed = parseManifestIfChanged(entry.path, cached);
156
- if (parsed) {
157
- cached = parsed;
158
- manifestIndex.set(entry.runId, cached);
159
- }
160
- if (cached) unique.set(entry.runId, cached);
161
- }
162
-
163
-
164
- const runs = [...unique.values()].filter((value): value is CachedManifest => value !== undefined).map((value) => value.manifest);
165
- const sorted = runs.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
166
- const limited = sorted.slice(0, Math.max(0, limit));
167
- if (manifestIndex.size > maxEntries) {
168
- const removeCount = manifestIndex.size - maxEntries;
169
- const oldest = [...manifestIndex.values()].sort((a, b) => a.loadedAtMs - b.loadedAtMs).slice(0, removeCount);
170
- for (const entry of oldest) manifestIndex.delete(entry.manifest.runId);
171
- }
172
- const result = limited;
173
- listCache.set(limit, { runs: result, limit, expireAtMs: now + ttlMs });
174
- return result;
175
- }
176
-
177
- function get(runId: string): TeamRunManifest | undefined {
178
- const cached = loadManifest(runId, roots);
179
- if (cached) return cached.manifest;
180
- return undefined;
181
- }
182
-
183
- if (options.watch ?? true) {
184
- for (const root of roots) {
185
- const watcher = watchWithErrorHandler(root, () => {
186
- scheduleListRefresh();
187
- }, () => {
188
- scheduleListRefresh();
189
- });
190
- if (watcher) {
191
- watcher.unref?.();
192
- watchers.push(watcher);
193
- }
194
- }
195
- }
196
-
197
- return {
198
- list,
199
- get,
200
- clear(runId) {
201
- invalidate(runId);
202
- },
203
- dispose() {
204
- if (listTimer) {
205
- clearTimeout(listTimer);
206
- listTimer = undefined;
207
- }
208
- for (const watcher of watchers) closeWatcher(watcher);
209
- watchers = [];
210
- manifestIndex.clear();
211
- listCache.clear();
212
- },
213
- };
214
- }
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { closeWatcher, watchWithErrorHandler } from "../utils/fs-watch.ts";
4
+ import { findRepoRoot, projectCrewRoot, userCrewRoot } from "../utils/paths.ts";
5
+ import type { TeamRunManifest } from "../state/types.ts";
6
+ import { DEFAULT_CACHE, DEFAULT_PATHS } from "../config/defaults.ts";
7
+
8
+ export interface ManifestCache {
9
+ list(limit?: number): TeamRunManifest[];
10
+ get(runId: string): TeamRunManifest | undefined;
11
+ clear(runId?: string): void;
12
+ dispose(): void;
13
+ }
14
+
15
+ interface CachedManifest {
16
+ path: string;
17
+ manifest: TeamRunManifest;
18
+ mtimeMs: number;
19
+ size: number;
20
+ loadedAtMs: number;
21
+ }
22
+
23
+ interface CachedList {
24
+ runs: TeamRunManifest[];
25
+ limit?: number;
26
+ expireAtMs: number;
27
+ }
28
+
29
+ export interface ManifestCacheOptions {
30
+ debounceMs?: number;
31
+ watch?: boolean;
32
+ maxEntries?: number;
33
+ }
34
+
35
+ const DEFAULT_TTL_MS = 500;
36
+
37
+ interface ParsedEntry {
38
+ runId: string;
39
+ path: string;
40
+ manifest?: TeamRunManifest;
41
+ }
42
+
43
+ function manifestPathForRun(root: string, runId: string): string {
44
+ return path.join(root, runId, DEFAULT_PATHS.state.manifestFile);
45
+ }
46
+
47
+ function parseManifest(filePath: string): TeamRunManifest | undefined {
48
+ try {
49
+ return JSON.parse(fs.readFileSync(filePath, "utf-8")) as TeamRunManifest;
50
+ } catch {
51
+ return undefined;
52
+ }
53
+ }
54
+
55
+ function parseManifestIfChanged(filePath: string, previous?: CachedManifest): CachedManifest | undefined {
56
+ let stat: fs.Stats;
57
+ try {
58
+ stat = fs.statSync(filePath);
59
+ } catch {
60
+ return undefined;
61
+ }
62
+ if (previous && previous.mtimeMs === stat.mtimeMs && previous.size === stat.size) {
63
+ return previous;
64
+ }
65
+ const manifest = parseManifest(filePath);
66
+ if (!manifest) return undefined;
67
+ return {
68
+ path: filePath,
69
+ manifest,
70
+ mtimeMs: stat.mtimeMs,
71
+ size: stat.size,
72
+ loadedAtMs: Date.now(),
73
+ };
74
+ }
75
+
76
+ function listRunRoots(cwd: string): string[] {
77
+ const base = findRepoRoot(cwd) ? projectCrewRoot(cwd) : userCrewRoot();
78
+ return [path.join(base, DEFAULT_PATHS.state.runsSubdir)];
79
+ }
80
+
81
+ function collectRoots(root: string): ParsedEntry[] {
82
+ if (!fs.existsSync(root)) return [];
83
+ let entries: string[];
84
+ try {
85
+ entries = fs.readdirSync(root);
86
+ } catch {
87
+ return [];
88
+ }
89
+ return entries
90
+ .filter((entry) => entry.length > 0)
91
+ .map((entry) => ({ runId: entry, path: manifestPathForRun(root, entry) }));
92
+ }
93
+
94
+ export function createManifestCache(cwd: string, options: ManifestCacheOptions = {}): ManifestCache {
95
+ const ttlMs = options.debounceMs ?? DEFAULT_TTL_MS;
96
+ const maxEntries = options.maxEntries ?? DEFAULT_CACHE.manifestMaxEntries;
97
+ const roots = listRunRoots(cwd);
98
+ const manifestIndex = new Map<string, CachedManifest>();
99
+ const listCache = new Map<number, CachedList>();
100
+ let listTimer: ReturnType<typeof setTimeout> | undefined;
101
+ let watchers: fs.FSWatcher[] = [];
102
+
103
+ function invalidate(runId?: string): void {
104
+ if (runId) {
105
+ manifestIndex.delete(runId);
106
+ } else {
107
+ manifestIndex.clear();
108
+ }
109
+ listCache.clear();
110
+ }
111
+
112
+ function scheduleListRefresh(): void {
113
+ if (listTimer) {
114
+ clearTimeout(listTimer);
115
+ }
116
+ listTimer = setTimeout(() => {
117
+ listTimer = undefined;
118
+ listCache.clear();
119
+ }, ttlMs);
120
+ listTimer.unref?.();
121
+ }
122
+
123
+ function loadManifest(runId: string, rootsToCheck: string[]): CachedManifest | undefined {
124
+ let cached = manifestIndex.get(runId);
125
+ for (const root of rootsToCheck) {
126
+ const manifestPath = manifestPathForRun(root, runId);
127
+ const parsed = parseManifestIfChanged(manifestPath, cached);
128
+ if (parsed) {
129
+ if (!cached || parsed.mtimeMs !== cached.mtimeMs || parsed.size !== cached.size) {
130
+ manifestIndex.set(runId, parsed);
131
+ if (manifestIndex.size > maxEntries) {
132
+ const oldest = [...manifestIndex.values()].sort((a, b) => a.loadedAtMs - b.loadedAtMs)[0];
133
+ if (oldest) manifestIndex.delete(oldest.manifest.runId);
134
+ }
135
+ }
136
+ return manifestIndex.get(runId);
137
+ }
138
+ }
139
+ return undefined;
140
+ }
141
+
142
+ function list(limit = DEFAULT_CACHE.manifestMaxEntries): TeamRunManifest[] {
143
+ const now = Date.now();
144
+ const cached = listCache.get(limit);
145
+ if (cached && cached.expireAtMs > now) {
146
+ return cached.runs;
147
+ }
148
+ const parsedEntries = roots.flatMap((root) => collectRoots(root));
149
+ const unique = new Map<string, CachedManifest | undefined>();
150
+ for (const entry of parsedEntries) {
151
+ if (entry.runId.length === 0) continue;
152
+ let cached = manifestIndex.get(entry.runId);
153
+ const parsed = parseManifestIfChanged(entry.path, cached);
154
+ if (parsed) {
155
+ cached = parsed;
156
+ manifestIndex.set(entry.runId, cached);
157
+ }
158
+ if (cached) unique.set(entry.runId, cached);
159
+ }
160
+
161
+
162
+ const runs = [...unique.values()].filter((value): value is CachedManifest => value !== undefined).map((value) => value.manifest);
163
+ const sorted = runs.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
164
+ const limited = sorted.slice(0, Math.max(0, limit));
165
+ if (manifestIndex.size > maxEntries) {
166
+ const removeCount = manifestIndex.size - maxEntries;
167
+ const oldest = [...manifestIndex.values()].sort((a, b) => a.loadedAtMs - b.loadedAtMs).slice(0, removeCount);
168
+ for (const entry of oldest) manifestIndex.delete(entry.manifest.runId);
169
+ }
170
+ const result = limited;
171
+ listCache.set(limit, { runs: result, limit, expireAtMs: now + ttlMs });
172
+ return result;
173
+ }
174
+
175
+ function get(runId: string): TeamRunManifest | undefined {
176
+ const cached = loadManifest(runId, roots);
177
+ if (cached) return cached.manifest;
178
+ return undefined;
179
+ }
180
+
181
+ if (options.watch ?? true) {
182
+ for (const root of roots) {
183
+ const watcher = watchWithErrorHandler(root, () => {
184
+ scheduleListRefresh();
185
+ }, () => {
186
+ scheduleListRefresh();
187
+ });
188
+ if (watcher) {
189
+ watcher.unref?.();
190
+ watchers.push(watcher);
191
+ }
192
+ }
193
+ }
194
+
195
+ return {
196
+ list,
197
+ get,
198
+ clear(runId) {
199
+ invalidate(runId);
200
+ },
201
+ dispose() {
202
+ if (listTimer) {
203
+ clearTimeout(listTimer);
204
+ listTimer = undefined;
205
+ }
206
+ for (const watcher of watchers) closeWatcher(watcher);
207
+ watchers = [];
208
+ manifestIndex.clear();
209
+ listCache.clear();
210
+ },
211
+ };
212
+ }