tau-coding-agent 0.1.6 → 0.2.0

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 (80) hide show
  1. package/README.md +15 -12
  2. package/extensions/answer.ts +129 -79
  3. package/extensions/branch-term/README.md +7 -0
  4. package/extensions/{branch-term.ts → branch-term/index.ts} +113 -104
  5. package/extensions/btw.ts +17 -2
  6. package/extensions/caffeinate/README.md +5 -0
  7. package/extensions/caffeinate/index.ts +144 -0
  8. package/extensions/fast.ts +292 -0
  9. package/extensions/ghostty.ts +214 -211
  10. package/extensions/git-diff-stats.ts +124 -84
  11. package/extensions/git-pr-status.ts +274 -208
  12. package/extensions/insights.ts +109 -191
  13. package/extensions/loop.ts +149 -145
  14. package/extensions/memory.ts +138 -86
  15. package/extensions/notify.ts +14 -26
  16. package/extensions/openai-verbosity.ts +108 -42
  17. package/extensions/review/fix.ts +15 -6
  18. package/extensions/review/git.ts +93 -103
  19. package/extensions/review/index.ts +60 -62
  20. package/extensions/review/interrupt.ts +117 -0
  21. package/extensions/review/message-queue.ts +45 -12
  22. package/extensions/review/models.ts +79 -3
  23. package/extensions/review/prompts.ts +41 -38
  24. package/extensions/review/review.ts +161 -166
  25. package/extensions/review/runner.ts +139 -147
  26. package/extensions/review/runtime.ts +173 -120
  27. package/extensions/review/triage.ts +41 -44
  28. package/extensions/sandbox/bash.ts +775 -0
  29. package/extensions/sandbox/command.ts +605 -0
  30. package/extensions/sandbox/config.ts +764 -0
  31. package/extensions/sandbox/index.ts +132 -3338
  32. package/extensions/sandbox/permissions/dialog.ts +118 -0
  33. package/extensions/sandbox/permissions/filesystem.ts +559 -0
  34. package/extensions/sandbox/permissions/mach-lookup.ts +186 -0
  35. package/extensions/sandbox/permissions/network.ts +164 -0
  36. package/extensions/sandbox/permissions/unsandboxed.ts +270 -0
  37. package/extensions/sandbox/runtime.ts +616 -0
  38. package/extensions/stash.ts +28 -15
  39. package/extensions/subagent/README.md +73 -0
  40. package/extensions/subagent/index.ts +822 -0
  41. package/extensions/subagent/interrupt.ts +117 -0
  42. package/extensions/subagent/permissions.ts +101 -0
  43. package/extensions/subagent/rpc.ts +177 -0
  44. package/extensions/tool-display-mode.ts +267 -64
  45. package/extensions/usage/index.ts +249 -356
  46. package/extensions/usage/openrouter.ts +10 -2
  47. package/extensions/websearch/README.md +12 -47
  48. package/extensions/websearch/config.ts +5 -2
  49. package/extensions/websearch/index.ts +94 -109
  50. package/extensions/websearch/output.ts +51 -0
  51. package/extensions/websearch/providers/anthropic.pi.ts +27 -44
  52. package/extensions/websearch/providers/gemini.browser.ts +68 -77
  53. package/extensions/websearch/providers/gemini.pi.ts +17 -17
  54. package/extensions/websearch/providers/openai-codex.pi.ts +145 -7
  55. package/extensions/websearch/providers/pi-model.shared.ts +33 -39
  56. package/extensions/websearch/providers/shared.ts +87 -2
  57. package/extensions/websearch/types.ts +0 -3
  58. package/extensions/worktree.ts +132 -172
  59. package/package.json +10 -5
  60. package/skills/browser-tools/SKILL.md +29 -234
  61. package/skills/browser-tools/references/cookies.md +36 -0
  62. package/skills/browser-tools/references/interaction.md +90 -0
  63. package/skills/browser-tools/references/logging.md +34 -0
  64. package/skills/git-clean-history/SKILL.md +6 -6
  65. package/skills/git-commit/SKILL.md +5 -3
  66. package/skills/github-pull-request/SKILL.md +60 -0
  67. package/skills/github-pull-request/references/create.md +40 -0
  68. package/skills/github-pull-request/references/stewardship.md +60 -0
  69. package/skills/oracle/SKILL.md +3 -3
  70. package/skills/oracle/scripts/oracle +10 -1
  71. package/skills/sentry/SKILL.md +15 -185
  72. package/skills/sentry/references/events.md +79 -0
  73. package/skills/sentry/references/issues.md +62 -0
  74. package/skills/sentry/references/logs.md +46 -0
  75. package/skills/update-changelog/SKILL.md +27 -121
  76. package/skills/web-design/SKILL.md +16 -105
  77. package/themes/tau-dark.json +4 -0
  78. package/extensions/openai-fast.ts +0 -229
  79. package/extensions/websearch/providers/openai-codex.browser.ts +0 -77
  80. package/extensions/websearch/providers/openai-codex.shared.ts +0 -123
