oh-my-opencode 5.0.0-beta.21 → 5.0.0-beta.23

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 (151) hide show
  1. package/dist/cli/index.js +290 -288
  2. package/dist/cli-node/index.js +290 -288
  3. package/dist/index.js +1675 -1657
  4. package/dist/skills/debugging/SKILL.md +2 -0
  5. package/dist/skills/debugging/references/methodology/00-setup.md +12 -0
  6. package/dist/skills/debugging/references/methodology/02-investigate.md +21 -0
  7. package/dist/skills/debugging/references/runtimes/go.md +15 -1
  8. package/dist/skills/debugging/references/runtimes/native-binary.md +38 -0
  9. package/dist/skills/debugging/references/runtimes/node.md +25 -0
  10. package/dist/skills/debugging/references/runtimes/python.md +13 -0
  11. package/dist/skills/debugging/references/runtimes/rust.md +41 -0
  12. package/dist/skills/debugging/references/scripts/dap.mjs +267 -0
  13. package/dist/skills/debugging/references/scripts/dap.test.ts +86 -0
  14. package/dist/skills/debugging/references/scripts/fixture-adapter.mjs +59 -0
  15. package/dist/skills/debugging/references/tools/dap.md +103 -0
  16. package/dist/skills/debugging/references/tools/frida.md +193 -0
  17. package/dist/tools/delegate-task/types.d.ts +3 -0
  18. package/dist/tui.js +131 -147
  19. package/package.json +18 -15
  20. package/packages/lsp-core/src/index.ts +1 -0
  21. package/packages/lsp-core/src/lsp/client-wrapper.ts +25 -5
  22. package/packages/lsp-core/src/lsp/client.ts +26 -0
  23. package/packages/lsp-core/src/lsp/connection.ts +16 -0
  24. package/packages/lsp-core/src/lsp/constants.ts +1 -0
  25. package/packages/lsp-core/src/lsp/format-document.test.ts +143 -0
  26. package/packages/lsp-core/src/lsp/format-document.ts +90 -0
  27. package/packages/lsp-core/src/lsp/manager-max-clients.test.ts +151 -0
  28. package/packages/lsp-core/src/lsp/manager.ts +34 -1
  29. package/packages/lsp-core/src/lsp/server-definitions.ts +29 -0
  30. package/packages/lsp-core/src/lsp/server-installation.test.ts +376 -0
  31. package/packages/lsp-core/src/lsp/server-installation.ts +200 -27
  32. package/packages/lsp-core/src/lsp/server-resolution-local-binary.test.ts +170 -0
  33. package/packages/lsp-core/src/lsp/server-resolution.ts +18 -5
  34. package/packages/lsp-core/src/lsp/transport.ts +9 -0
  35. package/packages/lsp-core/src/lsp/types.ts +8 -0
  36. package/packages/lsp-core/src/tool-surface.test.ts +13 -1
  37. package/packages/lsp-core/src/tools/definitions.ts +14 -0
  38. package/packages/lsp-core/src/tools/format.test.ts +56 -0
  39. package/packages/lsp-core/src/tools/format.ts +56 -0
  40. package/packages/lsp-core/src/tools/index.ts +1 -0
  41. package/packages/lsp-core/src/tools/types.ts +10 -0
  42. package/packages/lsp-daemon/dist/cli.js +432 -144
  43. package/packages/lsp-daemon/dist/client.d.ts +10 -0
  44. package/packages/lsp-daemon/dist/client.js +401 -107
  45. package/packages/lsp-daemon/dist/daemon-client.d.ts +1 -0
  46. package/packages/lsp-daemon/dist/daemon-client.js +3 -0
  47. package/packages/lsp-daemon/dist/index.js +395 -107
  48. package/packages/lsp-daemon/package.json +1 -1
  49. package/packages/lsp-tools-mcp/dist/cli.js +384 -99
  50. package/packages/lsp-tools-mcp/dist/lsp/manager.js +53 -0
  51. package/packages/lsp-tools-mcp/dist/mcp.js +384 -99
  52. package/packages/lsp-tools-mcp/dist/tools.js +385 -99
  53. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  54. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +2 -2
  55. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  56. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  57. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +69 -35
  58. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +21 -2
  59. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  60. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  61. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  62. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  63. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  64. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  65. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  66. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  67. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +404 -121
  68. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  69. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  70. package/packages/omo-codex/plugin/components/rules/dist/cli.js +3 -3
  71. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  72. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  73. package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +3 -3
  74. package/packages/omo-codex/plugin/components/rules/test/post-compact-budget.test.ts +4 -4
  75. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  76. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  77. package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +30 -15
  78. package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +30 -15
  79. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  80. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  81. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  82. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  83. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +2 -2
  84. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  85. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +5 -1
  86. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.d.ts +2 -1
  87. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.js +11 -1
  88. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +22 -10
  89. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  90. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  91. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +5 -1
  92. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-output.ts +13 -1
  93. package/packages/omo-codex/plugin/components/ulw-loop/src/cli.ts +3 -5
  94. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-entrypoint.test.ts +26 -2
  95. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  96. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  97. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  98. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  99. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  100. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  101. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  102. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  103. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  104. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  105. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  106. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  107. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  108. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  109. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  110. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  111. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  112. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  113. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-ulw-execute-continuation.json +1 -1
  114. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  115. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  116. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  117. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  118. package/packages/omo-codex/plugin/model-catalog.json +3 -3
  119. package/packages/omo-codex/plugin/package-lock.json +13 -13
  120. package/packages/omo-codex/plugin/package.json +1 -1
  121. package/packages/omo-codex/plugin/scripts/migrate-codex-config/catalog.mjs +3 -3
  122. package/packages/omo-codex/plugin/skills/debugging/SKILL.md +2 -0
  123. package/packages/omo-codex/plugin/skills/debugging/references/methodology/00-setup.md +12 -0
  124. package/packages/omo-codex/plugin/skills/debugging/references/methodology/02-investigate.md +21 -0
  125. package/packages/omo-codex/plugin/skills/debugging/references/runtimes/go.md +15 -1
  126. package/packages/omo-codex/plugin/skills/debugging/references/runtimes/native-binary.md +38 -0
  127. package/packages/omo-codex/plugin/skills/debugging/references/runtimes/node.md +25 -0
  128. package/packages/omo-codex/plugin/skills/debugging/references/runtimes/python.md +13 -0
  129. package/packages/omo-codex/plugin/skills/debugging/references/runtimes/rust.md +41 -0
  130. package/packages/omo-codex/plugin/skills/debugging/references/scripts/dap.mjs +267 -0
  131. package/packages/omo-codex/plugin/skills/debugging/references/scripts/fixture-adapter.mjs +59 -0
  132. package/packages/omo-codex/plugin/skills/debugging/references/tools/dap.md +103 -0
  133. package/packages/omo-codex/plugin/skills/debugging/references/tools/frida.md +193 -0
  134. package/packages/omo-codex/plugin/test/aggregate-model-catalog.test.mjs +1 -1
  135. package/packages/omo-codex/plugin/test/auto-update.test.mjs +2 -2
  136. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +8 -8
  137. package/packages/omo-codex/scripts/install-config-reasoning.test.mjs +2 -2
  138. package/packages/omo-codex/scripts/install-dist/install-local.mjs +38 -23
  139. package/packages/shared-skills/skills/debugging/SKILL.md +2 -0
  140. package/packages/shared-skills/skills/debugging/references/methodology/00-setup.md +12 -0
  141. package/packages/shared-skills/skills/debugging/references/methodology/02-investigate.md +21 -0
  142. package/packages/shared-skills/skills/debugging/references/runtimes/go.md +15 -1
  143. package/packages/shared-skills/skills/debugging/references/runtimes/native-binary.md +38 -0
  144. package/packages/shared-skills/skills/debugging/references/runtimes/node.md +25 -0
  145. package/packages/shared-skills/skills/debugging/references/runtimes/python.md +13 -0
  146. package/packages/shared-skills/skills/debugging/references/runtimes/rust.md +41 -0
  147. package/packages/shared-skills/skills/debugging/references/scripts/dap.mjs +267 -0
  148. package/packages/shared-skills/skills/debugging/references/scripts/dap.test.ts +86 -0
  149. package/packages/shared-skills/skills/debugging/references/scripts/fixture-adapter.mjs +59 -0
  150. package/packages/shared-skills/skills/debugging/references/tools/dap.md +103 -0
  151. package/packages/shared-skills/skills/debugging/references/tools/frida.md +193 -0
