@oh-my-pi/pi-coding-agent 14.2.0 → 14.3.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 +59 -0
- package/package.json +19 -19
- package/src/cli/args.ts +10 -1
- package/src/cli/shell-cli.ts +15 -3
- package/src/config/settings-schema.ts +60 -1
- package/src/dap/session.ts +8 -2
- package/src/debug/system-info.ts +6 -2
- package/src/discovery/claude.ts +58 -36
- package/src/discovery/opencode.ts +20 -2
- package/src/edit/index.ts +3 -1
- package/src/edit/modes/chunk.ts +133 -53
- package/src/edit/modes/hashline.ts +36 -11
- package/src/edit/renderer.ts +98 -133
- package/src/edit/streaming.ts +351 -0
- package/src/exec/bash-executor.ts +60 -5
- package/src/internal-urls/docs-index.generated.ts +5 -5
- package/src/internal-urls/pi-protocol.ts +0 -2
- package/src/lsp/client.ts +22 -6
- package/src/lsp/defaults.json +2 -1
- package/src/lsp/index.ts +53 -10
- package/src/lsp/types.ts +2 -0
- package/src/modes/acp/acp-agent.ts +76 -2
- package/src/modes/components/assistant-message.ts +1 -34
- package/src/modes/components/hook-editor.ts +1 -1
- package/src/modes/components/tool-execution.ts +111 -101
- package/src/modes/controllers/input-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +0 -2
- package/src/modes/theme/mermaid-cache.ts +13 -52
- package/src/modes/theme/theme.ts +2 -2
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/tools/ast-grep.md +1 -0
- package/src/prompts/tools/browser.md +1 -0
- package/src/prompts/tools/chunk-edit.md +25 -22
- package/src/prompts/tools/gh-pr-push.md +2 -1
- package/src/prompts/tools/grep.md +4 -3
- package/src/prompts/tools/lsp.md +6 -0
- package/src/prompts/tools/read-chunk.md +46 -7
- package/src/prompts/tools/read.md +7 -4
- package/src/sdk.ts +8 -5
- package/src/session/agent-session.ts +36 -20
- package/src/session/session-manager.ts +228 -57
- package/src/session/streaming-output.ts +11 -0
- package/src/system-prompt.ts +7 -2
- package/src/task/executor.ts +1 -0
- package/src/tools/ast-edit.ts +37 -2
- package/src/tools/bash.ts +75 -12
- package/src/tools/find.ts +19 -26
- package/src/tools/gh.ts +6 -16
- package/src/tools/grep.ts +94 -37
- package/src/tools/path-utils.ts +31 -3
- package/src/tools/resolve.ts +12 -3
- package/src/tools/sqlite-reader.ts +116 -3
- package/src/tools/vim.ts +1 -1
- package/src/web/search/providers/codex.ts +129 -6
|
@@ -45,7 +45,6 @@ export class PiProtocolHandler implements ProtocolHandler {
|
|
|
45
45
|
content,
|
|
46
46
|
contentType: "text/markdown",
|
|
47
47
|
size: Buffer.byteLength(content, "utf-8"),
|
|
48
|
-
sourcePath: "pi://",
|
|
49
48
|
};
|
|
50
49
|
}
|
|
51
50
|
|
|
@@ -78,7 +77,6 @@ export class PiProtocolHandler implements ProtocolHandler {
|
|
|
78
77
|
content,
|
|
79
78
|
contentType: "text/markdown",
|
|
80
79
|
size: Buffer.byteLength(content, "utf-8"),
|
|
81
|
-
sourcePath: `pi://${normalized}`,
|
|
82
80
|
};
|
|
83
81
|
}
|
|
84
82
|
}
|
package/src/lsp/client.ts
CHANGED
|
@@ -211,11 +211,19 @@ async function writeMessage(
|
|
|
211
211
|
message: LspJsonRpcRequest | LspJsonRpcNotification | LspJsonRpcResponse,
|
|
212
212
|
): Promise<void> {
|
|
213
213
|
const content = JSON.stringify(message);
|
|
214
|
-
sink.write(`Content-Length: ${Buffer.byteLength(content, "utf-8")}\r\n\r\n`);
|
|
215
|
-
sink.write(content);
|
|
214
|
+
sink.write(`Content-Length: ${Buffer.byteLength(content, "utf-8")}\r\n\r\n${content}`);
|
|
216
215
|
await sink.flush();
|
|
217
216
|
}
|
|
218
217
|
|
|
218
|
+
function queueWriteMessage(
|
|
219
|
+
client: LspClient,
|
|
220
|
+
message: LspJsonRpcRequest | LspJsonRpcNotification | LspJsonRpcResponse,
|
|
221
|
+
): Promise<void> {
|
|
222
|
+
const write = client.writeQueue.catch(() => {}).then(() => writeMessage(client.proc.stdin, message));
|
|
223
|
+
client.writeQueue = write.catch(() => {});
|
|
224
|
+
return write;
|
|
225
|
+
}
|
|
226
|
+
|
|
219
227
|
// =============================================================================
|
|
220
228
|
// Message Reader
|
|
221
229
|
// =============================================================================
|
|
@@ -382,7 +390,7 @@ async function sendResponse(
|
|
|
382
390
|
};
|
|
383
391
|
|
|
384
392
|
try {
|
|
385
|
-
await
|
|
393
|
+
await queueWriteMessage(client, response);
|
|
386
394
|
} catch (err) {
|
|
387
395
|
logger.error("LSP failed to respond.", { method, error: String(err) });
|
|
388
396
|
}
|
|
@@ -461,6 +469,7 @@ export async function getOrCreateClient(config: ServerConfig, cwd: string, initT
|
|
|
461
469
|
messageBuffer: new Uint8Array(0),
|
|
462
470
|
isReading: false,
|
|
463
471
|
lastActivity: Date.now(),
|
|
472
|
+
writeQueue: Promise.resolve(),
|
|
464
473
|
activeProgressTokens: new Set(),
|
|
465
474
|
projectLoaded,
|
|
466
475
|
resolveProjectLoaded,
|
|
@@ -475,7 +484,14 @@ export async function getOrCreateClient(config: ServerConfig, cwd: string, initT
|
|
|
475
484
|
|
|
476
485
|
// Reject any pending requests — the server is gone, they will never complete.
|
|
477
486
|
if (client.pendingRequests.size > 0) {
|
|
478
|
-
|
|
487
|
+
// Strip informational log lines (e.g. marksman's [INF]/[DBG] prefix)
|
|
488
|
+
// — they are startup noise, not actionable errors.
|
|
489
|
+
const rawStderr = proc.peekStderr().trim();
|
|
490
|
+
const stderr = rawStderr
|
|
491
|
+
.split("\n")
|
|
492
|
+
.filter(line => !/^\[\d{2}:\d{2}:\d{2} (?:INF|DBG|VRB)\]/.test(line))
|
|
493
|
+
.join("\n")
|
|
494
|
+
.trim();
|
|
479
495
|
const code = proc.exitCode;
|
|
480
496
|
const err = new Error(
|
|
481
497
|
stderr ? `LSP server exited (code ${code}): ${stderr}` : `LSP server exited unexpectedly (code ${code})`,
|
|
@@ -848,7 +864,7 @@ export async function sendRequest(
|
|
|
848
864
|
});
|
|
849
865
|
|
|
850
866
|
// Write request
|
|
851
|
-
|
|
867
|
+
queueWriteMessage(client, request).catch(err => {
|
|
852
868
|
if (timeout) clearTimeout(timeout);
|
|
853
869
|
client.pendingRequests.delete(id);
|
|
854
870
|
cleanup();
|
|
@@ -868,7 +884,7 @@ export async function sendNotification(client: LspClient, method: string, params
|
|
|
868
884
|
};
|
|
869
885
|
|
|
870
886
|
client.lastActivity = Date.now();
|
|
871
|
-
await
|
|
887
|
+
await queueWriteMessage(client, notification);
|
|
872
888
|
}
|
|
873
889
|
|
|
874
890
|
/**
|
package/src/lsp/defaults.json
CHANGED
package/src/lsp/index.ts
CHANGED
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
type LspParams,
|
|
41
41
|
type LspToolDetails,
|
|
42
42
|
lspSchema,
|
|
43
|
+
type Position,
|
|
43
44
|
type PublishedDiagnostics,
|
|
44
45
|
type ServerConfig,
|
|
45
46
|
type SymbolInformation,
|
|
@@ -262,6 +263,10 @@ function getLspServerForFile(config: LspConfig, filePath: string): [string, Serv
|
|
|
262
263
|
return servers.length > 0 ? servers[0] : null;
|
|
263
264
|
}
|
|
264
265
|
|
|
266
|
+
function isProjectAwareLspServer(serverConfig: ServerConfig): boolean {
|
|
267
|
+
return !serverConfig.createClient && !serverConfig.isLinter;
|
|
268
|
+
}
|
|
269
|
+
|
|
265
270
|
const DIAGNOSTIC_MESSAGE_LIMIT = 50;
|
|
266
271
|
const SINGLE_DIAGNOSTICS_WAIT_TIMEOUT_MS = 3000;
|
|
267
272
|
const BATCH_DIAGNOSTICS_WAIT_TIMEOUT_MS = 400;
|
|
@@ -278,6 +283,21 @@ function limitDiagnosticMessages(messages: string[]): string[] {
|
|
|
278
283
|
const LOCATION_CONTEXT_LINES = 1;
|
|
279
284
|
const REFERENCE_CONTEXT_LIMIT = 50;
|
|
280
285
|
|
|
286
|
+
const REFERENCES_RETRY_COUNT = 2;
|
|
287
|
+
const REFERENCES_RETRY_DELAY_MS = 250;
|
|
288
|
+
|
|
289
|
+
function comparePosition(a: Position, b: Position): number {
|
|
290
|
+
return a.line === b.line ? a.character - b.character : a.line - b.line;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function rangeContainsPosition(range: Location["range"], position: Position): boolean {
|
|
294
|
+
return comparePosition(range.start, position) <= 0 && comparePosition(position, range.end) <= 0;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function isOnlyQueriedDeclaration(locations: Location[], uri: string, position: Position): boolean {
|
|
298
|
+
return locations.length === 1 && locations[0]?.uri === uri && rangeContainsPosition(locations[0].range, position);
|
|
299
|
+
}
|
|
300
|
+
|
|
281
301
|
function normalizeLocationResult(result: Location | Location[] | LocationLink | LocationLink[] | null): Location[] {
|
|
282
302
|
if (!result) return [];
|
|
283
303
|
const raw = Array.isArray(result) ? result : [result];
|
|
@@ -560,6 +580,10 @@ async function getDiagnosticsForFile(
|
|
|
560
580
|
// Default: use LSP
|
|
561
581
|
const client = await getOrCreateClient(serverConfig, cwd);
|
|
562
582
|
throwIfAborted(signal);
|
|
583
|
+
if (isProjectAwareLspServer(serverConfig)) {
|
|
584
|
+
await waitForProjectLoaded(client, signal);
|
|
585
|
+
throwIfAborted(signal);
|
|
586
|
+
}
|
|
563
587
|
// Content already synced + didSave sent, wait for fresh diagnostics
|
|
564
588
|
const minVersion = minVersions?.get(serverName);
|
|
565
589
|
const expectedDocumentVersion = expectedDocumentVersions?.get(serverName);
|
|
@@ -1220,6 +1244,10 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1220
1244
|
continue;
|
|
1221
1245
|
}
|
|
1222
1246
|
const client = await getOrCreateClient(serverConfig, this.session.cwd);
|
|
1247
|
+
if (isProjectAwareLspServer(serverConfig)) {
|
|
1248
|
+
await waitForProjectLoaded(client, signal);
|
|
1249
|
+
throwIfAborted(signal);
|
|
1250
|
+
}
|
|
1223
1251
|
const minVersion = client.diagnosticsVersion;
|
|
1224
1252
|
await refreshFile(client, resolved, signal);
|
|
1225
1253
|
const expectedDocumentVersion = client.openFiles.get(uri)?.version;
|
|
@@ -1512,16 +1540,31 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1512
1540
|
break;
|
|
1513
1541
|
}
|
|
1514
1542
|
case "references": {
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
textDocument
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1543
|
+
let result: Location[] | null = null;
|
|
1544
|
+
for (let attempt = 0; attempt <= REFERENCES_RETRY_COUNT; attempt++) {
|
|
1545
|
+
result = (await sendRequest(
|
|
1546
|
+
client,
|
|
1547
|
+
"textDocument/references",
|
|
1548
|
+
{
|
|
1549
|
+
textDocument: { uri },
|
|
1550
|
+
position,
|
|
1551
|
+
context: { includeDeclaration: true },
|
|
1552
|
+
},
|
|
1553
|
+
signal,
|
|
1554
|
+
)) as Location[] | null;
|
|
1555
|
+
|
|
1556
|
+
const locations = result ?? [];
|
|
1557
|
+
if (!isProjectAwareLspServer(serverConfig) || attempt === REFERENCES_RETRY_COUNT) {
|
|
1558
|
+
break;
|
|
1559
|
+
}
|
|
1560
|
+
if (locations.length > 0 && !isOnlyQueriedDeclaration(locations, uri, position)) {
|
|
1561
|
+
break;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
await waitForProjectLoaded(client, signal);
|
|
1565
|
+
throwIfAborted(signal);
|
|
1566
|
+
await untilAborted(signal, () => Bun.sleep(REFERENCES_RETRY_DELAY_MS));
|
|
1567
|
+
}
|
|
1525
1568
|
|
|
1526
1569
|
if (!result || result.length === 0) {
|
|
1527
1570
|
output = "No references found";
|
package/src/lsp/types.ts
CHANGED
|
@@ -411,6 +411,8 @@ export interface LspClient {
|
|
|
411
411
|
isReading: boolean;
|
|
412
412
|
serverCapabilities?: LspServerCapabilities;
|
|
413
413
|
lastActivity: number;
|
|
414
|
+
/** Serializes outbound JSON-RPC writes to the server process. */
|
|
415
|
+
writeQueue: Promise<void>;
|
|
414
416
|
/** Tracks active work-done progress tokens from the server */
|
|
415
417
|
activeProgressTokens: Set<string | number>;
|
|
416
418
|
/** Resolves when the server's initial project loading completes (or after timeout) */
|
|
@@ -39,11 +39,14 @@ import {
|
|
|
39
39
|
} from "@agentclientprotocol/sdk";
|
|
40
40
|
import type { Model } from "@oh-my-pi/pi-ai";
|
|
41
41
|
import { logger, VERSION } from "@oh-my-pi/pi-utils";
|
|
42
|
+
import { disableProvider, enableProvider } from "../../capability";
|
|
43
|
+
import { Settings } from "../../config/settings";
|
|
42
44
|
import type { ExtensionUIContext } from "../../extensibility/extensions";
|
|
43
45
|
import { runExtensionCompact } from "../../extensibility/extensions/compact-handler";
|
|
44
46
|
import { loadSlashCommands } from "../../extensibility/slash-commands";
|
|
45
47
|
import { MCPManager } from "../../mcp/manager";
|
|
46
48
|
import type { MCPServerConfig } from "../../mcp/types";
|
|
49
|
+
import { loadAllExtensions } from "../../modes/components/extensions/state-manager";
|
|
47
50
|
import { theme } from "../../modes/theme/theme";
|
|
48
51
|
import type { AgentSession, AgentSessionEvent } from "../../session/agent-session";
|
|
49
52
|
import {
|
|
@@ -379,8 +382,79 @@ export class AcpAgent implements Agent {
|
|
|
379
382
|
}
|
|
380
383
|
}
|
|
381
384
|
|
|
382
|
-
async extMethod(
|
|
383
|
-
|
|
385
|
+
async extMethod(method: string, params: { [key: string]: unknown }): Promise<{ [key: string]: unknown }> {
|
|
386
|
+
switch (method) {
|
|
387
|
+
case "omp/sessions/listAll": {
|
|
388
|
+
const limit = typeof params.limit === "number" ? Math.max(1, Math.min(5000, params.limit as number)) : 1000;
|
|
389
|
+
const sessions = await SessionManager.listAll();
|
|
390
|
+
const sorted = sessions.sort((l, r) => r.modified.getTime() - l.modified.getTime()).slice(0, limit);
|
|
391
|
+
return {
|
|
392
|
+
sessions: sorted.map(s => this.#toSessionInfo(s)),
|
|
393
|
+
total: sessions.length,
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
case "omp/projects/list": {
|
|
397
|
+
const sessions = await SessionManager.listAll();
|
|
398
|
+
const buckets = new Map<
|
|
399
|
+
string,
|
|
400
|
+
{ cwd: string; sessionCount: number; lastActivityAt: number; lastTitle: string }
|
|
401
|
+
>();
|
|
402
|
+
for (const s of sessions) {
|
|
403
|
+
if (!s.cwd) continue;
|
|
404
|
+
const ts = s.modified.getTime();
|
|
405
|
+
const existing = buckets.get(s.cwd);
|
|
406
|
+
if (existing) {
|
|
407
|
+
existing.sessionCount += 1;
|
|
408
|
+
if (ts > existing.lastActivityAt) {
|
|
409
|
+
existing.lastActivityAt = ts;
|
|
410
|
+
existing.lastTitle = s.title ?? "";
|
|
411
|
+
}
|
|
412
|
+
} else {
|
|
413
|
+
buckets.set(s.cwd, {
|
|
414
|
+
cwd: s.cwd,
|
|
415
|
+
sessionCount: 1,
|
|
416
|
+
lastActivityAt: ts,
|
|
417
|
+
lastTitle: s.title ?? "",
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
const projects = Array.from(buckets.values()).sort((a, b) => b.lastActivityAt - a.lastActivityAt);
|
|
422
|
+
return { projects, totalSessions: sessions.length };
|
|
423
|
+
}
|
|
424
|
+
case "omp/chats/byCwd": {
|
|
425
|
+
const cwd = typeof params.cwd === "string" ? (params.cwd as string) : undefined;
|
|
426
|
+
if (!cwd) throw new Error("cwd required");
|
|
427
|
+
const limit = typeof params.limit === "number" ? Math.max(1, Math.min(500, params.limit as number)) : 100;
|
|
428
|
+
const sessions = await SessionManager.list(cwd);
|
|
429
|
+
const sorted = sessions.sort((l, r) => r.modified.getTime() - l.modified.getTime()).slice(0, limit);
|
|
430
|
+
return { sessions: sorted.map(s => this.#toSessionInfo(s)) };
|
|
431
|
+
}
|
|
432
|
+
case "omp/usage": {
|
|
433
|
+
const [firstRecord] = this.#sessions.values();
|
|
434
|
+
const target = firstRecord?.session ?? this.#initialSession;
|
|
435
|
+
const reports = await target.fetchUsageReports();
|
|
436
|
+
return { reports: reports ?? [] };
|
|
437
|
+
}
|
|
438
|
+
case "omp/extensions": {
|
|
439
|
+
const cwd = typeof params.cwd === "string" ? (params.cwd as string) : undefined;
|
|
440
|
+
const sm = await Settings.init();
|
|
441
|
+
const disabledIds = (sm.get("disabledExtensions") as string[] | undefined) ?? [];
|
|
442
|
+
const extensions = await loadAllExtensions(cwd, disabledIds);
|
|
443
|
+
return { extensions: extensions as unknown as Array<{ [key: string]: unknown }> };
|
|
444
|
+
}
|
|
445
|
+
case "omp/extensions/toggle": {
|
|
446
|
+
const providerId = params.providerId;
|
|
447
|
+
if (typeof providerId !== "string") throw new Error("providerId required");
|
|
448
|
+
if (params.enabled === false) {
|
|
449
|
+
disableProvider(providerId);
|
|
450
|
+
return { enabled: false };
|
|
451
|
+
}
|
|
452
|
+
enableProvider(providerId);
|
|
453
|
+
return { enabled: true };
|
|
454
|
+
}
|
|
455
|
+
default:
|
|
456
|
+
throw new Error(`Unknown ACP ext method: ${method}`);
|
|
457
|
+
}
|
|
384
458
|
}
|
|
385
459
|
|
|
386
460
|
async extNotification(_method: string, _params: { [key: string]: unknown }): Promise<void> {}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { AssistantMessage, ImageContent, Usage } from "@oh-my-pi/pi-ai";
|
|
2
2
|
import { Container, Image, ImageProtocol, Markdown, Spacer, TERMINAL, Text } from "@oh-my-pi/pi-tui";
|
|
3
|
-
import { formatNumber
|
|
3
|
+
import { formatNumber } from "@oh-my-pi/pi-utils";
|
|
4
4
|
import { settings } from "../../config/settings";
|
|
5
|
-
import { hasPendingMermaid, prerenderMermaid } from "../../modes/theme/mermaid-cache";
|
|
6
5
|
import { getMarkdownTheme, theme } from "../../modes/theme/theme";
|
|
7
6
|
import { resolveImageOptions } from "../../tools/render-utils";
|
|
8
7
|
|
|
@@ -12,7 +11,6 @@ import { resolveImageOptions } from "../../tools/render-utils";
|
|
|
12
11
|
export class AssistantMessageComponent extends Container {
|
|
13
12
|
#contentContainer: Container;
|
|
14
13
|
#lastMessage?: AssistantMessage;
|
|
15
|
-
#prerenderInFlight = false;
|
|
16
14
|
#toolImagesByCallId = new Map<string, ImageContent[]>();
|
|
17
15
|
#usageInfo?: Usage;
|
|
18
16
|
|
|
@@ -85,34 +83,6 @@ export class AssistantMessageComponent extends Container {
|
|
|
85
83
|
this.#contentContainer.addChild(new Text(theme.fg("toolOutput", `[Image: ${image.mimeType}]`), 1, 0));
|
|
86
84
|
}
|
|
87
85
|
}
|
|
88
|
-
#triggerMermaidPrerender(message: AssistantMessage): void {
|
|
89
|
-
if (!TERMINAL.imageProtocol || this.#prerenderInFlight) return;
|
|
90
|
-
|
|
91
|
-
// Check if any text content has pending mermaid blocks
|
|
92
|
-
const hasPending = message.content.some(c => c.type === "text" && c.text.trim() && hasPendingMermaid(c.text));
|
|
93
|
-
if (!hasPending) return;
|
|
94
|
-
|
|
95
|
-
this.#prerenderInFlight = true;
|
|
96
|
-
|
|
97
|
-
// Fire off background prerender
|
|
98
|
-
void (async () => {
|
|
99
|
-
try {
|
|
100
|
-
for (const content of message.content) {
|
|
101
|
-
if (content.type === "text" && content.text.trim() && hasPendingMermaid(content.text)) {
|
|
102
|
-
prerenderMermaid(content.text);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
} catch (error) {
|
|
106
|
-
logger.warn("Background mermaid prerender failed", {
|
|
107
|
-
error: error instanceof Error ? error.message : String(error),
|
|
108
|
-
});
|
|
109
|
-
} finally {
|
|
110
|
-
this.#prerenderInFlight = false;
|
|
111
|
-
// Invalidate to re-render with cached images
|
|
112
|
-
this.invalidate();
|
|
113
|
-
}
|
|
114
|
-
})();
|
|
115
|
-
}
|
|
116
86
|
|
|
117
87
|
updateContent(message: AssistantMessage): void {
|
|
118
88
|
this.#lastMessage = message;
|
|
@@ -120,9 +90,6 @@ export class AssistantMessageComponent extends Container {
|
|
|
120
90
|
// Clear content container
|
|
121
91
|
this.#contentContainer.clear();
|
|
122
92
|
|
|
123
|
-
// Trigger background mermaid pre-rendering if needed
|
|
124
|
-
this.#triggerMermaidPrerender(message);
|
|
125
|
-
|
|
126
93
|
const hasVisibleContent = message.content.some(
|
|
127
94
|
c => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()),
|
|
128
95
|
);
|
|
@@ -136,7 +136,7 @@ export class HookEditorComponent extends Container {
|
|
|
136
136
|
const editorCmd = getEditorCommand();
|
|
137
137
|
if (!editorCmd) return;
|
|
138
138
|
|
|
139
|
-
const currentText = this.#editor.
|
|
139
|
+
const currentText = this.#editor.getExpandedText();
|
|
140
140
|
try {
|
|
141
141
|
this.#tui.stop();
|
|
142
142
|
const result = await openInEditor(editorCmd, currentText);
|