oh-my-opencode 5.0.0-beta.10 → 5.0.0-beta.12

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 (73) hide show
  1. package/dist/cli/index.js +34 -86
  2. package/dist/cli-node/index.js +34 -86
  3. package/dist/index.js +80 -93
  4. package/dist/tools/delegate-task/builtin-categories.d.ts +1 -0
  5. package/dist/tools/delegate-task/builtin-category-definition.d.ts +1 -0
  6. package/dist/tools/delegate-task/tool-description.d.ts +3 -1
  7. package/dist/tui.js +19 -71
  8. package/package.json +14 -14
  9. package/packages/lsp-core/src/lsp/client-wrapper-outside-cwd.test.ts +78 -0
  10. package/packages/lsp-core/src/lsp/client-wrapper.test.ts +6 -6
  11. package/packages/lsp-core/src/lsp/client-wrapper.ts +5 -6
  12. package/packages/lsp-core/src/lsp/outside-context-workspace.ts +33 -0
  13. package/packages/lsp-core/src/lsp/workspace-markers.ts +1 -0
  14. package/packages/lsp-daemon/dist/cli.js +94 -59
  15. package/packages/lsp-daemon/dist/client.js +72 -37
  16. package/packages/lsp-daemon/dist/index.js +78 -43
  17. package/packages/lsp-tools-mcp/dist/cli.js +68 -33
  18. package/packages/lsp-tools-mcp/dist/mcp.js +68 -33
  19. package/packages/lsp-tools-mcp/dist/tools.js +68 -33
  20. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  21. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  22. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  23. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +9 -0
  24. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +9 -0
  25. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  26. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  27. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  28. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  29. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  30. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  31. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  32. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  33. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +83 -54
  34. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  35. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  36. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  37. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  38. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  39. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  40. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  41. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  42. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  43. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  44. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  45. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  46. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  47. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  48. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  49. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  50. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  51. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  52. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  53. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  54. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  55. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  56. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  57. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  58. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  59. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  60. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  61. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  62. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  63. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  64. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  65. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  66. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  67. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  68. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  69. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  70. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  71. package/packages/omo-codex/plugin/package-lock.json +13 -13
  72. package/packages/omo-codex/plugin/package.json +1 -1
  73. package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
@@ -0,0 +1,78 @@
1
+ import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, describe, expect, it } from "bun:test";
5
+
6
+ import { createStandaloneMcpRequestContext, runWithRequestContext } from "../request-context.js";
7
+ import { findWorkspaceRoot, resolveReadablePathInsideContext, resolvePathInsideContext } from "./client-wrapper.js";
8
+ import { LspInvalidPathError } from "./errors.js";
9
+
10
+ const tempDirectories: string[] = [];
11
+
12
+ afterEach(() => {
13
+ for (const directory of tempDirectories.splice(0)) {
14
+ rmSync(directory, { recursive: true, force: true });
15
+ }
16
+ });
17
+
18
+ function tempRoot(prefix: string): string {
19
+ const root = mkdtempSync(join(tmpdir(), prefix));
20
+ tempDirectories.push(root);
21
+ return root;
22
+ }
23
+
24
+ describe("LSP read-only access outside request cwd", () => {
25
+ it("#given an absolute file outside cwd #when resolving a read path #then returns the canonical path", () => {
26
+ const root = tempRoot("lsp-outcwd-root-");
27
+ const outside = tempRoot("lsp-outcwd-outside-");
28
+ const outsideFile = join(outside, "probe.ts");
29
+ writeFileSync(outsideFile, "export const value = 1;\n");
30
+
31
+ const resolved = runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
32
+ resolveReadablePathInsideContext(outsideFile),
33
+ );
34
+
35
+ expect(resolved).toBe(realpathSync(outsideFile));
36
+ });
37
+
38
+ it("#given an outside file inside its own marked project #when inferring workspace #then uses that project root", () => {
39
+ const root = tempRoot("lsp-outcwd-marker-root-");
40
+ const outside = tempRoot("lsp-outcwd-marker-outside-");
41
+ mkdirSync(join(outside, ".git"), { recursive: true });
42
+ mkdirSync(join(outside, "src"), { recursive: true });
43
+ const outsideFile = join(outside, "src", "probe.ts");
44
+ writeFileSync(outsideFile, "export const value = 1;\n");
45
+
46
+ const workspace = runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
47
+ findWorkspaceRoot(outsideFile),
48
+ );
49
+
50
+ expect(workspace).toBe(realpathSync(outside));
51
+ });
52
+
53
+ it("#given an outside file without any marker #when inferring workspace #then falls back to its own directory", () => {
54
+ const root = tempRoot("lsp-outcwd-nomarker-root-");
55
+ const outside = tempRoot("lsp-outcwd-nomarker-outside-");
56
+ const outsideFile = join(outside, "probe.ts");
57
+ writeFileSync(outsideFile, "export const value = 1;\n");
58
+
59
+ const workspace = runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
60
+ findWorkspaceRoot(outsideFile),
61
+ );
62
+
63
+ expect(workspace).toBe(realpathSync(outside));
64
+ });
65
+
66
+ it("#given an outside file #when resolving a mutating path #then still rejects to keep writes confined", () => {
67
+ const root = tempRoot("lsp-outcwd-write-root-");
68
+ const outside = tempRoot("lsp-outcwd-write-outside-");
69
+ const outsideFile = join(outside, "probe.ts");
70
+ writeFileSync(outsideFile, "export const value = 1;\n");
71
+
72
+ expect(() =>
73
+ runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
74
+ resolvePathInsideContext(outsideFile),
75
+ ),
76
+ ).toThrow(LspInvalidPathError);
77
+ });
78
+ });
@@ -36,17 +36,17 @@ describe("LSP client path confinement", () => {
36
36
  expect(workspace).toBe(realpathSync(root));
37
37
  });