@@ -5,7 +5,7 @@ import { argv, stderr } from "node:process";
5
5
 
6
6
  // src/proxy.ts
7
7
  import { existsSync as existsSync13, realpathSync as realpathSync6 } from "node:fs";
8
- import { basename as basename4, delimiter as delimiter4, dirname as dirname10, isAbsolute as isAbsolute5 } from "node:path";
8
+ import { basename as basename4, delimiter as delimiter4, dirname as dirname11, isAbsolute as isAbsolute6 } from "node:path";
9
9
 
10
10
  // ../mcp-stdio-core/src/record.ts
11
11
  function isPlainRecord(value) {
@@ -320,7 +320,7 @@ function createIdleTimer(idleTimeoutMs, log, onTimeout) {
320
320
  }
321
321
  // ../lsp-core/src/lsp/client-wrapper.ts
322
322
  import { existsSync as existsSync10, realpathSync as realpathSync5, statSync as statSync3 } from "node:fs";
323
- import { basename as basename3, dirname as dirname7, join as join5, resolve as resolve7 } from "node:path";
323
+ import { basename as basename3, dirname as dirname8, join as join5, resolve as resolve8 } from "node:path";
324
324
 
325
325
  // ../lsp-core/src/request-context.ts
326
326
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -592,6 +592,7 @@ var DEFAULT_MAX_DIRECTORY_FILES = 50;
592
592
  var REQUEST_TIMEOUT_MS = 15000;
593
593
  var INIT_TIMEOUT_MS = 60000;
594
594
  var IDLE_TIMEOUT_MS = 5 * 60000;
595
+ var MAX_RESIDENT_CLIENTS = 6;
595
596
  var REAPER_INTERVAL_MS = 60000;
596
597
  var STOP_HARD_KILL_TIMEOUT_MS = 5000;
597
598
  var STOP_SIGKILL_GRACE_MS = 1000;
@@ -1117,6 +1118,7 @@ class LspClientTransport {
1117
1118
  this.diagnosticsStore = new Map;
1118
1119
  this.workspaceApplyEditHandler = null;
1119
1120
  this.diagnosticPullSupported = false;
1121
+ this.documentFormattingSupported = false;
1120
1122
  this.requestTimeoutMs = timeouts.requestTimeoutMs ?? REQUEST_TIMEOUT_MS;
1121
1123
  this.initializeTimeoutMs = timeouts.initializeTimeoutMs ?? INIT_TIMEOUT_MS;
1122
1124
  }
@@ -1138,6 +1140,12 @@ class LspClientTransport {
1138
1140
  isDiagnosticPullSupported() {
1139
1141
  return this.diagnosticPullSupported;
1140
1142
  }
1143
+ setDocumentFormattingSupported(supported) {
1144
+ this.documentFormattingSupported = supported;
1145
+ }
1146
+ isDocumentFormattingSupported() {
1147
+ return this.documentFormattingSupported;
1148
+ }
1141
1149
  handlePublishDiagnostics(params) {
1142
1150
  this.diagnosticsStore.set(params.uri, [...params.diagnostics]);
1143
1151
  }
@@ -1351,6 +1359,14 @@ function supportsDiagnosticPull(capabilities) {
1351
1359
  return false;
1352
1360
  return Object.hasOwn(capabilities, "diagnosticProvider");
1353
1361
  }
1362
+ function supportsDocumentFormatting(capabilities) {
1363
+ if (capabilities === undefined)
1364
+ return false;
1365
+ const provider = capabilities.documentFormattingProvider;
1366
+ if (provider === undefined || provider === null || provider === false)
1367
+ return false;
1368
+ return provider === true || typeof provider === "object";
1369
+ }
1354
1370
 
1355
1371
  class LspClientConnection extends LspClientTransport {
1356
1372
  async initialize() {
@@ -1367,6 +1383,7 @@ class LspClientConnection extends LspClientTransport {
1367
1383
  references: {},
1368
1384
  documentSymbol: { hierarchicalDocumentSymbolSupport: true },
1369
1385
  publishDiagnostics: {},
1386
+ formatting: { dynamicRegistration: false },
1370
1387
  rename: {
1371
1388
  prepareSupport: true,
1372
1389
  prepareSupportDefaultBehavior: 1
@@ -1408,6 +1425,7 @@ class LspClientConnection extends LspClientTransport {
1408
1425
  initializationOptions: this.server.initialization
1409
1426
  }, { timeoutMs: this.initializeTimeoutMs });
1410
1427
  this.setDiagnosticPullSupported(supportsDiagnosticPull(result?.capabilities));
1428
+ this.setDocumentFormattingSupported(supportsDocumentFormatting(result?.capabilities));
1411
1429
  await this.sendNotification("initialized", {});
1412
1430
  await this.sendNotification("workspace/didChangeConfiguration", {
1413
1431
  settings: { json: { validate: { enable: true } } }
@@ -2992,6 +3010,18 @@ class LspClient extends LspClientConnection {
2992
3010
  textDocument: { uri: pathToFileURL3(absPath).href }
2993
3011
  }, options);
2994
3012
  }
3013
+ async formatDocument(filePath, options, signal) {
3014
+ if (!this.isDocumentFormattingSupported())
3015
+ return null;
3016
+ const absPath = this.resolveWorkspacePath(filePath);
3017
+ await this.openFile(absPath);
3018
+ const requestOptions = signal === undefined ? {} : { signal };
3019
+ const edits = await this.sendRequest("textDocument/formatting", {
3020
+ textDocument: { uri: pathToFileURL3(absPath).href },
3021
+ options
3022
+ }, requestOptions);
3023
+ return edits ?? [];
3024
+ }
2995
3025
  async workspaceSymbols(query, signal) {
2996
3026
  const options = signal === undefined ? {} : { signal };
2997
3027
  return this.sendRequest("workspace/symbol", { query }, options);
@@ -3244,6 +3274,7 @@ class LspManager {
3244
3274
  this.disposed = false;
3245
3275
  this.idleTimeoutMs = options.idleTimeoutMs ?? IDLE_TIMEOUT_MS;
3246
3276
  this.initTimeoutMs = options.initTimeoutMs ?? INIT_TIMEOUT_MS;
3277
+ this.maxResidentClients = options.maxResidentClients ?? MAX_RESIDENT_CLIENTS;
3247
3278
  this.reaperIntervalMs = options.reaperIntervalMs ?? REAPER_INTERVAL_MS;
3248
3279
  this.clientFactory = options.clientFactory ?? ((root, server2) => new LspClient(root, server2));
3249
3280
  this.now = options.now ?? (() => Date.now());
@@ -3277,6 +3308,26 @@ class LspManager {
3277
3308
  }
3278
3309
  }
3279
3310
  }
3311
+ evictForAdmission() {
3312
+ while (this.clients.size >= this.maxResidentClients) {
3313
+ const victim = this.leastRecentlyUsedIdleClient();
3314
+ if (!victim)
3315
+ return;
3316
+ this.clients.delete(victim.key);
3317
+ stopClientBestEffort(victim.managed.client);
3318
+ }
3319
+ }
3320
+ leastRecentlyUsedIdleClient() {
3321
+ let candidate = null;
3322
+ for (const [key, managed] of this.clients) {
3323
+ if (managed.refCount > 0 || managed.pendingWaiters > 0 || managed.isInitializing)
3324
+ continue;
3325
+ if (candidate === null || managed.lastUsedAt < candidate.managed.lastUsedAt) {
3326
+ candidate = { key, managed };
3327
+ }
3328
+ }
3329
+ return candidate;
3330
+ }
3280
3331
  async tryDeleteIfOrphaned(key, managed) {
3281
3332
  if (managed.refCount === 0 && managed.pendingWaiters === 0 && !managed.isInitializing && this.clients.get(key) === managed) {
3282
3333
  this.clients.delete(key);
@@ -3323,6 +3374,7 @@ class LspManager {
3323
3374
  managed.lastUsedAt = this.now();
3324
3375
  return managed.client;
3325
3376
  }
3377
+ this.evictForAdmission();
3326
3378
  const client = this.clientFactory(root, server2);
3327
3379
  const initStartedAt = this.now();
3328
3380
  const initPromise = (async () => {
@@ -3385,6 +3437,7 @@ class LspManager {
3385
3437
  const key = this.getKey(root, server2.id);
3386
3438
  if (this.clients.has(key))
3387
3439
  return;
3440
+ this.evictForAdmission();
3388
3441
  const client = this.clientFactory(root, server2);
3389
3442
  const initStartedAt = this.now();
3390
3443
  const initPromise = (async () => {
@@ -3510,57 +3563,6 @@ function nearestExistingAncestor(directory) {
3510
3563
  return current;
3511
3564
  }
3512
3565
 
3513
- // ../lsp-core/src/lsp/server-install-state.ts
3514
- import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
3515
- import { dirname as dirname6 } from "node:path";
3516
- function getInstallDecisionsPath() {
3517
- return lspRequestContext().installDecisionsPath;
3518
- }
3519
- function loadInstallDecisions() {
3520
- const path = getInstallDecisionsPath();
3521
- if (!existsSync7(path))
3522
- return {};
3523
- try {
3524
- const parsed = JSON.parse(readFileSync3(path, "utf8"));
3525
- return isInstallDecisions(parsed) ? parsed : {};
3526
- } catch {
3527
- return {};
3528
- }
3529
- }
3530
- function loadInstallDecision(serverId) {
3531
- return loadInstallDecisions()[serverId];
3532
- }
3533
- function recordInstallDecision(serverId, decision, decidedAt = new Date().toISOString()) {
3534
- const decisions = loadInstallDecisions();
3535
- decisions[serverId] = { decision, decidedAt };
3536
- writeInstallDecisions(decisions);
3537
- }
3538
- function isInstallDecision(value) {
3539
- return value === "declined" || value === "allowed";
3540
- }
3541
- function writeInstallDecisions(decisions) {
3542
- const path = getInstallDecisionsPath();
3543
- mkdirSync(dirname6(path), { recursive: true });
3544
- const tmpPath = `${path}.tmp`;
3545
- writeFileSync2(tmpPath, `${JSON.stringify(decisions, null, 2)}
3546
- `, "utf8");
3547
- renameSync2(tmpPath, path);
3548
- }
3549
- function isInstallDecisions(value) {
3550
- return isRecord5(value) && Object.values(value).every(isInstallDecisionRecord);
3551
- }
3552
- function isInstallDecisionRecord(value) {
3553
- if (!isRecord5(value))
3554
- return false;
3555
- return isInstallDecision(value["decision"]) && typeof value["decidedAt"] === "string";
3556
- }
3557
- function isRecord5(value) {
3558
- return typeof value === "object" && value !== null && !Array.isArray(value);
3559
- }
3560
-
3561
- // ../lsp-core/src/lsp/config-loader.ts
3562
- import { existsSync as existsSync8, readFileSync as readFileSync4 } from "node:fs";
3563
-
3564
3566
  // ../lsp-core/src/lsp/server-definitions.ts
3565
3567
  var LSP_INSTALL_HINTS = {
3566
3568
  typescript: "npm install -g typescript-language-server typescript",
@@ -3606,6 +3608,26 @@ var LSP_INSTALL_HINTS = {
3606
3608
  julials: `julia -e 'using Pkg; Pkg.add("LanguageServer")'`,
3607
3609
  razor: "Razor runs through the Roslyn language server (cohosting). " + "Install: dotnet tool install -g roslyn-language-server --prerelease (requires v5.8.0+). See https://github.com/dotnet/razor"
3608
3610
  };
3611
+ var LSP_LOCAL_INSTALL_HINTS = {
3612
+ typescript: "bun add -d typescript-language-server typescript",
3613
+ vue: "bun add -d @vue/language-server",
3614
+ eslint: "bun add -d vscode-langservers-extracted",
3615
+ oxlint: "bun add -d oxlint",
3616
+ biome: "bun add -d @biomejs/biome",
3617
+ svelte: "bun add -d svelte-language-server",
3618
+ astro: "bun add -d @astrojs/language-server",
3619
+ bash: "bun add -d bash-language-server",
3620
+ "bash-ls": "bun add -d bash-language-server",
3621
+ "yaml-ls": "bun add -d yaml-language-server",
3622
+ php: "bun add -d intelephense",
3623
+ prisma: "bun add -d prisma",
3624
+ dockerfile: "bun add -d dockerfile-language-server-nodejs",
3625
+ pyright: "uv add --dev pyright",
3626
+ basedpyright: "uv add --dev basedpyright",
3627
+ ruff: "uv add --dev ruff",
3628
+ ty: "uv add --dev ty",
3629
+ "ruby-lsp": "bundle add ruby-lsp --group development"
3630
+ };
3609
3631
  var BUILTIN_SERVERS = {
3610
3632
  typescript: {
3611
3633
  command: ["typescript-language-server", "--stdio"],
@@ -3708,7 +3730,56 @@ var BUILTIN_SERVERS = {
3708
3730
  }
3709
3731
  };
3710
3732
 
3733
+ // ../lsp-core/src/lsp/server-install-state.ts
3734
+ import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
3735
+ import { dirname as dirname6 } from "node:path";
3736
+ function getInstallDecisionsPath() {
3737
+ return lspRequestContext().installDecisionsPath;
3738
+ }
3739
+ function loadInstallDecisions() {
3740
+ const path = getInstallDecisionsPath();
3741
+ if (!existsSync7(path))
3742
+ return {};
3743
+ try {
3744
+ const parsed = JSON.parse(readFileSync3(path, "utf8"));
3745
+ return isInstallDecisions(parsed) ? parsed : {};
3746
+ } catch {
3747
+ return {};
3748
+ }
3749
+ }
3750
+ function loadInstallDecision(serverId) {
3751
+ return loadInstallDecisions()[serverId];
3752
+ }
3753
+ function recordInstallDecision(serverId, decision, decidedAt = new Date().toISOString()) {
3754
+ const decisions = loadInstallDecisions();
3755
+ decisions[serverId] = { decision, decidedAt };
3756
+ writeInstallDecisions(decisions);
3757
+ }
3758
+ function isInstallDecision(value) {
3759
+ return value === "declined" || value === "allowed";
3760
+ }
3761
+ function writeInstallDecisions(decisions) {
3762
+ const path = getInstallDecisionsPath();
3763
+ mkdirSync(dirname6(path), { recursive: true });
3764
+ const tmpPath = `${path}.tmp`;
3765
+ writeFileSync2(tmpPath, `${JSON.stringify(decisions, null, 2)}
3766
+ `, "utf8");
3767
+ renameSync2(tmpPath, path);
3768
+ }
3769
+ function isInstallDecisions(value) {
3770
+ return isRecord5(value) && Object.values(value).every(isInstallDecisionRecord);
3771
+ }
3772
+ function isInstallDecisionRecord(value) {
3773
+ if (!isRecord5(value))
3774
+ return false;
3775
+ return isInstallDecision(value["decision"]) && typeof value["decidedAt"] === "string";
3776
+ }
3777
+ function isRecord5(value) {
3778
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3779
+ }
3780
+
3711
3781
  // ../lsp-core/src/lsp/config-loader.ts
3782
+ import { existsSync as existsSync8, readFileSync as readFileSync4 } from "node:fs";
3712
3783
  function getProjectConfigPaths() {
3713
3784
  return lspRequestContext().projectConfigPaths;
3714
3785
  }
@@ -3905,53 +3976,154 @@ function getDisabledServerIds() {
3905
3976
 
3906
3977
  // ../lsp-core/src/lsp/server-installation.ts
3907
3978
  import { existsSync as existsSync9 } from "node:fs";
3908
- import { delimiter as delimiter3, join as join4 } from "node:path";
3909
- function isServerInstalled(command, _workingDirectory) {
3910
- if (command.length === 0)
3911
- return false;
3912
- const [cmd] = command;
3913
- if (!cmd)
3914
- return false;
3915
- if (cmd.includes("/") || cmd.includes("\\")) {
3916
- if (existsSync9(cmd))
3917
- return true;
3979
+ import { delimiter as delimiter3, dirname as dirname7, isAbsolute as isAbsolute3, join as join4, resolve as resolve7 } from "node:path";
3980
+ var LOCAL_BIN_RULES = [
3981
+ {
3982
+ markers: ["package.json", "bun.lock", "bun.lockb", "package-lock.json", "yarn.lock", "pnpm-lock.yaml"],
3983
+ binDirs: [join4("node_modules", ".bin")]
3984
+ },
3985
+ {
3986
+ markers: [
3987
+ "pyproject.toml",
3988
+ "ty.toml",
3989
+ "requirements.txt",
3990
+ "setup.py",
3991
+ "setup.cfg",
3992
+ "Pipfile",
3993
+ "pyrightconfig.json",
3994
+ "ruff.toml",
3995
+ ".ruff.toml"
3996
+ ],
3997
+ binDirs: [
3998
+ join4(".venv", "bin"),
3999
+ join4(".venv", "Scripts"),
4000
+ join4("venv", "bin"),
4001
+ join4("venv", "Scripts"),
4002
+ join4(".env", "bin"),
4003
+ join4(".env", "Scripts")
4004
+ ]
4005
+ },
4006
+ {
4007
+ markers: ["Gemfile", "Gemfile.lock"],
4008
+ binDirs: [join4("vendor", "bundle", "bin"), "bin"]
4009
+ },
4010
+ {
4011
+ markers: ["go.mod", "go.sum", "go.work"],
4012
+ binDirs: ["bin"]
4013
+ }
4014
+ ];
4015
+ var resolutionCache = new Map;
4016
+ var probeCount = 0;
4017
+ function probe(path) {
4018
+ probeCount += 1;
4019
+ return existsSync9(path);
4020
+ }
4021
+ function executableSuffixes(platform, pathExt) {
4022
+ if (platform !== "win32")
4023
+ return [""];
4024
+ const configured = pathExt ?? process.env["PATHEXT"] ?? "";
4025
+ const systemExts = configured.split(";").filter((entry) => entry.length > 0).map((entry) => entry.toLowerCase());
4026
+ return [...new Set(["", ...systemExts, ".exe", ".cmd", ".bat", ".ps1"])];
4027
+ }
4028
+ function probeWithSuffixes(directory, command, suffixes) {
4029
+ for (const suffix of suffixes) {
4030
+ const candidate = join4(directory, command + suffix);
4031
+ if (probe(candidate))
4032
+ return candidate;
3918
4033
  }
3919
- const isWindows = process.platform === "win32";
3920
- let exts = [""];
3921
- if (isWindows) {
3922
- const pathExt = process.env["PATHEXT"] ?? "";
3923
- if (pathExt) {
3924
- const systemExts = pathExt.split(";").filter(Boolean);
3925
- exts = [...new Set([...exts, ...systemExts, ".exe", ".cmd", ".bat", ".ps1"])];
3926
- } else {
3927
- exts = ["", ".exe", ".cmd", ".bat", ".ps1"];
4034
+ return null;
4035
+ }
4036
+ function resolveLocal(command, workingDirectory, suffixes, stopAt) {
4037
+ const boundary = stopAt === undefined ? undefined : resolve7(stopAt);
4038
+ let current = resolve7(workingDirectory);
4039
+ while (true) {
4040
+ for (const rule of LOCAL_BIN_RULES) {
4041
+ if (!rule.markers.some((marker) => probe(join4(current, marker))))
4042
+ continue;
4043
+ for (const binDir of rule.binDirs) {
4044
+ const found = probeWithSuffixes(join4(current, binDir), command, suffixes);
4045
+ if (found !== null)
4046
+ return found;
4047
+ }
3928
4048
  }
4049
+ if (probe(join4(current, ".git")))
4050
+ return null;
4051
+ if (boundary !== undefined && current === boundary)
4052
+ return null;
4053
+ const parent = dirname7(current);
4054
+ if (parent === current)
4055
+ return null;
4056
+ current = parent;
3929
4057
  }
3930
- let pathEnv = process.env["PATH"] ?? "";
3931
- if (isWindows && !pathEnv) {
4058
+ }
4059
+ function resolveFromPath(command, suffixes, platform, pathEnvOverride) {
4060
+ let pathEnv = pathEnvOverride ?? process.env["PATH"] ?? "";
4061
+ if (platform === "win32" && !pathEnv) {
3932
4062
  pathEnv = process.env["Path"] ?? "";
3933
4063
  }
3934
- const paths = pathEnv.split(delimiter3);
3935
- for (const p of paths) {
3936
- for (const suffix of exts) {
3937
- if (existsSync9(join4(p, cmd + suffix))) {
3938
- return true;
3939
- }
3940
- }
4064
+ for (const entry of pathEnv.split(delimiter3)) {
4065
+ if (entry.length === 0)
4066
+ continue;
4067
+ const found = probeWithSuffixes(entry, command, suffixes);
4068
+ if (found !== null)
4069
+ return found;
3941
4070
  }
3942
- if (cmd === "node")
3943
- return true;
3944
- return false;
4071
+ return null;
4072
+ }
4073
+ function resolveServerBinary(command, workingDirectory, options = {}) {
4074
+ if (command.length === 0)
4075
+ return null;
4076
+ const [cmd] = command;
4077
+ if (cmd === undefined || cmd.length === 0)
4078
+ return null;
4079
+ const platform = options.platform ?? process.platform;
4080
+ const suffixes = executableSuffixes(platform, options.pathExt);
4081
+ if (cmd.includes("/") || cmd.includes("\\")) {
4082
+ const explicit = isAbsolute3(cmd) ? cmd : resolve7(workingDirectory ?? process.cwd(), cmd);
4083
+ return probe(explicit) ? explicit : null;
4084
+ }
4085
+ const cacheKey = JSON.stringify([
4086
+ workingDirectory ?? "",
4087
+ cmd,
4088
+ platform,
4089
+ options.stopAt ?? "",
4090
+ options.pathExt ?? "",
4091
+ options.pathEnv ?? process.env["PATH"] ?? ""
4092
+ ]);
4093
+ const cached = resolutionCache.get(cacheKey);
4094
+ if (cached !== undefined)
4095
+ return cached;
4096
+ let resolved = null;
4097
+ if (workingDirectory !== undefined && workingDirectory.length > 0) {
4098
+ resolved = resolveLocal(cmd, workingDirectory, suffixes, options.stopAt);
4099
+ }
4100
+ resolved ??= resolveFromPath(cmd, suffixes, platform, options.pathEnv);
4101
+ if (resolved === null && cmd === "node")
4102
+ resolved = cmd;
4103
+ resolutionCache.set(cacheKey, resolved);
4104
+ return resolved;
4105
+ }
4106
+ function isServerInstalled(command, workingDirectory, options = {}) {
4107
+ if (command.length === 0)
4108
+ return false;
4109
+ return resolveServerBinary(command, workingDirectory, options) !== null;
3945
4110
  }
3946
4111
 
3947
4112
  // ../lsp-core/src/lsp/server-resolution.ts
4113
+ function withResolvedCommand(command, binaryPath) {
4114
+ return [binaryPath, ...command.slice(1)];
4115
+ }
3948
4116
  function findServerForExtension(ext) {
3949
4117
  const servers = getMergedServers();
4118
+ const workingDirectory = contextCwd();
3950
4119
  for (const server2 of servers) {
3951
- if (server2.extensions.includes(ext) && isServerInstalled(server2.command)) {
4120
+ if (!server2.extensions.includes(ext))
4121
+ continue;
4122
+ const binaryPath = resolveServerBinary(server2.command, workingDirectory);
4123
+ if (binaryPath !== null) {
3952
4124
  const resolvedServer = {
3953
4125
  id: server2.id,
3954
- command: server2.command,
4126
+ command: withResolvedCommand(server2.command, binaryPath),
3955
4127
  extensions: server2.extensions,
3956
4128
  priority: server2.priority
3957
4129
  };
@@ -3998,6 +4170,7 @@ function findServerForExtension(ext) {
3998
4170
  function getAllServers() {
3999
4171
  const servers = getMergedServers();
4000
4172
  const disabled = getDisabledServerIds();
4173
+ const workingDirectory = contextCwd();
4001
4174
  const result = [];
4002
4175
  const seen = new Set;
4003
4176
  for (const server2 of servers) {
@@ -4005,7 +4178,7 @@ function getAllServers() {
4005
4178
  continue;
4006
4179
  result.push({
4007
4180
  id: server2.id,
4008
- installed: isServerInstalled(server2.command),
4181
+ installed: isServerInstalled(server2.command, workingDirectory),
4009
4182
  extensions: server2.extensions,
4010
4183
  disabled: false,
4011
4184
  source: server2.source,
@@ -4019,7 +4192,7 @@ function getAllServers() {
4019
4192
  const builtin = BUILTIN_SERVERS[id];
4020
4193
  result.push({
4021
4194
  id,
4022
- installed: builtin ? isServerInstalled(builtin.command) : false,
4195
+ installed: builtin ? isServerInstalled(builtin.command, workingDirectory) : false,
4023
4196
  extensions: builtin?.extensions ?? [],
4024
4197
  disabled: true,
4025
4198
  source: "disabled",
@@ -4042,7 +4215,7 @@ function findWorkspaceRoot(filePath) {
4042
4215
  const abs = resolveReadablePathInsideContext(filePath);
4043
4216
  let dir = abs;
4044
4217
  if (!isDirectoryPath(dir)) {
4045
- dir = dirname7(dir);
4218
+ dir = dirname8(dir);
4046
4219
  }
4047
4220
  if (!isPathInside(cwd, abs))
4048
4221
  return findWorkspaceRootOutsideContext(dir);
@@ -4058,13 +4231,13 @@ function findWorkspaceRoot(filePath) {
4058
4231
  }
4059
4232
  if (dir === cwd)
4060
4233
  break;
4061
- dir = dirname7(dir);
4234
+ dir = dirname8(dir);
4062
4235
  }
4063
4236
  return fallbackRoot;
4064
4237
  }
4065
4238
  function resolveReadablePathInsideContext(filePath) {
4066
4239
  const cwd = contextCwd();
4067
- const abs = resolve7(cwd, filePath);
4240
+ const abs = resolve8(cwd, filePath);
4068
4241
  if (isPathInside(cwd, abs))
4069
4242
  return abs;
4070
4243
  const rebased = rebaseThroughCanonicalAncestor(abs, cwd);
@@ -4090,7 +4263,7 @@ function rebaseThroughCanonicalAncestor(path, cwd) {
4090
4263
  if (isPathInside(cwd, canonical))
4091
4264
  return suffix.length === 0 ? canonical : join5(canonical, ...suffix);
4092
4265
  }
4093
- const parent = dirname7(current);
4266
+ const parent = dirname8(current);
4094
4267
  if (parent === current)
4095
4268
  return;
4096
4269
  suffix.unshift(basename3(current));
@@ -4113,7 +4286,7 @@ function nearestExistingDirectoryInsideContext(directory, cwd) {
4113
4286
  return canonical;
4114
4287
  if (current === cwd)
4115
4288
  return;
4116
- current = dirname7(current);
4289
+ current = dirname8(current);
4117
4290
  }
4118
4291
  return;
4119
4292
  }
@@ -4140,6 +4313,19 @@ function formatServerLookupError(result) {
4140
4313
  ].join(`
4141
4314
  `);
4142
4315
  }
4316
+ function formatInstallOptions(serverId, installHint) {
4317
+ const localHint = LSP_LOCAL_INSTALL_HINTS[serverId];
4318
+ if (localHint === undefined) {
4319
+ return ["To install, run:", ` ${installHint}`];
4320
+ }
4321
+ return [
4322
+ "To install in THIS repository (preferred — no global install needed):",
4323
+ ` ${localHint}`,
4324
+ "",
4325
+ "Or install it globally:",
4326
+ ` ${installHint}`
4327
+ ];
4328
+ }
4143
4329
  function formatNotInstalled(result) {
4144
4330
  const { server: server2, installHint } = result;
4145
4331
  const extensions = server2.extensions.join(", ");
@@ -4154,19 +4340,19 @@ function formatNotInstalled(result) {
4154
4340
  `Command not found: ${server2.command[0]}`,
4155
4341
  ""
4156
4342
  ];
4343
+ const installOptions = formatInstallOptions(server2.id, installHint);
4157
4344
  if (decision === "allowed") {
4158
4345
  return [
4159
4346
  ...header,
4160
4347
  "The user has pre-authorized LSP installation. Run the install command, then retry this tool:",
4161
- ` ${installHint}`
4348
+ ...installOptions.slice(1)
4162
4349
  ].join(`
4163
4350
  `);
4164
4351
  }
4165
4352
  if (!context.capabilities.installDecisionTool) {
4166
4353
  return [
4167
4354
  ...header,
4168
- "To install, run:",
4169
- ` ${installHint}`,
4355
+ ...installOptions,
4170
4356
  "",
4171
4357
  "ACTION REQUIRED — ASK THE USER whether to install this LSP server.",
4172
4358
  "Install-decision recording is unavailable in this harness; proceed without LSP if the user declines."
@@ -4175,8 +4361,7 @@ function formatNotInstalled(result) {
4175
4361
  }
4176
4362
  return [
4177
4363
  ...header,
4178
- "To install, run:",
4179
- ` ${installHint}`,
4364
+ ...installOptions,
4180
4365
  "",
4181
4366
  "ACTION REQUIRED — ASK THE USER whether to install this LSP server.",
4182
4367
  "- If the user agrees: run the install command above, then retry this tool.",
@@ -4232,7 +4417,7 @@ async function withLspClient(filePath, fn, toolName, options = {}) {
4232
4417
 
4233
4418
  // ../lsp-core/src/lsp/directory-diagnostics.ts
4234
4419
  import { existsSync as existsSync11, lstatSync as lstatSync4, readdirSync as readdirSync3 } from "node:fs";
4235
- import { join as join6, resolve as resolve8 } from "node:path";
4420
+ import { join as join6, resolve as resolve9 } from "node:path";
4236
4421
 
4237
4422
  // ../lsp-core/src/lsp/formatters.ts
4238
4423
  import { fileURLToPath as fileURLToPath2 } from "node:url";
@@ -4387,7 +4572,7 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
4387
4572
  if (!extension.startsWith(".")) {
4388
4573
  throw new LspInvalidPathError(`Extension must start with a dot (e.g., ".ts", not "${extension}"). Use ".${extension}" instead.`);
4389
4574
  }
4390
- const absDir = resolve8(options.workspaceRoot ?? contextCwd(), directory);
4575
+ const absDir = resolve9(options.workspaceRoot ?? contextCwd(), directory);
4391
4576
  if (!existsSync11(absDir)) {
4392
4577
  throw new LspInvalidPathError(`Directory does not exist: ${absDir}`);
4393
4578
  }
@@ -4744,6 +4929,96 @@ async function executeLspDiagnostics(params, signal) {
4744
4929
  throw error;
4745
4930
  }
4746
4931
  }
4932
+ // ../lsp-core/src/lsp/format-document.ts
4933
+ import { readFileSync as readFileSync5, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
4934
+ var DEFAULT_FORMATTING_OPTIONS = {
4935
+ tabSize: 4,
4936
+ insertSpaces: false,
4937
+ trimTrailingWhitespace: true,
4938
+ insertFinalNewline: true,
4939
+ trimFinalNewlines: true
4940
+ };
4941
+ var UNCHANGED = { status: "unchanged", linesAdded: 0, linesRemoved: 0 };
4942
+ async function formatDocumentWithClient(client, filePath, options = {}) {
4943
+ const edits = await client.formatDocument(filePath, options.formattingOptions ?? DEFAULT_FORMATTING_OPTIONS, options.signal);
4944
+ if (edits === null)
4945
+ return { status: "unavailable", reason: "capability_not_advertised" };
4946
+ if (edits.length === 0)
4947
+ return UNCHANGED;
4948
+ const before = readFileSync5(filePath, "utf-8");
4949
+ const normalized = normalizeTextEdits(before, edits, 0);
4950
+ if (normalized.text === before)
4951
+ return UNCHANGED;
4952
+ writeAtomically(filePath, normalized.text);
4953
+ await client.openFile(filePath);
4954
+ return {
4955
+ status: "formatted",
4956
+ ...lineDelta(normalized.edits)
4957
+ };
4958
+ }
4959
+ function lineDelta(edits) {
4960
+ let linesAdded = 0;
4961
+ let linesRemoved = 0;
4962
+ for (const edit of edits) {
4963
+ linesRemoved += edit.range.end.line - edit.range.start.line + 1;
4964
+ linesAdded += edit.newText.split(`
4965
+ `).length;
4966
+ }
4967
+ return { linesAdded, linesRemoved };
4968
+ }
4969
+ function writeAtomically(filePath, content) {
4970
+ const tempPath = `${filePath}.omo-format.tmp`;
4971
+ writeFileSync3(tempPath, content, "utf-8");
4972
+ try {
4973
+ renameSync3(tempPath, filePath);
4974
+ } catch (error) {
4975
+ try {
4976
+ unlinkSync(tempPath);
4977
+ } catch {}
4978
+ throw error;
4979
+ }
4980
+ }
4981
+
4982
+ // ../lsp-core/src/tools/format.ts
4983
+ async function executeLspFormat(params, signal) {
4984
+ const filePath = requireString(params, "filePath");
4985
+ try {
4986
+ const result = await withLspClient(filePath, async (client, _workspaceRoot, resolvedFilePath) => formatDocumentWithClient(client, resolvedFilePath, signal === undefined ? {} : { signal }), "format", clientOptions(signal));
4987
+ return text(describeResult(filePath, result), formatDetails(filePath, result));
4988
+ } catch (error) {
4989
+ const missingDependency = missingDependencyResult(error, {
4990
+ filePath,
4991
+ status: "unavailable",
4992
+ reason: "server_unavailable",
4993
+ linesAdded: 0,
4994
+ linesRemoved: 0
4995
+ });
4996
+ if (missingDependency)
4997
+ return missingDependency;
4998
+ throw error;
4999
+ }
5000
+ }
5001
+ function formatDetails(filePath, result) {
5002
+ if (result.status === "unavailable") {
5003
+ return { filePath, status: "unavailable", reason: result.reason, linesAdded: 0, linesRemoved: 0 };
5004
+ }
5005
+ return {
5006
+ filePath,
5007
+ status: result.status,
5008
+ linesAdded: result.linesAdded,
5009
+ linesRemoved: result.linesRemoved
5010
+ };
5011
+ }
5012
+ function describeResult(filePath, result) {
5013
+ if (result.status === "unavailable") {
5014
+ return `Formatting unavailable for ${filePath}: the language server does not advertise documentFormattingProvider.`;
5015
+ }
5016
+ if (result.status === "unchanged") {
5017
+ return `Already formatted: ${filePath}`;
5018
+ }
5019
+ return `Formatted ${filePath} (+${result.linesAdded}/-${result.linesRemoved} lines)`;
5020
+ }
5021
+
4747
5022
  // ../lsp-core/src/tools/install-decision.ts
4748
5023
  async function executeLspInstallDecision(params) {
4749
5024
  const serverId = requireString(params, "server_id");
@@ -5076,6 +5351,16 @@ var LSP_MCP_TOOLS = [
5076
5351
  }, ["filePath", "line", "character", "newName"]),
5077
5352
  execute: executeLspRename
5078
5353
  },
5354
+ {
5355
+ name: "format",
5356
+ aliases: ["lsp_format"],
5357
+ title: "LSP Format",
5358
+ description: "Format a source file with its language server and write the returned edits to disk.",
5359
+ inputSchema: objectSchema({
5360
+ filePath: { type: "string", description: "Source file path to format." }
5361
+ }, ["filePath"]),
5362
+ execute: executeLspFormat
5363
+ },
5079
5364
  {
5080
5365
  name: "install_decision",
5081
5366
  aliases: ["lsp_install_decision"],
@@ -5254,7 +5539,7 @@ function errorText(error) {
5254
5539
  import { spawn as spawn2 } from "node:child_process";
5255
5540
  import { closeSync as closeSync2, existsSync as existsSync12, mkdirSync as mkdirSync3, openSync as openSync2, writeSync as writeSync2 } from "node:fs";
5256
5541
  import { Socket } from "node:net";
5257
- import { dirname as dirname9, isAbsolute as isAbsolute4 } from "node:path";
5542
+ import { dirname as dirname10, isAbsolute as isAbsolute5 } from "node:path";
5258
5543
  import { argv0, execPath } from "node:process";
5259
5544
 
5260
5545
  // src/ipc-protocol.ts
@@ -5268,11 +5553,11 @@ import {
5268
5553
  lstatSync as lstatSync6,
5269
5554
  mkdirSync as mkdirSync2,
5270
5555
  openSync,
5271
- readFileSync as readFileSync5,
5272
- unlinkSync,
5556
+ readFileSync as readFileSync6,
5557
+ unlinkSync as unlinkSync2,
5273
5558
  writeSync
5274
5559
  } from "node:fs";
5275
- import { dirname as dirname8 } from "node:path";
5560
+ import { dirname as dirname9 } from "node:path";
5276
5561
  var OMO_DAEMON_PROTOCOL_VERSION = 1;
5277
5562
  var AUTH_ERROR_CODE = -32001;
5278
5563
  var PROTOCOL_ERROR_CODE = -32002;
@@ -5292,7 +5577,7 @@ function authEnvelope(token) {
5292
5577
  }
5293
5578
  function readAuthToken(paths) {
5294
5579
  try {
5295
- const token = readFileSync5(paths.auth, "utf8").trim();
5580
+ const token = readFileSync6(paths.auth, "utf8").trim();
5296
5581
  return token.length > 0 ? token : null;
5297
5582
  } catch (error) {
5298
5583
  if (error instanceof Error)
@@ -5308,7 +5593,7 @@ function readOrCreateAuthToken(paths) {
5308
5593
  }
5309
5594
  function rotateAuthToken(paths) {
5310
5595
  try {
5311
- unlinkSync(paths.auth);
5596
+ unlinkSync2(paths.auth);
5312
5597
  } catch (error) {
5313
5598
  if (!(error instanceof Error))
5314
5599
  throw error;
@@ -5380,7 +5665,7 @@ function setPrivateFileMode(path) {
5380
5665
  chmodSync(path, 384);
5381
5666
  }
5382
5667
  function createAuthToken(paths) {
5383
- ensurePrivateDirectory(dirname8(paths.auth));
5668
+ ensurePrivateDirectory(dirname9(paths.auth));
5384
5669
  const token = randomBytes(AUTH_TOKEN_BYTES).toString("base64url");
5385
5670
  let fd;
5386
5671
  try {
@@ -5461,7 +5746,7 @@ import { fileURLToPath as fileURLToPath3 } from "node:url";
5461
5746
 
5462
5747
  // src/runtime-contract.ts
5463
5748
  import { statSync as statSync4 } from "node:fs";
5464
- import { isAbsolute as isAbsolute3 } from "node:path";
5749
+ import { isAbsolute as isAbsolute4 } from "node:path";
5465
5750
  var OMO_LSP_DAEMON_DIR = "OMO_LSP_DAEMON_DIR";
5466
5751
  var OMO_LSP_DAEMON_CLI = "OMO_LSP_DAEMON_CLI";
5467
5752
  var OMO_LSP_DAEMON_VERSION = "OMO_LSP_DAEMON_VERSION";
@@ -5498,12 +5783,12 @@ function resolveDaemonRuntime(env, defaults) {
5498
5783
  throw new InvalidRuntimeOverrideError("paired_values_required", `${OMO_LSP_DAEMON_CLI} and ${OMO_LSP_DAEMON_VERSION} must be set together`);
5499
5784
  }
5500
5785
  if (!hasCliOverride || !hasVersionOverride) {
5501
- if (!isAbsolute3(defaults.cliPath)) {
5786
+ if (!isAbsolute4(defaults.cliPath)) {
5502
5787
  throw new InvalidRuntimeOverrideError("packaged_cli_must_be_absolute", "Packaged LSP daemon CLI path must be absolute");
5503
5788
  }
5504
5789
  return { cliPath: defaults.cliPath, version: validateDaemonVersion(defaults.version) };
5505
5790
  }
5506
- if (!isAbsolute3(cliOverride)) {
5791
+ if (!isAbsolute4(cliOverride)) {
5507
5792
  throw new InvalidRuntimeOverrideError("cli_must_be_absolute", `${OMO_LSP_DAEMON_CLI} must be an absolute path to an existing regular file`);
5508
5793
  }
5509
5794
  let cliStats;
@@ -5681,7 +5966,7 @@ async function probeDaemon(paths, timeoutMs = PROBE_TIMEOUT_MS, signal) {
5681
5966
  return await pingDaemon(paths, token, timeoutMs, signal) !== null;
5682
5967
  }
5683
5968
  function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS, signal) {
5684
- return new Promise((resolve9) => {
5969
+ return new Promise((resolve10) => {
5685
5970
  const socket = new Socket;
5686
5971
  let settled = false;
5687
5972
  let timer;
@@ -5693,7 +5978,7 @@ function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS, signal) {
5693
5978
  clearTimeout(timer);
5694
5979
  signal?.removeEventListener("abort", onAbort);
5695
5980
  socket.destroy();
5696
- resolve9(value);
5981
+ resolve10(value);
5697
5982
  };
5698
5983
  const onAbort = () => finish(null);
5699
5984
  const decoder = createLineDecoder((message) => {
@@ -5717,7 +6002,7 @@ function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS, signal) {
5717
6002
  });
5718
6003
  }
5719
6004
  function spawnDaemonProcess(paths) {
5720
- mkdirSync3(dirname9(paths.log), { recursive: true });
6005
+ mkdirSync3(dirname10(paths.log), { recursive: true });
5721
6006
  const logFd = openSync2(paths.log, "a");
5722
6007
  try {
5723
6008
  const child = spawn2(resolveDaemonNodeExecutable(), [paths.cliPath, "daemon"], {
@@ -5740,7 +6025,7 @@ function spawnDaemonProcess(paths) {
5740
6025
  function resolveDaemonNodeExecutable(cachedExecPath = execPath, originalArgv0 = argv0, pathExists = existsSync12) {
5741
6026
  if (pathExists(cachedExecPath))
5742
6027
  return cachedExecPath;
5743
- if (isAbsolute4(originalArgv0) && pathExists(originalArgv0))
6028
+ if (isAbsolute5(originalArgv0) && pathExists(originalArgv0))
5744
6029
  return originalArgv0;
5745
6030
  return "node";
5746
6031
  }
@@ -5753,7 +6038,7 @@ function defaultEnsureDaemonDeps() {
5753
6038
  };
5754
6039
  }
5755
6040
  function sleepWithSignal(ms, signal) {
5756
- return new Promise((resolve9) => {
6041
+ return new Promise((resolve10) => {
5757
6042
  let settled = false;
5758
6043
  const finish = () => {
5759
6044
  if (settled)
@@ -5761,7 +6046,7 @@ function sleepWithSignal(ms, signal) {
5761
6046
  settled = true;
5762
6047
  clearTimeout(timer);
5763
6048
  signal?.removeEventListener("abort", finish);
5764
- resolve9();
6049
+ resolve10();
5765
6050
  };
5766
6051
  const timer = setTimeout(finish, ms);
5767
6052
  if (signal?.aborted) {
@@ -5776,7 +6061,7 @@ function awaitWithSignal2(promise, signal) {
5776
6061
  return promise;
5777
6062
  if (signal.aborted)
5778
6063
  return Promise.reject(abortError3(signal));
5779
- return new Promise((resolve9, reject) => {
6064
+ return new Promise((resolve10, reject) => {
5780
6065
  let settled = false;
5781
6066
  const finish = (run) => {
5782
6067
  if (settled)
@@ -5787,7 +6072,7 @@ function awaitWithSignal2(promise, signal) {
5787
6072
  };
5788
6073
  const onAbort = () => finish(() => reject(abortError3(signal)));
5789
6074
  signal.addEventListener("abort", onAbort, { once: true });
5790
- promise.then((value) => finish(() => resolve9(value)), (error) => finish(() => reject(error)));
6075
+ promise.then((value) => finish(() => resolve10(value)), (error) => finish(() => reject(error)));
5791
6076
  });
5792
6077
  }
5793
6078
  function throwIfAborted(signal) {
@@ -5955,6 +6240,9 @@ async function callToolViaDaemon(name, args, options) {
5955
6240
  function callDiagnosticsViaDaemon(filePath, options) {
5956
6241
  return callToolViaDaemon("diagnostics", { filePath, severity: "error" }, options);
5957
6242
  }
6243
+ function callFormatViaDaemon(filePath, options) {
6244
+ return callToolViaDaemon("format", { filePath }, options);
6245
+ }
5958
6246
  function currentRequestContext(env = process.env) {
5959
6247
  const cwd = process.cwd();
5960
6248
  const home = env["HOME"] ?? homedir3();
@@ -5977,7 +6265,7 @@ function withContext(args, context) {
5977
6265
  function ensureDaemonAvailable(paths, ensure, signal) {
5978
6266
  if (!signal)
5979
6267
  return ensure(paths);
5980
- return new Promise((resolve9, reject) => {
6268
+ return new Promise((resolve10, reject) => {
5981
6269
  let settled = false;
5982
6270
  const finish = (run) => {
5983
6271
  if (settled)
@@ -5992,11 +6280,11 @@ function ensureDaemonAvailable(paths, ensure, signal) {
5992
6280
  return;
5993
6281
  }
5994
6282
  signal.addEventListener("abort", onAbort, { once: true });
5995
- Promise.resolve().then(() => ensure(paths, signal)).then(() => finish(() => resolve9()), (error) => finish(() => reject(signal.aborted ? new DaemonRequestCancelledError(false) : error)));
6283
+ Promise.resolve().then(() => ensure(paths, signal)).then(() => finish(() => resolve10()), (error) => finish(() => reject(signal.aborted ? new DaemonRequestCancelledError(false) : error)));
5996
6284
  });
5997
6285
  }
5998
6286
  function sendToolCall(paths, token, name, args, options) {
5999
- return new Promise((resolve9, reject) => {
6287
+ return new Promise((resolve10, reject) => {
6000
6288
  const socket = connect(paths.socket);
6001
6289
  const requestId = allocateProxyRequestId();
6002
6290
  let settled = false;
@@ -6049,7 +6337,7 @@ function sendToolCall(paths, token, name, args, options) {
6049
6337
  }
6050
6338
  const result = toToolResult(message, requestId);
6051
6339
  if (result)
6052
- finish(() => resolve9(result));
6340
+ finish(() => resolve10(result));
6053
6341
  else
6054
6342
  finish(() => reject(new DaemonRequestError("invalid daemon response", requestWritten)));
6055
6343
  });
@@ -6217,18 +6505,18 @@ function canonicalizePathList(value) {
6217
6505
  return value.split(delimiter4).map((entry) => canonicalizePath(entry) ?? entry).join(delimiter4);
6218
6506
  }
6219
6507
  function canonicalizePath(value) {
6220
- if (value === undefined || !isAbsolute5(value) || !existsSync13(value))
6508
+ if (value === undefined || !isAbsolute6(value) || !existsSync13(value))
6221
6509
  return value;
6222
6510
  return realpathSync6(value);
6223
6511
  }
6224
6512
  function projectRootFromOpenCodeConfigPath(path2) {
6225
6513
  if (basename4(path2) !== "lsp.json" && basename4(path2) !== "lsp-client.json")
6226
6514
  return;
6227
- const configDir = dirname10(path2);
6515
+ const configDir = dirname11(path2);
6228
6516
  const configDirName = basename4(configDir);
6229
6517
  if (configDirName !== ".opencode" && configDirName !== ".omo")
6230
6518
  return;
6231
- return dirname10(configDir);
6519
+ return dirname11(configDir);
6232
6520
  }
6233
6521
 
6234
6522
  // src/daemon-server.ts
@@ -6237,12 +6525,12 @@ import { createServer as createServer2 } from "node:net";
6237
6525
 
6238
6526
  // src/ownership.ts
6239
6527
  import { randomUUID } from "node:crypto";
6240
- import { existsSync as existsSync14, lstatSync as lstatSync7, readFileSync as readFileSync7, statSync as statSync5, unlinkSync as unlinkSync3 } from "node:fs";
6241
- import { dirname as dirname12 } from "node:path";
6528
+ import { existsSync as existsSync14, lstatSync as lstatSync7, readFileSync as readFileSync8, statSync as statSync5, unlinkSync as unlinkSync4 } from "node:fs";
6529
+ import { dirname as dirname13 } from "node:path";
6242
6530
 
6243
6531
  // src/lock.ts
6244
- import { closeSync as closeSync3, mkdirSync as mkdirSync4, openSync as openSync3, readFileSync as readFileSync6, unlinkSync as unlinkSync2, writeSync as writeSync3 } from "node:fs";
6245
- import { dirname as dirname11 } from "node:path";
6532
+ import { closeSync as closeSync3, mkdirSync as mkdirSync4, openSync as openSync3, readFileSync as readFileSync7, unlinkSync as unlinkSync3, writeSync as writeSync3 } from "node:fs";
6533
+ import { dirname as dirname12 } from "node:path";
6246
6534
  function isProcessAlive2(pid) {
6247
6535
  if (!Number.isInteger(pid) || pid <= 0)
6248
6536
  return false;
@@ -6257,14 +6545,14 @@ function isProcessAlive2(pid) {
6257
6545
  }
6258
6546
  function readLockPid(lockPath) {
6259
6547
  try {
6260
- const pid = Number.parseInt(readFileSync6(lockPath, "utf8").trim(), 10);
6548
+ const pid = Number.parseInt(readFileSync7(lockPath, "utf8").trim(), 10);
6261
6549
  return Number.isInteger(pid) ? pid : null;
6262
6550
  } catch {
6263
6551
  return null;
6264
6552
  }
6265
6553
  }
6266
6554
  function tryAcquireLock(lockPath, ownerPid = process.pid) {
6267
- mkdirSync4(dirname11(lockPath), { recursive: true });
6555
+ mkdirSync4(dirname12(lockPath), { recursive: true });
6268
6556
  for (let attempt = 0;attempt < 2; attempt += 1) {
6269
6557
  const handle = writeLockFile(lockPath, ownerPid);
6270
6558
  if (handle)
@@ -6296,7 +6584,7 @@ function reapStaleLock(lockPath) {
6296
6584
  }
6297
6585
  function unlinkQuietly(path2) {
6298
6586
  try {
6299
- unlinkSync2(path2);
6587
+ unlinkSync3(path2);
6300
6588
  } catch (error) {
6301
6589
  if (error instanceof Error)
6302
6590
  return;
@@ -6347,11 +6635,11 @@ async function acquireStartupLease(paths, pingOwner) {
6347
6635
  function ensureDaemonDirectories(paths) {
6348
6636
  ensurePrivateDirectory(paths.dir);
6349
6637
  if (process.platform !== "win32")
6350
- ensurePrivateDirectory(dirname12(paths.socket));
6638
+ ensurePrivateDirectory(dirname13(paths.socket));
6351
6639
  }
6352
6640
  function readDaemonOwner(paths) {
6353
6641
  try {
6354
- return parseOwner(JSON.parse(readFileSync7(paths.owner, "utf8")));
6642
+ return parseOwner(JSON.parse(readFileSync8(paths.owner, "utf8")));
6355
6643
  } catch (error) {
6356
6644
  if (error instanceof Error)
6357
6645
  return null;
@@ -6430,7 +6718,7 @@ function cleanupDeadOwner(paths, owner) {
6430
6718
  if (process.platform !== "win32" && existsSync14(owner.endpoint.path)) {
6431
6719
  const stat = lstatSync7(owner.endpoint.path);
6432
6720
  if (stat.isSocket())
6433
- unlinkSync3(owner.endpoint.path);
6721
+ unlinkSync4(owner.endpoint.path);
6434
6722
  }
6435
6723
  unlinkQuietly(paths.pid);
6436
6724
  unlinkQuietly(paths.endpoint);
@@ -6478,7 +6766,7 @@ function parseEndpoint(value) {
6478
6766
  // src/version-reap.ts
6479
6767
  import { execFile } from "node:child_process";
6480
6768
  import { readdir, readFile, rm } from "node:fs/promises";
6481
- import { basename as basename5, dirname as dirname13, join as join9 } from "node:path";
6769
+ import { basename as basename5, dirname as dirname14, join as join9 } from "node:path";
6482
6770
  var TERM_GRACE_MS = 5000;
6483
6771
  var KILL_GRACE_MS = 1000;
6484
6772
  var VERSION_ENTRY_PATTERN = /^v([A-Za-z0-9][A-Za-z0-9._+-]{0,127})$/;
@@ -6499,7 +6787,7 @@ async function attestDaemonCliProcess(pid, platform, deps = {}) {
6499
6787
  return isNodeCliDaemonCommand(command.trim());
6500
6788
  }
6501
6789
  async function reapStaleDaemonVersions(ownPaths, deps = {}) {
6502
- const baseDir = dirname13(ownPaths.dir);
6790
+ const baseDir = dirname14(ownPaths.dir);
6503
6791
  const platform = deps.platform ?? process.platform;
6504
6792
  const isAlive = deps.isAlive ?? isProcessAlive2;
6505
6793
  const attest = deps.attest ?? ((pid) => attestDaemonCliProcess(pid, platform));
@@ -6620,13 +6908,13 @@ function defaultReadProcFile(path2) {
6620
6908
  return readFile(path2);
6621
6909
  }
6622
6910
  function defaultExecuteForStdout(file, args) {
6623
- return new Promise((resolve9) => {
6911
+ return new Promise((resolve10) => {
6624
6912
  execFile(file, [...args], { encoding: "utf8", maxBuffer: 1024 * 1024, timeout: 1000, windowsHide: true }, (error, stdout) => {
6625
6913
  if (error !== null) {
6626
- resolve9(null);
6914
+ resolve10(null);
6627
6915
  return;
6628
6916
  }
6629
- resolve9(stdout);
6917
+ resolve10(stdout);
6630
6918
  });
6631
6919
  });
6632
6920
  }
@@ -6645,7 +6933,7 @@ async function defaultWaitForExit(pid, timeoutMs) {
6645
6933
  return true;
6646
6934
  if (Date.now() >= deadline)
6647
6935
  return false;
6648
- await new Promise((resolve9) => setTimeout(resolve9, 100));
6936
+ await new Promise((resolve10) => setTimeout(resolve10, 100));
6649
6937
  }
6650
6938
  }
6651
6939
  function defaultLog(message) {
@@ -6752,17 +7040,17 @@ async function assertSelfProbe(paths, token, owner) {
6752
7040
  throw new DaemonStartupDeferredError("self_probe_failed");
6753
7041
  }
6754
7042
  function listen(server2, socketPath) {
6755
- return new Promise((resolve9, reject) => {
7043
+ return new Promise((resolve10, reject) => {
6756
7044
  const onError = (error) => reject(error);
6757
7045
  server2.once("error", onError);
6758
7046
  server2.listen(socketPath, () => {
6759
7047
  server2.removeListener("error", onError);
6760
- resolve9();
7048
+ resolve10();
6761
7049
  });
6762
7050
  });
6763
7051
  }
6764
7052
  function closeServer(server2) {
6765
- return new Promise((resolve9) => server2.close(() => resolve9()));
7053
+ return new Promise((resolve10) => server2.close(() => resolve10()));
6766
7054
  }
6767
7055
  function installSignalHandlers(close) {
6768
7056
  const handler = () => {