@oh-my-pi/pi-coding-agent 16.1.23 → 16.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +70 -0
- package/dist/cli.js +3817 -3735
- package/dist/types/advisor/__tests__/emission-guard.test.d.ts +1 -0
- package/dist/types/advisor/emission-guard.d.ts +73 -0
- package/dist/types/advisor/index.d.ts +1 -0
- package/dist/types/advisor/runtime.d.ts +7 -0
- package/dist/types/advisor/watchdog.d.ts +2 -0
- package/dist/types/cli/flag-tables.d.ts +1 -0
- package/dist/types/cli/gallery-fixtures/search.d.ts +1 -1
- package/dist/types/collab/display-name.d.ts +3 -0
- package/dist/types/collab/host.d.ts +0 -2
- package/dist/types/commands/worktree.d.ts +0 -3
- package/dist/types/config/inline-tool-descriptors-mode.d.ts +13 -0
- package/dist/types/config/model-discovery.d.ts +6 -1
- package/dist/types/config/model-registry.d.ts +6 -2
- package/dist/types/config/model-resolver.d.ts +12 -0
- package/dist/types/config/models-config-schema.d.ts +29 -2
- package/dist/types/config/models-config.d.ts +22 -1
- package/dist/types/config/settings-schema.d.ts +91 -21
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/dap/config.d.ts +1 -1
- package/dist/types/edit/hashline/filesystem.d.ts +4 -2
- package/dist/types/edit/renderer.d.ts +4 -0
- package/dist/types/extensibility/extensions/types.d.ts +17 -17
- package/dist/types/extensibility/hooks/types.d.ts +10 -10
- package/dist/types/extensibility/plugins/marketplace/types.d.ts +1 -0
- package/dist/types/internal-urls/__tests__/ssh-protocol.test.d.ts +1 -0
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/registry-helpers.d.ts +2 -0
- package/dist/types/internal-urls/router.d.ts +1 -1
- package/dist/types/internal-urls/ssh-protocol.d.ts +10 -0
- package/dist/types/internal-urls/types.d.ts +19 -2
- package/dist/types/irc/bus.d.ts +6 -0
- package/dist/types/modes/components/__tests__/move-overlay.test.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +7 -0
- package/dist/types/modes/components/move-overlay.d.ts +23 -0
- package/dist/types/modes/components/plugin-selector.d.ts +2 -1
- package/dist/types/modes/components/queue-mode-selector.d.ts +2 -1
- package/dist/types/modes/components/select-list-mouse-routing.d.ts +2 -0
- package/dist/types/modes/components/settings-defs.d.ts +4 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/show-images-selector.d.ts +2 -1
- package/dist/types/modes/components/status-line/component.d.ts +2 -0
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/theme-selector.d.ts +2 -1
- package/dist/types/modes/components/thinking-selector.d.ts +2 -1
- package/dist/types/modes/controllers/command-controller.d.ts +10 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +6 -0
- package/dist/types/modes/interactive-mode.d.ts +10 -1
- package/dist/types/modes/internal-url-autocomplete.d.ts +1 -1
- package/dist/types/modes/running-subagent-badge.d.ts +6 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +9 -1
- package/dist/types/sdk.d.ts +2 -2
- package/dist/types/session/agent-session.d.ts +2 -1
- package/dist/types/session/session-manager.d.ts +13 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
- package/dist/types/ssh/__tests__/connection-manager-args.test.d.ts +1 -0
- package/dist/types/ssh/__tests__/file-transfer-posix-guard.test.d.ts +1 -0
- package/dist/types/ssh/connection-manager.d.ts +2 -0
- package/dist/types/ssh/file-transfer.d.ts +79 -0
- package/dist/types/ssh/utils.d.ts +6 -0
- package/dist/types/system-prompt.d.ts +3 -0
- package/dist/types/task/executor.d.ts +16 -0
- package/dist/types/tiny/text.d.ts +1 -1
- package/dist/types/tools/builtin-names.d.ts +5 -1
- package/dist/types/tools/{find.d.ts → glob.d.ts} +15 -15
- package/dist/types/tools/{search.d.ts → grep.d.ts} +14 -14
- package/dist/types/tools/index.d.ts +3 -3
- package/dist/types/tools/path-utils.d.ts +29 -0
- package/dist/types/tools/read.d.ts +2 -2
- package/dist/types/tools/render-utils.d.ts +8 -0
- package/dist/types/tools/renderers.d.ts +11 -0
- package/dist/types/tools/ssh.d.ts +2 -0
- package/dist/types/tools/todo.d.ts +0 -16
- package/dist/types/tools/write.d.ts +2 -2
- package/dist/types/utils/active-repo-context.d.ts +8 -0
- package/dist/types/utils/image-resize.d.ts +1 -0
- package/dist/types/utils/markit-cache.d.ts +23 -0
- package/dist/types/utils/markit.d.ts +5 -1
- package/dist/types/utils/prompt-path.d.ts +1 -0
- package/dist/types/web/search/providers/duckduckgo.d.ts +14 -0
- package/dist/types/web/search/providers/firecrawl.d.ts +28 -0
- package/dist/types/web/search/providers/tinyfish.d.ts +29 -0
- package/dist/types/web/search/providers/xai.d.ts +13 -0
- package/dist/types/web/search/types.d.ts +18 -2
- package/package.json +30 -15
- package/scripts/build-binary.ts +9 -9
- package/scripts/bundle-dist.ts +2 -2
- package/src/advisor/__tests__/advisor.test.ts +40 -4
- package/src/advisor/__tests__/emission-guard.test.ts +147 -0
- package/src/advisor/advise-tool.ts +1 -1
- package/src/advisor/emission-guard.ts +172 -0
- package/src/advisor/index.ts +1 -0
- package/src/advisor/runtime.ts +11 -0
- package/src/advisor/watchdog.ts +12 -1
- package/src/cli/args.ts +5 -2
- package/src/cli/auth-broker-cli.ts +17 -0
- package/src/cli/config-cli.ts +4 -0
- package/src/cli/flag-tables.ts +7 -4
- package/src/cli/gallery-cli.ts +14 -2
- package/src/cli/gallery-fixtures/edit.ts +60 -0
- package/src/cli/gallery-fixtures/fs.ts +17 -17
- package/src/cli/gallery-fixtures/search.ts +4 -4
- package/src/cli/usage-cli.ts +20 -1
- package/src/cli/web-search-cli.ts +1 -1
- package/src/collab/display-name.ts +13 -0
- package/src/collab/guest.ts +5 -1
- package/src/collab/host.ts +2 -13
- package/src/commands/worktree.ts +6 -0
- package/src/config/inline-tool-descriptors-mode.ts +27 -0
- package/src/config/model-discovery.ts +77 -8
- package/src/config/model-registry.ts +89 -11
- package/src/config/model-resolver.ts +39 -1
- package/src/config/models-config-schema.ts +41 -5
- package/src/config/models-config.ts +4 -1
- package/src/config/settings-schema.ts +85 -27
- package/src/config/settings.ts +172 -1
- package/src/cursor.ts +1 -1
- package/src/dap/config.ts +152 -8
- package/src/dap/session.ts +1 -1
- package/src/discovery/helpers.ts +3 -1
- package/src/edit/hashline/diff.ts +14 -3
- package/src/edit/hashline/execute.ts +42 -6
- package/src/edit/hashline/filesystem.ts +38 -2
- package/src/edit/index.ts +1 -0
- package/src/edit/modes/patch.ts +11 -1
- package/src/edit/renderer.ts +140 -13
- package/src/eval/__tests__/agent-bridge.test.ts +101 -0
- package/src/eval/agent-bridge.ts +26 -3
- package/src/eval/js/tool-bridge.ts +2 -2
- package/src/export/html/index.ts +1 -1
- package/src/export/html/template.js +3 -1
- package/src/export/html/tool-views.generated.js +20 -20
- package/src/extensibility/extensions/types.ts +22 -22
- package/src/extensibility/hooks/types.ts +11 -11
- package/src/extensibility/plugins/legacy-pi-bundled-keys.ts +8 -3
- package/src/extensibility/plugins/legacy-pi-bundled-registry.ts +25 -10
- package/src/extensibility/plugins/marketplace/manager.ts +22 -0
- package/src/extensibility/plugins/marketplace/types.ts +1 -0
- package/src/internal-urls/__tests__/ssh-protocol.test.ts +331 -0
- package/src/internal-urls/docs-index.generated.txt +2 -2
- package/src/internal-urls/docs-index.ts +2 -3
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/registry-helpers.ts +19 -4
- package/src/internal-urls/router.ts +5 -3
- package/src/internal-urls/ssh-protocol.ts +367 -0
- package/src/internal-urls/types.ts +19 -2
- package/src/irc/bus.ts +11 -3
- package/src/mcp/tool-bridge.ts +32 -2
- package/src/memories/index.ts +1 -1
- package/src/modes/acp/acp-event-mapper.ts +2 -2
- package/src/modes/components/__tests__/move-overlay.test.ts +166 -0
- package/src/modes/components/agent-hub.ts +3 -0
- package/src/modes/components/agent-transcript-viewer.ts +9 -7
- package/src/modes/components/custom-editor.ts +90 -5
- package/src/modes/components/move-overlay.ts +282 -0
- package/src/modes/components/plan-review-overlay.ts +35 -35
- package/src/modes/components/plugin-selector.ts +6 -1
- package/src/modes/components/queue-mode-selector.ts +6 -1
- package/src/modes/components/select-list-mouse-routing.ts +35 -0
- package/src/modes/components/session-selector.ts +11 -10
- package/src/modes/components/settings-defs.ts +14 -1
- package/src/modes/components/settings-selector.ts +196 -29
- package/src/modes/components/show-images-selector.ts +6 -1
- package/src/modes/components/status-line/component.ts +108 -28
- package/src/modes/components/status-line/segments.ts +5 -1
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/theme-selector.ts +6 -1
- package/src/modes/components/thinking-selector.ts +6 -1
- package/src/modes/components/tool-execution.ts +25 -9
- package/src/modes/components/tree-selector.ts +5 -5
- package/src/modes/controllers/command-controller.ts +140 -47
- package/src/modes/controllers/event-controller.ts +59 -3
- package/src/modes/controllers/input-controller.ts +68 -2
- package/src/modes/controllers/selector-controller.ts +5 -2
- package/src/modes/controllers/streaming-reveal.ts +17 -0
- package/src/modes/controllers/tool-args-reveal.ts +1 -1
- package/src/modes/interactive-mode.ts +116 -50
- package/src/modes/internal-url-autocomplete.ts +17 -2
- package/src/modes/prompt-action-autocomplete.ts +3 -1
- package/src/modes/running-subagent-badge.ts +13 -0
- package/src/modes/setup-wizard/scenes/glyph.ts +2 -13
- package/src/modes/setup-wizard/scenes/providers.ts +2 -1
- package/src/modes/setup-wizard/scenes/theme.ts +6 -12
- package/src/modes/setup-wizard/scenes/web-search.ts +8 -13
- package/src/modes/setup-wizard/wizard-overlay.ts +6 -5
- package/src/modes/theme/theme.ts +9 -1
- package/src/modes/types.ts +9 -1
- package/src/modes/utils/interactive-context-helpers.ts +1 -1
- package/src/priority.json +15 -0
- package/src/prompts/advisor/active-repo-watchdog.md +6 -0
- package/src/prompts/advisor/system.md +21 -7
- package/src/prompts/agents/designer.md +1 -1
- package/src/prompts/agents/explore.md +1 -1
- package/src/prompts/agents/librarian.md +2 -2
- package/src/prompts/agents/plan.md +2 -2
- package/src/prompts/agents/reviewer.md +1 -1
- package/src/prompts/agents/task.md +2 -2
- package/src/prompts/system/active-repo-context.md +4 -0
- package/src/prompts/system/gemini-tool-call-reminder.md +9 -0
- package/src/prompts/system/plan-mode-active.md +3 -3
- package/src/prompts/system/project-prompt.md +2 -2
- package/src/prompts/system/system-prompt.md +6 -6
- package/src/prompts/tools/bash.md +2 -2
- package/src/prompts/tools/checkpoint.md +1 -1
- package/src/prompts/tools/{find.md → glob.md} +1 -1
- package/src/prompts/tools/{search.md → grep.md} +3 -3
- package/src/prompts/tools/read.md +4 -2
- package/src/sdk.ts +55 -25
- package/src/session/agent-session.ts +508 -132
- package/src/session/session-history-format.ts +2 -2
- package/src/session/session-manager.ts +46 -0
- package/src/slash-commands/builtin-registry.ts +138 -20
- package/src/slash-commands/helpers/usage-report.ts +23 -2
- package/src/ssh/__tests__/connection-manager-args.test.ts +69 -0
- package/src/ssh/__tests__/file-transfer-posix-guard.test.ts +68 -0
- package/src/ssh/connection-manager.ts +12 -15
- package/src/ssh/file-transfer.ts +209 -0
- package/src/ssh/utils.ts +24 -0
- package/src/system-prompt.ts +56 -28
- package/src/task/executor.ts +96 -36
- package/src/task/index.ts +3 -3
- package/src/task/render.ts +14 -13
- package/src/task/worktree.ts +38 -1
- package/src/tiny/text.ts +49 -4
- package/src/tiny/worker.ts +3 -3
- package/src/tools/bash.ts +2 -2
- package/src/tools/builtin-names.ts +26 -2
- package/src/tools/{find.ts → glob.ts} +48 -42
- package/src/tools/{search.ts → grep.ts} +311 -129
- package/src/tools/index.ts +13 -14
- package/src/tools/irc.ts +6 -2
- package/src/tools/path-utils.ts +64 -1
- package/src/tools/read.ts +35 -6
- package/src/tools/render-utils.ts +14 -0
- package/src/tools/renderers.ts +15 -4
- package/src/tools/ssh.ts +17 -2
- package/src/tools/todo.ts +4 -26
- package/src/tools/write.ts +25 -9
- package/src/utils/active-repo-context.ts +143 -0
- package/src/utils/image-resize.ts +88 -7
- package/src/utils/lang-from-path.ts +3 -3
- package/src/utils/markit-cache.ts +166 -0
- package/src/utils/markit.ts +86 -18
- package/src/utils/prompt-path.ts +3 -0
- package/src/utils/title-generator.ts +1 -1
- package/src/web/search/provider.ts +54 -34
- package/src/web/search/providers/duckduckgo.ts +140 -0
- package/src/web/search/providers/firecrawl.ts +144 -0
- package/src/web/search/providers/tinyfish.ts +159 -0
- package/src/web/search/providers/xai.ts +292 -0
- package/src/web/search/providers/zai.ts +142 -56
- package/src/web/search/types.ts +6 -2
|
@@ -9,6 +9,7 @@ import { settings } from "../../../config/settings";
|
|
|
9
9
|
import type { AgentSession } from "../../../session/agent-session";
|
|
10
10
|
import type { OAuthAccountIdentity } from "../../../session/auth-storage";
|
|
11
11
|
import { limitMatchesActiveAccount } from "../../../slash-commands/helpers/active-oauth-account";
|
|
12
|
+
import { type ActiveRepoContext, resolveActiveRepoContextSync } from "../../../utils/active-repo-context";
|
|
12
13
|
import * as git from "../../../utils/git";
|
|
13
14
|
import { getSessionAccentAnsi, getSessionAccentHex } from "../../../utils/session-color";
|
|
14
15
|
import { sanitizeStatusText } from "../../shared";
|
|
@@ -155,6 +156,12 @@ interface ContextUsageMemo {
|
|
|
155
156
|
skillsRef: readonly any[] | undefined;
|
|
156
157
|
}
|
|
157
158
|
|
|
159
|
+
interface ActiveRepoCache {
|
|
160
|
+
projectDir: string;
|
|
161
|
+
activeRepo: ActiveRepoContext | null;
|
|
162
|
+
effectiveGitCwd: string;
|
|
163
|
+
}
|
|
164
|
+
|
|
158
165
|
const EMPTY_MESSAGES: readonly AgentMessage[] = [];
|
|
159
166
|
const STATUS_USAGE_START_DELAY_MS = 0;
|
|
160
167
|
const STATUS_USAGE_REFRESH_TIMEOUT_MS = 2_000;
|
|
@@ -169,6 +176,10 @@ function hasGitSegment(segments: readonly StatusLineSegmentId[]): boolean {
|
|
|
169
176
|
function hasPrSegment(segments: readonly StatusLineSegmentId[]): boolean {
|
|
170
177
|
return segments.includes("pr");
|
|
171
178
|
}
|
|
179
|
+
function hasPathSegment(segments: readonly StatusLineSegmentId[]): boolean {
|
|
180
|
+
return segments.includes("path");
|
|
181
|
+
}
|
|
182
|
+
|
|
172
183
|
function hasGitBackedSegment(segments: readonly StatusLineSegmentId[]): boolean {
|
|
173
184
|
return hasGitSegment(segments) || hasPrSegment(segments);
|
|
174
185
|
}
|
|
@@ -189,23 +200,27 @@ export class StatusLineComponent implements Component {
|
|
|
189
200
|
#autoCompactEnabled: boolean = true;
|
|
190
201
|
#hookStatuses: Map<string, string> = new Map();
|
|
191
202
|
#subagentCount: number = 0;
|
|
203
|
+
#subagentHubHint: string | undefined;
|
|
192
204
|
#sessionStartTime: number = Date.now();
|
|
193
205
|
#planModeStatus: { enabled: boolean; paused: boolean } | null = null;
|
|
194
206
|
#loopModeStatus: { enabled: boolean } | null = null;
|
|
195
207
|
#goalModeStatus: { enabled: boolean; paused: boolean } | null = null;
|
|
196
208
|
#collabStatus: CollabStatus | null = null;
|
|
197
209
|
#focusedAgentId: string | undefined;
|
|
210
|
+
#activeRepoCache: ActiveRepoCache | undefined;
|
|
198
211
|
|
|
199
212
|
// Git status caching (1s TTL)
|
|
200
213
|
#cachedGitStatus: { staged: number; unstaged: number; untracked: number } | null = null;
|
|
214
|
+
#cachedGitStatusCwd: string | undefined = undefined;
|
|
201
215
|
#gitStatusLastFetch = 0;
|
|
202
|
-
#
|
|
216
|
+
#gitStatusInFlightCwd: string | undefined = undefined;
|
|
203
217
|
|
|
204
218
|
// PR lookup caching (invalidated on branch/repo context changes)
|
|
205
219
|
#cachedPr: { number: number; url: string } | null | undefined = undefined;
|
|
206
220
|
#cachedPrContext: PrCacheContext | undefined = undefined;
|
|
207
221
|
#prLookupInFlight = false;
|
|
208
222
|
#defaultBranch?: string;
|
|
223
|
+
#defaultBranchCwd: string | undefined = undefined;
|
|
209
224
|
#lastTokensPerSecond: number | null = null;
|
|
210
225
|
#lastTokensPerSecondTimestamp: number | null = null;
|
|
211
226
|
|
|
@@ -248,6 +263,18 @@ export class StatusLineComponent implements Component {
|
|
|
248
263
|
);
|
|
249
264
|
}
|
|
250
265
|
|
|
266
|
+
#resolveActiveRepoCache(): ActiveRepoCache {
|
|
267
|
+
const projectDir = getProjectDir();
|
|
268
|
+
if (this.#activeRepoCache?.projectDir === projectDir) {
|
|
269
|
+
return this.#activeRepoCache;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const activeRepo = resolveActiveRepoContextSync(projectDir);
|
|
273
|
+
const effectiveGitCwd = activeRepo?.repoRoot ?? projectDir;
|
|
274
|
+
this.#activeRepoCache = { projectDir, activeRepo, effectiveGitCwd };
|
|
275
|
+
return this.#activeRepoCache;
|
|
276
|
+
}
|
|
277
|
+
|
|
251
278
|
/**
|
|
252
279
|
* Re-point the status line at another session (focus proxy). Invalidate: model/context/usage all derive
|
|
253
280
|
* from it. `focusedAgentId` is the focused subagent id while the view is proxied, undefined for main.
|
|
@@ -279,6 +306,12 @@ export class StatusLineComponent implements Component {
|
|
|
279
306
|
this.#subagentCount = count;
|
|
280
307
|
}
|
|
281
308
|
|
|
309
|
+
/** Hub key label shown in the forced running-subagents badge. */
|
|
310
|
+
setSubagentHubHint(hint: string | undefined): void {
|
|
311
|
+
const trimmed = hint?.trim();
|
|
312
|
+
this.#subagentHubHint = trimmed ? trimmed : undefined;
|
|
313
|
+
}
|
|
314
|
+
|
|
282
315
|
/** Active subagent count as currently displayed (collab state mirroring). */
|
|
283
316
|
get subagentCount(): number {
|
|
284
317
|
return this.#subagentCount;
|
|
@@ -328,7 +361,8 @@ export class StatusLineComponent implements Component {
|
|
|
328
361
|
return;
|
|
329
362
|
}
|
|
330
363
|
|
|
331
|
-
const
|
|
364
|
+
const { effectiveGitCwd } = this.#resolveActiveRepoCache();
|
|
365
|
+
const repository = git.repo.resolveSync(effectiveGitCwd);
|
|
332
366
|
if (!repository) return;
|
|
333
367
|
|
|
334
368
|
const watchPath = git.repo.isReftableSync(repository)
|
|
@@ -383,17 +417,17 @@ export class StatusLineComponent implements Component {
|
|
|
383
417
|
this.#cachedBranchCwd = undefined;
|
|
384
418
|
this.#cachedPrContext = undefined;
|
|
385
419
|
}
|
|
386
|
-
#getCurrentBranch(): string | null {
|
|
420
|
+
#getCurrentBranch(effectiveGitCwd?: string): string | null {
|
|
387
421
|
if (!this.#gitEnabled()) return null;
|
|
388
422
|
|
|
389
|
-
const
|
|
390
|
-
if (this.#cachedBranch !== undefined && this.#cachedBranchCwd ===
|
|
423
|
+
const gitCwd = effectiveGitCwd ?? this.#resolveActiveRepoCache().effectiveGitCwd;
|
|
424
|
+
if (this.#cachedBranch !== undefined && this.#cachedBranchCwd === gitCwd) {
|
|
391
425
|
return this.#cachedBranch;
|
|
392
426
|
}
|
|
393
427
|
|
|
394
|
-
const head = git.head.resolveSync(
|
|
428
|
+
const head = git.head.resolveSync(gitCwd);
|
|
395
429
|
const gitHeadPath = head?.headPath ?? null;
|
|
396
|
-
this.#cachedBranchCwd =
|
|
430
|
+
this.#cachedBranchCwd = gitCwd;
|
|
397
431
|
this.#cachedBranchRepoId = gitHeadPath;
|
|
398
432
|
if (!head) {
|
|
399
433
|
this.#cachedBranch = null;
|
|
@@ -405,12 +439,18 @@ export class StatusLineComponent implements Component {
|
|
|
405
439
|
return this.#cachedBranch ?? null;
|
|
406
440
|
}
|
|
407
441
|
|
|
408
|
-
#isDefaultBranch(branch: string): boolean {
|
|
442
|
+
#isDefaultBranch(branch: string, effectiveGitCwd: string): boolean {
|
|
443
|
+
if (this.#defaultBranchCwd !== effectiveGitCwd) {
|
|
444
|
+
this.#defaultBranch = undefined;
|
|
445
|
+
this.#defaultBranchCwd = effectiveGitCwd;
|
|
446
|
+
}
|
|
447
|
+
|
|
409
448
|
if (this.#defaultBranch === undefined) {
|
|
410
449
|
this.#defaultBranch = "main";
|
|
450
|
+
const lookupCwd = effectiveGitCwd;
|
|
411
451
|
(async () => {
|
|
412
|
-
const resolved = await git.branch.default(
|
|
413
|
-
if (this.#disposed) return;
|
|
452
|
+
const resolved = await git.branch.default(lookupCwd);
|
|
453
|
+
if (this.#disposed || this.#defaultBranchCwd !== lookupCwd) return;
|
|
414
454
|
if (resolved) {
|
|
415
455
|
this.#defaultBranch = resolved;
|
|
416
456
|
if (this.#onBranchChange) {
|
|
@@ -422,32 +462,43 @@ export class StatusLineComponent implements Component {
|
|
|
422
462
|
return branch === this.#defaultBranch;
|
|
423
463
|
}
|
|
424
464
|
|
|
425
|
-
#getGitStatus(): { staged: number; unstaged: number; untracked: number } | null {
|
|
465
|
+
#getGitStatus(effectiveGitCwd?: string): { staged: number; unstaged: number; untracked: number } | null {
|
|
426
466
|
if (!this.#gitEnabled()) return null;
|
|
427
|
-
|
|
467
|
+
|
|
468
|
+
const gitCwd = effectiveGitCwd ?? this.#resolveActiveRepoCache().effectiveGitCwd;
|
|
469
|
+
if (this.#gitStatusInFlightCwd !== undefined) {
|
|
470
|
+
return this.#cachedGitStatusCwd === gitCwd ? this.#cachedGitStatus : null;
|
|
471
|
+
}
|
|
472
|
+
if (this.#cachedGitStatusCwd === gitCwd && Date.now() - this.#gitStatusLastFetch < 1000) {
|
|
428
473
|
return this.#cachedGitStatus;
|
|
429
474
|
}
|
|
430
475
|
|
|
431
|
-
this.#
|
|
476
|
+
this.#gitStatusInFlightCwd = gitCwd;
|
|
432
477
|
|
|
433
478
|
(async () => {
|
|
479
|
+
let nextStatus: { staged: number; unstaged: number; untracked: number } | null = null;
|
|
434
480
|
try {
|
|
435
|
-
|
|
481
|
+
nextStatus = await git.status.summary(gitCwd);
|
|
436
482
|
} catch {
|
|
437
|
-
|
|
483
|
+
nextStatus = null;
|
|
438
484
|
} finally {
|
|
439
|
-
this.#
|
|
440
|
-
|
|
485
|
+
if (this.#gitStatusInFlightCwd === gitCwd) {
|
|
486
|
+
this.#cachedGitStatus = nextStatus;
|
|
487
|
+
this.#cachedGitStatusCwd = gitCwd;
|
|
488
|
+
this.#gitStatusLastFetch = Date.now();
|
|
489
|
+
this.#gitStatusInFlightCwd = undefined;
|
|
490
|
+
}
|
|
441
491
|
}
|
|
442
492
|
})();
|
|
443
493
|
|
|
444
|
-
return this.#cachedGitStatus;
|
|
494
|
+
return this.#cachedGitStatusCwd === gitCwd ? this.#cachedGitStatus : null;
|
|
445
495
|
}
|
|
446
496
|
|
|
447
|
-
#lookupPr(): { number: number; url: string } | null {
|
|
497
|
+
#lookupPr(effectiveGitCwd?: string): { number: number; url: string } | null {
|
|
448
498
|
if (!this.#gitEnabled()) return null;
|
|
449
499
|
|
|
450
|
-
const
|
|
500
|
+
const gitCwd = effectiveGitCwd ?? this.#resolveActiveRepoCache().effectiveGitCwd;
|
|
501
|
+
const branch = this.#getCurrentBranch(gitCwd);
|
|
451
502
|
const currentContext = branch ? createPrCacheContext(branch, this.#cachedBranchRepoId ?? null) : null;
|
|
452
503
|
|
|
453
504
|
if (canReuseCachedPr(this.#cachedPr, this.#cachedPrContext, currentContext)) {
|
|
@@ -456,19 +507,26 @@ export class StatusLineComponent implements Component {
|
|
|
456
507
|
|
|
457
508
|
const stalePr = this.#cachedPr;
|
|
458
509
|
|
|
459
|
-
|
|
460
|
-
|
|
510
|
+
if (!branch) {
|
|
511
|
+
this.#cachedPr = null;
|
|
512
|
+
this.#cachedPrContext = undefined;
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Don't look up if detached, default branch, or already in flight.
|
|
517
|
+
if (branch === "detached" || this.#isDefaultBranch(branch, gitCwd) || this.#prLookupInFlight) {
|
|
461
518
|
return stalePr ?? null;
|
|
462
519
|
}
|
|
463
520
|
|
|
464
521
|
this.#prLookupInFlight = true;
|
|
465
522
|
const lookupContext = currentContext;
|
|
523
|
+
const lookupCwd = gitCwd;
|
|
466
524
|
|
|
467
525
|
// Fire async lookup, keep stale value visible until resolved
|
|
468
526
|
(async () => {
|
|
469
527
|
// Helper: only write cache if branch/repo context hasn't changed since launch
|
|
470
528
|
const setCachedPr = (value: { number: number; url: string } | null) => {
|
|
471
|
-
const latestBranch = this.#getCurrentBranch();
|
|
529
|
+
const latestBranch = this.#getCurrentBranch(lookupCwd);
|
|
472
530
|
const latestContext = latestBranch
|
|
473
531
|
? createPrCacheContext(latestBranch, this.#cachedBranchRepoId ?? null)
|
|
474
532
|
: undefined;
|
|
@@ -479,7 +537,7 @@ export class StatusLineComponent implements Component {
|
|
|
479
537
|
};
|
|
480
538
|
try {
|
|
481
539
|
// Requires `gh repo set-default` to be configured; fails gracefully if not
|
|
482
|
-
const result = await $`gh pr view --json number,url`.quiet().nothrow();
|
|
540
|
+
const result = await $`gh pr view --json number,url`.cwd(lookupCwd).quiet().nothrow();
|
|
483
541
|
if (this.#disposed) return;
|
|
484
542
|
if (result.exitCode !== 0) {
|
|
485
543
|
setCachedPr(null);
|
|
@@ -746,6 +804,7 @@ export class StatusLineComponent implements Component {
|
|
|
746
804
|
#buildSegmentContext(
|
|
747
805
|
width: number,
|
|
748
806
|
segmentOptions: StatusLineSettings["segmentOptions"],
|
|
807
|
+
includePath: boolean,
|
|
749
808
|
includeContext: boolean,
|
|
750
809
|
includeGit: boolean,
|
|
751
810
|
includePr: boolean,
|
|
@@ -788,13 +847,18 @@ export class StatusLineComponent implements Component {
|
|
|
788
847
|
contextPercent = collabState.contextUsage.percent ?? contextPercent;
|
|
789
848
|
}
|
|
790
849
|
|
|
791
|
-
const
|
|
792
|
-
const
|
|
793
|
-
const
|
|
794
|
-
|
|
850
|
+
const shouldResolveActiveRepo = this.#gitEnabled() && (includePath || includeGit || includePr);
|
|
851
|
+
const projectDir = getProjectDir();
|
|
852
|
+
const activeRepoCache = shouldResolveActiveRepo
|
|
853
|
+
? this.#resolveActiveRepoCache()
|
|
854
|
+
: { projectDir, activeRepo: null, effectiveGitCwd: projectDir };
|
|
855
|
+
const gitBranch = includeGit || includePr ? this.#getCurrentBranch(activeRepoCache.effectiveGitCwd) : null;
|
|
856
|
+
const gitStatus = includeGit ? this.#getGitStatus(activeRepoCache.effectiveGitCwd) : null;
|
|
857
|
+
const gitPr = includePr ? this.#lookupPr(activeRepoCache.effectiveGitCwd) : null;
|
|
795
858
|
return {
|
|
796
859
|
session: this.session,
|
|
797
860
|
focusedAgentId: this.#focusedAgentId,
|
|
861
|
+
activeRepo: activeRepoCache.activeRepo,
|
|
798
862
|
width,
|
|
799
863
|
options: segmentOptions ?? {},
|
|
800
864
|
planMode: this.#planModeStatus,
|
|
@@ -858,8 +922,17 @@ export class StatusLineComponent implements Component {
|
|
|
858
922
|
};
|
|
859
923
|
}
|
|
860
924
|
|
|
925
|
+
#subagentBadgeText(): string | undefined {
|
|
926
|
+
if (this.#subagentCount === 0) return undefined;
|
|
927
|
+
const noun = this.#subagentCount === 1 ? "agent" : "agents";
|
|
928
|
+
const hubHint = this.#subagentHubHint ? ` — ${this.#subagentHubHint} hub` : " — Agent Hub";
|
|
929
|
+
return theme.fg("statusLineSubagents", `${theme.icon.agents} ${this.#subagentCount} ${noun} running${hubHint}`);
|
|
930
|
+
}
|
|
931
|
+
|
|
861
932
|
#buildStatusLine(width: number): string {
|
|
862
933
|
const effectiveSettings = this.#resolveSettings();
|
|
934
|
+
const includePath =
|
|
935
|
+
hasPathSegment(effectiveSettings.leftSegments) || hasPathSegment(effectiveSettings.rightSegments);
|
|
863
936
|
const includeContext =
|
|
864
937
|
hasContextSegment(effectiveSettings.leftSegments) || hasContextSegment(effectiveSettings.rightSegments);
|
|
865
938
|
const gitEnabled = this.#gitEnabled();
|
|
@@ -871,6 +944,7 @@ export class StatusLineComponent implements Component {
|
|
|
871
944
|
const ctx = this.#buildSegmentContext(
|
|
872
945
|
width,
|
|
873
946
|
effectiveSettings.segmentOptions,
|
|
947
|
+
includePath,
|
|
874
948
|
includeContext,
|
|
875
949
|
includeGit,
|
|
876
950
|
includePr,
|
|
@@ -888,11 +962,13 @@ export class StatusLineComponent implements Component {
|
|
|
888
962
|
const transparentBg = bgAnsi === TRANSPARENT_BG_ANSI;
|
|
889
963
|
const fgAnsi = theme.getFgAnsi("text");
|
|
890
964
|
const sepAnsi = theme.getFgAnsi("statusLineSep");
|
|
965
|
+
const subagentBadge = this.#subagentBadgeText();
|
|
891
966
|
|
|
892
967
|
// Collect visible segment contents
|
|
893
968
|
const leftParts: string[] = [];
|
|
894
969
|
const leftSegIds: StatusLineSegmentId[] = [];
|
|
895
970
|
for (const segId of effectiveSettings.leftSegments) {
|
|
971
|
+
if (subagentBadge && segId === "subagents") continue;
|
|
896
972
|
const rendered = renderSegment(segId, ctx);
|
|
897
973
|
if (rendered.visible && rendered.content) {
|
|
898
974
|
leftParts.push(rendered.content);
|
|
@@ -902,6 +978,7 @@ export class StatusLineComponent implements Component {
|
|
|
902
978
|
|
|
903
979
|
const rightParts: string[] = [];
|
|
904
980
|
for (const segId of effectiveSettings.rightSegments) {
|
|
981
|
+
if (subagentBadge && segId === "subagents") continue;
|
|
905
982
|
const rendered = renderSegment(segId, ctx);
|
|
906
983
|
if (rendered.visible && rendered.content) {
|
|
907
984
|
rightParts.push(rendered.content);
|
|
@@ -912,6 +989,9 @@ export class StatusLineComponent implements Component {
|
|
|
912
989
|
if (runningBackgroundJobs > 0) {
|
|
913
990
|
rightParts.unshift(theme.fg("statusLineSubagents", `${theme.icon.job} ${runningBackgroundJobs}`));
|
|
914
991
|
}
|
|
992
|
+
if (subagentBadge) {
|
|
993
|
+
rightParts.unshift(subagentBadge);
|
|
994
|
+
}
|
|
915
995
|
const topFillWidth = Math.max(0, width);
|
|
916
996
|
const left = [...leftParts];
|
|
917
997
|
const right = [...rightParts];
|
|
@@ -202,7 +202,7 @@ const pathSegment: StatusLineSegment = {
|
|
|
202
202
|
render(ctx) {
|
|
203
203
|
const opts = ctx.options.path ?? {};
|
|
204
204
|
|
|
205
|
-
const projectDir = getProjectDir();
|
|
205
|
+
const projectDir = ctx.activeRepo?.cwd ?? getProjectDir();
|
|
206
206
|
const { scratch, relative } = classifyProjectDir(projectDir);
|
|
207
207
|
let pwd = projectDir;
|
|
208
208
|
|
|
@@ -213,6 +213,7 @@ const pathSegment: StatusLineSegment = {
|
|
|
213
213
|
pwd = stripDisplayRoot(pwd);
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
|
+
const repoSuffix = ctx.activeRepo ? ` ↳ ${ctx.activeRepo.relativeRepoRoot}` : "";
|
|
216
217
|
if (opts.abbreviate !== false) {
|
|
217
218
|
pwd = shortenPath(pwd);
|
|
218
219
|
}
|
|
@@ -223,6 +224,9 @@ const pathSegment: StatusLineSegment = {
|
|
|
223
224
|
const sliceLen = Math.max(0, maxLen - ellipsis.length);
|
|
224
225
|
pwd = `${ellipsis}${pwd.slice(-sliceLen)}`;
|
|
225
226
|
}
|
|
227
|
+
if (repoSuffix) {
|
|
228
|
+
pwd = `${pwd}${repoSuffix}`;
|
|
229
|
+
}
|
|
226
230
|
|
|
227
231
|
const showScratchIcon = scratch && opts.stripWorkPrefix !== false;
|
|
228
232
|
const icon = showScratchIcon ? theme.icon.scratchFolder : theme.icon.folder;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CollabSessionState } from "../../../collab/protocol";
|
|
2
2
|
import type { StatusLinePreset, StatusLineSegmentId, StatusLineSeparatorStyle } from "../../../config/settings-schema";
|
|
3
3
|
import type { AgentSession } from "../../../session/agent-session";
|
|
4
|
+
import type { ActiveRepoContext } from "../../../utils/active-repo-context";
|
|
4
5
|
|
|
5
6
|
export type { StatusLinePreset, StatusLineSegmentId, StatusLineSeparatorStyle };
|
|
6
7
|
|
|
@@ -47,6 +48,7 @@ export interface SegmentContext {
|
|
|
47
48
|
session: AgentSession;
|
|
48
49
|
/** Focused subagent id while the view is proxied at its session, undefined otherwise. */
|
|
49
50
|
focusedAgentId?: string | undefined;
|
|
51
|
+
activeRepo: ActiveRepoContext | null;
|
|
50
52
|
width: number;
|
|
51
53
|
options: StatusLineSegmentOptions;
|
|
52
54
|
planMode: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Container, type SelectItem, SelectList } from "@oh-my-pi/pi-tui";
|
|
1
|
+
import { Container, type SelectItem, SelectList, type SgrMouseEvent } from "@oh-my-pi/pi-tui";
|
|
2
2
|
import { getSelectListTheme } from "../../modes/theme/theme";
|
|
3
3
|
import { DynamicBorder } from "./dynamic-border";
|
|
4
|
+
import { routeSelectListMouseWithTopBorder } from "./select-list-mouse-routing";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Component that renders a theme selector.
|
|
@@ -60,4 +61,8 @@ export class ThemeSelectorComponent extends Container {
|
|
|
60
61
|
getSelectList(): SelectList {
|
|
61
62
|
return this.#selectList;
|
|
62
63
|
}
|
|
64
|
+
|
|
65
|
+
routeMouse(event: SgrMouseEvent, line: number, col: number): void {
|
|
66
|
+
routeSelectListMouseWithTopBorder(this.#selectList, event, line, col);
|
|
67
|
+
}
|
|
63
68
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Effort } from "@oh-my-pi/pi-ai";
|
|
2
|
-
import { Container, type SelectItem, SelectList } from "@oh-my-pi/pi-tui";
|
|
2
|
+
import { Container, type SelectItem, SelectList, type SgrMouseEvent } from "@oh-my-pi/pi-tui";
|
|
3
3
|
import { getSelectListTheme } from "../../modes/theme/theme";
|
|
4
4
|
import { getThinkingLevelMetadata } from "../../thinking";
|
|
5
5
|
import { DynamicBorder } from "./dynamic-border";
|
|
6
|
+
import { routeSelectListMouseWithTopBorder } from "./select-list-mouse-routing";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Component that renders a thinking level selector with borders
|
|
@@ -49,4 +50,8 @@ export class ThinkingSelectorComponent extends Container {
|
|
|
49
50
|
getSelectList(): SelectList {
|
|
50
51
|
return this.#selectList;
|
|
51
52
|
}
|
|
53
|
+
|
|
54
|
+
routeMouse(event: SgrMouseEvent, line: number, col: number): void {
|
|
55
|
+
routeSelectListMouseWithTopBorder(this.#selectList, event, line, col);
|
|
56
|
+
}
|
|
52
57
|
}
|
|
@@ -655,15 +655,31 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
655
655
|
// `provisionalPendingPreview` describes only the PENDING call preview
|
|
656
656
|
// (`renderCall`, before any result): the result render may re-anchor it
|
|
657
657
|
// wholesale, so its rows must never commit. Once a (streaming partial)
|
|
658
|
-
// result exists the result renderer is the live shape — its body
|
|
659
|
-
// top-anchored and grows append-only, and `deriveLiveCommitState`
|
|
660
|
-
// per-row durability — so the block is commit-stable like any
|
|
661
|
-
// stream. Gating the flag on the pending phase is what keeps a
|
|
662
|
-
// streaming eval/bash/ssh whose box outgrows the viewport from
|
|
663
|
-
// its head: while commit-unstable its scrolled-off top
|
|
664
|
-
// and repainted nowhere, so it read as truncated until
|
|
665
|
-
// flipped it stable.
|
|
666
|
-
|
|
658
|
+
// result exists the result renderer is usually the live shape — its body
|
|
659
|
+
// is top-anchored and grows append-only, and `deriveLiveCommitState`
|
|
660
|
+
// gates per-row durability — so the block is commit-stable like any
|
|
661
|
+
// settled stream. Gating the flag on the pending phase is what keeps a
|
|
662
|
+
// collapsed streaming eval/bash/ssh whose box outgrows the viewport from
|
|
663
|
+
// stranding its head: while commit-unstable its scrolled-off top
|
|
664
|
+
// committed nowhere and repainted nowhere, so it read as truncated until
|
|
665
|
+
// ctrl+o (expanded) flipped it stable.
|
|
666
|
+
//
|
|
667
|
+
// Renderers whose partial-result chrome (header glyph, frame state)
|
|
668
|
+
// differs from the final result render set `provisionalPartialResult`
|
|
669
|
+
// to opt out of stream-commit while `isPartial` holds: the ratchet
|
|
670
|
+
// would otherwise promote the stable partial chrome to native scrollback
|
|
671
|
+
// after `STABLE_PREFIX_COMMIT_FRAMES` and leave it stranded above the
|
|
672
|
+
// final frame once the chrome flips. Once the result settles
|
|
673
|
+
// (`isPartial === false`) the block is commit-stable again.
|
|
674
|
+
if (this.#result !== undefined) {
|
|
675
|
+
if (this.#isPartial) {
|
|
676
|
+
const tool = this.#tool as { provisionalPartialResult?: boolean } | undefined;
|
|
677
|
+
const provisionalPartialResult =
|
|
678
|
+
tool?.provisionalPartialResult ?? toolRenderers[this.#toolName]?.provisionalPartialResult;
|
|
679
|
+
if (provisionalPartialResult) return false;
|
|
680
|
+
}
|
|
681
|
+
return true;
|
|
682
|
+
}
|
|
667
683
|
const tool = this.#tool as { provisionalPendingPreview?: boolean | "collapsed" } | undefined;
|
|
668
684
|
const provisionalPendingPreview =
|
|
669
685
|
tool?.provisionalPendingPreview ?? toolRenderers[this.#toolName]?.provisionalPendingPreview;
|
|
@@ -15,8 +15,8 @@ import type { TreeFilterMode } from "../../config/settings-schema";
|
|
|
15
15
|
import { theme } from "../../modes/theme/theme";
|
|
16
16
|
import { matchesAppInterrupt, matchesSelectDown, matchesSelectUp } from "../../modes/utils/keybinding-matchers";
|
|
17
17
|
import type { SessionTreeNode } from "../../session/session-entries";
|
|
18
|
+
import { toPathList } from "../../tools/grep";
|
|
18
19
|
import { shortenPath } from "../../tools/render-utils";
|
|
19
|
-
import { toPathList } from "../../tools/search";
|
|
20
20
|
import { canonicalizeMessage } from "../../utils/thinking-display";
|
|
21
21
|
import { DynamicBorder } from "./dynamic-border";
|
|
22
22
|
import { centeredWindow, contentRowWidth, renderScrollableList } from "./selector-helpers";
|
|
@@ -740,7 +740,7 @@ class TreeList implements Component {
|
|
|
740
740
|
.slice(0, 50);
|
|
741
741
|
return `[bash: ${cmd}${rawCmd.length > 50 ? "..." : ""}]`;
|
|
742
742
|
}
|
|
743
|
-
case "
|
|
743
|
+
case "grep": {
|
|
744
744
|
const pattern = String(args.pattern || "");
|
|
745
745
|
const searchPathsInput =
|
|
746
746
|
typeof args.paths === "string" || Array.isArray(args.paths)
|
|
@@ -750,11 +750,11 @@ class TreeList implements Component {
|
|
|
750
750
|
: undefined;
|
|
751
751
|
const paths = toPathList(searchPathsInput);
|
|
752
752
|
const scope = paths.length > 0 ? paths.join(", ") : ".";
|
|
753
|
-
return `[
|
|
753
|
+
return `[grep: /${pattern}/ in ${shortenPath(scope)}]`;
|
|
754
754
|
}
|
|
755
|
-
case "
|
|
755
|
+
case "glob": {
|
|
756
756
|
const paths = Array.isArray(args.paths) ? args.paths.join(", ") : String(args.pattern || ".");
|
|
757
|
-
return `[
|
|
757
|
+
return `[glob: ${shortenPath(paths)}]`;
|
|
758
758
|
}
|
|
759
759
|
case "ls": {
|
|
760
760
|
const path = shortenPath(String(args.path || "."));
|