38
38
 
39
- it("#given an absolute file outside context cwd #when resolving #then rejects before workspace inference", () => {
39
+ it("#given an absolute file outside context cwd #when resolving #then infers the file's own project root", () => {
40
40
  const root = tempRoot("lsp-client-wrapper-cwd-");
41
41
  const outside = tempRoot("lsp-client-wrapper-outside-");
42
42
  mkdirSync(join(outside, ".git"), { recursive: true });
43
43
  writeFileSync(join(outside, "file.ts"), "export const outside = true;\n");
44
44
 
45
- expect(() =>
46
- runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
47
- findWorkspaceRoot(join(outside, "file.ts")),
48
- ),
49
- ).toThrow(LspInvalidPathError);
45
+ const workspace = runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
46
+ findWorkspaceRoot(join(outside, "file.ts")),
47
+ );
48
+
49
+ expect(workspace).toBe(realpathSync(outside));
50
50
  });
51
51
 
52
52
  it("#given a symlink inside cwd that points outside #when resolving read path #then keeps lexical path and cwd root", () => {
@@ -16,11 +16,11 @@ import {
16
16
  LspServerLookupError,
17
17
  } from "./errors.js";
18
18
  import { getLspManager, type LspManager } from "./manager.js";
19
+ import { findWorkspaceRootOutsideContext } from "./outside-context-workspace.js";
19
20
  import { loadInstallDecision } from "./server-install-state.js";
20
21
  import { findServerForExtension } from "./server-resolution.js";
21
22
  import type { ServerLookupResult } from "./types.js";
22
-
23
- const WORKSPACE_MARKERS = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
23
+ import { WORKSPACE_MARKERS } from "./workspace-markers.js";
24
24
 
25
25
  export function isDirectoryPath(filePath: string): boolean {
26
26
  try {
@@ -39,6 +39,8 @@ export function findWorkspaceRoot(filePath: string): string {
39
39
  dir = dirname(dir);
40
40
  }
41
41
 
42
+ if (!isPathInside(cwd, abs)) return findWorkspaceRootOutsideContext(dir);
43
+
42
44
  const fallbackRoot = nearestExistingDirectoryInsideContext(dir, cwd) ?? cwd;
43
45
  while (isPathInside(cwd, dir)) {
44
46
  const canonicalDir = existingDirectoryInsideContext(dir, cwd);
@@ -64,10 +66,7 @@ export function resolveReadablePathInsideContext(filePath: string): string {
64
66
  const rebased = rebaseThroughCanonicalAncestor(abs, cwd);
65
67
  if (rebased !== undefined) return rebased;
66
68
 
67
- const canonical = canonicalizeExistingOrNearestAncestor(abs);
68
- if (isPathInside(cwd, canonical)) return canonical;
69
-
70
- throw new LspInvalidPathError(`LSP file path must be inside request cwd: ${filePath}`);
69
+ return canonicalizeExistingOrNearestAncestor(abs);
71
70
  }
72
71
 
73
72
  export function resolvePathInsideContext(filePath: string): string {
@@ -0,0 +1,33 @@
1
+ import { existsSync, realpathSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+
4
+ import { WORKSPACE_MARKERS } from "./workspace-markers.js";
5
+
6
+ /** Read-only tools accept out-of-cwd files, so cwd cannot bound this search; the fallback keeps the server scoped to the file's own directory. */
7
+ export function findWorkspaceRootOutsideContext(directory: string): string {
8
+ const marked = nearestMarkedAncestor(directory);
9
+ if (marked !== undefined) return marked;
10
+ return realpathSync(nearestExistingAncestor(directory));
11
+ }
12
+
13
+ function nearestMarkedAncestor(directory: string): string | undefined {
14
+ let current = directory;
15
+ for (;;) {
16
+ if (existsSync(current) && WORKSPACE_MARKERS.some((marker) => existsSync(join(current, marker)))) {
17
+ return realpathSync(current);
18
+ }
19
+ const parent = dirname(current);
20
+ if (parent === current) return undefined;
21
+ current = parent;
22
+ }
23
+ }
24
+
25
+ function nearestExistingAncestor(directory: string): string {
26
+ let current = directory;
27
+ while (!existsSync(current)) {
28
+ const parent = dirname(current);
29
+ if (parent === current) return current;
30
+ current = parent;
31
+ }
32
+ return current;
33
+ }
@@ -0,0 +1 @@
1
+ export const WORKSPACE_MARKERS = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"] as const;
@@ -4,8 +4,8 @@
4
4
  import { argv, stderr } from "node:process";
5
5
 
6
6
  // src/proxy.ts
7
- import { existsSync as existsSync12, realpathSync as realpathSync5 } from "node:fs";
8
- import { basename as basename4, delimiter as delimiter4, dirname as dirname9, isAbsolute as isAbsolute5 } from "node:path";
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";
9
9
 
10
10
  // ../mcp-stdio-core/src/record.ts
11
11
  function isPlainRecord(value) {
@@ -315,8 +315,8 @@ function createIdleTimer(idleTimeoutMs, log, onTimeout) {
315
315
  };
316
316
  }
317
317
  // ../lsp-core/src/lsp/client-wrapper.ts
318
- import { existsSync as existsSync9, realpathSync as realpathSync4, statSync as statSync3 } from "node:fs";
319
- import { basename as basename3, dirname as dirname6, join as join4, resolve as resolve7 } from "node:path";
318
+ import { existsSync as existsSync10, realpathSync as realpathSync5, statSync as statSync3 } from "node:fs";
319
+ import { basename as basename3, dirname as dirname7, join as join5, resolve as resolve7 } from "node:path";
320
320
 
321
321
  // ../lsp-core/src/request-context.ts
322
322
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -3500,15 +3500,52 @@ async function disposeDefaultLspManager() {
3500
3500
  }
3501
3501
  }
3502
3502
 
3503
+ // ../lsp-core/src/lsp/outside-context-workspace.ts
3504
+ import { existsSync as existsSync6, realpathSync as realpathSync4 } from "node:fs";
3505
+ import { dirname as dirname5, join as join3 } from "node:path";
3506
+
3507
+ // ../lsp-core/src/lsp/workspace-markers.ts
3508
+ var WORKSPACE_MARKERS = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
3509
+
3510
+ // ../lsp-core/src/lsp/outside-context-workspace.ts
3511
+ function findWorkspaceRootOutsideContext(directory) {
3512
+ const marked = nearestMarkedAncestor(directory);
3513
+ if (marked !== undefined)
3514
+ return marked;
3515
+ return realpathSync4(nearestExistingAncestor(directory));
3516
+ }
3517
+ function nearestMarkedAncestor(directory) {
3518
+ let current = directory;
3519
+ for (;; ) {
3520
+ if (existsSync6(current) && WORKSPACE_MARKERS.some((marker) => existsSync6(join3(current, marker)))) {
3521
+ return realpathSync4(current);
3522
+ }
3523
+ const parent = dirname5(current);
3524
+ if (parent === current)
3525
+ return;
3526
+ current = parent;
3527
+ }
3528
+ }
3529
+ function nearestExistingAncestor(directory) {
3530
+ let current = directory;
3531
+ while (!existsSync6(current)) {
3532
+ const parent = dirname5(current);
3533
+ if (parent === current)
3534
+ return current;
3535
+ current = parent;
3536
+ }
3537
+ return current;
3538
+ }
3539
+
3503
3540
  // ../lsp-core/src/lsp/server-install-state.ts
3504
- import { existsSync as existsSync6, mkdirSync, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
3505
- import { dirname as dirname5 } from "node:path";
3541
+ import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
3542
+ import { dirname as dirname6 } from "node:path";
3506
3543
  function getInstallDecisionsPath() {
3507
3544
  return lspRequestContext().installDecisionsPath;
3508
3545
  }
3509
3546
  function loadInstallDecisions() {
3510
3547
  const path = getInstallDecisionsPath();
3511
- if (!existsSync6(path))
3548
+ if (!existsSync7(path))
3512
3549
  return {};
3513
3550
  try {
3514
3551
  const parsed = JSON.parse(readFileSync3(path, "utf8"));
@@ -3530,7 +3567,7 @@ function isInstallDecision(value) {
3530
3567
  }
3531
3568
  function writeInstallDecisions(decisions) {
3532
3569
  const path = getInstallDecisionsPath();
3533
- mkdirSync(dirname5(path), { recursive: true });
3570
+ mkdirSync(dirname6(path), { recursive: true });
3534
3571
  const tmpPath = `${path}.tmp`;
3535
3572
  writeFileSync2(tmpPath, `${JSON.stringify(decisions, null, 2)}
3536
3573
  `, "utf8");
@@ -3549,7 +3586,7 @@ function isRecord5(value) {
3549
3586
  }
3550
3587
 
3551
3588
  // ../lsp-core/src/lsp/config-loader.ts
3552
- import { existsSync as existsSync7, readFileSync as readFileSync4 } from "node:fs";
3589
+ import { existsSync as existsSync8, readFileSync as readFileSync4 } from "node:fs";
3553
3590
 
3554
3591
  // ../lsp-core/src/lsp/server-definitions.ts
3555
3592
  var LSP_INSTALL_HINTS = {
@@ -3706,7 +3743,7 @@ function getUserConfigPath() {
3706
3743
  return lspRequestContext().userConfigPath;
3707
3744
  }
3708
3745
  function loadJsonFile(path) {
3709
- if (!existsSync7(path))
3746
+ if (!existsSync8(path))
3710
3747
  return null;
3711
3748
  try {
3712
3749
  const parsed = JSON.parse(readFileSync4(path, "utf-8"));
@@ -3894,8 +3931,8 @@ function getDisabledServerIds() {
3894
3931
  }
3895
3932
 
3896
3933
  // ../lsp-core/src/lsp/server-installation.ts
3897
- import { existsSync as existsSync8 } from "node:fs";
3898
- import { delimiter as delimiter3, join as join3 } from "node:path";
3934
+ import { existsSync as existsSync9 } from "node:fs";
3935
+ import { delimiter as delimiter3, join as join4 } from "node:path";
3899
3936
  function isServerInstalled(command, _workingDirectory) {
3900
3937
  if (command.length === 0)
3901
3938
  return false;
@@ -3903,7 +3940,7 @@ function isServerInstalled(command, _workingDirectory) {
3903
3940
  if (!cmd)
3904
3941
  return false;
3905
3942
  if (cmd.includes("/") || cmd.includes("\\")) {
3906
- if (existsSync8(cmd))
3943
+ if (existsSync9(cmd))
3907
3944
  return true;
3908
3945
  }
3909
3946
  const isWindows = process.platform === "win32";
@@ -3924,7 +3961,7 @@ function isServerInstalled(command, _workingDirectory) {
3924
3961
  const paths = pathEnv.split(delimiter3);
3925
3962
  for (const p of paths) {
3926
3963
  for (const suffix of exts) {
3927
- if (existsSync8(join3(p, cmd + suffix))) {
3964
+ if (existsSync9(join4(p, cmd + suffix))) {
3928
3965
  return true;
3929
3966
  }
3930
3967
  }
@@ -4020,7 +4057,6 @@ function getAllServers() {
4020
4057
  }
4021
4058
 
4022
4059
  // ../lsp-core/src/lsp/client-wrapper.ts
4023
- var WORKSPACE_MARKERS = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
4024
4060
  function isDirectoryPath(filePath) {
4025
4061
  try {
4026
4062
  return statSync3(filePath).isDirectory();
@@ -4033,21 +4069,23 @@ function findWorkspaceRoot(filePath) {
4033
4069
  const abs = resolveReadablePathInsideContext(filePath);
4034
4070
  let dir = abs;
4035
4071
  if (!isDirectoryPath(dir)) {
4036
- dir = dirname6(dir);
4072
+ dir = dirname7(dir);
4037
4073
  }
4074
+ if (!isPathInside(cwd, abs))
4075
+ return findWorkspaceRootOutsideContext(dir);
4038
4076
  const fallbackRoot = nearestExistingDirectoryInsideContext(dir, cwd) ?? cwd;
4039
4077
  while (isPathInside(cwd, dir)) {
4040
4078
  const canonicalDir = existingDirectoryInsideContext(dir, cwd);
4041
4079
  if (canonicalDir !== undefined) {
4042
4080
  for (const marker of WORKSPACE_MARKERS) {
4043
- if (existsSync9(join4(dir, marker))) {
4081
+ if (existsSync10(join5(dir, marker))) {
4044
4082
  return canonicalDir;
4045
4083
  }
4046
4084
  }
4047
4085
  }
4048
4086
  if (dir === cwd)
4049
4087
  break;
4050
- dir = dirname6(dir);
4088
+ dir = dirname7(dir);
4051
4089
  }
4052
4090
  return fallbackRoot;
4053
4091
  }
@@ -4059,10 +4097,7 @@ function resolveReadablePathInsideContext(filePath) {
4059
4097
  const rebased = rebaseThroughCanonicalAncestor(abs, cwd);
4060
4098
  if (rebased !== undefined)
4061
4099
  return rebased;
4062
- const canonical = canonicalizeExistingOrNearestAncestor(abs);
4063
- if (isPathInside(cwd, canonical))
4064
- return canonical;
4065
- throw new LspInvalidPathError(`LSP file path must be inside request cwd: ${filePath}`);
4100
+ return canonicalizeExistingOrNearestAncestor(abs);
4066
4101
  }
4067
4102
  function resolvePathInsideContext(filePath) {
4068
4103
  const cwd = contextCwd();
@@ -4077,12 +4112,12 @@ function rebaseThroughCanonicalAncestor(path, cwd) {
4077
4112
  let current = path;
4078
4113
  const suffix = [];
4079
4114
  while (true) {
4080
- if (existsSync9(current)) {
4081
- const canonical = realpathSync4(current);
4115
+ if (existsSync10(current)) {
4116
+ const canonical = realpathSync5(current);
4082
4117
  if (isPathInside(cwd, canonical))
4083
- return suffix.length === 0 ? canonical : join4(canonical, ...suffix);
4118
+ return suffix.length === 0 ? canonical : join5(canonical, ...suffix);
4084
4119
  }
4085
- const parent = dirname6(current);
4120
+ const parent = dirname7(current);
4086
4121
  if (parent === current)
4087
4122
  return;
4088
4123
  suffix.unshift(basename3(current));
@@ -4090,9 +4125,9 @@ function rebaseThroughCanonicalAncestor(path, cwd) {
4090
4125
  }
4091
4126
  }
4092
4127
  function existingDirectoryInsideContext(directory, cwd) {
4093
- if (!existsSync9(directory))
4128
+ if (!existsSync10(directory))
4094
4129
  return;
4095
- const canonical = realpathSync4(directory);
4130
+ const canonical = realpathSync5(directory);
4096
4131
  if (!statSync3(canonical).isDirectory())
4097
4132
  return;
4098
4133
  return isPathInside(cwd, canonical) ? canonical : undefined;
@@ -4105,7 +4140,7 @@ function nearestExistingDirectoryInsideContext(directory, cwd) {
4105
4140
  return canonical;
4106
4141
  if (current === cwd)
4107
4142
  return;
4108
- current = dirname6(current);
4143
+ current = dirname7(current);
4109
4144
  }
4110
4145
  return;
4111
4146
  }
@@ -4223,8 +4258,8 @@ async function withLspClient(filePath, fn, toolName, options = {}) {
4223
4258
  }
4224
4259
 
4225
4260
  // ../lsp-core/src/lsp/directory-diagnostics.ts
4226
- import { existsSync as existsSync10, lstatSync as lstatSync4, readdirSync as readdirSync3 } from "node:fs";
4227
- import { join as join5, resolve as resolve8 } from "node:path";
4261
+ import { existsSync as existsSync11, lstatSync as lstatSync4, readdirSync as readdirSync3 } from "node:fs";
4262
+ import { join as join6, resolve as resolve8 } from "node:path";
4228
4263
 
4229
4264
  // ../lsp-core/src/lsp/formatters.ts
4230
4265
  import { fileURLToPath as fileURLToPath2 } from "node:url";
@@ -4354,7 +4389,7 @@ function collectFilesWithExtension(dir, extension, maxFiles) {
4354
4389
  for (const entry of entries) {
4355
4390
  if (files.length >= maxFiles)
4356
4391
  return;
4357
- const fullPath = join5(currentDir, entry);
4392
+ const fullPath = join6(currentDir, entry);
4358
4393
  let stat;
4359
4394
  try {
4360
4395
  stat = lstatSync4(fullPath);
@@ -4380,7 +4415,7 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
4380
4415
  throw new LspInvalidPathError(`Extension must start with a dot (e.g., ".ts", not "${extension}"). Use ".${extension}" instead.`);
4381
4416
  }
4382
4417
  const absDir = resolve8(options.workspaceRoot ?? contextCwd(), directory);
4383
- if (!existsSync10(absDir)) {
4418
+ if (!existsSync11(absDir)) {
4384
4419
  throw new LspInvalidPathError(`Directory does not exist: ${absDir}`);
4385
4420
  }
4386
4421
  const serverResult = options.server === undefined ? findServerForExtension(extension) : { status: "found", server: options.server };
@@ -4467,7 +4502,7 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
4467
4502
 
4468
4503
  // ../lsp-core/src/lsp/infer-extension.ts
4469
4504
  import { lstatSync as lstatSync5, readdirSync as readdirSync4 } from "node:fs";
4470
- import { join as join6 } from "node:path";
4505
+ import { join as join7 } from "node:path";
4471
4506
  var SKIP_DIRECTORIES2 = new Set(["node_modules", ".git", "dist", "build", ".next", "out"]);
4472
4507
  var MAX_SCAN_ENTRIES = 500;
4473
4508
  function inferExtensionFromDirectory(directory) {
@@ -4485,7 +4520,7 @@ function inferExtensionFromDirectory(directory) {
4485
4520
  for (const entry of entries) {
4486
4521
  if (scanned >= MAX_SCAN_ENTRIES)
4487
4522
  return;
4488
- const fullPath = join6(dir, entry);
4523
+ const fullPath = join7(dir, entry);
4489
4524
  let stat;
4490
4525
  try {
4491
4526
  stat = lstatSync5(fullPath);
@@ -5167,7 +5202,7 @@ function requestedProtocolVersion(params) {
5167
5202
  // src/daemon-client.ts
5168
5203
  import { connect } from "node:net";
5169
5204
  import { homedir as homedir3 } from "node:os";
5170
- import { join as join7 } from "node:path";
5205
+ import { join as join8 } from "node:path";
5171
5206
 
5172
5207
  // src/daemon-request-error.ts
5173
5208
  class DaemonRequestError extends Error {
@@ -5246,9 +5281,9 @@ function errorText(error) {
5246
5281
 
5247
5282
  // src/ensure-daemon.ts
5248
5283
  import { spawn as spawn2 } from "node:child_process";
5249
- import { closeSync as closeSync2, existsSync as existsSync11, mkdirSync as mkdirSync3, openSync as openSync2, writeSync as writeSync2 } from "node:fs";
5284
+ import { closeSync as closeSync2, existsSync as existsSync12, mkdirSync as mkdirSync3, openSync as openSync2, writeSync as writeSync2 } from "node:fs";
5250
5285
  import { Socket } from "node:net";
5251
- import { dirname as dirname8, isAbsolute as isAbsolute4 } from "node:path";
5286
+ import { dirname as dirname9, isAbsolute as isAbsolute4 } from "node:path";
5252
5287
  import { argv0, execPath } from "node:process";
5253
5288
 
5254
5289
  // src/ipc-protocol.ts
@@ -5266,7 +5301,7 @@ import {
5266
5301
  unlinkSync,
5267
5302
  writeSync
5268
5303
  } from "node:fs";
5269
- import { dirname as dirname7 } from "node:path";
5304
+ import { dirname as dirname8 } from "node:path";
5270
5305
  var OMO_DAEMON_PROTOCOL_VERSION = 1;
5271
5306
  var AUTH_ERROR_CODE = -32001;
5272
5307
  var PROTOCOL_ERROR_CODE = -32002;
@@ -5376,7 +5411,7 @@ function setPrivateFileMode(path) {
5376
5411
  chmodSync(path, 384);
5377
5412
  }
5378
5413
  function createAuthToken(paths) {
5379
- ensurePrivateDirectory(dirname7(paths.auth));
5414
+ ensurePrivateDirectory(dirname8(paths.auth));
5380
5415
  const token = randomBytes(AUTH_TOKEN_BYTES).toString("base64url");
5381
5416
  let fd;
5382
5417
  try {
@@ -5716,7 +5751,7 @@ function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS, signal) {
5716
5751
  });
5717
5752
  }
5718
5753
  function spawnDaemonProcess(paths) {
5719
- mkdirSync3(dirname8(paths.log), { recursive: true });
5754
+ mkdirSync3(dirname9(paths.log), { recursive: true });
5720
5755
  const logFd = openSync2(paths.log, "a");
5721
5756
  try {
5722
5757
  const child = spawn2(resolveDaemonNodeExecutable(), [paths.cliPath, "daemon"], {
@@ -5735,7 +5770,7 @@ function spawnDaemonProcess(paths) {
5735
5770
  throw error;
5736
5771
  }
5737
5772
  }
5738
- function resolveDaemonNodeExecutable(cachedExecPath = execPath, originalArgv0 = argv0, pathExists = existsSync11) {
5773
+ function resolveDaemonNodeExecutable(cachedExecPath = execPath, originalArgv0 = argv0, pathExists = existsSync12) {
5739
5774
  if (pathExists(cachedExecPath))
5740
5775
  return cachedExecPath;
5741
5776
  if (isAbsolute4(originalArgv0) && pathExists(originalArgv0))
@@ -5955,9 +5990,9 @@ function currentRequestContext(env = process.env) {
5955
5990
  const home = env["HOME"] ?? homedir3();
5956
5991
  return parseLspRequestContext({
5957
5992
  cwd,
5958
- projectConfigPaths: [join7(cwd, ".codex", "lsp-client.json")],
5959
- userConfigPath: join7(home, ".codex", "lsp-client.json"),
5960
- installDecisionsPath: join7(home, ".codex", "lsp-install-decisions.json"),
5993
+ projectConfigPaths: [join8(cwd, ".codex", "lsp-client.json")],
5994
+ userConfigPath: join8(home, ".codex", "lsp-client.json"),
5995
+ installDecisionsPath: join8(home, ".codex", "lsp-install-decisions.json"),
5961
5996
  capabilities: { installDecisionTool: true }
5962
5997
  });
5963
5998
  }
@@ -6212,18 +6247,18 @@ function canonicalizePathList(value) {
6212
6247
  return value.split(delimiter4).map((entry) => canonicalizePath(entry) ?? entry).join(delimiter4);
6213
6248
  }
6214
6249
  function canonicalizePath(value) {
6215
- if (value === undefined || !isAbsolute5(value) || !existsSync12(value))
6250
+ if (value === undefined || !isAbsolute5(value) || !existsSync13(value))
6216
6251
  return value;
6217
- return realpathSync5(value);
6252
+ return realpathSync6(value);
6218
6253
  }
6219
6254
  function projectRootFromOpenCodeConfigPath(path2) {
6220
6255
  if (basename4(path2) !== "lsp.json" && basename4(path2) !== "lsp-client.json")
6221
6256
  return;
6222
- const configDir = dirname9(path2);
6257
+ const configDir = dirname10(path2);
6223
6258
  const configDirName = basename4(configDir);
6224
6259
  if (configDirName !== ".opencode" && configDirName !== ".omo")
6225
6260
  return;
6226
- return dirname9(configDir);
6261
+ return dirname10(configDir);
6227
6262
  }
6228
6263
 
6229
6264
  // src/daemon-server.ts
@@ -6232,12 +6267,12 @@ import { createServer as createServer2 } from "node:net";
6232
6267
 
6233
6268
  // src/ownership.ts
6234
6269
  import { randomUUID } from "node:crypto";
6235
- import { existsSync as existsSync13, lstatSync as lstatSync7, readFileSync as readFileSync7, statSync as statSync5, unlinkSync as unlinkSync3 } from "node:fs";
6236
- import { dirname as dirname11 } from "node:path";
6270
+ import { existsSync as existsSync14, lstatSync as lstatSync7, readFileSync as readFileSync7, statSync as statSync5, unlinkSync as unlinkSync3 } from "node:fs";
6271
+ import { dirname as dirname12 } from "node:path";
6237
6272
 
6238
6273
  // src/lock.ts
6239
6274
  import { closeSync as closeSync3, mkdirSync as mkdirSync4, openSync as openSync3, readFileSync as readFileSync6, unlinkSync as unlinkSync2, writeSync as writeSync3 } from "node:fs";
6240
- import { dirname as dirname10 } from "node:path";
6275
+ import { dirname as dirname11 } from "node:path";
6241
6276
  function isProcessAlive2(pid) {
6242
6277
  if (!Number.isInteger(pid) || pid <= 0)
6243
6278
  return false;
@@ -6259,7 +6294,7 @@ function readLockPid(lockPath) {
6259
6294
  }
6260
6295
  }
6261
6296
  function tryAcquireLock(lockPath, ownerPid = process.pid) {
6262
- mkdirSync4(dirname10(lockPath), { recursive: true });
6297
+ mkdirSync4(dirname11(lockPath), { recursive: true });
6263
6298
  for (let attempt = 0;attempt < 2; attempt += 1) {
6264
6299
  const handle = writeLockFile(lockPath, ownerPid);
6265
6300
  if (handle)
@@ -6340,7 +6375,7 @@ async function acquireStartupLease(paths, pingOwner) {
6340
6375
  function ensureDaemonDirectories(paths) {
6341
6376
  ensurePrivateDirectory(paths.dir);
6342
6377
  if (process.platform !== "win32")
6343
- ensurePrivateDirectory(dirname11(paths.socket));
6378
+ ensurePrivateDirectory(dirname12(paths.socket));
6344
6379
  }
6345
6380
  function readDaemonOwner(paths) {
6346
6381
  try {
@@ -6420,7 +6455,7 @@ async function validateExistingOwner(paths, pingOwner) {
6420
6455
  throw new DaemonStartupDeferredError("reachable_owner_mismatch");
6421
6456
  }
6422
6457
  function cleanupDeadOwner(paths, owner) {
6423
- if (process.platform !== "win32" && existsSync13(owner.endpoint.path)) {
6458
+ if (process.platform !== "win32" && existsSync14(owner.endpoint.path)) {
6424
6459
  const stat = lstatSync7(owner.endpoint.path);
6425
6460
  if (stat.isSocket())
6426
6461
  unlinkSync3(owner.endpoint.path);
@@ -6471,7 +6506,7 @@ function parseEndpoint(value) {
6471
6506
  // src/version-reap.ts
6472
6507
  import { execFile } from "node:child_process";
6473
6508
  import { readdir, readFile, rm } from "node:fs/promises";
6474
- import { basename as basename5, dirname as dirname12, join as join8 } from "node:path";
6509
+ import { basename as basename5, dirname as dirname13, join as join9 } from "node:path";
6475
6510
  var TERM_GRACE_MS = 5000;
6476
6511
  var KILL_GRACE_MS = 1000;
6477
6512
  var VERSION_ENTRY_PATTERN = /^v([A-Za-z0-9][A-Za-z0-9._+-]{0,127})$/;
@@ -6492,7 +6527,7 @@ async function attestDaemonCliProcess(pid, platform, deps = {}) {
6492
6527
  return isNodeCliDaemonCommand(command.trim());
6493
6528
  }
6494
6529
  async function reapStaleDaemonVersions(ownPaths, deps = {}) {
6495
- const baseDir = dirname12(ownPaths.dir);
6530
+ const baseDir = dirname13(ownPaths.dir);
6496
6531
  const platform = deps.platform ?? process.platform;
6497
6532
  const isAlive = deps.isAlive ?? isProcessAlive2;
6498
6533
  const attest = deps.attest ?? ((pid) => attestDaemonCliProcess(pid, platform));
@@ -6511,7 +6546,7 @@ async function reapStaleDaemonVersions(ownPaths, deps = {}) {
6511
6546
  const version = parseVersionEntry(entry.name);
6512
6547
  if (version === null || !entry.isDirectory())
6513
6548
  continue;
6514
- const versionDir = join8(baseDir, entry.name);
6549
+ const versionDir = join9(baseDir, entry.name);
6515
6550
  const siblingPaths = daemonPaths({ [OMO_LSP_DAEMON_DIR]: baseDir }, { cliPath: ownPaths.cliPath, version });
6516
6551
  results.push(await reapOneVersion({
6517
6552
  version,
@@ -6535,7 +6570,7 @@ async function reapOneVersion(context) {
6535
6570
  const { version, versionDir } = context;
6536
6571
  const owner = context.readOwner(context.siblingPaths);
6537
6572
  if (!owner) {
6538
- const lockPid = readLockPid(join8(versionDir, "daemon.lock"));
6573
+ const lockPid = readLockPid(join9(versionDir, "daemon.lock"));
6539
6574
  if (lockPid !== null && context.isAlive(lockPid)) {
6540
6575
  context.log(`reap: sparing v${version}: owner metadata missing but daemon.lock is held by live pid ${lockPid}`);
6541
6576
  return { version, status: "spared", reason: `owner metadata missing but lock held by live pid ${lockPid}` };