tau-coding-agent 0.1.2 → 0.1.4
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.
- package/README.md +22 -21
- package/extensions/answer.ts +4 -4
- package/extensions/branch-term.ts +3 -3
- package/extensions/btw.ts +4 -4
- package/extensions/ghostty.ts +1 -1
- package/extensions/git-diff-stats.ts +1 -1
- package/extensions/git-pr-status.ts +1 -1
- package/extensions/insights.ts +3 -3
- package/extensions/interlude.ts +1 -1
- package/extensions/loop.ts +3 -3
- package/extensions/memory.ts +8 -2
- package/extensions/notify.ts +1 -1
- package/extensions/openai-fast.ts +1 -1
- package/extensions/openai-verbosity.ts +1 -1
- package/extensions/review.ts +111 -38
- package/extensions/sandbox/index.ts +84 -8
- package/extensions/tool-display-mode.ts +544 -0
- package/extensions/usage/anthropic.ts +1 -1
- package/extensions/usage/github-copilot.ts +1 -1
- package/extensions/usage/index.ts +3 -3
- package/extensions/usage/minimax.ts +1 -1
- package/extensions/usage/openai-codex.ts +1 -1
- package/extensions/usage/openrouter.ts +1 -1
- package/extensions/usage/providers.ts +0 -2
- package/extensions/usage/types.ts +1 -2
- package/extensions/usage/zai.ts +1 -1
- package/extensions/websearch/index.ts +3 -3
- package/extensions/websearch/providers/anthropic.pi.ts +1 -1
- package/extensions/websearch/providers/gemini.pi.ts +1 -1
- package/extensions/websearch/providers/openai-codex.pi.ts +1 -1
- package/extensions/websearch/providers/pi-model.shared.ts +2 -2
- package/extensions/worktree.ts +3 -3
- package/package.json +20 -7
- package/skills/oracle/scripts/oracle +0 -1
- package/extensions/usage/google-gemini-cli.ts +0 -232
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ExtensionAPI, ExtensionContext } from "@
|
|
2
|
-
import { BorderedLoader } from "@
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { BorderedLoader } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import {
|
|
4
4
|
Key,
|
|
5
5
|
matchesKey,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type TUI,
|
|
8
8
|
truncateToWidth,
|
|
9
9
|
visibleWidth,
|
|
10
|
-
} from "@
|
|
10
|
+
} from "@earendil-works/pi-tui";
|
|
11
11
|
import { createReadStream, type Dirent } from "node:fs";
|
|
12
12
|
import fs from "node:fs/promises";
|
|
13
13
|
import os from "node:os";
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import anthropicProvider from "./anthropic.js";
|
|
2
2
|
import githubCopilotProvider from "./github-copilot.js";
|
|
3
|
-
import googleGeminiCliProvider from "./google-gemini-cli.js";
|
|
4
3
|
import minimaxProvider from "./minimax.js";
|
|
5
4
|
import openAICodexProvider from "./openai-codex.js";
|
|
6
5
|
import openRouterProvider from "./openrouter.js";
|
|
@@ -11,7 +10,6 @@ export const SUPPORTED_PROVIDERS: readonly UsageProviderDefinition[] = [
|
|
|
11
10
|
openAICodexProvider,
|
|
12
11
|
anthropicProvider,
|
|
13
12
|
githubCopilotProvider,
|
|
14
|
-
googleGeminiCliProvider,
|
|
15
13
|
openRouterProvider,
|
|
16
14
|
zaiProvider,
|
|
17
15
|
minimaxProvider,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExtensionContext } from "@
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
|
|
3
3
|
export interface RGB {
|
|
4
4
|
r: number;
|
|
@@ -10,7 +10,6 @@ export type SupportedProviderId =
|
|
|
10
10
|
| "openai-codex"
|
|
11
11
|
| "anthropic"
|
|
12
12
|
| "github-copilot"
|
|
13
|
-
| "google-gemini-cli"
|
|
14
13
|
| "openrouter"
|
|
15
14
|
| "zai"
|
|
16
15
|
| "minimax";
|
package/extensions/usage/zai.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type { Api, Model } from "@
|
|
1
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
2
2
|
import {
|
|
3
3
|
defineTool,
|
|
4
4
|
getMarkdownTheme,
|
|
5
5
|
keyHint,
|
|
6
6
|
type ExtensionAPI,
|
|
7
7
|
type ExtensionContext,
|
|
8
|
-
} from "@
|
|
9
|
-
import { Container, Markdown, Spacer, Text } from "@
|
|
8
|
+
} from "@earendil-works/pi-coding-agent";
|
|
9
|
+
import { Container, Markdown, Spacer, Text } from "@earendil-works/pi-tui";
|
|
10
10
|
import { Type } from "typebox";
|
|
11
11
|
|
|
12
12
|
import { createBrowserSession, discoverProfiles } from "./browser/discovery.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Api, Model } from "@
|
|
1
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
2
2
|
|
|
3
3
|
import { dedupeSources, extractMarkdownSources, normalizeSourceTitle } from "../normalize.js";
|
|
4
4
|
import type { WebsearchResult, WebsearchSource } from "../types.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Api, Model } from "@
|
|
2
|
-
import type { ExtensionContext } from "@
|
|
1
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
|
|
4
4
|
export interface PiModelSelection {
|
|
5
5
|
model: Model<Api>;
|
package/extensions/worktree.ts
CHANGED
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
* Lists worktrees and their status.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import type { ExtensionAPI, ExtensionCommandContext, Theme } from "@
|
|
21
|
+
import type { ExtensionAPI, ExtensionCommandContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
22
22
|
import {
|
|
23
23
|
CURRENT_SESSION_VERSION,
|
|
24
24
|
DynamicBorder,
|
|
25
25
|
SessionManager,
|
|
26
26
|
highlightCode,
|
|
27
27
|
type SessionHeader,
|
|
28
|
-
} from "@
|
|
28
|
+
} from "@earendil-works/pi-coding-agent";
|
|
29
29
|
import {
|
|
30
30
|
Box,
|
|
31
31
|
Container,
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
SelectList,
|
|
34
34
|
Text,
|
|
35
35
|
matchesKey,
|
|
36
|
-
} from "@
|
|
36
|
+
} from "@earendil-works/pi-tui";
|
|
37
37
|
import { spawnSync } from "node:child_process";
|
|
38
38
|
import { randomUUID } from "node:crypto";
|
|
39
39
|
import * as fs from "node:fs";
|
package/package.json
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tau-coding-agent",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "Opinionated Pi package with web search, review workflows, sandboxing, memory, usage reporting, skills, and a theme",
|
|
5
5
|
"keywords": [
|
|
6
|
+
"agent-tools",
|
|
7
|
+
"code-review",
|
|
8
|
+
"coding-agent",
|
|
9
|
+
"memory",
|
|
10
|
+
"pi",
|
|
6
11
|
"pi-extension",
|
|
7
12
|
"pi-package",
|
|
8
|
-
"pi-skill"
|
|
13
|
+
"pi-skill",
|
|
14
|
+
"pi-theme",
|
|
15
|
+
"sandbox",
|
|
16
|
+
"websearch"
|
|
9
17
|
],
|
|
18
|
+
"homepage": "https://github.com/goncalossilva/tau#readme",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/goncalossilva/tau/issues"
|
|
21
|
+
},
|
|
10
22
|
"license": "MIT",
|
|
11
23
|
"repository": {
|
|
12
24
|
"type": "git",
|
|
@@ -23,10 +35,10 @@
|
|
|
23
35
|
"turndown-plugin-gfm": "^1.0.2"
|
|
24
36
|
},
|
|
25
37
|
"peerDependencies": {
|
|
26
|
-
"@
|
|
27
|
-
"@
|
|
28
|
-
"@
|
|
29
|
-
"@
|
|
38
|
+
"@earendil-works/pi-agent-core": "*",
|
|
39
|
+
"@earendil-works/pi-ai": "*",
|
|
40
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
41
|
+
"@earendil-works/pi-tui": "*",
|
|
30
42
|
"typebox": "*"
|
|
31
43
|
},
|
|
32
44
|
"engines": {
|
|
@@ -49,6 +61,7 @@
|
|
|
49
61
|
"extensions/openai-verbosity.ts",
|
|
50
62
|
"extensions/review.ts",
|
|
51
63
|
"extensions/sandbox/index.ts",
|
|
64
|
+
"extensions/tool-display-mode.ts",
|
|
52
65
|
"extensions/usage/index.ts",
|
|
53
66
|
"extensions/websearch/index.ts",
|
|
54
67
|
"extensions/worktree.ts"
|
|
@@ -161,7 +161,6 @@ function providerScore(provider, family) {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
if (family === "google") {
|
|
164
|
-
if (normalized === "google-gemini-cli") return 700;
|
|
165
164
|
if (normalized === "google") return 600;
|
|
166
165
|
if (normalized === "github-copilot") return 500;
|
|
167
166
|
if (normalized.includes("vertex")) return 450;
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import {
|
|
3
|
-
clampPercent,
|
|
4
|
-
LiveUsageUnavailableError,
|
|
5
|
-
makeMeterItem,
|
|
6
|
-
parseISODate,
|
|
7
|
-
readArray,
|
|
8
|
-
readString,
|
|
9
|
-
} from "./shared.js";
|
|
10
|
-
import type { LiveUsageItem, LiveUsageSnapshot, UsageProviderDefinition } from "./types.js";
|
|
11
|
-
|
|
12
|
-
const PROVIDER_ID = "google-gemini-cli";
|
|
13
|
-
const QUOTA_URL = "https://cloudcode-pa.googleapis.com/v1internal:retrieveUserQuota";
|
|
14
|
-
const LOAD_CODE_ASSIST_URL = "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist";
|
|
15
|
-
|
|
16
|
-
type GeminiTier = "free-tier" | "legacy-tier" | "standard-tier";
|
|
17
|
-
|
|
18
|
-
interface GeminiAuthPayload {
|
|
19
|
-
token?: string;
|
|
20
|
-
projectId?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface GeminiQuota {
|
|
24
|
-
modelId: string;
|
|
25
|
-
percentLeft: number;
|
|
26
|
-
resetTime?: Date;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function readAuthPayload(value: string): GeminiAuthPayload {
|
|
30
|
-
try {
|
|
31
|
-
return JSON.parse(value) as GeminiAuthPayload;
|
|
32
|
-
} catch {
|
|
33
|
-
throw new LiveUsageUnavailableError("Gemini CLI OAuth login is required for live quota data");
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function parsePlan(tier: GeminiTier | undefined): string | undefined {
|
|
38
|
-
switch (tier) {
|
|
39
|
-
case "standard-tier":
|
|
40
|
-
return "Paid";
|
|
41
|
-
case "free-tier":
|
|
42
|
-
return "Free";
|
|
43
|
-
case "legacy-tier":
|
|
44
|
-
return "Legacy";
|
|
45
|
-
default:
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function parseModelLabel(modelId: string): string {
|
|
51
|
-
const value = modelId.trim();
|
|
52
|
-
const slash = value.lastIndexOf("/");
|
|
53
|
-
return slash === -1 ? value : value.slice(slash + 1);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function isFlashLiteModel(id: string): boolean {
|
|
57
|
-
return id.includes("flash-lite");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function isFlashModel(id: string): boolean {
|
|
61
|
-
return id.includes("flash") && !isFlashLiteModel(id);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function isProModel(id: string): boolean {
|
|
65
|
-
return id.includes("pro");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function makeMeter(key: string, label: string, quota: GeminiQuota): LiveUsageItem {
|
|
69
|
-
return makeMeterItem({
|
|
70
|
-
key,
|
|
71
|
-
label,
|
|
72
|
-
usedPercent: clampPercent(100 - quota.percentLeft),
|
|
73
|
-
windowLabel: "24h",
|
|
74
|
-
resetsAt: quota.resetTime,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async function fetchJson(
|
|
79
|
-
url: string,
|
|
80
|
-
token: string,
|
|
81
|
-
body: Record<string, unknown>,
|
|
82
|
-
signal?: AbortSignal,
|
|
83
|
-
): Promise<Record<string, unknown>> {
|
|
84
|
-
const response = await fetch(url, {
|
|
85
|
-
method: "POST",
|
|
86
|
-
headers: {
|
|
87
|
-
Authorization: `Bearer ${token}`,
|
|
88
|
-
"Content-Type": "application/json",
|
|
89
|
-
},
|
|
90
|
-
body: JSON.stringify(body),
|
|
91
|
-
signal,
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
if (response.status === 401 || response.status === 403) {
|
|
95
|
-
throw new Error("Gemini CLI login expired. Re-run /login google-gemini-cli.");
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (!response.ok) {
|
|
99
|
-
const bodyText = await response.text().catch(() => "");
|
|
100
|
-
throw new Error(
|
|
101
|
-
bodyText.trim()
|
|
102
|
-
? `Gemini usage request failed (${response.status}): ${bodyText.trim()}`
|
|
103
|
-
: `Gemini usage request failed (${response.status}).`,
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return (await response.json()) as Record<string, unknown>;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async function loadCodeAssistStatus(
|
|
111
|
-
token: string,
|
|
112
|
-
signal?: AbortSignal,
|
|
113
|
-
): Promise<{ tier?: GeminiTier }> {
|
|
114
|
-
try {
|
|
115
|
-
const payload = await fetchJson(
|
|
116
|
-
LOAD_CODE_ASSIST_URL,
|
|
117
|
-
token,
|
|
118
|
-
{ metadata: { ideType: "GEMINI_CLI", pluginType: "GEMINI" } },
|
|
119
|
-
signal,
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
const currentTier = payload.currentTier as Record<string, unknown> | undefined;
|
|
123
|
-
const tierId = readString(currentTier?.id);
|
|
124
|
-
|
|
125
|
-
return {
|
|
126
|
-
tier:
|
|
127
|
-
tierId === "free-tier" || tierId === "legacy-tier" || tierId === "standard-tier"
|
|
128
|
-
? tierId
|
|
129
|
-
: undefined,
|
|
130
|
-
};
|
|
131
|
-
} catch {
|
|
132
|
-
return {};
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function parseQuotas(payload: Record<string, unknown>): GeminiQuota[] {
|
|
137
|
-
const buckets = readArray<Record<string, unknown>>(payload.buckets);
|
|
138
|
-
const quotaByModel = new Map<string, GeminiQuota>();
|
|
139
|
-
|
|
140
|
-
for (const bucket of buckets) {
|
|
141
|
-
const input = bucket as Record<string, unknown> | null | undefined;
|
|
142
|
-
const modelId = readString(input?.modelId);
|
|
143
|
-
const remainingFraction =
|
|
144
|
-
typeof input?.remainingFraction === "number" ? input.remainingFraction : undefined;
|
|
145
|
-
if (!modelId || remainingFraction === undefined) continue;
|
|
146
|
-
|
|
147
|
-
const percentLeft = clampPercent(remainingFraction * 100);
|
|
148
|
-
const resetTime = parseISODate(readString(input?.resetTime));
|
|
149
|
-
const quota: GeminiQuota = {
|
|
150
|
-
modelId,
|
|
151
|
-
percentLeft,
|
|
152
|
-
resetTime,
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const existing = quotaByModel.get(modelId);
|
|
156
|
-
if (!existing || quota.percentLeft < existing.percentLeft) {
|
|
157
|
-
quotaByModel.set(modelId, quota);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return [...quotaByModel.values()].sort((left, right) =>
|
|
162
|
-
left.modelId.localeCompare(right.modelId),
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function buildItems(quotas: GeminiQuota[]): LiveUsageItem[] {
|
|
167
|
-
const lower = quotas.map((quota) => ({ quota, modelId: quota.modelId.toLowerCase() }));
|
|
168
|
-
const pro = lower.filter((entry) => isProModel(entry.modelId)).map((entry) => entry.quota);
|
|
169
|
-
const flash = lower.filter((entry) => isFlashModel(entry.modelId)).map((entry) => entry.quota);
|
|
170
|
-
const flashLite = lower
|
|
171
|
-
.filter((entry) => isFlashLiteModel(entry.modelId))
|
|
172
|
-
.map((entry) => entry.quota);
|
|
173
|
-
|
|
174
|
-
const items: LiveUsageItem[] = [];
|
|
175
|
-
const proMin = pro.sort((left, right) => left.percentLeft - right.percentLeft)[0];
|
|
176
|
-
const flashMin = flash.sort((left, right) => left.percentLeft - right.percentLeft)[0];
|
|
177
|
-
const flashLiteMin = flashLite.sort((left, right) => left.percentLeft - right.percentLeft)[0];
|
|
178
|
-
|
|
179
|
-
if (proMin) items.push(makeMeter("pro", "Pro", proMin));
|
|
180
|
-
if (flashMin) items.push(makeMeter("flash", "Flash", flashMin));
|
|
181
|
-
if (flashLiteMin) items.push(makeMeter("flash-lite", "Flash Lite", flashLiteMin));
|
|
182
|
-
|
|
183
|
-
if (items.length > 0) return items;
|
|
184
|
-
|
|
185
|
-
for (const quota of quotas.slice(0, 3)) {
|
|
186
|
-
items.push(makeMeter(quota.modelId, parseModelLabel(quota.modelId), quota));
|
|
187
|
-
}
|
|
188
|
-
return items;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
async function fetchUsage(ctx: ExtensionContext, signal?: AbortSignal): Promise<LiveUsageSnapshot> {
|
|
192
|
-
const apiKey = await ctx.modelRegistry.getApiKeyForProvider(PROVIDER_ID);
|
|
193
|
-
if (!apiKey) throw new LiveUsageUnavailableError("no auth configured");
|
|
194
|
-
|
|
195
|
-
const auth = readAuthPayload(apiKey);
|
|
196
|
-
const token = typeof auth.token === "string" ? auth.token.trim() : "";
|
|
197
|
-
if (!token) {
|
|
198
|
-
throw new LiveUsageUnavailableError("Gemini CLI OAuth login is required for live quota data");
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const authProjectId = typeof auth.projectId === "string" ? auth.projectId.trim() : undefined;
|
|
202
|
-
const statusPromise = loadCodeAssistStatus(token, signal);
|
|
203
|
-
const quotaPromise = fetchJson(
|
|
204
|
-
QUOTA_URL,
|
|
205
|
-
token,
|
|
206
|
-
authProjectId ? { project: authProjectId } : {},
|
|
207
|
-
signal,
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
const [status, quotaPayload] = await Promise.all([statusPromise, quotaPromise]);
|
|
211
|
-
const quotas = parseQuotas(quotaPayload);
|
|
212
|
-
|
|
213
|
-
if (quotas.length === 0) {
|
|
214
|
-
throw new Error("Gemini did not return any live quota data.");
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return {
|
|
218
|
-
plan: parsePlan(status.tier),
|
|
219
|
-
fetchedAt: new Date(),
|
|
220
|
-
items: buildItems(quotas),
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const googleGeminiCliProvider: UsageProviderDefinition = {
|
|
225
|
-
id: PROVIDER_ID,
|
|
226
|
-
label: "Gemini CLI",
|
|
227
|
-
shortLabel: "gemini",
|
|
228
|
-
color: { r: 171, g: 135, b: 234 },
|
|
229
|
-
fetchLiveUsage: fetchUsage,
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
export default googleGeminiCliProvider;
|