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
@@ -5,16 +5,14 @@ import {
5
5
  type ExtensionAPI,
6
6
  type ExtensionContext,
7
7
  } from "@earendil-works/pi-coding-agent";
8
+ import type { KeyId } from "@earendil-works/pi-tui";
8
9
 
9
10
  const DEFAULT_SHORTCUTS = ["alt+x"] as const;
10
11
  const STATUS_KEY = "stash";
11
12
  const KEYBINDINGS_PATH = path.join(getAgentDir(), "keybindings.json");
12
13
 
13
- type ShortcutConfig = string | string[] | undefined;
14
-
15
14
  type KeybindingsConfig = {
16
- stash?: ShortcutConfig;
17
- [key: string]: unknown;
15
+ stash?: unknown;
18
16
  };
19
17
 
20
18
  function readKeybindings(filePath: string): KeybindingsConfig {
@@ -22,18 +20,19 @@ function readKeybindings(filePath: string): KeybindingsConfig {
22
20
  if (!fs.existsSync(filePath)) return {};
23
21
 
24
22
  const raw = fs.readFileSync(filePath, "utf8");
25
- const parsed = JSON.parse(raw);
26
- if (!parsed || typeof parsed !== "object") return {};
23
+ const parsed: unknown = JSON.parse(raw);
24
+ if (!parsed || typeof parsed !== "object" || !("stash" in parsed)) return {};
27
25
 
28
- return parsed as KeybindingsConfig;
26
+ return { stash: parsed.stash };
29
27
  } catch {
30
28
  return {};
31
29
  }
32
30
  }
33
31
 
