Skip to content

Commit c129d1c

Browse files
committed
feat(notification): add showHostNotification function for user alerts
feat(tools): remove terminal tools and related imports
1 parent ad91633 commit c129d1c

6 files changed

Lines changed: 32 additions & 554 deletions

File tree

src/client-pipe.ts

Lines changed: 1 addition & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,7 @@
88
* Client Pipe Client
99
*
1010
* Connects to the Client extension's pipe server (Extension Development Host)
11-
* to interact with terminal, output channel, and VS Code command APIs.
12-
*
13-
* Terminal methods (single-terminal model):
14-
* - terminal.run: Run a command, wait for completion/prompt/timeout
15-
* - terminal.input: Send input to a waiting prompt
16-
* - terminal.state: Check current terminal state
17-
* - terminal.kill: Send Ctrl+C to stop the running process
18-
* - terminal.listAll: List all terminals (tracked + untracked)
11+
* to interact with output channel, and VS Code command APIs.
1912
*
2013
* Output methods:
2114
* - output.listChannels: List VS Code output channels
@@ -36,8 +29,6 @@ const CLIENT_PIPE_PATH = IS_WINDOWS
3629
: '/tmp/vscode-devtools-client.sock';
3730

3831
const DEFAULT_TIMEOUT_MS = 10_000;
39-
// Terminal operations wait up to 35s so the 30s command timeout finishes first
40-
const TERMINAL_TIMEOUT_MS = 35_000;
4132

4233
// ── Types ────────────────────────────────────────────────
4334

@@ -67,25 +58,6 @@ function isJsonRpcResponse(value: unknown): value is JsonRpcResponse {
6758
);
6859
}
6960

70-
export type TerminalStatus =
71-
| 'idle'
72-
| 'running'
73-
| 'completed'
74-
| 'waiting_for_input'
75-
| 'timeout';
76-
77-
export type WaitMode = 'completion' | 'background';
78-
79-
export interface ActiveProcess {
80-
terminalName: string;
81-
pid?: number;
82-
command: string;
83-
status: TerminalStatus;
84-
startedAt: string;
85-
durationMs: number;
86-
exitCode?: number;
87-
}
88-
8961
export type ProcessStatus = 'running' | 'completed' | 'killed' | 'orphaned';
9062

9163
export interface ChildProcessInfo {
@@ -115,20 +87,6 @@ export interface ProcessLedgerSummary {
11587
sessionId: string;
11688
}
11789

118-
export interface TerminalRunResult {
119-
status: TerminalStatus;
120-
output: string;
121-
shell?: string;
122-
cwd?: string;
123-
exitCode?: number;
124-
prompt?: string;
125-
pid?: number;
126-
name?: string;
127-
durationMs?: number;
128-
activeProcesses?: ActiveProcess[];
129-
terminalSessions?: TerminalSessionInfo[];
130-
}
131-
13290
export interface TerminalSessionInfo {
13391
name: string;
13492
shell?: string;
@@ -254,81 +212,6 @@ function sendClientRequest(
254212
});
255213
}
256214

257-
// ── Terminal Methods (Multi-Terminal Model) ─────────────
258-
259-
/**
260-
* Run a PowerShell command in a named terminal.
261-
* Creates terminal if needed, rejects with state if busy.
262-
* Waits for completion, prompt detection, or timeout.
263-
*
264-
* @param command The PowerShell command to execute
265-
* @param cwd Absolute path to working directory for command execution
266-
* @param timeout Max wait time in milliseconds (default: 120000)
267-
* @param name Terminal name (default: 'default')
268-
* @param waitMode 'completion' blocks until done; 'background' returns immediately
269-
*/
270-
export async function terminalRun(
271-
command: string,
272-
cwd: string,
273-
timeout?: number,
274-
name?: string,
275-
waitMode?: WaitMode,
276-
): Promise<TerminalRunResult> {
277-
const result = await sendClientRequest(
278-
'terminal.run',
279-
{command, cwd, timeout, name, waitMode},
280-
TERMINAL_TIMEOUT_MS,
281-
);
282-
assertResult<TerminalRunResult>(result, 'terminal.run');
283-
return result;
284-
}
285-
286-
/**
287-
* Send input to a terminal waiting for a prompt.
288-
* Waits for the next completion or prompt after sending.
289-
*
290-
* @param text The text to send
291-
* @param addNewline Whether to press Enter after (default: true)
292-
* @param timeout Max wait time in milliseconds (default: 30000)
293-
* @param name Terminal name (default: 'default')
294-
*/
295-
export async function terminalInput(
296-
text: string,
297-
addNewline?: boolean,
298-
timeout?: number,
299-
name?: string,
300-
): Promise<TerminalRunResult> {
301-
const result = await sendClientRequest(
302-
'terminal.input',
303-
{text, addNewline, timeout, name},
304-
TERMINAL_TIMEOUT_MS,
305-
);
306-
assertResult<TerminalRunResult>(result, 'terminal.input');
307-
return result;
308-
}
309-
310-
/**
311-
* Get the current terminal state without modifying anything.
312-
*
313-
* @param name Terminal name (default: 'default')
314-
*/
315-
export async function terminalGetState(name?: string): Promise<TerminalRunResult> {
316-
const result = await sendClientRequest('terminal.state', {name});
317-
assertResult<TerminalRunResult>(result, 'terminal.state');
318-
return result;
319-
}
320-
321-
/**
322-
* Send Ctrl+C to kill the running process in a terminal.
323-
*
324-
* @param name Terminal name (default: 'default')
325-
*/
326-
export async function terminalKill(name?: string): Promise<TerminalRunResult> {
327-
const result = await sendClientRequest('terminal.kill', {name});
328-
assertResult<TerminalRunResult>(result, 'terminal.kill');
329-
return result;
330-
}
331-
332215
// ── Output Methods ───────────────────────────────────────
333216

