@docyrus/docyrus 0.0.34 → 0.0.36

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.
Files changed (66) hide show
  1. package/README.md +25 -0
  2. package/agent-loader.js +3 -2
  3. package/agent-loader.js.map +2 -2
  4. package/main.js +82162 -46093
  5. package/main.js.map +4 -4
  6. package/package.json +12 -3
  7. package/resources/chrome-tools/browser-content.js +46 -46
  8. package/resources/chrome-tools/browser-cookies.js +16 -16
  9. package/resources/chrome-tools/browser-eval.js +27 -27
  10. package/resources/chrome-tools/browser-hn-scraper.js +1 -1
  11. package/resources/chrome-tools/browser-nav.js +23 -23
  12. package/resources/chrome-tools/browser-pick.js +127 -127
  13. package/resources/chrome-tools/browser-screenshot.js +10 -10
  14. package/resources/chrome-tools/browser-start.js +38 -38
  15. package/resources/pi-agent/extensions/answer.ts +392 -384
  16. package/resources/pi-agent/extensions/context.ts +415 -415
  17. package/resources/pi-agent/extensions/control.ts +1287 -1287
  18. package/resources/pi-agent/extensions/diff.ts +171 -171
  19. package/resources/pi-agent/extensions/files.ts +155 -155
  20. package/resources/pi-agent/extensions/knowledge.ts +664 -0
  21. package/resources/pi-agent/extensions/loop.ts +375 -375
  22. package/resources/pi-agent/extensions/pi-bash-live-view/index.ts +1 -1
  23. package/resources/pi-agent/extensions/pi-bash-live-view/package.json +22 -22
  24. package/resources/pi-agent/extensions/pi-bash-live-view/pty-execute.ts +2 -2
  25. package/resources/pi-agent/extensions/pi-bash-live-view/pty-session.ts +2 -2
  26. package/resources/pi-agent/extensions/pi-bash-live-view/spawn-helper.ts +2 -2
  27. package/resources/pi-agent/extensions/pi-bash-live-view/terminal-emulator.ts +18 -18
  28. package/resources/pi-agent/extensions/pi-bash-live-view/truncate.ts +1 -1
  29. package/resources/pi-agent/extensions/pi-bash-live-view/widget.ts +4 -4
  30. package/resources/pi-agent/extensions/pi-custom-compaction/package.json +4 -4
  31. package/resources/pi-agent/extensions/pi-mcp-adapter/app-bridge.bundle.js +14 -14
  32. package/resources/pi-agent/extensions/pi-mcp-adapter/commands.ts +6 -6
  33. package/resources/pi-agent/extensions/pi-mcp-adapter/config.ts +9 -9
  34. package/resources/pi-agent/extensions/pi-mcp-adapter/consent-manager.ts +4 -4
  35. package/resources/pi-agent/extensions/pi-mcp-adapter/direct-tools.ts +13 -13
  36. package/resources/pi-agent/extensions/pi-mcp-adapter/glimpse-ui.ts +5 -5
  37. package/resources/pi-agent/extensions/pi-mcp-adapter/host-html-template.ts +13 -13
  38. package/resources/pi-agent/extensions/pi-mcp-adapter/index.ts +14 -14
  39. package/resources/pi-agent/extensions/pi-mcp-adapter/init.ts +17 -17
  40. package/resources/pi-agent/extensions/pi-mcp-adapter/lifecycle.ts +2 -2
  41. package/resources/pi-agent/extensions/pi-mcp-adapter/logger.ts +2 -2
  42. package/resources/pi-agent/extensions/pi-mcp-adapter/mcp-panel.ts +17 -17
  43. package/resources/pi-agent/extensions/pi-mcp-adapter/metadata-cache.ts +9 -9
  44. package/resources/pi-agent/extensions/pi-mcp-adapter/npx-resolver.ts +35 -35
  45. package/resources/pi-agent/extensions/pi-mcp-adapter/oauth-handler.ts +1 -1
  46. package/resources/pi-agent/extensions/pi-mcp-adapter/proxy-modes.ts +12 -12
  47. package/resources/pi-agent/extensions/pi-mcp-adapter/server-manager.ts +6 -6
  48. package/resources/pi-agent/extensions/pi-mcp-adapter/tool-metadata.ts +4 -4
  49. package/resources/pi-agent/extensions/pi-mcp-adapter/types.ts +2 -2
  50. package/resources/pi-agent/extensions/pi-mcp-adapter/ui-resource-handler.ts +6 -6
  51. package/resources/pi-agent/extensions/pi-mcp-adapter/ui-server.ts +17 -17
  52. package/resources/pi-agent/extensions/pi-mcp-adapter/ui-session.ts +22 -22
  53. package/resources/pi-agent/extensions/pi-mcp-adapter/utils.ts +2 -2
  54. package/resources/pi-agent/extensions/prompt-editor.ts +900 -900
  55. package/resources/pi-agent/extensions/prompt-url-widget.ts +122 -122
  56. package/resources/pi-agent/extensions/redraws.ts +14 -14
  57. package/resources/pi-agent/extensions/review.ts +1533 -1533
  58. package/resources/pi-agent/extensions/todos.ts +1735 -1735
  59. package/resources/pi-agent/extensions/tps.ts +40 -40
  60. package/resources/pi-agent/extensions/whimsical.ts +3 -3
  61. package/resources/pi-agent/prompts/agent-system.md +2 -0
  62. package/resources/pi-agent/prompts/coder-system.md +2 -0
  63. package/server-loader.js +82 -1
  64. package/server-loader.js.map +3 -3
  65. package/tui.mjs +2 -0
  66. package/tui.mjs.map +1 -1