@@ -1,3 +1,4 @@
1
+ import { spawn } from "node:child_process";
1
2
  import {
2
3
  isBashToolResult,
3
4
  type ExtensionAPI,
@@ -6,10 +7,11 @@ import {
6
7
 
7
8
  const STATUS_KEY = "1-git-pr-status";
8
9
 
9
- const REFRESH_DEBOUNCE_MS = 250;
10
10
  const PR_LOOKUP_TIMEOUT_MS = 5_000;
11
11
  const BRANCH_CHECK_TIMEOUT_MS = 1_000;
12
12
  const USER_BASH_SETTLE_DELAY_MS = 2_000;
13
+ const PROCESS_KILL_GRACE_MS = 500;
14
+ const PROCESS_PIPE_GRACE_MS = 100;
13
15
 
14
16
  const GIT_BRANCH_CHANGE_COMMAND_PATTERN = /\bgit\s+(checkout|switch)\b/;
15
17
  const GH_BRANCH_CHANGE_COMMAND_PATTERN = /\bgh\s+pr\s+checkout\b/;
@@ -28,6 +30,182 @@ type PullRequestLookupResult =
28
30
  | { kind: "none" }
29
31
  | { kind: "error" };
30
32
 
33
+ export default function gitPrStatusExtension(pi: ExtensionAPI) {
34
+ let ctx: ExtensionContext | undefined;
35
+ let currentBranch: string | null | undefined;
36
+ let pullRequest: PullRequestStatus | undefined;
37
+ let generation = 0;
38
+ let refreshTimer: NodeJS.Timeout | undefined;
39
+ let branchCheckTimer: NodeJS.Timeout | undefined;
40
+ let refreshInFlight: Promise<void> | undefined;
41
+ let branchCheckInFlight: Promise<void> | undefined;
42
+ let refreshQueued = false;
43
+ let branchCheckQueued = false;
44
+ let refreshController: AbortController | undefined;
45
+ let branchCheckController: AbortController | undefined;
46
+
47
+ pi.on("session_start", async (_event, nextCtx) => {
48
+ await reset(nextCtx);
49
+ });
50
+
51
+ pi.on("user_bash", (event, nextCtx) => {
52
+ if (!ctx) return;
53
+ ctx = nextCtx;
54
+
55
+ if (isBranchChangeCommand(event.command)) {
56
+ scheduleBranchCheck(USER_BASH_SETTLE_DELAY_MS);
57
+ } else if (isPullRequestStateCommand(event.command)) {
58
+ scheduleRefresh(USER_BASH_SETTLE_DELAY_MS);
59
+ }
60
+ });
61
+
62
+ pi.on("tool_result", async (event, nextCtx) => {
63
+ if (!ctx) return;
64
+ ctx = nextCtx;
65
+
66
+ // PowerShell is intentionally not observed because Tau does not officially support Windows.
67
+ if (!isBashToolResult(event)) return;
68
+ if (typeof event.input.command !== "string") return;
69
+
70
+ if (isBranchChangeCommand(event.input.command)) {
71
+ scheduleBranchCheck(0);
72
+ } else if (isPullRequestStateCommand(event.input.command)) {
73
+ scheduleRefresh(0);
74
+ }
75
+ });
76
+
77
+ pi.on("turn_end", async (_event, nextCtx) => {
78
+ if (!ctx) return;
79
+ ctx = nextCtx;
80
+ scheduleBranchCheck(0);
81
+ });
82
+
83
+ pi.on("session_shutdown", async () => {
84
+ await reset();
85
+ });
86
+
87
+ async function reset(nextCtx?: ExtensionContext): Promise<void> {
88
+ const resetGeneration = ++generation;
89
+ clearTimeout(refreshTimer);
90
+ clearTimeout(branchCheckTimer);
91
+ refreshTimer = undefined;
92
+ branchCheckTimer = undefined;
93
+ refreshQueued = false;
94
+ branchCheckQueued = false;
95
+ const pending = Promise.all([refreshInFlight, branchCheckInFlight]);
96
+ refreshController?.abort();
97
+ branchCheckController?.abort();
98
+ const previousCtx = ctx;
99
+ ctx = undefined;
100
+ currentBranch = undefined;
101
+ pullRequest = undefined;
102
+ try {
103
+ previousCtx?.ui.setStatus(STATUS_KEY, undefined);
104
+ } finally {
105
+ await pending;
106
+ }
107
+ if (generation === resetGeneration && nextCtx?.hasUI) {
108
+ ctx = nextCtx;
109
+ scheduleBranchCheck(0);
110
+ }
111
+ }
112
+
113
+ function scheduleBranchCheck(delay: number): void {
114
+ if (!ctx?.hasUI) return;
115
+ clearTimeout(branchCheckTimer);
116
+ const cwd = ctx.cwd;
117
+ const branchGeneration = generation;
118
+ branchCheckTimer = setTimeout(() => {
119
+ branchCheckTimer = undefined;
120
+ checkForBranchChange(cwd, branchGeneration);
121
+ }, delay);
122
+ }
123
+
124
+ function checkForBranchChange(cwd: string, branchGeneration: number): void {
125
+ if (branchCheckInFlight) {
126
+ branchCheckQueued = true;
127
+ return;
128
+ }
129
+ const controller = new AbortController();
130
+ branchCheckController = controller;
131
+ branchCheckInFlight = (async () => {
132
+ try {
133
+ const nextBranch = await loadCurrentBranch(cwd, controller.signal);
134
+ if (generation !== branchGeneration || controller.signal.aborted) return;
135
+ if (nextBranch === currentBranch) return;
136
+
137
+ currentBranch = nextBranch;
138
+ refreshController?.abort();
139
+ pullRequest = undefined;
140
+ applyStatus();
141
+ scheduleRefresh(0);
142
+ } catch {
143
+ // A failed branch check is not evidence that the previous PR disappeared.
144
+ } finally {
145
+ branchCheckInFlight = undefined;
146
+ branchCheckController = undefined;
147
+ const queued = branchCheckQueued;
148
+ branchCheckQueued = false;
149
+ if (queued && generation === branchGeneration) scheduleBranchCheck(0);
150
+ }
151
+ })();
152
+ }
153
+
154
+ function scheduleRefresh(delay: number): void {
155
+ if (!ctx?.hasUI) return;
156
+ if (currentBranch === undefined) {
157
+ scheduleBranchCheck(delay);
158
+ return;
159
+ }
160
+ clearTimeout(refreshTimer);
161
+ const cwd = ctx.cwd;
162
+ const refreshGeneration = generation;
163
+ refreshTimer = setTimeout(() => {
164
+ refreshTimer = undefined;
165
+ refresh(cwd, refreshGeneration);
166
+ }, delay);
167
+ }
168
+
169
+ function refresh(cwd: string, refreshGeneration: number): void {
170
+ if (refreshInFlight) {
171
+ refreshQueued = true;
172
+ return;
173
+ }
174
+ const controller = new AbortController();
175
+ refreshController = controller;
176
+ refreshInFlight = (async () => {
177
+ try {
178
+ const result = await loadPullRequestStatus(cwd, controller.signal);
179
+ if (generation !== refreshGeneration || controller.signal.aborted) return;
180
+
181
+ if (result.kind === "found") {
182
+ pullRequest = result.pullRequest;
183
+ applyStatus();
184
+ } else if (result.kind === "none") {
185
+ pullRequest = undefined;
186
+ applyStatus();
187
+ }
188
+ } catch {
189
+ // Cancellation and transport failures preserve the current branch's last known status.
190
+ } finally {
191
+ refreshInFlight = undefined;
192
+ refreshController = undefined;
193
+ const queued = refreshQueued;
194
+ refreshQueued = false;
195
+ if (queued && generation === refreshGeneration) scheduleRefresh(0);
196
+ }
197
+ })();
198
+ }
199
+
200
+ function applyStatus(): void {
201
+ if (!ctx?.hasUI) return;
202
+ ctx.ui.setStatus(
203
+ STATUS_KEY,
204
+ pullRequest ? formatPullRequestStatus(ctx, pullRequest) : undefined,
205
+ );
206
+ }
207
+ }
208
+
31
209
  function isBranchChangeCommand(command: string): boolean {
32
210
  return (
33
211
  GIT_BRANCH_CHANGE_COMMAND_PATTERN.test(command) ||
@@ -39,248 +217,136 @@ function isPullRequestStateCommand(command: string): boolean {
39
217
  return GH_PR_STATE_COMMAND_PATTERN.test(command);
40
218
  }
41
219
 
42
- function isNoPullRequestError(stderr: string): boolean {
43
- return stderr.toLowerCase().includes("no pull requests found for branch");
220
+ function formatPullRequestStatus(ctx: ExtensionContext, status: PullRequestStatus): string {
221
+ const suffix = status.state === "OPEN" ? "" : ` (${status.state.toLowerCase()})`;
222
+ return ctx.ui.theme.fg("dim", `#${status.number}${suffix}`);
44
223
  }
45
224
 
46
- async function loadCurrentBranch(pi: ExtensionAPI, cwd: string): Promise<string | undefined> {
47
- const result = await pi.exec("git", ["branch", "--show-current"], {
225
+ async function loadCurrentBranch(cwd: string, signal: AbortSignal): Promise<string | null> {
226
+ const result = await statusCommand(
227
+ "git",
228
+ ["branch", "--show-current"],
48
229
  cwd,
49
- timeout: BRANCH_CHECK_TIMEOUT_MS,
50
- });
51
- if (result.killed || result.code !== 0) return undefined;
52
-
53
- const branch = result.stdout.trim();
54
- return branch || undefined;
230
+ signal,
231
+ BRANCH_CHECK_TIMEOUT_MS,
232
+ );
233
+ if (result.code !== 0) throw new Error(result.stderr.trim() || "Could not read Git branch");
234
+ return result.stdout.trim() || null;
55
235
  }
56
236
 
57
237
  async function loadPullRequestStatus(
58
- pi: ExtensionAPI,
59
238
  cwd: string,
60
- signal?: AbortSignal,
239
+ signal: AbortSignal,
61
240
  ): Promise<PullRequestLookupResult> {
62
- const result = await pi.exec("gh", ["pr", "view", "--json", "number,url,state"], {
241
+ const result = await statusCommand(
242
+ "gh",
243
+ ["pr", "view", "--json", "number,url,state"],
63
244
  cwd,
64
245
  signal,
65
- timeout: PR_LOOKUP_TIMEOUT_MS,
66
- });
67
-
68
- if (result.killed) return { kind: "error" };
246
+ PR_LOOKUP_TIMEOUT_MS,
247
+ );
69
248
  if (result.code !== 0) {
70
- return isNoPullRequestError(result.stderr) ? { kind: "none" } : { kind: "error" };
249
+ return result.stderr.toLowerCase().includes("no pull requests found for branch")
250
+ ? { kind: "none" }
251
+ : { kind: "error" };
71
252
  }
72
253
 
73
- const output = result.stdout.trim();
74
- if (!output) return { kind: "error" };
75
-
76
254
  try {
77
- const parsed = JSON.parse(output) as {
255
+ const parsed = JSON.parse(result.stdout) as {
78
256
  number?: unknown;
79
257
  state?: unknown;
80
258
  url?: unknown;
81
259
  };
82
-
83
260
  if (parsed.state !== "OPEN" && parsed.state !== "CLOSED" && parsed.state !== "MERGED")
84
261
  return { kind: "error" };
85
262
  if (typeof parsed.number !== "number" || !Number.isInteger(parsed.number) || parsed.number <= 0)
86
263
  return { kind: "error" };
87
- if (typeof parsed.url !== "string") return { kind: "error" };
88
-
89
- const url = parsed.url.trim();
90
- if (!url) return { kind: "error" };
264
+ if (typeof parsed.url !== "string" || !parsed.url.trim()) return { kind: "error" };
91
265
 
92
266
  return {
93
267
  kind: "found",
94
- pullRequest: {
95
- number: parsed.number,
96
- url,
97
- state: parsed.state,
98
- },
268
+ pullRequest: { number: parsed.number, url: parsed.url.trim(), state: parsed.state },
99
269
  };
100
270
  } catch {
101
271
  return { kind: "error" };
102
272
  }
103
273
  }
104
274
 
105
- function formatPullRequestStatus(ctx: ExtensionContext, status: PullRequestStatus): string {
106
- const suffix = status.state === "OPEN" ? "" : ` (${status.state.toLowerCase()})`;
107
- return ctx.ui.theme.fg("dim", `#${status.number}${suffix}`);
108
- }
109
-
110
- export default function gitPrStatusExtension(pi: ExtensionAPI) {
111
- let ctx: ExtensionContext | undefined;
112
- let currentBranch: string | undefined;
113
- let pullRequest: PullRequestStatus | undefined;
114
- let generation = 0;
115
- let refreshTimer: NodeJS.Timeout | undefined;
116
- let branchCheckTimer: NodeJS.Timeout | undefined;
117
- let refreshInFlight: Promise<void> | null = null;
118
- let refreshQueued = false;
119
- let activeRefreshAbortController: AbortController | undefined;
120
-
121
- function reset(nextCtx?: ExtensionContext): void {
122
- ctx?.ui.setStatus(STATUS_KEY, undefined);
123
- ctx = nextCtx;
124
- currentBranch = undefined;
125
- pullRequest = undefined;
126
- generation += 1;
127
-
128
- if (refreshTimer) clearTimeout(refreshTimer);
129
- refreshTimer = undefined;
130
- if (branchCheckTimer) clearTimeout(branchCheckTimer);
131
- branchCheckTimer = undefined;
132
- activeRefreshAbortController?.abort();
133
- activeRefreshAbortController = undefined;
134
- refreshInFlight = null;
135
- refreshQueued = false;
136
-
137
- if (!nextCtx?.hasUI) return;
138
-
139
- primeCurrentBranch(nextCtx.cwd, generation);
140
- scheduleRefresh(0);
141
- }
142
-
143
- function applyStatus(): void {
144
- const activeCtx = ctx;
145
- if (!activeCtx?.hasUI) return;
146
-
147
- activeCtx.ui.setStatus(
148
- STATUS_KEY,
149
- pullRequest ? formatPullRequestStatus(activeCtx, pullRequest) : undefined,
150
- );
151
- }
152
-
153
- function scheduleRefresh(delay = REFRESH_DEBOUNCE_MS): void {
154
- const activeCtx = ctx;
155
- if (!activeCtx?.hasUI) return;
156
-
157
- if (refreshTimer) clearTimeout(refreshTimer);
158
-
159
- const refreshGeneration = generation;
160
- const cwd = activeCtx.cwd;
161
- refreshTimer = setTimeout(() => {
162
- refreshTimer = undefined;
163
- void refresh(cwd, refreshGeneration);
164
- }, delay);
165
- }
166
-
167
- async function primeCurrentBranch(cwd: string, branchGeneration: number): Promise<void> {
168
- const branch = await loadCurrentBranch(pi, cwd);
169
- if (generation !== branchGeneration) return;
170
- currentBranch = branch;
171
- }
172
-
173
- function scheduleBranchCheck(delay = 0): void {
174
- const activeCtx = ctx;
175
- if (!activeCtx?.hasUI) return;
176
-
177
- if (branchCheckTimer) clearTimeout(branchCheckTimer);
178
-
179
- const branchGeneration = generation;
180
- const cwd = activeCtx.cwd;
181
- branchCheckTimer = setTimeout(() => {
182
- branchCheckTimer = undefined;
183
- void checkForBranchChange(cwd, branchGeneration);
184
- }, delay);
185
- }
186
-
187
- async function checkForBranchChange(cwd: string, branchGeneration: number): Promise<void> {
188
- const nextBranch = await loadCurrentBranch(pi, cwd);
189
- if (generation !== branchGeneration) return;
190
- if (nextBranch === currentBranch) return;
191
-
192
- currentBranch = nextBranch;
193
- pullRequest = undefined;
194
- applyStatus();
195
- scheduleRefresh(0);
196
- }
197
-
198
- async function refresh(cwd: string, refreshGeneration: number): Promise<void> {
199
- if (refreshInFlight) {
200
- refreshQueued = true;
201
- return;
202
- }
203
-
204
- const abortController = new AbortController();
205
- activeRefreshAbortController = abortController;
206
-
207
- const promise = (async () => {
208
- const result = await loadPullRequestStatus(pi, cwd, abortController.signal);
209
- if (generation !== refreshGeneration) return;
210
-
211
- if (result.kind === "found") {
212
- pullRequest = result.pullRequest;
213
- applyStatus();
214
- return;
215
- }
216
-
217
- if (result.kind === "none") {
218
- pullRequest = undefined;
219
- applyStatus();
275
+ /** Own the command and its pipes through close, including cancellation and timeout escalation. */
276
+ async function statusCommand(
277
+ command: string,
278
+ args: string[],
279
+ cwd: string,
280
+ signal: AbortSignal,
281
+ timeout: number,
282
+ ): Promise<{ stdout: string; stderr: string; code: number }> {
283
+ signal.throwIfAborted();
284
+ return new Promise((resolve, reject) => {
285
+ const child = spawn(command, args, { cwd, detached: true, stdio: ["ignore", "pipe", "pipe"] });
286
+ let stdout = "";
287
+ let stderr = "";
288
+ let processError: Error | undefined;
289
+ let cancelled = false;
290
+ let escalated = false;
291
+ let killTimer: NodeJS.Timeout | undefined;
292
+ let pipeTimer: NodeJS.Timeout | undefined;
293
+ const releaseOrphanedPipes = () => {
294
+ if (!escalated || pipeTimer || (child.exitCode === null && child.signalCode === null)) return;
295
+ pipeTimer = setTimeout(() => {
296
+ child.stdout?.destroy();
297
+ child.stderr?.destroy();
298
+ }, PROCESS_PIPE_GRACE_MS);
299
+ };
300
+ const onAbort = () => {
301
+ if (cancelled) return;
302
+ cancelled = true;
303
+ killProcessGroup(child, "SIGTERM");
304
+ killTimer = setTimeout(() => {
305
+ killProcessGroup(child, "SIGKILL");
306
+ escalated = true;
307
+ releaseOrphanedPipes();
308
+ }, PROCESS_KILL_GRACE_MS);
309
+ };
310
+ const timeoutTimer = setTimeout(onAbort, timeout);
311
+ child.on("error", (error) => {
312
+ processError = error;
313
+ });
314
+ child.stdout?.setEncoding("utf8").on("data", (chunk: string) => {
315
+ stdout += chunk;
316
+ });
317
+ child.stderr?.setEncoding("utf8").on("data", (chunk: string) => {
318
+ stderr += chunk;
319
+ });
320
+ child.once("exit", releaseOrphanedPipes);
321
+ child.once("close", (code) => {
322
+ clearTimeout(timeoutTimer);
323
+ clearTimeout(killTimer);
324
+ clearTimeout(pipeTimer);
325
+ child.removeListener("exit", releaseOrphanedPipes);
326
+ signal.removeEventListener("abort", onAbort);
327
+ if (cancelled) {
328
+ killProcessGroup(child, "SIGKILL");
329
+ reject(signal.reason ?? new Error(`${command} timed out`));
330
+ } else if (processError) {
331
+ reject(processError);
332
+ } else {
333
+ resolve({ stdout, stderr, code: code ?? -1 });
220
334
  }
221
- })();
222
-
223
- refreshInFlight = promise;
335
+ });
336
+ if (signal.aborted) onAbort();
337
+ else signal.addEventListener("abort", onAbort, { once: true });
338
+ });
339
+ }
224
340
 
341
+ function killProcessGroup(child: ReturnType<typeof spawn>, signal: NodeJS.Signals): void {
342
+ if (!child.pid) return;
343
+ try {
344
+ process.kill(-child.pid, signal);
345
+ } catch {
225
346
  try {
226
- await promise;
347
+ child.kill(signal);
227
348
  } catch {
228
- if (generation !== refreshGeneration || abortController.signal.aborted) return;
229
- } finally {
230
- if (activeRefreshAbortController === abortController) {
231
- activeRefreshAbortController = undefined;
232
- }
233
- if (refreshInFlight === promise) {
234
- refreshInFlight = null;
235
- if (refreshQueued) {
236
- refreshQueued = false;
237
- scheduleRefresh(0);
238
- }
239
- }
349
+ // Process likely already exited.
240
350
  }
241
351
  }
242
-
243
- pi.on("session_start", async (_event, nextCtx) => {
244
- reset(nextCtx);
245
- });
246
-
247
- pi.on("user_bash", (event, nextCtx) => {
248
- ctx = nextCtx;
249
-
250
- if (isBranchChangeCommand(event.command)) {
251
- scheduleBranchCheck(USER_BASH_SETTLE_DELAY_MS);
252
- return undefined;
253
- }
254
-
255
- if (isPullRequestStateCommand(event.command)) {
256
- scheduleRefresh(USER_BASH_SETTLE_DELAY_MS);
257
- }
258
-
259
- return undefined;
260
- });
261
-
262
- pi.on("tool_result", async (event, nextCtx) => {
263
- ctx = nextCtx;
264
-
265
- if (!isBashToolResult(event)) return;
266
- if (typeof event.input.command !== "string") return;
267
-
268
- if (isBranchChangeCommand(event.input.command)) {
269
- scheduleBranchCheck(0);
270
- return;
271
- }
272
-
273
- if (isPullRequestStateCommand(event.input.command)) {
274
- scheduleRefresh(0);
275
- }
276
- });
277
-
278
- pi.on("turn_end", async (_event, nextCtx) => {
279
- ctx = nextCtx;
280
- scheduleBranchCheck(0);
281
- });
282
-
283
- pi.on("session_shutdown", async () => {
284
- reset(undefined);
285
- });
286
352
  }