@estebanforge/pi-ask-antigravity 1.2.1 → 1.2.3
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/extensions/index.ts +63 -35
- package/package.json +1 -1
package/extensions/index.ts
CHANGED
|
@@ -83,13 +83,13 @@ type Mode = "plan" | "accept-edits";
|
|
|
83
83
|
// whose full string equals the overlay target, the live entry wins. When it
|
|
84
84
|
// does not (older agy, missing model, plan gate), the overlay entry resolves
|
|
85
85
|
// the alias so the user can still type "sonnet" and get a working answer.
|
|
86
|
-
// "sonnet" ->
|
|
87
|
-
// "opus" ->
|
|
88
|
-
// "gpt-oss" ->
|
|
86
|
+
// "sonnet" -> claude-sonnet-4-6
|
|
87
|
+
// "opus" -> claude-opus-4-6-thinking
|
|
88
|
+
// "gpt-oss" -> gpt-oss-120b-medium
|
|
89
89
|
const STATIC_ALIAS_OVERLAY: ReadonlyArray<ModelEntry> = [
|
|
90
|
-
{ full: "
|
|
91
|
-
{ full: "
|
|
92
|
-
{ full: "
|
|
90
|
+
{ full: "claude-sonnet-4-6", family: "other", version: null, tier: null },
|
|
91
|
+
{ full: "claude-opus-4-6-thinking", family: "other", version: null, tier: null },
|
|
92
|
+
{ full: "gpt-oss-120b-medium", family: "other", version: null, tier: null },
|
|
93
93
|
];
|
|
94
94
|
|
|
95
95
|
// Short alias → overlay full string. Used by resolveModel to recognize
|
|
@@ -97,15 +97,15 @@ const STATIC_ALIAS_OVERLAY: ReadonlyArray<ModelEntry> = [
|
|
|
97
97
|
// not match. The overlay entries are also merged into the live catalog for
|
|
98
98
|
// exact-string passthrough, so this map only needs to cover the short names.
|
|
99
99
|
const STATIC_SHORT_ALIAS: ReadonlyMap<string, string> = new Map([
|
|
100
|
-
["sonnet", "
|
|
101
|
-
["opus", "
|
|
102
|
-
["gpt-oss", "
|
|
100
|
+
["sonnet", "claude-sonnet-4-6"],
|
|
101
|
+
["opus", "claude-opus-4-6-thinking"],
|
|
102
|
+
["gpt-oss", "gpt-oss-120b-medium"],
|
|
103
103
|
]);
|
|
104
104
|
|
|
105
105
|
/** Merge the live catalog with the static alias overlay. Live entries win on
|
|
106
106
|
* case-insensitive full-string equality so an updated `agy models` listing
|
|
107
107
|
* always takes precedence over the hardcoded fallback. */
|
|
108
|
-
function mergeCatalog(live: ModelEntry[]): ModelEntry[] {
|
|
108
|
+
export function mergeCatalog(live: ModelEntry[]): ModelEntry[] {
|
|
109
109
|
const seen = new Set(live.map((e) => e.full.toLowerCase()));
|
|
110
110
|
const merged = [...live];
|
|
111
111
|
for (const entry of STATIC_ALIAS_OVERLAY) {
|
|
@@ -139,13 +139,22 @@ COMPACT OUTPUT (param: digest): when true, the prompt is prefixed to request com
|
|
|
139
139
|
type ThinkingTier = "low" | "medium" | "high";
|
|
140
140
|
type Family = "flash" | "pro" | "other";
|
|
141
141
|
|
|
142
|
-
interface ModelEntry {
|
|
143
|
-
full: string; // exact agy
|
|
142
|
+
export interface ModelEntry {
|
|
143
|
+
full: string; // exact agy slug, e.g. "gemini-3.6-flash-medium"
|
|
144
144
|
family: Family;
|
|
145
|
-
version: string | null; // "3.
|
|
145
|
+
version: string | null; // "3.6"
|
|
146
146
|
tier: ThinkingTier | null;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
/** Argv-facing model resolution: the exact --model slug plus an optional
|
|
150
|
+
* --effort tier. Gemini bases split the tier out (the base slug alone is
|
|
151
|
+
* invalid without --effort); fixed-thinking families keep agy's exact slug
|
|
152
|
+
* and carry no effort. */
|
|
153
|
+
export interface ResolvedModel {
|
|
154
|
+
model: string;
|
|
155
|
+
effort?: ThinkingTier;
|
|
156
|
+
}
|
|
157
|
+
|
|
149
158
|
interface Config {
|
|
150
159
|
defaultModel: string;
|
|
151
160
|
defaultThinking: ThinkingTier;
|
|
@@ -243,8 +252,11 @@ function saveConfig(patch: Partial<Config>): SaveResult {
|
|
|
243
252
|
// --- Model parsing + alias resolution --------------------------------------
|
|
244
253
|
|
|
245
254
|
/** Parse one `agy models` line into a structured entry. */
|
|
246
|
-
function parseModelLine(line: string): ModelEntry | null {
|
|
247
|
-
|
|
255
|
+
export function parseModelLine(line: string): ModelEntry | null {
|
|
256
|
+
// agy prints TWO columns: "<slug> <display label>". --model takes only the
|
|
257
|
+
// slug (col 1), so split it off; the label is display-only. A bare-slug line
|
|
258
|
+
// (no whitespace) splits to itself.
|
|
259
|
+
const full = line.trim().split(/\s+/)[0] ?? "";
|
|
248
260
|
if (!full) return null;
|
|
249
261
|
|
|
250
262
|
const lower = full.toLowerCase();
|
|
@@ -257,7 +269,7 @@ function parseModelLine(line: string): ModelEntry | null {
|
|
|
257
269
|
const versionMatch = lower.match(/(\d+\.\d+)/);
|
|
258
270
|
const version = versionMatch ? versionMatch[1] : null;
|
|
259
271
|
|
|
260
|
-
const tierMatch = lower.match(
|
|
272
|
+
const tierMatch = lower.match(/-(low|medium|high)$/);
|
|
261
273
|
const tier = tierMatch ? (tierMatch[1] as ThinkingTier) : null;
|
|
262
274
|
|
|
263
275
|
return { full, family, version, tier };
|
|
@@ -276,21 +288,34 @@ function nearestTier(available: ThinkingTier[], preferred: ThinkingTier): Thinki
|
|
|
276
288
|
return sorted[0] ?? preferred;
|
|
277
289
|
}
|
|
278
290
|
|
|
291
|
+
/** Build the argv-facing resolution from a picked catalog entry. Gemini bases
|
|
292
|
+
* (slugs starting "gemini-") accept a separate --effort, so split the tier
|
|
293
|
+
* suffix out of the slug: the base alone (gemini-3.6-flash) is what --model
|
|
294
|
+
* wants, and the tier goes to --effort. Fixed-thinking families keep agy's
|
|
295
|
+
* exact slug even when it carries a -medium suffix (gpt-oss-120b-medium):
|
|
296
|
+
* agy rejects --effort for them, so the suffix stays part of the slug. */
|
|
297
|
+
function toResolved(full: string, tier: ThinkingTier | null): ResolvedModel {
|
|
298
|
+
if (tier && full.toLowerCase().startsWith("gemini-")) {
|
|
299
|
+
return { model: full.replace(/-(low|medium|high)$/, ""), effort: tier };
|
|
300
|
+
}
|
|
301
|
+
return { model: full };
|
|
302
|
+
}
|
|
303
|
+
|
|
279
304
|
/**
|
|
280
|
-
* Resolve a friendly alias / partial name to an
|
|
281
|
-
* Returns null
|
|
282
|
-
*
|
|
305
|
+
* Resolve a friendly alias / partial name to an argv-facing {model, effort?}.
|
|
306
|
+
* Returns null only when the family is unrecognized; the caller then passes
|
|
307
|
+
* the raw input straight to agy.
|
|
283
308
|
*/
|
|
284
|
-
function resolveModel(
|
|
309
|
+
export function resolveModel(
|
|
285
310
|
input: string,
|
|
286
311
|
entries: ModelEntry[],
|
|
287
312
|
defaultThinking: ThinkingTier,
|
|
288
|
-
):
|
|
313
|
+
): ResolvedModel | null {
|
|
289
314
|
const lower = input.toLowerCase().trim();
|
|
290
315
|
|
|
291
316
|
// 1. Exact full-string match (case-insensitive).
|
|
292
317
|
const exact = entries.find((e) => e.full.toLowerCase() === lower);
|
|
293
|
-
if (exact) return exact.full;
|
|
318
|
+
if (exact) return toResolved(exact.full, exact.tier);
|
|
294
319
|
|
|
295
320
|
// 1b. Static short alias ("sonnet" / "opus" / "gpt-oss"). Checked
|
|
296
321
|
// before the family parser because none of these names contain
|
|
@@ -303,9 +328,8 @@ function resolveModel(
|
|
|
303
328
|
// the model under different casing than the overlay.
|
|
304
329
|
if (STATIC_SHORT_ALIAS.has(lower)) {
|
|
305
330
|
const target = STATIC_SHORT_ALIAS.get(lower) as string;
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
return fromCatalog ? fromCatalog.full : target;
|
|
331
|
+
const fromCatalog = entries.find((e) => e.full.toLowerCase() === target.toLowerCase());
|
|
332
|
+
return toResolved(fromCatalog?.full ?? target, fromCatalog?.tier ?? null);
|
|
309
333
|
}
|
|
310
334
|
|
|
311
335
|
// 2. Parse the alias.
|
|
@@ -365,13 +389,14 @@ function resolveModel(
|
|
|
365
389
|
const familyTiers = new Set(
|
|
366
390
|
candidates.map((e) => e.tier).filter((t): t is ThinkingTier => t !== null),
|
|
367
391
|
);
|
|
368
|
-
if (familyTiers.size === 0) return candidates[0].full; // no tiers on any entry
|
|
392
|
+
if (familyTiers.size === 0) return toResolved(candidates[0].full, null); // no tiers on any entry
|
|
369
393
|
|
|
370
394
|
const preferred =
|
|
371
395
|
tier ??
|
|
372
396
|
(familyTiers.has(defaultThinking) ? defaultThinking : FAMILY_DEFAULT_TIER[family]);
|
|
373
397
|
const chosenTier = nearestTier([...familyTiers], preferred);
|
|
374
|
-
|
|
398
|
+
const picked = candidates.find((e) => e.tier === chosenTier) ?? candidates[0];
|
|
399
|
+
return toResolved(picked.full, picked.tier);
|
|
375
400
|
}
|
|
376
401
|
|
|
377
402
|
// --- agy process helpers ---------------------------------------------------
|
|
@@ -671,7 +696,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
671
696
|
` defaultModel: ${config.defaultModel}`,
|
|
672
697
|
` defaultThinking: ${config.defaultThinking}`,
|
|
673
698
|
` permissions: ${config.skipPermissions ? "auto-approved" : "prompt"}`,
|
|
674
|
-
` resolved: ${resolveModel(config.defaultModel, discovered, config.defaultThinking) ?? "(agy default)"}`,
|
|
699
|
+
` resolved: ${resolveModel(config.defaultModel, discovered, config.defaultThinking)?.model ?? "(agy default)"}`,
|
|
675
700
|
``,
|
|
676
701
|
`Edit: ~/.pi/agent/ask-antigravity.json`,
|
|
677
702
|
].join("\n"),
|
|
@@ -682,7 +707,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
682
707
|
|
|
683
708
|
// Resolve the display string for the current default model.
|
|
684
709
|
const currentResolved =
|
|
685
|
-
resolveModel(config.defaultModel, discovered, config.defaultThinking) ?? config.defaultModel;
|
|
710
|
+
resolveModel(config.defaultModel, discovered, config.defaultThinking)?.model ?? config.defaultModel;
|
|
686
711
|
|
|
687
712
|
const items: SettingItem[] = [
|
|
688
713
|
{
|
|
@@ -692,7 +717,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
692
717
|
"Friendly alias resolved to the latest matching agy model. 'flash' = latest Flash, 'pro' = latest Pro, 'gemini' = latest Flash.",
|
|
693
718
|
currentValue: `${config.defaultModel} → ${currentResolved}`,
|
|
694
719
|
values: MODEL_OPTIONS.map((m) => {
|
|
695
|
-
const r = resolveModel(m, discovered, config.defaultThinking) ?? m;
|
|
720
|
+
const r = resolveModel(m, discovered, config.defaultThinking)?.model ?? m;
|
|
696
721
|
return `${m} → ${r}`;
|
|
697
722
|
}),
|
|
698
723
|
},
|
|
@@ -877,7 +902,9 @@ export default async function (pi: ExtensionAPI) {
|
|
|
877
902
|
};
|
|
878
903
|
}
|
|
879
904
|
const resolved =
|
|
880
|
-
resolveModel(requestedModel, discovered, config.defaultThinking) ??
|
|
905
|
+
resolveModel(requestedModel, discovered, config.defaultThinking) ?? {
|
|
906
|
+
model: requestedModel,
|
|
907
|
+
};
|
|
881
908
|
|
|
882
909
|
const start = Date.now();
|
|
883
910
|
const cwd = params.cwd || ctx.cwd || process.cwd();
|
|
@@ -888,13 +915,13 @@ export default async function (pi: ExtensionAPI) {
|
|
|
888
915
|
if (!stat.isDirectory()) {
|
|
889
916
|
return {
|
|
890
917
|
content: [{ type: "text", text: `cwd is not a directory: ${cwd}` }],
|
|
891
|
-
details: emptyDetails(requestedModel, resolved),
|
|
918
|
+
details: emptyDetails(requestedModel, resolved.model),
|
|
892
919
|
};
|
|
893
920
|
}
|
|
894
921
|
} catch {
|
|
895
922
|
return {
|
|
896
923
|
content: [{ type: "text", text: `cwd does not exist: ${cwd}` }],
|
|
897
|
-
details: emptyDetails(requestedModel, resolved),
|
|
924
|
+
details: emptyDetails(requestedModel, resolved.model),
|
|
898
925
|
};
|
|
899
926
|
}
|
|
900
927
|
|
|
@@ -927,7 +954,8 @@ export default async function (pi: ExtensionAPI) {
|
|
|
927
954
|
const args: string[] = ["--add-dir", cwd];
|
|
928
955
|
const extra = extraArgs();
|
|
929
956
|
if (extra.length) args.push(...extra);
|
|
930
|
-
if (resolved) args.push("--model", resolved);
|
|
957
|
+
if (resolved.model) args.push("--model", resolved.model);
|
|
958
|
+
if (resolved.effort) args.push("--effort", resolved.effort);
|
|
931
959
|
args.push("--mode", mode);
|
|
932
960
|
// accept-edits auto-approves file edits but NOT shell commands, so a
|
|
933
961
|
// run_command would hang on an unanswerable y/n prompt in non-interactive
|
|
@@ -939,7 +967,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
939
967
|
|
|
940
968
|
const details: AgyDetails = {
|
|
941
969
|
model: requestedModel,
|
|
942
|
-
resolvedModel: resolved,
|
|
970
|
+
resolvedModel: resolved.model,
|
|
943
971
|
mode,
|
|
944
972
|
digest: useDigest,
|
|
945
973
|
conversationId: isContinuation ? (rawConvId as string) : null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@estebanforge/pi-ask-antigravity",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "Pi tool that delegates a self-contained sub-task to Google Antigravity's agy CLI (the CLI for Gemini). Friendly model aliases (flash/pro/gemini), configurable default model + thinking, and the AskAntigravity delegation tool.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|