@@ -9,7 +9,7 @@ import { getScopedMcpOAuthTokensPath } from "./paths.js";
9
9
  import { buildToolMetadata } from "./tool-metadata.js";
10
10
 
11
11
  export async function showStatus(state: McpExtensionState, ctx: ExtensionContext): Promise<void> {
12
- if (!ctx.hasUI) return;
12
+ if (!ctx.hasUI) {return;}
13
13
 
14
14
  const lines: string[] = ["MCP Server Status:", ""];
15
15
 
@@ -45,7 +45,7 @@ export async function showStatus(state: McpExtensionState, ctx: ExtensionContext
45
45
  }
46
46
 
47
47
  export async function showTools(state: McpExtensionState, ctx: ExtensionContext): Promise<void> {
48
- if (!ctx.hasUI) return;
48
+ if (!ctx.hasUI) {return;}
49
49
 
50
50
  const allTools = [...state.toolMetadata.values()].flat().map(m => m.name);
51
51
 
@@ -119,7 +119,7 @@ export async function authenticateServer(
119
119
  config: McpConfig,
120
120
  ctx: ExtensionContext
121
121
  ): Promise<void> {
122
- if (!ctx.hasUI) return;
122
+ if (!ctx.hasUI) {return;}
123
123
 
124
124
  const definition = config.mcpServers[serverName];
125
125
  if (!definition) {
@@ -172,7 +172,7 @@ export async function openMcpPanel(
172
172
  const provenanceMap = getServerProvenance(pi.getFlag("mcp-config") as string | undefined ?? configOverridePath);
173
173
 
174
174
  const callbacks: McpPanelCallbacks = {
175
- reconnect: async (serverName: string) => {
175
+ reconnect: async(serverName: string) => {
176
176
  return lazyConnect(state, serverName);
177
177
  },
178
178
  getConnectionStatus: (serverName: string) => {
@@ -181,8 +181,8 @@ export async function openMcpPanel(
181
181
  return "needs-auth";
182
182
  }
183
183
  const connection = state.manager.getConnection(serverName);
184
- if (connection?.status === "connected") return "connected";
185
- if (getFailureAgeSeconds(state, serverName) !== null) return "failed";
184
+ if (connection?.status === "connected") {return "connected";}
185
+ if (getFailureAgeSeconds(state, serverName) !== null) {return "failed";}
186
186
  return "idle";
187
187
  },
188
188
  refreshCacheAfterReconnect: (serverName: string) => {
@@ -37,13 +37,13 @@ export function loadMcpConfig(overridePath?: string): McpConfig {
37
37
  if (config.imports?.length) {
38
38
  for (const importKind of config.imports) {
39
39
  const importPath = IMPORT_PATHS[importKind];
40
- if (!importPath) continue;
40
+ if (!importPath) {continue;}
41
41
 
42
42
  const fullPath = importPath.startsWith(".")
43
43
  ? resolve(process.cwd(), importPath)
44
44
  : importPath;
45
45
 
46
- if (!existsSync(fullPath)) continue;
46
+ if (!existsSync(fullPath)) {continue;}
47
47
 
48
48
  try {
49
49
  const imported = JSON.parse(readFileSync(fullPath, "utf-8"));
@@ -102,7 +102,7 @@ function validateConfig(raw: unknown): McpConfig {
102
102
  }
103
103
 
104
104
  function extractServers(config: unknown, kind: ImportKind): Record<string, ServerEntry> {
105
- if (!config || typeof config !== "object") return {};
105
+ if (!config || typeof config !== "object") {return {};}
106
106
 
107
107
  const obj = config as Record<string, unknown>;
108
108
 
@@ -146,11 +146,11 @@ export function getServerProvenance(overridePath?: string): Map<string, ServerPr
146
146
  if (userConfig.imports?.length) {
147
147
  for (const importKind of userConfig.imports) {
148
148
  const importPath = IMPORT_PATHS[importKind];
149
- if (!importPath) continue;
149
+ if (!importPath) {continue;}
150
150
  const fullPath = importPath.startsWith(".")
151
151
  ? resolve(process.cwd(), importPath)
152
152
  : importPath;
153
- if (!existsSync(fullPath)) continue;
153
+ if (!existsSync(fullPath)) {continue;}
154
154
  try {
155
155
  const imported = JSON.parse(readFileSync(fullPath, "utf-8"));
156
156
  const servers = extractServers(imported, importKind);
@@ -185,11 +185,11 @@ export function writeDirectToolsConfig(
185
185
 
186
186
  for (const [serverName, value] of changes) {
187
187
  const prov = provenance.get(serverName);
188
- if (!prov) continue;
188
+ if (!prov) {continue;}
189
189
 
190
190
  const targetPath = prov.path;
191
191
 
192
- if (!byPath.has(targetPath)) byPath.set(targetPath, []);
192
+ if (!byPath.has(targetPath)) {byPath.set(targetPath, []);}
193
193
  byPath.get(targetPath)!.push({ name: serverName, value, prov });
194
194
  }
195
195
 
@@ -200,10 +200,10 @@ export function writeDirectToolsConfig(
200
200
  raw = JSON.parse(readFileSync(filePath, "utf-8"));
201
201
  } catch {}
202
202
  }
203
- if (!raw || typeof raw !== "object") raw = {};
203
+ if (!raw || typeof raw !== "object") {raw = {};}
204
204
 
205
205
  const servers = (raw.mcpServers ?? raw["mcp-servers"] ?? {}) as Record<string, ServerEntry>;
206
- if (typeof servers !== "object" || Array.isArray(servers)) continue;
206
+ if (typeof servers !== "object" || Array.isArray(servers)) {continue;}
207
207
 
208
208
  for (const { name, value, prov } of entries) {
209
209
  if (prov.kind === "import") {
@@ -13,9 +13,9 @@ export class ConsentManager {
13
13
  }
14
14
 
15
15
  requiresPrompt(serverName: string): boolean {
16
- if (this.mode === "never") return false;
17
- if (this.deniedServers.has(serverName)) return true;
18
- if (this.mode === "always") return true;
16
+ if (this.mode === "never") {return false;}
17
+ if (this.deniedServers.has(serverName)) {return true;}
18
+ if (this.mode === "always") {return true;}
19
19
  return !this.approvedServers.has(serverName);
20
20
  }
21
21
 
@@ -38,7 +38,7 @@ export class ConsentManager {
38
38
  }
39
39
 
40
40
  ensureApproved(serverName: string): void {
41
- if (this.mode === "never") return;
41
+ if (this.mode === "never") {return;}
42
42
  if (this.deniedServers.has(serverName)) {
43
43
  throw new ConsentError(serverName, { denied: true });
44
44
  }
@@ -19,7 +19,7 @@ export function resolveDirectTools(
19
19
  envOverride?: string[],
20
20
  ): DirectToolSpec[] {
21
21
  const specs: DirectToolSpec[] = [];
22
- if (!cache) return specs;
22
+ if (!cache) {return specs;}
23
23
 
24
24
  const seenNames = new Set<string>();
25
25
 
@@ -31,7 +31,7 @@ export function resolveDirectTools(
31
31
  if (item.includes("/")) {
32
32
  const [server, tool] = item.split("/", 2);
33
33
  if (server && tool) {
34
- if (!envTools.has(server)) envTools.set(server, new Set());
34
+ if (!envTools.has(server)) {envTools.set(server, new Set());}
35
35
  envTools.get(server)!.add(tool);
36
36
  } else if (server) {
37
37
  envServers.add(server);
@@ -46,7 +46,7 @@ export function resolveDirectTools(
46
46
 
47
47
  for (const [serverName, definition] of Object.entries(config.mcpServers)) {
48
48
  const serverCache = cache.servers[serverName];
49
- if (!serverCache || !isServerCacheValid(serverCache, definition)) continue;
49
+ if (!serverCache || !isServerCacheValid(serverCache, definition)) {continue;}
50
50
 
51
51
  let toolFilter: true | string[] | false = false;
52
52
 
@@ -64,10 +64,10 @@ export function resolveDirectTools(
64
64
  }
65
65
  }
66
66
 
67
- if (!toolFilter) continue;
67
+ if (!toolFilter) {continue;}
68
68
 
69
69
  for (const tool of serverCache.tools ?? []) {
70
- if (toolFilter !== true && !toolFilter.includes(tool.name)) continue;
70
+ if (toolFilter !== true && !toolFilter.includes(tool.name)) {continue;}
71
71
  const prefixedName = formatToolName(tool.name, serverName, prefix);
72
72
  if (BUILTIN_NAMES.has(prefixedName)) {
73
73
  console.warn(`MCP: skipping direct tool "${prefixedName}" (collides with builtin)`);
@@ -92,7 +92,7 @@ export function resolveDirectTools(
92
92
  if (definition.exposeResources !== false) {
93
93
  for (const resource of serverCache.resources ?? []) {
94
94
  const baseName = `get_${resourceNameToToolName(resource.name)}`;
95
- if (toolFilter !== true && !toolFilter.includes(baseName)) continue;
95
+ if (toolFilter !== true && !toolFilter.includes(baseName)) {continue;}
96
96
  const prefixedName = formatToolName(baseName, serverName, prefix);
97
97
  if (BUILTIN_NAMES.has(prefixedName)) {
98
98
  console.warn(`MCP: skipping direct resource tool "${prefixedName}" (collides with builtin)`);
@@ -142,7 +142,7 @@ export function buildProxyDescription(
142
142
  const toolCount = entry?.tools?.length ?? 0;
143
143
  const resourceCount = definition?.exposeResources !== false ? (entry?.resources?.length ?? 0) : 0;
144
144
  const totalItems = toolCount + resourceCount;
145
- if (totalItems === 0) continue;
145
+ if (totalItems === 0) {continue;}
146
146
  const directCount = directByServer.get(serverName) ?? 0;
147
147
  const proxyCount = totalItems - directCount;
148
148
  if (proxyCount > 0) {
@@ -233,12 +233,12 @@ export function createDirectToolExecutor(
233
233
  const hasUi = !!spec.uiResourceUri;
234
234
  uiSession = hasUi
235
235
  ? await maybeStartUiSession(state, {
236
- serverName: spec.serverName,
237
- toolName: spec.originalName,
238
- toolArgs: params ?? {},
239
- uiResourceUri: spec.uiResourceUri!,
240
- streamMode: spec.uiStreamMode,
241
- })
236
+ serverName: spec.serverName,
237
+ toolName: spec.originalName,
238
+ toolArgs: params ?? {},
239
+ uiResourceUri: spec.uiResourceUri!,
240
+ streamMode: spec.uiStreamMode,
241
+ })
242
242
  : null;
243
243
 
244
244
  const resultPromise = connection.client.callTool({
@@ -8,7 +8,7 @@ let glimpseAvailable: boolean | null = null;
8
8
  let resolvedBinaryPath: string | null = null;
9
9
 
10
10
  export function isGlimpseAvailable(): boolean {
11
- if (glimpseAvailable !== null) return glimpseAvailable;
11
+ if (glimpseAvailable !== null) {return glimpseAvailable;}
12
12
 
13
13
  if (platform() !== "darwin") {
14
14
  glimpseAvailable = false;
@@ -30,14 +30,14 @@ function getGlimpseBinaryPath(): string | null {
30
30
  const require = createRequire(import.meta.url);
31
31
  const glimpseuiPath = require.resolve("glimpseui");
32
32
  const binaryPath = join(dirname(glimpseuiPath), "glimpse");
33
- if (existsSync(binaryPath)) return binaryPath;
33
+ if (existsSync(binaryPath)) {return binaryPath;}
34
34
  } catch {}
35
35
 
36
36
  // Global npm install
37
37
  try {
38
38
  const globalRoot = execFileSync("npm", ["root", "-g"], { encoding: "utf-8" }).trim();
39
39
  const binaryPath = join(globalRoot, "glimpseui", "src", "glimpse");
40
- if (existsSync(binaryPath)) return binaryPath;
40
+ if (existsSync(binaryPath)) {return binaryPath;}
41
41
  } catch {}
42
42
 
43
43
  return null;
@@ -65,14 +65,14 @@ export async function openGlimpseWindow(
65
65
  });
66
66
 
67
67
  win.on("closed", () => {
68
- if (!active) return;
68
+ if (!active) {return;}
69
69
  active = false;
70
70
  options.onClosed();
71
71
  });
72
72
 
73
73
  return {
74
74
  close: () => {
75
- if (!active) return;
75
+ if (!active) {return;}
76
76
  active = false;
77
77
  win.close();
78
78
  },
@@ -357,7 +357,7 @@ export function buildHostHtmlTemplate(input: HostHtmlTemplateInput): string {
357
357
  }
358
358
 
359
359
  export function buildCspMetaContent(csp: UiResourceCsp | undefined): string | undefined {
360
- if (!csp) return undefined;
360
+ if (!csp) {return undefined;}
361
361
 
362
362
  const directives: string[] = [];
363
363
  directives.push("default-src 'none'");
@@ -372,27 +372,27 @@ export function buildCspMetaContent(csp: UiResourceCsp | undefined): string | un
372
372
  const workerSrc = toDirective("worker-src", csp.workerDomains);
373
373
  const baseUri = toDirective("base-uri", csp.baseUriDomains);
374
374
 
375
- if (scriptSrc) directives.push(scriptSrc);
376
- if (styleSrc) directives.push(styleSrc);
377
- if (fontSrc) directives.push(fontSrc);
378
- if (imgSrc) directives.push(imgSrc);
379
- if (mediaSrc) directives.push(mediaSrc);
380
- if (connectSrc) directives.push(connectSrc);
381
- if (frameSrc) directives.push(frameSrc);
382
- if (workerSrc) directives.push(workerSrc);
383
- if (baseUri) directives.push(baseUri);
375
+ if (scriptSrc) {directives.push(scriptSrc);}
376
+ if (styleSrc) {directives.push(styleSrc);}
377
+ if (fontSrc) {directives.push(fontSrc);}
378
+ if (imgSrc) {directives.push(imgSrc);}
379
+ if (mediaSrc) {directives.push(mediaSrc);}
380
+ if (connectSrc) {directives.push(connectSrc);}
381
+ if (frameSrc) {directives.push(frameSrc);}
382
+ if (workerSrc) {directives.push(workerSrc);}
383
+ if (baseUri) {directives.push(baseUri);}
384
384
 
385
385
  return directives.join("; ");
386
386
  }
387
387
 
388
388
  function toDirective(name: string, domains: string[] | undefined): string | null {
389
- if (!domains || domains.length === 0) return null;
389
+ if (!domains || domains.length === 0) {return null;}
390
390
  return `${name} ${domains.join(" ")}`;
391
391
  }
392
392
 
393
393
  export function applyCspMeta(html: string, cspContent: string | undefined): string {
394
- if (!cspContent) return html;
395
- if (/http-equiv=["']Content-Security-Policy["']/i.test(html)) return html;
394
+ if (!cspContent) {return html;}
395
+ if (/http-equiv=["']Content-Security-Policy["']/i.test(html)) {return html;}
396
396
  const metaTag = `<meta http-equiv="Content-Security-Policy" content="${escapeHtmlAttribute(cspContent)}">`;
397
397
  if (/<head[^>]*>/i.test(html)) {
398
398
  return html.replace(/<head[^>]*>/i, (match) => `${match}\n${metaTag}`);
@@ -22,11 +22,11 @@ export default function mcpAdapter(pi: ExtensionAPI) {
22
22
  const directSpecs = envRaw === "__none__"
23
23
  ? []
24
24
  : resolveDirectTools(
25
- earlyConfig,
26
- earlyCache,
27
- prefix,
28
- envRaw?.split(",").map(s => s.trim()).filter(Boolean),
29
- );
25
+ earlyConfig,
26
+ earlyCache,
27
+ prefix,
28
+ envRaw?.split(",").map(s => s.trim()).filter(Boolean),
29
+ );
30
30
 
31
31
  for (const spec of directSpecs) {
32
32
  pi.registerTool({
@@ -45,7 +45,7 @@ export default function mcpAdapter(pi: ExtensionAPI) {
45
45
  type: "string",
46
46
  });
47
47
 
48
- pi.on("session_start", async (_event, ctx) => {
48
+ pi.on("session_start", async(_event, ctx) => {
49
49
  initPromise = initializeMcp(pi, ctx);
50
50
 
51
51
  initPromise.then(s => {
@@ -58,7 +58,7 @@ export default function mcpAdapter(pi: ExtensionAPI) {
58
58
  });
59
59
  });
60
60
 
61
- pi.on("session_shutdown", async () => {
61
+ pi.on("session_shutdown", async() => {
62
62
  if (initPromise) {
63
63
  try {
64
64
  state = await initPromise;
@@ -80,17 +80,17 @@ export default function mcpAdapter(pi: ExtensionAPI) {
80
80
 
81
81
  pi.registerCommand("mcp", {
82
82
  description: "Show MCP server status",
83
- handler: async (args, ctx) => {
83
+ handler: async(args, ctx) => {
84
84
  if (!state && initPromise) {
85
85
  try {
86
86
  state = await initPromise;
87
87
  } catch {
88
- if (ctx.hasUI) ctx.ui.notify("MCP initialization failed", "error");
88
+ if (ctx.hasUI) {ctx.ui.notify("MCP initialization failed", "error");}
89
89
  return;
90
90
  }
91
91
  }
92
92
  if (!state) {
93
- if (ctx.hasUI) ctx.ui.notify("MCP not initialized", "error");
93
+ if (ctx.hasUI) {ctx.ui.notify("MCP not initialized", "error");}
94
94
  return;
95
95
  }
96
96
 
@@ -120,10 +120,10 @@ export default function mcpAdapter(pi: ExtensionAPI) {
120
120
 
121
121
  pi.registerCommand("mcp-auth", {
122
122
  description: "Authenticate with an MCP server (OAuth)",
123
- handler: async (args, ctx) => {
123
+ handler: async(args, ctx) => {
124
124
  const serverName = args?.trim();
125
125
  if (!serverName) {
126
- if (ctx.hasUI) ctx.ui.notify("Usage: /mcp-auth <server-name>", "error");
126
+ if (ctx.hasUI) {ctx.ui.notify("Usage: /mcp-auth <server-name>", "error");}
127
127
  return;
128
128
  }
129
129
 
@@ -131,12 +131,12 @@ export default function mcpAdapter(pi: ExtensionAPI) {
131
131
  try {
132
132
  state = await initPromise;
133
133
  } catch {
134
- if (ctx.hasUI) ctx.ui.notify("MCP initialization failed", "error");
134
+ if (ctx.hasUI) {ctx.ui.notify("MCP initialization failed", "error");}
135
135
  return;
136
136
  }
137
137
  }
138
138
  if (!state) {
139
- if (ctx.hasUI) ctx.ui.notify("MCP not initialized", "error");
139
+ if (ctx.hasUI) {ctx.ui.notify("MCP not initialized", "error");}
140
140
  return;
141
141
  }
142
142
 
@@ -97,15 +97,15 @@ export async function initializeMcp(
97
97
  const startupServers = bootstrapAll
98
98
  ? serverEntries
99
99
  : serverEntries.filter(([, definition]) => {
100
- const mode = definition.lifecycle ?? "lazy";
101
- return mode === "keep-alive" || mode === "eager";
102
- });
100
+ const mode = definition.lifecycle ?? "lazy";
101
+ return mode === "keep-alive" || mode === "eager";
102
+ });
103
103
 
104
104
  if (ctx.hasUI && startupServers.length > 0) {
105
105
  ctx.ui.setStatus("mcp", `MCP: connecting to ${startupServers.length} servers...`);
106
106
  }
107
107
 
108
- const results = await parallelLimit(startupServers, 10, async ([name, definition]) => {
108
+ const results = await parallelLimit(startupServers, 10, async([name, definition]) => {
109
109
  try {
110
110
  const connection = await manager.connect(name, definition);
111
111
  return { name, definition, connection, error: null };
@@ -154,7 +154,7 @@ export async function initializeMcp(
154
154
  const hasDirect = definition.directTools !== undefined
155
155
  ? !!definition.directTools
156
156
  : !!config.settings?.directTools;
157
- if (!hasDirect) continue;
157
+ if (!hasDirect) {continue;}
158
158
  const entry = currentCache?.servers?.[name];
159
159
  if (!entry || !isServerCacheValid(entry, definition)) {
160
160
  missingCacheServers.push(name);
@@ -165,7 +165,7 @@ export async function initializeMcp(
165
165
  const bootstrapResults = await parallelLimit(
166
166
  missingCacheServers.filter(name => !results.some(r => r.name === name && r.connection)),
167
167
  10,
168
- async (name) => {
168
+ async(name) => {
169
169
  const definition = config.mcpServers[name];
170
170
  try {
171
171
  const connection = await manager.connect(name, definition);
@@ -205,10 +205,10 @@ export async function initializeMcp(
205
205
 
206
206
  export function updateServerMetadata(state: McpExtensionState, serverName: string): void {
207
207
  const connection = state.manager.getConnection(serverName);
208
- if (!connection || connection.status !== "connected") return;
208
+ if (!connection || connection.status !== "connected") {return;}
209
209
 
210
210
  const definition = state.config.mcpServers[serverName];
211
- if (!definition) return;
211
+ if (!definition) {return;}
212
212
 
213
213
  const prefix = state.config.settings?.toolPrefix ?? "server";
214
214
 
@@ -218,10 +218,10 @@ export function updateServerMetadata(state: McpExtensionState, serverName: strin
218
218
 
219
219
  export function updateMetadataCache(state: McpExtensionState, serverName: string): void {
220
220
  const connection = state.manager.getConnection(serverName);
221
- if (!connection || connection.status !== "connected") return;
221
+ if (!connection || connection.status !== "connected") {return;}
222
222
 
223
223
  const definition = state.config.mcpServers[serverName];
224
- if (!definition) return;
224
+ if (!definition) {return;}
225
225
 
226
226
  const configHash = computeServerHash(definition);
227
227
  const existing = loadMetadataCache();
@@ -259,7 +259,7 @@ export function flushMetadataCache(state: McpExtensionState): void {
259
259
 
260
260
  export function updateStatusBar(state: McpExtensionState): void {
261
261
  const ui = state.ui;
262
- if (!ui) return;
262
+ if (!ui) {return;}
263
263
  const total = Object.keys(state.config.mcpServers).length;
264
264
  if (total === 0) {
265
265
  ui.setStatus("mcp", "");
@@ -271,9 +271,9 @@ export function updateStatusBar(state: McpExtensionState): void {
271
271
 
272
272
  export function getFailureAgeSeconds(state: McpExtensionState, serverName: string): number | null {
273
273
  const failedAt = state.failureTracker.get(serverName);
274
- if (!failedAt) return null;
274
+ if (!failedAt) {return null;}
275
275
  const ageMs = Date.now() - failedAt;
276
- if (ageMs > FAILURE_BACKOFF_MS) return null;
276
+ if (ageMs > FAILURE_BACKOFF_MS) {return null;}
277
277
  return Math.round(ageMs / 1000);
278
278
  }
279
279
 
@@ -285,10 +285,10 @@ export async function lazyConnect(state: McpExtensionState, serverName: string):
285
285
  }
286
286
 
287
287
  const failedAgo = getFailureAgeSeconds(state, serverName);
288
- if (failedAgo !== null) return false;
288
+ if (failedAgo !== null) {return false;}
289
289
 
290
290
  const definition = state.config.mcpServers[serverName];
291
- if (!definition) return false;
291
+ if (!definition) {return false;}
292
292
 
293
293
  try {
294
294
  if (state.ui) {
@@ -312,8 +312,8 @@ function getEffectiveIdleTimeoutMinutes(state: McpExtensionState, serverName: st
312
312
  if (!definition) {
313
313
  return typeof state.config.settings?.idleTimeout === "number" ? state.config.settings.idleTimeout : 10;
314
314
  }
315
- if (typeof definition.idleTimeout === "number") return definition.idleTimeout;
315
+ if (typeof definition.idleTimeout === "number") {return definition.idleTimeout;}
316
316
  const mode = definition.lifecycle ?? "lazy";
317
- if (mode === "eager") return 0;
317
+ if (mode === "eager") {return 0;}
318
318
  return typeof state.config.settings?.idleTimeout === "number" ? state.config.settings.idleTimeout : 10;
319
319
  }
@@ -69,7 +69,7 @@ export class McpLifecycleManager {
69
69
  }
70
70
 
71
71
  for (const [name] of this.allServers) {
72
- if (this.keepAliveServers.has(name)) continue;
72
+ if (this.keepAliveServers.has(name)) {continue;}
73
73
  const timeout = this.getIdleTimeout(name);
74
74
  if (timeout > 0 && this.manager.isIdle(name, timeout)) {
75
75
  await this.manager.close(name);
@@ -80,7 +80,7 @@ export class McpLifecycleManager {
80
80
 
81
81
  private getIdleTimeout(name: string): number {
82
82
  const perServer = this.serverSettings.get(name)?.idleTimeout;
83
- if (perServer !== undefined) return perServer * 60 * 1000;
83
+ if (perServer !== undefined) {return perServer * 60 * 1000;}
84
84
  return this.globalIdleTimeout;
85
85
  }
86
86
 
@@ -63,7 +63,7 @@ class Logger {
63
63
  }
64
64
 
65
65
  private emit(level: LogLevel, message: string, context?: LogContext, error?: Error): void {
66
- if (!this.shouldLog(level)) return;
66
+ if (!this.shouldLog(level)) {return;}
67
67
 
68
68
  const entry: LogEntry = {
69
69
  level,
@@ -150,7 +150,7 @@ class ChildLogger {
150
150
  }
151
151
 
152
152
  function formatContext(context?: LogContext): string {
153
- if (!context || Object.keys(context).length === 0) return "";
153
+ if (!context || Object.keys(context).length === 0) {return "";}
154
154
  const parts: string[] = [];
155
155
  for (const [key, value] of Object.entries(context)) {
156
156
  if (value !== undefined && value !== null) {
@@ -30,7 +30,7 @@ const DEFAULT_THEME: PanelTheme = {
30
30
  };
31
31
 
32
32
  function fg(code: string, text: string): string {
33
- if (!code) return text;
33
+ if (!code) {return text;}
34
34
  return `\x1b[${code}m${text}\x1b[0m`;
35
35
  }
36
36
 
@@ -56,7 +56,7 @@ function rainbowProgress(filled: number, total: number): string {
56
56
  function fuzzyScore(query: string, text: string): number {
57
57
  const lq = query.toLowerCase();
58
58
  const lt = text.toLowerCase();
59
- if (lt.includes(lq)) return 100 + (lq.length / lt.length) * 50;
59
+ if (lt.includes(lq)) {return 100 + (lq.length / lt.length) * 50;}
60
60
  let score = 0;
61
61
  let qi = 0;
62
62
  let consecutive = 0;
@@ -191,7 +191,7 @@ class McpPanel {
191
191
  }
192
192
 
193
193
  private resetInactivityTimeout(): void {
194
- if (this.inactivityTimeout) clearTimeout(this.inactivityTimeout);
194
+ if (this.inactivityTimeout) {clearTimeout(this.inactivityTimeout);}
195
195
  this.inactivityTimeout = setTimeout(() => {
196
196
  this.cleanup();
197
197
  this.done({ cancelled: true, changes: new Map() });
@@ -219,11 +219,11 @@ class McpPanel {
219
219
  if (query) {
220
220
  const score = mode === "name"
221
221
  ? Math.max(
222
- fuzzyScore(query, tool.name),
223
- fuzzyScore(query, server.name) * 0.6,
224
- )
222
+ fuzzyScore(query, tool.name),
223
+ fuzzyScore(query, server.name) * 0.6,
224
+ )
225
225
  : fuzzyScore(query, tool.description);
226
- if (score === 0) continue;
226
+ if (score === 0) {continue;}
227
227
  }
228
228
  this.visibleItems.push({ type: "tool", serverIndex: si, toolIndex: ti });
229
229
  }
@@ -250,7 +250,7 @@ class McpPanel {
250
250
  const changes = new Map<string, true | string[] | false>();
251
251
  for (const server of this.servers) {
252
252
  const changed = server.tools.some((t) => t.isDirect !== t.wasDirect);
253
- if (!changed) continue;
253
+ if (!changed) {continue;}
254
254
  const directTools = server.tools.filter((t) => t.isDirect);
255
255
  if (directTools.length === server.tools.length && server.tools.length > 0) {
256
256
  changes.set(server.name, true);
@@ -308,7 +308,7 @@ class McpPanel {
308
308
  if (matchesKey(data, "space")) {
309
309
  // Toggle even while in desc search
310
310
  const item = this.visibleItems[this.cursorIndex];
311
- if (item) this.toggleItem(item);
311
+ if (item) {this.toggleItem(item);}
312
312
  return;
313
313
  }
314
314
  if (data.length === 1 && data.charCodeAt(0) >= 32) {
@@ -342,13 +342,13 @@ class McpPanel {
342
342
 
343
343
  if (matchesKey(data, "space")) {
344
344
  const item = this.visibleItems[this.cursorIndex];
345
- if (item) this.toggleItem(item);
345
+ if (item) {this.toggleItem(item);}
346
346
  return;
347
347
  }
348
348
 
349
349
  if (matchesKey(data, "return")) {
350
350
  const item = this.visibleItems[this.cursorIndex];
351
- if (!item) return;
351
+ if (!item) {return;}
352
352
  const server = this.servers[item.serverIndex];
353
353
  if (item.type === "server") {
354
354
  if (server.connectionStatus === "needs-auth") {
@@ -371,9 +371,9 @@ class McpPanel {
371
371
 
372
372
  if (matchesKey(data, "ctrl+r")) {
373
373
  const item = this.visibleItems[this.cursorIndex];
374
- if (!item) return;
374
+ if (!item) {return;}
375
375
  const server = this.servers[item.serverIndex];
376
- if (server.connectionStatus === "connecting") return;
376
+ if (server.connectionStatus === "connecting") {return;}
377
377
  server.connectionStatus = "connecting";
378
378
  this.callbacks.reconnect(server.name).then(() => {
379
379
  server.connectionStatus = this.callbacks.getConnectionStatus(server.name);
@@ -426,7 +426,7 @@ class McpPanel {
426
426
  if (server.source === "import" && newState) {
427
427
  this.importNotice = `Imported from ${server.importKind ?? "external"} — will copy to user config on save`;
428
428
  }
429
- for (const t of server.tools) t.isDirect = newState;
429
+ for (const t of server.tools) {t.isDirect = newState;}
430
430
  } else if (item.toolIndex !== undefined) {
431
431
  const tool = server.tools[item.toolIndex];
432
432
  tool.isDirect = !tool.isDirect;
@@ -467,13 +467,13 @@ class McpPanel {
467
467
  }
468
468
 
469
469
  private moveCursor(delta: number): void {
470
- if (this.visibleItems.length === 0) return;
470
+ if (this.visibleItems.length === 0) {return;}
471
471
  this.cursorIndex = Math.max(0, Math.min(this.visibleItems.length - 1, this.cursorIndex + delta));
472
472
  }
473
473
 
474
474
  private rebuildServerTools(server: ServerState, entry: ServerCacheEntry): void {
475
475
  const existingState = new Map<string, boolean>();
476
- for (const t of server.tools) existingState.set(t.name, t.isDirect);
476
+ for (const t of server.tools) {existingState.set(t.name, t.isDirect);}
477
477
 
478
478
  const newTools: ToolState[] = [];
479
479
  for (const tool of entry.tools ?? []) {
@@ -633,7 +633,7 @@ class McpPanel {
633
633
  curW += needed;
634
634
  }
635
635
  }
636
- if (curLine) lines.push(row(fg(t.hint, curLine)));
636
+ if (curLine) {lines.push(row(fg(t.hint, curLine)));}
637
637
 
638
638
  lines.push(fg(t.border, "╰" + "─".repeat(innerW) + "╯"));
639
639