tau-coding-agent 0.1.5 → 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 (86) hide show
  1. package/README.md +15 -12
  2. package/extensions/answer.ts +129 -73
  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 +90 -4
  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 +138 -183
  13. package/extensions/loop.ts +150 -135
  14. package/extensions/memory.ts +172 -97
  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 +43 -40
  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/submit-review-tool.ts +1 -5
  28. package/extensions/review/submit-triage-tool.ts +1 -0
  29. package/extensions/review/triage.ts +41 -44
  30. package/extensions/sandbox/bash.ts +775 -0
  31. package/extensions/sandbox/command.ts +605 -0
  32. package/extensions/sandbox/config.ts +764 -0
  33. package/extensions/sandbox/index.ts +138 -2850
  34. package/extensions/sandbox/macos-sandbox-shell.mjs +202 -0
  35. package/extensions/sandbox/permissions/dialog.ts +118 -0
  36. package/extensions/sandbox/permissions/filesystem.ts +559 -0
  37. package/extensions/sandbox/permissions/mach-lookup.ts +186 -0
  38. package/extensions/sandbox/permissions/network.ts +164 -0
  39. package/extensions/sandbox/permissions/unsandboxed.ts +270 -0
  40. package/extensions/sandbox/runtime.ts +616 -0
  41. package/extensions/stash.ts +28 -15
  42. package/extensions/subagent/README.md +73 -0
  43. package/extensions/subagent/index.ts +822 -0
  44. package/extensions/subagent/interrupt.ts +117 -0
  45. package/extensions/subagent/permissions.ts +101 -0
  46. package/extensions/subagent/rpc.ts +177 -0
  47. package/extensions/tool-display-mode.ts +267 -64
  48. package/extensions/usage/anthropic.ts +5 -6
  49. package/extensions/usage/github-copilot.ts +30 -9
  50. package/extensions/usage/index.ts +250 -357
  51. package/extensions/usage/openai-codex.ts +2 -2
  52. package/extensions/usage/openrouter.ts +10 -2
  53. package/extensions/websearch/README.md +12 -47
  54. package/extensions/websearch/config.ts +5 -2
  55. package/extensions/websearch/index.ts +94 -109
  56. package/extensions/websearch/output.ts +51 -0
  57. package/extensions/websearch/providers/anthropic.pi.ts +27 -44
  58. package/extensions/websearch/providers/gemini.browser.ts +68 -77
  59. package/extensions/websearch/providers/gemini.pi.ts +17 -17
  60. package/extensions/websearch/providers/openai-codex.pi.ts +145 -7
  61. package/extensions/websearch/providers/pi-model.shared.ts +44 -32
  62. package/extensions/websearch/providers/shared.ts +87 -2
  63. package/extensions/websearch/types.ts +0 -3
  64. package/extensions/worktree.ts +132 -172
  65. package/package.json +10 -5
  66. package/skills/browser-tools/SKILL.md +29 -234
  67. package/skills/browser-tools/references/cookies.md +36 -0
  68. package/skills/browser-tools/references/interaction.md +90 -0
  69. package/skills/browser-tools/references/logging.md +34 -0
  70. package/skills/git-clean-history/SKILL.md +6 -6
  71. package/skills/git-commit/SKILL.md +5 -3
  72. package/skills/github-pull-request/SKILL.md +60 -0
  73. package/skills/github-pull-request/references/create.md +40 -0
  74. package/skills/github-pull-request/references/stewardship.md +60 -0
  75. package/skills/oracle/SKILL.md +4 -4
  76. package/skills/oracle/scripts/oracle +64 -51
  77. package/skills/sentry/SKILL.md +15 -185
  78. package/skills/sentry/references/events.md +79 -0
  79. package/skills/sentry/references/issues.md +62 -0
  80. package/skills/sentry/references/logs.md +46 -0
  81. package/skills/update-changelog/SKILL.md +27 -121
  82. package/skills/web-design/SKILL.md +16 -105
  83. package/themes/tau-dark.json +4 -0
  84. package/extensions/openai-fast.ts +0 -229
  85. package/extensions/websearch/providers/openai-codex.browser.ts +0 -77
  86. package/extensions/websearch/providers/openai-codex.shared.ts +0 -123
