@oh-my-pi/pi-coding-agent 17.2.8 → 17.2.10
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 +94 -1
- package/dist/{CHANGELOG-0b0w17se.md → CHANGELOG-rhwzpexa.md} +94 -1
- package/dist/cli.js +13216 -15614
- package/dist/types/async/job-manager.d.ts +12 -0
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/config/model-resolver.d.ts +13 -0
- package/dist/types/discovery/agents.d.ts +11 -0
- package/dist/types/extensibility/custom-commands/types.d.ts +5 -5
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
- package/dist/types/extensibility/extensions/loader.d.ts +2 -2
- package/dist/types/extensibility/extensions/types.d.ts +34 -8
- package/dist/types/extensibility/hooks/types.d.ts +5 -5
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +10 -0
- package/dist/types/hindsight/state.d.ts +0 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/lsp/utils.d.ts +8 -0
- package/dist/types/main.d.ts +1 -1
- package/dist/types/modes/acp/acp-agent.d.ts +1 -1
- package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -1
- package/dist/types/modes/acp/acp-event-mapper.d.ts +1 -1
- package/dist/types/modes/acp/acp-mode.d.ts +1 -1
- package/dist/types/modes/components/agent-hub-projection.d.ts +38 -0
- package/dist/types/modes/components/agent-hub-renderer.d.ts +41 -0
- package/dist/types/modes/components/agent-hub.d.ts +12 -5
- package/dist/types/modes/components/model-browser.d.ts +9 -1
- package/dist/types/modes/interactive-mode.d.ts +8 -1
- package/dist/types/modes/session-observer-registry.d.ts +2 -0
- package/dist/types/registry/agent-lifecycle.d.ts +1 -1
- package/dist/types/registry/agent-registry.d.ts +47 -0
- package/dist/types/registry/persisted-agents.d.ts +3 -1
- package/dist/types/session/agent-session-types.d.ts +7 -0
- package/dist/types/session/agent-session.d.ts +8 -4
- package/dist/types/session/session-entries.d.ts +10 -0
- package/dist/types/session/session-loader.d.ts +11 -1
- package/dist/types/session/session-manager.d.ts +9 -1
- package/dist/types/session/session-tools.d.ts +10 -1
- package/dist/types/session/turn-recovery.d.ts +6 -2
- package/dist/types/slash-commands/acp-builtins.d.ts +1 -1
- package/dist/types/slash-commands/available-commands.d.ts +1 -1
- package/dist/types/task/executor.d.ts +10 -0
- package/dist/types/task/index.d.ts +2 -3
- package/dist/types/task/read-only-policy.d.ts +3 -0
- package/dist/types/task/structured-subagent.d.ts +2 -0
- package/dist/types/task/types.d.ts +4 -0
- package/dist/types/tools/bash.d.ts +3 -3
- package/dist/types/tools/browser/launch.d.ts +3 -0
- package/dist/types/tools/browser/tab-worker.d.ts +2 -2
- package/dist/types/tools/path-utils.d.ts +8 -0
- package/dist/types/tools/read.d.ts +3 -5
- package/dist/types/tools/run-scope.d.ts +27 -1
- package/dist/types/tools/shell-tokenize.d.ts +21 -3
- package/dist/types/tools/terminal-output.d.ts +1 -1
- package/dist/types/utils/changelog.d.ts +3 -2
- package/dist/types/utils/late-cleanup.d.ts +2 -0
- package/dist/types/utils/turndown.d.ts +1 -1
- package/dist/types/vibe/runtime.d.ts +4 -3
- package/dist/types/web/scrapers/readthedocs.d.ts +0 -3
- package/dist/types/web/search/providers/browser-headers.d.ts +2 -3
- package/examples/custom-tools/README.md +1 -1
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/api-demo.ts +5 -6
- package/examples/extensions/chalk-logger.ts +1 -1
- package/examples/extensions/hello.ts +2 -2
- package/examples/extensions/reload-runtime.ts +1 -1
- package/examples/extensions/tools.ts +2 -2
- package/examples/extensions/with-deps/index.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/package.json +15 -31
- package/scripts/bundle-dist.ts +1 -11
- package/scripts/omp +11 -2
- package/src/advisor/runtime.ts +54 -0
- package/src/async/job-manager.ts +26 -0
- package/src/cli/agents-cli.ts +1 -1
- package/src/cli/args.ts +24 -2
- package/src/cli/auth-broker-cli.ts +1 -1
- package/src/cli/auth-gateway-cli.ts +1 -1
- package/src/cli/bench-cli.ts +1 -1
- package/src/cli/claude-trace-cli.ts +1 -1
- package/src/cli/config-cli.ts +1 -1
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/file-processor.ts +1 -1
- package/src/cli/flag-tables.ts +4 -0
- package/src/cli/grep-cli.ts +1 -1
- package/src/cli/grievances-cli.ts +1 -1
- package/src/cli/help-extra.ts +1 -1
- package/src/cli/models-cli.ts +1 -1
- package/src/cli/plugin-cli.ts +1 -1
- package/src/cli/read-cli.ts +1 -1
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli/shell-cli.ts +1 -1
- package/src/cli/ssh-cli.ts +1 -1
- package/src/cli/stats-cli.ts +4 -4
- package/src/cli/tiny-models-cli.ts +1 -1
- package/src/cli/ttsr-cli.ts +1 -1
- package/src/cli/update-cli.ts +3 -57
- package/src/cli/usage-cli.ts +1 -1
- package/src/cli/web-search-cli.ts +1 -1
- package/src/cli/worktree-cli.ts +1 -1
- package/src/commands/say.ts +1 -1
- package/src/commands/token.ts +1 -1
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/cli.ts +1 -1
- package/src/config/config-file.ts +2 -2
- package/src/config/keybindings.ts +3 -3
- package/src/config/model-discovery.ts +36 -2
- package/src/config/model-registry.ts +4 -1
- package/src/config/model-resolver.ts +54 -7
- package/src/discovery/agents.ts +73 -3
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +5 -4
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +1 -1
- package/src/discovery/claude.ts +8 -5
- package/src/discovery/cursor.ts +8 -5
- package/src/discovery/gemini.ts +11 -8
- package/src/discovery/vscode.ts +1 -0
- package/src/discovery/windsurf.ts +4 -2
- package/src/edit/hashline/filesystem.ts +7 -7
- package/src/eval/agent-bridge.ts +1 -3
- package/src/eval/jl/prelude.jl +4 -4
- package/src/eval/js/shared/prelude.txt +2 -2
- package/src/eval/py/prelude.py +0 -3
- package/src/eval/py/runner.py +38 -1
- package/src/eval/rb/prelude.rb +1 -2
- package/src/extensibility/custom-commands/loader.ts +4 -4
- package/src/extensibility/custom-commands/types.ts +5 -5
- package/src/extensibility/custom-tools/loader.ts +4 -4
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/loader.ts +7 -6
- package/src/extensibility/extensions/types.ts +39 -8
- package/src/extensibility/hooks/loader.ts +4 -5
- package/src/extensibility/hooks/types.ts +5 -5
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +23 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +53 -0
- package/src/hindsight/state.ts +2 -22
- package/src/index.ts +2 -2
- package/src/internal-urls/memory-protocol.ts +33 -0
- package/src/launch/terminal-output.ts +1 -1
- package/src/lsp/client.ts +2 -2
- package/src/lsp/utils.ts +29 -0
- package/src/main.ts +85 -21
- package/src/markit/converters/docx.ts +1 -1
- package/src/markit/converters/epub.ts +1 -1
- package/src/markit/converters/pptx.ts +1 -1
- package/src/markit/converters/xlsx.ts +1 -1
- package/src/modes/acp/acp-agent.ts +7 -6
- package/src/modes/acp/acp-client-bridge.ts +1 -1
- package/src/modes/acp/acp-event-mapper.ts +1 -1
- package/src/modes/acp/acp-mode.ts +1 -1
- package/src/modes/components/agent-hub-projection.ts +248 -0
- package/src/modes/components/agent-hub-renderer.ts +194 -0
- package/src/modes/components/agent-hub.ts +670 -192
- package/src/modes/components/agent-transcript-viewer.ts +1 -3
- package/src/modes/components/assistant-message.ts +1 -1
- package/src/modes/components/bordered-loader.ts +1 -1
- package/src/modes/components/custom-editor.ts +2 -2
- package/src/modes/components/extensions/inspector-panel.ts +11 -6
- package/src/modes/components/footer.ts +1 -3
- package/src/modes/components/hook-input.ts +1 -1
- package/src/modes/components/hook-selector.ts +2 -2
- package/src/modes/components/model-browser.ts +11 -3
- package/src/modes/components/model-hub.ts +4 -1
- package/src/modes/components/session-selector.ts +2 -2
- package/src/modes/components/status-line/segments.ts +4 -3
- package/src/modes/controllers/event-controller.ts +188 -7
- package/src/modes/controllers/extension-ui-controller.ts +4 -3
- package/src/modes/controllers/mcp-command-controller.ts +81 -20
- package/src/modes/controllers/selector-controller.ts +40 -49
- package/src/modes/controllers/streaming-reveal.ts +1 -1
- package/src/modes/interactive-mode.ts +13 -2
- package/src/modes/print-mode.ts +29 -12
- package/src/modes/rpc/rpc-mode.ts +2 -2
- package/src/modes/runtime-init.ts +1 -1
- package/src/modes/session-observer-registry.ts +5 -0
- package/src/modes/theme/theme.ts +2 -2
- package/src/prompts/tools/computer.md +1 -1
- package/src/registry/agent-lifecycle.ts +35 -10
- package/src/registry/agent-registry.ts +65 -2
- package/src/registry/persisted-agents.ts +341 -16
- package/src/sdk.ts +11 -5
- package/src/session/agent-session-types.ts +8 -0
- package/src/session/agent-session.ts +177 -188
- package/src/session/session-entries.ts +10 -0
- package/src/session/session-listing.ts +1 -1
- package/src/session/session-loader.ts +74 -8
- package/src/session/session-manager.ts +22 -2
- package/src/session/session-paths.ts +105 -110
- package/src/session/session-tools.ts +28 -1
- package/src/session/settings-stream-fn.ts +7 -3
- package/src/session/turn-recovery.ts +236 -18
- package/src/session/unexpected-stop-classifier.ts +12 -3
- package/src/slash-commands/acp-builtins.ts +1 -1
- package/src/slash-commands/available-commands.ts +1 -1
- package/src/slash-commands/helpers/stats-dashboard.ts +2 -1
- package/src/task/agents.ts +1 -1
- package/src/task/executor.ts +173 -47
- package/src/task/index.ts +9 -28
- package/src/task/isolation-runner.ts +43 -12
- package/src/task/persisted-revive.ts +12 -5
- package/src/task/read-only-policy.ts +27 -0
- package/src/task/structured-subagent.ts +31 -6
- package/src/task/types.ts +4 -0
- package/src/tiny/worker.ts +1 -1
- package/src/tools/auto-generated-guard.ts +1 -1
- package/src/tools/bash-interactive.ts +4 -4
- package/src/tools/bash-interceptor.ts +8 -4
- package/src/tools/bash.ts +6 -6
- package/src/tools/browser/cmux/cmux-tab.ts +66 -18
- package/src/tools/browser/launch.ts +76 -19
- package/src/tools/browser/readable.ts +9 -9
- package/src/tools/browser/tab-supervisor.ts +12 -3
- package/src/tools/browser/tab-worker-entry.ts +1 -1
- package/src/tools/browser/tab-worker.ts +101 -12
- package/src/tools/fetch.ts +1 -1
- package/src/tools/glob.ts +4 -2
- package/src/tools/path-utils.ts +22 -5
- package/src/tools/read.ts +22 -5
- package/src/tools/run-scope.ts +290 -4
- package/src/tools/shell-tokenize.ts +38 -9
- package/src/tools/terminal-output.ts +1 -1
- package/src/tools/todo.ts +1 -1
- package/src/utils/changelog.ts +5 -4
- package/src/utils/clipboard.ts +10 -2
- package/src/utils/external-editor.ts +4 -2
- package/src/utils/jj.ts +1 -1
- package/src/utils/late-cleanup.ts +17 -0
- package/src/utils/turndown.ts +1 -2
- package/src/vibe/runtime.ts +202 -58
- package/src/web/scrapers/arxiv.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +1 -1
- package/src/web/scrapers/hackage.ts +1 -12
- package/src/web/scrapers/iacr.ts +1 -1
- package/src/web/scrapers/readthedocs.ts +2 -1
- package/src/web/scrapers/twitter.ts +2 -2
- package/src/web/scrapers/types.ts +1 -1
- package/src/web/scrapers/wikipedia.ts +1 -1
- package/src/web/search/providers/browser-headers.ts +12 -39
- package/src/web/search/providers/codex.ts +3 -26
- package/src/web/search/providers/duckduckgo.ts +1 -1
- package/src/web/search/providers/ecosia.ts +2 -2
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/firecrawl.ts +1 -1
- package/src/web/search/providers/google.ts +1 -1
- package/src/web/search/providers/mojeek.ts +2 -2
- package/src/web/search/providers/perplexity.ts +1 -1
- package/src/web/search/providers/public.ts +1 -1
- package/src/web/search/providers/startpage.ts +2 -2
- package/src/markit/converters/mammoth.d.ts +0 -24
package/src/cli/update-cli.ts
CHANGED
|
@@ -10,9 +10,9 @@ import * as os from "node:os";
|
|
|
10
10
|
import * as path from "node:path";
|
|
11
11
|
import { Transform } from "node:stream";
|
|
12
12
|
import { pipeline } from "node:stream/promises";
|
|
13
|
-
import { $env, $which, APP_NAME, isEnoent, VERSION } from "@oh-my-pi/pi-utils";
|
|
13
|
+
import { $env, $which, APP_NAME, compareVersions, isEnoent, VERSION } from "@oh-my-pi/pi-utils";
|
|
14
|
+
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
14
15
|
import { $ } from "bun";
|
|
15
|
-
import chalk from "chalk";
|
|
16
16
|
import { theme } from "../modes/theme/theme";
|
|
17
17
|
import { isTimeoutError, withTimeoutSignal } from "../utils/fetch-timeout";
|
|
18
18
|
|
|
@@ -498,24 +498,6 @@ async function getLatestRelease(): Promise<ReleaseInfo> {
|
|
|
498
498
|
};
|
|
499
499
|
}
|
|
500
500
|
|
|
501
|
-
/**
|
|
502
|
-
* Compare semver versions. Returns:
|
|
503
|
-
* - negative if a < b
|
|
504
|
-
* - 0 if a == b
|
|
505
|
-
* - positive if a > b
|
|
506
|
-
*/
|
|
507
|
-
function compareVersions(a: string, b: string): number {
|
|
508
|
-
const pa = a.split(".").map(Number);
|
|
509
|
-
const pb = b.split(".").map(Number);
|
|
510
|
-
|
|
511
|
-
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
512
|
-
const na = pa[i] || 0;
|
|
513
|
-
const nb = pb[i] || 0;
|
|
514
|
-
if (na !== nb) return na - nb;
|
|
515
|
-
}
|
|
516
|
-
return 0;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
501
|
interface BunInstallCachePruneResult {
|
|
520
502
|
scannedPackages: number;
|
|
521
503
|
removedEntries: number;
|
|
@@ -532,42 +514,6 @@ function stripBunCacheVersionSuffix(name: string): string {
|
|
|
532
514
|
return metadataIndex === -1 ? name : name.slice(0, metadataIndex);
|
|
533
515
|
}
|
|
534
516
|
|
|
535
|
-
function compareSemverIdentifier(a: string, b: string): number {
|
|
536
|
-
const aNumber = /^\d+$/.test(a);
|
|
537
|
-
const bNumber = /^\d+$/.test(b);
|
|
538
|
-
if (aNumber && bNumber) return Number(a) - Number(b);
|
|
539
|
-
if (aNumber) return -1;
|
|
540
|
-
if (bNumber) return 1;
|
|
541
|
-
return a.localeCompare(b);
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
function compareSemverLikeVersions(a: string, b: string): number {
|
|
545
|
-
const [aCoreWithPrerelease] = a.split("+", 1);
|
|
546
|
-
const [bCoreWithPrerelease] = b.split("+", 1);
|
|
547
|
-
const [aCore, aPrerelease] = aCoreWithPrerelease.split("-", 2);
|
|
548
|
-
const [bCore, bPrerelease] = bCoreWithPrerelease.split("-", 2);
|
|
549
|
-
const aParts = aCore.split(".");
|
|
550
|
-
const bParts = bCore.split(".");
|
|
551
|
-
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
552
|
-
const diff = Number(aParts[i] ?? 0) - Number(bParts[i] ?? 0);
|
|
553
|
-
if (diff !== 0 && Number.isFinite(diff)) return diff;
|
|
554
|
-
}
|
|
555
|
-
if (!aPrerelease && !bPrerelease) return 0;
|
|
556
|
-
if (!aPrerelease) return 1;
|
|
557
|
-
if (!bPrerelease) return -1;
|
|
558
|
-
const aPrereleaseParts = aPrerelease.split(".");
|
|
559
|
-
const bPrereleaseParts = bPrerelease.split(".");
|
|
560
|
-
for (let i = 0; i < Math.max(aPrereleaseParts.length, bPrereleaseParts.length); i++) {
|
|
561
|
-
const aPart = aPrereleaseParts[i];
|
|
562
|
-
const bPart = bPrereleaseParts[i];
|
|
563
|
-
if (aPart === undefined) return -1;
|
|
564
|
-
if (bPart === undefined) return 1;
|
|
565
|
-
const diff = compareSemverIdentifier(aPart, bPart);
|
|
566
|
-
if (diff !== 0) return diff;
|
|
567
|
-
}
|
|
568
|
-
return 0;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
517
|
async function readdirIfExists(dir: string): Promise<fs.Dirent[]> {
|
|
572
518
|
try {
|
|
573
519
|
return await fs.promises.readdir(dir, { withFileTypes: true });
|
|
@@ -689,7 +635,7 @@ export async function pruneBunInstallCache(
|
|
|
689
635
|
scannedPackages++;
|
|
690
636
|
let latestVersion: string | undefined;
|
|
691
637
|
for (const version of group.actualDirs.keys()) {
|
|
692
|
-
if (!latestVersion ||
|
|
638
|
+
if (!latestVersion || compareVersions(version, latestVersion) > 0) latestVersion = version;
|
|
693
639
|
}
|
|
694
640
|
if (!latestVersion) continue;
|
|
695
641
|
for (const [version, paths] of group.actualDirs) {
|
package/src/cli/usage-cli.ts
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
type UsageUnit,
|
|
19
19
|
} from "@oh-my-pi/pi-ai";
|
|
20
20
|
import { formatDuration, formatNumber, sanitizeText } from "@oh-my-pi/pi-utils";
|
|
21
|
-
import chalk from "chalk";
|
|
21
|
+
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
22
22
|
import { ModelRegistry } from "../config/model-registry";
|
|
23
23
|
import { discoverAuthStorage } from "../sdk";
|
|
24
24
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { APP_NAME, getProjectDir } from "@oh-my-pi/pi-utils";
|
|
8
|
-
import chalk from "chalk";
|
|
8
|
+
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
9
9
|
import { applyProviderGlobalsFromSettings } from "../config/provider-globals";
|
|
10
10
|
import { Settings } from "../config/settings";
|
|
11
11
|
import { initTheme, theme } from "../modes/theme/theme";
|
package/src/cli/worktree-cli.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import * as fs from "node:fs/promises";
|
|
20
20
|
import * as path from "node:path";
|
|
21
21
|
import { getWorktreesDir, isEnoent } from "@oh-my-pi/pi-utils";
|
|
22
|
-
import chalk from "chalk";
|
|
22
|
+
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
23
23
|
import { hasLiveIsolationOwner, ISOLATION_OWNER_FILE } from "../task/isolation-ownership";
|
|
24
24
|
import * as git from "../utils/git";
|
|
25
25
|
|
package/src/commands/say.ts
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { getProjectDir } from "@oh-my-pi/pi-utils";
|
|
13
|
+
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
13
14
|
import { Args, Command, Flags } from "@oh-my-pi/pi-utils/cli";
|
|
14
|
-
import chalk from "chalk";
|
|
15
15
|
import { sayHelp as commandHelp } from "../cli/command-help";
|
|
16
16
|
import { Settings, settings } from "../config/settings";
|
|
17
17
|
import { TTS_LOCAL_VOICE_VALUES } from "../tts/models";
|
package/src/commands/token.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { PROVIDER_REGISTRY } from "@oh-my-pi/pi-ai";
|
|
6
|
+
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
6
7
|
import { Args, Command, Flags } from "@oh-my-pi/pi-utils/cli";
|
|
7
|
-
import chalk from "chalk";
|
|
8
8
|
import { tokenHelp as commandHelp } from "../cli/command-help";
|
|
9
9
|
import { isAuthenticated, ModelRegistry } from "../config/model-registry";
|
|
10
10
|
import { discoverAuthStorage } from "../sdk";
|
|
@@ -2,8 +2,8 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
|
2
2
|
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { Markdown } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
|
+
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
5
6
|
import { INTENT_FIELD } from "@oh-my-pi/pi-wire";
|
|
6
|
-
import chalk from "chalk";
|
|
7
7
|
import typesDescriptionPrompt from "../../commit/prompts/types-description.md" with { type: "text" };
|
|
8
8
|
import type { ModelRegistry } from "../../config/model-registry";
|
|
9
9
|
import type { Settings } from "../../config/settings";
|
package/src/commit/cli.ts
CHANGED
|
@@ -109,11 +109,11 @@ export class ConfigError extends Error {
|
|
|
109
109
|
this.#message = message;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
get message(): string {
|
|
112
|
+
override get message(): string {
|
|
113
113
|
return this.#message;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
toString(): string {
|
|
116
|
+
override toString(): string {
|
|
117
117
|
return this.message;
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -607,12 +607,12 @@ export class KeybindingsManager extends TuiKeybindingsManager {
|
|
|
607
607
|
this.setUserBindings(mergeKeybindingsConfig(inheritedConfig, profileConfig));
|
|
608
608
|
}
|
|
609
609
|
|
|
610
|
-
setUserBindings(userBindings: KeybindingsConfig): void {
|
|
610
|
+
override setUserBindings(userBindings: KeybindingsConfig): void {
|
|
611
611
|
this.#userBindings = userBindings;
|
|
612
612
|
super.setUserBindings(userBindings);
|
|
613
613
|
}
|
|
614
614
|
|
|
615
|
-
getKeys(keybinding: Keybinding): KeyId[] {
|
|
615
|
+
override getKeys(keybinding: Keybinding): KeyId[] {
|
|
616
616
|
const keys = super.getKeys(keybinding);
|
|
617
617
|
const fallbackKey = getFallbackKey(keybinding);
|
|
618
618
|
if (fallbackKey === undefined || this.#userBindings[keybinding] !== undefined) return keys;
|
|
@@ -620,7 +620,7 @@ export class KeybindingsManager extends TuiKeybindingsManager {
|
|
|
620
620
|
return removeKey(keys, fallbackKey);
|
|
621
621
|
}
|
|
622
622
|
|
|
623
|
-
getResolvedBindings(): KeybindingsConfig {
|
|
623
|
+
override getResolvedBindings(): KeybindingsConfig {
|
|
624
624
|
const resolved = super.getResolvedBindings();
|
|
625
625
|
resolved[FOLLOW_UP_KEYBINDING] = keyConfigValue(this.getKeys(FOLLOW_UP_KEYBINDING));
|
|
626
626
|
return resolved;
|
|
@@ -724,6 +724,31 @@ export async function discoverLlamaCppModelRuntimeMetadata(
|
|
|
724
724
|
}
|
|
725
725
|
}
|
|
726
726
|
|
|
727
|
+
/**
|
|
728
|
+
* Read image-input support from an OpenAI-compatible `/v1/models` row. Handles
|
|
729
|
+
* direct `input` arrays, Synthetic-style top-level `input_modalities`, and
|
|
730
|
+
* OpenRouter-style `architecture.input_modalities`; returns undefined when none
|
|
731
|
+
* is present so the bundled reference (or the `["text"]` default) can take over.
|
|
732
|
+
*/
|
|
733
|
+
function extractOpenAIModelsListInputCapabilities(item: {
|
|
734
|
+
input?: unknown;
|
|
735
|
+
input_modalities?: unknown;
|
|
736
|
+
architecture?: unknown;
|
|
737
|
+
}): ("text" | "image")[] | undefined {
|
|
738
|
+
const modalities = new Set<string>();
|
|
739
|
+
const collect = (value: unknown): void => {
|
|
740
|
+
if (!Array.isArray(value)) return;
|
|
741
|
+
for (const entry of value) {
|
|
742
|
+
if (typeof entry === "string") modalities.add(entry.toLowerCase());
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
collect(item.input);
|
|
746
|
+
collect(item.input_modalities);
|
|
747
|
+
if (isRecord(item.architecture)) collect(item.architecture.input_modalities);
|
|
748
|
+
if (modalities.size === 0) return undefined;
|
|
749
|
+
return modalities.has("image") ? ["text", "image"] : ["text"];
|
|
750
|
+
}
|
|
751
|
+
|
|
727
752
|
export async function discoverOpenAIModelsList(
|
|
728
753
|
providerConfig: DiscoveryProviderConfig,
|
|
729
754
|
ctx: DiscoveryContext,
|
|
@@ -752,7 +777,14 @@ export async function discoverOpenAIModelsList(
|
|
|
752
777
|
}
|
|
753
778
|
headers = h;
|
|
754
779
|
return (await res.json()) as {
|
|
755
|
-
data?: Array<{
|
|
780
|
+
data?: Array<{
|
|
781
|
+
id?: string;
|
|
782
|
+
max_model_len?: unknown;
|
|
783
|
+
context_length?: unknown;
|
|
784
|
+
input?: unknown;
|
|
785
|
+
input_modalities?: unknown;
|
|
786
|
+
architecture?: unknown;
|
|
787
|
+
}>;
|
|
756
788
|
};
|
|
757
789
|
}),
|
|
758
790
|
nativeMetadataPromise,
|
|
@@ -796,7 +828,9 @@ export async function discoverOpenAIModelsList(
|
|
|
796
828
|
baseUrl,
|
|
797
829
|
reasoning: reference?.reasoning ?? false,
|
|
798
830
|
thinking: inheritReferenceThinking(undefined, reference, providerConfig.provider),
|
|
799
|
-
input: nativeMetadataForModel?.input ??
|
|
831
|
+
input: nativeMetadataForModel?.input ??
|
|
832
|
+
extractOpenAIModelsListInputCapabilities(item) ??
|
|
833
|
+
reference?.input ?? ["text"],
|
|
800
834
|
...(providerConfig.discovery.type === "lm-studio" ? { imageInputDecoder: "stb" as const } : {}),
|
|
801
835
|
// Proxy/gateway pricing is provider-specific and rarely matches
|
|
802
836
|
// upstream bundled catalogs, so keep costs local-unknown even
|
|
@@ -1715,7 +1715,10 @@ export class ModelRegistry {
|
|
|
1715
1715
|
return resolveOllamaModelCacheProviderId(providerConfig.provider, providerConfig.baseUrl);
|
|
1716
1716
|
}
|
|
1717
1717
|
if (providerConfig.discovery.type === "openai-models-list") {
|
|
1718
|
-
|
|
1718
|
+
// context-v3 invalidates rows cached before server-advertised input
|
|
1719
|
+
// modalities were parsed from `/v1/models`; warm v2 rows pinned
|
|
1720
|
+
// vision-capable ids at `input: ["text"]` until a forced refresh.
|
|
1721
|
+
return `${providerConfig.provider}:openai-models-list-context-v3`;
|
|
1719
1722
|
}
|
|
1720
1723
|
if (providerConfig.discovery.type === "litellm") {
|
|
1721
1724
|
// rich-v2 invalidates rows cached before reseller usage-suffix stripping
|
|
@@ -27,7 +27,7 @@ import { DEFAULT_MODEL_PER_PROVIDER } from "@oh-my-pi/pi-catalog/provider-models
|
|
|
27
27
|
import { resolveBareVariantAlias, resolveVariantAlias } from "@oh-my-pi/pi-catalog/variant-collapse";
|
|
28
28
|
import { fuzzyMatch } from "@oh-my-pi/pi-tui";
|
|
29
29
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
30
|
-
import chalk from "chalk";
|
|
30
|
+
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
31
31
|
import MODEL_PRIO from "../priority.json" with { type: "json" };
|
|
32
32
|
import {
|
|
33
33
|
AUTO_THINKING,
|
|
@@ -932,6 +932,28 @@ function normalizeModelPatternList(value: string | string[] | undefined): string
|
|
|
932
932
|
return patterns.map(pattern => pattern.trim()).filter(Boolean);
|
|
933
933
|
}
|
|
934
934
|
|
|
935
|
+
/**
|
|
936
|
+
* Extract the first explicit model-role alias from a raw model selection.
|
|
937
|
+
*
|
|
938
|
+
* This intentionally runs before role expansion so callers can retain the
|
|
939
|
+
* source identity (`@smol`, `pi/slow`, or `*`) even when it resolves to a
|
|
940
|
+
* concrete provider/model or inherited fallback. Bare role names and explicit
|
|
941
|
+
* provider/model selectors are not role aliases.
|
|
942
|
+
*/
|
|
943
|
+
export function resolveExplicitModelRole(
|
|
944
|
+
value: string | string[] | undefined,
|
|
945
|
+
settings?: ModelRoleLookup,
|
|
946
|
+
): string | undefined {
|
|
947
|
+
for (const pattern of normalizeModelPatternList(value)) {
|
|
948
|
+
const prefixLength = modelRoleAliasPrefixLength(pattern);
|
|
949
|
+
if (prefixLength === undefined) continue;
|
|
950
|
+
const { base } = splitThinkingSuffix(pattern, prefixLength, MAX_THINKING_SUFFIX_OPTIONS);
|
|
951
|
+
const role = getModelRoleAlias(base, settings);
|
|
952
|
+
if (role) return role;
|
|
953
|
+
}
|
|
954
|
+
return undefined;
|
|
955
|
+
}
|
|
956
|
+
|
|
935
957
|
function isSessionInheritedAgentPattern(value: string): boolean {
|
|
936
958
|
return (
|
|
937
959
|
value === DEFAULT_MODEL_ROLE ||
|
|
@@ -1068,6 +1090,8 @@ export function resolveConfiguredModelPatterns(
|
|
|
1068
1090
|
});
|
|
1069
1091
|
}
|
|
1070
1092
|
export interface AgentModelPatternResolutionOptions {
|
|
1093
|
+
/** Highest-priority request selector, when supplied by a caller. */
|
|
1094
|
+
requestModel?: string | string[];
|
|
1071
1095
|
settingsOverride?: string | string[];
|
|
1072
1096
|
agentModel?: string | string[];
|
|
1073
1097
|
settings?: Settings;
|
|
@@ -1075,11 +1099,25 @@ export interface AgentModelPatternResolutionOptions {
|
|
|
1075
1099
|
fallbackModelPattern?: string;
|
|
1076
1100
|
}
|
|
1077
1101
|
|
|
1078
|
-
|
|
1079
|
-
|
|
1102
|
+
interface EffectiveAgentModelSelection {
|
|
1103
|
+
source?: string | string[];
|
|
1104
|
+
patterns: string[];
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
function resolveEffectiveAgentModelSelection(
|
|
1108
|
+
options: AgentModelPatternResolutionOptions,
|
|
1109
|
+
): EffectiveAgentModelSelection {
|
|
1110
|
+
const { requestModel, settingsOverride, agentModel, settings, activeModelPattern, fallbackModelPattern } = options;
|
|
1111
|
+
|
|
1112
|
+
const requestPatterns = resolveConfiguredModelPatterns(requestModel, settings);
|
|
1113
|
+
if (requestPatterns.length > 0) {
|
|
1114
|
+
return { source: requestModel, patterns: requestPatterns };
|
|
1115
|
+
}
|
|
1080
1116
|
|
|
1081
1117
|
const overridePatterns = resolveConfiguredModelPatterns(settingsOverride, settings);
|
|
1082
|
-
if (overridePatterns.length > 0)
|
|
1118
|
+
if (overridePatterns.length > 0) {
|
|
1119
|
+
return { source: settingsOverride, patterns: overridePatterns };
|
|
1120
|
+
}
|
|
1083
1121
|
|
|
1084
1122
|
const normalizedAgentPatterns = normalizeModelPatternList(agentModel);
|
|
1085
1123
|
const configuredAgentPatterns = resolveConfiguredModelPatterns(agentModel, settings);
|
|
@@ -1090,14 +1128,23 @@ export function resolveAgentModelPatterns(options: AgentModelPatternResolutionOp
|
|
|
1090
1128
|
singleAgentPattern === formatModelRoleAlias("task") ||
|
|
1091
1129
|
singleAgentPattern === `${LEGACY_MODEL_ROLE_ALIAS_PREFIX}task`
|
|
1092
1130
|
) {
|
|
1093
|
-
return configuredAgentPatterns;
|
|
1131
|
+
return { source: agentModel, patterns: configuredAgentPatterns };
|
|
1094
1132
|
}
|
|
1095
|
-
if (!agentInheritsSessionModel) return configuredAgentPatterns;
|
|
1133
|
+
if (!agentInheritsSessionModel) return { source: agentModel, patterns: configuredAgentPatterns };
|
|
1096
1134
|
}
|
|
1097
1135
|
|
|
1098
1136
|
const fallback =
|
|
1099
1137
|
activeModelPattern?.trim() || fallbackModelPattern?.trim() || settings?.getModelRole("default")?.trim() || "";
|
|
1100
|
-
return resolveConfiguredModelPatterns(fallback, settings);
|
|
1138
|
+
return { patterns: resolveConfiguredModelPatterns(fallback, settings) };
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/** Return the raw selector source that supplies the effective agent patterns. */
|
|
1142
|
+
export function resolveAgentModelSource(options: AgentModelPatternResolutionOptions): string | string[] | undefined {
|
|
1143
|
+
return resolveEffectiveAgentModelSelection(options).source;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
export function resolveAgentModelPatterns(options: AgentModelPatternResolutionOptions): string[] {
|
|
1147
|
+
return resolveEffectiveAgentModelSelection(options).patterns;
|
|
1101
1148
|
}
|
|
1102
1149
|
/** Default prewalk hand-off target when no explicit target is configured. */
|
|
1103
1150
|
export const DEFAULT_PREWALK_TARGET = "@smol";
|
package/src/discovery/agents.ts
CHANGED
|
@@ -28,9 +28,79 @@ const DISPLAY_NAME = "Agent Dirs (.agent/.agents)";
|
|
|
28
28
|
const PRIORITY = 70;
|
|
29
29
|
const AGENT_DIR_CANDIDATES = [".agent", ".agents"] as const;
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
interface UserPathCandidateOptions {
|
|
32
|
+
platform?: NodeJS.Platform;
|
|
33
|
+
env?: NodeJS.ProcessEnv;
|
|
34
|
+
windowsUserProfile?: () => string | undefined;
|
|
35
|
+
wslPath?: (windowsPath: string) => string | undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const WINDOWS_DRIVE_PROFILE_PATTERN = /^([A-Za-z]):[\\/](.*)$/;
|
|
39
|
+
|
|
40
|
+
function isWsl(platform: NodeJS.Platform, env: NodeJS.ProcessEnv): boolean {
|
|
41
|
+
return platform === "linux" && Boolean(env.WSL_DISTRO_NAME || env.WSL_INTEROP);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function convertWindowsPathToDefaultWslMount(windowsPath: string): string | undefined {
|
|
45
|
+
const trimmed = windowsPath.trim();
|
|
46
|
+
if (trimmed.length === 0) return undefined;
|
|
47
|
+
// The result is always a WSL (POSIX) path, so build it with posix
|
|
48
|
+
// semantics regardless of the host platform.
|
|
49
|
+
if (path.posix.isAbsolute(trimmed)) return path.posix.normalize(trimmed);
|
|
50
|
+
const match = WINDOWS_DRIVE_PROFILE_PATTERN.exec(trimmed);
|
|
51
|
+
if (!match) return undefined;
|
|
52
|
+
const [, drive, rest] = match;
|
|
53
|
+
const segments = rest.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
54
|
+
return path.posix.join("/mnt", drive.toLowerCase(), ...segments);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function resolveWithWslPath(windowsPath: string): string | undefined {
|
|
58
|
+
try {
|
|
59
|
+
const result = Bun.spawnSync(["wslpath", "-u", windowsPath], { stdout: "pipe", stderr: "ignore" });
|
|
60
|
+
if (result.exitCode !== 0) return undefined;
|
|
61
|
+
const resolved = result.stdout.toString().trim();
|
|
62
|
+
return resolved.length > 0 ? resolved : undefined;
|
|
63
|
+
} catch {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function resolveWindowsUserProfile(): string | undefined {
|
|
69
|
+
try {
|
|
70
|
+
const result = Bun.spawnSync(["cmd.exe", "/d", "/c", "echo", "%USERPROFILE%"], {
|
|
71
|
+
stdout: "pipe",
|
|
72
|
+
stderr: "ignore",
|
|
73
|
+
});
|
|
74
|
+
if (result.exitCode !== 0) return undefined;
|
|
75
|
+
const resolved = result.stdout.toString().trim();
|
|
76
|
+
return resolved.length > 0 && resolved !== "%USERPROFILE%" ? resolved : undefined;
|
|
77
|
+
} catch {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Resolve the Windows host profile home exposed to WSL, if available. */
|
|
83
|
+
export function getWslWindowsHomeCandidate(options: UserPathCandidateOptions = {}): string | undefined {
|
|
84
|
+
const platform = options.platform ?? process.platform;
|
|
85
|
+
const env = options.env ?? process.env;
|
|
86
|
+
if (!isWsl(platform, env)) return undefined;
|
|
87
|
+
const userProfile = env.USERPROFILE ?? (options.windowsUserProfile ?? resolveWindowsUserProfile)();
|
|
88
|
+
if (!userProfile) return undefined;
|
|
89
|
+
return (options.wslPath ?? resolveWithWslPath)(userProfile) ?? convertWindowsPathToDefaultWslMount(userProfile);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function getUserHomeCandidates(ctx: LoadContext): string[] {
|
|
93
|
+
const homes = [ctx.home];
|
|
94
|
+
const wslHome = getWslWindowsHomeCandidate();
|
|
95
|
+
if (wslHome && !homes.includes(wslHome)) homes.push(wslHome);
|
|
96
|
+
return homes;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** User-level paths: ~/.agent[s]/<segments>, plus the Windows host profile under WSL. */
|
|
100
|
+
export function getUserPathCandidates(ctx: LoadContext, ...segments: string[]): string[] {
|
|
101
|
+
return getUserHomeCandidates(ctx).flatMap(home =>
|
|
102
|
+
AGENT_DIR_CANDIDATES.map(baseDir => path.join(home, baseDir, ...segments)),
|
|
103
|
+
);
|
|
34
104
|
}
|
|
35
105
|
|
|
36
106
|
/**
|
|
@@ -27,14 +27,15 @@ const flag = (opts as { enabled: boolean })["enabled"];
|
|
|
27
27
|
|
|
28
28
|
## Use
|
|
29
29
|
|
|
30
|
-
Prefer a schema parse at the boundary when a validator is available
|
|
30
|
+
Prefer a schema parse at the boundary when a validator is available — validate
|
|
31
|
+
once, then read from a fully typed value:
|
|
31
32
|
|
|
32
33
|
```ts
|
|
33
|
-
import {
|
|
34
|
+
import { type } from "@oh-my-pi/omptype";
|
|
34
35
|
|
|
35
|
-
const Resp =
|
|
36
|
+
const Resp = type({ data: { id: "string" } });
|
|
36
37
|
|
|
37
|
-
const resp = Resp.
|
|
38
|
+
const resp = Resp.assert(raw); // throws on bad input; resp.data.id is typed string
|
|
38
39
|
const id = resp.data.id;
|
|
39
40
|
```
|
|
40
41
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Do not extract 1-2 line functions that only wrap an expression — inline them"
|
|
3
|
-
condition: "\\{\\s*return [^;{}\\n]+;?\\s*\\}|\\b(?:const|let|var)\\s+[\\w$]+\\s*=\\s*(\\([^)]*\\)|[a-zA-Z_$][\\w$]*)\\s*=>\\s*[^{\\n]+$"
|
|
3
|
+
condition: "(?m)\\{\\s*return [^;{}\\n]+;?\\s*\\}|\\b(?:const|let|var)\\s+[\\w$]+\\s*=\\s*(\\([^)]*\\)|[a-zA-Z_$][\\w$]*)\\s*=>\\s*[^{\\n]+$"
|
|
4
4
|
scope: "tool:edit(*.ts), tool:edit(*.tsx), tool:write(*.ts), tool:write(*.tsx)"
|
|
5
5
|
interruptMode: never
|
|
6
6
|
---
|
package/src/discovery/claude.ts
CHANGED
|
@@ -90,6 +90,7 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
|
|
90
90
|
const serverConfig = config as Record<string, unknown>;
|
|
91
91
|
return {
|
|
92
92
|
name,
|
|
93
|
+
enabled: typeof serverConfig.enabled === "boolean" ? serverConfig.enabled : undefined,
|
|
93
94
|
timeout: typeof serverConfig.timeout === "number" ? serverConfig.timeout : undefined,
|
|
94
95
|
command: serverConfig.command as string | undefined,
|
|
95
96
|
args: serverConfig.args as string[] | undefined,
|
|
@@ -102,17 +103,19 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
|
|
102
103
|
});
|
|
103
104
|
};
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
// Load project entries before user entries so a project `enabled: false`
|
|
107
|
+
// claims its dedupe key before a same-named user server can survive (#7654).
|
|
108
|
+
const projectOffset = userPaths.length;
|
|
109
|
+
for (let i = 0; i < projectPaths.length; i++) {
|
|
110
|
+
const servers = parseMcpServers(contents[projectOffset + i], projectPaths[i].path, projectPaths[i].level);
|
|
107
111
|
if (servers.length > 0) {
|
|
108
112
|
items.push(...servers);
|
|
109
113
|
break;
|
|
110
114
|
}
|
|
111
115
|
}
|
|
112
116
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const servers = parseMcpServers(contents[projectOffset + i], projectPaths[i].path, projectPaths[i].level);
|
|
117
|
+
for (let i = 0; i < userPaths.length; i++) {
|
|
118
|
+
const servers = parseMcpServers(contents[i], userPaths[i].path, userPaths[i].level);
|
|
116
119
|
if (servers.length > 0) {
|
|
117
120
|
items.push(...servers);
|
|
118
121
|
break;
|
package/src/discovery/cursor.ts
CHANGED
|
@@ -57,6 +57,7 @@ function parseMCPServers(
|
|
|
57
57
|
const serverConfig = config as Record<string, unknown>;
|
|
58
58
|
items.push({
|
|
59
59
|
name,
|
|
60
|
+
enabled: typeof serverConfig.enabled === "boolean" ? serverConfig.enabled : undefined,
|
|
60
61
|
command: serverConfig.command as string | undefined,
|
|
61
62
|
args: serverConfig.args as string[] | undefined,
|
|
62
63
|
env: serverConfig.env as Record<string, string> | undefined,
|
|
@@ -86,15 +87,17 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
|
|
86
87
|
|
|
87
88
|
const projectContentPromise = projectPath ? readFile(projectPath) : Promise.resolve(null);
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
// Load project entries before user entries so a project `enabled: false`
|
|
91
|
+
// claims its dedupe key before a same-named user server can survive (#7654).
|
|
92
|
+
const projectContent = await projectContentPromise;
|
|
93
|
+
if (projectContent && projectPath) {
|
|
94
|
+
const result = parseMCPServers(projectContent, projectPath, "project");
|
|
91
95
|
items.push(...result.items);
|
|
92
96
|
if (result.warning) warnings.push(result.warning);
|
|
93
97
|
}
|
|
94
98
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const result = parseMCPServers(projectContent, projectPath, "project");
|
|
99
|
+
if (userContent && userPath) {
|
|
100
|
+
const result = parseMCPServers(userContent, userPath, "user");
|
|
98
101
|
items.push(...result.items);
|
|
99
102
|
if (result.warning) warnings.push(result.warning);
|
|
100
103
|
}
|
package/src/discovery/gemini.ts
CHANGED
|
@@ -48,14 +48,8 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
|
|
48
48
|
const items: MCPServer[] = [];
|
|
49
49
|
const warnings: string[] = [];
|
|
50
50
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
if (userPath) {
|
|
54
|
-
const result = await loadMCPFromSettings(ctx, userPath, "user");
|
|
55
|
-
items.push(...result.items);
|
|
56
|
-
if (result.warnings) warnings.push(...result.warnings);
|
|
57
|
-
}
|
|
58
|
-
|
|
51
|
+
// Load project entries before user entries so a project `enabled: false`
|
|
52
|
+
// claims its dedupe key before a same-named user server can survive (#7654).
|
|
59
53
|
// Project-level: .gemini/settings.json → mcpServers
|
|
60
54
|
const projectPath = getProjectPath(ctx, "gemini", "settings.json");
|
|
61
55
|
if (projectPath) {
|
|
@@ -64,6 +58,14 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
|
|
64
58
|
if (result.warnings) warnings.push(...result.warnings);
|
|
65
59
|
}
|
|
66
60
|
|
|
61
|
+
// User-level: ~/.gemini/settings.json → mcpServers
|
|
62
|
+
const userPath = getUserPath(ctx, "gemini", "settings.json");
|
|
63
|
+
if (userPath) {
|
|
64
|
+
const result = await loadMCPFromSettings(ctx, userPath, "user");
|
|
65
|
+
items.push(...result.items);
|
|
66
|
+
if (result.warnings) warnings.push(...result.warnings);
|
|
67
|
+
}
|
|
68
|
+
|
|
67
69
|
return { items, warnings };
|
|
68
70
|
}
|
|
69
71
|
|
|
@@ -102,6 +104,7 @@ async function loadMCPFromSettings(
|
|
|
102
104
|
|
|
103
105
|
items.push({
|
|
104
106
|
name,
|
|
107
|
+
enabled: typeof raw.enabled === "boolean" ? raw.enabled : undefined,
|
|
105
108
|
command: typeof raw.command === "string" ? raw.command : undefined,
|
|
106
109
|
args: Array.isArray(raw.args) ? (raw.args as string[]) : undefined,
|
|
107
110
|
env: raw.env && typeof raw.env === "object" ? (raw.env as Record<string, string>) : undefined,
|
package/src/discovery/vscode.ts
CHANGED
|
@@ -83,6 +83,7 @@ async function loadMCPConfig(
|
|
|
83
83
|
|
|
84
84
|
const server: MCPServer = {
|
|
85
85
|
name,
|
|
86
|
+
enabled: typeof expanded.enabled === "boolean" ? expanded.enabled : undefined,
|
|
86
87
|
command: typeof expanded.command === "string" ? expanded.command : undefined,
|
|
87
88
|
args: Array.isArray(expanded.args) ? (expanded.args as string[]) : undefined,
|
|
88
89
|
env: expanded.env && typeof expanded.env === "object" ? (expanded.env as Record<string, string>) : undefined,
|
|
@@ -48,6 +48,7 @@ function parseServerConfig(
|
|
|
48
48
|
return {
|
|
49
49
|
server: {
|
|
50
50
|
name,
|
|
51
|
+
enabled: typeof server.enabled === "boolean" ? server.enabled : undefined,
|
|
51
52
|
command: server.command as string | undefined,
|
|
52
53
|
args: server.args as string[] | undefined,
|
|
53
54
|
env: server.env as Record<string, string> | undefined,
|
|
@@ -71,10 +72,11 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
|
|
71
72
|
]);
|
|
72
73
|
|
|
73
74
|
const projectContent = projectPath ? await readFile(projectPath) : null;
|
|
74
|
-
|
|
75
|
+
// Load project entries before user entries so a project `enabled: false`
|
|
76
|
+
// claims its dedupe key before a same-named user server can survive (#7654).
|
|
75
77
|
const configs: Array<{ content: string | null; path: string | null; scope: "user" | "project" }> = [
|
|
76
|
-
{ content: userContent, path: userPath, scope: "user" },
|
|
77
78
|
{ content: projectContent, path: projectPath, scope: "project" },
|
|
79
|
+
{ content: userContent, path: userPath, scope: "user" },
|
|
78
80
|
];
|
|
79
81
|
|
|
80
82
|
for (const { content, path, scope } of configs) {
|