tau-coding-agent 0.1.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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -0
  3. package/extensions/answer.ts +601 -0
  4. package/extensions/branch-term.ts +405 -0
  5. package/extensions/btw.ts +444 -0
  6. package/extensions/ghostty.ts +301 -0
  7. package/extensions/git-diff-stats.ts +277 -0
  8. package/extensions/git-pr-status.ts +286 -0
  9. package/extensions/insights.ts +2367 -0
  10. package/extensions/interlude.ts +144 -0
  11. package/extensions/loop.ts +529 -0
  12. package/extensions/memory.ts +1889 -0
  13. package/extensions/notify.ts +161 -0
  14. package/extensions/openai-fast.ts +227 -0
  15. package/extensions/openai-verbosity.ts +223 -0
  16. package/extensions/review.ts +4347 -0
  17. package/extensions/sandbox/index.ts +2578 -0
  18. package/extensions/usage/anthropic.ts +198 -0
  19. package/extensions/usage/github-copilot.ts +204 -0
  20. package/extensions/usage/google-gemini-cli.ts +232 -0
  21. package/extensions/usage/index.ts +2310 -0
  22. package/extensions/usage/minimax.ts +208 -0
  23. package/extensions/usage/openai-codex.ts +180 -0
  24. package/extensions/usage/openrouter.ts +168 -0
  25. package/extensions/usage/providers.ts +26 -0
  26. package/extensions/usage/shared.ts +156 -0
  27. package/extensions/usage/types.ts +53 -0
  28. package/extensions/usage/zai.ts +186 -0
  29. package/extensions/websearch/README.md +68 -0
  30. package/extensions/websearch/browser/chromium.ts +330 -0
  31. package/extensions/websearch/browser/discovery.ts +75 -0
  32. package/extensions/websearch/browser/firefox.ts +150 -0
  33. package/extensions/websearch/browser/sqlite.ts +38 -0
  34. package/extensions/websearch/config.ts +76 -0
  35. package/extensions/websearch/index.ts +312 -0
  36. package/extensions/websearch/normalize.ts +155 -0
  37. package/extensions/websearch/providers/anthropic.pi.ts +139 -0
  38. package/extensions/websearch/providers/gemini.browser.ts +185 -0
  39. package/extensions/websearch/providers/gemini.pi.ts +108 -0
  40. package/extensions/websearch/providers/openai-codex.browser.ts +77 -0
  41. package/extensions/websearch/providers/openai-codex.pi.ts +49 -0
  42. package/extensions/websearch/providers/openai-codex.shared.ts +123 -0
  43. package/extensions/websearch/providers/pi-model.shared.ts +86 -0
  44. package/extensions/websearch/providers/search-prompt.shared.ts +17 -0
  45. package/extensions/websearch/providers/shared.ts +76 -0
  46. package/extensions/websearch/types.ts +52 -0
  47. package/extensions/worktree.ts +2223 -0
  48. package/package.json +72 -0
  49. package/skills/browser-tools/SKILL.md +253 -0
  50. package/skills/browser-tools/scripts/browser-content.js +100 -0
  51. package/skills/browser-tools/scripts/browser-cookies.js +33 -0
  52. package/skills/browser-tools/scripts/browser-dismiss-cookies.js +455 -0
  53. package/skills/browser-tools/scripts/browser-eval.js +75 -0
  54. package/skills/browser-tools/scripts/browser-logs-tail.js +89 -0
  55. package/skills/browser-tools/scripts/browser-nav.js +28 -0
  56. package/skills/browser-tools/scripts/browser-net-summary.js +115 -0
  57. package/skills/browser-tools/scripts/browser-pick.js +133 -0
  58. package/skills/browser-tools/scripts/browser-screenshot.js +18 -0
  59. package/skills/browser-tools/scripts/browser-start.js +281 -0
  60. package/skills/browser-tools/scripts/browser-watch.js +321 -0
  61. package/skills/browser-tools/scripts/utils.js +60 -0
  62. package/skills/git-clean-history/SKILL.md +45 -0
  63. package/skills/git-commit/SKILL.md +55 -0
  64. package/skills/oracle/SKILL.md +64 -0
  65. package/skills/oracle/scripts/oracle +249 -0
  66. package/skills/oracle/scripts/oracle-bundle +227 -0
  67. package/skills/sentry/SKILL.md +198 -0
  68. package/skills/sentry/lib/auth.js +99 -0
  69. package/skills/sentry/scripts/fetch-event.js +325 -0
  70. package/skills/sentry/scripts/fetch-issue.js +368 -0
  71. package/skills/sentry/scripts/list-issues.js +245 -0
  72. package/skills/sentry/scripts/search-events.js +291 -0
  73. package/skills/sentry/scripts/search-logs.js +234 -0
  74. package/skills/update-changelog/SKILL.md +135 -0
  75. package/skills/web-design/SKILL.md +117 -0