@@ -35,11 +35,6 @@ const SUBMIT_REVIEW_PARAMS = Type.Object(
35
35
  ),
36
36
  { description: "Final review findings" },
37
37
  ),
38
- note: Type.Optional(
39
- Type.String({
40
- description: "Optional note when submitting zero findings or explaining uncertainty.",
41
- }),
42
- ),
43
38
  },
44
39
  { additionalProperties: false },
45
40
  );
@@ -51,6 +46,7 @@ const submitReviewTool = defineTool({
51
46
  promptSnippet: "Submit the final review findings",
52
47
  promptGuidelines: ["Use submit_review exactly once as your final action."],
53
48
  parameters: SUBMIT_REVIEW_PARAMS,
49
+ constrainedSampling: { type: "json_schema", strict: "prefer" },
54
50
 
55
51
  async execute(_toolCallId, params) {
56
52
  return {
@@ -44,6 +44,7 @@ const submitTriageTool = defineTool({
44
44
  promptSnippet: "Submit the final PR feedback triage",
45
45
  promptGuidelines: ["Use submit_triage exactly once as your final action."],
46
46
  parameters: SUBMIT_TRIAGE_PARAMS,
47
+ constrainedSampling: { type: "json_schema", strict: "prefer" },
47
48
 
48
49
  async execute(_toolCallId, params) {
49
50
  return {
@@ -1,3 +1,4 @@
1
+ import { setTimeout as delay } from "node:timers/promises";
1
2
  import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2
3
 
3
4
  import {
@@ -18,6 +19,8 @@ import {
18
19
  REVIEW_STARTUP_RETRY_DELAYS_MS,
19
20
  REVIEW_TASK_TIMEOUT_MS,
20
21
  runPiSubmitToolTask,
22
+ runReviewCommand,
23
+ type ReviewCommandContext,
21
24
  withJitter,
22
25
  } from "./runner.js";
23
26
  import {
@@ -39,9 +42,9 @@ import {
39
42
  import {
40
43
  notify,
41
44
  REVIEW_CANCELLED_ERROR,
42
- withManagedReviewRun,
45
+ joinAll,
43
46
  withSpinner,
44
- type ReviewExecutionControl,
47
+ type ReviewRuntime,
45
48
  } from "./runtime.js";
46
49
  import type { ReviewFingerprint } from "./schema.js";
47
50
  import { SUBMIT_TRIAGE_EXTENSION_PATH } from "./submit-triage-tool.js";
@@ -163,7 +166,7 @@ function getAuthorLogin(value: unknown): string {
163
166
  }
164
167
 
165
168
  async function fetchPrTriageMetadata(
166
- pi: ExtensionAPI,
169
+ workspace: ReviewCommandContext,
167
170
  prNumber: number,
168
171
  ): Promise<{
169
172
  prNumber: number;
@@ -176,7 +179,7 @@ async function fetchPrTriageMetadata(
176
179
  comments: TriageFeedbackItem[];
177
180
  reviews: TriageFeedbackItem[];
178
181
  } | null> {
179
- const { stdout, code } = await pi.exec("gh", [
182
+ const { stdout, code } = await runReviewCommand(workspace, "gh", [
180
183
  "api",
181
184
  "graphql",
182
185
  "-F",
@@ -281,11 +284,11 @@ function pickThreadAuthor(comments: TriageFeedbackComment[], prAuthor: string):
281
284
  }
282
285
 
283
286
  async function fetchPrReviewThreads(
284
- pi: ExtensionAPI,
287
+ workspace: ReviewCommandContext,
285
288
  prNumber: number,
286
289
  prAuthor: string,
287
290
  ): Promise<TriageFeedbackItem[] | null> {
288
- const { stdout, code } = await pi.exec("gh", [
291
+ const { stdout, code } = await runReviewCommand(workspace, "gh", [
289
292
  "api",
290
293
  "graphql",
291
294
  "--paginate",
@@ -427,14 +430,15 @@ function validateTriageOutput(parsed: unknown, feedbackItems: TriageFeedbackItem
427
430
  }
428
431
 
429
432
  async function runTriageTask(options: {
433
+ pi: ExtensionAPI;
430
434
  ctx: ExtensionCommandContext;
431
435
  cwd: string;
432
436
  prompt: string;
433
437
  model: ResolvedReviewModel;
434
438
  feedbackItems: TriageFeedbackItem[];
435
- control?: ReviewExecutionControl;
439
+ signal: AbortSignal;
436
440
  }): Promise<{ ok: true; items: TriageItem[] } | { ok: false; error: string }> {
437
- const { ctx, cwd, prompt, model, feedbackItems, control } = options;
441
+ const { pi, ctx, cwd, prompt, model, feedbackItems, signal } = options;
438
442
  const args = [
439
443
  "--mode",
440
444
  "json",
@@ -454,11 +458,10 @@ async function runTriageTask(options: {
454
458
  }
455
459
 
456
460
  for (let attempt = 0; ; attempt += 1) {
457
- if (control?.isCancelled()) {
458
- return { ok: false, error: REVIEW_CANCELLED_ERROR };
459
- }
461
+ signal.throwIfAborted();
460
462
 
461
463
  const taskResult = await withSpinner(
464
+ pi,
462
465
  ctx,
463
466
  () => `triaging PR feedback (${feedbackItems.length} items)`,
464
467
  () =>
@@ -467,7 +470,7 @@ async function runTriageTask(options: {
467
470
  prompt,
468
471
  cwd,
469
472
  timeoutMs: REVIEW_TASK_TIMEOUT_MS,
470
- control,
473
+ signal,
471
474
  submitTool: SUBMIT_TRIAGE_TOOL,
472
475
  }),
473
476
  );
@@ -501,7 +504,7 @@ async function runTriageTask(options: {
501
504
  const baseDelayMs =
502
505
  REVIEW_STARTUP_RETRY_DELAYS_MS[attempt] ??
503
506
  REVIEW_STARTUP_RETRY_DELAYS_MS[REVIEW_STARTUP_RETRY_DELAYS_MS.length - 1];
504
- await new Promise((resolve) => setTimeout(resolve, withJitter(baseDelayMs)));
507
+ await delay(withJitter(baseDelayMs), undefined, { signal });
505
508
  continue;
506
509
  }
507
510
  return { ok: false, error };
@@ -557,15 +560,16 @@ async function runTriageTask(options: {
557
560
  }
558
561
 
559
562
  async function prepareTriageContext(
560
- pi: ExtensionAPI,
561
563
  ctx: ExtensionCommandContext,
562
564
  prRef: string,
565
+ signal: AbortSignal,
563
566
  ): Promise<{ ok: false; error: string } | { ok: true; data: TriagePrContext }> {
564
- if (!(await isGitRepo(pi))) {
567
+ const workspace = { cwd: ctx.cwd, signal };
568
+ if (!(await isGitRepo(workspace))) {
565
569
  return { ok: false, error: "Not a git repository." };
566
570
  }
567
571
 
568
- const blockedError = await getPrCheckoutBlockedError(pi);
572
+ const blockedError = await getPrCheckoutBlockedError(workspace);
569
573
  if (blockedError) {
570
574
  return { ok: false, error: blockedError };
571
575
  }
@@ -576,7 +580,7 @@ async function prepareTriageContext(
576
580
  }
577
581
 
578
582
  notify(ctx, `Fetching PR #${prNumber} feedback...`, "info");
579
- const metadata = await fetchPrTriageMetadata(pi, prNumber);
583
+ const metadata = await fetchPrTriageMetadata(workspace, prNumber);
580
584
  if (!metadata) {
581
585
  return {
582
586
  ok: false,
@@ -584,13 +588,13 @@ async function prepareTriageContext(
584
588
  };
585
589
  }
586
590
 
587
- const [preparedPrScope, threads] = await Promise.all([
588
- preparePrCheckoutScope(pi, (message, type) => notify(ctx, message, type), {
591
+ const [preparedPrScope, threads] = await joinAll([
592
+ preparePrCheckoutScope(workspace, (message, type) => notify(ctx, message, type), {
589
593
  prNumber,
590
594
  baseBranch: metadata.baseBranch,
591
595
  headBranch: metadata.headBranch,
592
596
  }),
593
- fetchPrReviewThreads(pi, prNumber, metadata.author),
597
+ fetchPrReviewThreads(workspace, prNumber, metadata.author),
594
598
  ]);
595
599
  if (!preparedPrScope.ok) {
596
600
  return { ok: false, error: preparedPrScope.error };
@@ -607,7 +611,7 @@ async function prepareTriageContext(
607
611
  reviews: metadata.reviews,
608
612
  threads,
609
613
  });
610
- const baselineFingerprint = await computeCurrentFingerprint(pi, ctx.cwd, false);
614
+ const baselineFingerprint = await computeCurrentFingerprint(workspace, false);
611
615
 
612
616
  return {
613
617
  ok: true,
@@ -655,15 +659,15 @@ export async function runTriagePipeline(
655
659
  pi: ExtensionAPI,
656
660
  ctx: ExtensionCommandContext,
657
661
  prRef: string,
662
+ runtime: ReviewRuntime,
658
663
  ): Promise<TriageRunResult> {
659
664
  const startedAtMs = Date.now();
660
- const prepared = await prepareTriageContext(pi, ctx, prRef);
661
- if (!prepared.ok) {
662
- return { ok: false, error: prepared.error };
663
- }
664
-
665
- return withManagedReviewRun(pi, ctx, "triage", async (managed) => {
665
+ return runtime.run(ctx, "triage", async (signal): Promise<TriageRunResult> => {
666
+ const prepared = await prepareTriageContext(ctx, prRef, signal);
667
+ signal.throwIfAborted();
668
+ if (!prepared.ok) return prepared;
666
669
  const context = prepared.data;
670
+ const workspace = { cwd: ctx.cwd, signal };
667
671
  if (context.feedbackItems.length === 0) {
668
672
  const details = buildTriageMessageDetails(context, []);
669
673
  pi.sendMessage(
@@ -676,14 +680,14 @@ export async function runTriagePipeline(
676
680
  { deliverAs: "followUp" },
677
681
  );
678
682
  notify(ctx, "No PR feedback items found for triage.", "info");
679
- managed.markSuccessful();
680
683
  return { ok: true, details };
681
684
  }
682
685
 
683
- const [projectGuidelines, models] = await Promise.all([
684
- loadProjectReviewGuidelines(ctx.cwd),
685
- resolveModels(ctx, [], pi.getThinkingLevel()),
686
+ const [projectGuidelines, models] = await joinAll([
687
+ loadProjectReviewGuidelines(workspace),
688
+ resolveModels(ctx, [], pi.getThinkingLevel(), signal),
686
689
  ]);
690
+ signal.throwIfAborted();
687
691
  const model = models[0];
688
692
  notify(
689
693
  ctx,
@@ -692,25 +696,19 @@ export async function runTriagePipeline(
692
696
  );
693
697
 
694
698
  const triageResult = await runTriageTask({
699
+ pi,
695
700
  ctx,
696
701
  cwd: ctx.cwd,
697
702
  prompt: buildTriagePrompt(context, projectGuidelines),
698
703
  model,
699
704
  feedbackItems: context.feedbackItems,
700
- control: managed.control,
705
+ signal,
701
706
  });
702
- if (!triageResult.ok) {
703
- if (triageResult.error === REVIEW_CANCELLED_ERROR) {
704
- managed.markCancelled();
705
- }
706
- return triageResult;
707
- }
708
- if (managed.control.isCancelled()) {
709
- managed.markCancelled();
710
- return { ok: false, error: REVIEW_CANCELLED_ERROR };
711
- }
707
+ signal.throwIfAborted();
708
+ if (!triageResult.ok) return triageResult;
712
709
 
713
- const endingFingerprint = await computeCurrentFingerprint(pi, ctx.cwd, false);
710
+ const endingFingerprint = await computeCurrentFingerprint(workspace, false);
711
+ signal.throwIfAborted();
714
712
  if (!fingerprintsEqual(context.baselineFingerprint, endingFingerprint)) {
715
713
  return {
716
714
  ok: false,
@@ -740,7 +738,6 @@ export async function runTriagePipeline(
740
738
  { deliverAs: "followUp" },
741
739
  );
742
740
  notify(ctx, `Triage completed: ${items.length} feedback item(s).`, "info");
743
- managed.markSuccessful();
744
741
  return { ok: true, details };
745
742
  });
746
743
  }