pi-ui-extend 1.0.31 → 1.0.35
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/dist/app/app.js +0 -1
- package/dist/app/commands/command-host.d.ts +1 -2
- package/dist/app/commands/command-model-actions.js +5 -5
- package/dist/app/constants.d.ts +0 -1
- package/dist/app/constants.js +0 -34
- package/dist/app/popup/menu-items-controller.d.ts +1 -3
- package/dist/app/popup/menu-items-controller.js +10 -32
- package/dist/app/rendering/popup-menu-renderer.js +1 -1
- package/dist/app/runtime.js +1 -3
- package/dist/app/screen/file-link-opener.js +15 -49
- package/external/pi-tools-suite/README.md +15 -6
- package/external/pi-tools-suite/docs/browser-qa-subagent.md +24 -6
- package/external/pi-tools-suite/package.json +3 -3
- package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +13 -9
- package/external/pi-tools-suite/src/async-subagents/core/config.ts +2 -2
- package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +23 -4
- package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/SKILL.md +40 -4
- package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/qa-design.md +19 -0
- package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/scripts/browser-qa-runner.mjs +444 -13
- package/external/pi-tools-suite/src/coding-discipline/index.ts +12 -2
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +6 -6
- package/external/pi-tools-suite/src/tool-descriptions.ts +2 -0
- package/package.json +4 -4
package/dist/app/app.js
CHANGED
|
@@ -533,7 +533,6 @@ export class PiUiExtendApp {
|
|
|
533
533
|
getThinkingMenuItems: (query) => this.menuItems.getThinkingMenuItems(query),
|
|
534
534
|
awaitCurrentSessionExtensions: (runtime) => this.awaitCurrentSessionExtensions(runtime),
|
|
535
535
|
modelRef: (model) => this.menuItems.modelRef(model),
|
|
536
|
-
getFavoriteScopedModels: () => this.menuItems.getFavoriteScopedModels(),
|
|
537
536
|
setSessionStatus: (session) => this.setSessionStatus(session),
|
|
538
537
|
queueUserMessage: (text) => {
|
|
539
538
|
this.queuedMessages.deferUserMessage(this.queuedMessages.createSubmittedUserMessage(text, text, []));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AgentSession, AgentSessionRuntime, SessionInfo } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { SessionSearchResult } from "../session/session-search.js";
|
|
3
3
|
import type { AppRequestHistory } from "../session/request-history.js";
|
|
4
|
-
import type { ActivePopupMenu, AppOptions, Entry, ModelMenuValue, PixMenuItem, PixMenuOptions, PopupMenuPlacement,
|
|
4
|
+
import type { ActivePopupMenu, AppOptions, Entry, ModelMenuValue, PixMenuItem, PixMenuOptions, PopupMenuPlacement, SessionModel, ThinkingMenuValue } from "../types.js";
|
|
5
5
|
import type { ToastNotifier } from "../../ui.js";
|
|
6
6
|
export type DirectPopupMenu = Exclude<ActivePopupMenu, "slash">;
|
|
7
7
|
export type CommandControllerHost = {
|
|
@@ -27,7 +27,6 @@ export type CommandControllerHost = {
|
|
|
27
27
|
getModelMenuItems(query: string): readonly PixMenuItem<ModelMenuValue>[];
|
|
28
28
|
getThinkingMenuItems(query: string): readonly PixMenuItem<ThinkingMenuValue>[];
|
|
29
29
|
modelRef(model: SessionModel): string;
|
|
30
|
-
getFavoriteScopedModels(): ScopedSessionModel[];
|
|
31
30
|
setSessionStatus(session: AgentSession | undefined): void;
|
|
32
31
|
queueUserMessage(text: string): void;
|
|
33
32
|
resetSessionView(): void;
|
|
@@ -18,7 +18,7 @@ export class ModelCommandActions {
|
|
|
18
18
|
const enabledModels = settings.getEnabledModels();
|
|
19
19
|
const scopedModelText = enabledModels && enabledModels.length > 0
|
|
20
20
|
? enabledModels.map((model) => ` - ${model}`).join("\n")
|
|
21
|
-
: " -
|
|
21
|
+
: " - all available models (no scope)";
|
|
22
22
|
const text = [
|
|
23
23
|
"Settings summary",
|
|
24
24
|
`cwd: ${runtime.cwd}`,
|
|
@@ -191,7 +191,7 @@ export class ModelCommandActions {
|
|
|
191
191
|
else if (sessionModels.length > 0)
|
|
192
192
|
scopedModelLines = sessionModels;
|
|
193
193
|
else
|
|
194
|
-
scopedModelLines = [" -
|
|
194
|
+
scopedModelLines = [" - all available models (no scope)"];
|
|
195
195
|
this.host.addEntry({
|
|
196
196
|
id: createId("system"),
|
|
197
197
|
kind: "system",
|
|
@@ -200,7 +200,7 @@ export class ModelCommandActions {
|
|
|
200
200
|
...scopedModelLines,
|
|
201
201
|
"",
|
|
202
202
|
"Usage: /scoped-models <provider/model[:thinking]> [...more]",
|
|
203
|
-
"Use /scoped-models reset to
|
|
203
|
+
"Use /scoped-models reset to use all available models.",
|
|
204
204
|
].join("\n"),
|
|
205
205
|
});
|
|
206
206
|
this.host.setSessionStatus(runtime.session);
|
|
@@ -208,8 +208,8 @@ export class ModelCommandActions {
|
|
|
208
208
|
}
|
|
209
209
|
if (["reset", "default", "clear"].includes(value.toLowerCase())) {
|
|
210
210
|
runtime.services.settingsManager.setEnabledModels(undefined);
|
|
211
|
-
runtime.session.setScopedModels(
|
|
212
|
-
this.host.addEntry({ id: createId("system"), kind: "system", text: "
|
|
211
|
+
runtime.session.setScopedModels([]);
|
|
212
|
+
this.host.addEntry({ id: createId("system"), kind: "system", text: "Model scope reset to all available models." });
|
|
213
213
|
this.host.setSessionStatus(runtime.session);
|
|
214
214
|
return;
|
|
215
215
|
}
|
package/dist/app/constants.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { ResolvedToolRule } from "../config.js";
|
|
2
2
|
export declare const THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
3
3
|
export declare const THINKING_MENU_MAX_ROWS: number;
|
|
4
|
-
export declare const PI_FAVORITE_MODEL_REFS: readonly ["amazon-bedrock/us.anthropic.claude-opus-4-6-v1", "anthropic/claude-opus-4-8", "openai/gpt-5.4", "azure-openai-responses/gpt-5.4", "openai-codex/gpt-5.6-sol", "deepseek/deepseek-v4-pro", "google/gemini-3.1-pro-preview", "google-vertex/gemini-3.1-pro-preview", "github-copilot/gpt-5.4", "openrouter/moonshotai/kimi-k2.6", "vercel-ai-gateway/zai/glm-5.2", "xai/grok-4.5", "groq/openai/gpt-oss-120b", "cerebras/gpt-oss-120b", "zai/glm-5.3", "mistral/devstral-medium-latest", "minimax/MiniMax-M2.7", "minimax-cn/MiniMax-M2.7", "moonshotai/kimi-k2.6", "moonshotai-cn/kimi-k2.6", "huggingface/moonshotai/Kimi-K2.6", "fireworks/accounts/fireworks/models/kimi-k2p6", "together/moonshotai/Kimi-K2.6", "opencode/kimi-k2.6", "opencode-go/kimi-k2.6", "kimi-coding/kimi-for-coding", "cloudflare-workers-ai/@cf/moonshotai/kimi-k2.6", "cloudflare-ai-gateway/workers-ai/@cf/moonshotai/kimi-k2.6", "xiaomi/mimo-v2.5-pro", "xiaomi-token-plan-cn/mimo-v2.5-pro", "xiaomi-token-plan-ams/mimo-v2.5-pro", "xiaomi-token-plan-sgp/mimo-v2.5-pro"];
|
|
5
4
|
export declare const SLASH_COMMAND_MENU_MAX_ROWS = 6;
|
|
6
5
|
export declare const RESUME_MENU_MAX_ROWS = 20;
|
|
7
6
|
export declare const RESUME_MENU_INITIAL_SESSION_ROWS = 30;
|
package/dist/app/constants.js
CHANGED
|
@@ -1,40 +1,6 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
3
3
|
export const THINKING_MENU_MAX_ROWS = THINKING_LEVELS.length + 1;
|
|
4
|
-
export const PI_FAVORITE_MODEL_REFS = [
|
|
5
|
-
"amazon-bedrock/us.anthropic.claude-opus-4-6-v1",
|
|
6
|
-
"anthropic/claude-opus-4-8",
|
|
7
|
-
"openai/gpt-5.4",
|
|
8
|
-
"azure-openai-responses/gpt-5.4",
|
|
9
|
-
"openai-codex/gpt-5.6-sol",
|
|
10
|
-
"deepseek/deepseek-v4-pro",
|
|
11
|
-
"google/gemini-3.1-pro-preview",
|
|
12
|
-
"google-vertex/gemini-3.1-pro-preview",
|
|
13
|
-
"github-copilot/gpt-5.4",
|
|
14
|
-
"openrouter/moonshotai/kimi-k2.6",
|
|
15
|
-
"vercel-ai-gateway/zai/glm-5.2",
|
|
16
|
-
"xai/grok-4.5",
|
|
17
|
-
"groq/openai/gpt-oss-120b",
|
|
18
|
-
"cerebras/gpt-oss-120b",
|
|
19
|
-
"zai/glm-5.3",
|
|
20
|
-
"mistral/devstral-medium-latest",
|
|
21
|
-
"minimax/MiniMax-M2.7",
|
|
22
|
-
"minimax-cn/MiniMax-M2.7",
|
|
23
|
-
"moonshotai/kimi-k2.6",
|
|
24
|
-
"moonshotai-cn/kimi-k2.6",
|
|
25
|
-
"huggingface/moonshotai/Kimi-K2.6",
|
|
26
|
-
"fireworks/accounts/fireworks/models/kimi-k2p6",
|
|
27
|
-
"together/moonshotai/Kimi-K2.6",
|
|
28
|
-
"opencode/kimi-k2.6",
|
|
29
|
-
"opencode-go/kimi-k2.6",
|
|
30
|
-
"kimi-coding/kimi-for-coding",
|
|
31
|
-
"cloudflare-workers-ai/@cf/moonshotai/kimi-k2.6",
|
|
32
|
-
"cloudflare-ai-gateway/workers-ai/@cf/moonshotai/kimi-k2.6",
|
|
33
|
-
"xiaomi/mimo-v2.5-pro",
|
|
34
|
-
"xiaomi-token-plan-cn/mimo-v2.5-pro",
|
|
35
|
-
"xiaomi-token-plan-ams/mimo-v2.5-pro",
|
|
36
|
-
"xiaomi-token-plan-sgp/mimo-v2.5-pro",
|
|
37
|
-
];
|
|
38
4
|
export const SLASH_COMMAND_MENU_MAX_ROWS = 6;
|
|
39
5
|
export const RESUME_MENU_MAX_ROWS = 20;
|
|
40
6
|
export const RESUME_MENU_INITIAL_SESSION_ROWS = 30;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentSessionRuntime, SessionInfo } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { PopupMenuItem } from "../../ui.js";
|
|
3
|
-
import type { Entry, ModelMenuValue, QueueMessageMenuValue, ResumeMenuValue,
|
|
3
|
+
import type { Entry, ModelMenuValue, QueueMessageMenuValue, ResumeMenuValue, SessionModel, SlashCommand, ThinkingMenuValue, UserMessageJumpMenuValue, UserMessageMenuValue } from "../types.js";
|
|
4
4
|
export type AppMenuItemsControllerHost = {
|
|
5
5
|
runtime(): AgentSessionRuntime | undefined;
|
|
6
6
|
getBuiltinSlashCommands(): readonly SlashCommand[];
|
|
@@ -18,7 +18,6 @@ export declare class AppMenuItemsController {
|
|
|
18
18
|
getSlashCommandMatches(query: string, limit?: number): import("../../fuzzy.js").FuzzyMatch<SlashCommand>[];
|
|
19
19
|
getSlashCommandMenuItems(query: string): PopupMenuItem<SlashCommand>[];
|
|
20
20
|
modelRef(model: SessionModel): string;
|
|
21
|
-
getFavoriteScopedModels(): ScopedSessionModel[];
|
|
22
21
|
getModelMenuItems(query: string): PopupMenuItem<ModelMenuValue>[];
|
|
23
22
|
getThinkingMenuItems(query: string): PopupMenuItem<ThinkingMenuValue>[];
|
|
24
23
|
getUserMessageMenuItems(): PopupMenuItem<UserMessageMenuValue>[];
|
|
@@ -30,7 +29,6 @@ export declare class AppMenuItemsController {
|
|
|
30
29
|
private getResumeMenuItemsLoader;
|
|
31
30
|
private getAllSlashCommands;
|
|
32
31
|
private isCurrentModel;
|
|
33
|
-
private resolveScopedModelRefs;
|
|
34
32
|
private getModelMenuModels;
|
|
35
33
|
private thinkingLevelDescription;
|
|
36
34
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { fuzzySearch } from "../../fuzzy.js";
|
|
3
|
-
import {
|
|
3
|
+
import { THINKING_LEVELS } from "../constants.js";
|
|
4
4
|
import { APP_ICONS } from "../icons.js";
|
|
5
|
-
import { parseScopedModelRef } from "../model/model-ref.js";
|
|
6
5
|
import { buildUserMessageJumpItems, createSessionInfoMenuItemsLoader, filterUserMessageJumpItems } from "./popup-menu-controller.js";
|
|
7
6
|
import { getResourceSlashCommands, getSlashCommandMatches, parseSlashInput } from "../commands/slash-commands.js";
|
|
8
7
|
import { isRecord } from "../guards.js";
|
|
@@ -38,11 +37,6 @@ export class AppMenuItemsController {
|
|
|
38
37
|
modelRef(model) {
|
|
39
38
|
return `${model.provider}/${model.id}`;
|
|
40
39
|
}
|
|
41
|
-
getFavoriteScopedModels() {
|
|
42
|
-
const configuredRefs = this.host.runtime()?.services.settingsManager.getEnabledModels();
|
|
43
|
-
const refs = configuredRefs && configuredRefs.length > 0 ? configuredRefs : PI_FAVORITE_MODEL_REFS;
|
|
44
|
-
return this.resolveScopedModelRefs(refs);
|
|
45
|
-
}
|
|
46
40
|
getModelMenuItems(query) {
|
|
47
41
|
const models = [...this.getModelMenuModels()].sort((left, right) => {
|
|
48
42
|
const leftCurrent = this.isCurrentModel(left);
|
|
@@ -172,33 +166,17 @@ export class AppMenuItemsController {
|
|
|
172
166
|
const current = this.host.runtime()?.session.model;
|
|
173
167
|
return current?.provider === model.provider && current.id === model.id;
|
|
174
168
|
}
|
|
175
|
-
resolveScopedModelRefs(modelRefs) {
|
|
176
|
-
const modelRuntime = this.host.runtime()?.services.modelRuntime;
|
|
177
|
-
if (!modelRuntime)
|
|
178
|
-
return [];
|
|
179
|
-
const scopedModels = [];
|
|
180
|
-
for (const modelRef of modelRefs) {
|
|
181
|
-
const parsed = parseScopedModelRef(modelRef);
|
|
182
|
-
if (!parsed)
|
|
183
|
-
continue;
|
|
184
|
-
const model = modelRuntime.getModel(parsed.provider, parsed.modelId);
|
|
185
|
-
if (!model)
|
|
186
|
-
continue;
|
|
187
|
-
scopedModels.push({
|
|
188
|
-
model,
|
|
189
|
-
...(parsed.thinkingLevel === undefined ? {} : { thinkingLevel: parsed.thinkingLevel }),
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
return scopedModels;
|
|
193
|
-
}
|
|
194
169
|
getModelMenuModels() {
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
if (!scopedModels.length)
|
|
170
|
+
const runtime = this.host.runtime();
|
|
171
|
+
if (!runtime)
|
|
198
172
|
return [];
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
173
|
+
const { session } = runtime;
|
|
174
|
+
const modelRuntime = runtime.services.modelRuntime;
|
|
175
|
+
if (!session.scopedModels.length) {
|
|
176
|
+
return [...modelRuntime.getAvailableSnapshot()];
|
|
177
|
+
}
|
|
178
|
+
return session.scopedModels.map((scoped) => {
|
|
179
|
+
const refreshed = modelRuntime.getModel(scoped.model.provider, scoped.model.id);
|
|
202
180
|
return (refreshed ?? scoped.model);
|
|
203
181
|
});
|
|
204
182
|
}
|
|
@@ -101,7 +101,7 @@ export class PopupMenuRenderer {
|
|
|
101
101
|
const visibleItems = menu.visibleItems();
|
|
102
102
|
if (!this.hasPopupActionItems(menu.items)) {
|
|
103
103
|
lines.push({
|
|
104
|
-
text: this.host.session ? " No matching
|
|
104
|
+
text: this.host.session ? " No matching available models" : " Model menu unavailable",
|
|
105
105
|
variant: "muted",
|
|
106
106
|
});
|
|
107
107
|
}
|
package/dist/app/runtime.js
CHANGED
|
@@ -5,7 +5,6 @@ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, getAgentDir, SessionManager, } from "@earendil-works/pi-coding-agent";
|
|
7
7
|
import { loadPixConfig, resolveDefaultModelRef } from "../config.js";
|
|
8
|
-
import { PI_FAVORITE_MODEL_REFS } from "./constants.js";
|
|
9
8
|
import { isThinkingLevel, parseModelRef, parseScopedModelRef } from "./model/model-ref.js";
|
|
10
9
|
import { openLazySessionManager } from "./session/lazy-session-manager.js";
|
|
11
10
|
const BUNDLED_QUESTION_EXTENSION_NAME = "question";
|
|
@@ -267,8 +266,7 @@ export async function createPixRuntime(options, runtimeOptions = {}) {
|
|
|
267
266
|
throw new Error(`Model not found: ${parsedModel.provider}/${parsedModel.modelId}`);
|
|
268
267
|
}
|
|
269
268
|
const enabledModelRefs = services.settingsManager.getEnabledModels();
|
|
270
|
-
const
|
|
271
|
-
const scopedModels = favoriteModelRefs.flatMap((modelRef) => {
|
|
269
|
+
const scopedModels = (enabledModelRefs ?? []).flatMap((modelRef) => {
|
|
272
270
|
const scoped = parseScopedModelRef(modelRef);
|
|
273
271
|
if (!scoped)
|
|
274
272
|
return [];
|
|
@@ -2,6 +2,11 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { isAbsolute, posix, win32 } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
+
const MEDIA_EXTENSIONS = new Set([
|
|
6
|
+
".3g2", ".3gp", ".aac", ".aiff", ".alac", ".apng", ".avi", ".avif", ".bmp", ".flac", ".gif", ".heic", ".heif",
|
|
7
|
+
".ico", ".jpeg", ".jpg", ".m4a", ".m4v", ".mkv", ".mov", ".mp3", ".mp4", ".mpeg", ".mpg", ".oga", ".ogg",
|
|
8
|
+
".ogv", ".opus", ".png", ".svg", ".tif", ".tiff", ".wav", ".webm", ".webp", ".wma", ".wmv",
|
|
9
|
+
]);
|
|
5
10
|
let deps = { existsSync, env: process.env, platform: process.platform, spawn };
|
|
6
11
|
export function setFileLinkOpenerTestDeps(overrides) {
|
|
7
12
|
const previous = deps;
|
|
@@ -16,9 +21,11 @@ export function openFileLink(link) {
|
|
|
16
21
|
const filePath = link.filePath ?? filePathFromUrl(link.url);
|
|
17
22
|
if (!filePath)
|
|
18
23
|
return false;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
if (isZedTerminal(deps.env) && !isMediaFile(filePath)) {
|
|
25
|
+
const openedInZed = trySpawnCandidates(zedCommandCandidates(), [zedTarget(filePath, link.line, link.column)]);
|
|
26
|
+
if (openedInZed)
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
22
29
|
return openPathWithSystemViewer(filePath);
|
|
23
30
|
}
|
|
24
31
|
function isWebUrl(url) {
|
|
@@ -39,45 +46,12 @@ function zedTarget(filePath, line, column) {
|
|
|
39
46
|
return filePath;
|
|
40
47
|
return column === undefined ? `${filePath}:${line}` : `${filePath}:${line}:${column}`;
|
|
41
48
|
}
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
return filePath;
|
|
45
|
-
return column === undefined ? `${filePath}:${line}` : `${filePath}:${line}:${column}`;
|
|
46
|
-
}
|
|
47
|
-
function preferredEditorLaunch(filePath, line, column) {
|
|
48
|
-
switch (detectEditor(deps.env)) {
|
|
49
|
-
case "cursor":
|
|
50
|
-
return { args: ["--goto", gotoTarget(filePath, line, column)], candidates: commandCandidates(deps.env.CURSOR_CLI, "cursor") };
|
|
51
|
-
case "jetbrains":
|
|
52
|
-
return {
|
|
53
|
-
args: jetbrainsTargetArgs(filePath, line),
|
|
54
|
-
candidates: commandCandidates(deps.env.JETBRAINS_IDE_CLI, "idea", "idea64", "webstorm", "webstorm64", "pycharm", "pycharm64", "goland", "goland64", "clion", "clion64", "phpstorm", "phpstorm64", "rubymine", "rubymine64", "rider", "rider64"),
|
|
55
|
-
};
|
|
56
|
-
case "vscode":
|
|
57
|
-
return { args: ["--goto", gotoTarget(filePath, line, column)], candidates: commandCandidates(deps.env.VSCODE_CLI, "code", "code-insiders") };
|
|
58
|
-
case "windsurf":
|
|
59
|
-
return { args: ["--goto", gotoTarget(filePath, line, column)], candidates: commandCandidates(deps.env.WINDSURF_CLI, "windsurf") };
|
|
60
|
-
case "zed":
|
|
61
|
-
return { args: [zedTarget(filePath, line, column)], candidates: zedCommandCandidates() };
|
|
62
|
-
default:
|
|
63
|
-
return undefined;
|
|
64
|
-
}
|
|
49
|
+
function isZedTerminal(env) {
|
|
50
|
+
return env.TERM_PROGRAM?.trim().toLowerCase() === "zed" || Boolean(env.ZED_CLI);
|
|
65
51
|
}
|
|
66
|
-
function
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
const terminalProvider = env.TERMINAL_PROVIDER?.trim().toLowerCase();
|
|
70
|
-
if (termProgram === "cursor" || env.CURSOR_TRACE_ID || env.CURSOR_TRACE)
|
|
71
|
-
return "cursor";
|
|
72
|
-
if (termProgram === "windsurf")
|
|
73
|
-
return "windsurf";
|
|
74
|
-
if (termProgram === "zed" || env.ZED_CLI)
|
|
75
|
-
return "zed";
|
|
76
|
-
if (termProgram === "vscode" || env.VSCODE_IPC_HOOK_CLI || env.VSCODE_GIT_IPC_HANDLE)
|
|
77
|
-
return "vscode";
|
|
78
|
-
if (terminalEmulator?.includes("jetbrains") || terminalProvider === "jetbrains")
|
|
79
|
-
return "jetbrains";
|
|
80
|
-
return undefined;
|
|
52
|
+
function isMediaFile(filePath) {
|
|
53
|
+
const pathApi = deps.platform === "win32" ? win32 : posix;
|
|
54
|
+
return MEDIA_EXTENSIONS.has(pathApi.extname(filePath).toLowerCase());
|
|
81
55
|
}
|
|
82
56
|
function zedCommandCandidates() {
|
|
83
57
|
const candidates = [deps.env.ZED_CLI, "zed", "zeditor"];
|
|
@@ -85,14 +59,6 @@ function zedCommandCandidates() {
|
|
|
85
59
|
candidates.push("/opt/homebrew/bin/zed", "/usr/local/bin/zed");
|
|
86
60
|
return candidates.filter((candidate) => Boolean(candidate));
|
|
87
61
|
}
|
|
88
|
-
function commandCandidates(primary, ...rest) {
|
|
89
|
-
return [primary, ...rest].filter((candidate) => Boolean(candidate));
|
|
90
|
-
}
|
|
91
|
-
function jetbrainsTargetArgs(filePath, line) {
|
|
92
|
-
if (line === undefined)
|
|
93
|
-
return [filePath];
|
|
94
|
-
return ["--line", `${line}`, filePath];
|
|
95
|
-
}
|
|
96
62
|
function trySpawnCandidates(candidates, args) {
|
|
97
63
|
for (const command of candidates) {
|
|
98
64
|
if (!canRunCommand(command))
|
|
@@ -4,7 +4,7 @@ Local all-in-one Pi extension package.
|
|
|
4
4
|
|
|
5
5
|
This package keeps shared Pi tools as ordinary source folders under `src/` and registers them through one entrypoint.
|
|
6
6
|
|
|
7
|
-
- `src/coding-discipline` — injects a deduplicated silent-mode and quality-discipline block at the very top of the main-session per-turn system prompt for GLM main-session models only (`isGlmModel`) immediately before the LLM request; non-GLM models are left untouched; disabled for async sub-agents
|
|
7
|
+
- `src/coding-discipline` — injects a deduplicated silent-mode and quality-discipline block at the very top of the main-session per-turn system prompt for GLM main-session models only (`isGlmModel`) immediately before the LLM request; text-only GLM models get the `lookup` bridge while vision-capable `zai/glm-5.3-flash` inspects images directly; non-GLM models are left untouched; disabled for async sub-agents
|
|
8
8
|
- `src/credential-firewall` — opt-in secret firewall for high-confidence outbound/session credential redaction; disabled by default
|
|
9
9
|
- `src/ast-grep` — `ast_grep` / `ast_apply`
|
|
10
10
|
- `src/async-subagents` — `subagents` tool and sub-agent slash commands, including oh-my-openagent-style `/ultrawork` (`/ulw`) and `/hyperplan` orchestration prompts, plus config-defined sub-agent model/thinking/args presets selected via `/subagent-preset` from `asyncSubagents` in `~/.config/pi/pi-tools-suite.jsonc`; includes the `frontend` profile for Gemini-friendly UI/UX and visual frontend work and the `oracle` profile for cross-provider second opinions; enforces a 30-minute per-agent execution timeout, project-wide `maxConcurrent` queueing, optional retry/backoff, and `result.json` structured metadata/chaining fields next to raw `result.md`; stores project-local run files and a registry under `.pi/subagents/` so result/status collection can recover after compaction or reload while the main session remains alive
|
|
@@ -247,8 +247,8 @@ For an oh-my-openagent-style workflow, run `/ultrawork` or `/ulw` to ask the par
|
|
|
247
247
|
|
|
248
248
|
### Private browser QA and project auth
|
|
249
249
|
|
|
250
|
-
The built-in `browser-qa` role runs on `
|
|
251
|
-
`
|
|
250
|
+
The built-in `browser-qa` role runs on `zai/glm-5.3-flash`, with
|
|
251
|
+
`openai-codex/gpt-5.6-luna` as its fallback. Its browser
|
|
252
252
|
workflow is an explicit private skill under `src/async-subagents/private-skills/`,
|
|
253
253
|
outside normal Pi skill discovery. The role's first-class `isolatedSkills` setting launches the child with
|
|
254
254
|
`--no-skills` plus one self-contained private workflow. It bundles the relevant
|
|
@@ -316,9 +316,17 @@ parent to ask the user for an update and rerun. See
|
|
|
316
316
|
for complete profile shapes and `references/qa-flow.example.jsonc` beside it for
|
|
317
317
|
the declarative, non-executable QA action/assertion format.
|
|
318
318
|
|
|
319
|
+
Browser QA videos automatically visualize pointer interactions. Clicks and
|
|
320
|
+
double-clicks show a transient cursor and pulse. Native drag/drop is replayed
|
|
321
|
+
for 450 ms with a large orange cursor, progressively drawn high-contrast path,
|
|
322
|
+
and green drop marker. The isolated, pointer-transparent layer is installed for
|
|
323
|
+
the whole browser context, including same-origin frames, declared popups, and
|
|
324
|
+
form-auth submission, and clears during the normal post-action stable interval
|
|
325
|
+
so screenshots and assertions remain state-focused.
|
|
326
|
+
|
|
319
327
|
Async-subagents also injects a lightweight oh-my-openagent-style system-prompt strategy by model: non-GPT parents get `parallel-first`, an orchestration-first hint that favors ultrawork/subagents for broad work, while GPT-like parents get `deep-work`, a direct deep-worker hint that uses subagents only when clearly useful. Explicit custom system prompts (`--system-prompt`, `SYSTEM.md`, custom templates) are respected and skip this injection by default. Disable it with `PI_AGENT_STRATEGY=off`; force a strategy with `PI_AGENT_STRATEGY=parallel-first` or `PI_AGENT_STRATEGY=deep-work`; set `PI_AGENT_STRATEGY_WITH_CUSTOM_PROMPT=1` to append it even when a custom prompt is present.
|
|
320
328
|
|
|
321
|
-
For blind-model screenshot/image inspection, use the main-session `coding-discipline` lookup tool. Async-subagents still supports `imagePaths` on tasks when a broader delegated track genuinely needs images, but it no longer ships a dedicated `vision` role. Dynamic provider capabilities can be missing or stale after switching models, so blind parent models can still be configured explicitly with case-insensitive `*` masks under `asyncSubagents.vision.blindModelPatterns` in `~/.config/pi/pi-tools-suite.jsonc`;
|
|
329
|
+
For blind-model screenshot/image inspection, use the main-session `coding-discipline` lookup tool; the bundled default uses vision-capable `zai/glm-5.3-flash`. Async-subagents still supports `imagePaths` on tasks when a broader delegated track genuinely needs images, but it no longer ships a dedicated `vision` role. Dynamic provider capabilities can be missing or stale after switching models, so blind parent models can still be configured explicitly with case-insensitive `*` masks under `asyncSubagents.vision.blindModelPatterns` in `~/.config/pi/pi-tools-suite.jsonc`; do not include `zai/glm-5.3-flash` because it accepts image input. This keeps guidance honest, not a sub-agent role.
|
|
322
330
|
|
|
323
331
|
When a task omits `subagentType`, async-subagents asks a lightweight router model to choose one configured type for each task from the task text/scope and the `types.<name>.description` metadata. Explicit task `subagentType` still wins. Keep type descriptions short, literal, and distinct because they are inserted into the router prompt for a small model. Router settings live under `asyncSubagents.routing` (`enabled`, `model`, `maxTaskChars`, `maxTokens`, `maxRetries`, `timeoutMs`, `debug`); the default router model is `zai/glm-4.5-air`. If the router is disabled, unavailable, aborted, or returns invalid JSON, omitted types fall back to `defaultType`.
|
|
324
332
|
|
|
@@ -337,10 +345,11 @@ Example shared async-subagents config section:
|
|
|
337
345
|
},
|
|
338
346
|
"presets": {
|
|
339
347
|
"cheap": {
|
|
340
|
-
"description": "Use GLM for
|
|
348
|
+
"description": "Use GLM by role, including GLM-5.3 Flash for multimodal work.",
|
|
341
349
|
"types": {
|
|
342
350
|
"quick": { "model": "zai/glm-5.3", "thinking": "off" },
|
|
343
|
-
"frontend": { "model": "
|
|
351
|
+
"frontend": { "model": "zai/glm-5.3-flash", "thinking": "medium" },
|
|
352
|
+
"browser-qa": { "model": "zai/glm-5.3-flash", "fallbackModels": ["openai-codex/gpt-5.6-luna"], "thinking": "low" },
|
|
344
353
|
"review": { "model": "zai/glm-5.3", "thinking": "high" }
|
|
345
354
|
}
|
|
346
355
|
}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
Provide a cheap, fast `browser-qa` async-subagent that reproduces browser bugs
|
|
6
6
|
and proves fixes with deterministic assertions plus screenshot, video, and trace
|
|
7
|
-
evidence. The role uses `
|
|
8
|
-
`
|
|
7
|
+
evidence. The role uses `zai/glm-5.3-flash`, falling back to
|
|
8
|
+
`openai-codex/gpt-5.6-luna`.
|
|
9
9
|
|
|
10
10
|
## Private skill isolation
|
|
11
11
|
|
|
@@ -73,6 +73,20 @@ evidence. The role uses `openai-codex/gpt-5.6-luna`, falling back to
|
|
|
73
73
|
screenshots, video finalization, and redacted result output. Before retaining
|
|
74
74
|
a trace it removes network/non-image resource entries, redacts configured and
|
|
75
75
|
runtime storage credentials, and verifies those values are absent.
|
|
76
|
+
- After every navigation or visible interaction, the runner waits for DOM
|
|
77
|
+
readiness, completion of requests started by the action, and disappearance of
|
|
78
|
+
common visible busy/spinner/skeleton markers. It requires a 500 ms stable
|
|
79
|
+
interval before the next action so recordings remain readable; a page that
|
|
80
|
+
stays busy through the flow timeout fails closed instead of being tested as a
|
|
81
|
+
loading shell. App-specific readiness still requires an explicit declarative
|
|
82
|
+
wait/assertion in the authored flow.
|
|
83
|
+
- Before any page is created, the runner installs a context-wide, isolated
|
|
84
|
+
interaction visualizer. Recorded clicks/double-clicks show a transient cursor
|
|
85
|
+
and pulse. Native drag/drop is replayed for 450 ms with a large orange cursor,
|
|
86
|
+
progressively drawn high-contrast path, and green drop marker. It also covers
|
|
87
|
+
same-origin frames, declared popups, and form-auth submission. The layer is
|
|
88
|
+
accessibility-hidden, pointer-transparent, never cancels application events,
|
|
89
|
+
and its bounded animations clear within the post-action stable interval.
|
|
76
90
|
- Success and post-launch failure results include typed artifact groups. Every
|
|
77
91
|
item has an absolute filesystem path and a `file:` URI; the sub-agent must
|
|
78
92
|
present each item as a clickable Markdown link instead of reporting only the
|
|
@@ -112,7 +126,7 @@ evidence. The role uses `openai-codex/gpt-5.6-luna`, falling back to
|
|
|
112
126
|
|
|
113
127
|
1. `browser-qa` resolves to the intended model/fallback and its self-contained
|
|
114
128
|
private workflow, and its isolated child process can register the configured
|
|
115
|
-
|
|
129
|
+
model provider.
|
|
116
130
|
2. Spawn args contain `--no-skills` and the mandatory private skill for this
|
|
117
131
|
profile; ordinary profiles retain existing skill discovery behavior.
|
|
118
132
|
3. Auth profile listing and all error output are redacted; model-authored input
|
|
@@ -123,12 +137,16 @@ evidence. The role uses `openai-codex/gpt-5.6-luna`, falling back to
|
|
|
123
137
|
flows, and successful redacted evidence creation.
|
|
124
138
|
5. Browser QA flows/evidence live only inside the owning sub-agent directory;
|
|
125
139
|
deleting the run removes them while persistent auth config/state remains.
|
|
126
|
-
6.
|
|
140
|
+
6. Runner tests prove that network activity and visible loading indicators are
|
|
141
|
+
awaited, persistent loading fails the flow, visible actions retain a stable
|
|
142
|
+
500 ms video interval, and context-wide click/drag video visualization is
|
|
143
|
+
installed with bounded click pacing.
|
|
144
|
+
7. Completed test runs report clickable screenshot, video, and trace links
|
|
127
145
|
whenever those artifacts exist.
|
|
128
|
-
|
|
146
|
+
8. Timeout tests identify the last browser stage, launcher progress remains
|
|
129
147
|
available when full RPC logging is disabled, and process-tree tests prove a
|
|
130
148
|
descendant is terminated without signalling unrelated processes.
|
|
131
|
-
|
|
149
|
+
9. Suite tests/typecheck, host checks, and suite sync pass.
|
|
132
150
|
|
|
133
151
|
## Real-browser regression test
|
|
134
152
|
|
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
"vscode-languageserver-protocol": "^3.17.5"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
|
-
"@earendil-works/pi-ai": "0.84.
|
|
48
|
-
"@earendil-works/pi-coding-agent": "0.84.
|
|
49
|
-
"@earendil-works/pi-tui": "0.84.
|
|
47
|
+
"@earendil-works/pi-ai": "0.84.4",
|
|
48
|
+
"@earendil-works/pi-coding-agent": "0.84.4",
|
|
49
|
+
"@earendil-works/pi-tui": "0.84.4",
|
|
50
50
|
"typebox": "*"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// Global async-subagents routing.
|
|
6
6
|
// Rule of thumb:
|
|
7
7
|
// - GLM: cheap/fast discovery, grep-style scanning, docs/test inventory.
|
|
8
|
-
// -
|
|
8
|
+
// - GLM-5.3 Flash: frontend/UI/UX, visual/product polish, and multimodal-heavy analysis.
|
|
9
9
|
// - GPT/Claude: review/audit, deep reasoning, tests, and implementation/refactoring.
|
|
10
10
|
// Override exact model IDs here if your pi provider uses different names.
|
|
11
11
|
"defaultType": "quick",
|
|
@@ -34,7 +34,11 @@
|
|
|
34
34
|
// screenshot inspection should use lookup; this only keeps guidance honest.
|
|
35
35
|
// Matching is case-insensitive and supports `*` wildcards.
|
|
36
36
|
"vision": {
|
|
37
|
-
"blindModelPatterns": [
|
|
37
|
+
"blindModelPatterns": [
|
|
38
|
+
"zai/glm-4.5*", "glm-4.5*", "*/glm-4.5*",
|
|
39
|
+
"zai/glm-5-turbo*", "glm-5-turbo*", "*/glm-5-turbo*",
|
|
40
|
+
"zai/glm-5.3", "glm-5.3", "*/glm-5.3"
|
|
41
|
+
]
|
|
38
42
|
},
|
|
39
43
|
|
|
40
44
|
// Optional per-type prompt customization (leave commented until needed):
|
|
@@ -109,14 +113,14 @@
|
|
|
109
113
|
|
|
110
114
|
"presets": {
|
|
111
115
|
"cheap": {
|
|
112
|
-
"description": "Use
|
|
116
|
+
"description": "Use GLM models by role, including GLM-5.3 Flash for multimodal work.",
|
|
113
117
|
"types": {
|
|
114
118
|
"quick": { "model": "zai/glm-4.5-air", "thinking": "off" },
|
|
115
119
|
"scan": { "model": "zai/glm-4.5-air", "thinking": "off" },
|
|
116
120
|
"research": { "model": "zai/glm-5-turbo", "thinking": "low" },
|
|
117
121
|
"docs": { "model": "zai/glm-4.5-air", "thinking": "low" },
|
|
118
|
-
"frontend": { "model": "
|
|
119
|
-
"browser-qa": { "model": "
|
|
122
|
+
"frontend": { "model": "zai/glm-5.3-flash", "thinking": "medium" },
|
|
123
|
+
"browser-qa": { "model": "zai/glm-5.3-flash", "fallbackModels": ["openai-codex/gpt-5.6-luna"], "thinking": "low" },
|
|
120
124
|
"tests": { "model": "zai/glm-5-turbo", "thinking": "medium" },
|
|
121
125
|
"review": { "model": "zai/glm-5.3", "thinking": "high" },
|
|
122
126
|
"implement": { "model": "zai/glm-5.3", "thinking": "high" },
|
|
@@ -132,7 +136,7 @@
|
|
|
132
136
|
"research": { "model": "openai-codex/gpt-5.6-terra", "fallbackModels": ["zai/glm-5-turbo"], "thinking": "low" },
|
|
133
137
|
"docs": { "model": "openai-codex/gpt-5.6-luna", "fallbackModels": ["zai/glm-4.5-air"], "thinking": "low" },
|
|
134
138
|
"frontend": { "model": "openai-codex/gpt-5.6-terra", "fallbackModels": ["antigravity/gemini-3-flash-preview", "zai/glm-5.3"], "thinking": "medium" },
|
|
135
|
-
"browser-qa": { "model": "
|
|
139
|
+
"browser-qa": { "model": "zai/glm-5.3-flash", "fallbackModels": ["openai-codex/gpt-5.6-luna"], "thinking": "low" },
|
|
136
140
|
"tests": { "model": "openai-codex/gpt-5.6-terra", "fallbackModels": ["zai/glm-5-turbo"], "thinking": "medium" },
|
|
137
141
|
"review": { "model": "openai-codex/gpt-5.6-sol", "fallbackModels": ["zai/glm-5.3"], "thinking": "high" },
|
|
138
142
|
"implement": { "model": "openai-codex/gpt-5.6-sol", "fallbackModels": ["zai/glm-5.3"], "thinking": "high" },
|
|
@@ -148,7 +152,7 @@
|
|
|
148
152
|
"research": { "model": "antigravity/gemini-3.1-pro-preview", "fallbackModels": ["openai-codex/gpt-5.6-luna", "zai/glm-5-turbo"], "thinking": "medium" },
|
|
149
153
|
"docs": { "model": "antigravity/gemini-2.5-flash", "fallbackModels": ["openai-codex/gpt-5.6-luna", "zai/glm-4.5-air"], "thinking": "medium" },
|
|
150
154
|
"frontend": { "model": "antigravity/gemini-3.1-pro-preview-customtools", "fallbackModels": ["openai-codex/gpt-5.6-luna", "zai/glm-5.3"], "thinking": "low" },
|
|
151
|
-
"browser-qa": { "model": "
|
|
155
|
+
"browser-qa": { "model": "zai/glm-5.3-flash", "fallbackModels": ["openai-codex/gpt-5.6-luna"], "thinking": "low" },
|
|
152
156
|
"tests": { "model": "antigravity/antigravity-claude-sonnet-4-6", "fallbackModels": ["openai-codex/gpt-5.6-luna", "zai/glm-5-turbo"], "thinking": "high" },
|
|
153
157
|
"review": { "model": "antigravity/antigravity-claude-sonnet-4-6", "fallbackModels": ["openai-codex/gpt-5.6-sol", "zai/glm-5.3"], "thinking": "high" },
|
|
154
158
|
"implement": { "model": "openai-codex/gpt-5.6-sol", "fallbackModels": ["zai/glm-5.3"], "thinking": "high" },
|
|
@@ -203,8 +207,8 @@
|
|
|
203
207
|
|
|
204
208
|
"browser-qa": {
|
|
205
209
|
"description": "Use for browser-based visual QA: reproduce UI bugs and verify fixes with deterministic assertions, screenshots, video, and traces.",
|
|
206
|
-
"model": "
|
|
207
|
-
"fallbackModels": ["
|
|
210
|
+
"model": "zai/glm-5.3-flash",
|
|
211
|
+
"fallbackModels": ["openai-codex/gpt-5.6-luna"],
|
|
208
212
|
"thinking": "low",
|
|
209
213
|
"timeoutMs": 120000,
|
|
210
214
|
"tools": ["read", "grep", "bash"]
|
|
@@ -215,8 +215,8 @@ const BUILTIN_CONFIG: SubagentConfig = {
|
|
|
215
215
|
},
|
|
216
216
|
"browser-qa": {
|
|
217
217
|
description: "Use for browser-based visual QA: reproduce UI bugs and verify fixes with deterministic assertions, screenshots, video, and traces.",
|
|
218
|
-
model: "
|
|
219
|
-
fallbackModels: ["
|
|
218
|
+
model: "zai/glm-5.3-flash",
|
|
219
|
+
fallbackModels: ["openai-codex/gpt-5.6-luna"],
|
|
220
220
|
thinking: "low",
|
|
221
221
|
timeoutMs: 120_000,
|
|
222
222
|
tools: ["read", "grep", "bash"],
|
|
@@ -88,15 +88,18 @@ export function spawnAgent(
|
|
|
88
88
|
// detached subprocesses do not depend on persisted local pi settings.
|
|
89
89
|
const persistSessions = shouldPersistSubagentSessions();
|
|
90
90
|
const sessionDir = persistSessions ? getAgentSessionDir(agentDir) : undefined;
|
|
91
|
+
const forwardedExtraArgs = options.isolatedSkills?.length ? withoutSkillArgs(extraArgs) : extraArgs;
|
|
91
92
|
if (sessionDir) fs.mkdirSync(sessionDir, { recursive: true });
|
|
92
93
|
const piArgs: string[] = ["--mode", "rpc"];
|
|
93
94
|
if (sessionDir) piArgs.push("--session-dir", sessionDir);
|
|
94
95
|
else piArgs.push("--no-session");
|
|
95
96
|
piArgs.push("--no-extensions");
|
|
96
97
|
piArgs.push("--extension", getModelToolsExtensionPath());
|
|
97
|
-
// `--no-extensions`
|
|
98
|
-
//
|
|
99
|
-
|
|
98
|
+
// Preserve `--no-extensions` unless this invocation explicitly selects an
|
|
99
|
+
// Antigravity model. Environment/default models do not opt the provider in.
|
|
100
|
+
if (usesAntigravityModel(task.model, forwardedExtraArgs)) {
|
|
101
|
+
piArgs.push("--extension", getAntigravityAuthExtensionPath());
|
|
102
|
+
}
|
|
100
103
|
if (options.isolatedSkills && options.isolatedSkills.length > 0) {
|
|
101
104
|
piArgs.push("--no-skills");
|
|
102
105
|
for (const skillPath of options.isolatedSkills) piArgs.push("--skill", skillPath);
|
|
@@ -111,7 +114,7 @@ export function spawnAgent(
|
|
|
111
114
|
if (task.thinking) piArgs.push("--thinking", task.thinking);
|
|
112
115
|
|
|
113
116
|
// User-supplied extra args (e.g. --thinking high)
|
|
114
|
-
piArgs.push(...
|
|
117
|
+
piArgs.push(...forwardedExtraArgs);
|
|
115
118
|
// Keep recursive/interactive parent-only tools disabled even if explicit
|
|
116
119
|
// sub-agent extraArgs load additional extensions or override --tools.
|
|
117
120
|
piArgs.push("--extension", getSubagentToolGuardExtensionPath());
|
|
@@ -470,6 +473,22 @@ function withoutSkillArgs(args: string[]): string[] {
|
|
|
470
473
|
return filtered;
|
|
471
474
|
}
|
|
472
475
|
|
|
476
|
+
function usesAntigravityModel(taskModel: string | undefined, extraArgs: string[]): boolean {
|
|
477
|
+
let model = taskModel?.trim() || undefined;
|
|
478
|
+
for (let index = 0; index < extraArgs.length; index += 1) {
|
|
479
|
+
const arg = extraArgs[index];
|
|
480
|
+
if (arg === "--model" || arg === "-m") {
|
|
481
|
+
model = extraArgs[index + 1]?.trim() || undefined;
|
|
482
|
+
index += 1;
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
if (arg.startsWith("--model=")) {
|
|
486
|
+
model = arg.slice("--model=".length).trim() || undefined;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return Boolean(model?.startsWith("antigravity/") && model.length > "antigravity/".length);
|
|
490
|
+
}
|
|
491
|
+
|
|
473
492
|
function getModelToolsExtensionPath(): string {
|
|
474
493
|
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "model-tools", "index.ts");
|
|
475
494
|
}
|