34
- function normalizeShortcuts(value: ShortcutConfig): string[] {
32
+ function normalizeShortcuts(value: unknown): string[] {
35
33
  const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
36
34
  const normalized = values
35
+ .filter((shortcut: unknown): shortcut is string => typeof shortcut === "string")
37
36
  .map((shortcut) => shortcut.trim().toLowerCase())
38
37
  .filter((shortcut) => shortcut.length > 0);
39
38
 
@@ -68,12 +67,12 @@ export default function stashExtension(pi: ExtensionAPI): void {
68
67
  updateStatus(ctx);
69
68
  }
70
69
 
71
- function restoreDraft(ctx: ExtensionContext): void {
72
- if (!ctx.hasUI || !armed || stashedDraft === null) return;
70
+ function restoreDraft(ctx: ExtensionContext): boolean {
71
+ if (!ctx.hasUI || !armed || stashedDraft === null || ctx.ui.getEditorText()) return false;
73
72
 
74
- const draftToRestore = stashedDraft;
73
+ ctx.ui.setEditorText(stashedDraft);
75
74
  clearStash(ctx);
76
- ctx.ui.setEditorText(draftToRestore);
75
+ return true;
77
76
  }
78
77
 
79
78
  function stashOrRestore(ctx: ExtensionContext): void {
@@ -82,8 +81,14 @@ export default function stashExtension(pi: ExtensionAPI): void {
82
81
  const currentText = ctx.ui.getEditorText();
83
82
 
84
83
  if (armed && stashedDraft !== null) {
85
- restoreDraft(ctx);
86
- ctx.ui.notify("Stashed draft restored", "info");
84
+ if (restoreDraft(ctx)) {
85
+ ctx.ui.notify("Stashed draft restored", "info");
86
+ } else {
87
+ ctx.ui.notify(
88
+ "Send or clear the current editor text before restoring the stash",
89
+ "warning",
90
+ );
91
+ }
87
92
  return;
88
93
  }
89
94
 
@@ -103,7 +108,7 @@ export default function stashExtension(pi: ExtensionAPI): void {
103
108
  }
104
109
 
105
110
  for (const shortcut of shortcuts) {
106
- pi.registerShortcut(shortcut as never, {
111
+ pi.registerShortcut(shortcut as KeyId, {
107
112
  description: "Stash the current message draft, send one message, then restore it",
108
113
  handler: async (ctx) => {
109
114
  stashOrRestore(ctx);
@@ -136,6 +141,14 @@ export default function stashExtension(pi: ExtensionAPI): void {
136
141
  restoreDraft(ctx);
137
142
  });
138
143
 
144
+ pi.on("session_shutdown", (event, ctx) => {
145
+ if (event.reason !== "reload" || !ctx.hasUI || stashedDraft === null) return;
146
+
147
+ const text = [stashedDraft, ctx.ui.getEditorText()].filter(Boolean).join("\n\n");
148
+ ctx.ui.setEditorText(text);
149
+ clearStash(ctx);
150
+ });
151
+
139
152
  pi.on("session_start", async (event, ctx) => {
140
153
  if (event.reason === "new" || event.reason === "resume" || event.reason === "fork") {
141
154
  clearStash(ctx);
@@ -0,0 +1,73 @@
1
+ # Subagent
2
+
3
+ Use background Pi agents to investigate competing hypotheses, run independent experiments in parallel, or handle bounded subtasks whose detailed exploration would clutter the main context.
4
+
5
+ The main agent gets one `subagent` tool with four actions:
6
+
7
+ | Action | Arguments | Behavior |
8
+ | -------- | ---------------------------------------------- | -------------------------------------------------------------------------- |
9
+ | `start` | `goal`, `prompt`, optional `model`, `thinking` | Starts a child and returns its ID without waiting for the answer. |
10
+ | `status` | Optional `id` | Lists children, or shows one child's activity and latest response. |
11
+ | `steer` | `id`, `message` | Redirects a running child or continues its conversation after it finishes. |
12
+ | `stop` | `id` | Cancels the child and closes its process. |
13
+
14
+ Completed answers arrive automatically when the parent's current run settles. If that run is interrupted, completed answers are still added to its history without restarting the cancelled parent. The main agent can keep working instead of checking repeatedly.
15
+
16
+ ## Tasks, models, and thinking
17
+
18
+ `goal` is a short label of a few words. `prompt` contains the full task: relevant context, constraints, files to work on, and the result needed. Children start with fresh conversations, not copies of the parent's history.
19
+
20
+ Children **share the parent's checkout**. Separate processes do not isolate file changes. The tool guidance tells the parent to assign non-overlapping edits, including its own work, and preserve other agents' changes.
21
+
22
+ The model and thinking level default to the parent's settings when the child starts. `model` accepts an exact model ID or `provider/model` ID. `thinking` accepts the levels Pi supports, but an explicit override must also be supported by the selected model. The UI shows the actual model and thinking level. Later changes in the parent do not change existing children.
23
+
24
+ Match model capability to the task. Favor faster, less capable models for mechanical work and well-defined, bounded tasks. Favor more capable models for complex, ambiguous, or high-stakes work. Override the parent's model in either direction when there is a clear benefit. Choose model and thinking level independently. If no suitable alternative is known to be available, omit `model` to inherit.
25
+
26
+ The thinking-level guidance favors inheritance. Override the thinking level only when the task clearly warrants more or less reasoning. When choosing an override:
27
+
28
+ - **Low:** mechanical searches and extraction.
29
+ - **Medium:** bounded edits or tests with a well-defined approach.
30
+ - **High:** non-trivial implementation tasks, cross-cutting changes, and security or concurrency review with a reasonably understood problem and direction.
31
+ - **Extra-high:** difficult, open-ended reasoning that requires resolving substantial uncertainty or evaluating competing explanations and approaches. Ambiguous debugging and difficult investigations are examples.
32
+
33
+ These are recommendations, not fixed tiers. There is no automatic upgrade or downgrade, or separate model-selection call.
34
+
35
+ `steer` is cooperative: a running child receives the message after its current tool batch, before its next model call. It does not interrupt an in-flight shell command. An idle child starts another turn with its existing history.
36
+
37
+ ## Display and cancellation
38
+
39
+ A compact running count appears only while children are active. With Tool Display Mode and an embedding-capable editor, it joins the composer's working indicator, including when only children are running. Otherwise it appears above the composer. Pi's tool-expansion shortcut, normally **Ctrl+O**, reveals rows above the composer with each child's status, goal, model, and thinking level. There is no separate shortcut or transcript viewer.
40
+
41
+ Completed rows remain while you read the current response and disappear on your next request. Active children and pending approvals stay visible. Steering a hidden child shows it again; hiding rows does not stop processes or remove conversations from `status` and `steer`. Automatic completion reports and extension-injected prompts do not clear recent rows.
42
+
43
+ Completed answers reach the main agent and remain in session history, but their internal report messages are hidden from the chat. The main agent's response is the user-facing result.
44
+
45
+ Ghostty's title spinner stays active while children are working, even when the parent is idle. Subagent emits session-scoped `subagent:start` and `subagent:end` events with `{ sessionKey }` when the first child starts work and the last finishes. These aggregate events include startup, follow-ups, answer finalization, and cancellation cleanup, in all modes. Retained idle conversations do not count as active work. Approval dialogs keep the title's waiting-for-input marker.
46
+
47
+ While children or Review tasks are active, Escape asks **“Cancel all ongoing work?”**. **Enter** confirms cancellation of the parent and all active background work. Escape again or **No** dismisses the confirmation without stopping anything. Existing dialogs keep their own Escape handling, and idle children do not require confirmation. `stop` still cancels one child directly without affecting others.
48
+
49
+ Idle conversations remain available until stopped or the parent session closes. Reload, session replacement, and exit stop children and join their processes, pipes, pending startup, and approval requests. Navigating to another conversation branch also stops children so their answers cannot arrive on the wrong branch. Shutdown clears queued directions and requests a native abort before sending SIGTERM. Startup or unresponsive requests cannot block this indefinitely; SIGKILL is the final fallback.
50
+
51
+ Children are marked with `TAU_SUBAGENT_CHILD=1`. They do not get the delegation tool or Telegram integration themselves. The parent also sets `TAU_SUBAGENT_UNSANDBOXED_APPROVAL=1` to advertise support for forwarding sandbox approval requests. These are internal launch markers, not user configuration or permission grants.
52
+
53
+ ## Sandbox approvals
54
+
55
+ Children load normal Pi configuration, authentication, and extensions from the parent's agent directory and use the parent's project-trust decision. Pi's `--approve` flag trusts project-local resources; it does not approve sandbox permission requests. This is not a clone of arbitrary in-memory SDK configuration.
56
+
57
+ When Tau's Sandbox is loaded, it explicitly passes its **current session policy** to the child, including temporary changes. A blocked or uninitialized parent sandbox prevents starting a child. A child still needs its own working sandbox prerequisites. Permissions subsequently granted to one child do not automatically grant access to its siblings or parent.
58
+
59
+ Standard child selection, confirmation, and text-input requests appear in the parent UI, labelled with the child's ID and goal. A single queue handles these requests and the parent's Sandbox approvals. Cancelling a queued child removes its request without dismissing another child's dialog. No agent message counts as permission; only the user's actual response is returned to the child. Non-interactive sandbox policy remains non-interactive, and requests without a parent UI are denied.
60
+
61
+ Pi does not queue arbitrary extension dialogs. This integration waits for an already-open Pi prompt, but unrelated extensions can still open their own dialogs without joining the approval queue. RPC also cannot display custom TUI components; multiline editor requests are cancelled because Pi does not provide cancellable forwarding for them.
62
+
63
+ ## Lifetime and output
64
+
65
+ Child histories and full answers live in a private temporary directory. Answers injected into the parent's context are bounded; truncated answers include a path to the full text. Follow-ups do not overwrite earlier answer files. `status` provides a bounded snapshot rather than streaming entire transcripts into the parent.
66
+
67
+ Those temporary files are removed when the parent session closes or reloads. The answers already delivered to the parent remain in its normal history. There is no daemon, restart recovery, automatic worktree creation, task scheduler, or agent-profile configuration.
68
+
69
+ The extension requires the `pi` executable on PATH and supports macOS and Linux.
70
+
71
+ ## References
72
+
73
+ The design draws on Pi's [official subagent example](https://github.com/earendil-works/pi/tree/main/packages/coding-agent/examples/extensions/subagent), [mjakl/pi-subagent](https://github.com/mjakl/pi-subagent), and the delegation guidance in [Codex](https://developers.openai.com/codex/subagents), [Claude Code](https://code.claude.com/docs/en/sub-agents), and [Hermes](https://hermes-agent.nousresearch.com/docs/user-guide/features/delegation/). It uses Pi's native RPC protocol rather than adopting another orchestration framework.