@opengsd/gsd-pi 1.0.2-dev.5961fbf → 1.0.2-dev.5f7864c
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 +63 -12
- package/dist/onboarding.js +22 -3
- package/dist/resource-loader.d.ts +2 -0
- package/dist/resource-loader.js +18 -1
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/context7/index.js +12 -2
- package/dist/resources/extensions/get-secrets-from-user.js +16 -16
- package/dist/resources/extensions/google-cli/index.js +30 -0
- package/dist/resources/extensions/google-cli/models.js +55 -0
- package/dist/resources/extensions/google-cli/package.json +11 -0
- package/dist/resources/extensions/google-cli/readiness.js +12 -0
- package/dist/resources/extensions/google-cli/stream-adapter.js +191 -0
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto-start.js +232 -49
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -3
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -15
- package/dist/resources/extensions/gsd/closeout-recovery.js +7 -1
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +9 -1
- package/dist/resources/extensions/gsd/commands-handlers.js +3 -0
- package/dist/resources/extensions/gsd/commands-usage.js +105 -1
- package/dist/resources/extensions/gsd/config-overlay.js +20 -14
- package/dist/resources/extensions/gsd/context-overlay.js +22 -16
- package/dist/resources/extensions/gsd/dashboard-overlay.js +10 -23
- package/dist/resources/extensions/gsd/doctor-providers.js +54 -24
- package/dist/resources/extensions/gsd/git-conflict-state.js +26 -1
- package/dist/resources/extensions/gsd/guided-flow.js +1 -1
- package/dist/resources/extensions/gsd/key-manager.js +45 -13
- package/dist/resources/extensions/gsd/notification-overlay.js +8 -9
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +15 -13
- package/dist/resources/extensions/gsd/prompt-loader.js +2 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +4 -2
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
- package/dist/resources/extensions/gsd/queue-reorder-ui.js +28 -18
- package/dist/resources/extensions/gsd/tools/complete-task.js +9 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +40 -1
- package/dist/resources/extensions/gsd/tui/render-kit.js +51 -0
- package/dist/resources/extensions/gsd/vision-ask.js +22 -0
- package/dist/resources/extensions/gsd/visualizer-overlay.js +8 -36
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -3
- package/dist/resources/extensions/search-the-web/native-search.js +57 -8
- package/dist/resources/extensions/shared/confirm-ui.js +9 -6
- package/dist/resources/extensions/shared/dialog-frame.js +42 -0
- package/dist/resources/extensions/shared/interview-ui.js +42 -30
- package/dist/resources/extensions/shared/next-action-ui.js +6 -6
- package/dist/resources/shared/package-manager-detection.js +36 -0
- package/dist/update-check.d.ts +6 -2
- package/dist/update-check.js +7 -3
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
- package/dist/web/standalone/.next/server/chunks/1834.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts +12 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js +45 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts +3 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js +11 -11
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts +3 -3
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js +13 -11
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts +3 -3
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js +12 -10
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js +2 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts +6 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js +9 -6
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts +3 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +144 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js +2 -14
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/workflow-tools.js +1 -1
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +13 -13
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +57 -17
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +64 -28
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +50 -0
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +2 -0
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/scripts/install/detect-existing.js +17 -3
- package/scripts/install/npm-global.js +103 -33
- package/scripts/install.js +1 -0
- package/src/resources/extensions/context7/index.ts +15 -2
- package/src/resources/extensions/get-secrets-from-user.ts +17 -16
- package/src/resources/extensions/google-cli/index.ts +34 -0
- package/src/resources/extensions/google-cli/models.ts +57 -0
- package/src/resources/extensions/google-cli/package.json +11 -0
- package/src/resources/extensions/google-cli/readiness.ts +15 -0
- package/src/resources/extensions/google-cli/stream-adapter.ts +245 -0
- package/src/resources/extensions/gsd/auto/session.ts +3 -0
- package/src/resources/extensions/gsd/auto-start.ts +307 -56
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -3
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -15
- package/src/resources/extensions/gsd/closeout-recovery.ts +6 -1
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -1
- package/src/resources/extensions/gsd/commands-handlers.ts +2 -0
- package/src/resources/extensions/gsd/commands-usage.ts +110 -5
- package/src/resources/extensions/gsd/config-overlay.ts +19 -16
- package/src/resources/extensions/gsd/context-overlay.ts +24 -19
- package/src/resources/extensions/gsd/dashboard-overlay.ts +14 -27
- package/src/resources/extensions/gsd/doctor-providers.ts +55 -27
- package/src/resources/extensions/gsd/git-conflict-state.ts +25 -1
- package/src/resources/extensions/gsd/guided-flow.ts +1 -1
- package/src/resources/extensions/gsd/key-manager.ts +57 -14
- package/src/resources/extensions/gsd/notification-overlay.ts +12 -11
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +16 -12
- package/src/resources/extensions/gsd/prompt-loader.ts +2 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +4 -2
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
- package/src/resources/extensions/gsd/queue-reorder-ui.ts +29 -20
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +436 -0
- package/src/resources/extensions/gsd/tests/closeout-recovery.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/commands-context.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +15 -2
- package/src/resources/extensions/gsd/tests/commands-usage.test.ts +97 -0
- package/src/resources/extensions/gsd/tests/context-chart.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +105 -0
- package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +23 -4
- package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +70 -10
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/show-config-command.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +13 -2
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +24 -1
- package/src/resources/extensions/gsd/tests/tui-border-assertions.ts +28 -0
- package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/vision-ask.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +16 -1
- package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +45 -1
- package/src/resources/extensions/gsd/tools/complete-task.ts +9 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +56 -4
- package/src/resources/extensions/gsd/tui/render-kit.ts +82 -0
- package/src/resources/extensions/gsd/vision-ask.ts +28 -0
- package/src/resources/extensions/gsd/visualizer-overlay.ts +12 -40
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +37 -2
- package/src/resources/extensions/search-the-web/native-search.ts +60 -8
- package/src/resources/extensions/shared/confirm-ui.ts +8 -12
- package/src/resources/extensions/shared/dialog-frame.ts +71 -0
- package/src/resources/extensions/shared/interview-ui.ts +43 -42
- package/src/resources/extensions/shared/next-action-ui.ts +6 -6
- package/src/resources/extensions/shared/tests/confirm-ui.test.ts +57 -0
- package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +163 -0
- package/src/resources/extensions/shared/tests/next-action-ui-hasui.test.ts +55 -0
- package/src/resources/shared/package-manager-detection.ts +39 -0
- /package/dist/web/standalone/.next/static/{spUYLkQXoHJyxYOMH9VQy → IjxvcC7sl_MHNKXsUZrAy}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{spUYLkQXoHJyxYOMH9VQy → IjxvcC7sl_MHNKXsUZrAy}/_ssgManifest.js +0 -0
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
* Shows current LLM context window usage and session token totals.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { ExtensionCommandContext, ContextUsage, SessionEntry } from "@gsd/pi-coding-agent";
|
|
7
|
+
import type { ExtensionCommandContext, ContextUsage, SessionEntry, Theme } from "@gsd/pi-coding-agent";
|
|
8
|
+
import { Key, matchesKey } from "@gsd/pi-tui";
|
|
8
9
|
|
|
9
10
|
import { formatCost, formatPercent, formatTokenCount } from "./metrics.js";
|
|
10
11
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
12
|
+
import { renderDialogFrame, renderKeyHints } from "./tui/render-kit.js";
|
|
11
13
|
|
|
12
14
|
export interface SessionTokenTotals {
|
|
13
15
|
input: number;
|
|
@@ -160,6 +162,101 @@ export function formatUsageReport(options: {
|
|
|
160
162
|
return lines.join("\n");
|
|
161
163
|
}
|
|
162
164
|
|
|
165
|
+
async function showUsageDialog(
|
|
166
|
+
ctx: ExtensionCommandContext,
|
|
167
|
+
reportText: string,
|
|
168
|
+
): Promise<boolean | undefined> {
|
|
169
|
+
return ctx.ui.custom<boolean>((tui, theme: Theme, _kb, done) => {
|
|
170
|
+
let cachedLines: string[] | undefined;
|
|
171
|
+
let cachedWidth: number | undefined;
|
|
172
|
+
let cachedRows: number | undefined;
|
|
173
|
+
let cachedScrollOffset: number | undefined;
|
|
174
|
+
let scrollOffset = 0;
|
|
175
|
+
let lastMaxScroll = 0;
|
|
176
|
+
let lastVisibleRows = 1;
|
|
177
|
+
|
|
178
|
+
function render(width: number): string[] {
|
|
179
|
+
const terminalRows = process.stdout.rows || 0;
|
|
180
|
+
if (
|
|
181
|
+
cachedLines &&
|
|
182
|
+
cachedWidth === width &&
|
|
183
|
+
cachedRows === terminalRows &&
|
|
184
|
+
cachedScrollOffset === scrollOffset
|
|
185
|
+
) {
|
|
186
|
+
return cachedLines;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const contentWidth = Math.max(1, width - 4);
|
|
190
|
+
const body = reportText.split("\n");
|
|
191
|
+
if (body[0] === "Context Usage") body.shift();
|
|
192
|
+
while (body[0] === "") body.shift();
|
|
193
|
+
const maxOverlayRows = terminalRows > 0 ? Math.max(5, Math.floor(terminalRows * 0.8)) : 24;
|
|
194
|
+
const frameRows = 4;
|
|
195
|
+
const visibleRows = Math.max(1, maxOverlayRows - frameRows);
|
|
196
|
+
const maxScroll = Math.max(0, body.length - visibleRows);
|
|
197
|
+
scrollOffset = Math.min(Math.max(scrollOffset, 0), maxScroll);
|
|
198
|
+
lastMaxScroll = maxScroll;
|
|
199
|
+
lastVisibleRows = visibleRows;
|
|
200
|
+
const visible = body.slice(scrollOffset, scrollOffset + visibleRows);
|
|
201
|
+
const scrollable = body.length > visibleRows;
|
|
202
|
+
|
|
203
|
+
cachedLines = renderDialogFrame(theme, "Context Usage", visible, width, {
|
|
204
|
+
footer: renderKeyHints(theme, scrollable ? ["↑↓ scroll", "any key close"] : ["any key close"], contentWidth),
|
|
205
|
+
scroll: { offset: scrollOffset, visibleRows, totalRows: body.length },
|
|
206
|
+
});
|
|
207
|
+
cachedWidth = width;
|
|
208
|
+
cachedRows = terminalRows;
|
|
209
|
+
cachedScrollOffset = scrollOffset;
|
|
210
|
+
return cachedLines;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function scrollBy(delta: number): boolean {
|
|
214
|
+
if (lastMaxScroll <= 0) return false;
|
|
215
|
+
const nextOffset = Math.min(Math.max(scrollOffset + delta, 0), lastMaxScroll);
|
|
216
|
+
if (nextOffset !== scrollOffset) {
|
|
217
|
+
scrollOffset = nextOffset;
|
|
218
|
+
cachedLines = undefined;
|
|
219
|
+
cachedScrollOffset = undefined;
|
|
220
|
+
tui.requestRender();
|
|
221
|
+
}
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
render,
|
|
227
|
+
invalidate: () => {
|
|
228
|
+
cachedLines = undefined;
|
|
229
|
+
cachedWidth = undefined;
|
|
230
|
+
cachedRows = undefined;
|
|
231
|
+
cachedScrollOffset = undefined;
|
|
232
|
+
},
|
|
233
|
+
handleInput: (data: string) => {
|
|
234
|
+
if (matchesKey(data, Key.down) || matchesKey(data, "j")) {
|
|
235
|
+
if (scrollBy(1)) return;
|
|
236
|
+
}
|
|
237
|
+
if (matchesKey(data, Key.up) || matchesKey(data, "k")) {
|
|
238
|
+
if (scrollBy(-1)) return;
|
|
239
|
+
}
|
|
240
|
+
if (matchesKey(data, Key.pageDown)) {
|
|
241
|
+
if (scrollBy(lastVisibleRows)) return;
|
|
242
|
+
}
|
|
243
|
+
if (matchesKey(data, Key.pageUp)) {
|
|
244
|
+
if (scrollBy(-lastVisibleRows)) return;
|
|
245
|
+
}
|
|
246
|
+
done(true);
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
}, {
|
|
250
|
+
overlay: true,
|
|
251
|
+
overlayOptions: {
|
|
252
|
+
width: "70%",
|
|
253
|
+
minWidth: 64,
|
|
254
|
+
maxHeight: "80%",
|
|
255
|
+
anchor: "center",
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
163
260
|
export async function handleUsage(args: string, ctx: ExtensionCommandContext): Promise<void> {
|
|
164
261
|
const contextUsage = ctx.getContextUsage?.();
|
|
165
262
|
const sessionTotals = scanSessionTokenTotals(ctx.sessionManager.getEntries());
|
|
@@ -187,8 +284,16 @@ export async function handleUsage(args: string, ctx: ExtensionCommandContext): P
|
|
|
187
284
|
return;
|
|
188
285
|
}
|
|
189
286
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
287
|
+
const reportText = formatUsageReport({ modelLabel, contextUsage, sessionTotals });
|
|
288
|
+
|
|
289
|
+
if (ctx.hasUI) {
|
|
290
|
+
try {
|
|
291
|
+
const result = await showUsageDialog(ctx, reportText);
|
|
292
|
+
if (result !== undefined) return;
|
|
293
|
+
} catch {
|
|
294
|
+
// Fall back to text notify below when custom overlays are unavailable.
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
ctx.ui.notify(reportText, "info");
|
|
194
299
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import type { Theme } from "@gsd/pi-coding-agent";
|
|
11
11
|
import { matchesKey, Key, truncateToWidth } from "@gsd/pi-tui";
|
|
12
12
|
|
|
13
|
+
import { renderDialogFrame, renderKeyHints } from "./tui/render-kit.js";
|
|
13
14
|
import {
|
|
14
15
|
loadEffectiveGSDPreferences,
|
|
15
16
|
loadGlobalGSDPreferences,
|
|
@@ -231,6 +232,7 @@ export class GSDConfigOverlay {
|
|
|
231
232
|
private onClose: () => void;
|
|
232
233
|
private sections: ConfigSection[];
|
|
233
234
|
private cachedLines?: string[];
|
|
235
|
+
private cachedWidth?: number;
|
|
234
236
|
private scrollOffset = 0;
|
|
235
237
|
private disposed = false;
|
|
236
238
|
|
|
@@ -247,6 +249,7 @@ export class GSDConfigOverlay {
|
|
|
247
249
|
|
|
248
250
|
invalidate(): void {
|
|
249
251
|
this.cachedLines = undefined;
|
|
252
|
+
this.cachedWidth = undefined;
|
|
250
253
|
}
|
|
251
254
|
|
|
252
255
|
dispose(): void {
|
|
@@ -286,16 +289,13 @@ export class GSDConfigOverlay {
|
|
|
286
289
|
}
|
|
287
290
|
|
|
288
291
|
render(width: number): string[] {
|
|
289
|
-
if (this.cachedLines) return this.cachedLines;
|
|
292
|
+
if (this.cachedLines && this.cachedWidth === width) return this.cachedLines;
|
|
290
293
|
|
|
291
294
|
const t = this.theme;
|
|
292
|
-
const w = Math.max(
|
|
295
|
+
const w = Math.max(1, width);
|
|
296
|
+
const contentWidth = Math.max(1, w - 4);
|
|
293
297
|
const allLines: string[] = [];
|
|
294
298
|
|
|
295
|
-
// Header
|
|
296
|
-
allLines.push(t.bold(t.fg("accent", " GSD Configuration ")));
|
|
297
|
-
allLines.push(t.fg("muted", "\u2500".repeat(w)));
|
|
298
|
-
|
|
299
299
|
// Find max label width for alignment
|
|
300
300
|
let maxLabel = 0;
|
|
301
301
|
for (const section of this.sections) {
|
|
@@ -306,26 +306,29 @@ export class GSDConfigOverlay {
|
|
|
306
306
|
const labelPad = Math.min(maxLabel + 2, 24);
|
|
307
307
|
|
|
308
308
|
for (const section of this.sections) {
|
|
309
|
-
allLines.push("");
|
|
309
|
+
if (allLines.length > 0) allLines.push("");
|
|
310
310
|
allLines.push(t.bold(t.fg("accent", ` ${section.title}`)));
|
|
311
311
|
|
|
312
312
|
for (const row of section.rows) {
|
|
313
313
|
const label = t.fg("muted", ` ${row.label.padEnd(labelPad)}`);
|
|
314
314
|
const value = row.accent ? t.bold(row.value) : row.value;
|
|
315
|
-
allLines.push(truncateToWidth(`${label}${value}`,
|
|
315
|
+
allLines.push(truncateToWidth(`${label}${value}`, contentWidth));
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
-
allLines.push("");
|
|
320
|
-
allLines.push(t.fg("muted", ` ${"\u2500".repeat(w - 4)}`));
|
|
321
|
-
allLines.push(t.fg("muted", " esc/q close \u2502 \u2191\u2193/jk scroll \u2502 /gsd prefs to edit"));
|
|
322
|
-
|
|
323
319
|
// Apply scroll
|
|
324
|
-
const
|
|
320
|
+
const terminalRows = process.stdout.rows || 32;
|
|
321
|
+
const maxBodyRows = Math.max(1, Math.min(allLines.length, terminalRows - 12));
|
|
322
|
+
const maxScroll = Math.max(0, allLines.length - maxBodyRows);
|
|
325
323
|
this.scrollOffset = Math.min(this.scrollOffset, maxScroll);
|
|
326
|
-
const visible = allLines.slice(this.scrollOffset);
|
|
324
|
+
const visible = allLines.slice(this.scrollOffset, this.scrollOffset + maxBodyRows);
|
|
325
|
+
const footer = renderKeyHints(t, ["esc/q close", "\u2191\u2193/jk scroll", "/gsd prefs to edit"], contentWidth);
|
|
327
326
|
|
|
328
|
-
this.cachedLines = visible
|
|
329
|
-
|
|
327
|
+
this.cachedLines = renderDialogFrame(t, "GSD Configuration", visible, w, {
|
|
328
|
+
footer,
|
|
329
|
+
scroll: { offset: this.scrollOffset, visibleRows: maxBodyRows, totalRows: allLines.length },
|
|
330
|
+
});
|
|
331
|
+
this.cachedWidth = width;
|
|
332
|
+
return this.cachedLines;
|
|
330
333
|
}
|
|
331
334
|
}
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { Theme, ThemeColor } from "@gsd/pi-coding-agent";
|
|
6
|
-
import { matchesKey, Key, truncateToWidth
|
|
6
|
+
import { matchesKey, Key, truncateToWidth } from "@gsd/pi-tui";
|
|
7
7
|
|
|
8
8
|
import type { ContextBreakdownReport, ContextSectionBreakdown } from "./commands-context.js";
|
|
9
9
|
import { formatTokenCount } from "./metrics.js";
|
|
10
|
-
import { renderProgressBar, rightAlign } from "./tui/render-kit.js";
|
|
10
|
+
import { renderDialogFrame, renderKeyHints, renderProgressBar, rightAlign } from "./tui/render-kit.js";
|
|
11
11
|
|
|
12
12
|
const SECTION_COLORS: ThemeColor[] = ["accent", "success", "warning", "borderAccent", "text"];
|
|
13
13
|
|
|
@@ -70,6 +70,7 @@ export class GSDContextOverlay {
|
|
|
70
70
|
private onClose: () => void;
|
|
71
71
|
private report: ContextBreakdownReport;
|
|
72
72
|
private cachedLines?: string[];
|
|
73
|
+
private cachedWidth?: number;
|
|
73
74
|
private scrollOffset = 0;
|
|
74
75
|
private disposed = false;
|
|
75
76
|
|
|
@@ -87,6 +88,7 @@ export class GSDContextOverlay {
|
|
|
87
88
|
|
|
88
89
|
invalidate(): void {
|
|
89
90
|
this.cachedLines = undefined;
|
|
91
|
+
this.cachedWidth = undefined;
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
dispose(): void {
|
|
@@ -125,24 +127,22 @@ export class GSDContextOverlay {
|
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
render(width: number): string[] {
|
|
128
|
-
if (this.cachedLines) return this.cachedLines;
|
|
130
|
+
if (this.cachedLines && this.cachedWidth === width) return this.cachedLines;
|
|
129
131
|
|
|
130
132
|
const theme = this.theme;
|
|
131
|
-
const w = Math.max(
|
|
133
|
+
const w = Math.max(1, width);
|
|
134
|
+
const contentWidth = Math.max(1, w - 4);
|
|
132
135
|
const totals = getContextChartTotals(this.report);
|
|
133
136
|
const chartTotal = Math.max(totals.inContext, totals.estimated, 1);
|
|
134
137
|
const lines: string[] = [];
|
|
135
138
|
|
|
136
|
-
lines.push(theme.bold(theme.fg("accent", " Context Breakdown ")));
|
|
137
|
-
lines.push(theme.fg("muted", "─".repeat(w)));
|
|
138
|
-
|
|
139
139
|
if (this.report.modelLabel) {
|
|
140
|
-
lines.push(rightAlign(theme.fg("muted", "Model"), theme.fg("text", this.report.modelLabel),
|
|
140
|
+
lines.push(rightAlign(theme.fg("muted", "Model"), theme.fg("text", this.report.modelLabel), contentWidth));
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
lines.push("");
|
|
144
144
|
if (totals.window != null) {
|
|
145
|
-
const usageBarWidth = Math.max(
|
|
145
|
+
const usageBarWidth = Math.max(8, contentWidth - 28);
|
|
146
146
|
const usageBar = renderProgressBar(theme, totals.inContext, totals.window, usageBarWidth, {
|
|
147
147
|
filledColor: totals.inContext / totals.window > 0.85 ? "warning" : "accent",
|
|
148
148
|
});
|
|
@@ -151,7 +151,7 @@ export class GSDContextOverlay {
|
|
|
151
151
|
lines.push(` ${theme.fg("muted", "Estimated")} ${theme.fg("text", formatTokenCount(chartTotal))}`);
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
const splitWidth = Math.max(
|
|
154
|
+
const splitWidth = Math.max(8, Math.floor((contentWidth - 8) / 2));
|
|
155
155
|
const systemBar = renderProgressBar(theme, totals.systemTokens, chartTotal, splitWidth, { filledColor: "accent" });
|
|
156
156
|
const convBar = renderProgressBar(theme, totals.conversationTokens, chartTotal, splitWidth, { filledColor: "success" });
|
|
157
157
|
lines.push("");
|
|
@@ -160,11 +160,11 @@ export class GSDContextOverlay {
|
|
|
160
160
|
|
|
161
161
|
lines.push("");
|
|
162
162
|
lines.push(theme.bold(theme.fg("accent", " System prompt")));
|
|
163
|
-
lines.push(...renderSectionBars(theme, this.report.systemSections, chartTotal,
|
|
163
|
+
lines.push(...renderSectionBars(theme, this.report.systemSections, chartTotal, contentWidth, 22));
|
|
164
164
|
|
|
165
165
|
lines.push("");
|
|
166
166
|
lines.push(theme.bold(theme.fg("accent", " Conversation")));
|
|
167
|
-
lines.push(...renderSectionBars(theme, this.report.conversationSections, chartTotal,
|
|
167
|
+
lines.push(...renderSectionBars(theme, this.report.conversationSections, chartTotal, contentWidth, 22));
|
|
168
168
|
|
|
169
169
|
lines.push("");
|
|
170
170
|
lines.push(theme.bold(theme.fg("accent", " Skills")));
|
|
@@ -172,7 +172,7 @@ export class GSDContextOverlay {
|
|
|
172
172
|
if (skills.available.length > 0) {
|
|
173
173
|
lines.push(` ${theme.fg("muted", "Available")} ${theme.fg("text", `${skills.available.length}`)}`);
|
|
174
174
|
const preview = skills.available.slice(0, 8).join(", ");
|
|
175
|
-
lines.push(truncateToWidth(` ${preview}${skills.available.length > 8 ? "…" : ""}`,
|
|
175
|
+
lines.push(truncateToWidth(` ${preview}${skills.available.length > 8 ? "…" : ""}`, contentWidth));
|
|
176
176
|
} else {
|
|
177
177
|
lines.push(theme.fg("dim", " none in prompt"));
|
|
178
178
|
}
|
|
@@ -187,13 +187,18 @@ export class GSDContextOverlay {
|
|
|
187
187
|
lines.push(theme.bold(theme.fg("accent", " Agents")));
|
|
188
188
|
lines.push(` ${theme.fg("muted", "Subagent spawns")} ${theme.fg("text", String(this.report.subagentSpawns))}`);
|
|
189
189
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const maxScroll = Math.max(0, lines.length - 24);
|
|
190
|
+
const terminalRows = process.stdout.rows || 32;
|
|
191
|
+
const maxBodyRows = Math.max(1, Math.min(lines.length, terminalRows - 12));
|
|
192
|
+
const maxScroll = Math.max(0, lines.length - maxBodyRows);
|
|
195
193
|
this.scrollOffset = Math.min(this.scrollOffset, maxScroll);
|
|
196
|
-
|
|
194
|
+
const visible = lines.slice(this.scrollOffset, this.scrollOffset + maxBodyRows);
|
|
195
|
+
const footer = renderKeyHints(theme, ["esc/q close", "↑↓ scroll", "/gsd context --open"], contentWidth);
|
|
196
|
+
|
|
197
|
+
this.cachedLines = renderDialogFrame(theme, "Context Breakdown", visible, w, {
|
|
198
|
+
footer,
|
|
199
|
+
scroll: { offset: this.scrollOffset, visibleRows: maxBodyRows, totalRows: lines.length },
|
|
200
|
+
});
|
|
201
|
+
this.cachedWidth = width;
|
|
197
202
|
return this.cachedLines;
|
|
198
203
|
}
|
|
199
204
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { Theme } from "@gsd/pi-coding-agent";
|
|
11
|
-
import { truncateToWidth,
|
|
11
|
+
import { truncateToWidth, matchesKey, Key } from "@gsd/pi-tui";
|
|
12
12
|
import { deriveState } from "./state.js";
|
|
13
13
|
import { loadFile } from "./files.js";
|
|
14
14
|
import { isDbAvailable, getMilestoneSlices, getSliceTasks } from "./gsd-db.js";
|
|
@@ -29,6 +29,7 @@ import { estimateTimeRemaining } from "./auto-dashboard.js";
|
|
|
29
29
|
import { computeProgressScore, formatProgressLine } from "./progress-score.js";
|
|
30
30
|
import { runEnvironmentChecks, type EnvironmentCheckResult } from "./doctor-environment.js";
|
|
31
31
|
import { formattedShortcutPair } from "./shortcut-defs.js";
|
|
32
|
+
import { renderDialogFrame, renderKeyHints } from "./tui/render-kit.js";
|
|
32
33
|
|
|
33
34
|
export function unitLabel(type: string): string {
|
|
34
35
|
switch (type) {
|
|
@@ -258,35 +259,26 @@ export class GSDDashboardOverlay {
|
|
|
258
259
|
|
|
259
260
|
const content = this.buildContentLines(width);
|
|
260
261
|
const viewportHeight = Math.max(5, process.stdout.rows ? process.stdout.rows - 8 : 24);
|
|
261
|
-
const
|
|
262
|
-
const visibleContentRows = Math.max(1, viewportHeight - chromeHeight);
|
|
262
|
+
const visibleContentRows = Math.max(1, viewportHeight - 4);
|
|
263
263
|
const maxScroll = Math.max(0, content.length - visibleContentRows);
|
|
264
264
|
this.scrollOffset = Math.min(this.scrollOffset, maxScroll);
|
|
265
265
|
const visibleContent = content.slice(this.scrollOffset, this.scrollOffset + visibleContentRows);
|
|
266
|
-
|
|
267
|
-
const
|
|
266
|
+
const contentWidth = Math.max(1, width - 4);
|
|
267
|
+
const footer = renderKeyHints(
|
|
268
|
+
this.theme,
|
|
269
|
+
["↑↓ scroll", "g/G top/end", `Esc/${formattedShortcutPair("dashboard")} close`],
|
|
270
|
+
contentWidth,
|
|
271
|
+
);
|
|
272
|
+
const lines = renderDialogFrame(this.theme, "GSD Dashboard", visibleContent, width, {
|
|
273
|
+
footer,
|
|
274
|
+
scroll: { offset: this.scrollOffset, visibleRows: visibleContentRows, totalRows: content.length },
|
|
275
|
+
});
|
|
268
276
|
|
|
269
277
|
this.cachedWidth = width;
|
|
270
278
|
this.cachedLines = lines;
|
|
271
279
|
return lines;
|
|
272
280
|
}
|
|
273
281
|
|
|
274
|
-
private wrapInBox(inner: string[], width: number): string[] {
|
|
275
|
-
const th = this.theme;
|
|
276
|
-
const border = (s: string) => th.fg("borderAccent", s);
|
|
277
|
-
const innerWidth = width - 4;
|
|
278
|
-
const lines: string[] = [];
|
|
279
|
-
|
|
280
|
-
lines.push(border("╭" + "─".repeat(width - 2) + "╮"));
|
|
281
|
-
for (const line of inner) {
|
|
282
|
-
const truncated = truncateToWidth(line, innerWidth);
|
|
283
|
-
const padWidth = Math.max(0, innerWidth - visibleWidth(truncated));
|
|
284
|
-
lines.push(border("│") + " " + truncated + " ".repeat(padWidth) + " " + border("│"));
|
|
285
|
-
}
|
|
286
|
-
lines.push(border("╰" + "─".repeat(width - 2) + "╯"));
|
|
287
|
-
return lines;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
282
|
private buildContentLines(width: number): string[] {
|
|
291
283
|
const th = this.theme;
|
|
292
284
|
const shellWidth = width - 4;
|
|
@@ -303,7 +295,6 @@ export class GSDDashboardOverlay {
|
|
|
303
295
|
const hr = () => row(th.fg("dim", "─".repeat(contentWidth)));
|
|
304
296
|
const centered = (content: string) => row(centerLine(content, contentWidth));
|
|
305
297
|
|
|
306
|
-
const title = th.fg("accent", th.bold("GSD Dashboard"));
|
|
307
298
|
const isRemote = !!this.dashData.remoteSession;
|
|
308
299
|
const status = this.dashData.active
|
|
309
300
|
? `${Date.now() % 2000 < 1000 ? th.fg("success", "●") : th.fg("dim", "○")} ${th.fg("success", "AUTO")}`
|
|
@@ -328,7 +319,7 @@ export class GSDDashboardOverlay {
|
|
|
328
319
|
} else if (isRemote) {
|
|
329
320
|
elapsedParts = th.fg("dim", `since ${this.dashData.remoteSession!.startedAt.replace("T", " ").slice(0, 19)}`);
|
|
330
321
|
}
|
|
331
|
-
lines.push(row(joinColumns(`${
|
|
322
|
+
lines.push(row(joinColumns(`${status}${worktreeTag}`, elapsedParts, contentWidth)));
|
|
332
323
|
|
|
333
324
|
// Progress score — traffic light indicator (#1221)
|
|
334
325
|
if (this.dashData.active || this.dashData.paused) {
|
|
@@ -610,10 +601,6 @@ export class GSDDashboardOverlay {
|
|
|
610
601
|
}
|
|
611
602
|
}
|
|
612
603
|
|
|
613
|
-
lines.push(blank());
|
|
614
|
-
lines.push(hr());
|
|
615
|
-
lines.push(centered(th.fg("dim", `↑↓ scroll · g/G top/end · Esc/${formattedShortcutPair("dashboard")} close`)));
|
|
616
|
-
|
|
617
604
|
return lines;
|
|
618
605
|
}
|
|
619
606
|
|
|
@@ -16,7 +16,7 @@ import { delimiter, join } from "node:path";
|
|
|
16
16
|
import { AuthStorage } from "@gsd/pi-coding-agent";
|
|
17
17
|
import { getEnvApiKey } from "@gsd/pi-ai";
|
|
18
18
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
19
|
-
import { getAuthPath, PROVIDER_REGISTRY, type ProviderCategory } from "./key-manager.js";
|
|
19
|
+
import { getAuthPath, PROVIDER_REGISTRY, supportsBrowserOAuth, type ProviderCategory } from "./key-manager.js";
|
|
20
20
|
import { homedir } from "node:os";
|
|
21
21
|
|
|
22
22
|
// ── Types ──────────────────────────────────────────────────────────────────────
|
|
@@ -42,11 +42,10 @@ export interface ProviderCheckResult {
|
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Providers that use external CLI authentication (not API keys).
|
|
45
|
-
*
|
|
45
|
+
* When explicitly selected, the provider's own CLI/session owns auth.
|
|
46
46
|
*/
|
|
47
47
|
const CLI_AUTH_PROVIDERS = new Set([
|
|
48
48
|
"claude-code",
|
|
49
|
-
"openai-codex",
|
|
50
49
|
"google-gemini-cli",
|
|
51
50
|
"google-antigravity",
|
|
52
51
|
]);
|
|
@@ -156,26 +155,30 @@ interface KeyLookup {
|
|
|
156
155
|
* Map of CLI provider IDs to their binary names on disk.
|
|
157
156
|
* Used for lightweight binary-presence checks (PATH scan, no subprocess).
|
|
158
157
|
*/
|
|
159
|
-
const CLI_BINARY_MAP: Record<string, string> = {
|
|
160
|
-
"claude-code": "claude",
|
|
161
|
-
"
|
|
162
|
-
"google-
|
|
163
|
-
"google-antigravity": "antigravity",
|
|
158
|
+
const CLI_BINARY_MAP: Record<string, string[]> = {
|
|
159
|
+
"claude-code": ["claude", "claude-code"],
|
|
160
|
+
"google-gemini-cli": ["gemini"],
|
|
161
|
+
"google-antigravity": ["agy"],
|
|
164
162
|
};
|
|
165
163
|
|
|
164
|
+
const CLI_AUTH_PATH_CHECK_PROVIDERS = new Set([
|
|
165
|
+
"google-gemini-cli",
|
|
166
|
+
"google-antigravity",
|
|
167
|
+
]);
|
|
168
|
+
|
|
166
169
|
/**
|
|
167
170
|
* Check if a CLI provider's binary exists anywhere in PATH.
|
|
168
171
|
* Fast filesystem scan — no subprocess, no network, sub-1ms.
|
|
169
172
|
*/
|
|
170
173
|
function isCliBinaryInPath(providerId: string): boolean {
|
|
171
|
-
const
|
|
172
|
-
if (!
|
|
174
|
+
const binaries = CLI_BINARY_MAP[providerId];
|
|
175
|
+
if (!binaries) return false;
|
|
173
176
|
|
|
174
177
|
const pathDirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
175
178
|
|
|
176
179
|
// On Windows, command shims are commonly installed as .cmd/.exe/.bat/.com.
|
|
177
180
|
// Scan PATHEXT candidates in addition to the bare binary name.
|
|
178
|
-
const executableNames: string[] = [
|
|
181
|
+
const executableNames: string[] = [...binaries];
|
|
179
182
|
if (process.platform === "win32") {
|
|
180
183
|
const rawPathExt = process.env.PATHEXT
|
|
181
184
|
?.split(";")
|
|
@@ -185,9 +188,11 @@ function isCliBinaryInPath(providerId: string): boolean {
|
|
|
185
188
|
ext.startsWith(".") ? ext.toLowerCase() : `.${ext.toLowerCase()}`,
|
|
186
189
|
);
|
|
187
190
|
const defaultExt = [".exe", ".cmd", ".bat", ".com"];
|
|
188
|
-
for (const
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
+
for (const binary of binaries) {
|
|
192
|
+
for (const ext of [...normalizedPathExt, ...defaultExt]) {
|
|
193
|
+
const candidate = `${binary}${ext}`;
|
|
194
|
+
if (!executableNames.includes(candidate)) executableNames.push(candidate);
|
|
195
|
+
}
|
|
191
196
|
}
|
|
192
197
|
}
|
|
193
198
|
|
|
@@ -224,12 +229,6 @@ function hasModelsJsonApiKey(providerId: string): boolean {
|
|
|
224
229
|
function resolveKey(providerId: string): KeyLookup {
|
|
225
230
|
const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
|
|
226
231
|
|
|
227
|
-
// claude-code never stores credentials in auth.json — GSD delegates entirely to
|
|
228
|
-
// the local CLI binary. Presence of the binary in PATH is the only signal.
|
|
229
|
-
if (providerId === "claude-code") {
|
|
230
|
-
return { found: isCliBinaryInPath("claude-code"), source: "env", backedOff: false };
|
|
231
|
-
}
|
|
232
|
-
|
|
233
232
|
if (providerId === "anthropic-vertex" && process.env.ANTHROPIC_VERTEX_PROJECT_ID) {
|
|
234
233
|
return { found: true, source: "env", backedOff: false };
|
|
235
234
|
}
|
|
@@ -242,9 +241,15 @@ function resolveKey(providerId: string): KeyLookup {
|
|
|
242
241
|
const creds = auth.getCredentialsForProvider(providerId);
|
|
243
242
|
if (creds.length > 0) {
|
|
244
243
|
// Filter out empty placeholder keys (from skipped onboarding)
|
|
245
|
-
const hasRealKey = creds.some(c =>
|
|
246
|
-
|
|
247
|
-
|
|
244
|
+
const hasRealKey = creds.some(c => {
|
|
245
|
+
if (c.type === "oauth") return true;
|
|
246
|
+
if (c.type !== "api_key") return false;
|
|
247
|
+
|
|
248
|
+
const key = (c as { key?: string }).key?.trim();
|
|
249
|
+
if (!key) return false;
|
|
250
|
+
|
|
251
|
+
return !(CLI_AUTH_PROVIDERS.has(providerId) && key === "cli");
|
|
252
|
+
});
|
|
248
253
|
if (hasRealKey) {
|
|
249
254
|
return {
|
|
250
255
|
found: true,
|
|
@@ -275,6 +280,12 @@ function resolveKey(providerId: string): KeyLookup {
|
|
|
275
280
|
return { found: true, source: "models.json", backedOff: false };
|
|
276
281
|
}
|
|
277
282
|
|
|
283
|
+
// Cross-provider routes can use a local CLI when it is installed. Explicit
|
|
284
|
+
// external CLI provider selections are handled in checkLlmProviders() below.
|
|
285
|
+
if (CLI_AUTH_PROVIDERS.has(providerId) && isCliBinaryInPath(providerId)) {
|
|
286
|
+
return { found: true, source: "env", backedOff: false };
|
|
287
|
+
}
|
|
288
|
+
|
|
278
289
|
return { found: false, source: "none", backedOff: false };
|
|
279
290
|
}
|
|
280
291
|
|
|
@@ -285,15 +296,32 @@ function checkLlmProviders(): ProviderCheckResult[] {
|
|
|
285
296
|
const results: ProviderCheckResult[] = [];
|
|
286
297
|
|
|
287
298
|
for (const providerId of required) {
|
|
288
|
-
// CLI-authenticated providers don't need API keys
|
|
299
|
+
// CLI-authenticated providers don't need API keys. The provider's own
|
|
300
|
+
// request path validates CLI sessions when it is used.
|
|
289
301
|
if (CLI_AUTH_PROVIDERS.has(providerId)) {
|
|
290
302
|
const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
|
|
303
|
+
const label = info?.label ?? providerId;
|
|
304
|
+
if (CLI_AUTH_PATH_CHECK_PROVIDERS.has(providerId) && !isCliBinaryInPath(providerId)) {
|
|
305
|
+
const binaries = CLI_BINARY_MAP[providerId]?.map(binary => `\`${binary}\``).join(" or ");
|
|
306
|
+
results.push({
|
|
307
|
+
name: providerId,
|
|
308
|
+
label,
|
|
309
|
+
category: "llm",
|
|
310
|
+
status: "error",
|
|
311
|
+
message: `${label} — CLI not found`,
|
|
312
|
+
detail: binaries
|
|
313
|
+
? `Install ${label} and ensure ${binaries} is on PATH`
|
|
314
|
+
: `Install ${label} and ensure its CLI is on PATH`,
|
|
315
|
+
required: true,
|
|
316
|
+
});
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
291
319
|
results.push({
|
|
292
320
|
name: providerId,
|
|
293
|
-
label
|
|
321
|
+
label,
|
|
294
322
|
category: "llm",
|
|
295
323
|
status: "ok",
|
|
296
|
-
message: `${
|
|
324
|
+
message: `${label} — CLI auth (no key needed)`,
|
|
297
325
|
required: true,
|
|
298
326
|
});
|
|
299
327
|
continue;
|
|
@@ -333,7 +361,7 @@ function checkLlmProviders(): ProviderCheckResult[] {
|
|
|
333
361
|
message: `${label} — not configured`,
|
|
334
362
|
detail: providerId === "anthropic-vertex"
|
|
335
363
|
? "Set ANTHROPIC_VERTEX_PROJECT_ID and authenticate with Google ADC"
|
|
336
|
-
: info
|
|
364
|
+
: info && supportsBrowserOAuth(info)
|
|
337
365
|
? `Run /gsd keys to authenticate`
|
|
338
366
|
: `Set ${envVar} or run /gsd keys`,
|
|
339
367
|
required: true,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import { spawnSync } from "node:child_process";
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
6
|
-
import { join } from "node:path";
|
|
6
|
+
import { dirname, join, resolve } from "node:path";
|
|
7
7
|
|
|
8
8
|
import { autoResolveSafeConflictPaths } from "./git-conflict-resolve.js";
|
|
9
9
|
import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
|
|
@@ -14,6 +14,21 @@ function splitZeroDelimited(output: string): string[] {
|
|
|
14
14
|
return output.split("\0").filter(Boolean);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
function hasGitMarker(basePath: string): boolean {
|
|
18
|
+
try {
|
|
19
|
+
let dir = resolve(basePath);
|
|
20
|
+
for (let i = 0; i < 30; i++) {
|
|
21
|
+
if (existsSync(join(dir, ".git"))) return true;
|
|
22
|
+
const parent = dirname(dir);
|
|
23
|
+
if (parent === dir) break;
|
|
24
|
+
dir = parent;
|
|
25
|
+
}
|
|
26
|
+
} catch {
|
|
27
|
+
// Fall through to the git probes, which will report unknown on failure.
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
17
32
|
export function listUnmergedGitPaths(basePath: string): string[] | null {
|
|
18
33
|
try {
|
|
19
34
|
const output = spawnSync("git", ["diff", "--name-only", "--diff-filter=U", "-z"], {
|
|
@@ -75,6 +90,15 @@ export interface GitConflictProbeResult {
|
|
|
75
90
|
}
|
|
76
91
|
|
|
77
92
|
export function probeGitConflictState(basePath: string): GitConflictProbeResult {
|
|
93
|
+
if (!hasGitMarker(basePath)) {
|
|
94
|
+
return {
|
|
95
|
+
status: "clean",
|
|
96
|
+
unmerged: [],
|
|
97
|
+
checkFailures: [],
|
|
98
|
+
mergeStateBlockers: [],
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
78
102
|
const unmerged = listUnmergedGitPaths(basePath);
|
|
79
103
|
if (unmerged === null) {
|
|
80
104
|
return {
|
|
@@ -1358,7 +1358,7 @@ function buildHeadlessDiscussPrompt(nextId: string, seedContext: string, _basePa
|
|
|
1358
1358
|
* Run preparation phase if enabled, then build the discuss prompt.
|
|
1359
1359
|
* Preparation analyzes the codebase and prior context, injecting the results
|
|
1360
1360
|
* as supplementary context into the standard discuss template. The discuss
|
|
1361
|
-
* template drives the conversation
|
|
1361
|
+
* template drives the conversation with a variable vision opener, while
|
|
1362
1362
|
* the preparation briefs give the agent grounding in the existing codebase.
|
|
1363
1363
|
*
|
|
1364
1364
|
* @param ctx - Extension command context with UI for progress notifications
|