334217
/**

src/host-pipe.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,18 @@ export async function restartMcpServer(): Promise<void> {
239239
}
240240
}
241241

242+
/**
243+
* Send a notification message to the Host extension for display
244+
* in the VS Code window. Fire-and-forget — failures are silently ignored.
245+
*/
246+
export async function showHostNotification(message: string): Promise<void> {
247+
try {
248+
await sendHostRequest('showNotification', {message}, 5_000);
249+
} catch {
250+
// Non-critical — notification may not be shown
251+
}
252+
}
253+
242254
/**
243255
* Check if the Host pipe is reachable via a system.ping.
244256
*/

src/main.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import process from 'node:process';
1212
import {parseArguments} from './cli.js';
1313
import {loadConfig, type ResolvedConfig} from './config.js';
1414
import {hasBuildChangedSinceWindowStart, isBuildStale} from './extension-watcher.js';
15-
import {restartMcpServer} from './host-pipe.js';
15+
import {restartMcpServer, showHostNotification} from './host-pipe.js';
1616
import {loadIssueDescriptions} from './issue-descriptions.js';
1717
import {logger, saveLogsToFile} from './logger.js';
1818
import {McpResponse} from './McpResponse.js';
@@ -54,6 +54,10 @@ const mcpProcessStartTime = Date.now();
5454
let mcpServerHotReloadInfo: {builtAt: number} | null = null;
5555
let mcpServerRestartScheduled = false;
5656

57+
// ── Extension Hot-Reload State ──────────────────────────
58+
59+
let extensionHotReloadInfo: {builtAt: number} | null = null;
60+
5761
/**
5862
* Run an incremental build for hot-reload: `tsc` + `post-build` WITHOUT
5963
* `rmSync('build')`. The full `pnpm run build` cleans the build dir first,
@@ -384,6 +388,7 @@ function registerTool(tool: ToolDefinition): void {
384388
// Check 1: Source files newer than build output → needs rebuild.
385389
if (!mcpServerRestartScheduled && hasMcpServerSourceChanged(mcpServerDir)) {
386390
logger(`[tool:${tool.name}] MCP server source changed — triggering self rebuild…`);
391+
showHostNotification('🔨 MCP Server: Rebuilding…').catch(() => {});
387392

388393
try {
389394
await runMcpServerBuild();
@@ -482,6 +487,7 @@ function registerTool(tool: ToolDefinition): void {
482487
const reason = stale ? 'source stale' : 'manual build detected';
483488
logger(`[tool:${tool.name}] Extension needs hot-reload (${reason}) — reloading…`);
484489
await lifecycleService.handleHotReload();
490+
extensionHotReloadInfo = {builtAt: Date.now()};
485491
logger(`[tool:${tool.name}] Hot-reload complete — reconnected`);
486492
}
487493
}
@@ -580,6 +586,16 @@ function registerTool(tool: ToolDefinition): void {
580586
mcpServerHotReloadInfo = null;
581587
}
582588

589+
// Inject extension hot-reload banner on first tool call after extension reload
590+
if (extensionHotReloadInfo) {
591+
const secondsAgo = Math.round((Date.now() - extensionHotReloadInfo.builtAt) / 1000);
592+
result.content.unshift({
593+
type: 'text',
594+
text: `✅ **Extension was recently updated.** Latest build completed ${secondsAgo}s ago. The Extension Development Host is now running the newest code.`,
595+
});
596+
extensionHotReloadInfo = null;
597+
}
598+
583599
return result;
584600
} catch (err) {
585601
logger(`[tool:${tool.name}] ERROR: ${err && 'message' in err ? err.message : String(err)}`);

src/tools/reload-mcp-server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import process from 'node:process';
88

9-
import {restartMcpServer} from '../host-pipe.js';
9+
import {restartMcpServer, showHostNotification} from '../host-pipe.js';
1010
import {logger} from '../logger.js';
1111
import {
1212
getMcpServerRoot,
@@ -64,6 +64,7 @@ Examples:
6464
logger(`[reload_mcp_server] ${status}`);
6565

6666
writeHotReloadMarker(mcpServerDir);
67+
showHostNotification('🔄 MCP Server: Restarting…').catch(() => {});
6768

6869
// Schedule restart after this response is flushed via stdio
6970
setTimeout(async () => {

0 commit comments

Comments
 (0)