@@ -0,0 +1,321 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Background logging for browser-tools.
5
+ *
6
+ * Logs console output, page errors, and network activity to JSONL.
7
+ *
8
+ * Default log root:
9
+ * $TMPDIR/agent-browser-tools/logs
10
+ *
11
+ * Override with:
12
+ * BROWSER_TOOLS_LOG_ROOT=/some/dir
13
+ */
14
+
15
+ import {
16
+ createWriteStream,
17
+ existsSync,
18
+ mkdirSync,
19
+ readFileSync,
20
+ writeFileSync,
21
+ unlinkSync,
22
+ } from "node:fs";
23
+ import { tmpdir } from "node:os";
24
+ import { join } from "node:path";
25
+ import puppeteer from "puppeteer-core";
26
+
27
+ const DEFAULT_TMP = process.platform === "win32" ? tmpdir() : "/tmp";
28
+
29
+ const LOG_ROOT =
30
+ process.env.BROWSER_TOOLS_LOG_ROOT || join(DEFAULT_TMP, "agent-browser-tools", "logs");
31
+
32
+ const PID_FILE = join(LOG_ROOT, ".watch.pid");
33
+
34
+ function ensureDir(dir) {
35
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
36
+ }
37
+
38
+ function isProcessAlive(pid) {
39
+ try {
40
+ process.kill(pid, 0);
41
+ return true;
42
+ } catch {
43
+ return false;
44
+ }
45
+ }
46
+
47
+ function getDateDir() {
48
+ const now = new Date();
49
+ const yyyy = String(now.getFullYear());
50
+ const mm = String(now.getMonth() + 1).padStart(2, "0");
51
+ const dd = String(now.getDate()).padStart(2, "0");
52
+ return join(LOG_ROOT, `${yyyy}-${mm}-${dd}`);
53
+ }
54
+
55
+ function safeFileName(value) {
56
+ return String(value).replace(/[^a-zA-Z0-9._-]/g, "_");
57
+ }
58
+
59
+ function nowIso() {
60
+ return new Date().toISOString();
61
+ }
62
+
63
+ async function connectBrowser(timeout = 5000) {
64
+ return Promise.race([
65
+ puppeteer.connect({ browserURL: "http://127.0.0.1:9222", defaultViewport: null }),
66
+ new Promise((_, reject) => {
67
+ setTimeout(() => reject(new Error("timeout")), timeout).unref();
68
+ }),
69
+ ]);
70
+ }
71
+
72
+ ensureDir(LOG_ROOT);
73
+
74
+ // Prevent duplicate watchers.
75
+ if (existsSync(PID_FILE)) {
76
+ try {
77
+ const existing = Number(readFileSync(PID_FILE, "utf8").trim());
78
+ if (existing && isProcessAlive(existing)) {
79
+ console.log("✓ watch already running");
80
+ process.exit(0);
81
+ }
82
+ } catch {
83
+ // ignore stale pid
84
+ }
85
+ }
86
+
87
+ writeFileSync(PID_FILE, String(process.pid));
88
+
89
+ const dateDir = getDateDir();
90
+ ensureDir(dateDir);
91
+
92
+ // targetId -> { stream, filepath }
93
+ const targetState = new Map();
94
+ // request object -> requestId
95
+ const requestIds = new WeakMap();
96
+ // page -> targetId
97
+ const pageToTarget = new WeakMap();
98
+ const instrumentedPages = new WeakSet();
99
+ let nextSyntheticPageId = 1;
100
+
101
+ function getOrCreateTargetState(targetId) {
102
+ let state = targetState.get(targetId);
103
+ if (state) return state;
104
+
105
+ const filename = `${safeFileName(targetId)}.jsonl`;
106
+ const filepath = join(dateDir, filename);
107
+ const stream = createWriteStream(filepath, { flags: "a" });
108
+
109
+ state = { stream, filepath };
110
+ targetState.set(targetId, state);
111
+ return state;
112
+ }
113
+
114
+ function writeLog(targetId, payload) {
115
+ const state = getOrCreateTargetState(targetId);
116
+ const record = {
117
+ ts: nowIso(),
118
+ targetId,
119
+ ...payload,
120
+ };
121
+ state.stream.write(`${JSON.stringify(record)}\n`);
122
+ }
123
+
124
+ function internalTargetId(page) {
125
+ try {
126
+ const t = page.target?.();
127
+ return t?._targetId || t?._targetInfo?.targetId || t?._id || null;
128
+ } catch {
129
+ return null;
130
+ }
131
+ }
132
+
133
+ function ensureTargetId(page) {
134
+ const existing = pageToTarget.get(page);
135
+ if (existing) return existing;
136
+
137
+ const internal = internalTargetId(page);
138
+ const chosen = internal ? String(internal) : `page-${nextSyntheticPageId++}`;
139
+ pageToTarget.set(page, chosen);
140
+ return chosen;
141
+ }
142
+
143
+ function ensureRequestId(targetId, request) {
144
+ if (requestIds.has(request)) return requestIds.get(request);
145
+ const id = `${targetId}-${Math.random().toString(16).slice(2)}-${Date.now()}`;
146
+ requestIds.set(request, id);
147
+ return id;
148
+ }
149
+
150
+ function closeTarget(targetId) {
151
+ const state = targetState.get(targetId);
152
+ if (!state) return;
153
+ try {
154
+ state.stream.end();
155
+ } catch {
156
+ // ignore
157
+ }
158
+ targetState.delete(targetId);
159
+ }
160
+
161
+ async function instrumentPage(page) {
162
+ if (instrumentedPages.has(page)) return;
163
+ instrumentedPages.add(page);
164
+
165
+ const targetId = ensureTargetId(page);
166
+
167
+ // Do not double-instrument.
168
+ if (targetState.has(targetId)) {
169
+ // Note: this assumes a 1:1 mapping between targetId and page.
170
+ // That’s fine for our usage.
171
+ }
172
+
173
+ writeLog(targetId, {
174
+ type: "target.attached",
175
+ url: page.url?.() || null,
176
+ });
177
+
178
+ page.on("console", (msg) => {
179
+ try {
180
+ writeLog(targetId, {
181
+ type: "console",
182
+ level: msg.type?.() || null,
183
+ text: msg.text?.() || null,
184
+ args: (msg.args?.() || []).map((a) => a.toString()),
185
+ });
186
+ } catch {
187
+ // ignore
188
+ }
189
+ });
190
+
191
+ page.on("pageerror", (err) => {
192
+ writeLog(targetId, {
193
+ type: "exception",
194
+ text: err?.message || String(err),
195
+ stack: err?.stack || null,
196
+ });
197
+ });
198
+
199
+ page.on("error", (err) => {
200
+ writeLog(targetId, {
201
+ type: "page.crash",
202
+ text: err?.message || String(err),
203
+ stack: err?.stack || null,
204
+ });
205
+ });
206
+
207
+ page.on("request", (req) => {
208
+ const requestId = ensureRequestId(targetId, req);
209
+ writeLog(targetId, {
210
+ type: "network.request",
211
+ requestId,
212
+ method: req.method?.() || null,
213
+ url: req.url?.() || null,
214
+ resourceType: req.resourceType?.() || null,
215
+ isNavigationRequest: !!req.isNavigationRequest?.(),
216
+ hasPostData: !!req.postData?.(),
217
+ });
218
+ });
219
+
220
+ page.on("response", (res) => {
221
+ try {
222
+ const req = res.request();
223
+ const requestId = ensureRequestId(targetId, req);
224
+ const headers = res.headers?.() || {};
225
+ const contentType = headers["content-type"] || headers["Content-Type"] || null;
226
+
227
+ writeLog(targetId, {
228
+ type: "network.response",
229
+ requestId,
230
+ url: res.url?.() || null,
231
+ status: res.status?.(),
232
+ statusText: res.statusText?.() || null,
233
+ mimeType: contentType,
234
+ fromDiskCache: !!res.fromCache?.(),
235
+ fromServiceWorker: !!res.fromServiceWorker?.(),
236
+ });
237
+ } catch (e) {
238
+ writeLog(targetId, {
239
+ type: "network.response",
240
+ requestId: null,
241
+ url: null,
242
+ status: null,
243
+ statusText: null,
244
+ mimeType: null,
245
+ error: e?.message || String(e),
246
+ });
247
+ }
248
+ });
249
+
250
+ page.on("requestfailed", (req) => {
251
+ const requestId = ensureRequestId(targetId, req);
252
+ writeLog(targetId, {
253
+ type: "network.failure",
254
+ requestId,
255
+ url: req.url?.() || null,
256
+ errorText: req.failure?.()?.errorText || null,
257
+ canceled: req.failure?.()?.errorText === "net::ERR_ABORTED",
258
+ });
259
+ });
260
+
261
+ page.on("framenavigated", (frame) => {
262
+ if (frame === page.mainFrame()) {
263
+ writeLog(targetId, {
264
+ type: "page.navigated",
265
+ url: frame.url?.() || null,
266
+ });
267
+ }
268
+ });
269
+
270
+ page.on("close", () => {
271
+ writeLog(targetId, { type: "target.closed" });
272
+ closeTarget(targetId);
273
+ });
274
+ }
275
+
276
+ function cleanupAndExit(code = 0) {
277
+ try {
278
+ for (const targetId of targetState.keys()) {
279
+ closeTarget(targetId);
280
+ }
281
+ try {
282
+ unlinkSync(PID_FILE);
283
+ } catch {
284
+ // ignore
285
+ }
286
+ } finally {
287
+ process.exit(code);
288
+ }
289
+ }
290
+
291
+ process.on("SIGINT", () => cleanupAndExit(0));
292
+ process.on("SIGTERM", () => cleanupAndExit(0));
293
+
294
+ async function main() {
295
+ const browser = await connectBrowser(5000).catch((e) => {
296
+ console.error("✗ Could not connect to browser:", e.message);
297
+ console.error(" Run: browser-start.js");
298
+ process.exit(1);
299
+ });
300
+
301
+ // Existing pages.
302
+ const pages = await browser.pages();
303
+ for (const page of pages) {
304
+ await instrumentPage(page);
305
+ }
306
+
307
+ // New pages.
308
+ browser.on("targetcreated", async (target) => {
309
+ try {
310
+ if (target.type?.() !== "page") return;
311
+ const page = await target.page();
312
+ if (page) await instrumentPage(page);
313
+ } catch {
314
+ // ignore
315
+ }
316
+ });
317
+
318
+ console.log(`✓ watch started (logs: ${dateDir})`);
319
+ }
320
+
321
+ await main();
@@ -0,0 +1,60 @@
1
+ import puppeteer from "puppeteer-core";
2
+
3
+ /**
4
+ * Connect to browser with timeout, exit on failure.
5
+ * @param {number} timeout - Connection timeout in ms (default: 5000)
6
+ * @returns {Promise<Browser>}
7
+ */
8
+ export async function connectBrowser(timeout = 5000) {
9
+ const browser = await Promise.race([
10
+ puppeteer.connect({
11
+ browserURL: "http://127.0.0.1:9222",
12
+ defaultViewport: null,
13
+ }),
14
+ new Promise((_, reject) => {
15
+ setTimeout(() => reject(new Error("timeout")), timeout).unref();
16
+ }),
17
+ ]).catch((e) => {
18
+ console.error("✗ Could not connect to browser:", e.message);
19
+ console.error(" Run: browser-start.js");
20
+ process.exit(1);
21
+ });
22
+ return browser;
23
+ }
24
+
25
+ /**
26
+ * Get the active/focused page. Fall back to the last http/https page, then the last page.
27
+ * Exit if none found.
28
+ * @param {Browser} browser
29
+ * @returns {Promise<Page>}
30
+ */
31
+ export async function getActivePage(browser) {
32
+ const pages = await browser.pages();
33
+ if (pages.length === 0) {
34
+ console.error("✗ No active tab found");
35
+ process.exit(1);
36
+ }
37
+
38
+ for (const page of [...pages].reverse()) {
39
+ try {
40
+ const isActive = await page.evaluate(
41
+ () => document.visibilityState === "visible" && document.hasFocus(),
42
+ );
43
+ if (isActive) return page;
44
+ } catch {}
45
+ }
46
+
47
+ return pages.filter((page) => page.url().startsWith("http")).at(-1) || pages.at(-1);
48
+ }
49
+
50
+ /**
51
+ * Print result, using JSON for objects/arrays.
52
+ * @param {any} result
53
+ */
54
+ export function printResult(result) {
55
+ if (typeof result === "object" && result !== null) {
56
+ console.log(JSON.stringify(result, null, 2));
57
+ } else {
58
+ console.log(result);
59
+ }
60
+ }
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: git-clean-history
3
+ description: "Reimplement the current Git branch on a fresh branch off `main` with a clean, narrative-quality commit history."
4
+ ---
5
+
6
+ # Git Rebase
7
+
8
+ Use this skill to reimplement the current branch on a new branch with a clean, narrative-quality git commit history suitable for reviewer comprehension.
9
+
10
+ ## Steps
11
+
12
+ 1. **Validate the source branch**
13
+ - Ensure no uncommitted changes or merge conflicts
14
+ - Confirm it is up to date with `main`
15
+
16
+ 2. **Analyze the diff**
17
+ - Study all changes between source branch and `main`
18
+ - Form a clear understanding of the final intended state
19
+
20
+ 3. **Create the clean branch**
21
+ - Create a new branch off of `main` using the new branch name
22
+ - Use the `{source_branch}-clean` name unless another name is provided by the user
23
+
24
+ 4. **Plan the commit storyline**
25
+ - Break the implementation into self-contained logical steps
26
+ - Each step should reflect a stage of development, as if writing a tutorial
27
+
28
+ 5. **Reimplement the work**
29
+ - Recreate changes in the clean branch, committing step by step
30
+ - Each commit must:
31
+ - Introduce a single coherent idea
32
+ - Include a clear commit message and description
33
+ - Follow best practices for the message as outlined by the `git-commit` skill
34
+ - **Use `git commit --no-verify` for all intermediate commits**
35
+ - Pre-commit hooks check tests, types, and imports that may not pass until the full implementation is complete; do not waste time fixing issues in intermediate commits that will be resolved by later commits
36
+
37
+ 6. **Verify correctness**
38
+ - Confirm the final state exactly matches the source branch
39
+ - Run the final commit **without** `--no-verify` to ensure all checks pass
40
+
41
+ ### Rules
42
+
43
+ - Do not add yourself as an author or contributor
44
+ - Do not include "Generated with ...", "Co-Authored-By: ...", or any AI attribution
45
+ - The end state of the clean branch must be identical to the source branch
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: git-commit
3
+ description: "Commit changes with an emphasis on tidiness and reviewability: small, focused commits, with clear rationale in messages."
4
+ ---
5
+
6
+ # Git Commit
7
+
8
+ Use this skill as the playbook for producing reviewable commits and a clean, consistent commit history.
9
+
10
+ ## Phase 1: Review changes
11
+
12
+ 1. Check what files have changed
13
+ 2. If there are no changes to commit, inform the user and stop
14
+ 3. If there are unstaged changes, stage them with `git add`
15
+
16
+ Rules:
17
+
18
+ - Keep commits small, focused, and atomic
19
+ - Avoid commits that mix unrelated changes (e.g., "Address feedback")
20
+ - Never stage or commit files that look like secrets (e.g. `.env`, credentials, API keys)
21
+
22
+ ## Phase 2: Generate commit message
23
+
24
+ 1. Remember commit messages best practices
25
+ 2. Write a short subject line:
26
+ - Up to 50 chars
27
+ - Start subject with a capital letter, don’t end with a period
28
+ - Use imperative mood (e.g. "Fix memory leak while scrolling widget")
29
+ 3. Leave a blank line between subject and body
30
+ 4. Only when needed (don’t force it for trivial changes), write a body:
31
+ - Wrap lines at 72 chars
32
+ - Focus on "what" and "why", not the "how"
33
+ - Explain the motivation
34
+ - Mention side effects, trade-offs, or alternatives considered
35
+ - Reference relevant issue IDs / tickets at the bottom if known
36
+ 5. Check style of recent commits in the repo (tone, structure, capitalization, length, etc): `git log -10 --oneline`
37
+ 6. Generate a message that adheres to the style in the repo while following best practices
38
+
39
+ Rules:
40
+
41
+ - Never stage files that look like secrets (e.g. `.env`, credentials, API keys)
42
+ - Do not add yourself as an author or contributor
43
+ - Do not include "Generated with ...", "Co-Authored-By: ...", or any AI attribution
44
+
45
+ ## Phase 3: Commit
46
+
47
+ 1. Commit staged changes with `git commit`
48
+
49
+ Rules:
50
+
51
+ - Avoid `git commit --amend` unless explicitly requested
52
+ - Do not bypass hooks with `git commit --no-verify`
53
+ - If `git commit` fails due to hooks, fix issues and retry
54
+ - Use `git commit -F` (or heredoc) when there is a body, never newline escapes in `-m`.
55
+ - Never push to remotes unless explicitly requested
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: oracle
3
+ description: Get a second opinion by bundling a prompt + a curated file set, then asking a strong model from a different family through a separate Pi invocation.
4
+ ---
5
+
6
+ # Oracle
7
+
8
+ Use this skill when you want a second opinion from a capable model in a different family than the current session.
9
+
10
+ The oracle workflow bundles selected files into a standalone prompt, selects an alternate model with `pi --list-models`, and sends the bundle to `pi -p --no-tools`. It is read-only by default because the oracle receives the selected context directly and does not get tools.
11
+
12
+ ## Model selection
13
+
14
+ The `./scripts/oracle` wrapper chooses the strongest known model it can find in the first usable family from this order:
15
+
16
+ | Current family | Oracle preference |
17
+ | ------------------ | ----------------------------------------------------- |
18
+ | OpenAI / Codex | Claude / Anthropic → Gemini / Google → OpenAI / Codex |
19
+ | Claude / Anthropic | OpenAI / Codex → Gemini / Google → Claude / Anthropic |
20
+ | Gemini / Google | OpenAI / Codex → Claude / Anthropic → Gemini / Google |
21
+ | Unknown | Claude / Anthropic → OpenAI / Codex → Gemini / Google |
22
+
23
+ It prefers models listed in Pi's `enabledModels`, then falls back to the best matching model shown by `pi --list-models`. Override only when needed with `--model provider/model`.
24
+
25
+ If the current model is not obvious to the script, pass it explicitly with `--current provider/model`. Otherwise it falls back to Pi's default model from `settings.json`.
26
+
27
+ ## Workflow
28
+
29
+ 1. Pick the smallest file set that contains the truth. Avoid secrets by default.
30
+ 2. Preview selected files before sending.
31
+ 3. Ask the oracle with a standalone prompt that states the task, constraints, and desired output format.
32
+ 4. Treat the result as advice, not authority. Reason from first principles before applying recommendations.
33
+
34
+ ## Commands
35
+
36
+ Run commands from this skill directory:
37
+
38
+ ```bash
39
+ # Preview selected files
40
+ ./scripts/oracle-bundle --dry-run -p "<task>" --file "src/**" --file "!**/*.test.*"
41
+
42
+ # Preview the full bundle
43
+ ./scripts/oracle-bundle -p "<task>" --file "src/**" --file "!**/*.test.*"
44
+
45
+ # Show which oracle model would be selected
46
+ ./scripts/oracle --list-models --current openai-codex/gpt-5.5
47
+
48
+ # Ask the automatically selected oracle model
49
+ ./scripts/oracle --current openai-codex/gpt-5.5 \
50
+ -p "<task>" --file "src/**" --file "!**/*.test.*"
51
+
52
+ # Override the oracle model when the automatic choice is wrong
53
+ ./scripts/oracle --model github-copilot/claude-opus-4.7 \
54
+ -p "<task>" --file "src/**" --file "!**/*.test.*"
55
+ ```
56
+
57
+ ## Tips
58
+
59
+ - Prefer a minimal file set over the whole repo.
60
+ - If you need diffs reviewed, paste the diff into the prompt or attach the diff file via `--file`.
61
+ - Make the prompt completely standalone: include intent, goals, constraints, error text, and whether you want a plan, review, pros/cons, or patch guidance.
62
+ - Never include secrets (`.env`, tokens, key files).
63
+ - For general code review, ask for feedback on correctness, consistency with existing code, simplicity, maintainability, performance, security, and edge cases.
64
+ - Oracle can be slow while it reasons. Allow it several minutes to process.