@oh-my-pi/pi-coding-agent 16.0.5 → 16.0.6
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 +53 -0
- package/dist/cli.js +1927 -1376
- package/dist/types/advisor/advise-tool.d.ts +22 -19
- package/dist/types/autoresearch/tools/init-experiment.d.ts +13 -17
- package/dist/types/autoresearch/tools/log-experiment.d.ts +17 -19
- package/dist/types/autoresearch/tools/run-experiment.d.ts +3 -4
- package/dist/types/autoresearch/tools/update-notes.d.ts +4 -5
- package/dist/types/cli/ttsr-cli.d.ts +39 -0
- package/dist/types/commands/ttsr.d.ts +57 -0
- package/dist/types/commit/agentic/tools/analyze-file.d.ts +4 -5
- package/dist/types/commit/agentic/tools/git-file-diff.d.ts +4 -5
- package/dist/types/commit/agentic/tools/git-hunk.d.ts +5 -6
- package/dist/types/commit/agentic/tools/git-overview.d.ts +4 -5
- package/dist/types/commit/agentic/tools/propose-changelog.d.ts +23 -24
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +11 -32
- package/dist/types/commit/agentic/tools/recent-commits.d.ts +3 -4
- package/dist/types/commit/agentic/tools/schemas.d.ts +6 -27
- package/dist/types/commit/agentic/tools/split-commit.d.ts +28 -49
- package/dist/types/commit/changelog/generate.d.ts +12 -13
- package/dist/types/commit/shared-llm.d.ts +10 -37
- package/dist/types/config/config-file.d.ts +4 -4
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/models-config-schema.d.ts +625 -990
- package/dist/types/config/models-config.d.ts +229 -217
- package/dist/types/config/settings-schema.d.ts +53 -23
- package/dist/types/edit/hashline/params.d.ts +7 -11
- package/dist/types/edit/index.d.ts +2 -1
- package/dist/types/edit/modes/apply-patch.d.ts +4 -5
- package/dist/types/edit/modes/patch.d.ts +15 -24
- package/dist/types/edit/modes/replace.d.ts +16 -17
- package/dist/types/eval/js/index.d.ts +1 -0
- package/dist/types/extensibility/custom-commands/types.d.ts +6 -3
- package/dist/types/extensibility/custom-tools/types.d.ts +8 -5
- package/dist/types/extensibility/extensions/types.d.ts +6 -3
- package/dist/types/extensibility/hooks/types.d.ts +7 -4
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +13 -5
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +17 -0
- package/dist/types/extensibility/typebox.d.ts +80 -58
- package/dist/types/goals/tools/goal-tool.d.ts +11 -24
- package/dist/types/index.d.ts +2 -0
- package/dist/types/lsp/index.d.ts +11 -26
- package/dist/types/lsp/types.d.ts +12 -28
- package/dist/types/mcp/client.d.ts +8 -0
- package/dist/types/modes/components/btw-panel.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +3 -1
- package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/setup-wizard/index.d.ts +1 -0
- package/dist/types/modes/setup-wizard/startup-splash.d.ts +7 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +3 -0
- package/dist/types/sdk.d.ts +5 -0
- package/dist/types/session/agent-session.d.ts +4 -0
- package/dist/types/startup-splash.d.ts +12 -0
- package/dist/types/task/types.d.ts +47 -48
- package/dist/types/tools/ask.d.ts +26 -27
- package/dist/types/tools/ast-edit.d.ts +17 -17
- package/dist/types/tools/ast-grep.d.ts +12 -13
- package/dist/types/tools/bash.d.ts +20 -17
- package/dist/types/tools/browser.d.ts +46 -71
- package/dist/types/tools/checkpoint.d.ts +14 -15
- package/dist/types/tools/debug.d.ts +82 -145
- package/dist/types/tools/eval.d.ts +30 -40
- package/dist/types/tools/find.d.ts +17 -18
- package/dist/types/tools/gh.d.ts +49 -78
- package/dist/types/tools/image-gen.d.ts +20 -36
- package/dist/types/tools/inspect-image.d.ts +10 -11
- package/dist/types/tools/irc.d.ts +22 -33
- package/dist/types/tools/job.d.ts +11 -12
- package/dist/types/tools/learn.d.ts +21 -28
- package/dist/types/tools/manage-skill.d.ts +13 -22
- package/dist/types/tools/memory-edit.d.ts +15 -24
- package/dist/types/tools/memory-recall.d.ts +7 -8
- package/dist/types/tools/memory-reflect.d.ts +9 -10
- package/dist/types/tools/memory-retain.d.ts +13 -14
- package/dist/types/tools/read.d.ts +7 -8
- package/dist/types/tools/resolve.d.ts +11 -18
- package/dist/types/tools/review.d.ts +9 -15
- package/dist/types/tools/search-tool-bm25.d.ts +9 -10
- package/dist/types/tools/search.d.ts +16 -17
- package/dist/types/tools/ssh.d.ts +14 -15
- package/dist/types/tools/todo.d.ts +27 -43
- package/dist/types/tools/tts.d.ts +8 -9
- package/dist/types/tools/write.d.ts +9 -10
- package/dist/types/tui/index.d.ts +1 -0
- package/dist/types/tui/width-aware-text.d.ts +23 -0
- package/dist/types/utils/markit.d.ts +10 -1
- package/dist/types/web/search/index.d.ts +17 -28
- package/dist/types/web/search/providers/perplexity.d.ts +0 -2
- package/dist/types/web/search/types.d.ts +32 -26
- package/package.json +14 -13
- package/scripts/omp +1 -1
- package/src/advisor/__tests__/advisor.test.ts +44 -1
- package/src/advisor/advise-tool.ts +34 -11
- package/src/autoresearch/tools/init-experiment.ts +13 -16
- package/src/autoresearch/tools/log-experiment.ts +15 -18
- package/src/autoresearch/tools/run-experiment.ts +3 -3
- package/src/autoresearch/tools/update-notes.ts +4 -4
- package/src/cli/ttsr-cli.ts +995 -0
- package/src/cli-commands.ts +1 -0
- package/src/cli.ts +7 -1
- package/src/commands/ttsr.ts +125 -0
- package/src/commit/agentic/tools/analyze-file.ts +4 -4
- package/src/commit/agentic/tools/git-file-diff.ts +4 -4
- package/src/commit/agentic/tools/git-hunk.ts +7 -5
- package/src/commit/agentic/tools/git-overview.ts +4 -4
- package/src/commit/agentic/tools/propose-changelog.ts +18 -15
- package/src/commit/agentic/tools/propose-commit.ts +6 -6
- package/src/commit/agentic/tools/recent-commits.ts +3 -3
- package/src/commit/agentic/tools/schemas.ts +8 -20
- package/src/commit/agentic/tools/split-commit.ts +19 -23
- package/src/commit/analysis/summary.ts +7 -5
- package/src/commit/changelog/generate.ts +15 -11
- package/src/commit/shared-llm.ts +17 -24
- package/src/config/config-file.ts +13 -15
- package/src/config/keybindings.ts +6 -0
- package/src/config/models-config-schema.ts +206 -179
- package/src/config/settings-schema.ts +34 -0
- package/src/discovery/builtin-rules/index.ts +2 -0
- package/src/discovery/builtin-rules/ts-import-type.md +2 -2
- package/src/discovery/builtin-rules/ts-no-any.md +11 -2
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +55 -0
- package/src/edit/hashline/params.ts +12 -11
- package/src/edit/index.ts +5 -4
- package/src/edit/modes/apply-patch.ts +4 -4
- package/src/edit/modes/patch.ts +15 -18
- package/src/edit/modes/replace.ts +13 -17
- package/src/edit/renderer.ts +0 -1
- package/src/eval/agent-bridge.ts +11 -13
- package/src/eval/completion-bridge.ts +25 -17
- package/src/eval/js/context-manager.ts +17 -2
- package/src/eval/js/index.ts +1 -1
- package/src/eval/py/executor.ts +2 -2
- package/src/extensibility/custom-commands/loader.ts +5 -3
- package/src/extensibility/custom-commands/types.ts +6 -3
- package/src/extensibility/custom-tools/loader.ts +4 -2
- package/src/extensibility/custom-tools/types.ts +8 -5
- package/src/extensibility/extensions/loader.ts +4 -2
- package/src/extensibility/extensions/types.ts +6 -3
- package/src/extensibility/hooks/loader.ts +5 -2
- package/src/extensibility/hooks/types.ts +7 -4
- package/src/extensibility/legacy-pi-ai-shim.ts +42 -5
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +113 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +13 -13
- package/src/extensibility/tool-proxy.ts +4 -1
- package/src/extensibility/typebox.ts +778 -251
- package/src/goals/guided-setup.ts +12 -3
- package/src/goals/tools/goal-tool.ts +6 -6
- package/src/index.ts +2 -0
- package/src/internal-urls/docs-index.generated.ts +11 -9
- package/src/lsp/types.ts +13 -27
- package/src/main.ts +19 -18
- package/src/mcp/client.ts +38 -13
- package/src/mcp/render.ts +102 -89
- package/src/modes/components/agent-hub.ts +11 -4
- package/src/modes/components/btw-panel.ts +5 -1
- package/src/modes/components/custom-editor.ts +18 -0
- package/src/modes/components/status-line/component.ts +8 -1
- package/src/modes/components/tool-execution.ts +17 -10
- package/src/modes/controllers/btw-controller.ts +69 -1
- package/src/modes/controllers/input-controller.ts +29 -0
- package/src/modes/interactive-mode.ts +38 -8
- package/src/modes/setup-wizard/index.ts +1 -0
- package/src/modes/setup-wizard/scenes/sign-in.ts +77 -5
- package/src/modes/setup-wizard/startup-splash.ts +107 -0
- package/src/modes/theme/theme.ts +133 -143
- package/src/modes/types.ts +3 -0
- package/src/modes/utils/context-usage.ts +9 -5
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/sdk.ts +21 -4
- package/src/session/agent-session.ts +160 -33
- package/src/session/session-history-format.ts +11 -2
- package/src/session/snapcompact-inline.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +4 -11
- package/src/startup-splash.ts +19 -0
- package/src/task/executor.ts +11 -6
- package/src/task/types.ts +44 -41
- package/src/tool-discovery/tool-index.ts +17 -4
- package/src/tools/ask.ts +14 -14
- package/src/tools/ast-edit.ts +17 -14
- package/src/tools/ast-grep.ts +10 -9
- package/src/tools/bash.ts +15 -10
- package/src/tools/browser/launch.ts +13 -0
- package/src/tools/browser.ts +26 -32
- package/src/tools/checkpoint.ts +7 -7
- package/src/tools/debug.ts +72 -69
- package/src/tools/eval.ts +18 -19
- package/src/tools/find.ts +20 -13
- package/src/tools/gh.ts +29 -49
- package/src/tools/image-gen.ts +27 -32
- package/src/tools/inspect-image.ts +8 -9
- package/src/tools/irc.ts +12 -12
- package/src/tools/job.ts +6 -6
- package/src/tools/learn.ts +11 -14
- package/src/tools/manage-skill.ts +19 -23
- package/src/tools/memory-edit.ts +8 -8
- package/src/tools/memory-recall.ts +4 -4
- package/src/tools/memory-reflect.ts +5 -5
- package/src/tools/memory-retain.ts +9 -11
- package/src/tools/puppeteer/02_stealth_hairline.txt +1 -1
- package/src/tools/puppeteer/04_stealth_iframe.txt +4 -4
- package/src/tools/puppeteer/05_stealth_webgl.txt +1 -1
- package/src/tools/puppeteer/10_stealth_plugins.txt +6 -4
- package/src/tools/puppeteer/12_stealth_codecs.txt +2 -2
- package/src/tools/puppeteer/13_stealth_worker.txt +1 -1
- package/src/tools/read.ts +169 -13
- package/src/tools/report-tool-issue.ts +6 -6
- package/src/tools/resolve.ts +6 -6
- package/src/tools/review.ts +10 -12
- package/src/tools/search-tool-bm25.ts +5 -5
- package/src/tools/search.ts +20 -29
- package/src/tools/ssh.ts +8 -8
- package/src/tools/todo.ts +16 -19
- package/src/tools/tts.ts +16 -15
- package/src/tools/write.ts +5 -5
- package/src/tui/index.ts +1 -0
- package/src/tui/width-aware-text.ts +58 -0
- package/src/utils/markit.ts +17 -2
- package/src/web/search/index.ts +9 -9
- package/src/web/search/providers/perplexity.ts +373 -126
- package/src/web/search/types.ts +28 -48
package/src/lsp/types.ts
CHANGED
|
@@ -1,38 +1,24 @@
|
|
|
1
1
|
import type { ptree } from "@oh-my-pi/pi-utils";
|
|
2
|
-
import {
|
|
2
|
+
import { type } from "arktype";
|
|
3
3
|
|
|
4
4
|
// =============================================================================
|
|
5
5
|
// Tool Schema
|
|
6
6
|
// =============================================================================
|
|
7
7
|
|
|
8
|
-
export const lspSchema =
|
|
9
|
-
action:
|
|
10
|
-
"diagnostics",
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"implementation",
|
|
20
|
-
"status",
|
|
21
|
-
"reload",
|
|
22
|
-
"capabilities",
|
|
23
|
-
"request",
|
|
24
|
-
]),
|
|
25
|
-
file: z.string().describe("file path or source path for rename_file").optional(),
|
|
26
|
-
line: z.number().describe("line number (1-indexed)").optional(),
|
|
27
|
-
symbol: z.string().describe("symbol substring on the line").optional(),
|
|
28
|
-
query: z.string().describe("search query or code-action selector").optional(),
|
|
29
|
-
new_name: z.string().describe("new symbol name or destination path").optional(),
|
|
30
|
-
apply: z.boolean().describe("apply edits").optional(),
|
|
31
|
-
timeout: z.number().describe("request timeout in seconds").optional(),
|
|
32
|
-
payload: z.string().describe("json-encoded request params").optional(),
|
|
8
|
+
export const lspSchema = type({
|
|
9
|
+
action:
|
|
10
|
+
"'diagnostics' | 'definition' | 'references' | 'hover' | 'symbols' | 'rename' | 'rename_file' | 'code_actions' | 'type_definition' | 'implementation' | 'status' | 'reload' | 'capabilities' | 'request'",
|
|
11
|
+
file: "string?",
|
|
12
|
+
line: "number?",
|
|
13
|
+
symbol: "string?",
|
|
14
|
+
query: "string?",
|
|
15
|
+
new_name: "string?",
|
|
16
|
+
apply: "boolean?",
|
|
17
|
+
timeout: "number?",
|
|
18
|
+
payload: "string?",
|
|
33
19
|
});
|
|
34
20
|
|
|
35
|
-
export type LspParams =
|
|
21
|
+
export type LspParams = typeof lspSchema.infer;
|
|
36
22
|
|
|
37
23
|
export interface LspToolDetails {
|
|
38
24
|
serverName?: string;
|
package/src/main.ts
CHANGED
|
@@ -68,6 +68,7 @@ import type { AuthStorage } from "./session/auth-storage";
|
|
|
68
68
|
import { resolveResumableSession, type SessionInfo } from "./session/session-listing";
|
|
69
69
|
import { SessionManager } from "./session/session-manager";
|
|
70
70
|
import { executeBuiltinSlashCommand } from "./slash-commands/builtin-registry";
|
|
71
|
+
import { shouldShowStartupSplash } from "./startup-splash";
|
|
71
72
|
import { discoverTitleSystemPromptFile, resolvePromptInput } from "./system-prompt";
|
|
72
73
|
import { createPersistedSubagentReviverFactory } from "./task/persisted-revive";
|
|
73
74
|
import { initTelemetryExport, isTelemetryExportEnabled } from "./telemetry-export";
|
|
@@ -90,19 +91,6 @@ type RunRpcMode = (
|
|
|
90
91
|
eventBus?: EventBus,
|
|
91
92
|
) => Promise<never>;
|
|
92
93
|
|
|
93
|
-
function maybeShowStartupSplash(options: {
|
|
94
|
-
isInteractive: boolean;
|
|
95
|
-
resuming: boolean;
|
|
96
|
-
quiet: boolean;
|
|
97
|
-
version: string;
|
|
98
|
-
}): void {
|
|
99
|
-
if (!options.isInteractive) return;
|
|
100
|
-
if (options.resuming || options.quiet) return;
|
|
101
|
-
if ($env.PI_TIMING) return;
|
|
102
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
103
|
-
//process.stdout.write(`${chalk.dim(`omp ${options.version}`)}\n${chalk.dim("Initializing session…")}\n`);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
94
|
export function writeStartupNotice(parsedArgs: Pick<Args, "mode">, text: string): void {
|
|
107
95
|
(parsedArgs.mode === "json" ? process.stderr : process.stdout).write(text);
|
|
108
96
|
}
|
|
@@ -391,6 +379,7 @@ async function runInteractiveMode(
|
|
|
391
379
|
mcpManager: MCPManager | undefined,
|
|
392
380
|
resuming: boolean,
|
|
393
381
|
forceSetupWizard: boolean,
|
|
382
|
+
showStartupSplash: boolean,
|
|
394
383
|
eventBus?: EventBus,
|
|
395
384
|
initialMessage?: string,
|
|
396
385
|
initialImages?: ImageContent[],
|
|
@@ -411,10 +400,13 @@ async function runInteractiveMode(
|
|
|
411
400
|
// Cold-launch gate: the full setup wizard (every scene + the overlay and
|
|
412
401
|
// their TUI/OAuth/search/theme deps) is heavy, yet the common case only needs
|
|
413
402
|
// to know whether the stored setup version is current. Lazy-load the wizard
|
|
414
|
-
// barrel only when setup is stale or
|
|
403
|
+
// barrel only when setup is stale, forced, or the explicit startup splash
|
|
404
|
+
// setting needs the shared setup splash renderer.
|
|
415
405
|
const storedSetupVersion = settings.get("setupVersion");
|
|
416
406
|
const setupWizard =
|
|
417
|
-
forceSetupWizard || storedSetupVersion < CURRENT_SETUP_VERSION
|
|
407
|
+
forceSetupWizard || storedSetupVersion < CURRENT_SETUP_VERSION || showStartupSplash
|
|
408
|
+
? await import("./modes/setup-wizard")
|
|
409
|
+
: undefined;
|
|
418
410
|
const setupScenes = setupWizard
|
|
419
411
|
? await setupWizard.selectSetupScenes(storedSetupVersion, setupWizard.ALL_SCENES, mode, {
|
|
420
412
|
resuming,
|
|
@@ -423,12 +415,17 @@ async function runInteractiveMode(
|
|
|
423
415
|
force: forceSetupWizard,
|
|
424
416
|
})
|
|
425
417
|
: [];
|
|
418
|
+
const playStartupSplash = showStartupSplash && setupScenes.length === 0;
|
|
426
419
|
|
|
427
420
|
await mode.init({
|
|
428
|
-
suppressWelcomeIntro: resuming || setupScenes.length > 0,
|
|
421
|
+
suppressWelcomeIntro: resuming || setupScenes.length > 0 || playStartupSplash,
|
|
429
422
|
clearInitialTerminalHistory: true,
|
|
430
423
|
});
|
|
431
424
|
|
|
425
|
+
if (setupWizard && playStartupSplash) {
|
|
426
|
+
await setupWizard.runStartupSplash(mode);
|
|
427
|
+
}
|
|
428
|
+
|
|
432
429
|
if (setupWizard && setupScenes.length > 0) {
|
|
433
430
|
await setupWizard.runSetupWizard(mode, setupScenes);
|
|
434
431
|
}
|
|
@@ -1261,11 +1258,14 @@ export async function runRootCommand(
|
|
|
1261
1258
|
stdinContent: pipedInput,
|
|
1262
1259
|
});
|
|
1263
1260
|
|
|
1264
|
-
|
|
1261
|
+
const showStartupSplash = shouldShowStartupSplash({
|
|
1262
|
+
configured: settingsInstance.get("startup.showSplash"),
|
|
1265
1263
|
isInteractive,
|
|
1266
1264
|
resuming: Boolean(parsedArgs.continue || parsedArgs.resume || parsedArgs.fork),
|
|
1267
1265
|
quiet: settingsInstance.get("startup.quiet"),
|
|
1268
|
-
|
|
1266
|
+
timing: Boolean($env.PI_TIMING),
|
|
1267
|
+
stdinIsTTY: process.stdin.isTTY,
|
|
1268
|
+
stdoutIsTTY: process.stdout.isTTY,
|
|
1269
1269
|
});
|
|
1270
1270
|
|
|
1271
1271
|
const { session, setToolUIContext, modelFallbackMessage, lspServers, mcpManager } = await createSession({
|
|
@@ -1357,6 +1357,7 @@ export async function runRootCommand(
|
|
|
1357
1357
|
mcpManager,
|
|
1358
1358
|
Boolean(parsedArgs.continue || parsedArgs.resume || parsedArgs.fork),
|
|
1359
1359
|
deps.forceSetupWizard === true,
|
|
1360
|
+
showStartupSplash,
|
|
1360
1361
|
eventBus,
|
|
1361
1362
|
initialMessage,
|
|
1362
1363
|
initialImages,
|
package/src/mcp/client.ts
CHANGED
|
@@ -303,8 +303,22 @@ export async function listResources(
|
|
|
303
303
|
return allResources;
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
+
/** True when an error is a JSON-RPC "method not found" (-32601) response. */
|
|
307
|
+
function isMethodNotFoundError(error: unknown): boolean {
|
|
308
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
309
|
+
return message.includes("-32601") || /method not found/i.test(message);
|
|
310
|
+
}
|
|
311
|
+
|
|
306
312
|
/**
|
|
307
313
|
* List resource templates from a connected server.
|
|
314
|
+
*
|
|
315
|
+
* A server MAY advertise the `resources` capability without implementing the
|
|
316
|
+
* optional `resources/templates/list` method (it is optional in the MCP spec).
|
|
317
|
+
* Such servers reject the request with JSON-RPC -32601 ("Method not found").
|
|
318
|
+
* Treat that as "no templates" and return `[]` rather than throwing — otherwise
|
|
319
|
+
* a caller that loads resources and templates together (see `MCPManager`'s
|
|
320
|
+
* `Promise.all([listResources, listResourceTemplates])`) would discard the
|
|
321
|
+
* server's concrete resources too. Any other error still propagates.
|
|
308
322
|
*/
|
|
309
323
|
export async function listResourceTemplates(
|
|
310
324
|
connection: MCPServerConnection,
|
|
@@ -321,20 +335,31 @@ export async function listResourceTemplates(
|
|
|
321
335
|
const allTemplates: MCPResourceTemplate[] = [];
|
|
322
336
|
let cursor: string | undefined;
|
|
323
337
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
338
|
+
try {
|
|
339
|
+
do {
|
|
340
|
+
const params: Record<string, unknown> = {};
|
|
341
|
+
if (cursor) {
|
|
342
|
+
params.cursor = cursor;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const result = await connection.transport.request<MCPResourceTemplatesListResult>(
|
|
346
|
+
"resources/templates/list",
|
|
347
|
+
params,
|
|
348
|
+
options,
|
|
349
|
+
);
|
|
350
|
+
allTemplates.push(...result.resourceTemplates);
|
|
351
|
+
cursor = result.nextCursor;
|
|
352
|
+
} while (cursor);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
// A server that doesn't implement the optional templates method answers
|
|
355
|
+
// -32601; cache an empty list so we neither retry nor let the failure
|
|
356
|
+
// bubble up and discard the server's concrete resources.
|
|
357
|
+
if (isMethodNotFoundError(error)) {
|
|
358
|
+
connection.resourceTemplates = [];
|
|
359
|
+
return [];
|
|
328
360
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
"resources/templates/list",
|
|
332
|
-
params,
|
|
333
|
-
options,
|
|
334
|
-
);
|
|
335
|
-
allTemplates.push(...result.resourceTemplates);
|
|
336
|
-
cursor = result.nextCursor;
|
|
337
|
-
} while (cursor);
|
|
361
|
+
throw error;
|
|
362
|
+
}
|
|
338
363
|
|
|
339
364
|
connection.resourceTemplates = allTemplates;
|
|
340
365
|
return allTemplates;
|
package/src/mcp/render.ts
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* showing args and output in JSON tree format similar to task tool.
|
|
6
6
|
*/
|
|
7
7
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
8
|
-
import { Text } from "@oh-my-pi/pi-tui";
|
|
9
8
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
10
9
|
import type { Theme } from "../modes/theme/theme";
|
|
11
10
|
import {
|
|
@@ -20,25 +19,33 @@ import {
|
|
|
20
19
|
} from "../tools/json-tree";
|
|
21
20
|
import { formatStyledTruncationWarning, stripOutputNotice } from "../tools/output-meta";
|
|
22
21
|
import { formatExpandHint, truncateToWidth } from "../tools/render-utils";
|
|
23
|
-
import { renderStatusLine } from "../tui";
|
|
22
|
+
import { renderStatusLine, WidthAwareText } from "../tui";
|
|
24
23
|
import type { MCPToolDetails } from "./tool-bridge";
|
|
25
24
|
|
|
26
25
|
/**
|
|
27
26
|
* Render MCP tool call.
|
|
28
27
|
*/
|
|
29
28
|
export function renderMCPCall(args: Record<string, unknown>, theme: Theme, label: string): Component {
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
return new WidthAwareText(
|
|
30
|
+
contentWidth => {
|
|
31
|
+
const lines: string[] = [];
|
|
32
|
+
lines.push(renderStatusLine({ icon: "pending", title: label }, theme));
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
if (args && typeof args === "object" && Object.keys(args).length > 0) {
|
|
35
|
+
// Inline preview budgeted against the render width, leaving room for
|
|
36
|
+
// the ` └─ ` connector prefix instead of a fixed cap.
|
|
37
|
+
const inlineBudget = Math.max(20, contentWidth - Bun.stringWidth(theme.tree.last) - 2);
|
|
38
|
+
const preview = formatArgsInline(args, inlineBudget);
|
|
39
|
+
if (preview) {
|
|
40
|
+
lines.push(` ${theme.fg("dim", theme.tree.last)} ${theme.fg("dim", preview)}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
return lines.join("\n");
|
|
45
|
+
},
|
|
46
|
+
0,
|
|
47
|
+
0,
|
|
48
|
+
);
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
/**
|
|
@@ -51,92 +58,98 @@ export function renderMCPResult(
|
|
|
51
58
|
args?: Record<string, unknown>,
|
|
52
59
|
): Component {
|
|
53
60
|
const { expanded } = options;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (expanded && args && typeof args === "object" && Object.keys(args).length > 0) {
|
|
67
|
-
lines.push(`${theme.fg("dim", "Args")}`);
|
|
68
|
-
const maxDepth = JSON_TREE_MAX_DEPTH_EXPANDED;
|
|
69
|
-
const maxLines = JSON_TREE_MAX_LINES_EXPANDED;
|
|
70
|
-
const tree = renderJsonTreeLines(args, theme, maxDepth, maxLines, JSON_TREE_SCALAR_LEN_EXPANDED);
|
|
71
|
-
for (const line of tree.lines) {
|
|
72
|
-
lines.push(line);
|
|
73
|
-
}
|
|
74
|
-
if (tree.truncated) {
|
|
75
|
-
lines.push(theme.fg("dim", "…"));
|
|
76
|
-
}
|
|
77
|
-
lines.push(""); // Blank line before output
|
|
78
|
-
}
|
|
61
|
+
return new WidthAwareText(
|
|
62
|
+
contentWidth => {
|
|
63
|
+
const lines: string[] = [];
|
|
64
|
+
const isError = result.isError ?? result.details?.isError ?? false;
|
|
65
|
+
const title = result.details ? `${result.details.serverName}/${result.details.mcpToolName}` : "MCP";
|
|
66
|
+
const success = !isError;
|
|
67
|
+
lines.push(
|
|
68
|
+
renderStatusLine(
|
|
69
|
+
success ? { iconOverride: theme.styledSymbol("tool.mcp", "accent"), title } : { icon: "error", title },
|
|
70
|
+
theme,
|
|
71
|
+
),
|
|
72
|
+
);
|
|
79
73
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const trimmedOutput = stripOutputNotice(textContent, result.details?.meta).trimEnd();
|
|
87
|
-
const truncationWarning = result.details?.meta?.truncation
|
|
88
|
-
? formatStyledTruncationWarning(result.details.meta, theme)
|
|
89
|
-
: null;
|
|
90
|
-
|
|
91
|
-
if (!trimmedOutput) {
|
|
92
|
-
lines.push(theme.fg("dim", "(no output)"));
|
|
93
|
-
return new Text(lines.join("\n"), 0, 0);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Try to parse as JSON for structured display
|
|
97
|
-
if (trimmedOutput.startsWith("{") || trimmedOutput.startsWith("[")) {
|
|
98
|
-
try {
|
|
99
|
-
const parsed = JSON.parse(trimmedOutput);
|
|
100
|
-
const maxDepth = expanded ? JSON_TREE_MAX_DEPTH_EXPANDED : JSON_TREE_MAX_DEPTH_COLLAPSED;
|
|
101
|
-
const maxLines = expanded ? JSON_TREE_MAX_LINES_EXPANDED : JSON_TREE_MAX_LINES_COLLAPSED;
|
|
102
|
-
const maxScalarLen = expanded ? JSON_TREE_SCALAR_LEN_EXPANDED : JSON_TREE_SCALAR_LEN_COLLAPSED;
|
|
103
|
-
const tree = renderJsonTreeLines(parsed, theme, maxDepth, maxLines, maxScalarLen);
|
|
104
|
-
|
|
105
|
-
if (tree.lines.length > 0) {
|
|
74
|
+
// Args section (when expanded)
|
|
75
|
+
if (expanded && args && typeof args === "object" && Object.keys(args).length > 0) {
|
|
76
|
+
lines.push(`${theme.fg("dim", "Args")}`);
|
|
77
|
+
const maxDepth = JSON_TREE_MAX_DEPTH_EXPANDED;
|
|
78
|
+
const maxLines = JSON_TREE_MAX_LINES_EXPANDED;
|
|
79
|
+
const tree = renderJsonTreeLines(args, theme, maxDepth, maxLines, JSON_TREE_SCALAR_LEN_EXPANDED);
|
|
106
80
|
for (const line of tree.lines) {
|
|
107
81
|
lines.push(line);
|
|
108
82
|
}
|
|
109
|
-
|
|
110
|
-
if (!expanded) {
|
|
111
|
-
lines.push(formatExpandHint(theme, expanded, true));
|
|
112
|
-
} else if (tree.truncated) {
|
|
83
|
+
if (tree.truncated) {
|
|
113
84
|
lines.push(theme.fg("dim", "…"));
|
|
114
85
|
}
|
|
115
|
-
|
|
116
|
-
|
|
86
|
+
lines.push(""); // Blank line before output
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Output section
|
|
90
|
+
const textContent = result.content?.find(c => c.type === "text")?.text ?? "";
|
|
91
|
+
// Strip the LLM-facing spill notice before parsing/rendering: a spilled
|
|
92
|
+
// result appends `[Showing… artifact://N]` to the body, which would break
|
|
93
|
+
// JSON detection and bury the recovery link. Surface it as a styled warning
|
|
94
|
+
// instead, mirroring the built-in read/bash/ssh/browser renderers.
|
|
95
|
+
const trimmedOutput = stripOutputNotice(textContent, result.details?.meta).trimEnd();
|
|
96
|
+
const truncationWarning = result.details?.meta?.truncation
|
|
97
|
+
? formatStyledTruncationWarning(result.details.meta, theme)
|
|
98
|
+
: null;
|
|
99
|
+
|
|
100
|
+
if (!trimmedOutput) {
|
|
101
|
+
lines.push(theme.fg("dim", "(no output)"));
|
|
102
|
+
return lines.join("\n");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Try to parse as JSON for structured display
|
|
106
|
+
if (trimmedOutput.startsWith("{") || trimmedOutput.startsWith("[")) {
|
|
107
|
+
try {
|
|
108
|
+
const parsed = JSON.parse(trimmedOutput);
|
|
109
|
+
const maxDepth = expanded ? JSON_TREE_MAX_DEPTH_EXPANDED : JSON_TREE_MAX_DEPTH_COLLAPSED;
|
|
110
|
+
const maxLines = expanded ? JSON_TREE_MAX_LINES_EXPANDED : JSON_TREE_MAX_LINES_COLLAPSED;
|
|
111
|
+
const maxScalarLen = expanded ? JSON_TREE_SCALAR_LEN_EXPANDED : JSON_TREE_SCALAR_LEN_COLLAPSED;
|
|
112
|
+
const tree = renderJsonTreeLines(parsed, theme, maxDepth, maxLines, maxScalarLen);
|
|
113
|
+
|
|
114
|
+
if (tree.lines.length > 0) {
|
|
115
|
+
for (const line of tree.lines) {
|
|
116
|
+
lines.push(line);
|
|
117
|
+
}
|
|
118
|
+
// Always show expand hint when collapsed (expanded view shows longer values and deeper nesting)
|
|
119
|
+
if (!expanded) {
|
|
120
|
+
lines.push(formatExpandHint(theme, expanded, true));
|
|
121
|
+
} else if (tree.truncated) {
|
|
122
|
+
lines.push(theme.fg("dim", "…"));
|
|
123
|
+
}
|
|
124
|
+
if (truncationWarning) lines.push(truncationWarning);
|
|
125
|
+
return lines.join("\n");
|
|
126
|
+
}
|
|
127
|
+
} catch {
|
|
128
|
+
// Fall through to raw output
|
|
129
|
+
}
|
|
117
130
|
}
|
|
118
|
-
} catch {
|
|
119
|
-
// Fall through to raw output
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
131
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
132
|
+
// Raw text output
|
|
133
|
+
const outputLines = trimmedOutput.split("\n");
|
|
134
|
+
const maxOutputLines = expanded ? 12 : 4;
|
|
135
|
+
const displayLines = outputLines.slice(0, maxOutputLines);
|
|
127
136
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
137
|
+
for (const line of displayLines) {
|
|
138
|
+
lines.push(theme.fg("toolOutput", truncateToWidth(line, contentWidth)));
|
|
139
|
+
}
|
|
131
140
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
if (outputLines.length > maxOutputLines) {
|
|
142
|
+
const remaining = outputLines.length - maxOutputLines;
|
|
143
|
+
lines.push(`${theme.fg("dim", `… ${remaining} more lines`)} ${formatExpandHint(theme, expanded, true)}`);
|
|
144
|
+
} else if (!expanded) {
|
|
145
|
+
// Show expand hint when collapsed even if all lines shown (lines may be truncated)
|
|
146
|
+
lines.push(formatExpandHint(theme, expanded, true));
|
|
147
|
+
}
|
|
139
148
|
|
|
140
|
-
|
|
141
|
-
|
|
149
|
+
if (truncationWarning) lines.push(truncationWarning);
|
|
150
|
+
return lines.join("\n");
|
|
151
|
+
},
|
|
152
|
+
0,
|
|
153
|
+
0,
|
|
154
|
+
);
|
|
142
155
|
}
|
|
@@ -17,7 +17,7 @@ import * as fs from "node:fs";
|
|
|
17
17
|
import * as path from "node:path";
|
|
18
18
|
import type { AgentMessage, AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
19
19
|
import type { Usage } from "@oh-my-pi/pi-ai";
|
|
20
|
-
import { Container, Editor, matchesKey, ScrollView, Text, type TUI } from "@oh-my-pi/pi-tui";
|
|
20
|
+
import { Container, Editor, Ellipsis, matchesKey, ScrollView, Text, type TUI } from "@oh-my-pi/pi-tui";
|
|
21
21
|
import { formatAge, formatBytes, formatDuration, formatNumber, getProjectDir, logger } from "@oh-my-pi/pi-utils";
|
|
22
22
|
import type { AdvisorMessageDetails } from "../../advisor";
|
|
23
23
|
import { COLLAB_PROMPT_MESSAGE_TYPE, type CollabPromptDetails } from "../../collab/protocol";
|
|
@@ -81,7 +81,12 @@ function contentWidth(): number {
|
|
|
81
81
|
|
|
82
82
|
/** Sanitize a line for TUI display: replace tabs, then truncate to viewport width. */
|
|
83
83
|
function sanitizeLine(text: string, maxWidth?: number): string {
|
|
84
|
-
|
|
84
|
+
const singleLine = replaceTabs(text).replace(/[\r\n]+/g, " ");
|
|
85
|
+
return truncateToWidth(singleLine, maxWidth ?? contentWidth());
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function clampHubLine(line: string, width: number): string {
|
|
89
|
+
return truncateToWidth(line.replace(/[\r\n]+/g, " "), Math.max(1, width - 2), Ellipsis.Omit);
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
const STATUS_ORDER: Record<AgentStatus, number> = { running: 0, idle: 1, parked: 2, aborted: 3 };
|
|
@@ -298,7 +303,8 @@ export class AgentHubOverlayComponent extends Container {
|
|
|
298
303
|
}
|
|
299
304
|
|
|
300
305
|
override render(width: number): readonly string[] {
|
|
301
|
-
|
|
306
|
+
const lines = this.#view === "table" ? this.#renderTable(width) : this.#renderChat(width);
|
|
307
|
+
return lines.map(line => clampHubLine(line, width));
|
|
302
308
|
}
|
|
303
309
|
|
|
304
310
|
handleInput(keyData: string): void {
|
|
@@ -490,7 +496,8 @@ export class AgentHubOverlayComponent extends Container {
|
|
|
490
496
|
parts.push(theme.fg("warning", `⧉ ${unread}`));
|
|
491
497
|
}
|
|
492
498
|
parts.push(theme.fg("dim", formatAge(Math.max(1, Math.round((Date.now() - ref.lastActivity) / 1000)))));
|
|
493
|
-
|
|
499
|
+
const rawLine = ` ${cursor} ${parts.join(theme.sep.dot)}`;
|
|
500
|
+
return truncateToWidth(rawLine.replace(/[\r\n]+/g, " "), Math.max(1, width - 1));
|
|
494
501
|
}
|
|
495
502
|
|
|
496
503
|
#handleTableInput(keyData: string): void {
|
|
@@ -58,6 +58,10 @@ export class BtwPanelComponent extends Container {
|
|
|
58
58
|
this.#rebuild();
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
isBranchable(): boolean {
|
|
62
|
+
return this.#state === "complete" && this.#answer.trim().length > 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
61
65
|
close(): void {
|
|
62
66
|
this.#closed = true;
|
|
63
67
|
}
|
|
@@ -85,7 +89,7 @@ export class BtwPanelComponent extends Container {
|
|
|
85
89
|
case "running":
|
|
86
90
|
return theme.fg("muted", "Esc cancel /btw");
|
|
87
91
|
case "complete":
|
|
88
|
-
return theme.fg("muted", "Esc dismiss");
|
|
92
|
+
return theme.fg("muted", this.isBranchable() ? "b branch · Esc dismiss" : "Esc dismiss");
|
|
89
93
|
case "aborted":
|
|
90
94
|
return theme.fg("warning", `${theme.status.warning} Cancelled · Esc dismiss`);
|
|
91
95
|
case "error":
|
|
@@ -22,6 +22,7 @@ type ConfigurableEditorAction = Extract<
|
|
|
22
22
|
| "app.editor.external"
|
|
23
23
|
| "app.history.search"
|
|
24
24
|
| "app.message.dequeue"
|
|
25
|
+
| "app.retry"
|
|
25
26
|
| "app.clipboard.pasteImage"
|
|
26
27
|
| "app.clipboard.pasteTextRaw"
|
|
27
28
|
| "app.clipboard.copyPrompt"
|
|
@@ -43,6 +44,7 @@ const DEFAULT_ACTION_KEYS: Record<ConfigurableEditorAction, KeyId[]> = {
|
|
|
43
44
|
"app.editor.external": ["ctrl+g"],
|
|
44
45
|
"app.history.search": ["ctrl+r"],
|
|
45
46
|
"app.message.dequeue": ["alt+up"],
|
|
47
|
+
"app.retry": ["alt+r"],
|
|
46
48
|
"app.clipboard.pasteImage": ["ctrl+v"],
|
|
47
49
|
"app.clipboard.pasteTextRaw": ["ctrl+shift+v", "alt+shift+v"],
|
|
48
50
|
"app.clipboard.copyPrompt": ["alt+shift+c"],
|
|
@@ -268,6 +270,8 @@ export class CustomEditor extends Editor {
|
|
|
268
270
|
onPasteTextRaw?: () => void;
|
|
269
271
|
/** Called when the configured dequeue shortcut is pressed. */
|
|
270
272
|
onDequeue?: () => void;
|
|
273
|
+
/** Called when the configured retry shortcut is pressed. */
|
|
274
|
+
onRetry?: () => void;
|
|
271
275
|
/** Called when Caps Lock is pressed. */
|
|
272
276
|
onCapsLock?: () => void;
|
|
273
277
|
/** Called when left-arrow is pressed while the editor is empty (cursor necessarily at start). */
|
|
@@ -588,6 +592,20 @@ export class CustomEditor extends Editor {
|
|
|
588
592
|
return;
|
|
589
593
|
}
|
|
590
594
|
|
|
595
|
+
// Intercept configured retry shortcut. Later user/custom handlers keep
|
|
596
|
+
// precedence so adding the default Alt+R binding does not steal existing
|
|
597
|
+
// shortcuts such as app.plan.toggle or extension commands; copy-prompt is
|
|
598
|
+
// checked above for the same reason.
|
|
599
|
+
if (this.#matchesAction(canonical, "app.retry") && this.onRetry) {
|
|
600
|
+
const customHandler = this.#customMatchKeys.get(canonical);
|
|
601
|
+
if (customHandler) {
|
|
602
|
+
customHandler();
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
this.onRetry();
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
|
|
591
609
|
// Check custom key handlers (extensions)
|
|
592
610
|
const handler = this.#customMatchKeys.get(canonical);
|
|
593
611
|
if (handler) {
|
|
@@ -181,6 +181,7 @@ export class StatusLineComponent implements Component {
|
|
|
181
181
|
#cachedBranchCwd: string | undefined = undefined;
|
|
182
182
|
#gitWatcher: fs.FSWatcher | null = null;
|
|
183
183
|
#onBranchChange: (() => void) | null = null;
|
|
184
|
+
#disposed = false;
|
|
184
185
|
#autoCompactEnabled: boolean = true;
|
|
185
186
|
#hookStatuses: Map<string, string> = new Map();
|
|
186
187
|
#subagentCount: number = 0;
|
|
@@ -329,6 +330,7 @@ export class StatusLineComponent implements Component {
|
|
|
329
330
|
|
|
330
331
|
try {
|
|
331
332
|
this.#gitWatcher = fs.watch(watchPath, () => {
|
|
333
|
+
if (this.#disposed) return;
|
|
332
334
|
this.#invalidateGitCaches();
|
|
333
335
|
if (this.#onBranchChange) {
|
|
334
336
|
this.#onBranchChange();
|
|
@@ -340,6 +342,8 @@ export class StatusLineComponent implements Component {
|
|
|
340
342
|
}
|
|
341
343
|
|
|
342
344
|
dispose(): void {
|
|
345
|
+
this.#disposed = true;
|
|
346
|
+
this.#onBranchChange = null;
|
|
343
347
|
if (this.#gitWatcher) {
|
|
344
348
|
this.#gitWatcher.close();
|
|
345
349
|
this.#gitWatcher = null;
|
|
@@ -391,6 +395,7 @@ export class StatusLineComponent implements Component {
|
|
|
391
395
|
this.#defaultBranch = "main";
|
|
392
396
|
(async () => {
|
|
393
397
|
const resolved = await git.branch.default(getProjectDir());
|
|
398
|
+
if (this.#disposed) return;
|
|
394
399
|
if (resolved) {
|
|
395
400
|
this.#defaultBranch = resolved;
|
|
396
401
|
if (this.#onBranchChange) {
|
|
@@ -460,6 +465,7 @@ export class StatusLineComponent implements Component {
|
|
|
460
465
|
try {
|
|
461
466
|
// Requires `gh repo set-default` to be configured; fails gracefully if not
|
|
462
467
|
const result = await $`gh pr view --json number,url`.quiet().nothrow();
|
|
468
|
+
if (this.#disposed) return;
|
|
463
469
|
if (result.exitCode !== 0) {
|
|
464
470
|
setCachedPr(null);
|
|
465
471
|
return;
|
|
@@ -471,10 +477,11 @@ export class StatusLineComponent implements Component {
|
|
|
471
477
|
setCachedPr(null);
|
|
472
478
|
}
|
|
473
479
|
} catch {
|
|
480
|
+
if (this.#disposed) return;
|
|
474
481
|
setCachedPr(null);
|
|
475
482
|
} finally {
|
|
476
483
|
this.#prLookupInFlight = false;
|
|
477
|
-
if (this.#onBranchChange) {
|
|
484
|
+
if (!this.#disposed && this.#onBranchChange) {
|
|
478
485
|
this.#onBranchChange();
|
|
479
486
|
}
|
|
480
487
|
}
|